eoas 2.2.81 → 2.3.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/dist/commands/publish.d.ts +1 -1
- package/dist/commands/publish.js +40 -36
- package/dist/commands/republish.d.ts +12 -0
- package/dist/commands/republish.js +141 -0
- package/dist/commands/rollback.d.ts +12 -0
- package/dist/commands/rollback.js +155 -0
- package/dist/lib/channel.d.ts +2 -0
- package/dist/lib/channel.js +24 -0
- package/dist/lib/expoConfig.d.ts +1 -0
- package/dist/lib/expoConfig.js +17 -1
- package/package.json +2 -2
- package/bin/dev.cmd +0 -3
- package/bin/dev.js +0 -6
- package/bin/run.cmd +0 -3
- package/bin/run.js +0 -7
|
@@ -5,7 +5,7 @@ export default class Publish extends Command {
|
|
|
5
5
|
static examples: string[];
|
|
6
6
|
static flags: {
|
|
7
7
|
platform: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
-
channel: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
branch: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
10
|
nonInteractive: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
outputDir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
package/dist/commands/publish.js
CHANGED
|
@@ -33,7 +33,10 @@ class Publish extends core_1.Command {
|
|
|
33
33
|
}),
|
|
34
34
|
channel: core_1.Flags.string({
|
|
35
35
|
description: 'Name of the channel to publish the update to',
|
|
36
|
-
required:
|
|
36
|
+
required: false,
|
|
37
|
+
deprecated: {
|
|
38
|
+
message: 'Channel was initially used to provide RELEASE_CHANNEL in the environment when resolving the runtime version. It is no longer needed, you can use RELEASE_CHANNEL={channel} eoas publish --branch={branch} instead',
|
|
39
|
+
},
|
|
37
40
|
}),
|
|
38
41
|
branch: core_1.Flags.string({
|
|
39
42
|
description: 'Name of the branch to point to',
|
|
@@ -53,8 +56,8 @@ class Publish extends core_1.Command {
|
|
|
53
56
|
platform: flags.platform,
|
|
54
57
|
branch: flags.branch,
|
|
55
58
|
nonInteractive: flags.nonInteractive,
|
|
56
|
-
channel: flags.channel,
|
|
57
59
|
outputDir: flags.outputDir,
|
|
60
|
+
providedDeprecatedChannel: flags.channel,
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
async run() {
|
|
@@ -64,47 +67,32 @@ class Publish extends core_1.Command {
|
|
|
64
67
|
process.exit(1);
|
|
65
68
|
}
|
|
66
69
|
const { flags } = await this.parse(Publish);
|
|
67
|
-
const { platform, nonInteractive, branch,
|
|
70
|
+
const { platform, nonInteractive, branch, outputDir, providedDeprecatedChannel } = this.sanitizeFlags(flags);
|
|
68
71
|
if (!branch) {
|
|
69
72
|
log_1.default.error('Branch name is required');
|
|
70
73
|
process.exit(1);
|
|
71
74
|
}
|
|
72
|
-
if (!channel) {
|
|
73
|
-
log_1.default.error('Channel name is required');
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
const vcsClient = (0, vcs_1.resolveVcsClient)(true);
|
|
77
|
-
await vcsClient.ensureRepoExistsAsync();
|
|
78
|
-
const commitHash = await vcsClient.getCommitHashAsync();
|
|
79
|
-
await (0, repo_1.ensureRepoIsCleanAsync)(vcsClient, nonInteractive);
|
|
80
75
|
const projectDir = process.cwd();
|
|
81
76
|
const hasExpo = (0, package_1.isExpoInstalled)(projectDir);
|
|
82
77
|
if (!hasExpo) {
|
|
83
78
|
log_1.default.error('Expo is not installed in this project. Please install Expo first.');
|
|
84
79
|
process.exit(1);
|
|
85
80
|
}
|
|
86
|
-
const
|
|
81
|
+
const vcsClient = (0, vcs_1.resolveVcsClient)(true);
|
|
82
|
+
await (0, repo_1.ensureRepoIsCleanAsync)(vcsClient, nonInteractive);
|
|
83
|
+
const config = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, {
|
|
87
84
|
env: {
|
|
88
|
-
|
|
85
|
+
...process.env,
|
|
86
|
+
...(providedDeprecatedChannel ? { RELEASE_CHANNEL: providedDeprecatedChannel } : {}),
|
|
89
87
|
},
|
|
90
88
|
});
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
log_1.default.error("Update url is not setup in your config. Please run 'eoas init' to setup the update url");
|
|
89
|
+
const serverUrl = await (0, expoConfig_1.resolveServerUrl)(config).catch(e => {
|
|
90
|
+
log_1.default.error(e.message);
|
|
94
91
|
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
let baseUrl;
|
|
97
|
-
try {
|
|
98
|
-
const parsedUrl = new URL(updateUrl);
|
|
99
|
-
baseUrl = parsedUrl.origin;
|
|
100
|
-
}
|
|
101
|
-
catch (e) {
|
|
102
|
-
log_1.default.error('Invalid URL', e);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
92
|
+
});
|
|
105
93
|
if (!nonInteractive) {
|
|
106
94
|
const confirmed = await (0, prompts_1.confirmAsync)({
|
|
107
|
-
message: `Is this the correct URL of your self-hosted update server? ${
|
|
95
|
+
message: `Is this the correct URL of your self-hosted update server? ${serverUrl}`,
|
|
108
96
|
name: 'export',
|
|
109
97
|
type: 'confirm',
|
|
110
98
|
});
|
|
@@ -113,18 +101,22 @@ class Publish extends core_1.Command {
|
|
|
113
101
|
process.exit(1);
|
|
114
102
|
}
|
|
115
103
|
}
|
|
104
|
+
const commitHash = await vcsClient.getCommitHashAsync();
|
|
116
105
|
const runtimeSpinner = (0, ora_1.ora)('🔄 Resolving runtime version...').start();
|
|
117
106
|
const runtimeVersions = [
|
|
118
107
|
...(!platform || platform === expoConfig_1.RequestedPlatform.All || platform === expoConfig_1.RequestedPlatform.Ios
|
|
119
108
|
? [
|
|
120
109
|
{
|
|
121
110
|
runtimeVersion: (await (0, runtimeVersion_1.resolveRuntimeVersionAsync)({
|
|
122
|
-
exp:
|
|
111
|
+
exp: config,
|
|
123
112
|
platform: 'ios',
|
|
124
113
|
workflow: await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.IOS, vcsClient),
|
|
125
114
|
projectDir,
|
|
126
115
|
env: {
|
|
127
|
-
|
|
116
|
+
...process.env,
|
|
117
|
+
...(providedDeprecatedChannel
|
|
118
|
+
? { RELEASE_CHANNEL: providedDeprecatedChannel }
|
|
119
|
+
: {}),
|
|
128
120
|
},
|
|
129
121
|
}))?.runtimeVersion,
|
|
130
122
|
platform: 'ios',
|
|
@@ -135,12 +127,15 @@ class Publish extends core_1.Command {
|
|
|
135
127
|
? [
|
|
136
128
|
{
|
|
137
129
|
runtimeVersion: (await (0, runtimeVersion_1.resolveRuntimeVersionAsync)({
|
|
138
|
-
exp:
|
|
130
|
+
exp: config,
|
|
139
131
|
platform: 'android',
|
|
140
132
|
workflow: await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.ANDROID, vcsClient),
|
|
141
133
|
projectDir,
|
|
142
134
|
env: {
|
|
143
|
-
|
|
135
|
+
...process.env,
|
|
136
|
+
...(providedDeprecatedChannel
|
|
137
|
+
? { RELEASE_CHANNEL: providedDeprecatedChannel }
|
|
138
|
+
: {}),
|
|
144
139
|
},
|
|
145
140
|
}))?.runtimeVersion,
|
|
146
141
|
platform: 'android',
|
|
@@ -154,6 +149,16 @@ class Publish extends core_1.Command {
|
|
|
154
149
|
process.exit(1);
|
|
155
150
|
}
|
|
156
151
|
runtimeSpinner.succeed('✅ Runtime versions resolved');
|
|
152
|
+
const cleaningSpinner = (0, ora_1.ora)(`🗑️ Cleaning up ${outputDir} directory...`).start();
|
|
153
|
+
try {
|
|
154
|
+
await (0, spawn_async_1.default)('rm', ['-rf', outputDir], { cwd: projectDir });
|
|
155
|
+
cleaningSpinner.succeed('✅ Cleanup completed');
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
cleaningSpinner.fail('❌ Failed to clean up the output directory');
|
|
159
|
+
log_1.default.error(e);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
157
162
|
const exportSpinner = (0, ora_1.ora)('📦 Exporting project files...').start();
|
|
158
163
|
try {
|
|
159
164
|
await (0, spawn_async_1.default)('rm', ['-rf', outputDir], { cwd: projectDir });
|
|
@@ -200,7 +205,7 @@ class Publish extends core_1.Command {
|
|
|
200
205
|
body: {
|
|
201
206
|
fileNames: files.map(file => file.path),
|
|
202
207
|
},
|
|
203
|
-
requestUploadUrl: `${
|
|
208
|
+
requestUploadUrl: `${serverUrl}/requestUploadUrl/${branch}`,
|
|
204
209
|
auth: credentials,
|
|
205
210
|
runtimeVersion,
|
|
206
211
|
platform,
|
|
@@ -212,7 +217,7 @@ class Publish extends core_1.Command {
|
|
|
212
217
|
}));
|
|
213
218
|
const allItems = uploadUrls.flatMap(({ uploadRequests }) => uploadRequests);
|
|
214
219
|
await Promise.all(allItems.map(async (itm) => {
|
|
215
|
-
const isLocalBucketFileUpload = itm.requestUploadUrl.startsWith(`${
|
|
220
|
+
const isLocalBucketFileUpload = itm.requestUploadUrl.startsWith(`${serverUrl}/uploadLocalFile`);
|
|
216
221
|
const formData = new form_data_1.default();
|
|
217
222
|
let file;
|
|
218
223
|
try {
|
|
@@ -271,7 +276,7 @@ class Publish extends core_1.Command {
|
|
|
271
276
|
}
|
|
272
277
|
const markAsFinishedSpinner = (0, ora_1.ora)('🔗 Marking the updates as finished...').start();
|
|
273
278
|
const results = await Promise.all(uploadUrls.map(async ({ updateId, platform, runtimeVersion }) => {
|
|
274
|
-
const response = await (0, fetch_1.fetchWithRetries)(`${
|
|
279
|
+
const response = await (0, fetch_1.fetchWithRetries)(`${serverUrl}/markUpdateAsUploaded/${branch}?platform=${platform}&updateId=${updateId}&runtimeVersion=${runtimeVersion}`, {
|
|
275
280
|
method: 'POST',
|
|
276
281
|
headers: {
|
|
277
282
|
...(0, auth_1.getAuthExpoHeaders)(credentials),
|
|
@@ -305,10 +310,9 @@ class Publish extends core_1.Command {
|
|
|
305
310
|
throw new Error();
|
|
306
311
|
}
|
|
307
312
|
else {
|
|
308
|
-
markAsFinishedSpinner.succeed(`\n✅ Your update has been successfully pushed to ${
|
|
313
|
+
markAsFinishedSpinner.succeed(`\n✅ Your update has been successfully pushed to ${serverUrl}`);
|
|
309
314
|
}
|
|
310
315
|
if (hasSuccess) {
|
|
311
|
-
log_1.default.withInfo(`🔗 Channel: \`${channel}\``);
|
|
312
316
|
log_1.default.withInfo(`🌿 Branch: \`${branch}\``);
|
|
313
317
|
log_1.default.withInfo(`⏳ Deployed at: \`${new Date().toUTCString()}\`\n`);
|
|
314
318
|
log_1.default.withInfo('🔥 Your users will receive the latest update automatically!');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Publish extends Command {
|
|
3
|
+
static args: {};
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
branch: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
private sanitizeFlags;
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const ora_1 = tslib_1.__importDefault(require("ora"));
|
|
6
|
+
const auth_1 = require("../lib/auth");
|
|
7
|
+
const expoConfig_1 = require("../lib/expoConfig");
|
|
8
|
+
const fetch_1 = require("../lib/fetch");
|
|
9
|
+
const log_1 = tslib_1.__importDefault(require("../lib/log"));
|
|
10
|
+
const package_1 = require("../lib/package");
|
|
11
|
+
const prompts_1 = require("../lib/prompts");
|
|
12
|
+
const vcs_1 = require("../lib/vcs");
|
|
13
|
+
class Publish extends core_1.Command {
|
|
14
|
+
static args = {};
|
|
15
|
+
static description = 'Republish a previous update to a branch';
|
|
16
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
17
|
+
static flags = {
|
|
18
|
+
branch: core_1.Flags.string({
|
|
19
|
+
description: 'Name of the branch to point to',
|
|
20
|
+
required: true,
|
|
21
|
+
}),
|
|
22
|
+
platform: core_1.Flags.string({
|
|
23
|
+
type: 'option',
|
|
24
|
+
options: ['ios', 'android'],
|
|
25
|
+
default: 'all',
|
|
26
|
+
required: true,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
sanitizeFlags(flags) {
|
|
30
|
+
return {
|
|
31
|
+
branch: flags.branch,
|
|
32
|
+
platform: flags.platform,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async run() {
|
|
36
|
+
const credentials = (0, auth_1.retrieveExpoCredentials)();
|
|
37
|
+
if (!credentials.token && !credentials.sessionSecret) {
|
|
38
|
+
log_1.default.error('You are not logged to eas, please run `eas login`');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const { flags } = await this.parse(Publish);
|
|
42
|
+
const { branch, platform } = this.sanitizeFlags(flags);
|
|
43
|
+
if (!branch) {
|
|
44
|
+
log_1.default.error('Branch name is required');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
if (!platform) {
|
|
48
|
+
log_1.default.error('Platform is required');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const vcsClient = (0, vcs_1.resolveVcsClient)(true);
|
|
52
|
+
await vcsClient.ensureRepoExistsAsync();
|
|
53
|
+
// const commitHash = await vcsClient.getCommitHashAsync();
|
|
54
|
+
const projectDir = process.cwd();
|
|
55
|
+
const hasExpo = (0, package_1.isExpoInstalled)(projectDir);
|
|
56
|
+
if (!hasExpo) {
|
|
57
|
+
log_1.default.error('Expo is not installed in this project. Please install Expo first.');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const privateConfig = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, {
|
|
61
|
+
env: process.env,
|
|
62
|
+
});
|
|
63
|
+
const updateUrl = (0, expoConfig_1.getExpoConfigUpdateUrl)(privateConfig);
|
|
64
|
+
if (!updateUrl) {
|
|
65
|
+
log_1.default.error("Update url is not setup in your config. Please run 'eoas init' to setup the update url");
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
let baseUrl;
|
|
69
|
+
try {
|
|
70
|
+
const parsedUrl = new URL(updateUrl);
|
|
71
|
+
baseUrl = parsedUrl.origin;
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
log_1.default.error('Invalid URL', e);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const runtimeVersionsEndpoint = `${baseUrl}/api/branch/${branch}/runtimeVersions`;
|
|
78
|
+
const response = await (0, fetch_1.fetchWithRetries)(runtimeVersionsEndpoint, {
|
|
79
|
+
headers: { ...(0, auth_1.getAuthExpoHeaders)(credentials), 'use-expo-auth': 'true' },
|
|
80
|
+
});
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
log_1.default.error(`Failed to fetch runtime versions: ${await response.text()}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const runtimeVersions = (await response.json());
|
|
86
|
+
const filteredRuntimeVersions = runtimeVersions.filter(runtimeVersion => runtimeVersion.numberOfUpdates > 1);
|
|
87
|
+
if (filteredRuntimeVersions.length === 0) {
|
|
88
|
+
log_1.default.error('No runtime versions found');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
// Ask the user to select a runtime version
|
|
92
|
+
const selectedRuntimeVersion = await (0, prompts_1.promptAsync)({
|
|
93
|
+
type: 'select',
|
|
94
|
+
name: 'runtimeVersion',
|
|
95
|
+
message: 'Select a runtime version',
|
|
96
|
+
choices: filteredRuntimeVersions.map(runtimeVersion => ({
|
|
97
|
+
title: runtimeVersion.runtimeVersion,
|
|
98
|
+
value: runtimeVersion.runtimeVersion,
|
|
99
|
+
})),
|
|
100
|
+
});
|
|
101
|
+
log_1.default.log(`Selected runtime version: ${selectedRuntimeVersion.runtimeVersion}`);
|
|
102
|
+
const updatesEndpoint = `${baseUrl}/api/branch/${branch}/runtimeVersion/${selectedRuntimeVersion.runtimeVersion}/updates`;
|
|
103
|
+
const updatesResponse = await (0, fetch_1.fetchWithRetries)(updatesEndpoint, {
|
|
104
|
+
headers: { ...(0, auth_1.getAuthExpoHeaders)(credentials), 'use-expo-auth': 'true' },
|
|
105
|
+
});
|
|
106
|
+
if (!updatesResponse.ok) {
|
|
107
|
+
log_1.default.error(`Failed to fetch updates: ${await updatesResponse.text()}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
const updates = (await updatesResponse.json()).filter(u => {
|
|
111
|
+
return u.updateUUID !== 'Rollback to embedded' && u.platform === platform;
|
|
112
|
+
});
|
|
113
|
+
const selectedUpdated = await (0, prompts_1.promptAsync)({
|
|
114
|
+
type: 'select',
|
|
115
|
+
name: 'update',
|
|
116
|
+
message: 'Select an update to republish',
|
|
117
|
+
choices: updates.map(update => ({
|
|
118
|
+
title: update.updateUUID,
|
|
119
|
+
value: update,
|
|
120
|
+
description: `Created at: ${update.createdAt}, Platform: ${update.platform}, Commit hash: ${update.commitHash}`,
|
|
121
|
+
})),
|
|
122
|
+
});
|
|
123
|
+
log_1.default.log(`Re-publishing update: ${selectedUpdated.update.updateUUID}`);
|
|
124
|
+
const republishEndpoint = `${baseUrl}/republish/${branch}?platform=${platform}&runtimeVersion=${selectedRuntimeVersion.runtimeVersion}&updateId=${selectedUpdated.update.updateId}&commitHash=${selectedUpdated.update.commitHash}`;
|
|
125
|
+
const republishSpinner = (0, ora_1.default)('🔄 Republishing update...').start();
|
|
126
|
+
const republishResponse = await (0, fetch_1.fetchWithRetries)(republishEndpoint, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: {
|
|
129
|
+
...(0, auth_1.getAuthExpoHeaders)(credentials),
|
|
130
|
+
'Content-Type': 'application/json',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
if (!republishResponse.ok) {
|
|
134
|
+
republishSpinner.fail('❌ Republish failed');
|
|
135
|
+
log_1.default.error(`Failed to republish update: ${await republishResponse.text()}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
republishSpinner.succeed('✅ Republish successful');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.default = Publish;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Publish extends Command {
|
|
3
|
+
static args: {};
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
branch: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
private sanitizeFlags;
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const auth_1 = require("../lib/auth");
|
|
7
|
+
const expoConfig_1 = require("../lib/expoConfig");
|
|
8
|
+
const fetch_1 = require("../lib/fetch");
|
|
9
|
+
const log_1 = tslib_1.__importDefault(require("../lib/log"));
|
|
10
|
+
const ora_1 = require("../lib/ora");
|
|
11
|
+
const package_1 = require("../lib/package");
|
|
12
|
+
const prompts_1 = require("../lib/prompts");
|
|
13
|
+
const runtimeVersion_1 = require("../lib/runtimeVersion");
|
|
14
|
+
const vcs_1 = require("../lib/vcs");
|
|
15
|
+
const workflow_1 = require("../lib/workflow");
|
|
16
|
+
class Publish extends core_1.Command {
|
|
17
|
+
static args = {};
|
|
18
|
+
static description = 'Publish a new rollback to the self-hosted update server';
|
|
19
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
20
|
+
static flags = {
|
|
21
|
+
platform: core_1.Flags.string({
|
|
22
|
+
type: 'option',
|
|
23
|
+
options: Object.values(expoConfig_1.RequestedPlatform),
|
|
24
|
+
default: expoConfig_1.RequestedPlatform.All,
|
|
25
|
+
required: false,
|
|
26
|
+
}),
|
|
27
|
+
branch: core_1.Flags.string({
|
|
28
|
+
description: 'Name of the branch to point to',
|
|
29
|
+
required: true,
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
sanitizeFlags(flags) {
|
|
33
|
+
return {
|
|
34
|
+
platform: flags.platform,
|
|
35
|
+
branch: flags.branch,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async run() {
|
|
39
|
+
const credentials = (0, auth_1.retrieveExpoCredentials)();
|
|
40
|
+
if (!credentials.token && !credentials.sessionSecret) {
|
|
41
|
+
log_1.default.error('You are not logged to eas, please run `eas login`');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const { flags } = await this.parse(Publish);
|
|
45
|
+
const { platform, branch } = this.sanitizeFlags(flags);
|
|
46
|
+
if (!branch) {
|
|
47
|
+
log_1.default.error('Branch name is required');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const vcsClient = (0, vcs_1.resolveVcsClient)(true);
|
|
51
|
+
await vcsClient.ensureRepoExistsAsync();
|
|
52
|
+
const commitHash = await vcsClient.getCommitHashAsync();
|
|
53
|
+
const projectDir = process.cwd();
|
|
54
|
+
const hasExpo = (0, package_1.isExpoInstalled)(projectDir);
|
|
55
|
+
if (!hasExpo) {
|
|
56
|
+
log_1.default.error('Expo is not installed in this project. Please install Expo first.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const confirmed = await (0, prompts_1.confirmAsync)({
|
|
60
|
+
message: `Are you sure you want to publish a rollback to the branch ${branch} ?`,
|
|
61
|
+
name: 'export',
|
|
62
|
+
type: 'confirm',
|
|
63
|
+
});
|
|
64
|
+
if (!confirmed) {
|
|
65
|
+
log_1.default.error('Operation cancelled');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
const privateConfig = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, {
|
|
69
|
+
env: process.env,
|
|
70
|
+
});
|
|
71
|
+
if (privateConfig?.updates?.disableAntiBrickingMeasures) {
|
|
72
|
+
log_1.default.error('When using disableAntiBrickingMeasures, expo-updates is ignoring the embeded update of the app, please use republish command instead');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const updateUrl = (0, expoConfig_1.getExpoConfigUpdateUrl)(privateConfig);
|
|
76
|
+
if (!updateUrl) {
|
|
77
|
+
log_1.default.error("Update url is not setup in your config. Please run 'eoas init' to setup the update url");
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
let baseUrl;
|
|
81
|
+
try {
|
|
82
|
+
const parsedUrl = new URL(updateUrl);
|
|
83
|
+
baseUrl = parsedUrl.origin;
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
log_1.default.error('Invalid URL', e);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const runtimeSpinner = (0, ora_1.ora)('🔄 Resolving runtime version...').start();
|
|
90
|
+
const runtimeVersions = [
|
|
91
|
+
...(!platform || platform === expoConfig_1.RequestedPlatform.All || platform === expoConfig_1.RequestedPlatform.Ios
|
|
92
|
+
? [
|
|
93
|
+
{
|
|
94
|
+
runtimeVersion: (await (0, runtimeVersion_1.resolveRuntimeVersionAsync)({
|
|
95
|
+
exp: privateConfig,
|
|
96
|
+
platform: 'ios',
|
|
97
|
+
workflow: await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.IOS, vcsClient),
|
|
98
|
+
projectDir,
|
|
99
|
+
env: process.env,
|
|
100
|
+
}))?.runtimeVersion,
|
|
101
|
+
platform: 'ios',
|
|
102
|
+
},
|
|
103
|
+
]
|
|
104
|
+
: []),
|
|
105
|
+
...(!platform || platform === expoConfig_1.RequestedPlatform.All || platform === expoConfig_1.RequestedPlatform.Android
|
|
106
|
+
? [
|
|
107
|
+
{
|
|
108
|
+
runtimeVersion: (await (0, runtimeVersion_1.resolveRuntimeVersionAsync)({
|
|
109
|
+
exp: privateConfig,
|
|
110
|
+
platform: 'android',
|
|
111
|
+
workflow: await (0, workflow_1.resolveWorkflowAsync)(projectDir, eas_build_job_1.Platform.ANDROID, vcsClient),
|
|
112
|
+
projectDir,
|
|
113
|
+
env: process.env,
|
|
114
|
+
}))?.runtimeVersion,
|
|
115
|
+
platform: 'android',
|
|
116
|
+
},
|
|
117
|
+
]
|
|
118
|
+
: []),
|
|
119
|
+
].filter(({ runtimeVersion }) => !!runtimeVersion);
|
|
120
|
+
if (!runtimeVersions.length) {
|
|
121
|
+
runtimeSpinner.fail('Could not resolve runtime versions for the requested platforms');
|
|
122
|
+
log_1.default.error('Could not resolve runtime versions for the requested platforms');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
runtimeSpinner.succeed('✅ Runtime versions resolved');
|
|
126
|
+
const rollbackSpinner = (0, ora_1.ora)('📦 Uploading rollback...').start();
|
|
127
|
+
const erroredPlatforms = [];
|
|
128
|
+
await Promise.all(runtimeVersions.map(async ({ runtimeVersion, platform }) => {
|
|
129
|
+
const endpoint = `${baseUrl}/rollback/${branch}?commitHash=${commitHash}&platform=${platform}&runtimeVersion=${runtimeVersion}`;
|
|
130
|
+
const response = await (0, fetch_1.fetchWithRetries)(endpoint, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
headers: {
|
|
133
|
+
...(0, auth_1.getAuthExpoHeaders)(credentials),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
erroredPlatforms.push({
|
|
138
|
+
platform,
|
|
139
|
+
reason: await response.text(),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}));
|
|
143
|
+
if (erroredPlatforms.length) {
|
|
144
|
+
rollbackSpinner.fail('❌ Rollback failed');
|
|
145
|
+
erroredPlatforms.forEach(({ platform, reason }) => {
|
|
146
|
+
log_1.default.error(`Failed to publish rollback for ${platform}: ${reason}`);
|
|
147
|
+
});
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
rollbackSpinner.succeed('✅ Rollback published successfully');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.default = Publish;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveReleaseChannelDynamicallyFromBranch = void 0;
|
|
4
|
+
const auth_1 = require("./auth");
|
|
5
|
+
const fetch_1 = require("./fetch");
|
|
6
|
+
async function resolveReleaseChannelDynamicallyFromBranch(baseUrl, branch, credentials) {
|
|
7
|
+
const branchesEndpoint = `${baseUrl}/api/branches`;
|
|
8
|
+
const response = await (0, fetch_1.fetchWithRetries)(branchesEndpoint, {
|
|
9
|
+
headers: { ...(0, auth_1.getAuthExpoHeaders)(credentials), 'use-expo-auth': 'true' },
|
|
10
|
+
});
|
|
11
|
+
if (!response.ok) {
|
|
12
|
+
throw new Error(`Failed to retrieve branches from server: ${await response.text()}`);
|
|
13
|
+
}
|
|
14
|
+
const branches = (await response.json());
|
|
15
|
+
const branchInfo = branches.find(b => b.branchName === branch);
|
|
16
|
+
if (!branchInfo) {
|
|
17
|
+
throw new Error(`Branch ${branch} not found`);
|
|
18
|
+
}
|
|
19
|
+
if (!branchInfo.releaseChannel) {
|
|
20
|
+
throw new Error(`Branch ${branch} does not have a release channel linked`);
|
|
21
|
+
}
|
|
22
|
+
return branchInfo.releaseChannel;
|
|
23
|
+
}
|
|
24
|
+
exports.resolveReleaseChannelDynamicallyFromBranch = resolveReleaseChannelDynamicallyFromBranch;
|
package/dist/lib/expoConfig.d.ts
CHANGED
|
@@ -21,3 +21,4 @@ export declare function isUsingStaticExpoConfig(projectDir: string): boolean;
|
|
|
21
21
|
export declare function getPublicExpoConfigAsync(projectDir: string, opts?: ExpoConfigOptions): Promise<PublicExpoConfig>;
|
|
22
22
|
export declare function getExpoConfigUpdateUrl(config: ExpoConfig): string | undefined;
|
|
23
23
|
export declare function createOrModifyExpoConfigAsync(projectDir: string, exp: Partial<ExpoConfig>): Promise<void>;
|
|
24
|
+
export declare function resolveServerUrl(config: ExpoConfig): Promise<string>;
|
package/dist/lib/expoConfig.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createOrModifyExpoConfigAsync = exports.getExpoConfigUpdateUrl = exports.getPublicExpoConfigAsync = exports.isUsingStaticExpoConfig = exports.ensureExpoConfigExists = exports.getPrivateExpoConfigAsync = exports.RequestedPlatform = void 0;
|
|
3
|
+
exports.resolveServerUrl = exports.createOrModifyExpoConfigAsync = exports.getExpoConfigUpdateUrl = exports.getPublicExpoConfigAsync = exports.isUsingStaticExpoConfig = exports.ensureExpoConfigExists = exports.getPrivateExpoConfigAsync = exports.RequestedPlatform = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
// This file is copied from eas-cli[https://github.com/expo/eas-cli] to ensure consistent user experience across the CLI.
|
|
6
6
|
const config_1 = require("@expo/config");
|
|
@@ -196,3 +196,19 @@ function createValueNode(j, value) {
|
|
|
196
196
|
function stringifyWithEnv(obj) {
|
|
197
197
|
return JSON.stringify(obj, null, 2).replace(/"process\.env\.(\w+)"/g, 'process.env.$1');
|
|
198
198
|
}
|
|
199
|
+
async function resolveServerUrl(config) {
|
|
200
|
+
const updateUrl = config.updates?.url;
|
|
201
|
+
if (!updateUrl) {
|
|
202
|
+
throw new Error('No update URL found in the Expo config.');
|
|
203
|
+
}
|
|
204
|
+
let baseUrl;
|
|
205
|
+
try {
|
|
206
|
+
const parsedUrl = new URL(updateUrl);
|
|
207
|
+
baseUrl = parsedUrl.origin;
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
throw new Error('Invalid update URL.');
|
|
211
|
+
}
|
|
212
|
+
return baseUrl;
|
|
213
|
+
}
|
|
214
|
+
exports.resolveServerUrl = resolveServerUrl;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eoas",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc --project tsconfig.json",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"repository": "axelmarciano/expo-open-ota",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@expo/code-signing-certificates": "^0.0.5",
|
|
26
|
-
"@expo/config": "10.0.
|
|
26
|
+
"@expo/config": "10.0.11",
|
|
27
27
|
"@expo/config-plugins": "9.0.12",
|
|
28
28
|
"@expo/eas-build-job": "1.0.165",
|
|
29
29
|
"@expo/fingerprint": "^0.11.7",
|
package/bin/dev.cmd
DELETED
package/bin/dev.js
DELETED
package/bin/run.cmd
DELETED