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.
@@ -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>;
@@ -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: true,
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, channel, outputDir } = this.sanitizeFlags(flags);
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 privateConfig = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, {
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
- RELEASE_CHANNEL: channel,
85
+ ...process.env,
86
+ ...(providedDeprecatedChannel ? { RELEASE_CHANNEL: providedDeprecatedChannel } : {}),
89
87
  },
90
88
  });
91
- const updateUrl = (0, expoConfig_1.getExpoConfigUpdateUrl)(privateConfig);
92
- if (!updateUrl) {
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? ${baseUrl}`,
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: privateConfig,
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
- RELEASE_CHANNEL: channel,
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: privateConfig,
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
- RELEASE_CHANNEL: channel,
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: `${baseUrl}/requestUploadUrl/${branch}`,
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(`${baseUrl}/uploadLocalFile`);
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)(`${baseUrl}/markUpdateAsUploaded/${branch}?platform=${platform}&updateId=${updateId}&runtimeVersion=${runtimeVersion}`, {
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 ${updateUrl}`);
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,2 @@
1
+ import { ExpoCredentials } from './auth';
2
+ export declare function resolveReleaseChannelDynamicallyFromBranch(baseUrl: string, branch: string, credentials: ExpoCredentials): Promise<string>;
@@ -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;
@@ -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>;
@@ -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.2.81",
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.6",
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
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\dev" %*
package/bin/dev.js DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- // eslint-disable-next-line node/shebang, unicorn/prefer-top-level-await
3
- (async () => {
4
- const oclif = await import('@oclif/core');
5
- await oclif.execute({ development: true, dir: __dirname });
6
- })();
package/bin/run.cmd DELETED
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\run" %*
package/bin/run.js DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // eslint-disable-next-line unicorn/prefer-top-level-await
4
- (async () => {
5
- const oclif = await import('@oclif/core')
6
- await oclif.execute({dir: __dirname})
7
- })()