eas-cli 0.38.1 → 0.40.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 +31 -31
- package/build/build/android/configure.d.ts +3 -2
- package/build/build/configure.js +1 -1
- package/build/build/createContext.js +1 -1
- package/build/build/ios/configure.d.ts +3 -2
- package/build/build/ios/version.d.ts +1 -1
- package/build/build/ios/version.js +4 -1
- package/build/build/local.js +1 -1
- package/build/build/metadata.js +14 -0
- package/build/commandUtils/EasCommand.js +0 -2
- package/build/commands/branch/publish.d.ts +1 -18
- package/build/commands/branch/publish.js +2 -344
- package/build/commands/build/configure.js +1 -1
- package/build/commands/build/index.js +18 -11
- package/build/commands/channel/edit.js +1 -1
- package/build/commands/channel/view.js +3 -3
- package/build/commands/config.js +1 -1
- package/build/commands/credentials.js +1 -7
- package/build/commands/diagnostics.d.ts +1 -0
- package/build/commands/diagnostics.js +19 -1
- package/build/commands/submit.js +1 -1
- package/build/commands/update/configure.d.ts +8 -0
- package/build/commands/update/configure.js +76 -0
- package/build/commands/update/index.d.ts +20 -0
- package/build/commands/update/index.js +361 -0
- package/build/credentials/context.d.ts +5 -0
- package/build/credentials/context.js +12 -7
- package/build/credentials/ios/actions/AscApiKeyUtils.js +1 -1
- package/build/credentials/ios/appstore/ensureAppExists.js +3 -3
- package/build/credentials/manager/ManageAndroid.d.ts +3 -3
- package/build/credentials/manager/ManageAndroid.js +21 -13
- package/build/credentials/manager/ManageIos.d.ts +3 -3
- package/build/credentials/manager/ManageIos.js +23 -13
- package/build/credentials/manager/SelectBuildProfileFromEasJson.d.ts +2 -3
- package/build/credentials/manager/SelectBuildProfileFromEasJson.js +4 -7
- package/build/credentials/manager/SelectIosDistributionTypeGraphqlFromBuildProfile.d.ts +3 -2
- package/build/credentials/manager/SelectPlatform.d.ts +1 -2
- package/build/credentials/manager/SelectPlatform.js +3 -3
- package/build/project/ios/scheme.d.ts +3 -2
- package/build/project/ios/target.js +32 -7
- package/build/submit/context.js +0 -1
- package/build/submit/ios/AppProduce.js +3 -3
- package/build/submit/ios/IosSubmitCommand.js +1 -1
- package/build/submit/utils/wait.js +1 -1
- package/build/update/utils.js +1 -1
- package/build/utils/profiles.d.ts +2 -3
- package/build/utils/profiles.js +20 -7
- package/oclif.manifest.json +1 -1
- package/package.json +10 -9
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { flags } from '@oclif/command';
|
|
2
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
3
|
+
import { PublishPlatform } from '../../project/publish';
|
|
4
|
+
export declare const defaultPublishPlatforms: PublishPlatform[];
|
|
5
|
+
export default class UpdatePublish extends EasCommand {
|
|
6
|
+
static hidden: boolean;
|
|
7
|
+
static description: string;
|
|
8
|
+
static flags: {
|
|
9
|
+
branch: flags.IOptionFlag<string | undefined>;
|
|
10
|
+
message: flags.IOptionFlag<string | undefined>;
|
|
11
|
+
republish: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
|
|
12
|
+
group: flags.IOptionFlag<string | undefined>;
|
|
13
|
+
'input-dir': flags.IOptionFlag<string>;
|
|
14
|
+
'skip-bundler': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
|
|
15
|
+
platform: flags.IOptionFlag<string>;
|
|
16
|
+
json: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
|
|
17
|
+
auto: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
runAsync(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultPublishPlatforms = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const config_1 = require("@expo/config");
|
|
6
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
7
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
8
|
+
const command_1 = require("@oclif/command");
|
|
9
|
+
const assert_1 = (0, tslib_1.__importDefault)(require("assert"));
|
|
10
|
+
const chalk_1 = (0, tslib_1.__importDefault)(require("chalk"));
|
|
11
|
+
const dateformat_1 = (0, tslib_1.__importDefault)(require("dateformat"));
|
|
12
|
+
const graphql_tag_1 = (0, tslib_1.__importDefault)(require("graphql-tag"));
|
|
13
|
+
const EasCommand_1 = (0, tslib_1.__importDefault)(require("../../commandUtils/EasCommand"));
|
|
14
|
+
const client_1 = require("../../graphql/client");
|
|
15
|
+
const PublishMutation_1 = require("../../graphql/mutations/PublishMutation");
|
|
16
|
+
const log_1 = (0, tslib_1.__importDefault)(require("../../log"));
|
|
17
|
+
const ora_1 = require("../../ora");
|
|
18
|
+
const projectUtils_1 = require("../../project/projectUtils");
|
|
19
|
+
const publish_1 = require("../../project/publish");
|
|
20
|
+
const workflow_1 = require("../../project/workflow");
|
|
21
|
+
const prompts_1 = require("../../prompts");
|
|
22
|
+
const utils_1 = require("../../update/utils");
|
|
23
|
+
const uniqBy_1 = (0, tslib_1.__importDefault)(require("../../utils/expodash/uniqBy"));
|
|
24
|
+
const formatFields_1 = (0, tslib_1.__importDefault)(require("../../utils/formatFields"));
|
|
25
|
+
const vcs_1 = require("../../vcs");
|
|
26
|
+
const create_1 = require("../branch/create");
|
|
27
|
+
const list_1 = require("../branch/list");
|
|
28
|
+
const view_1 = require("../branch/view");
|
|
29
|
+
const create_2 = require("../channel/create");
|
|
30
|
+
const configure_1 = require("./configure");
|
|
31
|
+
exports.defaultPublishPlatforms = ['android', 'ios'];
|
|
32
|
+
async function getUpdateGroupAsync({ group, }) {
|
|
33
|
+
const { updatesByGroup } = await (0, client_1.withErrorHandlingAsync)(client_1.graphqlClient
|
|
34
|
+
.query((0, graphql_tag_1.default) `
|
|
35
|
+
query getUpdateGroupAsync($group: ID!) {
|
|
36
|
+
updatesByGroup(group: $group) {
|
|
37
|
+
id
|
|
38
|
+
group
|
|
39
|
+
runtimeVersion
|
|
40
|
+
manifestFragment
|
|
41
|
+
platform
|
|
42
|
+
message
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`, {
|
|
46
|
+
group,
|
|
47
|
+
}, { additionalTypenames: ['Update'] })
|
|
48
|
+
.toPromise());
|
|
49
|
+
return updatesByGroup;
|
|
50
|
+
}
|
|
51
|
+
async function ensureChannelExistsAsync({ appId, branchId, channelName, }) {
|
|
52
|
+
var _a;
|
|
53
|
+
try {
|
|
54
|
+
await (0, create_2.createUpdateChannelOnAppAsync)({
|
|
55
|
+
appId,
|
|
56
|
+
channelName,
|
|
57
|
+
branchId,
|
|
58
|
+
});
|
|
59
|
+
log_1.default.withTick(`Created a channel: ${chalk_1.default.bold(channelName)} pointed at branch: ${chalk_1.default.bold(channelName)}.`);
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
const isIgnorableError = ((_a = e.graphQLErrors) === null || _a === void 0 ? void 0 : _a.length) === 1 &&
|
|
63
|
+
e.graphQLErrors[0].extensions.errorCode === 'CHANNEL_ALREADY_EXISTS';
|
|
64
|
+
if (!isIgnorableError) {
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function ensureBranchExistsAsync({ appId, name: branchName, }) {
|
|
70
|
+
const { app } = await (0, view_1.viewUpdateBranchAsync)({
|
|
71
|
+
appId,
|
|
72
|
+
name: branchName,
|
|
73
|
+
});
|
|
74
|
+
const updateBranch = app === null || app === void 0 ? void 0 : app.byId.updateBranchByName;
|
|
75
|
+
if (updateBranch) {
|
|
76
|
+
const { id, updates } = updateBranch;
|
|
77
|
+
await ensureChannelExistsAsync({ appId, branchId: id, channelName: branchName });
|
|
78
|
+
return { id, updates };
|
|
79
|
+
}
|
|
80
|
+
const newUpdateBranch = await (0, create_1.createUpdateBranchOnAppAsync)({ appId, name: branchName });
|
|
81
|
+
log_1.default.withTick(`Created branch: ${chalk_1.default.bold(branchName)}`);
|
|
82
|
+
await ensureChannelExistsAsync({ appId, branchId: newUpdateBranch.id, channelName: branchName });
|
|
83
|
+
return { id: newUpdateBranch.id, updates: [] };
|
|
84
|
+
}
|
|
85
|
+
class UpdatePublish extends EasCommand_1.default {
|
|
86
|
+
async runAsync() {
|
|
87
|
+
var _a, _b, _c;
|
|
88
|
+
let { flags: { branch: branchName, json: jsonFlag, auto: autoFlag, message, republish, group, 'input-dir': inputDir, 'skip-bundler': skipBundler, }, } = this.parse(UpdatePublish);
|
|
89
|
+
const platformFlag = this.parse(UpdatePublish).flags.platform;
|
|
90
|
+
// If a group was specified, that means we are republishing it.
|
|
91
|
+
republish = group ? true : republish;
|
|
92
|
+
const projectDir = await (0, projectUtils_1.findProjectRootAsync)();
|
|
93
|
+
const { exp } = (0, config_1.getConfig)(projectDir, {
|
|
94
|
+
skipSDKVersionRequirement: true,
|
|
95
|
+
isPublicConfig: true,
|
|
96
|
+
});
|
|
97
|
+
const runtimeVersions = await getRuntimeVersionObjectAsync(exp, platformFlag, projectDir);
|
|
98
|
+
const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
|
|
99
|
+
await checkEASUpdateURLIsSetAsync(exp);
|
|
100
|
+
if (!branchName && autoFlag) {
|
|
101
|
+
branchName =
|
|
102
|
+
(await (0, vcs_1.getVcsClient)().getBranchNameAsync()) ||
|
|
103
|
+
`branch-${Math.random().toString(36).substr(2, 4)}`;
|
|
104
|
+
}
|
|
105
|
+
if (!branchName) {
|
|
106
|
+
const validationMessage = 'branch name may not be empty.';
|
|
107
|
+
if (jsonFlag) {
|
|
108
|
+
throw new Error(validationMessage);
|
|
109
|
+
}
|
|
110
|
+
const branches = await (0, list_1.listBranchesAsync)({ projectId });
|
|
111
|
+
branchName = await (0, prompts_1.selectAsync)('which branch would you like to publish on?', branches.map(branch => {
|
|
112
|
+
return {
|
|
113
|
+
title: `${branch.name} ${chalk_1.default.grey(`- current update: ${(0, utils_1.formatUpdate)(branch.updates[0])}`)}`,
|
|
114
|
+
value: branch.name,
|
|
115
|
+
};
|
|
116
|
+
}));
|
|
117
|
+
(0, assert_1.default)(branchName, 'branch name must be specified.');
|
|
118
|
+
}
|
|
119
|
+
const { id: branchId, updates } = await ensureBranchExistsAsync({
|
|
120
|
+
appId: projectId,
|
|
121
|
+
name: branchName,
|
|
122
|
+
});
|
|
123
|
+
let unsortedUpdateInfoGroups = {};
|
|
124
|
+
let oldMessage, oldRuntimeVersion;
|
|
125
|
+
if (republish) {
|
|
126
|
+
// If we are republishing, we don't need to worry about building the bundle or uploading the assets.
|
|
127
|
+
// Instead we get the `updateInfoGroup` from the update we wish to republish.
|
|
128
|
+
let updatesToRepublish;
|
|
129
|
+
if (group) {
|
|
130
|
+
updatesToRepublish = await getUpdateGroupAsync({ group });
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Drop into interactive mode if the user has not specified an update group to republish.
|
|
134
|
+
if (jsonFlag) {
|
|
135
|
+
throw new Error('You must specify the update group to republish.');
|
|
136
|
+
}
|
|
137
|
+
const updateGroups = (0, uniqBy_1.default)(updates, u => u.group)
|
|
138
|
+
.filter(update => {
|
|
139
|
+
// Only show groups that have updates on the specified platform(s).
|
|
140
|
+
return platformFlag === 'all' || update.platform === platformFlag;
|
|
141
|
+
})
|
|
142
|
+
.map(update => ({
|
|
143
|
+
title: formatUpdateTitle(update),
|
|
144
|
+
value: update.group,
|
|
145
|
+
}));
|
|
146
|
+
if (updateGroups.length === 0) {
|
|
147
|
+
throw new Error(`There are no updates on branch "${branchName}" published on the platform(s) ${platformFlag}. Did you mean to publish a new update instead?`);
|
|
148
|
+
}
|
|
149
|
+
const selectedUpdateGroup = await (0, prompts_1.selectAsync)('which update would you like to republish?', updateGroups);
|
|
150
|
+
updatesToRepublish = updates.filter(update => update.group === selectedUpdateGroup);
|
|
151
|
+
}
|
|
152
|
+
const updatesToRepublishFilteredByPlatform = updatesToRepublish.filter(
|
|
153
|
+
// Only republish to the specified platforms
|
|
154
|
+
update => platformFlag === 'all' || update.platform === platformFlag);
|
|
155
|
+
if (updatesToRepublishFilteredByPlatform.length === 0) {
|
|
156
|
+
throw new Error(`There are no updates on branch "${branchName}" published on the platform(s) "${platformFlag}" with group ID "${group ? group : updatesToRepublish[0].group}". Did you mean to publish a new update instead?`);
|
|
157
|
+
}
|
|
158
|
+
let publicationPlatformMessage;
|
|
159
|
+
if (platformFlag === 'all') {
|
|
160
|
+
if (updatesToRepublishFilteredByPlatform.length !== exports.defaultPublishPlatforms.length) {
|
|
161
|
+
log_1.default.warn(`You are republishing an update that wasn't published for all platforms.`);
|
|
162
|
+
}
|
|
163
|
+
publicationPlatformMessage = `The republished update will appear on the same plaforms it was originally published on: ${updatesToRepublishFilteredByPlatform
|
|
164
|
+
.map(update => update.platform)
|
|
165
|
+
.join(',')}`;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
publicationPlatformMessage = `The republished update will appear only on: ${platformFlag}`;
|
|
169
|
+
}
|
|
170
|
+
log_1.default.withTick(publicationPlatformMessage);
|
|
171
|
+
for (const update of updatesToRepublishFilteredByPlatform) {
|
|
172
|
+
const { manifestFragment } = update;
|
|
173
|
+
const platform = update.platform;
|
|
174
|
+
unsortedUpdateInfoGroups[platform] = JSON.parse(manifestFragment);
|
|
175
|
+
}
|
|
176
|
+
// These are the same for each member of an update group
|
|
177
|
+
group = updatesToRepublishFilteredByPlatform[0].group;
|
|
178
|
+
oldMessage = (_a = updatesToRepublishFilteredByPlatform[0].message) !== null && _a !== void 0 ? _a : '';
|
|
179
|
+
oldRuntimeVersion = updatesToRepublishFilteredByPlatform[0].runtimeVersion;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
if (!message && autoFlag) {
|
|
183
|
+
message = (_b = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _b === void 0 ? void 0 : _b.trim();
|
|
184
|
+
}
|
|
185
|
+
if (!message) {
|
|
186
|
+
const validationMessage = 'publish message may not be empty.';
|
|
187
|
+
if (jsonFlag) {
|
|
188
|
+
throw new Error(validationMessage);
|
|
189
|
+
}
|
|
190
|
+
({ publishMessage: message } = await (0, prompts_1.promptAsync)({
|
|
191
|
+
type: 'text',
|
|
192
|
+
name: 'publishMessage',
|
|
193
|
+
message: `Please enter an update message.`,
|
|
194
|
+
initial: republish
|
|
195
|
+
? `Republish "${oldMessage}" - group: ${group}`
|
|
196
|
+
: (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim(),
|
|
197
|
+
validate: (value) => (value ? true : validationMessage),
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
// build bundle and upload assets for a new publish
|
|
201
|
+
if (!skipBundler) {
|
|
202
|
+
await (0, publish_1.buildBundlesAsync)({ projectDir, inputDir });
|
|
203
|
+
}
|
|
204
|
+
const assetSpinner = (0, ora_1.ora)('Uploading assets...').start();
|
|
205
|
+
try {
|
|
206
|
+
const platforms = platformFlag === 'all' ? exports.defaultPublishPlatforms : [platformFlag];
|
|
207
|
+
const assets = await (0, publish_1.collectAssetsAsync)({ inputDir: inputDir, platforms });
|
|
208
|
+
await (0, publish_1.uploadAssetsAsync)(assets);
|
|
209
|
+
unsortedUpdateInfoGroups = await (0, publish_1.buildUnsortedUpdateInfoGroupAsync)(assets, exp);
|
|
210
|
+
assetSpinner.succeed('Uploaded assets!');
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
assetSpinner.fail('Failed to upload assets');
|
|
214
|
+
throw e;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const runtimeToPlatformMapping = {};
|
|
218
|
+
for (const runtime of new Set(Object.values(runtimeVersions))) {
|
|
219
|
+
runtimeToPlatformMapping[runtime] = Object.entries(runtimeVersions)
|
|
220
|
+
.filter(pair => pair[1] === runtime)
|
|
221
|
+
.map(pair => pair[0]);
|
|
222
|
+
}
|
|
223
|
+
// Sort the updates into different groups based on their platform specific runtime versions
|
|
224
|
+
const updateGroups = Object.entries(runtimeToPlatformMapping).map(([runtime, platforms]) => {
|
|
225
|
+
const localUpdateInfoGroup = Object.fromEntries(platforms.map(platform => [
|
|
226
|
+
platform,
|
|
227
|
+
unsortedUpdateInfoGroups[platform],
|
|
228
|
+
]));
|
|
229
|
+
if (republish && !oldRuntimeVersion) {
|
|
230
|
+
throw new Error('Can not find the runtime version of the update group that is being republished.');
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
branchId,
|
|
234
|
+
updateInfoGroup: localUpdateInfoGroup,
|
|
235
|
+
runtimeVersion: republish ? oldRuntimeVersion : runtime,
|
|
236
|
+
message,
|
|
237
|
+
};
|
|
238
|
+
});
|
|
239
|
+
let newUpdates;
|
|
240
|
+
const publishSpinner = (0, ora_1.ora)('Publishing...').start();
|
|
241
|
+
try {
|
|
242
|
+
newUpdates = await PublishMutation_1.PublishMutation.publishUpdateGroupAsync(updateGroups);
|
|
243
|
+
publishSpinner.succeed('Published!');
|
|
244
|
+
}
|
|
245
|
+
catch (e) {
|
|
246
|
+
publishSpinner.fail('Failed to published updates');
|
|
247
|
+
throw e;
|
|
248
|
+
}
|
|
249
|
+
if (jsonFlag) {
|
|
250
|
+
log_1.default.log(JSON.stringify(newUpdates));
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
if (new Set(newUpdates.map(update => update.group)).size > 1) {
|
|
254
|
+
log_1.default.addNewLineIfNone();
|
|
255
|
+
log_1.default.log('👉 Since multiple runtime versions are defined, multiple update groups have been published.');
|
|
256
|
+
}
|
|
257
|
+
log_1.default.addNewLineIfNone();
|
|
258
|
+
for (const runtime of new Set(Object.values(runtimeVersions))) {
|
|
259
|
+
const platforms = newUpdates
|
|
260
|
+
.filter(update => update.runtimeVersion === runtime)
|
|
261
|
+
.map(update => update.platform);
|
|
262
|
+
const newUpdate = newUpdates.find(update => update.runtimeVersion === runtime);
|
|
263
|
+
if (!newUpdate) {
|
|
264
|
+
throw new Error(`Publish response is missing updates with runtime ${runtime}.`);
|
|
265
|
+
}
|
|
266
|
+
log_1.default.log((0, formatFields_1.default)([
|
|
267
|
+
{ label: 'branch', value: branchName },
|
|
268
|
+
{ label: 'runtime version', value: runtime },
|
|
269
|
+
{ label: 'platform', value: platforms.join(', ') },
|
|
270
|
+
{ label: 'update group ID', value: newUpdate.group },
|
|
271
|
+
{ label: 'message', value: message },
|
|
272
|
+
]));
|
|
273
|
+
log_1.default.addNewLineIfNone();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
exports.default = UpdatePublish;
|
|
279
|
+
UpdatePublish.hidden = true;
|
|
280
|
+
UpdatePublish.description = 'Publish an update group.';
|
|
281
|
+
UpdatePublish.flags = {
|
|
282
|
+
branch: command_1.flags.string({
|
|
283
|
+
description: 'Branch to publish the update group on',
|
|
284
|
+
required: false,
|
|
285
|
+
}),
|
|
286
|
+
message: command_1.flags.string({
|
|
287
|
+
description: 'A short message describing the update',
|
|
288
|
+
required: false,
|
|
289
|
+
}),
|
|
290
|
+
republish: command_1.flags.boolean({
|
|
291
|
+
description: 'Republish an update group',
|
|
292
|
+
exclusive: ['input-dir', 'skip-bundler'],
|
|
293
|
+
}),
|
|
294
|
+
group: command_1.flags.string({
|
|
295
|
+
description: 'Update group to republish',
|
|
296
|
+
exclusive: ['input-dir', 'skip-bundler'],
|
|
297
|
+
}),
|
|
298
|
+
'input-dir': command_1.flags.string({
|
|
299
|
+
description: 'Location of the bundle',
|
|
300
|
+
default: 'dist',
|
|
301
|
+
required: false,
|
|
302
|
+
}),
|
|
303
|
+
'skip-bundler': command_1.flags.boolean({
|
|
304
|
+
description: `Skip running Expo CLI to bundle the app before publishing`,
|
|
305
|
+
default: false,
|
|
306
|
+
}),
|
|
307
|
+
platform: command_1.flags.enum({
|
|
308
|
+
char: 'p',
|
|
309
|
+
options: [...exports.defaultPublishPlatforms, 'all'],
|
|
310
|
+
default: 'all',
|
|
311
|
+
required: false,
|
|
312
|
+
}),
|
|
313
|
+
json: command_1.flags.boolean({
|
|
314
|
+
description: 'Enable JSON output, non-JSON messages will be printed to stderr',
|
|
315
|
+
default: false,
|
|
316
|
+
}),
|
|
317
|
+
auto: command_1.flags.boolean({
|
|
318
|
+
description: 'Use the current git branch and commit message for the EAS branch and update message',
|
|
319
|
+
default: false,
|
|
320
|
+
}),
|
|
321
|
+
};
|
|
322
|
+
async function getRuntimeVersionObjectAsync(exp, platformFlag, projectDir) {
|
|
323
|
+
var _a, _b;
|
|
324
|
+
const platforms = (platformFlag === 'all' ? ['android', 'ios'] : [platformFlag]);
|
|
325
|
+
for (const platform of platforms) {
|
|
326
|
+
const isPolicy = typeof ((_b = (_a = exp[platform]) === null || _a === void 0 ? void 0 : _a.runtimeVersion) !== null && _b !== void 0 ? _b : exp.runtimeVersion) === 'object';
|
|
327
|
+
if (isPolicy) {
|
|
328
|
+
const isManaged = (await (0, workflow_1.resolveWorkflowAsync)(projectDir, platform)) === eas_build_job_1.Workflow.MANAGED;
|
|
329
|
+
if (!isManaged) {
|
|
330
|
+
throw new Error('Runtime version policies are only supported in the managed workflow.');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return Object.fromEntries(platforms.map(platform => [platform, config_plugins_1.Updates.getRuntimeVersion(exp, platform)]));
|
|
335
|
+
}
|
|
336
|
+
function formatUpdateTitle(update) {
|
|
337
|
+
const { message, createdAt, actor, runtimeVersion } = update;
|
|
338
|
+
let actorName;
|
|
339
|
+
switch (actor === null || actor === void 0 ? void 0 : actor.__typename) {
|
|
340
|
+
case 'User': {
|
|
341
|
+
actorName = actor.username;
|
|
342
|
+
break;
|
|
343
|
+
}
|
|
344
|
+
case 'Robot': {
|
|
345
|
+
const { firstName, id } = actor;
|
|
346
|
+
actorName = firstName !== null && firstName !== void 0 ? firstName : `robot: ${id.slice(0, 4)}...`;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
default:
|
|
350
|
+
actorName = 'unknown';
|
|
351
|
+
}
|
|
352
|
+
return `[${(0, dateformat_1.default)(createdAt, 'mmm dd HH:MM')} by ${actorName}, runtimeVersion: ${runtimeVersion}] ${message}`;
|
|
353
|
+
}
|
|
354
|
+
async function checkEASUpdateURLIsSetAsync(exp) {
|
|
355
|
+
var _a;
|
|
356
|
+
const configuredURL = (_a = exp.updates) === null || _a === void 0 ? void 0 : _a.url;
|
|
357
|
+
const expectedURL = await (0, configure_1.getEASUpdateURLAsync)(exp);
|
|
358
|
+
if (configuredURL !== expectedURL) {
|
|
359
|
+
throw new Error(`The update URL is incorrectly configured for EAS Update. Please set updates.url to ${expectedURL} in your app.json.`);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExpoConfig } from '@expo/config';
|
|
2
|
+
import { Env } from '@expo/eas-build-job';
|
|
2
3
|
import { Actor } from '../user/User';
|
|
3
4
|
import * as AndroidGraphqlClient from './android/api/GraphqlClient';
|
|
4
5
|
import * as IosGraphqlClient from './ios/api/GraphqlClient';
|
|
@@ -18,7 +19,11 @@ export declare class CredentialsContext {
|
|
|
18
19
|
nonInteractive?: boolean;
|
|
19
20
|
projectDir: string;
|
|
20
21
|
user: Actor;
|
|
22
|
+
env?: Env;
|
|
21
23
|
});
|
|
24
|
+
static getExpoConfigInProject(projectDir: string, { env }?: {
|
|
25
|
+
env?: Env;
|
|
26
|
+
}): ExpoConfig | null;
|
|
22
27
|
get hasProjectContext(): boolean;
|
|
23
28
|
get exp(): ExpoConfig;
|
|
24
29
|
ensureProjectContext(): void;
|
|
@@ -14,7 +14,7 @@ const IosGraphqlClient = (0, tslib_1.__importStar)(require("./ios/api/GraphqlCli
|
|
|
14
14
|
const AppStoreApi_1 = (0, tslib_1.__importDefault)(require("./ios/appstore/AppStoreApi"));
|
|
15
15
|
class CredentialsContext {
|
|
16
16
|
constructor(options) {
|
|
17
|
-
var _a;
|
|
17
|
+
var _a, _b;
|
|
18
18
|
this.options = options;
|
|
19
19
|
this.android = AndroidGraphqlClient;
|
|
20
20
|
this.appStore = new AppStoreApi_1.default();
|
|
@@ -25,12 +25,17 @@ class CredentialsContext {
|
|
|
25
25
|
this.nonInteractive = (_a = options.nonInteractive) !== null && _a !== void 0 ? _a : false;
|
|
26
26
|
this.resolvedExp = options.exp;
|
|
27
27
|
if (!this.resolvedExp) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
this.resolvedExp =
|
|
29
|
+
(_b = CredentialsContext.getExpoConfigInProject(this.projectDir, { env: options.env })) !== null && _b !== void 0 ? _b : undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
static getExpoConfigInProject(projectDir, { env } = {}) {
|
|
33
|
+
try {
|
|
34
|
+
return (0, expoConfig_1.getExpoConfig)(projectDir, { env });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
// ignore error, context might be created outside of expo project
|
|
38
|
+
return null;
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
get hasProjectContext() {
|
|
@@ -93,7 +93,7 @@ async function promptForAscApiKeyAsync(ctx) {
|
|
|
93
93
|
}
|
|
94
94
|
async function promptForKeyP8AndIdAsync() {
|
|
95
95
|
var _a, _b;
|
|
96
|
-
log_1.default.log(chalk_1.default.bold('An App Store Connect Api key is required to upload your app to the Apple App Store'));
|
|
96
|
+
log_1.default.log(chalk_1.default.bold('An App Store Connect Api key is required to upload your app to the Apple App Store Connect'));
|
|
97
97
|
log_1.default.log(`If you're not sure what this is or how to create one, ${(0, log_1.learnMore)('https://expo.fyi/creating-asc-api-key')}`);
|
|
98
98
|
const { keyP8Path } = await (0, prompts_1.promptAsync)({
|
|
99
99
|
type: 'text',
|
|
@@ -96,10 +96,10 @@ exports.syncCapabilityIdentifiersAsync = syncCapabilityIdentifiersAsync;
|
|
|
96
96
|
async function ensureAppExistsAsync(authCtx, { name, language, companyName, bundleIdentifier, sku, }) {
|
|
97
97
|
var _a;
|
|
98
98
|
const context = (0, authenticate_1.getRequestContext)(authCtx);
|
|
99
|
-
const spinner = (0, ora_1.ora)(`Linking to App Store ${chalk_1.default.dim(bundleIdentifier)}`).start();
|
|
99
|
+
const spinner = (0, ora_1.ora)(`Linking to App Store Connect ${chalk_1.default.dim(bundleIdentifier)}`).start();
|
|
100
100
|
let app = await apple_utils_1.App.findAsync(context, { bundleId: bundleIdentifier });
|
|
101
101
|
if (!app) {
|
|
102
|
-
spinner.text = `Creating App Store app ${chalk_1.default.bold(name)} ${chalk_1.default.dim(bundleIdentifier)}`;
|
|
102
|
+
spinner.text = `Creating App Store Connect app ${chalk_1.default.bold(name)} ${chalk_1.default.dim(bundleIdentifier)}`;
|
|
103
103
|
try {
|
|
104
104
|
// Assert contract errors when the user needs to create an app.
|
|
105
105
|
await (0, contractMessages_1.assertContractMessagesAsync)(context, spinner);
|
|
@@ -125,7 +125,7 @@ async function ensureAppExistsAsync(authCtx, { name, language, companyName, bund
|
|
|
125
125
|
else {
|
|
126
126
|
// TODO: Update app name when API gives us that possibility
|
|
127
127
|
}
|
|
128
|
-
spinner.succeed(`Prepared App Store for ${chalk_1.default.bold(name)} ${chalk_1.default.dim(bundleIdentifier)}`);
|
|
128
|
+
spinner.succeed(`Prepared App Store Connect for ${chalk_1.default.bold(name)} ${chalk_1.default.dim(bundleIdentifier)}`);
|
|
129
129
|
return app;
|
|
130
130
|
}
|
|
131
131
|
exports.ensureAppExistsAsync = ensureAppExistsAsync;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { CredentialsContext } from '../context';
|
|
2
1
|
import { ActionInfo } from './Actions';
|
|
3
2
|
import { Action } from './HelperActions';
|
|
4
3
|
export declare class ManageAndroid {
|
|
5
4
|
private callingAction;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
private projectDir;
|
|
6
|
+
constructor(callingAction: Action, projectDir: string);
|
|
7
|
+
runAsync(currentActions?: ActionInfo[]): Promise<void>;
|
|
8
8
|
private createProjectContextAsync;
|
|
9
9
|
private runProjectSpecificActionAsync;
|
|
10
10
|
}
|
|
@@ -25,16 +25,27 @@ const SetUpGoogleServiceAccountKey_1 = require("../android/actions/SetUpGoogleSe
|
|
|
25
25
|
const UpdateCredentialsJson_1 = require("../android/actions/UpdateCredentialsJson");
|
|
26
26
|
const UseExistingGoogleServiceAccountKey_1 = require("../android/actions/UseExistingGoogleServiceAccountKey");
|
|
27
27
|
const printCredentials_1 = require("../android/utils/printCredentials");
|
|
28
|
+
const context_1 = require("../context");
|
|
28
29
|
const Actions_1 = require("./Actions");
|
|
29
30
|
const AndroidActions_1 = require("./AndroidActions");
|
|
30
31
|
const HelperActions_1 = require("./HelperActions");
|
|
31
32
|
const SelectAndroidBuildCredentials_1 = require("./SelectAndroidBuildCredentials");
|
|
32
33
|
const SelectBuildProfileFromEasJson_1 = require("./SelectBuildProfileFromEasJson");
|
|
33
34
|
class ManageAndroid {
|
|
34
|
-
constructor(callingAction) {
|
|
35
|
+
constructor(callingAction, projectDir) {
|
|
35
36
|
this.callingAction = callingAction;
|
|
37
|
+
this.projectDir = projectDir;
|
|
36
38
|
}
|
|
37
|
-
async runAsync(
|
|
39
|
+
async runAsync(currentActions = AndroidActions_1.highLevelActions) {
|
|
40
|
+
const hasProjectContext = !!context_1.CredentialsContext.getExpoConfigInProject(this.projectDir);
|
|
41
|
+
const buildProfile = hasProjectContext
|
|
42
|
+
? await new SelectBuildProfileFromEasJson_1.SelectBuildProfileFromEasJson(this.projectDir, eas_build_job_1.Platform.ANDROID).runAsync()
|
|
43
|
+
: null;
|
|
44
|
+
const ctx = new context_1.CredentialsContext({
|
|
45
|
+
projectDir: process.cwd(),
|
|
46
|
+
user: await (0, actions_1.ensureLoggedInAsync)(),
|
|
47
|
+
env: buildProfile === null || buildProfile === void 0 ? void 0 : buildProfile.env,
|
|
48
|
+
});
|
|
38
49
|
const accountName = ctx.hasProjectContext
|
|
39
50
|
? (0, projectUtils_1.getProjectAccountName)(ctx.exp, ctx.user)
|
|
40
51
|
: (0, actions_1.ensureActorHasUsername)(ctx.user);
|
|
@@ -42,7 +53,11 @@ class ManageAndroid {
|
|
|
42
53
|
if (!account) {
|
|
43
54
|
throw new Error(`You do not have access to account: ${accountName}`);
|
|
44
55
|
}
|
|
45
|
-
|
|
56
|
+
let gradleContext;
|
|
57
|
+
if (ctx.hasProjectContext) {
|
|
58
|
+
(0, assert_1.default)(buildProfile, 'buildProfile must be defined in a project context');
|
|
59
|
+
gradleContext = await this.createProjectContextAsync(ctx, buildProfile);
|
|
60
|
+
}
|
|
46
61
|
while (true) {
|
|
47
62
|
try {
|
|
48
63
|
if (ctx.hasProjectContext) {
|
|
@@ -114,20 +129,13 @@ class ManageAndroid {
|
|
|
114
129
|
await new HelperActions_1.PressAnyKeyToContinue().runAsync();
|
|
115
130
|
}
|
|
116
131
|
}
|
|
117
|
-
async createProjectContextAsync(ctx) {
|
|
118
|
-
|
|
119
|
-
return {};
|
|
120
|
-
}
|
|
132
|
+
async createProjectContextAsync(ctx, buildProfile) {
|
|
133
|
+
(0, assert_1.default)(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.');
|
|
121
134
|
const maybeProjectId = await (0, projectUtils_1.promptToCreateProjectIfNotExistsAsync)(ctx.exp);
|
|
122
135
|
if (!maybeProjectId) {
|
|
123
136
|
throw new Error('Your project must be registered with EAS in order to use the credentials manager.');
|
|
124
137
|
}
|
|
125
|
-
|
|
126
|
-
const gradleContext = await (0, gradle_1.resolveGradleBuildContextAsync)(ctx.projectDir, buildProfile);
|
|
127
|
-
return {
|
|
128
|
-
gradleContext,
|
|
129
|
-
buildProfile,
|
|
130
|
-
};
|
|
138
|
+
return await (0, gradle_1.resolveGradleBuildContextAsync)(ctx.projectDir, buildProfile);
|
|
131
139
|
}
|
|
132
140
|
async runProjectSpecificActionAsync(ctx, action, gradleContext) {
|
|
133
141
|
(0, assert_1.default)(ctx.hasProjectContext, 'You must be in your project directory in order to perform this action');
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { CredentialsContext } from '../context';
|
|
2
1
|
import { ActionInfo } from './Actions';
|
|
3
2
|
import { Action } from './HelperActions';
|
|
4
3
|
export declare class ManageIos {
|
|
5
4
|
private callingAction;
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
private projectDir;
|
|
6
|
+
constructor(callingAction: Action, projectDir: string);
|
|
7
|
+
runAsync(currentActions?: ActionInfo[]): Promise<void>;
|
|
8
8
|
private createProjectContextAsync;
|
|
9
9
|
private runAccountSpecificActionAsync;
|
|
10
10
|
private runProjectSpecificActionAsync;
|
|
@@ -13,6 +13,7 @@ const projectUtils_1 = require("../../project/projectUtils");
|
|
|
13
13
|
const prompts_1 = require("../../prompts");
|
|
14
14
|
const Account_1 = require("../../user/Account");
|
|
15
15
|
const actions_1 = require("../../user/actions");
|
|
16
|
+
const context_1 = require("../context");
|
|
16
17
|
const AscApiKeyUtils_1 = require("../ios/actions/AscApiKeyUtils");
|
|
17
18
|
const AssignAscApiKey_1 = require("../ios/actions/AssignAscApiKey");
|
|
18
19
|
const AssignPushKey_1 = require("../ios/actions/AssignPushKey");
|
|
@@ -41,10 +42,20 @@ const IosActions_1 = require("./IosActions");
|
|
|
41
42
|
const SelectBuildProfileFromEasJson_1 = require("./SelectBuildProfileFromEasJson");
|
|
42
43
|
const SelectIosDistributionTypeGraphqlFromBuildProfile_1 = require("./SelectIosDistributionTypeGraphqlFromBuildProfile");
|
|
43
44
|
class ManageIos {
|
|
44
|
-
constructor(callingAction) {
|
|
45
|
+
constructor(callingAction, projectDir) {
|
|
45
46
|
this.callingAction = callingAction;
|
|
47
|
+
this.projectDir = projectDir;
|
|
46
48
|
}
|
|
47
|
-
async runAsync(
|
|
49
|
+
async runAsync(currentActions = IosActions_1.highLevelActions) {
|
|
50
|
+
const hasProjectContext = !!context_1.CredentialsContext.getExpoConfigInProject(this.projectDir);
|
|
51
|
+
const buildProfile = hasProjectContext
|
|
52
|
+
? await new SelectBuildProfileFromEasJson_1.SelectBuildProfileFromEasJson(this.projectDir, eas_build_job_1.Platform.IOS).runAsync()
|
|
53
|
+
: null;
|
|
54
|
+
const ctx = new context_1.CredentialsContext({
|
|
55
|
+
projectDir: process.cwd(),
|
|
56
|
+
user: await (0, actions_1.ensureLoggedInAsync)(),
|
|
57
|
+
env: buildProfile === null || buildProfile === void 0 ? void 0 : buildProfile.env,
|
|
58
|
+
});
|
|
48
59
|
const buildCredentialsActions = (0, IosActions_1.getBuildCredentialsActions)(ctx);
|
|
49
60
|
const pushKeyActions = (0, IosActions_1.getPushKeyActions)(ctx);
|
|
50
61
|
const ascApiKeyActions = (0, IosActions_1.getAscApiKeyActions)(ctx);
|
|
@@ -56,7 +67,14 @@ class ManageIos {
|
|
|
56
67
|
if (!account) {
|
|
57
68
|
throw new Error(`You do not have access to account: ${accountName}`);
|
|
58
69
|
}
|
|
59
|
-
|
|
70
|
+
let app = null;
|
|
71
|
+
let targets = null;
|
|
72
|
+
if (ctx.hasProjectContext) {
|
|
73
|
+
(0, assert_1.default)(buildProfile, 'buildProfile must be defined in project context');
|
|
74
|
+
const projectContext = await this.createProjectContextAsync(ctx, account, buildProfile);
|
|
75
|
+
app = projectContext.app;
|
|
76
|
+
targets = projectContext.targets;
|
|
77
|
+
}
|
|
60
78
|
while (true) {
|
|
61
79
|
try {
|
|
62
80
|
if (ctx.hasProjectContext) {
|
|
@@ -128,20 +146,13 @@ class ManageIos {
|
|
|
128
146
|
await new HelperActions_1.PressAnyKeyToContinue().runAsync();
|
|
129
147
|
}
|
|
130
148
|
}
|
|
131
|
-
async createProjectContextAsync(ctx, account) {
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
app: null,
|
|
135
|
-
targets: null,
|
|
136
|
-
buildProfile: null,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
149
|
+
async createProjectContextAsync(ctx, account, buildProfile) {
|
|
150
|
+
(0, assert_1.default)(ctx.hasProjectContext, 'createProjectContextAsync: must have project context.');
|
|
139
151
|
const maybeProjectId = await (0, projectUtils_1.promptToCreateProjectIfNotExistsAsync)(ctx.exp);
|
|
140
152
|
if (!maybeProjectId) {
|
|
141
153
|
throw new Error('Your project must be registered with EAS in order to use the credentials manager.');
|
|
142
154
|
}
|
|
143
155
|
const app = { account, projectName: ctx.exp.slug };
|
|
144
|
-
const buildProfile = await new SelectBuildProfileFromEasJson_1.SelectBuildProfileFromEasJson(ctx.projectDir, eas_build_job_1.Platform.IOS).runAsync(ctx);
|
|
145
156
|
const xcodeBuildContext = await (0, scheme_1.resolveXcodeBuildContextAsync)({
|
|
146
157
|
projectDir: ctx.projectDir,
|
|
147
158
|
nonInteractive: ctx.nonInteractive,
|
|
@@ -151,7 +162,6 @@ class ManageIos {
|
|
|
151
162
|
return {
|
|
152
163
|
app,
|
|
153
164
|
targets,
|
|
154
|
-
buildProfile,
|
|
155
165
|
};
|
|
156
166
|
}
|
|
157
167
|
async runAccountSpecificActionAsync(ctx, account, action) {
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Platform } from '@expo/eas-build-job';
|
|
2
2
|
import { BuildProfile } from '@expo/eas-json';
|
|
3
|
-
import { CredentialsContext } from '../context';
|
|
4
3
|
export declare class SelectBuildProfileFromEasJson<T extends Platform> {
|
|
5
4
|
private platform;
|
|
6
5
|
private easJsonReader;
|
|
7
6
|
constructor(projectDir: string, platform: T);
|
|
8
|
-
runAsync(
|
|
9
|
-
getProfileNameFromEasConfigAsync(
|
|
7
|
+
runAsync(): Promise<BuildProfile<T>>;
|
|
8
|
+
getProfileNameFromEasConfigAsync(): Promise<string>;
|
|
10
9
|
}
|