eoas 2.0.0 → 2.1.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.
|
@@ -17,7 +17,7 @@ class GenerateCerts extends core_1.Command {
|
|
|
17
17
|
message: 'In which directory would you like to store your code signing certificate (used by your expo app)?',
|
|
18
18
|
name: 'certificateOutputDir',
|
|
19
19
|
type: 'text',
|
|
20
|
-
initial: './
|
|
20
|
+
initial: './certs',
|
|
21
21
|
validate: v => {
|
|
22
22
|
try {
|
|
23
23
|
// eslint-disable-next-line
|
|
@@ -30,10 +30,10 @@ class GenerateCerts extends core_1.Command {
|
|
|
30
30
|
},
|
|
31
31
|
});
|
|
32
32
|
const { keyOutputDir } = await (0, prompts_1.promptAsync)({
|
|
33
|
-
message: 'In which directory would you like to store your key pair (used by your OTA Server) ?. ⚠️ Those
|
|
33
|
+
message: 'In which directory would you like to store your key pair (used by your OTA Server) ?. ⚠️ Those certss are sensitive and should be kept private.',
|
|
34
34
|
name: 'keyOutputDir',
|
|
35
35
|
type: 'text',
|
|
36
|
-
initial: './
|
|
36
|
+
initial: './certs',
|
|
37
37
|
validate: v => {
|
|
38
38
|
try {
|
|
39
39
|
// eslint-disable-next-line
|
package/dist/commands/init.js
CHANGED
|
@@ -49,19 +49,19 @@ class Init extends core_1.Command {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
const confirmed = await (0, prompts_1.confirmAsync)({
|
|
52
|
-
message: 'Do you have already generated your certificates
|
|
52
|
+
message: 'Do you have already generated your certificates for code signing?',
|
|
53
53
|
name: 'certificates',
|
|
54
54
|
type: 'confirm',
|
|
55
55
|
});
|
|
56
56
|
if (!confirmed) {
|
|
57
|
-
log_1.default.fail('You need to generate your certificates first by using npx eoas generate-
|
|
57
|
+
log_1.default.fail('You need to generate your certificates first by using npx eoas generate-certs');
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
const { codeSigningCertificatePath } = await (0, prompts_1.promptAsync)({
|
|
61
|
-
message: 'Enter the path to your code signing certificate (ex: ./
|
|
61
|
+
message: 'Enter the path to your code signing certificate (ex: ./certs/certificate.pem)',
|
|
62
62
|
name: 'codeSigningCertificatePath',
|
|
63
63
|
type: 'text',
|
|
64
|
-
initial: './
|
|
64
|
+
initial: './certs/certificate.pem',
|
|
65
65
|
validate: v => {
|
|
66
66
|
try {
|
|
67
67
|
const fullPath = path_1.default.resolve(projectDir, v);
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import { Config } from '@oclif/core/lib/config';
|
|
3
|
-
import { Client } from '../lib/vcs/vcs';
|
|
4
2
|
export default class Publish extends Command {
|
|
5
|
-
vcsClient: Client;
|
|
6
|
-
constructor(argv: string[], config: Config);
|
|
7
3
|
static args: {};
|
|
8
4
|
static description: string;
|
|
9
5
|
static examples: string[];
|
package/dist/commands/publish.js
CHANGED
|
@@ -21,11 +21,6 @@ const runtimeVersion_1 = require("../lib/runtimeVersion");
|
|
|
21
21
|
const vcs_1 = require("../lib/vcs");
|
|
22
22
|
const workflow_1 = require("../lib/workflow");
|
|
23
23
|
class Publish extends core_1.Command {
|
|
24
|
-
vcsClient;
|
|
25
|
-
constructor(argv, config) {
|
|
26
|
-
super(argv, config);
|
|
27
|
-
this.vcsClient = (0, vcs_1.resolveVcsClient)(false);
|
|
28
|
-
}
|
|
29
24
|
static args = {};
|
|
30
25
|
static description = 'Publish a new update to the self-hosted update server';
|
|
31
26
|
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
@@ -61,25 +56,26 @@ class Publish extends core_1.Command {
|
|
|
61
56
|
const credentials = (0, auth_1.retrieveExpoCredentials)();
|
|
62
57
|
if (!credentials.token && !credentials.sessionSecret) {
|
|
63
58
|
log_1.default.error('You are not logged to eas, please run `eas login`');
|
|
64
|
-
|
|
59
|
+
process.exit(1);
|
|
65
60
|
}
|
|
66
61
|
const { flags } = await this.parse(Publish);
|
|
67
62
|
const { platform, nonInteractive, branch, channel } = this.sanitizeFlags(flags);
|
|
68
63
|
if (!branch) {
|
|
69
64
|
log_1.default.error('Branch name is required');
|
|
70
|
-
|
|
65
|
+
process.exit(1);
|
|
71
66
|
}
|
|
72
67
|
if (!channel) {
|
|
73
68
|
log_1.default.error('Channel name is required');
|
|
74
|
-
|
|
69
|
+
process.exit(1);
|
|
75
70
|
}
|
|
76
|
-
|
|
77
|
-
await
|
|
71
|
+
const vcsClient = (0, vcs_1.resolveVcsClient)(true);
|
|
72
|
+
await vcsClient.ensureRepoExistsAsync();
|
|
73
|
+
await (0, repo_1.ensureRepoIsCleanAsync)(vcsClient, nonInteractive);
|
|
78
74
|
const projectDir = process.cwd();
|
|
79
75
|
const hasExpo = (0, package_1.isExpoInstalled)(projectDir);
|
|
80
76
|
if (!hasExpo) {
|
|
81
77
|
log_1.default.error('Expo is not installed in this project. Please install Expo first.');
|
|
82
|
-
|
|
78
|
+
process.exit(1);
|
|
83
79
|
}
|
|
84
80
|
const privateConfig = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, {
|
|
85
81
|
env: {
|
|
@@ -89,7 +85,7 @@ class Publish extends core_1.Command {
|
|
|
89
85
|
const updateUrl = (0, expoConfig_1.getExpoConfigUpdateUrl)(privateConfig);
|
|
90
86
|
if (!updateUrl) {
|
|
91
87
|
log_1.default.error("Update url is not setup in your config. Please run 'eoas init' to setup the update url");
|
|
92
|
-
|
|
88
|
+
process.exit(1);
|
|
93
89
|
}
|
|
94
90
|
let baseUrl;
|
|
95
91
|
try {
|
|
@@ -98,7 +94,7 @@ class Publish extends core_1.Command {
|
|
|
98
94
|
}
|
|
99
95
|
catch (e) {
|
|
100
96
|
log_1.default.error('Invalid URL', e);
|
|
101
|
-
|
|
97
|
+
process.exit(1);
|
|
102
98
|
}
|
|
103
99
|
if (!nonInteractive) {
|
|
104
100
|
const confirmed = await (0, prompts_1.confirmAsync)({
|
|
@@ -108,44 +104,51 @@ class Publish extends core_1.Command {
|
|
|
108
104
|
});
|
|
109
105
|
if (!confirmed) {
|
|
110
106
|
log_1.default.error('Please run `eoas init` to setup the correct update url');
|
|
107
|
+
process.exit(1);
|
|
111
108
|
}
|
|
112
109
|
}
|
|
113
|
-
const runtimeSpinner = (0, ora_1.ora)('Resolving runtime version').start();
|
|
110
|
+
const runtimeSpinner = (0, ora_1.ora)('🔄 Resolving runtime version...').start();
|
|
114
111
|
const runtimeVersions = [
|
|
115
112
|
...(!platform || platform === expoConfig_1.RequestedPlatform.All || platform === expoConfig_1.RequestedPlatform.Ios
|
|
116
113
|
? [
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
{
|
|
115
|
+
runtimeVersion: (await (0, runtimeVersion_1.resolveRuntimeVersionAsync)({
|
|
116
|
+
exp: privateConfig,
|
|
117
|
+
platform: 'ios',
|
|
118
|
+
workflow: await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.IOS, vcsClient),
|
|
119
|
+
projectDir,
|
|
120
|
+
env: {
|
|
121
|
+
RELEASE_CHANNEL: channel,
|
|
122
|
+
},
|
|
123
|
+
}))?.runtimeVersion,
|
|
119
124
|
platform: 'ios',
|
|
120
|
-
|
|
121
|
-
projectDir,
|
|
122
|
-
env: {
|
|
123
|
-
RELEASE_CHANNEL: channel,
|
|
124
|
-
},
|
|
125
|
-
}))?.runtimeVersion,
|
|
125
|
+
},
|
|
126
126
|
]
|
|
127
127
|
: []),
|
|
128
128
|
...(!platform || platform === expoConfig_1.RequestedPlatform.All || platform === expoConfig_1.RequestedPlatform.Android
|
|
129
129
|
? [
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
{
|
|
131
|
+
runtimeVersion: (await (0, runtimeVersion_1.resolveRuntimeVersionAsync)({
|
|
132
|
+
exp: privateConfig,
|
|
133
|
+
platform: 'android',
|
|
134
|
+
workflow: await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.ANDROID, vcsClient),
|
|
135
|
+
projectDir,
|
|
136
|
+
env: {
|
|
137
|
+
RELEASE_CHANNEL: channel,
|
|
138
|
+
},
|
|
139
|
+
}))?.runtimeVersion,
|
|
132
140
|
platform: 'android',
|
|
133
|
-
|
|
134
|
-
projectDir,
|
|
135
|
-
env: {
|
|
136
|
-
RELEASE_CHANNEL: channel,
|
|
137
|
-
},
|
|
138
|
-
}))?.runtimeVersion,
|
|
141
|
+
},
|
|
139
142
|
]
|
|
140
143
|
: []),
|
|
141
|
-
].filter(
|
|
144
|
+
].filter(({ runtimeVersion }) => !!runtimeVersion);
|
|
142
145
|
if (!runtimeVersions.length) {
|
|
143
146
|
runtimeSpinner.fail('Could not resolve runtime versions for the requested platforms');
|
|
144
147
|
log_1.default.error('Could not resolve runtime versions for the requested platforms');
|
|
145
|
-
|
|
148
|
+
process.exit(1);
|
|
146
149
|
}
|
|
147
|
-
runtimeSpinner.succeed('Runtime versions resolved');
|
|
148
|
-
const exportSpinner = (0, ora_1.ora)(
|
|
150
|
+
runtimeSpinner.succeed('✅ Runtime versions resolved');
|
|
151
|
+
const exportSpinner = (0, ora_1.ora)('📦 Exporting project files...').start();
|
|
149
152
|
try {
|
|
150
153
|
await (0, spawn_async_1.default)('rm', ['-rf', 'dist'], { cwd: projectDir });
|
|
151
154
|
const { stdout } = await (0, spawn_async_1.default)('npx', ['expo', 'export', '--output-dir', 'dist'], {
|
|
@@ -155,39 +158,46 @@ class Publish extends core_1.Command {
|
|
|
155
158
|
EXPO_NO_DOTENV: '1',
|
|
156
159
|
},
|
|
157
160
|
});
|
|
158
|
-
exportSpinner.succeed('Project exported successfully');
|
|
161
|
+
exportSpinner.succeed('🚀 Project exported successfully');
|
|
159
162
|
log_1.default.withInfo(stdout);
|
|
160
163
|
}
|
|
161
164
|
catch {
|
|
162
|
-
exportSpinner.fail('Failed to export the project');
|
|
165
|
+
exportSpinner.fail('❌ Failed to export the project');
|
|
166
|
+
process.exit(1);
|
|
163
167
|
}
|
|
164
168
|
const publicConfig = await (0, expoConfig_1.getPublicExpoConfigAsync)(projectDir, {
|
|
165
169
|
skipSDKVersionRequirement: true,
|
|
166
170
|
});
|
|
167
171
|
if (!publicConfig) {
|
|
168
172
|
log_1.default.error('Could not find Expo config in this project. Please make sure you have an Expo config.');
|
|
169
|
-
|
|
173
|
+
process.exit(1);
|
|
170
174
|
}
|
|
171
175
|
// eslint-disable-next-line
|
|
172
176
|
fs_extra_1.default.writeJsonSync(path_1.default.join(projectDir, 'dist', 'expoConfig.json'), publicConfig, {
|
|
173
177
|
spaces: 2,
|
|
174
178
|
});
|
|
175
179
|
log_1.default.withInfo('expoConfig.json file created in dist directory');
|
|
176
|
-
const uploadFilesSpinner = (0, ora_1.ora)('Uploading files
|
|
180
|
+
const uploadFilesSpinner = (0, ora_1.ora)('📤 Uploading files...').start();
|
|
177
181
|
const files = (0, assets_1.computeFilesRequests)(projectDir, platform || expoConfig_1.RequestedPlatform.All);
|
|
178
182
|
if (!files.length) {
|
|
179
183
|
uploadFilesSpinner.fail('No files to upload');
|
|
184
|
+
process.exit(1);
|
|
180
185
|
}
|
|
186
|
+
let uploadUrls = [];
|
|
181
187
|
try {
|
|
182
|
-
|
|
188
|
+
uploadUrls = await Promise.all(runtimeVersions.map(async ({ runtimeVersion, platform }) => {
|
|
183
189
|
if (!runtimeVersion) {
|
|
184
190
|
throw new Error('Runtime version is not resolved');
|
|
185
191
|
}
|
|
186
|
-
return
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
return {
|
|
193
|
+
...(await (0, assets_1.requestUploadUrls)({
|
|
194
|
+
fileNames: files.map(file => file.path),
|
|
195
|
+
}, `${baseUrl}/requestUploadUrl/${branch}`, credentials, runtimeVersion)),
|
|
196
|
+
runtimeVersion,
|
|
197
|
+
platform,
|
|
198
|
+
};
|
|
189
199
|
}));
|
|
190
|
-
const allItems = uploadUrls.
|
|
200
|
+
const allItems = uploadUrls.flatMap(({ uploadRequests }) => uploadRequests);
|
|
191
201
|
await Promise.all(allItems.map(async (itm) => {
|
|
192
202
|
const isLocalBucketFileUpload = itm.requestUploadUrl.startsWith(`${baseUrl}/uploadLocalFile`);
|
|
193
203
|
const formData = new form_data_1.default();
|
|
@@ -234,15 +244,61 @@ class Publish extends core_1.Command {
|
|
|
234
244
|
body: buffer,
|
|
235
245
|
});
|
|
236
246
|
if (!response.ok) {
|
|
237
|
-
log_1.default.error('
|
|
238
|
-
|
|
247
|
+
log_1.default.error('❌ File upload failed', await response.text());
|
|
248
|
+
process.exit(1);
|
|
239
249
|
}
|
|
240
250
|
file.close();
|
|
241
251
|
}));
|
|
242
|
-
uploadFilesSpinner.succeed('Files uploaded successfully');
|
|
252
|
+
uploadFilesSpinner.succeed('✅ Files uploaded successfully');
|
|
243
253
|
}
|
|
244
|
-
catch {
|
|
245
|
-
uploadFilesSpinner.fail('Failed to upload static files');
|
|
254
|
+
catch (e) {
|
|
255
|
+
uploadFilesSpinner.fail('❌ Failed to upload static files');
|
|
256
|
+
log_1.default.error(e);
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
const markAsFinishedSpinner = (0, ora_1.ora)('🔗 Marking the updates as finished...').start();
|
|
260
|
+
const results = await Promise.all(uploadUrls.map(async ({ updateId, platform, runtimeVersion }) => {
|
|
261
|
+
const response = await (0, node_fetch_1.default)(`${baseUrl}/markUpdateAsUploaded/${branch}?platform=${platform}&updateId=${updateId}&runtimeVersion=${runtimeVersion}`, {
|
|
262
|
+
method: 'POST',
|
|
263
|
+
headers: {
|
|
264
|
+
...(0, auth_1.getAuthExpoHeaders)(credentials),
|
|
265
|
+
'Content-Type': 'application/json',
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
// If success and status code = 200
|
|
269
|
+
if (response.ok) {
|
|
270
|
+
log_1.default.withInfo(`✅ Update ready for ${platform}`);
|
|
271
|
+
return 'deployed';
|
|
272
|
+
}
|
|
273
|
+
// If response.status === 406 duplicate update
|
|
274
|
+
if (response.status === 406) {
|
|
275
|
+
log_1.default.withInfo(`⚠️ There is no change in the update for ${platform}, ignored...`);
|
|
276
|
+
return 'identical';
|
|
277
|
+
}
|
|
278
|
+
log_1.default.error('❌ Failed to mark the update as finished for platform', platform);
|
|
279
|
+
log_1.default.newLine();
|
|
280
|
+
log_1.default.error(await response.text());
|
|
281
|
+
return 'error';
|
|
282
|
+
}));
|
|
283
|
+
const erroredUpdates = results.filter(result => result === 'error');
|
|
284
|
+
const hasSuccess = results.some(result => result === 'deployed');
|
|
285
|
+
const allIdentical = results.every(result => result === 'identical');
|
|
286
|
+
if (allIdentical) {
|
|
287
|
+
markAsFinishedSpinner.warn('⚠️ No changes found in the update, nothing to deploy');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (erroredUpdates.length) {
|
|
291
|
+
markAsFinishedSpinner.fail('❌ Some errors occurred while marking updates as finished');
|
|
292
|
+
throw new Error();
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
markAsFinishedSpinner.succeed(`\n✅ Your update has been successfully pushed to ${updateUrl}`);
|
|
296
|
+
}
|
|
297
|
+
if (hasSuccess) {
|
|
298
|
+
log_1.default.withInfo(`🔗 Channel: \`${channel}\``);
|
|
299
|
+
log_1.default.withInfo(`🌿 Branch: \`${branch}\``);
|
|
300
|
+
log_1.default.withInfo(`⏳ Deployed at: \`${new Date().toUTCString()}\`\n`);
|
|
301
|
+
log_1.default.withInfo('🔥 Your users will receive the latest update automatically!');
|
|
246
302
|
}
|
|
247
303
|
}
|
|
248
304
|
}
|
package/dist/lib/assets.d.ts
CHANGED
|
@@ -15,5 +15,8 @@ export interface RequestUploadUrlItem {
|
|
|
15
15
|
}
|
|
16
16
|
export declare function requestUploadUrls(body: {
|
|
17
17
|
fileNames: string[];
|
|
18
|
-
}, requestUploadUrl: string, auth: ExpoCredentials, runtimeVersion: string): Promise<
|
|
18
|
+
}, requestUploadUrl: string, auth: ExpoCredentials, runtimeVersion: string): Promise<{
|
|
19
|
+
uploadRequests: RequestUploadUrlItem[];
|
|
20
|
+
updateId: string;
|
|
21
|
+
}>;
|
|
19
22
|
export {};
|