eas-cli 3.8.0 → 3.9.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 +79 -54
- package/build/build/build.d.ts +1 -0
- package/build/build/build.js +21 -20
- package/build/build/errors.d.ts +15 -0
- package/build/build/errors.js +25 -0
- package/build/build/runBuildAndSubmit.js +1 -1
- package/build/commandUtils/EasCommand.d.ts +11 -10
- package/build/commandUtils/EasCommand.js +20 -0
- package/build/commandUtils/context/DynamicProjectConfigContextField.js +1 -1
- package/build/commandUtils/context/contextUtils/findProjectDirAndVerifyProjectSetupAsync.js +1 -1
- package/build/commandUtils/errors.d.ts +3 -0
- package/build/commandUtils/errors.js +11 -0
- package/build/commands/build/index.js +1 -1
- package/build/commands/build/internal.js +3 -0
- package/build/commands/build/resign.js +1 -1
- package/build/commands/build/version/set.js +1 -1
- package/build/commands/build/version/sync.js +1 -1
- package/build/commands/channel/rollout.d.ts +0 -2
- package/build/commands/channel/rollout.js +44 -18
- package/build/commands/config.js +1 -1
- package/build/commands/metadata/lint.js +1 -1
- package/build/commands/metadata/pull.js +1 -1
- package/build/commands/metadata/push.js +1 -1
- package/build/commands/submit.js +1 -1
- package/build/commands/update/index.d.ts +0 -2
- package/build/commands/update/index.js +32 -137
- package/build/commands/update/republish.js +1 -1
- package/build/commands/update/roll-back-to-embedded.d.ts +22 -0
- package/build/commands/update/roll-back-to-embedded.js +253 -0
- package/build/credentials/ios/IosCredentialsProvider.js +1 -1
- package/build/credentials/manager/SelectBuildProfileFromEasJson.js +1 -1
- package/build/graphql/generated.d.ts +139 -22
- package/build/graphql/generated.js +0 -2
- package/build/graphql/types/Update.js +1 -0
- package/build/project/customBuildConfig.js +19 -4
- package/build/project/publish.d.ts +31 -0
- package/build/project/publish.js +147 -2
- package/build/update/configure.js +1 -1
- package/build/update/utils.d.ts +2 -0
- package/build/update/utils.js +30 -13
- package/build/utils/code-signing.d.ts +4 -3
- package/build/utils/code-signing.js +23 -5
- package/build/utils/expoCli.js +3 -2
- package/build/utils/expodash/areSetsEqual.d.ts +1 -0
- package/build/utils/expodash/areSetsEqual.js +6 -0
- package/oclif.manifest.json +1 -1
- package/package.json +4 -3
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _a;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.defaultPublishPlatforms = void 0;
|
|
5
4
|
const tslib_1 = require("tslib");
|
|
6
|
-
const config_plugins_1 = require("@expo/config-plugins");
|
|
7
|
-
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
8
5
|
const core_1 = require("@oclif/core");
|
|
9
|
-
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
10
6
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
11
7
|
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
12
8
|
const queries_1 = require("../../branch/queries");
|
|
13
|
-
const utils_1 = require("../../branch/utils");
|
|
14
9
|
const url_1 = require("../../build/utils/url");
|
|
15
10
|
const queries_2 = require("../../channel/queries");
|
|
16
11
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
@@ -21,42 +16,21 @@ const generated_1 = require("../../graphql/generated");
|
|
|
21
16
|
const PublishMutation_1 = require("../../graphql/mutations/PublishMutation");
|
|
22
17
|
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
23
18
|
const ora_1 = require("../../ora");
|
|
24
|
-
const platform_1 = require("../../platform");
|
|
25
19
|
const projectUtils_1 = require("../../project/projectUtils");
|
|
26
20
|
const publish_1 = require("../../project/publish");
|
|
27
|
-
const workflow_1 = require("../../project/workflow");
|
|
28
|
-
const prompts_1 = require("../../prompts");
|
|
29
21
|
const configure_1 = require("../../update/configure");
|
|
30
|
-
const
|
|
31
|
-
const utils_2 = require("../../update/utils");
|
|
22
|
+
const utils_1 = require("../../update/utils");
|
|
32
23
|
const code_signing_1 = require("../../utils/code-signing");
|
|
33
24
|
const uniqBy_1 = tslib_1.__importDefault(require("../../utils/expodash/uniqBy"));
|
|
34
25
|
const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
|
|
35
26
|
const json_1 = require("../../utils/json");
|
|
36
27
|
const statuspageService_1 = require("../../utils/statuspageService");
|
|
37
28
|
const vcs_1 = require("../../vcs");
|
|
38
|
-
exports.defaultPublishPlatforms = ['android', 'ios'];
|
|
39
|
-
function getRequestedPlatform(platform) {
|
|
40
|
-
switch (platform) {
|
|
41
|
-
case 'android':
|
|
42
|
-
return platform_1.RequestedPlatform.Android;
|
|
43
|
-
case 'ios':
|
|
44
|
-
return platform_1.RequestedPlatform.Ios;
|
|
45
|
-
case 'web':
|
|
46
|
-
return null;
|
|
47
|
-
case 'all':
|
|
48
|
-
return platform_1.RequestedPlatform.All;
|
|
49
|
-
default:
|
|
50
|
-
throw new Error(`Unsupported platform: ${platform}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
29
|
class UpdatePublish extends EasCommand_1.default {
|
|
54
30
|
async runAsync() {
|
|
55
|
-
var _b, _c;
|
|
56
31
|
const { flags: rawFlags } = await this.parse(UpdatePublish);
|
|
57
32
|
const paginatedQueryOptions = (0, pagination_1.getPaginatedQueryOptions)(rawFlags);
|
|
58
|
-
|
|
59
|
-
let branchName = this.sanitizeFlags(rawFlags).branchName;
|
|
33
|
+
const { auto: autoFlag, platform: platformFlag, channelName: channelNameArg, updateMessage: updateMessageArg, inputDir, skipBundler, privateKeyPath, json: jsonFlag, nonInteractive, branchName: branchNameArg, } = this.sanitizeFlags(rawFlags);
|
|
60
34
|
const { getDynamicProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdatePublish, {
|
|
61
35
|
nonInteractive,
|
|
62
36
|
});
|
|
@@ -66,76 +40,33 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
66
40
|
const { exp: expPossiblyWithoutEasUpdateConfigured, projectId, projectDir, } = await getDynamicProjectConfigAsync({
|
|
67
41
|
isPublicConfig: true,
|
|
68
42
|
});
|
|
69
|
-
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
|
70
|
-
isPublicConfig: false,
|
|
71
|
-
});
|
|
72
43
|
await (0, statuspageService_1.maybeWarnAboutEasOutagesAsync)(graphqlClient, [generated_1.StatuspageServiceName.EasUpdate]);
|
|
73
44
|
await (0, configure_1.ensureEASUpdateIsConfiguredAsync)(graphqlClient, {
|
|
74
45
|
exp: expPossiblyWithoutEasUpdateConfigured,
|
|
75
|
-
platform: getRequestedPlatform(platformFlag),
|
|
46
|
+
platform: (0, publish_1.getRequestedPlatform)(platformFlag),
|
|
76
47
|
projectDir,
|
|
77
48
|
projectId,
|
|
78
49
|
});
|
|
79
|
-
const { exp } = await getDynamicProjectConfigAsync({});
|
|
50
|
+
const { exp } = await getDynamicProjectConfigAsync({ isPublicConfig: true });
|
|
51
|
+
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
|
52
|
+
isPublicConfig: false,
|
|
53
|
+
});
|
|
80
54
|
const codeSigningInfo = await (0, code_signing_1.getCodeSigningInfoAsync)(expPrivate, privateKeyPath);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const branch = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
|
|
98
|
-
projectId,
|
|
99
|
-
promptTitle: `Which branch would you like to publish on?`,
|
|
100
|
-
displayTextForListItem: updateBranch => ({
|
|
101
|
-
title: `${updateBranch.name} ${chalk_1.default.grey(`- current update: ${(0, utils_2.formatUpdateMessage)(updateBranch.updates[0])}`)}`,
|
|
102
|
-
}),
|
|
103
|
-
paginatedQueryOptions,
|
|
104
|
-
});
|
|
105
|
-
branchName = branch.name;
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
// unable to select a branch (network error or no branches for project)
|
|
109
|
-
({ name: branchName } = await (0, prompts_1.promptAsync)({
|
|
110
|
-
type: 'text',
|
|
111
|
-
name: 'name',
|
|
112
|
-
message: 'No branches found. Provide a branch name:',
|
|
113
|
-
initial: await (0, utils_1.getDefaultBranchNameAsync)(),
|
|
114
|
-
validate: value => (value ? true : 'Branch name may not be empty.'),
|
|
115
|
-
}));
|
|
116
|
-
}
|
|
117
|
-
(0, assert_1.default)(branchName, 'Branch name must be specified.');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (!updateMessage && autoFlag) {
|
|
121
|
-
updateMessage = (_b = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _b === void 0 ? void 0 : _b.trim();
|
|
122
|
-
}
|
|
123
|
-
if (!updateMessage) {
|
|
124
|
-
if (nonInteractive) {
|
|
125
|
-
throw new Error('Must supply --message or use --auto when in non-interactive mode');
|
|
126
|
-
}
|
|
127
|
-
const validationMessage = 'publish message may not be empty.';
|
|
128
|
-
if (jsonFlag) {
|
|
129
|
-
throw new Error(validationMessage);
|
|
130
|
-
}
|
|
131
|
-
({ updateMessage } = await (0, prompts_1.promptAsync)({
|
|
132
|
-
type: 'text',
|
|
133
|
-
name: 'updateMessage',
|
|
134
|
-
message: `Provide an update message.`,
|
|
135
|
-
initial: (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim(),
|
|
136
|
-
validate: (value) => (value ? true : validationMessage),
|
|
137
|
-
}));
|
|
138
|
-
}
|
|
55
|
+
const branchName = await (0, publish_1.getBranchNameForCommandAsync)({
|
|
56
|
+
graphqlClient,
|
|
57
|
+
projectId,
|
|
58
|
+
channelNameArg,
|
|
59
|
+
branchNameArg,
|
|
60
|
+
autoFlag,
|
|
61
|
+
nonInteractive,
|
|
62
|
+
paginatedQueryOptions,
|
|
63
|
+
});
|
|
64
|
+
const updateMessage = await (0, publish_1.getUpdateMessageForCommandAsync)({
|
|
65
|
+
updateMessageArg,
|
|
66
|
+
autoFlag,
|
|
67
|
+
nonInteractive,
|
|
68
|
+
jsonFlag,
|
|
69
|
+
});
|
|
139
70
|
// build bundle and upload assets for a new publish
|
|
140
71
|
if (!skipBundler) {
|
|
141
72
|
const bundleSpinner = (0, ora_1.ora)().start('Exporting...');
|
|
@@ -154,6 +85,7 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
154
85
|
let unsortedUpdateInfoGroups = {};
|
|
155
86
|
let uploadedAssetCount = 0;
|
|
156
87
|
let assetLimitPerUpdateGroup = 0;
|
|
88
|
+
let realizedPlatforms = [];
|
|
157
89
|
try {
|
|
158
90
|
const collectedAssets = await (0, publish_1.collectAssetsAsync)(distRoot);
|
|
159
91
|
const assets = (0, publish_1.filterExportedPlatformsByFlag)(collectedAssets, platformFlag);
|
|
@@ -190,20 +122,8 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
190
122
|
assetSpinner.fail('Failed to upload');
|
|
191
123
|
throw e;
|
|
192
124
|
}
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
log_1.default.warn('Update message exceeds the allowed 1024 character limit. Truncating message...');
|
|
196
|
-
}
|
|
197
|
-
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, realizedPlatforms, projectDir);
|
|
198
|
-
const runtimeToPlatformMapping = [];
|
|
199
|
-
for (const runtime of runtimeVersions) {
|
|
200
|
-
const platforms = runtimeVersions
|
|
201
|
-
.filter(({ runtimeVersion }) => runtimeVersion === runtime.runtimeVersion)
|
|
202
|
-
.map(({ platform }) => platform);
|
|
203
|
-
if (!runtimeToPlatformMapping.find(item => item.runtimeVersion === runtime.runtimeVersion)) {
|
|
204
|
-
runtimeToPlatformMapping.push({ runtimeVersion: runtime.runtimeVersion, platforms });
|
|
205
|
-
}
|
|
206
|
-
}
|
|
125
|
+
const runtimeVersions = await (0, publish_1.getRuntimeVersionObjectAsync)(exp, realizedPlatforms, projectDir);
|
|
126
|
+
const runtimeToPlatformMapping = (0, publish_1.getRuntimeToPlatformMappingFromRuntimeVersions)(runtimeVersions);
|
|
207
127
|
const { branchId, createdBranch } = await (0, queries_1.ensureBranchExistsAsync)(graphqlClient, {
|
|
208
128
|
appId: projectId,
|
|
209
129
|
branchName,
|
|
@@ -229,7 +149,7 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
229
149
|
branchId,
|
|
230
150
|
updateInfoGroup: localUpdateInfoGroup,
|
|
231
151
|
runtimeVersion,
|
|
232
|
-
message:
|
|
152
|
+
message: updateMessage,
|
|
233
153
|
gitCommitHash,
|
|
234
154
|
isGitWorkingTreeDirty,
|
|
235
155
|
awaitingCodeSigningInfo: !!codeSigningInfo,
|
|
@@ -243,7 +163,7 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
243
163
|
log_1.default.log('🔒 Signing updates');
|
|
244
164
|
const updatesTemp = [...newUpdates];
|
|
245
165
|
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
|
|
246
|
-
const newUpdates = updatesTemp.splice(0, Object.keys(updateGroup.updateInfoGroup).length);
|
|
166
|
+
const newUpdates = updatesTemp.splice(0, Object.keys((0, nullthrows_1.default)(updateGroup.updateInfoGroup)).length);
|
|
247
167
|
return {
|
|
248
168
|
updateGroup,
|
|
249
169
|
newUpdates,
|
|
@@ -256,8 +176,8 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
256
176
|
headers: { accept: 'multipart/mixed' },
|
|
257
177
|
});
|
|
258
178
|
const manifestBody = (0, nullthrows_1.default)(await (0, code_signing_1.getManifestBodyAsync)(response));
|
|
259
|
-
(0, code_signing_1.checkManifestBodyAgainstUpdateInfoGroup)(manifestBody, (0, nullthrows_1.default)(updateGroup.updateInfoGroup[newUpdate.platform]));
|
|
260
|
-
const manifestSignature = (0, code_signing_1.
|
|
179
|
+
(0, code_signing_1.checkManifestBodyAgainstUpdateInfoGroup)(manifestBody, (0, nullthrows_1.default)((0, nullthrows_1.default)(updateGroup.updateInfoGroup)[newUpdate.platform]));
|
|
180
|
+
const manifestSignature = (0, code_signing_1.signBody)(manifestBody, codeSigningInfo);
|
|
261
181
|
await PublishMutation_1.PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
|
262
182
|
alg: codeSigningInfo.codeSigningMetadata.alg,
|
|
263
183
|
keyid: codeSigningInfo.codeSigningMetadata.keyid,
|
|
@@ -273,7 +193,7 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
273
193
|
throw e;
|
|
274
194
|
}
|
|
275
195
|
if (jsonFlag) {
|
|
276
|
-
(0, json_1.printJsonOnlyOutput)((0,
|
|
196
|
+
(0, json_1.printJsonOnlyOutput)((0, utils_1.getUpdateGroupJsonInfo)(newUpdates));
|
|
277
197
|
}
|
|
278
198
|
else {
|
|
279
199
|
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
|
@@ -303,7 +223,7 @@ class UpdatePublish extends EasCommand_1.default {
|
|
|
303
223
|
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
|
|
304
224
|
: []),
|
|
305
225
|
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
|
|
306
|
-
{ label: 'Message', value:
|
|
226
|
+
{ label: 'Message', value: updateMessage },
|
|
307
227
|
...(gitCommitHash
|
|
308
228
|
? [
|
|
309
229
|
{
|
|
@@ -396,7 +316,7 @@ UpdatePublish.flags = {
|
|
|
396
316
|
char: 'p',
|
|
397
317
|
options: [
|
|
398
318
|
// TODO: Add web when it's fully supported
|
|
399
|
-
...
|
|
319
|
+
...publish_1.defaultPublishPlatforms,
|
|
400
320
|
'all',
|
|
401
321
|
],
|
|
402
322
|
default: 'all',
|
|
@@ -416,28 +336,3 @@ UpdatePublish.contextDefinition = {
|
|
|
416
336
|
..._a.ContextOptions.DynamicProjectConfig,
|
|
417
337
|
..._a.ContextOptions.LoggedIn,
|
|
418
338
|
};
|
|
419
|
-
/** Get runtime versions grouped by platform. Runtime version is always `null` on web where the platform is always backwards compatible. */
|
|
420
|
-
async function getRuntimeVersionObjectAsync(exp, platforms, projectDir) {
|
|
421
|
-
var _b, _c;
|
|
422
|
-
for (const platform of platforms) {
|
|
423
|
-
if (platform === 'web') {
|
|
424
|
-
continue;
|
|
425
|
-
}
|
|
426
|
-
const isPolicy = typeof ((_c = (_b = exp[platform]) === null || _b === void 0 ? void 0 : _b.runtimeVersion) !== null && _c !== void 0 ? _c : exp.runtimeVersion) === 'object';
|
|
427
|
-
if (isPolicy) {
|
|
428
|
-
const isManaged = (await (0, workflow_1.resolveWorkflowAsync)(projectDir, platform)) === eas_build_job_1.Workflow.MANAGED;
|
|
429
|
-
if (!isManaged) {
|
|
430
|
-
throw new Error('Runtime version policies are only supported in the managed workflow. In the bare workflow, runtime version needs to be set manually.');
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return [...new Set(platforms)].map(platform => {
|
|
435
|
-
if (platform === 'web') {
|
|
436
|
-
return { platform: 'web', runtimeVersion: 'UNVERSIONED' };
|
|
437
|
-
}
|
|
438
|
-
return {
|
|
439
|
-
platform,
|
|
440
|
-
runtimeVersion: (0, nullthrows_1.default)(config_plugins_1.Updates.getRuntimeVersion(exp, platform), `Unable to determine runtime version for ${platform_1.requestedPlatformDisplayNames[platform]}. ${(0, log_1.learnMore)('https://docs.expo.dev/eas-update/runtime-versions/')}`),
|
|
441
|
-
};
|
|
442
|
-
});
|
|
443
|
-
}
|
|
@@ -131,7 +131,7 @@ class UpdateRepublish extends EasCommand_1.default {
|
|
|
131
131
|
}
|
|
132
132
|
exports.default = UpdateRepublish;
|
|
133
133
|
_a = UpdateRepublish;
|
|
134
|
-
UpdateRepublish.description = '
|
|
134
|
+
UpdateRepublish.description = 'roll back to an existing update';
|
|
135
135
|
UpdateRepublish.flags = {
|
|
136
136
|
channel: core_1.Flags.string({
|
|
137
137
|
description: 'Channel name to select an update to republish from',
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
export default class UpdateRollBackToEmbedded extends EasCommand {
|
|
3
|
+
static hidden: boolean;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
branch: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
9
|
+
channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
10
|
+
message: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
11
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<string>;
|
|
12
|
+
auto: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
'private-key-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
14
|
+
};
|
|
15
|
+
static contextDefinition: {
|
|
16
|
+
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
17
|
+
getDynamicProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicProjectConfigContextField;
|
|
18
|
+
};
|
|
19
|
+
runAsync(): Promise<void>;
|
|
20
|
+
private publishRollbacksAsync;
|
|
21
|
+
private sanitizeFlags;
|
|
22
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var _a;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
8
|
+
const queries_1 = require("../../branch/queries");
|
|
9
|
+
const url_1 = require("../../build/utils/url");
|
|
10
|
+
const queries_2 = require("../../channel/queries");
|
|
11
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
12
|
+
const flags_1 = require("../../commandUtils/flags");
|
|
13
|
+
const pagination_1 = require("../../commandUtils/pagination");
|
|
14
|
+
const fetch_1 = tslib_1.__importDefault(require("../../fetch"));
|
|
15
|
+
const generated_1 = require("../../graphql/generated");
|
|
16
|
+
const PublishMutation_1 = require("../../graphql/mutations/PublishMutation");
|
|
17
|
+
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
18
|
+
const ora_1 = require("../../ora");
|
|
19
|
+
const projectUtils_1 = require("../../project/projectUtils");
|
|
20
|
+
const publish_1 = require("../../project/publish");
|
|
21
|
+
const configure_1 = require("../../update/configure");
|
|
22
|
+
const utils_1 = require("../../update/utils");
|
|
23
|
+
const code_signing_1 = require("../../utils/code-signing");
|
|
24
|
+
const uniqBy_1 = tslib_1.__importDefault(require("../../utils/expodash/uniqBy"));
|
|
25
|
+
const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
|
|
26
|
+
const json_1 = require("../../utils/json");
|
|
27
|
+
const statuspageService_1 = require("../../utils/statuspageService");
|
|
28
|
+
const vcs_1 = require("../../vcs");
|
|
29
|
+
class UpdateRollBackToEmbedded extends EasCommand_1.default {
|
|
30
|
+
async runAsync() {
|
|
31
|
+
const { flags: rawFlags } = await this.parse(UpdateRollBackToEmbedded);
|
|
32
|
+
const paginatedQueryOptions = (0, pagination_1.getPaginatedQueryOptions)(rawFlags);
|
|
33
|
+
const { auto: autoFlag, platform: platformFlag, channelName: channelNameArg, updateMessage: updateMessageArg, privateKeyPath, json: jsonFlag, nonInteractive, branchName: branchNameArg, } = this.sanitizeFlags(rawFlags);
|
|
34
|
+
const { getDynamicProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateRollBackToEmbedded, {
|
|
35
|
+
nonInteractive,
|
|
36
|
+
});
|
|
37
|
+
if (jsonFlag) {
|
|
38
|
+
(0, json_1.enableJsonOutput)();
|
|
39
|
+
}
|
|
40
|
+
const { exp: expPossiblyWithoutEasUpdateConfigured, projectId, projectDir, } = await getDynamicProjectConfigAsync({
|
|
41
|
+
isPublicConfig: true,
|
|
42
|
+
});
|
|
43
|
+
await (0, statuspageService_1.maybeWarnAboutEasOutagesAsync)(graphqlClient, [generated_1.StatuspageServiceName.EasUpdate]);
|
|
44
|
+
await (0, configure_1.ensureEASUpdateIsConfiguredAsync)(graphqlClient, {
|
|
45
|
+
exp: expPossiblyWithoutEasUpdateConfigured,
|
|
46
|
+
platform: (0, publish_1.getRequestedPlatform)(platformFlag),
|
|
47
|
+
projectDir,
|
|
48
|
+
projectId,
|
|
49
|
+
});
|
|
50
|
+
const { exp } = await getDynamicProjectConfigAsync({ isPublicConfig: true });
|
|
51
|
+
const { exp: expPrivate } = await getDynamicProjectConfigAsync({
|
|
52
|
+
isPublicConfig: false,
|
|
53
|
+
});
|
|
54
|
+
const codeSigningInfo = await (0, code_signing_1.getCodeSigningInfoAsync)(expPrivate, privateKeyPath);
|
|
55
|
+
const branchName = await (0, publish_1.getBranchNameForCommandAsync)({
|
|
56
|
+
graphqlClient,
|
|
57
|
+
projectId,
|
|
58
|
+
channelNameArg,
|
|
59
|
+
branchNameArg,
|
|
60
|
+
autoFlag,
|
|
61
|
+
nonInteractive,
|
|
62
|
+
paginatedQueryOptions,
|
|
63
|
+
});
|
|
64
|
+
const updateMessage = await (0, publish_1.getUpdateMessageForCommandAsync)({
|
|
65
|
+
updateMessageArg,
|
|
66
|
+
autoFlag,
|
|
67
|
+
nonInteractive,
|
|
68
|
+
jsonFlag,
|
|
69
|
+
});
|
|
70
|
+
const realizedPlatforms = platformFlag === 'all' ? publish_1.defaultPublishPlatforms : [platformFlag];
|
|
71
|
+
const { branchId, createdBranch } = await (0, queries_1.ensureBranchExistsAsync)(graphqlClient, {
|
|
72
|
+
appId: projectId,
|
|
73
|
+
branchName,
|
|
74
|
+
});
|
|
75
|
+
if (createdBranch) {
|
|
76
|
+
await (0, queries_2.ensureChannelExistsAsync)(graphqlClient, {
|
|
77
|
+
appId: projectId,
|
|
78
|
+
branchId,
|
|
79
|
+
channelName: branchName,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
log_1.default.withTick(`Channel: ${chalk_1.default.bold(branchName)} pointed at branch: ${chalk_1.default.bold(branchName)}`);
|
|
83
|
+
const vcsClient = (0, vcs_1.getVcsClient)();
|
|
84
|
+
const gitCommitHash = await vcsClient.getCommitHashAsync();
|
|
85
|
+
const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
|
|
86
|
+
const runtimeVersions = await (0, publish_1.getRuntimeVersionObjectAsync)(exp, realizedPlatforms, projectDir);
|
|
87
|
+
let newUpdates;
|
|
88
|
+
const publishSpinner = (0, ora_1.ora)('Publishing...').start();
|
|
89
|
+
try {
|
|
90
|
+
newUpdates = await this.publishRollbacksAsync({
|
|
91
|
+
graphqlClient,
|
|
92
|
+
isGitWorkingTreeDirty,
|
|
93
|
+
gitCommitHash,
|
|
94
|
+
updateMessage,
|
|
95
|
+
branchId,
|
|
96
|
+
codeSigningInfo,
|
|
97
|
+
runtimeVersions,
|
|
98
|
+
realizedPlatforms,
|
|
99
|
+
});
|
|
100
|
+
publishSpinner.succeed('Published!');
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
publishSpinner.fail('Failed to publish updates');
|
|
104
|
+
throw e;
|
|
105
|
+
}
|
|
106
|
+
if (jsonFlag) {
|
|
107
|
+
(0, json_1.printJsonOnlyOutput)((0, utils_1.getUpdateGroupJsonInfo)(newUpdates));
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
|
111
|
+
log_1.default.addNewLineIfNone();
|
|
112
|
+
log_1.default.log('👉 Since multiple runtime versions are defined, multiple update groups have been published.');
|
|
113
|
+
}
|
|
114
|
+
log_1.default.addNewLineIfNone();
|
|
115
|
+
for (const runtime of (0, uniqBy_1.default)(runtimeVersions, version => version.runtimeVersion)) {
|
|
116
|
+
const newUpdatesForRuntimeVersion = newUpdates.filter(update => update.runtimeVersion === runtime.runtimeVersion);
|
|
117
|
+
if (newUpdatesForRuntimeVersion.length === 0) {
|
|
118
|
+
throw new Error(`Publish response is missing updates with runtime ${runtime.runtimeVersion}.`);
|
|
119
|
+
}
|
|
120
|
+
const platforms = newUpdatesForRuntimeVersion.map(update => update.platform);
|
|
121
|
+
const newAndroidUpdate = newUpdatesForRuntimeVersion.find(update => update.platform === 'android');
|
|
122
|
+
const newIosUpdate = newUpdatesForRuntimeVersion.find(update => update.platform === 'ios');
|
|
123
|
+
const updateGroupId = newUpdatesForRuntimeVersion[0].group;
|
|
124
|
+
const projectName = exp.slug;
|
|
125
|
+
const accountName = (await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId)).name;
|
|
126
|
+
const updateGroupUrl = (0, url_1.getUpdateGroupUrl)(accountName, projectName, updateGroupId);
|
|
127
|
+
const updateGroupLink = (0, log_1.link)(updateGroupUrl, { dim: false });
|
|
128
|
+
log_1.default.log((0, formatFields_1.default)([
|
|
129
|
+
{ label: 'Branch', value: branchName },
|
|
130
|
+
{ label: 'Runtime version', value: runtime.runtimeVersion },
|
|
131
|
+
{ label: 'Platform', value: platforms.join(', ') },
|
|
132
|
+
{ label: 'Update group ID', value: updateGroupId },
|
|
133
|
+
...(newAndroidUpdate
|
|
134
|
+
? [{ label: 'Android update ID', value: newAndroidUpdate.id }]
|
|
135
|
+
: []),
|
|
136
|
+
...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
|
|
137
|
+
{ label: 'Message', value: updateMessage },
|
|
138
|
+
...(gitCommitHash
|
|
139
|
+
? [
|
|
140
|
+
{
|
|
141
|
+
label: 'Commit',
|
|
142
|
+
value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
|
|
143
|
+
},
|
|
144
|
+
]
|
|
145
|
+
: []),
|
|
146
|
+
{ label: 'Website link', value: updateGroupLink },
|
|
147
|
+
]));
|
|
148
|
+
log_1.default.addNewLineIfNone();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async publishRollbacksAsync({ graphqlClient, isGitWorkingTreeDirty, gitCommitHash, updateMessage, branchId, codeSigningInfo, runtimeVersions, realizedPlatforms, }) {
|
|
153
|
+
const runtimeToPlatformMapping = (0, publish_1.getRuntimeToPlatformMappingFromRuntimeVersions)(runtimeVersions);
|
|
154
|
+
const rollbackInfoGroups = Object.fromEntries(realizedPlatforms.map(platform => [platform, true]));
|
|
155
|
+
// Sort the updates into different groups based on their platform specific runtime versions
|
|
156
|
+
const updateGroups = runtimeToPlatformMapping.map(({ runtimeVersion, platforms }) => {
|
|
157
|
+
const localRollbackInfoGroup = Object.fromEntries(platforms.map(platform => [platform, rollbackInfoGroups[platform]]));
|
|
158
|
+
return {
|
|
159
|
+
branchId,
|
|
160
|
+
rollBackToEmbeddedInfoGroup: localRollbackInfoGroup,
|
|
161
|
+
runtimeVersion,
|
|
162
|
+
message: updateMessage,
|
|
163
|
+
gitCommitHash,
|
|
164
|
+
isGitWorkingTreeDirty,
|
|
165
|
+
awaitingCodeSigningInfo: !!codeSigningInfo,
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
const newUpdates = await PublishMutation_1.PublishMutation.publishUpdateGroupAsync(graphqlClient, updateGroups);
|
|
169
|
+
if (codeSigningInfo) {
|
|
170
|
+
log_1.default.log('🔒 Signing roll back');
|
|
171
|
+
const updatesTemp = [...newUpdates];
|
|
172
|
+
const updateGroupsAndTheirUpdates = updateGroups.map(updateGroup => {
|
|
173
|
+
const newUpdates = updatesTemp.splice(0, Object.keys((0, nullthrows_1.default)(updateGroup.rollBackToEmbeddedInfoGroup)).length);
|
|
174
|
+
return {
|
|
175
|
+
updateGroup,
|
|
176
|
+
newUpdates,
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
await Promise.all(updateGroupsAndTheirUpdates.map(async ({ newUpdates }) => {
|
|
180
|
+
await Promise.all(newUpdates.map(async (newUpdate) => {
|
|
181
|
+
const response = await (0, fetch_1.default)(newUpdate.manifestPermalink, {
|
|
182
|
+
method: 'GET',
|
|
183
|
+
headers: { accept: 'multipart/mixed' },
|
|
184
|
+
});
|
|
185
|
+
const directiveBody = (0, nullthrows_1.default)(await (0, code_signing_1.getDirectiveBodyAsync)(response));
|
|
186
|
+
(0, code_signing_1.checkDirectiveBodyAgainstUpdateInfoGroup)(directiveBody);
|
|
187
|
+
const directiveSignature = (0, code_signing_1.signBody)(directiveBody, codeSigningInfo);
|
|
188
|
+
await PublishMutation_1.PublishMutation.setCodeSigningInfoAsync(graphqlClient, newUpdate.id, {
|
|
189
|
+
alg: codeSigningInfo.codeSigningMetadata.alg,
|
|
190
|
+
keyid: codeSigningInfo.codeSigningMetadata.keyid,
|
|
191
|
+
sig: directiveSignature,
|
|
192
|
+
});
|
|
193
|
+
}));
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
return newUpdates;
|
|
197
|
+
}
|
|
198
|
+
sanitizeFlags(flags) {
|
|
199
|
+
var _b, _c;
|
|
200
|
+
const nonInteractive = (_b = flags['non-interactive']) !== null && _b !== void 0 ? _b : false;
|
|
201
|
+
const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
|
|
202
|
+
if (nonInteractive && !auto && !(updateMessage && (branchName || channelName))) {
|
|
203
|
+
core_1.Errors.error('--branch and --message, or --channel and --message are required when updating in non-interactive mode unless --auto is specified', { exit: 1 });
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
auto,
|
|
207
|
+
branchName,
|
|
208
|
+
channelName,
|
|
209
|
+
updateMessage,
|
|
210
|
+
platform: flags.platform,
|
|
211
|
+
privateKeyPath: flags['private-key-path'],
|
|
212
|
+
nonInteractive,
|
|
213
|
+
json: (_c = flags.json) !== null && _c !== void 0 ? _c : false,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.default = UpdateRollBackToEmbedded;
|
|
218
|
+
_a = UpdateRollBackToEmbedded;
|
|
219
|
+
UpdateRollBackToEmbedded.hidden = true; // until we launch
|
|
220
|
+
UpdateRollBackToEmbedded.description = 'roll back to the embedded update';
|
|
221
|
+
UpdateRollBackToEmbedded.flags = {
|
|
222
|
+
branch: core_1.Flags.string({
|
|
223
|
+
description: 'Branch to publish the rollback to embedded update group on',
|
|
224
|
+
required: false,
|
|
225
|
+
}),
|
|
226
|
+
channel: core_1.Flags.string({
|
|
227
|
+
description: 'Channel that the published rollback to embedded update should affect',
|
|
228
|
+
required: false,
|
|
229
|
+
}),
|
|
230
|
+
message: core_1.Flags.string({
|
|
231
|
+
description: 'A short message describing the rollback to embedded update',
|
|
232
|
+
required: false,
|
|
233
|
+
}),
|
|
234
|
+
platform: core_1.Flags.enum({
|
|
235
|
+
char: 'p',
|
|
236
|
+
options: [...publish_1.defaultPublishPlatforms, 'all'],
|
|
237
|
+
default: 'all',
|
|
238
|
+
required: false,
|
|
239
|
+
}),
|
|
240
|
+
auto: core_1.Flags.boolean({
|
|
241
|
+
description: 'Use the current git branch and commit message for the EAS branch and update message',
|
|
242
|
+
default: false,
|
|
243
|
+
}),
|
|
244
|
+
'private-key-path': core_1.Flags.string({
|
|
245
|
+
description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
|
|
246
|
+
required: false,
|
|
247
|
+
}),
|
|
248
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
249
|
+
};
|
|
250
|
+
UpdateRollBackToEmbedded.contextDefinition = {
|
|
251
|
+
..._a.ContextOptions.DynamicProjectConfig,
|
|
252
|
+
..._a.ContextOptions.LoggedIn,
|
|
253
|
+
};
|
|
@@ -92,7 +92,7 @@ class IosCredentialsProvider {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
async disablePushNotificationsSetupInEasJsonAsync(ctx) {
|
|
95
|
-
const easJsonAccessor =
|
|
95
|
+
const easJsonAccessor = eas_json_1.EasJsonAccessor.fromProjectPath(ctx.projectDir);
|
|
96
96
|
await easJsonAccessor.readRawJsonAsync();
|
|
97
97
|
easJsonAccessor.patch(easJsonRawObject => {
|
|
98
98
|
easJsonRawObject.cli = {
|
|
@@ -8,7 +8,7 @@ const prompts_1 = require("../../prompts");
|
|
|
8
8
|
class SelectBuildProfileFromEasJson {
|
|
9
9
|
constructor(projectDir, platform) {
|
|
10
10
|
this.platform = platform;
|
|
11
|
-
this.easJsonAccessor =
|
|
11
|
+
this.easJsonAccessor = eas_json_1.EasJsonAccessor.fromProjectPath(projectDir);
|
|
12
12
|
}
|
|
13
13
|
async runAsync() {
|
|
14
14
|
const profileName = await this.getProfileNameFromEasConfigAsync();
|