Skip to content

Commit

Permalink
address PR feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
dsokal committed Jun 9, 2021
1 parent cc28f39 commit 3982742
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 98 deletions.
8 changes: 1 addition & 7 deletions packages/eas-cli/src/build/ios/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,7 @@ export async function prepareIosBuildAsync(
nonInteractive: commandCtx.nonInteractive,
exp: commandCtx.exp,
},
{
workflow: buildProfile.workflow,
...(buildProfile.workflow === Workflow.GENERIC && {
buildScheme: buildProfile.scheme,
buildConfiguration: buildProfile.schemeBuildConfiguration,
}),
}
buildProfile
);
const targets = await resolveTargetsAsync(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,16 @@ describe('update credentials.json', () => {

await updateIosCredentialsAsync(ctx, app, targets, IosDistributionType.AppStore);

const { targetName } = targets[0];
const certP12 = await fs.readFile(`./credentials/ios/${targetName}-dist-cert.p12`, 'base64');
const pprofile = await fs.readFile(
`./credentials/ios/${targetName}-profile.mobileprovision`,
'base64'
);
const certP12 = await fs.readFile('./credentials/ios/dist-cert.p12', 'base64');
const pprofile = await fs.readFile('./credentials/ios/profile.mobileprovision', 'base64');
const credJson = await fs.readJson('./credentials.json');
expect(certP12).toEqual(testAllCredentialsForApp.distCredentials.certP12);
expect(pprofile).toEqual(testAllCredentialsForApp.credentials.provisioningProfile);
expect(credJson).toEqual({
ios: {
provisioningProfilePath: `credentials/ios/${targetName}-profile.mobileprovision`,
provisioningProfilePath: `credentials/ios/profile.mobileprovision`,
distributionCertificate: {
path: `credentials/ios/${targetName}-dist-cert.p12`,
path: `credentials/ios/dist-cert.p12`,
password: testAllCredentialsForApp.distCredentials.certPassword,
},
},
Expand Down Expand Up @@ -306,7 +302,7 @@ describe('update credentials.json', () => {
await updateIosCredentialsAsync(ctx, app, targets, IosDistributionType.AppStore);
throw new Error('updateIosCredentialsAsync should throw na error');
} catch (e) {
expect(e.message).toMatch("Some of the build targets don't have credentials configured");
expect(e.message).toMatch('There are no credentials configured');
}
const certP12 = await fs.readFile('./cert.p12', 'base64');
const pprofile = await fs.readFile('./pprofile', 'base64');
Expand Down
92 changes: 73 additions & 19 deletions packages/eas-cli/src/credentials/credentialsJson/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,24 @@ export async function updateIosCredentialsAsync(
}

if (!areAllTargetsConfigured) {
throw new Error(
`Some of the build targets don't have credentials configured for the ${distributionType} distribution of this project are not on EAS servers: ${notConfiguredTargetLabels}`
);
const errorMessage =
targets.length === 1
? `There are no credentials configured for the ${distributionType} distribution of this project on EAS servers`
: `Some of the build targets don't have credentials configured for the ${distributionType} distribution of this project on EAS servers: ${notConfiguredTargetLabels}`;
throw new Error(errorMessage);
}

const iosCredentials: CredentialsJsonIosCredentials = {};
const targetCredentialsPathsMap = createTargetCredentialsPathsMap(
targets,
rawCredentialsJson.ios
);
for (const target of targets) {
iosCredentials[target.targetName] = await updateIosTargetCredentialsAsync(
ctx,
target,
iosCredentials[target.targetName] = await backupTargetCredentialsAsync(ctx, {
// app build credentials must exist for target because otherwise an error must have been thrown earlier
nullthrows(targetBuildsCredentialsMap[target.targetName]),
rawCredentialsJson.ios?.[target.targetName]
);
targetCredentials: nullthrows(targetBuildsCredentialsMap[target.targetName]),
targetCredentialsPaths: targetCredentialsPathsMap[target.targetName],
});
}

if (Object.keys(iosCredentials).length === 1) {
Expand Down Expand Up @@ -160,6 +164,57 @@ export async function updateIosCredentialsAsync(
displayUntrackedFilesWarning(newFilePaths);
}

interface TargetCredentialsPaths {
provisioningProfilePath: string;
distCertPath: string;
}
type TargetCredentialsPathsMap = Record<string, TargetCredentialsPaths>;
function createTargetCredentialsPathsMap(
targets: Target[],
rawCredentialsJsonMap?: any
): TargetCredentialsPathsMap {
const hasManyTargets = targets.length > 1;
const paths: TargetCredentialsPathsMap = {};

// 1. Create initial target credentials paths map
for (const target of targets) {
const rawTargetCredentialsJson = rawCredentialsJsonMap?.[target.targetName];
const filePrefix = hasManyTargets ? `${target.targetName}-` : '';

paths[target.targetName] = {
provisioningProfilePath:
rawTargetCredentialsJson?.provisioningProfilePath ??
`credentials/ios/${filePrefix}profile.mobileprovision`,
distCertPath:
rawTargetCredentialsJson?.distributionCertificate?.path ??
`credentials/ios/${filePrefix}dist-cert.p12`,
};
}

// 2. Look for duplicates and prefix them with target names
const deduplicatedPaths: TargetCredentialsPathsMap = {};
const usedProfilePaths = new Set<string>();
const usedDistCertPaths = new Set<string>();
for (const [targetName, { provisioningProfilePath, distCertPath }] of Object.entries(paths)) {
const newProvisioningProfilePath = usedProfilePaths.has(provisioningProfilePath)
? `${targetName}-${provisioningProfilePath}`
: provisioningProfilePath;
usedProfilePaths.add(newProvisioningProfilePath);

const newDistCertPath = usedDistCertPaths.has(distCertPath)
? `${targetName}-${distCertPath}`
: distCertPath;
usedDistCertPaths.add(newDistCertPath);

deduplicatedPaths[targetName] = {
distCertPath: newDistCertPath,
provisioningProfilePath: newProvisioningProfilePath,
};
}

return deduplicatedPaths;
}

async function getTargetBuildCredentialsAsync(
ctx: Context,
app: App,
Expand Down Expand Up @@ -199,18 +254,17 @@ async function getTargetBuildCredentialsAsync(
};
}

async function updateIosTargetCredentialsAsync(
async function backupTargetCredentialsAsync(
ctx: Context,
target: Target,
targetCredentials: TargetCredentials,
currentRawTargetCredentialsObject?: any
{
targetCredentials,
targetCredentialsPaths,
}: {
targetCredentials: TargetCredentials;
targetCredentialsPaths: TargetCredentialsPaths;
}
): Promise<CredentialsJsonIosTargetCredentials> {
const provisioningProfilePath: string =
currentRawTargetCredentialsObject?.provisioningProfilePath ??
`credentials/ios/${target.targetName}-profile.mobileprovision`;
const distCertPath: string =
currentRawTargetCredentialsObject?.distributionCertificate?.path ??
`credentials/ios/${target.targetName}-dist-cert.p12`;
const { provisioningProfilePath, distCertPath } = targetCredentialsPaths;

Log.log(`Writing Provisioning Profile to ${provisioningProfilePath}`);
await updateFileAsync(
Expand Down
19 changes: 13 additions & 6 deletions packages/eas-cli/src/credentials/credentialsJson/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export function getCredentialsJsonPath(projectDir: string): string {
return path.join(projectDir, 'credentials.json');
}

export function ensureAllTargetsAreConfigured(targets: Target[], credentialsJson: IosCredentials) {
export function ensureAllTargetsAreConfigured(
targets: Target[],
credentialsJson: IosCredentials
): void {
const notConfiguredTargets: string[] = [];
for (const target of targets) {
if (!(target.targetName in credentialsJson)) {
Expand All @@ -17,10 +20,14 @@ export function ensureAllTargetsAreConfigured(targets: Target[], credentialsJson
}

if (notConfiguredTargets.length > 0) {
throw new Error(
`Credentials for target${
notConfiguredTargets.length === 1 ? '' : 's'
} ${notConfiguredTargets.map(i => `'${i}'`).join(',')} are not defined in credentials.json`
);
const errorMessage =
targets.length === 1
? 'Credentials are not defined in credentials.json'
: `Credentials for target${
notConfiguredTargets.length === 1 ? '' : 's'
} ${notConfiguredTargets
.map(i => `'${i}'`)
.join(',')} are not defined in credentials.json`;
throw new Error(errorMessage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function sortBuildCredentialsByDistributionType(

export function displayIosCredentials(
app: App,
appCredentials: IosAppCredentialsMap,
appCredentialsMap: IosAppCredentialsMap,
targets: Target[]
): void {
const projectFullName = `@${app.account.name}/${app.projectName}`;
Expand All @@ -75,7 +75,7 @@ export function displayIosCredentials(
Log.log(` Target: ${chalk.bold(targetName)}`);
}
Log.log(` Bundle Identifier: ${chalk.bold(bundleIdentifier)}`);
const targetAppCredentials = appCredentials[targetName];
const targetAppCredentials = appCredentialsMap[targetName];
if (!targetAppCredentials || targetAppCredentials.iosAppBuildCredentialsList.length === 0) {
Log.newLine();
Log.log(` No credentials set up yet!`);
Expand Down
88 changes: 48 additions & 40 deletions packages/eas-cli/src/credentials/manager/ManageIos.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Workflow } from '@expo/eas-build-job';
import { EasJsonReader, IosBuildProfile } from '@expo/eas-json';
import assert from 'assert';
import nullthrows from 'nullthrows';

import {
Expand Down Expand Up @@ -74,50 +74,23 @@ export class ManageIos implements Action {
ctx: Context,
currentActions: ActionInfo[] = highLevelActions
): Promise<void> {
let app: App | null = null;
let targets: Target[] | null = null;
let buildProfile: IosBuildProfile | null = null;
await ctx.bestEffortAppStoreAuthenticateAsync();

while (true) {
try {
app = null;
targets = null;
buildProfile = null;

await ctx.bestEffortAppStoreAuthenticateAsync();
const accountName = ctx.hasProjectContext
? getProjectAccountName(ctx.exp, ctx.user)
: ensureActorHasUsername(ctx.user);

const accountName = ctx.hasProjectContext
? getProjectAccountName(ctx.exp, ctx.user)
: ensureActorHasUsername(ctx.user);
const account = findAccountByName(ctx.user.accounts, accountName);
if (!account) {
throw new Error(`You do not have access to account: ${accountName}`);
}

const account = findAccountByName(ctx.user.accounts, accountName);
if (!account) {
throw new Error(`You do not have access to account: ${accountName}`);
}
while (true) {
try {
const { app, targets, buildProfile } = await this.createProjectContextAsync(ctx, account);

if (ctx.hasProjectContext) {
app = { account, projectName: ctx.exp.slug };
const easJsonReader = new EasJsonReader(ctx.projectDir, 'ios');
const easConfig = await new SelectBuildProfileFromEasJson(easJsonReader).runAsync(ctx);
buildProfile = nullthrows(easConfig.builds.ios, 'iOS build profile must be defined');
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
{
projectDir: ctx.projectDir,
nonInteractive: ctx.nonInteractive,
exp: ctx.exp,
},
{
workflow: buildProfile.workflow,
...(buildProfile.workflow === Workflow.GENERIC && {
buildConfiguration: buildProfile.schemeBuildConfiguration,
buildScheme: buildProfile.scheme,
}),
}
);
targets = await resolveTargetsAsync(
{ exp: ctx.exp, projectDir: ctx.projectDir },
xcodeBuildContext
);
assert(targets && app);
const iosAppCredentialsMap: IosAppCredentialsMap = {};
for (const target of targets) {
const appLookupParams = getAppLookupParamsFromContext(ctx, target);
Expand Down Expand Up @@ -229,6 +202,41 @@ export class ManageIos implements Action {
}
}

private async createProjectContextAsync(
ctx: Context,
account: Account
): Promise<{ app: App | null; targets: Target[] | null; buildProfile: IosBuildProfile | null }> {
if (!ctx.hasProjectContext) {
return {
app: null,
targets: null,
buildProfile: null,
};
}

const app = { account, projectName: ctx.exp.slug };
const easJsonReader = new EasJsonReader(ctx.projectDir, 'ios');
const easConfig = await new SelectBuildProfileFromEasJson(easJsonReader).runAsync(ctx);
const buildProfile = nullthrows(easConfig.builds.ios, 'iOS build profile must be defined');
const xcodeBuildContext = await resolveXcodeBuildContextAsync(
{
projectDir: ctx.projectDir,
nonInteractive: ctx.nonInteractive,
exp: ctx.exp,
},
buildProfile
);
const targets = await resolveTargetsAsync(
{ exp: ctx.exp, projectDir: ctx.projectDir },
xcodeBuildContext
);
return {
app,
targets,
buildProfile,
};
}

private async runAccountSpecificActionAsync(
ctx: Context,
account: Account,
Expand Down
21 changes: 6 additions & 15 deletions packages/eas-cli/src/project/ios/scheme.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ExpoConfig } from '@expo/config';
import { IOSConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { Workflow } from '@expo/eas-build-job';
import { IosBuildProfile } from '@expo/eas-json';
import chalk from 'chalk';
import sortBy from 'lodash/sortBy';

import Log from '../../log';
import { promptAsync } from '../../prompts';
import { sanitizedProjectName } from '../projectUtils';
import { resolveWorkflow } from '../workflow';

export interface XcodeBuildContext {
buildScheme: string;
Expand All @@ -20,28 +20,19 @@ export async function resolveXcodeBuildContextAsync(
projectDir,
nonInteractive,
}: { exp: ExpoConfig; projectDir: string; nonInteractive: boolean },
{
workflow: _workflow,
buildScheme: _buildScheme,
buildConfiguration,
}: {
buildScheme?: string;
buildConfiguration?: string;
workflow?: Workflow;
} = {}
buildProfile: IosBuildProfile
): Promise<XcodeBuildContext> {
const workflow = _workflow ?? resolveWorkflow(projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
if (buildProfile.workflow === Workflow.GENERIC) {
const buildScheme =
_buildScheme ??
buildProfile.scheme ??
(await selectSchemeAsync({
projectDir,
nonInteractive,
}));
return {
buildScheme,
buildConfiguration:
buildConfiguration ??
buildProfile.schemeBuildConfiguration ??
(await IOSConfig.BuildScheme.getArchiveBuildConfigurationForSchemeAsync(
projectDir,
buildScheme
Expand Down

0 comments on commit 3982742

Please sign in to comment.