eas-cli 12.4.0 → 12.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ const eas_build_job_1 = require("@expo/eas-build-job");
6
+ const eas_json_1 = require("@expo/eas-json");
7
+ const core_1 = require("@oclif/core");
8
+ const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
9
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
10
+ const generated_1 = require("../../graphql/generated");
11
+ const AppStoreConnectApiKeyQuery_1 = require("../../graphql/queries/AppStoreConnectApiKeyQuery");
12
+ const GoogleServiceAccountKeyQuery_1 = require("../../graphql/queries/GoogleServiceAccountKeyQuery");
13
+ const AndroidSubmitCommand_1 = tslib_1.__importDefault(require("../../submit/android/AndroidSubmitCommand"));
14
+ const context_1 = require("../../submit/context");
15
+ const IosSubmitCommand_1 = tslib_1.__importDefault(require("../../submit/ios/IosSubmitCommand"));
16
+ const json_1 = require("../../utils/json");
17
+ const gitNoCommit_1 = tslib_1.__importDefault(require("../../vcs/clients/gitNoCommit"));
18
+ /**
19
+ * This command will be run on the EAS workers.
20
+ * This command resolves credentials and other
21
+ * configuration, that normally would be included in the
22
+ * job and metadata objects, and prints them to stdout.
23
+ */
24
+ class SubmitInternal extends EasCommand_1.default {
25
+ async runAsync() {
26
+ const { flags } = await this.parse(_a);
27
+ // This command is always run with implicit --non-interactive and --json options
28
+ (0, json_1.enableJsonOutput)();
29
+ const { loggedIn: { actor, graphqlClient }, privateProjectConfig: { exp, projectId, projectDir }, analytics, vcsClient, } = await this.getContextAsync(_a, {
30
+ nonInteractive: true,
31
+ vcsClientOverride: new gitNoCommit_1.default(),
32
+ });
33
+ const submissionProfile = await eas_json_1.EasJsonUtils.getSubmitProfileAsync(eas_json_1.EasJsonAccessor.fromProjectPath(projectDir), flags.platform, flags.profile);
34
+ const ctx = await (0, context_1.createSubmissionContextAsync)({
35
+ platform: flags.platform,
36
+ projectDir,
37
+ profile: submissionProfile,
38
+ archiveFlags: {
39
+ id: flags.id,
40
+ },
41
+ nonInteractive: true,
42
+ isVerboseFastlaneEnabled: false,
43
+ actor,
44
+ graphqlClient,
45
+ analytics,
46
+ exp,
47
+ projectId,
48
+ vcsClient,
49
+ specifiedProfile: flags.profile,
50
+ });
51
+ let config;
52
+ if (ctx.platform === eas_build_job_1.Platform.IOS) {
53
+ const command = new IosSubmitCommand_1.default(ctx);
54
+ const submitter = await command.runAsync();
55
+ const mutationInput = await submitter.getSubmissionInputAsync();
56
+ const iosConfig = mutationInput.submissionConfig;
57
+ const ascApiKeyJson = await getAppStoreConnectApiKeyJsonAsync({
58
+ iosConfig,
59
+ graphqlClient,
60
+ });
61
+ const configInput = {
62
+ ascAppIdentifier: iosConfig.ascAppIdentifier,
63
+ isVerboseFastlaneEnabled: iosConfig.isVerboseFastlaneEnabled ?? undefined,
64
+ ...(ascApiKeyJson
65
+ ? { ascApiJsonKey: ascApiKeyJson }
66
+ : {
67
+ appleIdUsername: (0, nullthrows_1.default)(iosConfig.appleIdUsername),
68
+ appleAppSpecificPassword: (0, nullthrows_1.default)(iosConfig.appleAppSpecificPassword),
69
+ }),
70
+ };
71
+ config = eas_build_job_1.SubmissionConfig.Ios.SchemaZ.parse(configInput);
72
+ }
73
+ else if (ctx.platform === eas_build_job_1.Platform.ANDROID) {
74
+ const command = new AndroidSubmitCommand_1.default(ctx);
75
+ const submitter = await command.runAsync();
76
+ const mutationInput = await submitter.getSubmissionInputAsync();
77
+ const androidConfig = mutationInput.submissionConfig;
78
+ const changesNotSentForReview = androidConfig.changesNotSentForReview ?? undefined;
79
+ const releaseStatus = androidConfig.releaseStatus
80
+ ? graphQlReleaseStatusToConfigReleaseStatus[androidConfig.releaseStatus]
81
+ : undefined;
82
+ const googleServiceAccountKeyJson = (0, nullthrows_1.default)(await getGoogleServiceAccountKeyJsonAsync({
83
+ androidConfig,
84
+ graphqlClient,
85
+ }));
86
+ const track = graphQlTrackToConfigTrack[androidConfig.track];
87
+ const configInput = {
88
+ changesNotSentForReview,
89
+ googleServiceAccountKeyJson,
90
+ track,
91
+ ...(releaseStatus === eas_build_job_1.SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS
92
+ ? {
93
+ releaseStatus: eas_build_job_1.SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS,
94
+ rollout: androidConfig.rollout ?? undefined,
95
+ }
96
+ : { releaseStatus }),
97
+ };
98
+ config = eas_build_job_1.SubmissionConfig.Android.SchemaZ.parse(configInput);
99
+ }
100
+ else {
101
+ throw new Error(`Unsupported platform: ${ctx.platform}`);
102
+ }
103
+ (0, json_1.printJsonOnlyOutput)({ config });
104
+ }
105
+ }
106
+ _a = SubmitInternal;
107
+ SubmitInternal.hidden = true;
108
+ SubmitInternal.flags = {
109
+ platform: core_1.Flags.enum({
110
+ options: [eas_build_job_1.Platform.ANDROID, eas_build_job_1.Platform.IOS],
111
+ required: true,
112
+ }),
113
+ profile: core_1.Flags.string({
114
+ description: 'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
115
+ }),
116
+ id: core_1.Flags.string({
117
+ description: 'ID of the build to submit',
118
+ required: true,
119
+ }),
120
+ };
121
+ SubmitInternal.contextDefinition = {
122
+ ..._a.ContextOptions.LoggedIn,
123
+ ..._a.ContextOptions.ProjectConfig,
124
+ ..._a.ContextOptions.ProjectDir,
125
+ ..._a.ContextOptions.Analytics,
126
+ ..._a.ContextOptions.Vcs,
127
+ };
128
+ exports.default = SubmitInternal;
129
+ async function getGoogleServiceAccountKeyJsonAsync({ androidConfig, graphqlClient, }) {
130
+ if (androidConfig.googleServiceAccountKeyJson) {
131
+ return androidConfig.googleServiceAccountKeyJson;
132
+ }
133
+ else if (androidConfig.googleServiceAccountKeyId) {
134
+ const key = await GoogleServiceAccountKeyQuery_1.GoogleServiceAccountKeyQuery.getByIdAsync(graphqlClient, androidConfig.googleServiceAccountKeyId);
135
+ return key.keyJson;
136
+ }
137
+ return null;
138
+ }
139
+ async function getAppStoreConnectApiKeyJsonAsync({ iosConfig, graphqlClient, }) {
140
+ if (iosConfig.ascApiKey) {
141
+ return JSON.stringify({
142
+ key_id: iosConfig.ascApiKey.keyIdentifier,
143
+ issuer_id: iosConfig.ascApiKey.issuerIdentifier,
144
+ key: iosConfig.ascApiKey.keyP8,
145
+ });
146
+ }
147
+ else if (iosConfig.ascApiKeyId) {
148
+ const key = await AppStoreConnectApiKeyQuery_1.AppStoreConnectApiKeyQuery.getByIdAsync(graphqlClient, iosConfig.ascApiKeyId);
149
+ return JSON.stringify({
150
+ key_id: key.keyIdentifier,
151
+ issuer_id: key.issuerIdentifier,
152
+ key: key.keyP8,
153
+ });
154
+ }
155
+ return null;
156
+ }
157
+ const graphQlReleaseStatusToConfigReleaseStatus = {
158
+ [generated_1.SubmissionAndroidReleaseStatus.Draft]: eas_build_job_1.SubmissionConfig.Android.ReleaseStatus.DRAFT,
159
+ [generated_1.SubmissionAndroidReleaseStatus.InProgress]: eas_build_job_1.SubmissionConfig.Android.ReleaseStatus.IN_PROGRESS,
160
+ [generated_1.SubmissionAndroidReleaseStatus.Completed]: eas_build_job_1.SubmissionConfig.Android.ReleaseStatus.COMPLETED,
161
+ [generated_1.SubmissionAndroidReleaseStatus.Halted]: eas_build_job_1.SubmissionConfig.Android.ReleaseStatus.HALTED,
162
+ };
163
+ const graphQlTrackToConfigTrack = {
164
+ [generated_1.SubmissionAndroidTrack.Production]: eas_build_job_1.SubmissionConfig.Android.ReleaseTrack.PRODUCTION,
165
+ [generated_1.SubmissionAndroidTrack.Beta]: eas_build_job_1.SubmissionConfig.Android.ReleaseTrack.BETA,
166
+ [generated_1.SubmissionAndroidTrack.Alpha]: eas_build_job_1.SubmissionConfig.Android.ReleaseTrack.ALPHA,
167
+ [generated_1.SubmissionAndroidTrack.Internal]: eas_build_job_1.SubmissionConfig.Android.ReleaseTrack.INTERNAL,
168
+ };
@@ -14,14 +14,12 @@ const deployment_1 = require("../../worker/deployment");
14
14
  const logs_1 = require("../../worker/utils/logs");
