eoas 2.3.17 → 3.0.0-alpha.1

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.
@@ -27,6 +27,16 @@ class Init extends core_1.Command {
27
27
  log_1.default.error('Could not find Expo config in this project. Please make sure you have an Expo config.');
28
28
  return;
29
29
  }
30
+ const detectedAppId = config.extra?.eas
31
+ ?.projectId;
32
+ const { appId } = await (0, prompts_1.promptAsync)({
33
+ message: 'Enter the Expo project id for this project (sent as the expo-app-id header).\n' +
34
+ ' See https://axelmarciano.github.io/expo-open-ota/docs/getting-started/prerequisites for details.',
35
+ name: 'appId',
36
+ type: 'text',
37
+ initial: detectedAppId,
38
+ validate: v => !!v,
39
+ });
30
40
  const { updateUrl: promptedUrl } = await (0, prompts_1.promptAsync)({
31
41
  message: 'Enter the URL of your update server (ex: https://customota.com)',
32
42
  name: 'updateUrl',
@@ -95,6 +105,7 @@ class Init extends core_1.Command {
95
105
  enabled: true,
96
106
  requestHeaders: {
97
107
  'expo-channel-name': 'process.env.RELEASE_CHANNEL',
108
+ 'expo-app-id': appId,
98
109
  },
99
110
  };
100
111
  const updateConfigSpinner = (0, ora_1.ora)('Updating Expo config').start();
@@ -111,6 +111,7 @@ class Publish extends core_1.Command {
111
111
  log_1.default.error(e.message);
112
112
  process.exit(1);
113
113
  });
114
+ const appId = (0, expoConfig_1.requireExpoAppId)(config);
114
115
  if (!nonInteractive) {
115
116
  const confirmed = await (0, prompts_1.confirmAsync)({
116
117
  message: `Is this the correct URL of your self-hosted update server? ${serverUrl}`,
@@ -231,7 +232,7 @@ class Publish extends core_1.Command {
231
232
  body: {
232
233
  fileNames: files.map(file => file.path),
233
234
  },
234
- requestUploadUrl: `${serverUrl}/requestUploadUrl/${branch}`,
235
+ requestUploadUrl: `${serverUrl}/${appId}/requestUploadUrl/${branch}`,
235
236
  auth: credentials,
236
237
  runtimeVersion,
237
238
  platform,
@@ -244,7 +245,7 @@ class Publish extends core_1.Command {
244
245
  }));
245
246
  const allItems = uploadUrls.flatMap(({ uploadRequests }) => uploadRequests);
246
247
  await Promise.all(allItems.map(async (itm) => {
247
- const isLocalBucketFileUpload = itm.requestUploadUrl.startsWith(`${serverUrl}/uploadLocalFile`);
248
+ const isLocalBucketFileUpload = itm.requestUploadUrl.startsWith(`${serverUrl}/${appId}/uploadLocalFile`);
248
249
  const formData = new form_data_1.default();
249
250
  let file;
250
251
  try {
@@ -303,7 +304,7 @@ class Publish extends core_1.Command {
303
304
  }
304
305
  const markAsFinishedSpinner = (0, ora_1.ora)('🔗 Marking the updates as finished...').start();
305
306
  const results = await Promise.all(uploadUrls.map(async ({ updateId, platform, runtimeVersion }) => {
306
- const markAsUploadedUrl = new URL(`${serverUrl}/markUpdateAsUploaded/${branch}`);
307
+ const markAsUploadedUrl = new URL(`${serverUrl}/${appId}/markUpdateAsUploaded/${branch}`);
307
308
  markAsUploadedUrl.searchParams.set('platform', platform);
308
309
  markAsUploadedUrl.searchParams.set('updateId', updateId);
309
310
  markAsUploadedUrl.searchParams.set('runtimeVersion', runtimeVersion);
@@ -65,6 +65,7 @@ class Publish extends core_1.Command {
65
65
  log_1.default.error("Update url is not setup in your config. Please run 'eoas init' to setup the update url");
66
66
  process.exit(1);
67
67
  }
68
+ const appId = (0, expoConfig_1.requireExpoAppId)(privateConfig);
68
69
  let baseUrl;
69
70
  try {
70
71
  const parsedUrl = new URL(updateUrl);
@@ -74,9 +75,12 @@ class Publish extends core_1.Command {
74
75
  log_1.default.error('Invalid URL', e);
75
76
  process.exit(1);
76
77
  }
77
- const runtimeVersionsEndpoint = `${baseUrl}/api/branch/${branch}/runtimeVersions`;
78
+ const runtimeVersionsEndpoint = `${baseUrl}/api/apps/${appId}/branch/${branch}/runtimeVersions`;
78
79
  const response = await (0, fetch_1.fetchWithRetries)(runtimeVersionsEndpoint, {
79
- headers: { ...(0, auth_1.getAuthExpoHeaders)(credentials), 'use-expo-auth': 'true' },
80
+ headers: {
81
+ ...(0, auth_1.getAuthExpoHeaders)(credentials),
82
+ 'use-expo-auth': 'true',
83
+ },
80
84
  });
81
85
  if (!response.ok) {
82
86
  log_1.default.error(`Failed to fetch runtime versions: ${await response.text()}`);
@@ -99,9 +103,12 @@ class Publish extends core_1.Command {
99
103
  })),
100
104
  });
101
105
  log_1.default.log(`Selected runtime version: ${selectedRuntimeVersion.runtimeVersion}`);
102
- const updatesEndpoint = `${baseUrl}/api/branch/${branch}/runtimeVersion/${selectedRuntimeVersion.runtimeVersion}/updates`;
106
+ const updatesEndpoint = `${baseUrl}/api/apps/${appId}/branch/${branch}/runtimeVersion/${selectedRuntimeVersion.runtimeVersion}/updates`;
103
107
  const updatesResponse = await (0, fetch_1.fetchWithRetries)(updatesEndpoint, {
104
- headers: { ...(0, auth_1.getAuthExpoHeaders)(credentials), 'use-expo-auth': 'true' },
108
+ headers: {
109
+ ...(0, auth_1.getAuthExpoHeaders)(credentials),
110
+ 'use-expo-auth': 'true',
111
+ },
105
112
  });
106
113
  if (!updatesResponse.ok) {
107
114
  log_1.default.error(`Failed to fetch updates: ${await updatesResponse.text()}`);
@@ -121,7 +128,7 @@ class Publish extends core_1.Command {
121
128
  })),
122
129
  });
123
130
  log_1.default.log(`Re-publishing update: ${selectedUpdated.update.updateUUID}`);
124
- const republishUrl = new URL(`${baseUrl}/republish/${branch}`);
131
+ const republishUrl = new URL(`${baseUrl}/${appId}/republish/${branch}`);
125
132
  republishUrl.searchParams.set('platform', platform);
126
133
  republishUrl.searchParams.set('runtimeVersion', selectedRuntimeVersion.runtimeVersion);
127
134
  republishUrl.searchParams.set('updateId', selectedUpdated.update.updateId);
@@ -6,6 +6,7 @@ export default class Publish extends Command {
6
6
  static flags: {
7
7
  platform: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
8
  branch: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ nonInteractive: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
10
  };
10
11
  private sanitizeFlags;
11
12
  run(): Promise<void>;
@@ -28,11 +28,16 @@ class Publish extends core_1.Command {
28
28
  description: 'Name of the branch to point to',
29
29
  required: true,
30
30
  }),
31
+ nonInteractive: core_1.Flags.boolean({
32
+ description: 'Run command in non-interactive mode',
33
+ default: false,
34
+ }),
31
35
  };
32
36
  sanitizeFlags(flags) {
33
37
  return {
34
38
  platform: flags.platform,
35
39
  branch: flags.branch,
40
+ nonInteractive: flags.nonInteractive,
36
41
  };
37
42
  }
38
43
  async run() {
@@ -42,7 +47,7 @@ class Publish extends core_1.Command {
42
47
  process.exit(1);
43
48
  }
44
49
  const { flags } = await this.parse(Publish);
45
- const { platform, branch } = this.sanitizeFlags(flags);
50
+ const { platform, branch, nonInteractive } = this.sanitizeFlags(flags);
46
51
  if (!branch) {
47
52
  log_1.default.error('Branch name is required');
48
53
  process.exit(1);
@@ -56,14 +61,16 @@ class Publish extends core_1.Command {
56
61
  log_1.default.error('Expo is not installed in this project. Please install Expo first.');
57
62
  process.exit(1);
58
63
  }
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);
64
+ if (!nonInteractive) {
65
+ const confirmed = await (0, prompts_1.confirmAsync)({
66
+ message: `Are you sure you want to publish a rollback to the branch ${branch} ?`,
67
+ name: 'export',
68
+ type: 'confirm',
69
+ });
70
+ if (!confirmed) {
71
+ log_1.default.error('Operation cancelled');
72
+ process.exit(1);
73
+ }
67
74
  }
68
75
  const privateConfig = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, {
69
76
  env: process.env,
@@ -77,6 +84,7 @@ class Publish extends core_1.Command {
77
84
  log_1.default.error("Update url is not setup in your config. Please run 'eoas init' to setup the update url");
78
85
  process.exit(1);
79
86
  }
87
+ const appId = (0, expoConfig_1.requireExpoAppId)(privateConfig);
80
88
  let baseUrl;
81
89
  try {
82
90
  const parsedUrl = new URL(updateUrl);
@@ -126,7 +134,7 @@ class Publish extends core_1.Command {
126
134
  const rollbackSpinner = (0, ora_1.ora)('📦 Uploading rollback...').start();
127
135
  const erroredPlatforms = [];
128
136
  await Promise.all(runtimeVersions.map(async ({ runtimeVersion, platform }) => {
129
- const rollbackUrl = new URL(`${baseUrl}/rollback/${branch}`);
137
+ const rollbackUrl = new URL(`${baseUrl}/${appId}/rollback/${branch}`);
130
138
  rollbackUrl.searchParams.set('commitHash', commitHash ?? '');
131
139
  rollbackUrl.searchParams.set('platform', platform);
132
140
  rollbackUrl.searchParams.set('runtimeVersion', runtimeVersion ?? '');
@@ -21,5 +21,6 @@ export declare function ensureExpoConfigExists(projectDir: string): void;
21
21
  export declare function isUsingStaticExpoConfig(projectDir: string): boolean;
22
22
  export declare function getPublicExpoConfigAsync(projectDir: string, opts?: ExpoConfigOptions): Promise<PublicExpoConfig>;
23
23
  export declare function getExpoConfigUpdateUrl(config: ExpoConfig): string | undefined;
24
+ export declare function requireExpoAppId(config: ExpoConfig): string;
24
25
  export declare function createOrModifyExpoConfigAsync(projectDir: string, exp: Partial<ExpoConfig>): Promise<void>;
25
26
  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.resolveServerUrl = exports.createOrModifyExpoConfigAsync = exports.getExpoConfigUpdateUrl = exports.getPublicExpoConfigAsync = exports.isUsingStaticExpoConfig = exports.ensureExpoConfigExists = exports.getPrivateExpoConfigAsync = exports.RequestedPlatform = void 0;
3
+ exports.resolveServerUrl = exports.createOrModifyExpoConfigAsync = exports.requireExpoAppId = 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");
@@ -111,6 +111,18 @@ function getExpoConfigUpdateUrl(config) {
111
111
  return config.updates?.url;
112
112
  }
113
113
  exports.getExpoConfigUpdateUrl = getExpoConfigUpdateUrl;
114
+ function requireExpoAppId(config) {
115
+ const appId = config.updates
116
+ ?.requestHeaders?.['expo-app-id'];
117
+ if (!appId) {
118
+ log_1.default.error("Your Expo config is missing the 'expo-app-id' entry in updates.requestHeaders.");
119
+ log_1.default.error("This usually means you're running eoas v3+ against a v3-style single-app config or your config is missing the 'expo-app-id' entry.");
120
+ log_1.default.error("Fix: run 'npx eoas init' to migrate, or pin to the previous CLI via 'npx eoas@2 ...'.");
121
+ process.exit(1);
122
+ }
123
+ return appId;
124
+ }
125
+ exports.requireExpoAppId = requireExpoAppId;
114
126
  async function createOrModifyExpoConfigAsync(projectDir, exp) {
115
127
  try {
116
128
  ensureExpoConfigExists(projectDir);
@@ -208,7 +220,7 @@ async function resolveServerUrl(config) {
208
220
  const parsedUrl = new URL(updateUrl);
209
221
  baseUrl = parsedUrl.origin;
210
222
  }
211
- catch (e) {
223
+ catch {
212
224
  throw new Error('Invalid update URL.');
213
225
  }
214
226
  return baseUrl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eoas",
3
- "version": "2.3.17",
3
+ "version": "3.0.0-alpha.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "build": "tsc --project tsconfig.json",
@@ -1,2 +0,0 @@
1
- import { ExpoCredentials } from './auth';
2
- export declare function resolveReleaseChannelDynamicallyFromBranch(baseUrl: string, branch: string, credentials: ExpoCredentials): Promise<string>;
@@ -1,24 +0,0 @@
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;