mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-13 20:28:08 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aad0bec1d |
No files matched your search
+1
-5
@@ -9,12 +9,10 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
### 🎉 New features
|
||||
|
||||
- Add ASC Api Key generation workflow. ([#718](https://github.com/expo/eas-cli/pull/718) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Add support for removal of ASC Api Keys. ([#721](https://github.com/expo/eas-cli/pull/721) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Add support for removal of App Store Connect Api Keys. ([#721](https://github.com/expo/eas-cli/pull/721) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Allow users to assign an ASC Api Key to their project. ([#719](https://github.com/expo/eas-cli/pull/719) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Add setup support for ASC Api Keys. ([#733](https://github.com/expo/eas-cli/pull/733) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Show initiating user display name when selecting a build to submit. ([#730](https://github.com/expo/eas-cli/pull/730) by [@barthap](https://github.com/barthap))
|
||||
- Handle Apple servers maintenance error in `eas submit`. ([#738](https://github.com/expo/eas-cli/pull/738) by [@barthap](https://github.com/barthap))
|
||||
- Integrate ASC Api Key with submissions workflow. ([#737](https://github.com/expo/eas-cli/pull/737) by [@quinlanj](https://github.com/quinlanj))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
@@ -22,8 +20,6 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
- Clean up credentials prompt method. ([#728](https://github.com/expo/eas-cli/pull/728) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Bump @expo/apple-utils to 0.0.0-alpha.26. ([#723](https://github.com/expo/eas-cli/pull/723] by [@brentvatne](https://github.com/brentvatne))
|
||||
- Grammar: replace setup with set up. ([#735](https://github.com/expo/eas-cli/pull/735) by [@quinlanj](https://github.com/quinlanj))
|
||||
- Improve VCS workflow migration experience. ([#732](https://github.com/expo/eas-cli/pull/732) by [@wkozyra95](https://github.com/wkozyra95))
|
||||
|
||||
## [0.34.1](https://github.com/expo/eas-cli/releases/tag/0.34.1) - 2021-11-02
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { BuildContext, createBuildContextAsync } from '../../build/context';
|
||||
import { prepareIosBuildAsync } from '../../build/ios/build';
|
||||
import { ensureExpoDevClientInstalledForDevClientBuildsAsync } from '../../build/utils/devClient';
|
||||
import { printBuildResults, printLogsUrls } from '../../build/utils/printBuildInfo';
|
||||
import { ensureRepoIsCleanAsync, reviewAndCommitChangesAsync } from '../../build/utils/repository';
|
||||
import { ensureRepoIsCleanAsync } from '../../build/utils/repository';
|
||||
import EasCommand from '../../commandUtils/EasCommand';
|
||||
import { CredentialsContext } from '../../credentials/context';
|
||||
import {
|
||||
@@ -392,8 +392,7 @@ export async function handleDeprecatedEasJsonAsync(
|
||||
projectDir: string,
|
||||
nonInteractive: boolean
|
||||
): Promise<void> {
|
||||
const easJsonPath = EasJsonReader.formatEasJsonPath(projectDir);
|
||||
if (!(await fs.pathExists(easJsonPath))) {
|
||||
if (!(await fs.pathExists(EasJsonReader.formatEasJsonPath(projectDir)))) {
|
||||
return;
|
||||
}
|
||||
const easJsonReader = new EasJsonReader(projectDir);
|
||||
@@ -433,21 +432,12 @@ export async function handleDeprecatedEasJsonAsync(
|
||||
{ title: 'Allow builds with dirty Git working tree (new default)', value: 'noCommit' },
|
||||
]);
|
||||
|
||||
if (mode === 'requireCommit') {
|
||||
setVcsClient(new GitClient());
|
||||
await ensureRepoIsCleanAsync(nonInteractive);
|
||||
}
|
||||
|
||||
rawEasJson.cli =
|
||||
mode === 'requireCommit'
|
||||
? { version: `>= ${easCliVersion}`, requireCommit: true }
|
||||
: { version: `>= ${easCliVersion}` };
|
||||
await fs.writeJSON(easJsonPath, rawEasJson, { spaces: 2 });
|
||||
Log.withTick('Updated eas.json');
|
||||
await fs.writeJSON(EasJsonReader.formatEasJsonPath(projectDir), rawEasJson, { spaces: 2 });
|
||||
if (mode === 'requireCommit') {
|
||||
await getVcsClient().trackFileAsync(easJsonPath);
|
||||
await reviewAndCommitChangesAsync('Set cli.requireCommit to true in eas.json', {
|
||||
nonInteractive,
|
||||
});
|
||||
setVcsClient(new GitClient());
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import nullthrows from 'nullthrows';
|
||||
import { AndroidAppBuildCredentialsFragment } from '../../graphql/generated';
|
||||
import { CredentialsContext } from '../context';
|
||||
import * as credentialsJsonReader from '../credentialsJson/read';
|
||||
import { SetUpBuildCredentials } from './actions/SetUpBuildCredentials';
|
||||
import { SetupBuildCredentials } from './actions/SetupBuildCredentials';
|
||||
import { AppLookupParams } from './api/GraphqlClient';
|
||||
import { Keystore } from './credentials';
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class AndroidCredentialsProvider {
|
||||
}
|
||||
|
||||
private async getRemoteAsync(): Promise<AndroidCredentials> {
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentials({ app: this.options.app });
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentials({ app: this.options.app });
|
||||
const buildCredentials = await setupBuildCredentialsAction.runAsync(this.ctx);
|
||||
return this.toAndroidCredentials(buildCredentials);
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ interface Options {
|
||||
* Sets up Build Credentials for Android
|
||||
* @name: sets up build credentials for the specified configuration. If no name is specified, the default configuration is setup
|
||||
*/
|
||||
export class SetUpBuildCredentials {
|
||||
export class SetupBuildCredentials {
|
||||
constructor(private options: Options) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<AndroidAppBuildCredentialsFragment> {
|
||||
+1
-1
@@ -14,7 +14,7 @@ import { AppLookupParams } from '../api/GraphqlClient';
|
||||
import { getKeystoreWithType } from '../utils/keystoreNew';
|
||||
import { BackupKeystore } from './DownloadKeystore';
|
||||
|
||||
export class SetUpBuildCredentialsFromCredentialsJson {
|
||||
export class SetupBuildCredentialsFromCredentialsJson {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<AndroidAppBuildCredentialsFragment | null> {
|
||||
+1
-1
@@ -13,7 +13,7 @@ import { AssignGoogleServiceAccountKey } from './AssignGoogleServiceAccountKey';
|
||||
import { CreateGoogleServiceAccountKey } from './CreateGoogleServiceAccountKey';
|
||||
import { UseExistingGoogleServiceAccountKey } from './UseExistingGoogleServiceAccountKey';
|
||||
|
||||
export class SetUpGoogleServiceAccountKey {
|
||||
export class SetupGoogleServiceAccountKey {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
public async runAsync(ctx: CredentialsContext): Promise<CommonAndroidAppCredentialsFragment> {
|
||||
+5
-5
@@ -9,7 +9,7 @@ import { createCtxMock } from '../../../__tests__/fixtures-context';
|
||||
import { MissingCredentialsNonInteractiveError } from '../../../errors';
|
||||
import { generateRandomKeystoreAsync } from '../../utils/keystore';
|
||||
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
|
||||
import { SetUpBuildCredentials } from '../SetUpBuildCredentials';
|
||||
import { SetupBuildCredentials } from '../SetupBuildCredentials';
|
||||
|
||||
jest.mock('../../../../ora');
|
||||
jest.mock('../../../../project/ensureProjectExists');
|
||||
@@ -17,7 +17,7 @@ jest.mock('../../../../user/actions', () => ({ ensureLoggedInAsync: jest.fn(() =
|
||||
jest.mock('../../../../prompts', () => ({ confirmAsync: jest.fn(() => true) }));
|
||||
jest.mock('../../utils/keystore', () => ({ generateRandomKeystoreAsync: jest.fn() }));
|
||||
|
||||
describe('SetUpBuildCredentials', () => {
|
||||
describe('SetupBuildCredentials', () => {
|
||||
beforeEach(() => {
|
||||
asMock(generateRandomKeystoreAsync).mockReset();
|
||||
});
|
||||
@@ -33,7 +33,7 @@ describe('SetUpBuildCredentials', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentials({ app: appLookupParams });
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentials({ app: appLookupParams });
|
||||
await setupBuildCredentialsAction.runAsync(ctx);
|
||||
|
||||
// expect keystore not to be created
|
||||
@@ -48,7 +48,7 @@ describe('SetUpBuildCredentials', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentials({ app: appLookupParams });
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentials({ app: appLookupParams });
|
||||
await setupBuildCredentialsAction.runAsync(ctx);
|
||||
|
||||
// expect keystore to be created
|
||||
@@ -63,7 +63,7 @@ describe('SetUpBuildCredentials', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentials({ app: appLookupParams });
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentials({ app: appLookupParams });
|
||||
await expect(setupBuildCredentialsAction.runAsync(ctx)).rejects.toThrowError(
|
||||
MissingCredentialsNonInteractiveError
|
||||
);
|
||||
+5
-5
@@ -14,7 +14,7 @@ import {
|
||||
SelectAndroidBuildCredentialsResultType,
|
||||
} from '../../../manager/SelectAndroidBuildCredentials';
|
||||
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
|
||||
import { SetUpBuildCredentialsFromCredentialsJson } from '../SetUpBuildCredentialsFromCredentialsJson';
|
||||
import { SetupBuildCredentialsFromCredentialsJson } from '../SetupBuildCredentialsFromCredentialsJson';
|
||||
|
||||
jest.mock('fs');
|
||||
|
||||
@@ -26,7 +26,7 @@ beforeEach(() => {
|
||||
vol.reset();
|
||||
});
|
||||
|
||||
describe(SetUpBuildCredentialsFromCredentialsJson, () => {
|
||||
describe(SetupBuildCredentialsFromCredentialsJson, () => {
|
||||
it('sets up a new build configuration from credentials.json upon user request', async () => {
|
||||
asMock(SelectAndroidBuildCredentials).mockImplementation(() => {
|
||||
return {
|
||||
@@ -59,7 +59,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
|
||||
'keystore.jks': 'some-binary-content',
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentialsFromCredentialsJson(
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams
|
||||
);
|
||||
await setupBuildCredentialsAction.runAsync(ctx);
|
||||
@@ -109,7 +109,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
|
||||
'keystore.jks': 'some-binary-content',
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentialsFromCredentialsJson(
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams
|
||||
);
|
||||
await setupBuildCredentialsAction.runAsync(ctx);
|
||||
@@ -132,7 +132,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupBuildCredentialsAction = new SetUpBuildCredentialsFromCredentialsJson(
|
||||
const setupBuildCredentialsAction = new SetupBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams
|
||||
);
|
||||
await expect(setupBuildCredentialsAction.runAsync(ctx)).rejects.toThrowError();
|
||||
+5
-5
@@ -10,7 +10,7 @@ import {
|
||||
import { createCtxMock } from '../../../__tests__/fixtures-context';
|
||||
import { MissingCredentialsNonInteractiveError } from '../../../errors';
|
||||
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
|
||||
import { SetUpGoogleServiceAccountKey } from '../SetUpGoogleServiceAccountKey';
|
||||
import { SetupGoogleServiceAccountKey } from '../SetupGoogleServiceAccountKey';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
jest.mock('fs');
|
||||
@@ -22,7 +22,7 @@ beforeEach(() => {
|
||||
vol.reset();
|
||||
});
|
||||
|
||||
describe(SetUpGoogleServiceAccountKey, () => {
|
||||
describe(SetupGoogleServiceAccountKey, () => {
|
||||
it('skips setup when there is a Google Service Account Key already assigned to the project', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: false,
|
||||
@@ -34,7 +34,7 @@ describe(SetUpGoogleServiceAccountKey, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
const setupGoogleServiceAccountKeyAction = new SetupGoogleServiceAccountKey(appLookupParams);
|
||||
await setupGoogleServiceAccountKeyAction.runAsync(ctx);
|
||||
|
||||
expect(ctx.android.createGoogleServiceAccountKeyAsync).not.toHaveBeenCalled();
|
||||
@@ -57,7 +57,7 @@ describe(SetUpGoogleServiceAccountKey, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
const setupGoogleServiceAccountKeyAction = new SetupGoogleServiceAccountKey(appLookupParams);
|
||||
await setupGoogleServiceAccountKeyAction.runAsync(ctx);
|
||||
|
||||
expect(ctx.android.createGoogleServiceAccountKeyAsync).toHaveBeenCalledTimes(1);
|
||||
@@ -68,7 +68,7 @@ describe(SetUpGoogleServiceAccountKey, () => {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
const setupGoogleServiceAccountKeyAction = new SetupGoogleServiceAccountKey(appLookupParams);
|
||||
await expect(setupGoogleServiceAccountKeyAction.runAsync(ctx)).rejects.toThrowError(
|
||||
MissingCredentialsNonInteractiveError
|
||||
);
|
||||
@@ -9,8 +9,8 @@ import { CredentialsContext } from '../context';
|
||||
import * as credentialsJsonReader from '../credentialsJson/read';
|
||||
import { ensureAllTargetsAreConfigured } from '../credentialsJson/utils';
|
||||
import { getAppFromContext } from './actions/BuildCredentialsUtils';
|
||||
import { SetUpBuildCredentials } from './actions/SetUpBuildCredentials';
|
||||
import { SetUpPushKey } from './actions/SetUpPushKey';
|
||||
import { SetupBuildCredentials } from './actions/SetupBuildCredentials';
|
||||
import { SetupPushKey } from './actions/SetupPushKey';
|
||||
import { IosCapabilitiesOptions } from './appstore/ensureAppExists';
|
||||
import { App, IosCredentials, Target } from './types';
|
||||
import { isAdHocProfile } from './utils/provisioningProfile';
|
||||
@@ -58,7 +58,7 @@ export default class IosCredentialsProvider {
|
||||
}
|
||||
|
||||
private async getRemoteAsync(): Promise<IosCredentials> {
|
||||
return await new SetUpBuildCredentials({
|
||||
return await new SetupBuildCredentials({
|
||||
app: this.options.app,
|
||||
targets: this.options.targets,
|
||||
distribution: this.options.distribution,
|
||||
@@ -83,7 +83,7 @@ export default class IosCredentialsProvider {
|
||||
parentBundleIdentifier: applicationTarget.parentBundleIdentifier,
|
||||
};
|
||||
|
||||
const setupPushKeyAction = new SetUpPushKey(appLookupParams);
|
||||
const setupPushKeyAction = new SetupPushKey(appLookupParams);
|
||||
const isPushKeySetup = await setupPushKeyAction.isPushKeySetupAsync(ctx);
|
||||
if (isPushKeySetup) {
|
||||
Log.succeed(
|
||||
|
||||
@@ -124,7 +124,7 @@ export class SetUpAscApiKey {
|
||||
type: 'select',
|
||||
name: 'choice',
|
||||
message: 'Select the App Store Connect Api Key to use for your project:',
|
||||
choices,
|
||||
choices: this.choices,
|
||||
});
|
||||
choice = result.choice;
|
||||
}
|
||||
|
||||
+3
-3
@@ -21,7 +21,7 @@ import { validateProvisioningProfileAsync } from '../validators/validateProvisio
|
||||
import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
|
||||
import { assignBuildCredentialsAsync, getBuildCredentialsAsync } from './BuildCredentialsUtils';
|
||||
import { chooseDevicesAsync, formatDeviceLabel } from './DeviceUtils';
|
||||
import { SetUpDistributionCertificate } from './SetUpDistributionCertificate';
|
||||
import { SetupDistributionCertificate } from './SetupDistributionCertificate';
|
||||
|
||||
enum ReuseAction {
|
||||
Yes,
|
||||
@@ -29,11 +29,11 @@ enum ReuseAction {
|
||||
No,
|
||||
}
|
||||
|
||||
export class SetUpAdhocProvisioningProfile {
|
||||
export class SetupAdhocProvisioningProfile {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
|
||||
const distCert = await new SetUpDistributionCertificate(
|
||||
const distCert = await new SetupDistributionCertificate(
|
||||
this.app,
|
||||
IosDistributionType.AdHoc
|
||||
).runAsync(ctx);
|
||||
+3
-3
@@ -7,7 +7,7 @@ import { CredentialsContext } from '../../context';
|
||||
import { IosCapabilitiesOptions } from '../appstore/ensureAppExists';
|
||||
import { App, IosAppBuildCredentialsMap, IosCredentials, Target } from '../types';
|
||||
import { displayProjectCredentials } from '../utils/printCredentials';
|
||||
import { SetUpTargetBuildCredentials } from './SetUpTargetBuildCredentials';
|
||||
import { SetupTargetBuildCredentials } from './SetupTargetBuildCredentials';
|
||||
|
||||
interface Options {
|
||||
app: App;
|
||||
@@ -17,7 +17,7 @@ interface Options {
|
||||
iosCapabilitiesOptions?: IosCapabilitiesOptions;
|
||||
}
|
||||
|
||||
export class SetUpBuildCredentials {
|
||||
export class SetupBuildCredentials {
|
||||
constructor(private options: Options) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<IosCredentials> {
|
||||
@@ -49,7 +49,7 @@ export class SetUpBuildCredentials {
|
||||
} else {
|
||||
Log.newLine();
|
||||
}
|
||||
const action = new SetUpTargetBuildCredentials({
|
||||
const action = new SetupTargetBuildCredentials({
|
||||
...this.options,
|
||||
app: {
|
||||
...this.options.app,
|
||||
+3
-3
@@ -8,9 +8,9 @@ import { readIosCredentialsAsync } from '../../credentialsJson/read';
|
||||
import { IosCredentials } from '../../credentialsJson/types';
|
||||
import { ensureAllTargetsAreConfigured } from '../../credentialsJson/utils';
|
||||
import { App, Target } from '../types';
|
||||
import { SetUpTargetBuildCredentialsFromCredentialsJson } from './SetUpTargetBuildCredentialsFromCredentialsJson';
|
||||
import { SetupTargetBuildCredentialsFromCredentialsJson } from './SetupTargetBuildCredentialsFromCredentialsJson';
|
||||
|
||||
export class SetUpBuildCredentialsFromCredentialsJson {
|
||||
export class SetupBuildCredentialsFromCredentialsJson {
|
||||
constructor(
|
||||
private app: App,
|
||||
private targets: Target[],
|
||||
@@ -32,7 +32,7 @@ export class SetUpBuildCredentialsFromCredentialsJson {
|
||||
);
|
||||
Log.newLine();
|
||||
}
|
||||
await new SetUpTargetBuildCredentialsFromCredentialsJson(
|
||||
await new SetupTargetBuildCredentialsFromCredentialsJson(
|
||||
{
|
||||
account: this.app.account,
|
||||
projectName: this.app.projectName,
|
||||
+1
-1
@@ -18,7 +18,7 @@ import { resolveAppleTeamIfAuthenticatedAsync } from './AppleTeamUtils';
|
||||
import { CreateDistributionCertificate } from './CreateDistributionCertificate';
|
||||
import { formatDistributionCertificate } from './DistributionCertificateUtils';
|
||||
|
||||
export class SetUpDistributionCertificate {
|
||||
export class SetupDistributionCertificate {
|
||||
private validDistCerts?: AppleDistributionCertificateFragment[];
|
||||
|
||||
constructor(private app: AppLookupParams, private distributionType: IosDistributionType) {}
|
||||
+5
-5
@@ -4,8 +4,8 @@ import { promptAsync } from '../../../prompts';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
import { getAllBuildCredentialsAsync } from './BuildCredentialsUtils';
|
||||
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
|
||||
import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
|
||||
import { SetupAdhocProvisioningProfile } from './SetupAdhocProvisioningProfile';
|
||||
import { SetupProvisioningProfile } from './SetupProvisioningProfile';
|
||||
|
||||
/**
|
||||
* It's used when setting up credentials for internal distribution but `enterpriseProvisioning` is not set.
|
||||
@@ -14,7 +14,7 @@ import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
|
||||
* to choose if they want to set up an adhoc or universal distribution provisioning profile. Otherwise, always
|
||||
* set up an adhoc provisioning profile.
|
||||
*/
|
||||
export class SetUpInternalProvisioningProfile {
|
||||
export class SetupInternalProvisioningProfile {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
|
||||
@@ -82,13 +82,13 @@ export class SetUpInternalProvisioningProfile {
|
||||
private async setupAdhocProvisioningProfileAsync(
|
||||
ctx: CredentialsContext
|
||||
): Promise<IosAppBuildCredentialsFragment> {
|
||||
return await new SetUpAdhocProvisioningProfile(this.app).runAsync(ctx);
|
||||
return await new SetupAdhocProvisioningProfile(this.app).runAsync(ctx);
|
||||
}
|
||||
|
||||
private async setupUniversalProvisioningProfileAsync(
|
||||
ctx: CredentialsContext
|
||||
): Promise<IosAppBuildCredentialsFragment> {
|
||||
return await new SetUpProvisioningProfile(this.app, IosDistributionType.Enterprise).runAsync(
|
||||
return await new SetupProvisioningProfile(this.app, IosDistributionType.Enterprise).runAsync(
|
||||
ctx
|
||||
);
|
||||
}
|
||||
+3
-3
@@ -20,12 +20,12 @@ import {
|
||||
import { ConfigureProvisioningProfile } from './ConfigureProvisioningProfile';
|
||||
import { CreateProvisioningProfile } from './CreateProvisioningProfile';
|
||||
import { formatProvisioningProfileFromApple } from './ProvisioningProfileUtils';
|
||||
import { SetUpDistributionCertificate } from './SetUpDistributionCertificate';
|
||||
import { SetupDistributionCertificate } from './SetupDistributionCertificate';
|
||||
|
||||
/**
|
||||
* Sets up either APP_STORE or ENTERPRISE provisioning profiles
|
||||
*/
|
||||
export class SetUpProvisioningProfile {
|
||||
export class SetupProvisioningProfile {
|
||||
constructor(private app: AppLookupParams, private distributionType: IosDistributionType) {}
|
||||
|
||||
async areBuildCredentialsSetupAsync(ctx: CredentialsContext): Promise<boolean> {
|
||||
@@ -84,7 +84,7 @@ export class SetUpProvisioningProfile {
|
||||
}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
|
||||
const distCert = await new SetUpDistributionCertificate(
|
||||
const distCert = await new SetupDistributionCertificate(
|
||||
this.app,
|
||||
this.distributionType
|
||||
).runAsync(ctx);
|
||||
+1
-1
@@ -14,7 +14,7 @@ import {
|
||||
selectPushKeyAsync,
|
||||
} from './PushKeyUtils';
|
||||
|
||||
export class SetUpPushKey {
|
||||
export class SetupPushKey {
|
||||
constructor(private app: AppLookupParams) {}
|
||||
|
||||
async isPushKeySetupAsync(ctx: CredentialsContext): Promise<boolean> {
|
||||
@@ -0,0 +1,61 @@
|
||||
import chalk from 'chalk';
|
||||
import wrapAnsi from 'wrap-ansi';
|
||||
|
||||
import { CommonIosAppCredentialsFragment } from '../../../graphql/generated';
|
||||
import Log, { learnMore } from '../../../log';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { UnsupportedCredentialsChoiceError } from '../../errors';
|
||||
import { AppLookupParams } from '../api/GraphqlClient';
|
||||
import { AppStoreApiKeyPurpose } from './AscApiKeyUtils';
|
||||
import { SetupAscApiKey } from './SetupAscApiKey';
|
||||
|
||||
const PROMPT_FOR_APP_SPECIFIC_PASSWORD = 'PROMPT_FOR_APP_SPECIFIC_PASSWORD';
|
||||
export class SetupSubmissionCredential {
|
||||
private setupAscApiKeyAction;
|
||||
constructor(app: AppLookupParams) {
|
||||
this.setupAscApiKeyAction = new SetupAscApiKey(app, AppStoreApiKeyPurpose.SUBMISSION_SERVICE);
|
||||
this.setupAscApiKeyAction.choices = this.setupAscApiKeyAction.choices.concat({
|
||||
title: '[Enter an App Specific Password]',
|
||||
value: PROMPT_FOR_APP_SPECIFIC_PASSWORD,
|
||||
});
|
||||
}
|
||||
|
||||
public async runAsync(
|
||||
ctx: CredentialsContext
|
||||
): Promise<CommonIosAppCredentialsFragment | string> {
|
||||
try {
|
||||
return await this.setupAscApiKeyAction.runAsync(ctx);
|
||||
} catch (e) {
|
||||
if (e instanceof UnsupportedCredentialsChoiceError) {
|
||||
const choice = e.choice;
|
||||
if (choice === PROMPT_FOR_APP_SPECIFIC_PASSWORD) {
|
||||
return await this.promptForAppSpecificPasswordAsync();
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async promptForAppSpecificPasswordAsync(): Promise<string> {
|
||||
Log.addNewLineIfNone();
|
||||
Log.warn(
|
||||
wrapAnsi(
|
||||
`This option will be deprecated soon. You will still be able to provide your password with the ${chalk.bold(
|
||||
'EXPO_APPLE_APP_SPECIFIC_PASSWORD'
|
||||
)} environment variable.`,
|
||||
process.stdout.columns || 80
|
||||
)
|
||||
);
|
||||
Log.log(`Please enter your Apple app-specific password.`);
|
||||
Log.log(learnMore('https://expo.fyi/apple-app-specific-password'));
|
||||
|
||||
const { appSpecificPassword } = await promptAsync({
|
||||
name: 'appSpecificPassword',
|
||||
message: 'Your Apple app-specific password:',
|
||||
type: 'password',
|
||||
validate: (val: string) => val !== '' || 'Apple app-specific password cannot be empty!',
|
||||
});
|
||||
return appSpecificPassword;
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -8,9 +8,9 @@ import Log from '../../../log';
|
||||
import { CredentialsContext } from '../../context';
|
||||
import { AppLookupParams as GraphQLAppLookupParams } from '../api/GraphqlClient';
|
||||
import { IosCapabilitiesOptions } from '../appstore/ensureAppExists';
|
||||
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
|
||||
import { SetUpInternalProvisioningProfile } from './SetUpInternalProvisioningProfile';
|
||||
import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
|
||||
import { SetupAdhocProvisioningProfile } from './SetupAdhocProvisioningProfile';
|
||||
import { SetupInternalProvisioningProfile } from './SetupInternalProvisioningProfile';
|
||||
import { SetupProvisioningProfile } from './SetupProvisioningProfile';
|
||||
|
||||
interface Options {
|
||||
app: GraphQLAppLookupParams;
|
||||
@@ -18,7 +18,7 @@ interface Options {
|
||||
enterpriseProvisioning?: IosEnterpriseProvisioning;
|
||||
iosCapabilitiesOptions?: IosCapabilitiesOptions;
|
||||
}
|
||||
export class SetUpTargetBuildCredentials {
|
||||
export class SetupTargetBuildCredentials {
|
||||
constructor(private options: Options) {}
|
||||
|
||||
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
|
||||
@@ -50,17 +50,17 @@ export class SetUpTargetBuildCredentials {
|
||||
const { app, distribution, enterpriseProvisioning } = this.options;
|
||||
if (distribution === 'internal') {
|
||||
if (enterpriseProvisioning === 'adhoc') {
|
||||
return await new SetUpAdhocProvisioningProfile(app).runAsync(ctx);
|
||||
return await new SetupAdhocProvisioningProfile(app).runAsync(ctx);
|
||||
} else if (enterpriseProvisioning === 'universal') {
|
||||
return await new SetUpProvisioningProfile(
|
||||
return await new SetupProvisioningProfile(
|
||||
app,
|
||||
GraphQLIosDistributionType.Enterprise
|
||||
).runAsync(ctx);
|
||||
} else {
|
||||
return await new SetUpInternalProvisioningProfile(app).runAsync(ctx);
|
||||
return await new SetupInternalProvisioningProfile(app).runAsync(ctx);
|
||||
}
|
||||
} else {
|
||||
return await new SetUpProvisioningProfile(app, GraphQLIosDistributionType.AppStore).runAsync(
|
||||
return await new SetupProvisioningProfile(app, GraphQLIosDistributionType.AppStore).runAsync(
|
||||
ctx
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -22,7 +22,7 @@ import {
|
||||
getProvisioningProfileAsync,
|
||||
} from './BuildCredentialsUtils';
|
||||
|
||||
export class SetUpTargetBuildCredentialsFromCredentialsJson {
|
||||
export class SetupTargetBuildCredentialsFromCredentialsJson {
|
||||
constructor(
|
||||
private app: AppLookupParams,
|
||||
private distributionType: IosDistributionType,
|
||||
+1
-1
@@ -5,7 +5,7 @@ import {
|
||||
import { testDistCertFragmentOneDependency } from '../../../__tests__/fixtures-ios';
|
||||
import { CredentialsContext } from '../../../context';
|
||||
import { AppLookupParams } from '../../api/GraphqlClient';
|
||||
export class SetUpDistributionCertificate {
|
||||
export class SetupDistributionCertificate {
|
||||
constructor(private app: AppLookupParams, private distributionType: IosDistributionType) {}
|
||||
|
||||
public async runAsync(_ctx: CredentialsContext): Promise<AppleDistributionCertificateFragment> {
|
||||
@@ -115,23 +115,6 @@ describe(SetUpAscApiKey, () => {
|
||||
// expect configuration to be updated with a new ASC Api Key
|
||||
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
|
||||
});
|
||||
it('works in Non Interactive Mode if ASC Key is configured', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: true,
|
||||
ios: {
|
||||
...getNewIosApiMock(),
|
||||
getIosAppCredentialsWithCommonFieldsAsync: jest.fn(
|
||||
() => testCommonIosAppCredentialsFragment
|
||||
),
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupAscApiKeyAction = new SetUpAscApiKey(
|
||||
appLookupParams,
|
||||
AppStoreApiKeyPurpose.SUBMISSION_SERVICE
|
||||
);
|
||||
await expect(setupAscApiKeyAction.runAsync(ctx)).resolves.not.toThrowError();
|
||||
});
|
||||
it('errors in Non Interactive Mode', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: true,
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { doUDIDsMatch } from '../SetUpAdhocProvisioningProfile';
|
||||
import { doUDIDsMatch } from '../SetupAdhocProvisioningProfile';
|
||||
|
||||
describe(doUDIDsMatch, () => {
|
||||
it('return false if UDIDs do not match', () => {
|
||||
+28
-28
@@ -4,13 +4,13 @@ import { promptAsync } from '../../../../prompts';
|
||||
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
|
||||
import { createCtxMock } from '../../../__tests__/fixtures-context';
|
||||
import { getAllBuildCredentialsAsync } from '../BuildCredentialsUtils';
|
||||
import { SetUpAdhocProvisioningProfile } from '../SetUpAdhocProvisioningProfile';
|
||||
import { SetUpInternalProvisioningProfile } from '../SetUpInternalProvisioningProfile';
|
||||
import { SetUpProvisioningProfile } from '../SetUpProvisioningProfile';
|
||||
import { SetupAdhocProvisioningProfile } from '../SetupAdhocProvisioningProfile';
|
||||
import { SetupInternalProvisioningProfile } from '../SetupInternalProvisioningProfile';
|
||||
import { SetupProvisioningProfile } from '../SetupProvisioningProfile';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
jest.mock('../SetUpAdhocProvisioningProfile');
|
||||
jest.mock('../SetUpProvisioningProfile');
|
||||
jest.mock('../SetupAdhocProvisioningProfile');
|
||||
jest.mock('../SetupProvisioningProfile');
|
||||
jest.mock('../BuildCredentialsUtils', () => ({ getAllBuildCredentialsAsync: jest.fn() }));
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -33,15 +33,15 @@ const testEnterpriseBuildCredentials: IosAppBuildCredentialsFragment = {
|
||||
iosDistributionType: IosDistributionType.Enterprise,
|
||||
};
|
||||
|
||||
describe(SetUpInternalProvisioningProfile, () => {
|
||||
describe(SetupInternalProvisioningProfile, () => {
|
||||
describe('interactive mode', () => {
|
||||
describe('when authenticated with apple', () => {
|
||||
it('runs the SetUpAdhocProvisioningProfile action for non-enterprise team', async () => {
|
||||
it('runs the SetupAdhocProvisioningProfile action for non-enterprise team', async () => {
|
||||
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
|
||||
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
|
||||
return buildCredentials;
|
||||
});
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -56,7 +56,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpAdhocProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupAdhocProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
@@ -72,7 +72,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
return buildCredentials;
|
||||
});
|
||||
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -90,7 +90,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
@@ -110,7 +110,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
return buildCredentials;
|
||||
});
|
||||
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -125,14 +125,14 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
expect(runAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('runs the SetUpAdhocProvisioningProfile action when adhoc credentials exist', async () => {
|
||||
it('runs the SetupAdhocProvisioningProfile action when adhoc credentials exist', async () => {
|
||||
asMock(promptAsync).mockImplementationOnce(() => ({
|
||||
distributionType: IosDistributionType.Enterprise,
|
||||
}));
|
||||
@@ -141,7 +141,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
return buildCredentials;
|
||||
});
|
||||
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -156,14 +156,14 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpAdhocProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupAdhocProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
expect(runAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('runs the SetUpProvisioningProfile action when enterprise credentials exist', async () => {
|
||||
it('runs the SetupProvisioningProfile action when enterprise credentials exist', async () => {
|
||||
asMock(promptAsync).mockImplementationOnce(() => ({
|
||||
distributionType: IosDistributionType.Enterprise,
|
||||
}));
|
||||
@@ -174,7 +174,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
return buildCredentials;
|
||||
});
|
||||
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -189,7 +189,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
@@ -205,7 +205,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
return buildCredentials;
|
||||
});
|
||||
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -235,7 +235,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
];
|
||||
return buildCredentials;
|
||||
});
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -253,7 +253,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
|
||||
return buildCredentials;
|
||||
});
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -264,12 +264,12 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
await expect(action.runAsync(ctx)).rejects.toThrow(/couldn't find any credentials/);
|
||||
});
|
||||
|
||||
it('runs the SetUpAdhocProvisioningProfile action when adhoc credentials exist', async () => {
|
||||
it('runs the SetupAdhocProvisioningProfile action when adhoc credentials exist', async () => {
|
||||
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
|
||||
const buildCredentials: IosAppBuildCredentialsFragment[] = [testAdhocBuildCredentials];
|
||||
return buildCredentials;
|
||||
});
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -279,19 +279,19 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpAdhocProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupAdhocProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
expect(runAsync).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('runs the SetUpProvisioningProfile action when enterprise credentials exist', async () => {
|
||||
it('runs the SetupProvisioningProfile action when enterprise credentials exist', async () => {
|
||||
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
|
||||
const buildCredentials: IosAppBuildCredentialsFragment[] = [testEnterpriseBuildCredentials];
|
||||
return buildCredentials;
|
||||
});
|
||||
const action = new SetUpInternalProvisioningProfile({
|
||||
const action = new SetupInternalProvisioningProfile({
|
||||
account: { id: 'account-id', name: 'account-name' },
|
||||
bundleIdentifier: 'com.expo.test',
|
||||
projectName: 'testproject',
|
||||
@@ -301,7 +301,7 @@ describe(SetUpInternalProvisioningProfile, () => {
|
||||
});
|
||||
|
||||
const runAsync = jest.fn();
|
||||
SetUpProvisioningProfile.prototype.runAsync = runAsync;
|
||||
SetupProvisioningProfile.prototype.runAsync = runAsync;
|
||||
|
||||
await action.runAsync(ctx);
|
||||
|
||||
+8
-8
@@ -16,16 +16,16 @@ import {
|
||||
import { MissingCredentialsNonInteractiveError } from '../../../errors';
|
||||
import { validateProvisioningProfileAsync } from '../../validators/validateProvisioningProfile';
|
||||
import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
|
||||
import { SetUpProvisioningProfile } from '../SetUpProvisioningProfile';
|
||||
import { SetupProvisioningProfile } from '../SetupProvisioningProfile';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
asMock(confirmAsync).mockImplementation(() => true);
|
||||
jest.mock('../SetUpDistributionCertificate');
|
||||
jest.mock('../SetupDistributionCertificate');
|
||||
jest.mock('../ConfigureProvisioningProfile');
|
||||
jest.mock('../CreateProvisioningProfile');
|
||||
jest.mock('../../validators/validateProvisioningProfile');
|
||||
|
||||
describe('SetUpProvisioningProfile', () => {
|
||||
describe('SetupProvisioningProfile', () => {
|
||||
it('repairs existing Provisioning Profile with bad build credentials in Interactive Mode', async () => {
|
||||
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
|
||||
const ctx = createCtxMock({
|
||||
@@ -54,7 +54,7 @@ describe('SetUpProvisioningProfile', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
|
||||
const setupProvisioningProfileAction = new SetupProvisioningProfile(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore
|
||||
);
|
||||
@@ -84,7 +84,7 @@ describe('SetUpProvisioningProfile', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
|
||||
const setupProvisioningProfileAction = new SetupProvisioningProfile(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore
|
||||
);
|
||||
@@ -114,7 +114,7 @@ describe('SetUpProvisioningProfile', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
|
||||
const setupProvisioningProfileAction = new SetupProvisioningProfile(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore
|
||||
);
|
||||
@@ -140,7 +140,7 @@ describe('SetUpProvisioningProfile', () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
|
||||
const setupProvisioningProfileAction = new SetupProvisioningProfile(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore
|
||||
);
|
||||
@@ -157,7 +157,7 @@ describe('SetUpProvisioningProfile', () => {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupProvisioningProfileAction = new SetUpProvisioningProfile(
|
||||
const setupProvisioningProfileAction = new SetupProvisioningProfile(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore
|
||||
);
|
||||
+6
-6
@@ -5,12 +5,12 @@ import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appsto
|
||||
import { createCtxMock } from '../../../__tests__/fixtures-context';
|
||||
import { getNewIosApiMock, testPushKey, testTargets } from '../../../__tests__/fixtures-ios';
|
||||
import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
|
||||
import { SetUpPushKey } from '../SetUpPushKey';
|
||||
import { SetupPushKey } from '../SetupPushKey';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
asMock(confirmAsync).mockImplementation(() => true);
|
||||
|
||||
describe(SetUpPushKey, () => {
|
||||
describe(SetupPushKey, () => {
|
||||
it('skips setting up a Push Key if it is already configured', async () => {
|
||||
const ctx = createCtxMock({
|
||||
nonInteractive: false,
|
||||
@@ -20,7 +20,7 @@ describe(SetUpPushKey, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupPushKeyAction = new SetUpPushKey(appLookupParams);
|
||||
const setupPushKeyAction = new SetupPushKey(appLookupParams);
|
||||
await setupPushKeyAction.runAsync(ctx);
|
||||
|
||||
// expect not to create a new push key on expo servers
|
||||
@@ -38,7 +38,7 @@ describe(SetUpPushKey, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupPushKeyAction = new SetUpPushKey(appLookupParams);
|
||||
const setupPushKeyAction = new SetupPushKey(appLookupParams);
|
||||
await setupPushKeyAction.runAsync(ctx);
|
||||
|
||||
// expect to create a new push key on expo servers
|
||||
@@ -62,7 +62,7 @@ describe(SetUpPushKey, () => {
|
||||
},
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupPushKeyAction = new SetUpPushKey(appLookupParams);
|
||||
const setupPushKeyAction = new SetupPushKey(appLookupParams);
|
||||
await setupPushKeyAction.runAsync(ctx);
|
||||
|
||||
// expect not to create a new push key on expo servers, we are using the existing one
|
||||
@@ -75,7 +75,7 @@ describe(SetUpPushKey, () => {
|
||||
nonInteractive: true,
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const createPushKeyAction = new SetUpPushKey(appLookupParams);
|
||||
const createPushKeyAction = new SetupPushKey(appLookupParams);
|
||||
await expect(createPushKeyAction.runAsync(ctx)).rejects.toThrowError();
|
||||
});
|
||||
});
|
||||
+6
-6
@@ -15,7 +15,7 @@ import {
|
||||
} from '../../../__tests__/fixtures-ios';
|
||||
import { IosTargetCredentials } from '../../../credentialsJson/types';
|
||||
import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
|
||||
import { SetUpTargetBuildCredentialsFromCredentialsJson } from '../SetUpTargetBuildCredentialsFromCredentialsJson';
|
||||
import { SetupTargetBuildCredentialsFromCredentialsJson } from '../SetupTargetBuildCredentialsFromCredentialsJson';
|
||||
|
||||
jest.mock('../../../../prompts');
|
||||
jest.mock('../../../credentialsJson/read');
|
||||
@@ -25,7 +25,7 @@ beforeEach(() => {
|
||||
asMock(confirmAsync).mockImplementation(() => true);
|
||||
});
|
||||
|
||||
describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
|
||||
describe('SetupTargetBuildCredentialsFromCredentialsJson', () => {
|
||||
const targetCredentials: IosTargetCredentials = {
|
||||
distributionCertificate: {
|
||||
certificateP12: testDistCert.certP12,
|
||||
@@ -53,7 +53,7 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupBuildCredentialsFromCredentialsJsonAction =
|
||||
new SetUpTargetBuildCredentialsFromCredentialsJson(
|
||||
new SetupTargetBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore,
|
||||
targetCredentials
|
||||
@@ -82,7 +82,7 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupBuildCredentialsFromCredentialsJsonAction =
|
||||
new SetUpTargetBuildCredentialsFromCredentialsJson(
|
||||
new SetupTargetBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore,
|
||||
targetCredentials
|
||||
@@ -115,7 +115,7 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupBuildCredentialsFromCredentialsJsonAction =
|
||||
new SetUpTargetBuildCredentialsFromCredentialsJson(
|
||||
new SetupTargetBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore,
|
||||
targetCredentials
|
||||
@@ -149,7 +149,7 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
|
||||
});
|
||||
const appLookupParams = getAppLookupParamsFromContext(ctx, findApplicationTarget(testTargets));
|
||||
const setupBuildCredentialsFromCredentialsJsonAction =
|
||||
new SetUpTargetBuildCredentialsFromCredentialsJson(
|
||||
new SetupTargetBuildCredentialsFromCredentialsJson(
|
||||
appLookupParams,
|
||||
IosDistributionType.AppStore,
|
||||
targetCredentials
|
||||
@@ -25,9 +25,9 @@ export enum AndroidActionType {
|
||||
CreateGsaKey,
|
||||
UseExistingGsaKey,
|
||||
RemoveGsaKey,
|
||||
SetUpGsaKey,
|
||||
SetupGsaKey,
|
||||
UpdateCredentialsJson,
|
||||
SetUpBuildCredentialsFromCredentialsJson,
|
||||
SetupBuildCredentialsFromCredentialsJson,
|
||||
}
|
||||
|
||||
export enum IosActionType {
|
||||
@@ -37,14 +37,14 @@ export enum IosActionType {
|
||||
ManageAscApiKey,
|
||||
GoBackToCaller,
|
||||
GoBackToHighLevelActions,
|
||||
SetUpBuildCredentials,
|
||||
SetUpBuildCredentialsFromCredentialsJson,
|
||||
SetupBuildCredentials,
|
||||
SetupBuildCredentialsFromCredentialsJson,
|
||||
UpdateCredentialsJson,
|
||||
UseExistingDistributionCertificate,
|
||||
RemoveProvisioningProfile,
|
||||
CreateDistributionCertificate,
|
||||
RemoveDistributionCertificate,
|
||||
SetUpPushKey,
|
||||
SetupPushKey,
|
||||
CreatePushKey,
|
||||
UseExistingPushKey,
|
||||
RemovePushKey,
|
||||
|
||||
@@ -35,7 +35,7 @@ export const credentialsJsonActions: ActionInfo[] = [
|
||||
scope: Scope.Project,
|
||||
},
|
||||
{
|
||||
value: AndroidActionType.SetUpBuildCredentialsFromCredentialsJson,
|
||||
value: AndroidActionType.SetupBuildCredentialsFromCredentialsJson,
|
||||
title: 'Upload credentials from credentials.json to EAS',
|
||||
scope: Scope.Project,
|
||||
},
|
||||
@@ -89,8 +89,8 @@ export const fcmActions: ActionInfo[] = [
|
||||
|
||||
export const gsaKeyActions: ActionInfo[] = [
|
||||
{
|
||||
value: AndroidActionType.SetUpGsaKey,
|
||||
title: 'Set up a Google Service Account Key',
|
||||
value: AndroidActionType.SetupGsaKey,
|
||||
title: 'Setup a Google Service Account Key',
|
||||
scope: Scope.Project,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ export const credentialsJsonActions: ActionInfo[] = [
|
||||
scope: Scope.Project,
|
||||
},
|
||||
{
|
||||
value: IosActionType.SetUpBuildCredentialsFromCredentialsJson,
|
||||
value: IosActionType.SetupBuildCredentialsFromCredentialsJson,
|
||||
title: 'Upload credentials from credentials.json to EAS',
|
||||
scope: Scope.Project,
|
||||
},
|
||||
@@ -50,8 +50,8 @@ export const credentialsJsonActions: ActionInfo[] = [
|
||||
export function getPushKeyActions(ctx: CredentialsContext): ActionInfo[] {
|
||||
return [
|
||||
{
|
||||
value: IosActionType.SetUpPushKey,
|
||||
title: 'Set up your project to use Push Notifications',
|
||||
value: IosActionType.SetupPushKey,
|
||||
title: 'Setup your project to use Push Notifications',
|
||||
scope: Scope.Project,
|
||||
},
|
||||
{
|
||||
@@ -112,7 +112,7 @@ export function getBuildCredentialsActions(ctx: CredentialsContext): ActionInfo[
|
||||
{
|
||||
// This command will be triggered during build to ensure all credentials are ready
|
||||
// I'm leaving it here for now to simplify testing
|
||||
value: IosActionType.SetUpBuildCredentials,
|
||||
value: IosActionType.SetupBuildCredentials,
|
||||
title: 'All: Set up all the required credentials to build your project',
|
||||
scope: Scope.Project,
|
||||
},
|
||||
|
||||
@@ -25,8 +25,8 @@ import { DownloadKeystore } from '../android/actions/DownloadKeystore';
|
||||
import { RemoveFcm } from '../android/actions/RemoveFcm';
|
||||
import { SelectAndRemoveGoogleServiceAccountKey } from '../android/actions/RemoveGoogleServiceAccountKey';
|
||||
import { RemoveKeystore } from '../android/actions/RemoveKeystore';
|
||||
import { SetUpBuildCredentialsFromCredentialsJson } from '../android/actions/SetUpBuildCredentialsFromCredentialsJson';
|
||||
import { SetUpGoogleServiceAccountKey } from '../android/actions/SetUpGoogleServiceAccountKey';
|
||||
import { SetupBuildCredentialsFromCredentialsJson } from '../android/actions/SetupBuildCredentialsFromCredentialsJson';
|
||||
import { SetupGoogleServiceAccountKey } from '../android/actions/SetupGoogleServiceAccountKey';
|
||||
import { UpdateCredentialsJson } from '../android/actions/UpdateCredentialsJson';
|
||||
import { UseExistingGoogleServiceAccountKey } from '../android/actions/UseExistingGoogleServiceAccountKey';
|
||||
import {
|
||||
@@ -228,8 +228,8 @@ export class ManageAndroid {
|
||||
}
|
||||
} else if (action === AndroidActionType.RemoveGsaKey) {
|
||||
await new SelectAndRemoveGoogleServiceAccountKey(appLookupParams.account).runAsync(ctx);
|
||||
} else if (action === AndroidActionType.SetUpGsaKey) {
|
||||
await new SetUpGoogleServiceAccountKey(appLookupParams).runAsync(ctx);
|
||||
} else if (action === AndroidActionType.SetupGsaKey) {
|
||||
await new SetupGoogleServiceAccountKey(appLookupParams).runAsync(ctx);
|
||||
} else if (action === AndroidActionType.UpdateCredentialsJson) {
|
||||
const buildCredentials = await new SelectExistingAndroidBuildCredentials(
|
||||
appLookupParams
|
||||
@@ -237,8 +237,8 @@ export class ManageAndroid {
|
||||
if (buildCredentials) {
|
||||
await new UpdateCredentialsJson().runAsync(ctx, buildCredentials);
|
||||
}
|
||||
} else if (action === AndroidActionType.SetUpBuildCredentialsFromCredentialsJson) {
|
||||
await new SetUpBuildCredentialsFromCredentialsJson(appLookupParams).runAsync(ctx);
|
||||
} else if (action === AndroidActionType.SetupBuildCredentialsFromCredentialsJson) {
|
||||
await new SetupBuildCredentialsFromCredentialsJson(appLookupParams).runAsync(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,12 @@ import { SelectAndRemoveAscApiKey } from '../ios/actions/RemoveAscApiKey';
|
||||
import { SelectAndRemoveDistributionCertificate } from '../ios/actions/RemoveDistributionCertificate';
|
||||
import { RemoveProvisioningProfiles } from '../ios/actions/RemoveProvisioningProfile';
|
||||
import { SelectAndRemovePushKey } from '../ios/actions/RemovePushKey';
|
||||
import { SetUpAdhocProvisioningProfile } from '../ios/actions/SetUpAdhocProvisioningProfile';
|
||||
import { SetUpAscApiKey } from '../ios/actions/SetUpAscApiKey';
|
||||
import { SetUpBuildCredentials } from '../ios/actions/SetUpBuildCredentials';
|
||||
import { SetUpBuildCredentialsFromCredentialsJson } from '../ios/actions/SetUpBuildCredentialsFromCredentialsJson';
|
||||
import { SetUpProvisioningProfile } from '../ios/actions/SetUpProvisioningProfile';
|
||||
import { SetUpPushKey } from '../ios/actions/SetUpPushKey';
|
||||
import { SetupAdhocProvisioningProfile } from '../ios/actions/SetupAdhocProvisioningProfile';
|
||||
import { SetupBuildCredentials } from '../ios/actions/SetupBuildCredentials';
|
||||
import { SetupBuildCredentialsFromCredentialsJson } from '../ios/actions/SetupBuildCredentialsFromCredentialsJson';
|
||||
import { SetupProvisioningProfile } from '../ios/actions/SetupProvisioningProfile';
|
||||
import { SetupPushKey } from '../ios/actions/SetupPushKey';
|
||||
import { UpdateCredentialsJson } from '../ios/actions/UpdateCredentialsJson';
|
||||
import { AppLookupParams } from '../ios/api/GraphqlClient';
|
||||
import { getManagedEntitlementsJsonAsync } from '../ios/appstore/entitlements';
|
||||
@@ -222,8 +222,8 @@ export class ManageIos {
|
||||
buildProfile: IosBuildProfile,
|
||||
action: IosActionType
|
||||
): Promise<void> {
|
||||
if (action === IosActionType.SetUpBuildCredentials) {
|
||||
await new SetUpBuildCredentials({
|
||||
if (action === IosActionType.SetupBuildCredentials) {
|
||||
await new SetupBuildCredentials({
|
||||
app,
|
||||
targets,
|
||||
distribution: buildProfile.distribution,
|
||||
@@ -239,8 +239,8 @@ export class ManageIos {
|
||||
buildProfile
|
||||
).runAsync(ctx);
|
||||
|
||||
if (action === IosActionType.SetUpBuildCredentialsFromCredentialsJson) {
|
||||
await new SetUpBuildCredentialsFromCredentialsJson(app, targets, distributionType).runAsync(
|
||||
if (action === IosActionType.SetupBuildCredentialsFromCredentialsJson) {
|
||||
await new SetupBuildCredentialsFromCredentialsJson(app, targets, distributionType).runAsync(
|
||||
ctx
|
||||
);
|
||||
return;
|
||||
@@ -305,15 +305,15 @@ export class ManageIos {
|
||||
}
|
||||
return;
|
||||
}
|
||||
case IosActionType.SetUpPushKey: {
|
||||
const setupPushKeyAction = await new SetUpPushKey(appLookupParams);
|
||||
case IosActionType.SetupPushKey: {
|
||||
const setupPushKeyAction = await new SetupPushKey(appLookupParams);
|
||||
const isPushKeySetup = await setupPushKeyAction.isPushKeySetupAsync(ctx);
|
||||
if (isPushKeySetup) {
|
||||
Log.log(
|
||||
`Push Key is already set up for ${appLookupParams.projectName} ${appLookupParams.bundleIdentifier}`
|
||||
);
|
||||
} else {
|
||||
await new SetUpPushKey(appLookupParams).runAsync(ctx);
|
||||
await new SetupPushKey(appLookupParams).runAsync(ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -385,11 +385,11 @@ export class ManageIos {
|
||||
Log.log(`Setting up ${appLookupParams.projectName} to use Distribution Certificate`);
|
||||
Log.log(`Creating provisioning profile...`);
|
||||
if (distributionType === IosDistributionTypeGraphql.AdHoc) {
|
||||
return await new SetUpAdhocProvisioningProfile(
|
||||
return await new SetupAdhocProvisioningProfile(
|
||||
appLookupParams
|
||||
).runWithDistributionCertificateAsync(ctx, distCert);
|
||||
} else {
|
||||
return await new SetUpProvisioningProfile(
|
||||
return await new SetupProvisioningProfile(
|
||||
appLookupParams,
|
||||
distributionType
|
||||
).createAndAssignProfileAsync(ctx, distCert);
|
||||
|
||||
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import { SetUpGoogleServiceAccountKey } from '../../credentials/android/actions/SetUpGoogleServiceAccountKey';
|
||||
import { SetupGoogleServiceAccountKey } from '../../credentials/android/actions/SetupGoogleServiceAccountKey';
|
||||
import { readAndValidateServiceAccountKey } from '../../credentials/android/utils/googleServiceAccountKey';
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { promptAsync } from '../../prompts';
|
||||
@@ -109,7 +109,7 @@ export async function getServiceAccountFromCredentialsServiceAsync(
|
||||
projectName: ctx.projectName,
|
||||
androidApplicationIdentifier,
|
||||
};
|
||||
const setupGoogleServiceAccountKeyAction = new SetUpGoogleServiceAccountKey(appLookupParams);
|
||||
const setupGoogleServiceAccountKeyAction = new SetupGoogleServiceAccountKey(appLookupParams);
|
||||
const androidAppCredentials = await setupGoogleServiceAccountKeyAction.runAsync(
|
||||
ctx.credentialsCtx
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { asMock } from '../../../__tests__/utils';
|
||||
import { testAndroidAppCredentialsFragment } from '../../../credentials/__tests__/fixtures-android';
|
||||
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { SetUpGoogleServiceAccountKey } from '../../../credentials/android/actions/SetUpGoogleServiceAccountKey';
|
||||
import { SetupGoogleServiceAccountKey } from '../../../credentials/android/actions/SetupGoogleServiceAccountKey';
|
||||
import { createTestProject } from '../../../project/__tests__/project-utils';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { createSubmissionContextAsync } from '../../context';
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
jest.mock('fs');
|
||||
jest.mock('../../../prompts');
|
||||
jest.mock('../../../project/projectUtils');
|
||||
jest.mock('../../../credentials/android/actions/SetUpGoogleServiceAccountKey');
|
||||
jest.mock('../../../credentials/android/actions/SetupGoogleServiceAccountKey');
|
||||
jest.mock('../../../user/User', () => ({
|
||||
getUserAsync: jest.fn(() => mockJester),
|
||||
}));
|
||||
@@ -52,7 +52,7 @@ beforeAll(() => {
|
||||
'/other_dir/invalid_file.txt': 'this is not even a JSON',
|
||||
});
|
||||
jest
|
||||
.spyOn(SetUpGoogleServiceAccountKey.prototype, 'runAsync')
|
||||
.spyOn(SetupGoogleServiceAccountKey.prototype, 'runAsync')
|
||||
.mockImplementation(async () => testAndroidAppCredentialsFragment);
|
||||
});
|
||||
afterAll(() => {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import chalk from 'chalk';
|
||||
import wrapAnsi from 'wrap-ansi';
|
||||
|
||||
import Log, { learnMore } from '../../log';
|
||||
import { promptAsync } from '../../prompts';
|
||||
|
||||
export enum AppSpecificPasswordSourceType {
|
||||
userDefined,
|
||||
prompt,
|
||||
}
|
||||
|
||||
interface AppSpecificPasswordSourceBase {
|
||||
@@ -11,13 +18,38 @@ interface AppSpecificPasswordUserDefinedSource extends AppSpecificPasswordSource
|
||||
appSpecificPassword: string;
|
||||
}
|
||||
|
||||
export type AppSpecificPasswordSource = AppSpecificPasswordUserDefinedSource;
|
||||
interface AppSpecificPasswordPromptSource extends AppSpecificPasswordSourceBase {
|
||||
sourceType: AppSpecificPasswordSourceType.prompt;
|
||||
}
|
||||
|
||||
export type AppSpecificPasswordSource =
|
||||
| AppSpecificPasswordUserDefinedSource
|
||||
| AppSpecificPasswordPromptSource;
|
||||
|
||||
export async function getAppSpecificPasswordAsync(
|
||||
source: AppSpecificPasswordSource
|
||||
): Promise<string> {
|
||||
if (source.sourceType === AppSpecificPasswordSourceType.userDefined) {
|
||||
return source.appSpecificPassword;
|
||||
} else if (source.sourceType === AppSpecificPasswordSourceType.prompt) {
|
||||
Log.addNewLineIfNone();
|
||||
Log.log(
|
||||
wrapAnsi(
|
||||
`Please enter your Apple app-specific password. You can also provide it by using the ${chalk.bold(
|
||||
'EXPO_APPLE_APP_SPECIFIC_PASSWORD'
|
||||
)} environment variable.`,
|
||||
process.stdout.columns || 80
|
||||
)
|
||||
);
|
||||
Log.log(learnMore('https://expo.fyi/apple-app-specific-password'));
|
||||
|
||||
const { appSpecificPassword } = await promptAsync({
|
||||
name: 'appSpecificPassword',
|
||||
message: 'Your Apple app-specific password:',
|
||||
type: 'password',
|
||||
validate: (val: string) => val !== '' || 'Apple app-specific password cannot be empty!',
|
||||
});
|
||||
return appSpecificPassword;
|
||||
} else {
|
||||
// exhaustive -- should never happen
|
||||
throw new Error(`Unknown app specific password source type "${(source as any)?.sourceType}"`);
|
||||
|
||||
@@ -1,33 +1,21 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import fs from 'fs-extra';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
import {
|
||||
AppStoreApiKeyPurpose,
|
||||
promptForAscApiKeyPathAsync,
|
||||
} from '../../credentials/ios/actions/AscApiKeyUtils';
|
||||
import { SetUpAscApiKey } from '../../credentials/ios/actions/SetUpAscApiKey';
|
||||
import { promptForAscApiKeyPathAsync } from '../../credentials/ios/actions/AscApiKeyUtils';
|
||||
import { AscApiKeyPath, MinimalAscApiKey } from '../../credentials/ios/credentials';
|
||||
import Log from '../../log';
|
||||
import { getBundleIdentifierAsync } from '../../project/ios/bundleIdentifier';
|
||||
import { findAccountByName } from '../../user/Account';
|
||||
import { SubmissionContext } from '../context';
|
||||
import { isExistingFileAsync } from '../utils/files';
|
||||
|
||||
export enum AscApiKeySourceType {
|
||||
path,
|
||||
prompt,
|
||||
credentialsService,
|
||||
}
|
||||
|
||||
interface AscApiKeySourceBase {
|
||||
sourceType: AscApiKeySourceType;
|
||||
}
|
||||
|
||||
interface AscApiKeyCredentialsServiceSource extends AscApiKeySourceBase {
|
||||
sourceType: AscApiKeySourceType.credentialsService;
|
||||
}
|
||||
|
||||
interface AscApiKeyPromptSource extends AscApiKeySourceBase {
|
||||
sourceType: AscApiKeySourceType.prompt;
|
||||
}
|
||||
@@ -37,76 +25,20 @@ interface AscApiKeyEnvVarSource extends AscApiKeySourceBase {
|
||||
path: AscApiKeyPath;
|
||||
}
|
||||
|
||||
export type AscApiKeySource =
|
||||
| AscApiKeyEnvVarSource
|
||||
| AscApiKeyPromptSource
|
||||
| AscApiKeyCredentialsServiceSource;
|
||||
export type AscApiKeySource = AscApiKeyEnvVarSource | AscApiKeyPromptSource;
|
||||
|
||||
type AscApiKeySummary = {
|
||||
source: 'local' | 'EAS servers';
|
||||
path?: string;
|
||||
keyId: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export type AscApiKeyFromExpoServers = {
|
||||
ascApiKeyId: string;
|
||||
};
|
||||
|
||||
export type AscApiKeyResult = {
|
||||
result: MinimalAscApiKey | AscApiKeyFromExpoServers;
|
||||
result: MinimalAscApiKey;
|
||||
summary: AscApiKeySummary;
|
||||
};
|
||||
|
||||
export async function getAscApiKeyResultAsync(
|
||||
ctx: SubmissionContext<Platform.IOS>,
|
||||
source: AscApiKeySource
|
||||
): Promise<AscApiKeyResult> {
|
||||
if (source.sourceType === AscApiKeySourceType.credentialsService) {
|
||||
return await getAscApiKeyFromCredentialsServiceAsync(ctx);
|
||||
} else {
|
||||
return await getAscApiKeyLocallyAsync(ctx, source);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAscApiKeyFromCredentialsServiceAsync(
|
||||
ctx: SubmissionContext<Platform.IOS>
|
||||
): Promise<AscApiKeyResult> {
|
||||
const bundleIdentifier = await getBundleIdentifierAsync(ctx.projectDir, ctx.exp);
|
||||
Log.log(`Looking up credentials configuration for ${bundleIdentifier}...`);
|
||||
|
||||
const appLookupParams = {
|
||||
account: nullthrows(
|
||||
findAccountByName(ctx.user.accounts, ctx.accountName),
|
||||
`You do not have access to account: ${ctx.accountName}`
|
||||
),
|
||||
projectName: ctx.projectName,
|
||||
bundleIdentifier,
|
||||
};
|
||||
const setupAscApiKeyAction = new SetUpAscApiKey(
|
||||
appLookupParams,
|
||||
AppStoreApiKeyPurpose.SUBMISSION_SERVICE
|
||||
);
|
||||
const iosAppCredentials = await setupAscApiKeyAction.runAsync(ctx.credentialsCtx);
|
||||
const ascKeyForSubmissions = nullthrows(
|
||||
iosAppCredentials.appStoreConnectApiKeyForSubmissions,
|
||||
`An EAS Submit ASC Api Key could not be found for ${iosAppCredentials.appleAppIdentifier.bundleIdentifier}`
|
||||
);
|
||||
const { id, keyIdentifier, name } = ascKeyForSubmissions;
|
||||
Log.log(`Using Api Key ID: ${keyIdentifier}${name ? ` (${name})` : ''}`);
|
||||
return {
|
||||
result: {
|
||||
ascApiKeyId: id,
|
||||
},
|
||||
summary: {
|
||||
source: 'EAS servers',
|
||||
keyId: keyIdentifier,
|
||||
name: name ?? undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function getAscApiKeyLocallyAsync(
|
||||
export async function getAscApiKeyLocallyAsync(
|
||||
ctx: SubmissionContext<Platform.IOS>,
|
||||
source: AscApiKeySource
|
||||
): Promise<AscApiKeyResult> {
|
||||
@@ -133,8 +65,6 @@ export async function getAscApiKeyPathAsync(
|
||||
return await handlePathSourceAsync(ctx, source);
|
||||
case AscApiKeySourceType.prompt:
|
||||
return await handlePromptSourceAsync(ctx, source);
|
||||
case AscApiKeySourceType.credentialsService:
|
||||
throw new Error(`AscApiKeySourceType ${source} does not return a path.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,17 +34,15 @@ export default class IosSubmitCommand {
|
||||
| { appSpecificPasswordSource: Result<AppSpecificPasswordSource> }
|
||||
| { ascApiKeySource: Result<AscApiKeySource> }
|
||||
> {
|
||||
// if an App Specific Password env var is not specified, use ASC Api Key
|
||||
const appSpecificPasswordSource = this.resolveAppSpecificPasswordSource();
|
||||
const shouldSkipAppSpecificPasswordSource =
|
||||
!appSpecificPasswordSource.ok &&
|
||||
appSpecificPasswordSource.enforceError() instanceof MissingCredentialsError;
|
||||
if (!shouldSkipAppSpecificPasswordSource) {
|
||||
return { appSpecificPasswordSource: this.resolveAppSpecificPasswordSource() };
|
||||
}
|
||||
|
||||
// Fall back to app specific password if no ascApiKey defined
|
||||
const ascApiKeySource = this.resolveAscApiKeySource();
|
||||
return { ascApiKeySource };
|
||||
const shouldUseAppSpecificPassword =
|
||||
!ascApiKeySource.ok && ascApiKeySource.enforceError() instanceof MissingCredentialsError;
|
||||
if (shouldUseAppSpecificPassword) {
|
||||
return { appSpecificPasswordSource: this.resolveAppSpecificPasswordSource() };
|
||||
} else {
|
||||
return { ascApiKeySource };
|
||||
}
|
||||
}
|
||||
|
||||
private async resolveSubmissionOptionsAsync(): Promise<IosSubmissionOptions> {
|
||||
@@ -56,6 +54,7 @@ export default class IosSubmitCommand {
|
||||
: null;
|
||||
const maybeAscApiKeySource =
|
||||
'ascApiKeySource' in credentialsSource ? credentialsSource.ascApiKeySource : null;
|
||||
const maybeCredentialsSource = null; // todo
|
||||
const ascAppIdentifier = await this.resolveAscAppIdentifierAsync();
|
||||
const appleIdUsername = await this.resolveAppleIdUsernameAsync();
|
||||
|
||||
@@ -98,13 +97,13 @@ export default class IosSubmitCommand {
|
||||
sourceType: AppSpecificPasswordSourceType.userDefined,
|
||||
appSpecificPassword: envAppSpecificPassword,
|
||||
});
|
||||
} else if (this.ctx.nonInteractive) {
|
||||
return result(new Error('Set the EXPO_APPLE_APP_SPECIFIC_PASSWORD environment variable.'));
|
||||
} else {
|
||||
return result({
|
||||
sourceType: AppSpecificPasswordSourceType.prompt,
|
||||
});
|
||||
}
|
||||
|
||||
return result(
|
||||
new MissingCredentialsError(
|
||||
'The EXPO_APPLE_APP_SPECIFIC_PASSWORD environment variable must be set.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private resolveAscApiKeySource(): Result<AscApiKeySource> {
|
||||
@@ -129,7 +128,11 @@ export default class IosSubmitCommand {
|
||||
});
|
||||
}
|
||||
|
||||
return result({ sourceType: AscApiKeySourceType.credentialsService });
|
||||
return result(
|
||||
new MissingCredentialsError(
|
||||
'Set the ascApiKeyPath, ascApiKeyIssuerId and ascApiKeyId fields in eas.json.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private resolveArchiveSource(): Result<ArchiveSource> {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Platform } from '@expo/eas-build-job';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { MinimalAscApiKey } from '../../credentials/ios/credentials';
|
||||
import { IosSubmissionConfigInput, SubmissionFragment } from '../../graphql/generated';
|
||||
import { SubmissionMutation } from '../../graphql/mutations/SubmissionMutation';
|
||||
import formatFields from '../../utils/formatFields';
|
||||
@@ -16,12 +15,7 @@ import {
|
||||
AppSpecificPasswordSource,
|
||||
getAppSpecificPasswordAsync,
|
||||
} from './AppSpecificPasswordSource';
|
||||
import {
|
||||
AscApiKeyFromExpoServers,
|
||||
AscApiKeyResult,
|
||||
AscApiKeySource,
|
||||
getAscApiKeyResultAsync,
|
||||
} from './AscApiKeySource';
|
||||
import { AscApiKeyResult, AscApiKeySource, getAscApiKeyLocallyAsync } from './AscApiKeySource';
|
||||
|
||||
export interface IosSubmissionOptions
|
||||
extends Pick<IosSubmissionConfigInput, 'appleIdUsername' | 'ascAppIdentifier'> {
|
||||
@@ -75,7 +69,7 @@ export default class IosSubmitter extends BaseSubmitter<Platform.IOS, IosSubmiss
|
||||
? await getAppSpecificPasswordAsync(this.options.appSpecificPasswordSource)
|
||||
: null;
|
||||
const maybeAppStoreConnectApiKey = this.options.ascApiKeySource
|
||||
? await getAscApiKeyResultAsync(this.ctx, this.options.ascApiKeySource)
|
||||
? await getAscApiKeyLocallyAsync(this.ctx, this.options.ascApiKeySource)
|
||||
: null;
|
||||
return {
|
||||
archive,
|
||||
@@ -94,24 +88,18 @@ export default class IosSubmitter extends BaseSubmitter<Platform.IOS, IosSubmiss
|
||||
appleIdUsername,
|
||||
archiveUrl: archive.url,
|
||||
appleAppSpecificPassword: appSpecificPassword,
|
||||
...(ascApiKeyResult?.result ? this.formatAscApiKeyResult(ascApiKeyResult.result) : null),
|
||||
...(ascApiKeyResult?.result
|
||||
? {
|
||||
ascApiKey: {
|
||||
keyP8: ascApiKeyResult?.result.keyP8,
|
||||
keyIdentifier: ascApiKeyResult?.result.keyId,
|
||||
issuerIdentifier: ascApiKeyResult?.result.issuerId,
|
||||
},
|
||||
}
|
||||
: null),
|
||||
};
|
||||
}
|
||||
|
||||
private formatAscApiKeyResult(
|
||||
result: MinimalAscApiKey | AscApiKeyFromExpoServers
|
||||
): Pick<IosSubmissionConfigInput, 'ascApiKey'> | Pick<IosSubmissionConfigInput, 'ascApiKeyId'> {
|
||||
return 'ascApiKeyId' in result
|
||||
? { ascApiKeyId: result.ascApiKeyId }
|
||||
: {
|
||||
ascApiKey: {
|
||||
keyP8: result.keyP8,
|
||||
keyIdentifier: result.keyId,
|
||||
issuerIdentifier: result.issuerId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private prepareSummaryData(
|
||||
options: IosSubmissionOptions,
|
||||
{ archive, ascApiKeyResult }: ResolvedSourceOptions
|
||||
@@ -123,7 +111,9 @@ export default class IosSubmitter extends BaseSubmitter<Platform.IOS, IosSubmiss
|
||||
ascAppIdentifier,
|
||||
appleIdUsername: appleIdUsername ?? undefined,
|
||||
projectId,
|
||||
...(ascApiKeyResult ? { formattedAscApiKey: formatAscApiKeySummary(ascApiKeyResult) } : null),
|
||||
...(ascApiKeyResult
|
||||
? { formattedAscApiKey: formatServiceAccountSummary(ascApiKeyResult) }
|
||||
: null),
|
||||
...formatArchiveSourceSummary(archive),
|
||||
};
|
||||
}
|
||||
@@ -146,18 +136,10 @@ const SummaryHumanReadableKeys: Record<keyof SummaryData, string> = {
|
||||
formattedAscApiKey: 'App Store Connect Api Key',
|
||||
};
|
||||
|
||||
function formatAscApiKeySummary({ summary }: AscApiKeyResult): string {
|
||||
const { source, path, keyId, name } = summary;
|
||||
function formatServiceAccountSummary({ summary }: AscApiKeyResult): string {
|
||||
const { source, path, keyId } = summary;
|
||||
|
||||
const fields = [
|
||||
{
|
||||
label: 'Key Name',
|
||||
value: name,
|
||||
},
|
||||
{
|
||||
label: 'Key ID',
|
||||
value: keyId,
|
||||
},
|
||||
{
|
||||
label: 'Key Source',
|
||||
value: source,
|
||||
@@ -166,6 +148,10 @@ function formatAscApiKeySummary({ summary }: AscApiKeyResult): string {
|
||||
label: 'Key Path',
|
||||
value: path,
|
||||
},
|
||||
{
|
||||
label: 'Key ID',
|
||||
value: keyId,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredFields = fields.filter(({ value }) => value !== undefined && value !== null) as {
|
||||
|
||||
@@ -4,18 +4,15 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { asMock } from '../../../__tests__/utils';
|
||||
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
|
||||
import { testCommonIosAppCredentialsFragment } from '../../../credentials/__tests__/fixtures-ios';
|
||||
import { SetUpAscApiKey } from '../../../credentials/ios/actions/SetUpAscApiKey';
|
||||
import { getCredentialsFromUserAsync } from '../../../credentials/utils/promptForCredentials';
|
||||
import { createTestProject } from '../../../project/__tests__/project-utils';
|
||||
import { getBundleIdentifierAsync } from '../../../project/ios/bundleIdentifier';
|
||||
import { promptAsync } from '../../../prompts';
|
||||
import { SubmissionContext, createSubmissionContextAsync } from '../../context';
|
||||
import {
|
||||
AscApiKeySource,
|
||||
AscApiKeySourceType,
|
||||
getAscApiKeyLocallyAsync,
|
||||
getAscApiKeyPathAsync,
|
||||
getAscApiKeyResultAsync,
|
||||
} from '../AscApiKeySource';
|
||||
|
||||
jest.mock('fs');
|
||||
@@ -27,7 +24,6 @@ jest.mock('../../../user/User', () => ({
|
||||
jest.mock('../../../user/Account', () => ({
|
||||
findAccountByName: jest.fn(() => mockJester.accounts[0]),
|
||||
}));
|
||||
jest.mock('../../../project/ios/bundleIdentifier');
|
||||
|
||||
const testProject = createTestProject(mockJester, {
|
||||
android: {
|
||||
@@ -171,14 +167,14 @@ describe(getAscApiKeyPathAsync, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe(getAscApiKeyResultAsync, () => {
|
||||
describe(getAscApiKeyLocallyAsync, () => {
|
||||
it('returns a local Asc Api Key file with a AscApiKeySourceType.path source', async () => {
|
||||
const ctx = await getIosSubmissionContextAsync();
|
||||
const source: AscApiKeySource = {
|
||||
sourceType: AscApiKeySourceType.path,
|
||||
path: { keyP8Path: '/asc-api-key.p8', keyId: 'test-key-id', issuerId: 'test-issuer-id' },
|
||||
};
|
||||
const ascApiKeyResult = await getAscApiKeyResultAsync(ctx, source);
|
||||
const ascApiKeyResult = await getAscApiKeyLocallyAsync(ctx, source);
|
||||
expect(ascApiKeyResult).toMatchObject({
|
||||
result: {
|
||||
keyP8: 'super secret',
|
||||
@@ -205,7 +201,7 @@ describe(getAscApiKeyResultAsync, () => {
|
||||
const source: AscApiKeySource = {
|
||||
sourceType: AscApiKeySourceType.prompt,
|
||||
};
|
||||
const serviceAccountResult = await getAscApiKeyResultAsync(ctx, source);
|
||||
const serviceAccountResult = await getAscApiKeyLocallyAsync(ctx, source);
|
||||
expect(serviceAccountResult).toMatchObject({
|
||||
result: {
|
||||
keyP8: 'super secret',
|
||||
@@ -219,39 +215,4 @@ describe(getAscApiKeyResultAsync, () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an Asc Api Key from server with a AscApiKeySourceType.credentialService source', async () => {
|
||||
const ctx = await createSubmissionContextAsync({
|
||||
platform: Platform.IOS,
|
||||
projectDir: testProject.projectRoot,
|
||||
projectId,
|
||||
archiveFlags: {
|
||||
url: 'http://expo.dev/fake.apk',
|
||||
},
|
||||
profile: {
|
||||
language: 'en-US',
|
||||
},
|
||||
nonInteractive: true,
|
||||
});
|
||||
const source: AscApiKeySource = {
|
||||
sourceType: AscApiKeySourceType.credentialsService,
|
||||
};
|
||||
jest
|
||||
.spyOn(SetUpAscApiKey.prototype, 'runAsync')
|
||||
.mockImplementation(async _ctx => testCommonIosAppCredentialsFragment);
|
||||
asMock(getBundleIdentifierAsync).mockImplementation(() => 'com.hello.world');
|
||||
|
||||
const result = await getAscApiKeyResultAsync(ctx, source);
|
||||
expect(result).toEqual({
|
||||
result: {
|
||||
ascApiKeyId: testCommonIosAppCredentialsFragment.appStoreConnectApiKeyForSubmissions?.id,
|
||||
},
|
||||
summary: {
|
||||
keyId:
|
||||
testCommonIosAppCredentialsFragment.appStoreConnectApiKeyForSubmissions?.keyIdentifier,
|
||||
name: testCommonIosAppCredentialsFragment.appStoreConnectApiKeyForSubmissions?.name,
|
||||
source: 'EAS servers',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,6 @@ enum SubmissionErrorCode {
|
||||
IOS_INVALID_SIGNATURE = 'SUBMISSION_SERVICE_IOS_INVALID_SIGNATURE',
|
||||
IOS_INCORRECT_CREDENTIALS = 'SUBMISSION_SERVICE_IOS_INVALID_CREDENTIALS',
|
||||
IOS_IPAD_INVALID_ORIENTATION = 'SUBMISSION_SERVICE_IOS_IPAD_INVALID_ORIENTATION',
|
||||
IOS_APPLE_MAINTENANCE = 'SUBMISSION_SERVICE_IOS_APPLE_MAINTENANCE',
|
||||
}
|
||||
|
||||
const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
|
||||
@@ -63,8 +62,6 @@ const SubmissionErrorMessages: Record<SubmissionErrorCode, string> = {
|
||||
"Your app doesn't support iPad multitasking and has to require full screen.\n" +
|
||||
"If you're submitting a managed Expo project, set the `expo.ios.requireFullScreen` to true in app.json and build the project again.\n" +
|
||||
`${learnMore('https://expo.fyi/ipad-requires-fullscreen')}`,
|
||||
[SubmissionErrorCode.IOS_APPLE_MAINTENANCE]:
|
||||
'It looks like Apple servers are undergoing an unscheduled maintenance. Please try again later.',
|
||||
};
|
||||
|
||||
export function printSubmissionError(error: SubmissionError): boolean {
|
||||
|
||||
Reference in new issue
Block a user