15
15
  class WorkerAlias extends EasCommand_1.default {
16
16
  async runAsync() {
17
- // NOTE(cedric): `Log.warn` uses `console.log`, which is incorrect when running with `--json`
18
- // eslint-disable-next-line no-console
19
- console.warn(chalk_1.default.yellow('EAS Worker Deployments are in beta and subject to breaking changes.'));
20
17
  const { flags: rawFlags } = await this.parse(_a);
21
18
  const flags = this.sanitizeFlags(rawFlags);
22
19
  if (flags.json) {
23
20
  (0, json_1.enableJsonOutput)();
24
21
  }
22
+ log_1.default.warn('EAS Worker Deployments are in beta and subject to breaking changes.');
25
23
  const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(_a, {
26
24
  nonInteractive: true,
27
25
  });
@@ -110,16 +108,16 @@ WorkerAlias.state = 'beta';
110
108
  WorkerAlias.flags = {
111
109
  prod: core_1.Flags.boolean({
112
110
  aliases: ['production'],
113
- description: 'Promote an existing deployment to production',
111
+ description: 'Promote an existing deployment to production.',
114
112
  default: false,
115
113
  }),
116
114
  alias: core_1.Flags.string({
117
- description: 'Custom alias to assign to the existing deployment',
115
+ description: 'Custom alias to assign to the existing deployment.',
118
116
  helpValue: 'name',
119
117
  required: false,
120
118
  }),
121
119
  id: core_1.Flags.string({
122
- description: 'Unique identifier of an existing deployment',
120
+ description: 'Unique identifier of an existing deployment.',
123
121
  helpValue: 'xyz123',
124
122
  required: false,
125
123
  }),
@@ -7,13 +7,63 @@ export default class WorkerDeploy extends EasCommand {
7
7
  static hidden: boolean;
8
8
  static state: string;
9
9
  static flags: {
10
- environment: import("@oclif/core/lib/interfaces").OptionFlag<EnvironmentVariableEnvironment | undefined>;
11
10
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
11
  'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
12
  prod: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
13
  alias: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
15
14
  id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
16
15
  'export-dir': import("@oclif/core/lib/interfaces").OptionFlag<string>;
16
+ environment: {
17
+ description: string;
18
+ name: string;
19
+ char?: import("@oclif/core/lib/interfaces").AlphabetUppercase | import("@oclif/core/lib/interfaces").AlphabetLowercase | undefined;
20
+ summary?: string | undefined;
21
+ helpLabel?: string | undefined;
22
+ helpGroup?: string | undefined;
23
+ env?: string | undefined;
24
+ hidden?: boolean | undefined;
25
+ required?: boolean | undefined;
26
+ dependsOn?: string[] | undefined;
27
+ exclusive?: string[] | undefined;
28
+ exactlyOne?: string[] | undefined;
29
+ relationships?: import("@oclif/core/lib/interfaces/parser").Relationship[] | undefined;
30
+ deprecated?: true | import("@oclif/core/lib/interfaces/parser").Deprecation | undefined;
31
+ aliases?: string[] | undefined;
32
+ deprecateAliases?: boolean | undefined;
33
+ parse: import("@oclif/core/lib/interfaces/parser").FlagParser<EnvironmentVariableEnvironment | undefined, string, any>;
34
+ type: "option";
35
+ helpValue?: string | undefined;
36
+ options?: string[] | undefined;
37
+ multiple: false;
38
+ defaultHelp?: import("@oclif/core/lib/interfaces/parser").DefaultHelp<EnvironmentVariableEnvironment | undefined, Record<string, unknown>>;
39
+ input: string[];
40
+ default?: import("@oclif/core/lib/interfaces").Default<EnvironmentVariableEnvironment | undefined, Record<string, unknown>>;
41
+ } | {
42
+ description: string;
43
+ name: string;
44
+ char?: import("@oclif/core/lib/interfaces").AlphabetUppercase | import("@oclif/core/lib/interfaces").AlphabetLowercase | undefined;
45
+ summary?: string | undefined;
46
+ helpLabel?: string | undefined;
47
+ helpGroup?: string | undefined;
48
+ env?: string | undefined;
49
+ hidden?: boolean | undefined;
50
+ required?: boolean | undefined;
51
+ dependsOn?: string[] | undefined;
52
+ exclusive?: string[] | undefined;
53
+ exactlyOne?: string[] | undefined;
54
+ relationships?: import("@oclif/core/lib/interfaces/parser").Relationship[] | undefined;
55
+ deprecated?: true | import("@oclif/core/lib/interfaces/parser").Deprecation | undefined;
56
+ aliases?: string[] | undefined;
57
+ deprecateAliases?: boolean | undefined;
58
+ parse: import("@oclif/core/lib/interfaces/parser").FlagParser<EnvironmentVariableEnvironment | undefined, string, any>;
59
+ type: "option";
60
+ helpValue?: string | undefined;
61
+ options?: string[] | undefined;
62
+ multiple: true;
63
+ defaultHelp?: import("@oclif/core/lib/interfaces/parser").DefaultHelp<EnvironmentVariableEnvironment | undefined, Record<string, unknown>>;
64
+ input: string[];
65
+ default?: import("@oclif/core/lib/interfaces").Default<(EnvironmentVariableEnvironment | undefined)[] | undefined, Record<string, unknown>>;
66
+ };
17
67
  };
18
68
  static contextDefinition: {
19
69
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
@@ -23,19 +23,15 @@ const isDirectory = (directoryPath) => node_fs_1.default.promises
23
23
  .catch(() => false);
24
24
  class WorkerDeploy extends EasCommand_1.default {
25
25
  async runAsync() {
26
- // NOTE(cedric): `Log.warn` uses `console.log`, which is incorrect when running with `--json`
27
- // eslint-disable-next-line no-console
28
- console.warn(chalk_1.default.yellow('EAS Worker Deployments are in beta and subject to breaking changes.'));
29
26
  const { flags: rawFlags } = await this.parse(_a);
30
27
  const flags = this.sanitizeFlags(rawFlags);
31
28
  if (flags.json) {
32
29
  (0, json_1.enableJsonOutput)();
33
30
  }
31
+ log_1.default.warn('EAS Worker Deployments are in beta and subject to breaking changes.');
34
32
  const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, projectDir, } = await this.getContextAsync(_a, flags);
35
- const [{ projectId }, projectDist] = await Promise.all([
36
- getDynamicPrivateProjectConfigAsync(),
37
- resolveExportedProjectAsync(flags, projectDir),
38
- ]);
33
+ const projectDist = await resolveExportedProjectAsync(flags, projectDir);
34
+ const { projectId } = await getDynamicPrivateProjectConfigAsync();
39
35
  logExportedProjectInfo(projectDist);
40
36
  async function* emitWorkerTarballAsync(params) {
41
37
  yield ['assets.json', JSON.stringify(params.assetMap)];
@@ -243,25 +239,27 @@ WorkerDeploy.state = 'beta';
243
239
  WorkerDeploy.flags = {
244
240
  prod: core_1.Flags.boolean({
245
241
  aliases: ['production'],
246
- description: 'Create a new production deployment',
242
+ description: 'Create a new production deployment.',
247
243
  default: false,
248
244
  }),
249
245
  alias: core_1.Flags.string({
250
- description: 'Custom alias to assign to the new deployment',
246
+ description: 'Custom alias to assign to the new deployment.',
251
247
  helpValue: 'name',
252
248
  }),
253
249
  id: core_1.Flags.string({
254
- description: 'Custom unique identifier for the new deployment',
250
+ description: 'Custom unique identifier for the new deployment.',
255
251
  helpValue: 'xyz123',
256
252
  }),
257
253
  'export-dir': core_1.Flags.string({
258
- description: 'Directory where the Expo project was exported',
254
+ description: 'Directory where the Expo project was exported.',
259
255
  helpValue: 'dir',
260
256
  default: 'dist',
261
257
  }),
262
- // TODO(@kitten): Allow deployment identifier to be specified
258
+ environment: {
259
+ ...flags_1.EASEnvironmentFlag.environment,
260
+ description: 'Deploy with EAS Environment Variables matching the specified environment.',
261
+ },
263
262
  ...flags_1.EasNonInteractiveAndJsonFlags,
264
- ...flags_1.EASEnvironmentFlag,
265
263
  };
266
264
  WorkerDeploy.contextDefinition = {
267
265
  ..._a.ContextOptions.DynamicProjectConfig,
@@ -273,17 +271,30 @@ async function resolveExportedProjectAsync(flags, projectDir) {
273
271
  const exportPath = path.join(projectDir, flags.exportDir);
274
272
  const serverPath = path.join(exportPath, 'server');
275
273
  const clientPath = path.join(exportPath, 'client');
276
- const [hasServerPath, hasClientPath, modifiedAt] = await Promise.all([
274
+ const [hasServerPath, hasClientPath, exportStat] = await Promise.all([
277
275
  isDirectory(serverPath),
278
276
  isDirectory(clientPath),
279
- node_fs_1.default.promises.stat(exportPath).then(stat => stat.mtime),
277
+ node_fs_1.default.promises.stat(exportPath).catch(() => null),
280
278
  ]);
279
+ if (!exportStat?.isDirectory()) {
280
+ throw new Error(`No "${flags.exportDir}/" folder found. Prepare your project for deployment with "npx expo export --platform web"`);
281
+ }
281
282
  if (hasServerPath && hasClientPath) {
282
- return { type: 'server', path: exportPath, modifiedAt, serverPath, clientPath };
283
+ return {
284
+ type: 'server',
285
+ path: exportPath,
286
+ modifiedAt: exportStat.mtime,
287
+ serverPath,
288
+ clientPath,
289
+ };
283
290
  }
284
- return { type: 'static', path: exportPath, modifiedAt };
291
+ return { type: 'static', path: exportPath, modifiedAt: exportStat.mtime };
285
292
  }
286
293
  function logExportedProjectInfo(project) {
287
- const modifiedAgo = (0, timeago_js_1.format)(project.modifiedAt);
288
- log_1.default.log((0, chalk_1.default) `{dim > Project export: ${project.type} - created ${modifiedAgo}}`);
294
+ let modifiedAgo = '';
295
+ // Only show the timestamp for exports older than 1 minute
296
+ if (project.modifiedAt && Date.now() - project.modifiedAt.getTime() > 60000) {
297
+ modifiedAgo = ` - exported ${(0, timeago_js_1.format)(project.modifiedAt)}`;
298
+ }
299
+ log_1.default.log((0, chalk_1.default) `{dim > Project export: ${project.type}${modifiedAgo}}`);
289
300
  }