eas-cli 18.3.0 → 18.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.
- package/README.md +98 -95
- package/build/build/android/prepareJob.js +2 -2
- package/build/build/build.js +27 -6
- package/build/build/ios/prepareJob.js +2 -2
- package/build/build/metadata.js +2 -1
- package/build/commandUtils/EasCommand.js +23 -2
- package/build/commandUtils/context/contextUtils/getProjectIdAsync.js +2 -0
- package/build/commandUtils/flags.d.ts +1 -0
- package/build/commandUtils/flags.js +12 -0
- package/build/commandUtils/workflow/fetchLogs.js +11 -2
- package/build/commandUtils/workflow/types.d.ts +5 -1
- package/build/commandUtils/workflow/utils.js +22 -16
- package/build/commands/build/dev.d.ts +1 -0
- package/build/commands/build/dev.js +9 -1
- package/build/commands/metadata/pull.d.ts +1 -0
- package/build/commands/metadata/pull.js +11 -4
- package/build/commands/metadata/push.d.ts +1 -0
- package/build/commands/metadata/push.js +11 -4
- package/build/commands/project/onboarding.js +3 -0
- package/build/commands/workflow/logs.js +12 -12
- package/build/graphql/generated.d.ts +673 -46
- package/build/graphql/generated.js +58 -20
- package/build/graphql/queries/UserQuery.js +3 -0
- package/build/graphql/types/Update.js +3 -0
- package/build/metadata/apple/config/reader.d.ts +5 -1
- package/build/metadata/apple/config/reader.js +8 -0
- package/build/metadata/apple/config/writer.d.ts +5 -1
- package/build/metadata/apple/config/writer.js +13 -0
- package/build/metadata/apple/data.d.ts +6 -2
- package/build/metadata/apple/rules/infoRestrictedWords.js +6 -1
- package/build/metadata/apple/tasks/age-rating.d.ts +1 -1
- package/build/metadata/apple/tasks/age-rating.js +19 -3
- package/build/metadata/apple/tasks/app-review-detail.js +7 -2
- package/build/metadata/apple/tasks/index.js +4 -0
- package/build/metadata/apple/tasks/previews.d.ts +18 -0
- package/build/metadata/apple/tasks/previews.js +208 -0
- package/build/metadata/apple/tasks/screenshots.d.ts +18 -0
- package/build/metadata/apple/tasks/screenshots.js +224 -0
- package/build/metadata/apple/types.d.ts +34 -1
- package/build/metadata/auth.d.ts +11 -1
- package/build/metadata/auth.js +96 -2
- package/build/metadata/download.d.ts +5 -1
- package/build/metadata/download.js +16 -8
- package/build/metadata/upload.d.ts +5 -1
- package/build/metadata/upload.js +11 -4
- package/build/project/projectUtils.d.ts +0 -2
- package/build/project/projectUtils.js +0 -12
- package/build/project/workflow.js +1 -1
- package/build/sentry.d.ts +2 -0
- package/build/sentry.js +22 -0
- package/build/update/utils.d.ts +2 -2
- package/build/update/utils.js +1 -0
- package/build/user/User.d.ts +2 -2
- package/build/user/User.js +3 -0
- package/build/user/expoBrowserAuthFlowLauncher.js +70 -13
- package/oclif.manifest.json +1995 -1918
- package/package.json +12 -11
- package/schema/metadata-0.json +36 -0
|
@@ -6,13 +6,13 @@ const eas_build_job_1 = require("@expo/eas-build-job");
|
|
|
6
6
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
7
|
const slash_1 = tslib_1.__importDefault(require("slash"));
|
|
8
8
|
const customBuildConfig_1 = require("../../project/customBuildConfig");
|
|
9
|
-
const
|
|
9
|
+
const User_1 = require("../../user/User");
|
|
10
10
|
const cacheDefaults = {
|
|
11
11
|
disabled: false,
|
|
12
12
|
paths: [],
|
|
13
13
|
};
|
|
14
14
|
async function prepareJobAsync(ctx, jobData) {
|
|
15
|
-
const username = (0,
|
|
15
|
+
const username = (0, User_1.getActorUsername)(ctx.user) ?? undefined;
|
|
16
16
|
const buildProfile = ctx.buildProfile;
|
|
17
17
|
const projectRootDirectory = (0, slash_1.default)(path_1.default.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
|
|
18
18
|
const { credentials } = jobData;
|
package/build/build/build.js
CHANGED
|
@@ -281,6 +281,8 @@ async function getBuildsSafelyAsync(graphqlClient, buildIds) {
|
|
|
281
281
|
}
|
|
282
282
|
let queueProgressBarStarted = false;
|
|
283
283
|
const queueProgressBar = new cli_progress_1.default.SingleBar({ format: '|{bar}| {estimatedWaitTime}' }, cli_progress_1.default.Presets.rect);
|
|
284
|
+
let statusNewSetAt = null;
|
|
285
|
+
const NEW_STATUS_GRACE_PERIOD_MS = 15_000;
|
|
284
286
|
async function handleSingleBuildProgressAsync({ build, accountName, }, { spinner }) {
|
|
285
287
|
if (build === null) {
|
|
286
288
|
spinner.text = 'Could not fetch the build status. Check your network connection.';
|
|
@@ -301,9 +303,18 @@ async function handleSingleBuildProgressAsync({ build, accountName, }, { spinner
|
|
|
301
303
|
case generated_1.BuildStatus.Finished:
|
|
302
304
|
spinner.succeed('Build finished');
|
|
303
305
|
return { refetch: false };
|
|
304
|
-
case generated_1.BuildStatus.New:
|
|
305
|
-
|
|
306
|
+
case generated_1.BuildStatus.New: {
|
|
307
|
+
const now = Date.now();
|
|
308
|
+
statusNewSetAt ??= now;
|
|
309
|
+
const newStatusDurationMs = now - statusNewSetAt;
|
|
310
|
+
if (newStatusDurationMs < NEW_STATUS_GRACE_PERIOD_MS) {
|
|
311
|
+
spinner.text = 'Waiting for build to get enqueued…';
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
spinner.text = `Build concurrency limit reached for your account. Build will enter queue once a concurrency becomes available. Add additional concurrencies at ${(0, log_1.link)(formatAccountBillingUrl(accountName))}.`;
|
|
315
|
+
}
|
|
306
316
|
break;
|
|
317
|
+
}
|
|
307
318
|
case generated_1.BuildStatus.InQueue: {
|
|
308
319
|
spinner.text = 'Build queued...';
|
|
309
320
|
const progressBarPayload = typeof build.estimatedWaitTimeLeftSeconds === 'number'
|
|
@@ -356,7 +367,7 @@ const priorityToQueueDisplayName = {
|
|
|
356
367
|
[generated_1.BuildPriority.High]: 'priority queue',
|
|
357
368
|
};
|
|
358
369
|
const statusToDisplayName = {
|
|
359
|
-
[generated_1.BuildStatus.New]: 'waiting to enter the queue
|
|
370
|
+
[generated_1.BuildStatus.New]: 'waiting to enter the queue',
|
|
360
371
|
[generated_1.BuildStatus.InQueue]: 'in queue',
|
|
361
372
|
[generated_1.BuildStatus.InProgress]: 'in progress',
|
|
362
373
|
[generated_1.BuildStatus.PendingCancel]: 'canceled',
|
|
@@ -368,6 +379,7 @@ const platforms = [generated_1.AppPlatform.Android, generated_1.AppPlatform.Ios]
|
|
|
368
379
|
async function handleMultipleBuildsProgressAsync({ builds: maybeBuilds }, { spinner, originalSpinnerText }) {
|
|
369
380
|
const buildCount = maybeBuilds.length;
|
|
370
381
|
const builds = maybeBuilds.filter(isBuildFragment);
|
|
382
|
+
const someNew = builds.some(build => build.status === generated_1.BuildStatus.New);
|
|
371
383
|
const allFinished = builds.filter(build => build.status === generated_1.BuildStatus.Finished).length === buildCount;
|
|
372
384
|
const allSettled = builds.filter(build => [
|
|
373
385
|
generated_1.BuildStatus.Finished,
|
|
@@ -375,6 +387,9 @@ async function handleMultipleBuildsProgressAsync({ builds: maybeBuilds }, { spin
|
|
|
375
387
|
generated_1.BuildStatus.Canceled,
|
|
376
388
|
generated_1.BuildStatus.PendingCancel,
|
|
377
389
|
].includes(build.status)).length === buildCount;
|
|
390
|
+
if (someNew && statusNewSetAt === null) {
|
|
391
|
+
statusNewSetAt = Date.now();
|
|
392
|
+
}
|
|
378
393
|
if (allSettled) {
|
|
379
394
|
if (allFinished) {
|
|
380
395
|
spinner.succeed(formatSettledBuildsText(builds));
|
|
@@ -385,7 +400,10 @@ async function handleMultipleBuildsProgressAsync({ builds: maybeBuilds }, { spin
|
|
|
385
400
|
return { refetch: false };
|
|
386
401
|
}
|
|
387
402
|
else {
|
|
388
|
-
|
|
403
|
+
const showConcurrencyWarning = someNew &&
|
|
404
|
+
statusNewSetAt !== null &&
|
|
405
|
+
Date.now() - statusNewSetAt >= NEW_STATUS_GRACE_PERIOD_MS;
|
|
406
|
+
spinner.text = formatPendingBuildsText(originalSpinnerText, builds, showConcurrencyWarning);
|
|
389
407
|
return { refetch: true };
|
|
390
408
|
}
|
|
391
409
|
}
|
|
@@ -397,12 +415,15 @@ function formatSettledBuildsText(builds) {
|
|
|
397
415
|
})
|
|
398
416
|
.join('\n ');
|
|
399
417
|
}
|
|
400
|
-
function formatPendingBuildsText(originalSpinnerText, builds) {
|
|
418
|
+
function formatPendingBuildsText(originalSpinnerText, builds, showConcurrencyWarning) {
|
|
401
419
|
return [
|
|
402
420
|
originalSpinnerText,
|
|
403
421
|
...platforms.map(platform => {
|
|
404
422
|
const build = builds.find(build => build.platform === platform);
|
|
405
423
|
const status = build ? statusToDisplayName[build.status] : 'unknown';
|
|
424
|
+
const concurrencyWarning = showConcurrencyWarning && build?.status === generated_1.BuildStatus.New
|
|
425
|
+
? ' (concurrency limit reached)'
|
|
426
|
+
: '';
|
|
406
427
|
let extraInfo = '';
|
|
407
428
|
if (build?.status === generated_1.BuildStatus.InQueue &&
|
|
408
429
|
typeof build.initialQueuePosition === 'number' &&
|
|
@@ -415,7 +436,7 @@ function formatPendingBuildsText(originalSpinnerText, builds) {
|
|
|
415
436
|
: '';
|
|
416
437
|
extraInfo = ` - queue progress: ${chalk_1.default.bold(`${percent}%`)}${estimatedWaitTime}`;
|
|
417
438
|
}
|
|
418
|
-
return `${platform_1.appPlatformEmojis[platform]} ${platform_1.appPlatformDisplayNames[platform]} build - status: ${chalk_1.default.bold(status)}${extraInfo}`;
|
|
439
|
+
return `${platform_1.appPlatformEmojis[platform]} ${platform_1.appPlatformDisplayNames[platform]} build - status: ${chalk_1.default.bold(status)}${concurrencyWarning}${extraInfo}`;
|
|
419
440
|
}),
|
|
420
441
|
].join('\n ');
|
|
421
442
|
}
|
|
@@ -8,13 +8,13 @@ const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
|
8
8
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
9
9
|
const slash_1 = tslib_1.__importDefault(require("slash"));
|
|
10
10
|
const customBuildConfig_1 = require("../../project/customBuildConfig");
|
|
11
|
-
const
|
|
11
|
+
const User_1 = require("../../user/User");
|
|
12
12
|
const cacheDefaults = {
|
|
13
13
|
disabled: false,
|
|
14
14
|
paths: [],
|
|
15
15
|
};
|
|
16
16
|
async function prepareJobAsync(ctx, jobData) {
|
|
17
|
-
const username = (0,
|
|
17
|
+
const username = (0, User_1.getActorUsername)(ctx.user) ?? undefined;
|
|
18
18
|
const buildProfile = ctx.buildProfile;
|
|
19
19
|
const projectRootDirectory = (0, slash_1.default)(path_1.default.relative(await ctx.vcsClient.getRootPathAsync(), ctx.projectDir)) || '.';
|
|
20
20
|
const buildCredentials = {};
|
package/build/build/metadata.js
CHANGED
|
@@ -16,6 +16,7 @@ const projectUtils_1 = require("../project/projectUtils");
|
|
|
16
16
|
const UpdatesModule_1 = require("../update/android/UpdatesModule");
|
|
17
17
|
const UpdatesModule_2 = require("../update/ios/UpdatesModule");
|
|
18
18
|
const easCli_1 = require("../utils/easCli");
|
|
19
|
+
const User_1 = require("../user/User");
|
|
19
20
|
async function collectMetadataAsync(ctx, runtimeAndFingerprintMetadata) {
|
|
20
21
|
const channelObject = await resolveChannelAsync(ctx);
|
|
21
22
|
const distribution = ctx.buildProfile.distribution ?? types_1.BuildDistributionType.STORE;
|
|
@@ -39,7 +40,7 @@ async function collectMetadataAsync(ctx, runtimeAndFingerprintMetadata) {
|
|
|
39
40
|
isGitWorkingTreeDirty: ctx.localBuildOptions.localBuildMode === local_1.LocalBuildMode.INTERNAL
|
|
40
41
|
? false
|
|
41
42
|
: await ctx.vcsClient.hasUncommittedChangesAsync(),
|
|
42
|
-
username: (0,
|
|
43
|
+
username: (0, User_1.getActorUsername)(ctx.user) ?? undefined,
|
|
43
44
|
message: ctx.message,
|
|
44
45
|
...(ctx.platform === eas_build_job_1.Platform.IOS && {
|
|
45
46
|
iosEnterpriseProvisioning: resolveIosEnterpriseProvisioning(ctx),
|
|
@@ -20,7 +20,9 @@ const VcsClientContextField_1 = tslib_1.__importDefault(require("./context/VcsCl
|
|
|
20
20
|
const errors_1 = require("./errors");
|
|
21
21
|
const AnalyticsManager_1 = require("../analytics/AnalyticsManager");
|
|
22
22
|
const log_1 = tslib_1.__importStar(require("../log"));
|
|
23
|
+
const sentry_1 = tslib_1.__importDefault(require("../sentry"));
|
|
23
24
|
const SessionManager_1 = tslib_1.__importDefault(require("../user/SessionManager"));
|
|
25
|
+
const User_1 = require("../user/User");
|
|
24
26
|
const BASE_GRAPHQL_ERROR_MESSAGE = 'GraphQL request failed.';
|
|
25
27
|
class EasCommand extends core_1.Command {
|
|
26
28
|
static ContextOptions = {
|
|
@@ -159,7 +161,13 @@ class EasCommand extends core_1.Command {
|
|
|
159
161
|
this.sessionManagerInternal = new SessionManager_1.default(this.analytics);
|
|
160
162
|
// this is needed for logEvent call below as it identifies the user in the analytics system
|
|
161
163
|
// if possible
|
|
162
|
-
await this.sessionManager.getUserAsync();
|
|
164
|
+
const actor = await this.sessionManager.getUserAsync();
|
|
165
|
+
if (actor) {
|
|
166
|
+
sentry_1.default.setUser({
|
|
167
|
+
id: actor.id,
|
|
168
|
+
username: (0, User_1.getActorDisplayName)(actor),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
163
171
|
this.analytics.logEvent(AnalyticsManager_1.CommandEvent.ACTION, {
|
|
164
172
|
// id is assigned by oclif in constructor based on the filepath:
|
|
165
173
|
// commands/submit === submit, commands/build/list === build:list
|
|
@@ -170,10 +178,12 @@ class EasCommand extends core_1.Command {
|
|
|
170
178
|
// eslint-disable-next-line async-protect/async-suffix
|
|
171
179
|
async finally(err) {
|
|
172
180
|
await this.analytics.flushAsync();
|
|
181
|
+
await sentry_1.default.flush(2000);
|
|
173
182
|
return await super.finally(err);
|
|
174
183
|
}
|
|
175
184
|
catch(err) {
|
|
176
|
-
|
|
185
|
+
const commandId = this.id ?? 'unknown';
|
|
186
|
+
let baseMessage = `${commandId} command failed.`;
|
|
177
187
|
if (err instanceof errors_1.EasCommandError) {
|
|
178
188
|
log_1.default.error(err.message);
|
|
179
189
|
}
|
|
@@ -206,6 +216,17 @@ class EasCommand extends core_1.Command {
|
|
|
206
216
|
log_1.default.error(err.message);
|
|
207
217
|
}
|
|
208
218
|
log_1.default.debug(err);
|
|
219
|
+
sentry_1.default.withScope(scope => {
|
|
220
|
+
scope.setTag('command', commandId);
|
|
221
|
+
scope.setTag('error_name', err.name);
|
|
222
|
+
scope.setExtra('commandId', commandId);
|
|
223
|
+
scope.setExtra('commandMessage', baseMessage);
|
|
224
|
+
scope.setExtra('originalMessage', err.message);
|
|
225
|
+
if (err instanceof core_2.CombinedError) {
|
|
226
|
+
scope.setExtra('graphQLErrorCount', err.graphQLErrors.length);
|
|
227
|
+
}
|
|
228
|
+
sentry_1.default.captureException(err);
|
|
229
|
+
});
|
|
209
230
|
const sanitizedError = new Error(baseMessage);
|
|
210
231
|
sanitizedError.stack = err.stack;
|
|
211
232
|
throw sanitizedError;
|
|
@@ -114,6 +114,8 @@ async function validateOrSetProjectIdAsync({ exp, graphqlClient, actor, options,
|
|
|
114
114
|
return user.username;
|
|
115
115
|
case 'SSOUser':
|
|
116
116
|
return user.username;
|
|
117
|
+
case 'PartnerActor':
|
|
118
|
+
return user.username;
|
|
117
119
|
case 'Robot':
|
|
118
120
|
throw new Error('Must configure EAS project by running "eas init" before using a robot user to manage the project.');
|
|
119
121
|
}
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EasUpdateEnvironmentRequiredFlag = exports.EasUpdateEnvironmentFlag = exports.EasJsonOnlyFlag = exports.EASNonInteractiveFlag = exports.EASEnvironmentVariableScopeFlag = exports.EASVariableVisibilityFlag = exports.EASVariableFormatFlag = exports.EASMultiEnvironmentFlag = exports.EASEnvironmentFlag = exports.EasEnvironmentFlagParameters = exports.EasNonInteractiveAndJsonFlags = void 0;
|
|
4
|
+
exports.isNonInteractiveByDefault = isNonInteractiveByDefault;
|
|
4
5
|
exports.resolveNonInteractiveAndJsonFlags = resolveNonInteractiveAndJsonFlags;
|
|
5
6
|
const core_1 = require("@oclif/core");
|
|
7
|
+
const getenv_1 = require("getenv");
|
|
8
|
+
function isNonInteractiveByDefault() {
|
|
9
|
+
return (0, getenv_1.boolish)('CI', false) || !process.stdin.isTTY;
|
|
10
|
+
}
|
|
6
11
|
exports.EasNonInteractiveAndJsonFlags = {
|
|
7
12
|
json: core_1.Flags.boolean({
|
|
8
13
|
description: 'Enable JSON output, non-JSON messages will be printed to stderr. Implies --non-interactive.',
|
|
9
14
|
}),
|
|
10
15
|
'non-interactive': core_1.Flags.boolean({
|
|
11
16
|
description: 'Run the command in non-interactive mode.',
|
|
17
|
+
default: () => Promise.resolve(isNonInteractiveByDefault()),
|
|
18
|
+
noCacheDefault: true,
|
|
12
19
|
}),
|
|
13
20
|
};
|
|
14
21
|
function resolveNonInteractiveAndJsonFlags(flags) {
|
|
@@ -53,6 +60,11 @@ exports.EASEnvironmentVariableScopeFlag = {
|
|
|
53
60
|
exports.EASNonInteractiveFlag = {
|
|
54
61
|
'non-interactive': core_1.Flags.boolean({
|
|
55
62
|
description: 'Run the command in non-interactive mode.',
|
|
63
|
+
// eslint-disable-next-line async-protect/async-suffix
|
|
64
|
+
default: async () => {
|
|
65
|
+
return isNonInteractiveByDefault();
|
|
66
|
+
},
|
|
67
|
+
noCacheDefault: true,
|
|
56
68
|
}),
|
|
57
69
|
};
|
|
58
70
|
exports.EasJsonOnlyFlag = {
|
|
@@ -16,6 +16,16 @@ async function fetchRawLogsForCustomJobAsync(job) {
|
|
|
16
16
|
return rawLogs;
|
|
17
17
|
}
|
|
18
18
|
async function fetchRawLogsForBuildJobAsync(state, job) {
|
|
19
|
+
// Prefer turtleJobRun logs, which contain JSONL-formatted step logs
|
|
20
|
+
// for both built-in and custom build steps
|
|
21
|
+
const turtleLogFileUrl = job.turtleJobRun?.logFileUrls?.[0];
|
|
22
|
+
if (turtleLogFileUrl) {
|
|
23
|
+
const response = await fetch(turtleLogFileUrl, {
|
|
24
|
+
method: 'GET',
|
|
25
|
+
});
|
|
26
|
+
return await response.text();
|
|
27
|
+
}
|
|
28
|
+
// Fall back to build logFiles
|
|
19
29
|
const buildId = job.outputs?.build_id;
|
|
20
30
|
if (!buildId) {
|
|
21
31
|
return null;
|
|
@@ -30,6 +40,5 @@ async function fetchRawLogsForBuildJobAsync(state, job) {
|
|
|
30
40
|
const response = await fetch(firstLogFileUrl, {
|
|
31
41
|
method: 'GET',
|
|
32
42
|
});
|
|
33
|
-
|
|
34
|
-
return rawLogs;
|
|
43
|
+
return await response.text();
|
|
35
44
|
}
|
|
@@ -74,15 +74,14 @@ function choiceFromWorkflowJob(job, index) {
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
function choicesFromWorkflowLogs(logs) {
|
|
77
|
-
return Array.from(logs.
|
|
78
|
-
.map(
|
|
79
|
-
const
|
|
80
|
-
const stepStatus = logLines?.filter((line) => line.marker === 'end-step')[0]?.result ?? '';
|
|
77
|
+
return Array.from(logs.values())
|
|
78
|
+
.map(({ key, label, logLines }) => {
|
|
79
|
+
const stepStatus = logLines?.filter((line) => line.marker === 'end-step' || line.marker === 'END_PHASE')[0]?.result ?? '';
|
|
81
80
|
return {
|
|
82
|
-
title: `${
|
|
83
|
-
name:
|
|
81
|
+
title: `${label} - ${stepStatus}`,
|
|
82
|
+
name: label,
|
|
84
83
|
status: stepStatus,
|
|
85
|
-
value:
|
|
84
|
+
value: key,
|
|
86
85
|
logLines,
|
|
87
86
|
};
|
|
88
87
|
})
|
|
@@ -123,19 +122,26 @@ async function fetchAndProcessLogsFromJobAsync(state, job) {
|
|
|
123
122
|
}
|
|
124
123
|
log_1.default.debug(`rawLogs = ${JSON.stringify(rawLogs, null, 2)}`);
|
|
125
124
|
const logs = new Map();
|
|
126
|
-
const logKeys = new Set();
|
|
127
125
|
rawLogs.split('\n').forEach((line, index) => {
|
|
128
126
|
log_1.default.debug(`line ${index} = ${JSON.stringify(line, null, 2)}`);
|
|
129
127
|
try {
|
|
130
128
|
const parsedLine = JSON.parse(line);
|
|
131
|
-
const { buildStepDisplayName,
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
logs.set(
|
|
129
|
+
const { buildStepDisplayName, buildStepId, phase, time, msg, result, marker, err } = parsedLine;
|
|
130
|
+
const stepKey = buildStepId ?? buildStepDisplayName ?? phase;
|
|
131
|
+
const stepLabel = buildStepDisplayName ?? buildStepId ?? phase;
|
|
132
|
+
if (stepKey && stepLabel) {
|
|
133
|
+
if (!logs.has(stepKey)) {
|
|
134
|
+
logs.set(stepKey, {
|
|
135
|
+
key: stepKey,
|
|
136
|
+
label: stepLabel,
|
|
137
|
+
logLines: [],
|
|
138
|
+
});
|
|
137
139
|
}
|
|
138
|
-
logs.get(
|
|
140
|
+
const logGroup = logs.get(stepKey);
|
|
141
|
+
if (buildStepDisplayName) {
|
|
142
|
+
logGroup.label = buildStepDisplayName;
|
|
143
|
+
}
|
|
144
|
+
logGroup.logLines.push({ time, msg, result, marker, err });
|
|
139
145
|
}
|
|
140
146
|
}
|
|
141
147
|
catch { }
|
|
@@ -212,7 +218,7 @@ async function infoForFailedWorkflowRunAsync(graphqlClient, workflowRun, maxLogL
|
|
|
212
218
|
statusValues.push({ label: '', value: '' });
|
|
213
219
|
statusValues.push({ label: ' Failed job', value: job.name });
|
|
214
220
|
if (steps.length > 0) {
|
|
215
|
-
const failedStep = steps.find(step => step.status === 'fail');
|
|
221
|
+
const failedStep = steps.find(step => step.status === 'fail' || step.status === 'failed');
|
|
216
222
|
if (failedStep) {
|
|
217
223
|
const logs = failedStep.logLines?.map(line => line.msg).slice(-logLinesToKeep) ?? [];
|
|
218
224
|
statusValues.push({ label: ' Failed step', value: failedStep.name });
|
|
@@ -6,6 +6,7 @@ export default class BuildDev extends EasCommand {
|
|
|
6
6
|
static flags: {
|
|
7
7
|
platform: import("@oclif/core/lib/interfaces").OptionFlag<Platform | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
8
|
profile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
'skip-build-if-not-found': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
};
|
|
10
11
|
static contextDefinition: {
|
|
11
12
|
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
@@ -34,6 +34,10 @@ class BuildDev extends EasCommand_1.default {
|
|
|
34
34
|
description: `Name of the build profile from eas.json. It must be a profile allowing to create emulator/simulator internal distribution dev client builds. The "${DEFAULT_EAS_BUILD_RUN_PROFILE_NAME}" build profile will be selected by default.`,
|
|
35
35
|
helpValue: 'PROFILE_NAME',
|
|
36
36
|
}),
|
|
37
|
+
'skip-build-if-not-found': core_1.Flags.boolean({
|
|
38
|
+
description: 'Skip build if no successful build with matching fingerprint is found.',
|
|
39
|
+
default: false,
|
|
40
|
+
}),
|
|
37
41
|
};
|
|
38
42
|
static contextDefinition = {
|
|
39
43
|
...this.ContextOptions.LoggedIn,
|
|
@@ -100,9 +104,13 @@ class BuildDev extends EasCommand_1.default {
|
|
|
100
104
|
return;
|
|
101
105
|
}
|
|
102
106
|
else {
|
|
103
|
-
log_1.default.warn('Artifacts for this build expired.
|
|
107
|
+
log_1.default.warn('Artifacts for this build expired.');
|
|
104
108
|
}
|
|
105
109
|
}
|
|
110
|
+
if (flags['skip-build-if-not-found']) {
|
|
111
|
+
log_1.default.log('No successful build with matching fingerprint found. Skipping build.');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
106
114
|
log_1.default.log('🚀 No successful build with matching fingerprint found. Starting a new build...');
|
|
107
115
|
const previousBuildsForSelectedProfile = await this.getBuildsAsync({
|
|
108
116
|
graphqlClient,
|
|
@@ -3,6 +3,7 @@ export default class MetadataPull extends EasCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
5
|
profile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
};
|
|
7
8
|
static contextDefinition: {
|
|
8
9
|
vcsClient: import("../../commandUtils/context/VcsClientContextField").default;
|
|
@@ -20,6 +20,10 @@ class MetadataPull extends EasCommand_1.default {
|
|
|
20
20
|
char: 'e',
|
|
21
21
|
description: 'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
|
|
22
22
|
}),
|
|
23
|
+
'non-interactive': core_1.Flags.boolean({
|
|
24
|
+
default: false,
|
|
25
|
+
description: 'Run the command in non-interactive mode.',
|
|
26
|
+
}),
|
|
23
27
|
};
|
|
24
28
|
static contextDefinition = {
|
|
25
29
|
...this.ContextOptions.ProjectConfig,
|
|
@@ -30,12 +34,12 @@ class MetadataPull extends EasCommand_1.default {
|
|
|
30
34
|
async runAsync() {
|
|
31
35
|
log_1.default.warn('EAS Metadata is in beta and subject to breaking changes.');
|
|
32
36
|
const { flags } = await this.parse(MetadataPull);
|
|
37
|
+
const nonInteractive = flags['non-interactive'];
|
|
33
38
|
const { loggedIn: { actor, graphqlClient }, privateProjectConfig: { exp, projectId, projectDir }, analytics, vcsClient, } = await this.getContextAsync(MetadataPull, {
|
|
34
|
-
nonInteractive
|
|
39
|
+
nonInteractive,
|
|
35
40
|
withServerSideEnvironment: null,
|
|
36
41
|
});
|
|
37
|
-
|
|
38
|
-
await (0, configure_1.ensureProjectConfiguredAsync)({ projectDir, nonInteractive: false, vcsClient });
|
|
42
|
+
await (0, configure_1.ensureProjectConfiguredAsync)({ projectDir, nonInteractive, vcsClient });
|
|
39
43
|
const submitProfiles = await (0, profiles_1.getProfilesAsync)({
|
|
40
44
|
type: 'submit',
|
|
41
45
|
easJsonAccessor: eas_json_1.EasJsonAccessor.fromProjectPath(projectDir),
|
|
@@ -53,7 +57,7 @@ class MetadataPull extends EasCommand_1.default {
|
|
|
53
57
|
user: actor,
|
|
54
58
|
graphqlClient,
|
|
55
59
|
analytics,
|
|
56
|
-
nonInteractive
|
|
60
|
+
nonInteractive,
|
|
57
61
|
vcsClient,
|
|
58
62
|
});
|
|
59
63
|
try {
|
|
@@ -63,6 +67,9 @@ class MetadataPull extends EasCommand_1.default {
|
|
|
63
67
|
credentialsCtx,
|
|
64
68
|
projectDir,
|
|
65
69
|
profile: submitProfile,
|
|
70
|
+
nonInteractive,
|
|
71
|
+
graphqlClient,
|
|
72
|
+
projectId,
|
|
66
73
|
});
|
|
67
74
|
const relativePath = path_1.default.relative(process.cwd(), filePath);
|
|
68
75
|
log_1.default.addNewLineIfNone();
|
|
@@ -3,6 +3,7 @@ export default class MetadataPush extends EasCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
5
|
profile: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
};
|
|
7
8
|
static contextDefinition: {
|
|
8
9
|
vcsClient: import("../../commandUtils/context/VcsClientContextField").default;
|
|
@@ -18,6 +18,10 @@ class MetadataPush extends EasCommand_1.default {
|
|
|
18
18
|
char: 'e',
|
|
19
19
|
description: 'Name of the submit profile from eas.json. Defaults to "production" if defined in eas.json.',
|
|
20
20
|
}),
|
|
21
|
+
'non-interactive': core_1.Flags.boolean({
|
|
22
|
+
default: false,
|
|
23
|
+
description: 'Run the command in non-interactive mode.',
|
|
24
|
+
}),
|
|
21
25
|
};
|
|
22
26
|
static contextDefinition = {
|
|
23
27
|
...this.ContextOptions.ProjectConfig,
|
|
@@ -28,12 +32,12 @@ class MetadataPush extends EasCommand_1.default {
|
|
|
28
32
|
async runAsync() {
|
|
29
33
|
log_1.default.warn('EAS Metadata is in beta and subject to breaking changes.');
|
|
30
34
|
const { flags } = await this.parse(MetadataPush);
|
|
35
|
+
const nonInteractive = flags['non-interactive'];
|
|
31
36
|
const { loggedIn: { actor, graphqlClient }, privateProjectConfig: { exp, projectId, projectDir }, analytics, vcsClient, } = await this.getContextAsync(MetadataPush, {
|
|
32
|
-
nonInteractive
|
|
37
|
+
nonInteractive,
|
|
33
38
|
withServerSideEnvironment: null,
|
|
34
39
|
});
|
|
35
|
-
|
|
36
|
-
await (0, configure_1.ensureProjectConfiguredAsync)({ projectDir, nonInteractive: false, vcsClient });
|
|
40
|
+
await (0, configure_1.ensureProjectConfiguredAsync)({ projectDir, nonInteractive, vcsClient });
|
|
37
41
|
const submitProfiles = await (0, profiles_1.getProfilesAsync)({
|
|
38
42
|
type: 'submit',
|
|
39
43
|
easJsonAccessor: eas_json_1.EasJsonAccessor.fromProjectPath(projectDir),
|
|
@@ -51,7 +55,7 @@ class MetadataPush extends EasCommand_1.default {
|
|
|
51
55
|
user: actor,
|
|
52
56
|
graphqlClient,
|
|
53
57
|
analytics,
|
|
54
|
-
nonInteractive
|
|
58
|
+
nonInteractive,
|
|
55
59
|
vcsClient,
|
|
56
60
|
});
|
|
57
61
|
try {
|
|
@@ -61,6 +65,9 @@ class MetadataPush extends EasCommand_1.default {
|
|
|
61
65
|
credentialsCtx,
|
|
62
66
|
projectDir,
|
|
63
67
|
profile: submitProfile,
|
|
68
|
+
nonInteractive,
|
|
69
|
+
graphqlClient,
|
|
70
|
+
projectId,
|
|
64
71
|
});
|
|
65
72
|
log_1.default.addNewLineIfNone();
|
|
66
73
|
log_1.default.log(`🎉 Store configuration is synced with the app stores.
|
|
@@ -44,6 +44,9 @@ class Onboarding extends EasCommand_1.default {
|
|
|
44
44
|
if (actor.__typename === 'Robot') {
|
|
45
45
|
throw new Error('This command is not available for robot users. Make sure you are not using a robot token and try again.');
|
|
46
46
|
}
|
|
47
|
+
if (actor.__typename === 'PartnerActor') {
|
|
48
|
+
throw new Error('This command is not available for partner actors. Make sure you are using a user token and try again.');
|
|
49
|
+
}
|
|
47
50
|
if (!actor.preferences.onboarding) {
|
|
48
51
|
throw new Error('This command can only be run as part of the onboarding process started on the Expo website. Visit https://expo.new to start a new project.');
|
|
49
52
|
}
|
|
@@ -8,14 +8,14 @@ const stateMachine_1 = require("../../commandUtils/workflow/stateMachine");
|
|
|
8
8
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
9
9
|
const json_1 = require("../../utils/json");
|
|
10
10
|
function printLogsForAllSteps(logs) {
|
|
11
|
-
[...logs.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
log_1.default.log(`Step: ${step}`);
|
|
15
|
-
logLines.forEach(line => {
|
|
16
|
-
log_1.default.log(` ${line.time} ${line.msg}`);
|
|
17
|
-
});
|
|
11
|
+
[...logs.values()].forEach(({ label, logLines }) => {
|
|
12
|
+
if (logLines.length === 0) {
|
|
13
|
+
return;
|
|
18
14
|
}
|
|
15
|
+
log_1.default.log(`Step: ${label}`);
|
|
16
|
+
logLines.forEach(line => {
|
|
17
|
+
log_1.default.log(` ${line.time} ${line.msg}`);
|
|
18
|
+
});
|
|
19
19
|
log_1.default.addNewLineIfNone();
|
|
20
20
|
});
|
|
21
21
|
}
|
|
@@ -68,7 +68,7 @@ class WorkflowLogView extends EasCommand_1.default {
|
|
|
68
68
|
if (allSteps) {
|
|
69
69
|
if (logs) {
|
|
70
70
|
if (flags.json) {
|
|
71
|
-
(0, json_1.printJsonOnlyOutput)(Object.fromEntries(logs));
|
|
71
|
+
(0, json_1.printJsonOnlyOutput)(Object.fromEntries(Array.from(logs.entries()).map(([key, value]) => [key, value.logLines])));
|
|
72
72
|
}
|
|
73
73
|
else {
|
|
74
74
|
printLogsForAllSteps(logs);
|
|
@@ -77,15 +77,15 @@ class WorkflowLogView extends EasCommand_1.default {
|
|
|
77
77
|
}
|
|
78
78
|
else {
|
|
79
79
|
const selectedStep = finalSelectionState?.step;
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
80
|
+
const logGroup = logs?.get(selectedStep);
|
|
81
|
+
if (logGroup) {
|
|
82
82
|
if (flags.json) {
|
|
83
83
|
const output = {};
|
|
84
|
-
output[selectedStep] = logLines
|
|
84
|
+
output[selectedStep] = logGroup.logLines;
|
|
85
85
|
(0, json_1.printJsonOnlyOutput)(output);
|
|
86
86
|
}
|
|
87
87
|
else {
|
|
88
|
-
logLines.forEach(line => {
|
|
88
|
+
logGroup.logLines.forEach(line => {
|
|
89
89
|
log_1.default.log(` ${line.time} ${line.msg}`);
|
|
90
90
|
});
|
|
91
91
|
}
|