expo-release 2.2.0 → 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/README.md +25 -5
- package/action.yml +6 -0
- package/dist/args.d.ts +48 -9
- package/dist/cli.js +101 -170
- package/dist/index.js +54 -130
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +65 -20
- package/dist/runtime/push.d.ts +19 -7
- package/package.json +5 -1
- package/src/args.ts +128 -259
- package/src/cli.ts +77 -47
- package/src/runtime/index.ts +1 -0
- package/src/runtime/push.ts +98 -21
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { ErrorBoundary, type ErrorBoundaryFallbackProps, type ErrorBoundaryProps } from './error-boundary';
|
|
2
2
|
export { checkForOTAUpdate, type ReloadApp, type UseOTAUpdatesOptions, useOTAUpdates } from './ota';
|
|
3
|
-
export { createPushNotifications, type KeyValueStorage, type NotificationListeners, type PushNotifications, type PushNotificationsOptions, usePushNotifications } from './push';
|
|
3
|
+
export { createPushNotifications, type KeyValueStorage, type NotificationListeners, type PushNotifications, type PushNotificationsOptions, type UsePushNotificationsOptions, usePushNotifications } from './push';
|
package/dist/runtime/index.js
CHANGED
|
@@ -57,7 +57,8 @@ function createPushNotifications(options) {
|
|
|
57
57
|
onUnavailable,
|
|
58
58
|
onError = (message, error) => console.error(message, error),
|
|
59
59
|
projectId,
|
|
60
|
-
storageKey = "@notifications_permission_asked"
|
|
60
|
+
storageKey = "@notifications_permission_asked",
|
|
61
|
+
tokenStorageKey = "@notifications_push_token"
|
|
61
62
|
} = options;
|
|
62
63
|
Notifications.setNotificationHandler({
|
|
63
64
|
handleNotification: async () => options.foregroundBehavior ?? {
|
|
@@ -74,13 +75,23 @@ function createPushNotifications(options) {
|
|
|
74
75
|
}).catch((error) => onError("Failed to create the notification channel", error));
|
|
75
76
|
let pendingPrompt = null;
|
|
76
77
|
const getPermissionStatus = () => Notifications.getPermissionsAsync();
|
|
77
|
-
async function
|
|
78
|
+
async function read(key) {
|
|
78
79
|
try {
|
|
79
|
-
return await storage.getItem(
|
|
80
|
+
return await storage.getItem(key);
|
|
80
81
|
} catch {
|
|
81
|
-
return
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function rememberToken(token) {
|
|
86
|
+
try {
|
|
87
|
+
await storage.setItem(tokenStorageKey, token);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
onError("Failed to remember the push token", error);
|
|
82
90
|
}
|
|
83
91
|
}
|
|
92
|
+
async function hasPrompted() {
|
|
93
|
+
return await read(storageKey) === "true";
|
|
94
|
+
}
|
|
84
95
|
async function requestPermissions() {
|
|
85
96
|
if (!Device.isDevice) {
|
|
86
97
|
onUnavailable?.("not-a-device");
|
|
@@ -96,30 +107,40 @@ function createPushNotifications(options) {
|
|
|
96
107
|
return false;
|
|
97
108
|
}
|
|
98
109
|
}
|
|
99
|
-
async function getPushToken() {
|
|
110
|
+
async function getPushToken(devicePushToken) {
|
|
100
111
|
if (!Device.isDevice)
|
|
101
112
|
return null;
|
|
102
113
|
try {
|
|
103
|
-
const token = await Notifications.getExpoPushTokenAsync(projectId ? { projectId } : undefined);
|
|
114
|
+
const token = await Notifications.getExpoPushTokenAsync(projectId || devicePushToken ? { projectId, devicePushToken } : undefined);
|
|
104
115
|
return token.data;
|
|
105
116
|
} catch (error) {
|
|
106
117
|
onError("Failed to get the Expo push token", error);
|
|
107
118
|
return null;
|
|
108
119
|
}
|
|
109
120
|
}
|
|
110
|
-
async function
|
|
111
|
-
if (!(await getPermissionStatus()).granted)
|
|
112
|
-
return false;
|
|
113
|
-
const token = await getPushToken();
|
|
114
|
-
if (!token)
|
|
115
|
-
return false;
|
|
121
|
+
async function sendToken(token) {
|
|
116
122
|
try {
|
|
117
123
|
await registerDevice(token);
|
|
118
|
-
return true;
|
|
119
124
|
} catch (error) {
|
|
120
125
|
onError("Failed to register the push token", error);
|
|
121
126
|
return false;
|
|
122
127
|
}
|
|
128
|
+
await rememberToken(token);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
async function syncToken() {
|
|
132
|
+
if (!(await getPermissionStatus()).granted)
|
|
133
|
+
return false;
|
|
134
|
+
const token = await getPushToken();
|
|
135
|
+
return token ? sendToken(token) : false;
|
|
136
|
+
}
|
|
137
|
+
async function refreshToken(devicePushToken) {
|
|
138
|
+
const sent = await read(tokenStorageKey);
|
|
139
|
+
if (!sent || !(await getPermissionStatus()).granted)
|
|
140
|
+
return;
|
|
141
|
+
const token = await getPushToken(devicePushToken);
|
|
142
|
+
if (token && token !== sent)
|
|
143
|
+
await sendToken(token);
|
|
123
144
|
}
|
|
124
145
|
async function runPrompt() {
|
|
125
146
|
if (await hasPrompted())
|
|
@@ -157,11 +178,15 @@ function createPushNotifications(options) {
|
|
|
157
178
|
await syncToken();
|
|
158
179
|
return true;
|
|
159
180
|
},
|
|
160
|
-
disable() {
|
|
161
|
-
|
|
181
|
+
async disable() {
|
|
182
|
+
await registerDevice(null);
|
|
183
|
+
await rememberToken("");
|
|
162
184
|
},
|
|
163
|
-
listen({ onReceived, onResponse }) {
|
|
185
|
+
listen({ onReceived, onResponse } = {}) {
|
|
186
|
+
const refresh = (devicePushToken) => refreshToken(devicePushToken).catch((error) => onError("Failed to refresh the push token", error));
|
|
187
|
+
refresh();
|
|
164
188
|
const subscriptions = [
|
|
189
|
+
Notifications.addPushTokenListener(refresh),
|
|
165
190
|
onReceived && Notifications.addNotificationReceivedListener(onReceived),
|
|
166
191
|
onResponse && Notifications.addNotificationResponseReceivedListener(onResponse)
|
|
167
192
|
];
|
|
@@ -175,17 +200,31 @@ function createPushNotifications(options) {
|
|
|
175
200
|
}
|
|
176
201
|
};
|
|
177
202
|
}
|
|
178
|
-
function usePushNotifications(push, { enabled = false } = {}) {
|
|
203
|
+
function usePushNotifications(push, { enabled = false, persist } = {}) {
|
|
179
204
|
const [permission, setPermission] = useState(null);
|
|
180
205
|
const [isLoading, setIsLoading] = useState(false);
|
|
206
|
+
const [optimistic, setOptimistic] = useState(null);
|
|
207
|
+
const preference = optimistic ?? enabled;
|
|
181
208
|
const refresh = useCallback(async () => {
|
|
182
209
|
setPermission(await push.getPermissionStatus());
|
|
183
210
|
}, [push]);
|
|
184
211
|
useEffect2(() => {
|
|
212
|
+
setOptimistic(null);
|
|
185
213
|
refresh();
|
|
186
214
|
if (enabled)
|
|
187
215
|
push.syncToken();
|
|
188
216
|
}, [push, enabled, refresh]);
|
|
217
|
+
const save = useCallback(async (next) => {
|
|
218
|
+
if (!persist)
|
|
219
|
+
return;
|
|
220
|
+
setOptimistic(next);
|
|
221
|
+
try {
|
|
222
|
+
await persist(next);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
setOptimistic(null);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}, [persist]);
|
|
189
228
|
const withLoading = useCallback((action) => async () => {
|
|
190
229
|
setIsLoading(true);
|
|
191
230
|
try {
|
|
@@ -197,11 +236,17 @@ function usePushNotifications(push, { enabled = false } = {}) {
|
|
|
197
236
|
}, [refresh]);
|
|
198
237
|
return {
|
|
199
238
|
permission,
|
|
200
|
-
isEnabled:
|
|
239
|
+
isEnabled: preference && permission?.granted === true,
|
|
201
240
|
isLoading,
|
|
202
241
|
prompt: withLoading(push.prompt),
|
|
203
|
-
enable: withLoading(
|
|
204
|
-
|
|
242
|
+
enable: withLoading(async () => {
|
|
243
|
+
await save(true);
|
|
244
|
+
return push.enable();
|
|
245
|
+
}),
|
|
246
|
+
disable: withLoading(async () => {
|
|
247
|
+
await save(false);
|
|
248
|
+
await push.disable();
|
|
249
|
+
}),
|
|
205
250
|
refresh
|
|
206
251
|
};
|
|
207
252
|
}
|
package/dist/runtime/push.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface KeyValueStorage {
|
|
|
5
5
|
setItem(key: string, value: string): Promise<void>;
|
|
6
6
|
}
|
|
7
7
|
export interface PushNotificationsOptions {
|
|
8
|
-
/** Remembers that the one-time prompt ran. */
|
|
8
|
+
/** Remembers that the one-time prompt ran and the last token sent. */
|
|
9
9
|
storage: KeyValueStorage;
|
|
10
10
|
/** Saves this device's Expo push token on your backend; `null` removes it. */
|
|
11
11
|
registerDevice: (token: string | null) => Promise<void>;
|
|
@@ -24,6 +24,8 @@ export interface PushNotificationsOptions {
|
|
|
24
24
|
/** How notifications show while the app is in the foreground. */
|
|
25
25
|
foregroundBehavior?: Notifications.NotificationBehavior;
|
|
26
26
|
storageKey?: string;
|
|
27
|
+
/** Holds the last token passed to `registerDevice`. */
|
|
28
|
+
tokenStorageKey?: string;
|
|
27
29
|
}
|
|
28
30
|
export interface NotificationListeners {
|
|
29
31
|
onReceived?: (notification: Notifications.Notification) => void;
|
|
@@ -39,7 +41,7 @@ export declare function createPushNotifications(options: PushNotificationsOption
|
|
|
39
41
|
getPermissionStatus: () => Promise<Notifications.NotificationPermissionsStatus>;
|
|
40
42
|
hasPrompted: () => Promise<boolean>;
|
|
41
43
|
requestPermissions: () => Promise<boolean>;
|
|
42
|
-
getPushToken: () => Promise<string | null>;
|
|
44
|
+
getPushToken: (devicePushToken?: Notifications.DevicePushToken) => Promise<string | null>;
|
|
43
45
|
syncToken: () => Promise<boolean>;
|
|
44
46
|
/**
|
|
45
47
|
* Asks for push once per install (after sign-in, say), then registers the
|
|
@@ -50,16 +52,26 @@ export declare function createPushNotifications(options: PushNotificationsOption
|
|
|
50
52
|
enable(): Promise<boolean>;
|
|
51
53
|
/** Removes this device's token from your backend. */
|
|
52
54
|
disable(): Promise<void>;
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Subscribes to notifications and keeps the registered token current: on
|
|
57
|
+
* start and whenever Expo rotates it. Returns the unsubscribe function.
|
|
58
|
+
*/
|
|
59
|
+
listen({ onReceived, onResponse }?: NotificationListeners): () => void;
|
|
55
60
|
};
|
|
61
|
+
export interface UsePushNotificationsOptions {
|
|
62
|
+
/** The user's saved preference, for example from your API. */
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Saves the preference. `enable()` and `disable()` call it first and show
|
|
66
|
+
* the new value at once; when it rejects, they roll back and rethrow.
|
|
67
|
+
*/
|
|
68
|
+
persist?: (enabled: boolean) => Promise<unknown>;
|
|
69
|
+
}
|
|
56
70
|
/**
|
|
57
71
|
* Push state for a settings screen. `enabled` is the user's saved preference;
|
|
58
72
|
* while it is on, the token is re-registered on mount.
|
|
59
73
|
*/
|
|
60
|
-
export declare function usePushNotifications(push: PushNotifications, { enabled }?: {
|
|
61
|
-
enabled?: boolean;
|
|
62
|
-
}): {
|
|
74
|
+
export declare function usePushNotifications(push: PushNotifications, { enabled, persist }?: UsePushNotificationsOptions): {
|
|
63
75
|
permission: Notifications.NotificationPermissionsStatus | null;
|
|
64
76
|
isEnabled: boolean;
|
|
65
77
|
isLoading: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-release",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Fingerprint-driven Expo EAS release: OTA update or native store build, plus app runtime helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -57,6 +57,10 @@
|
|
|
57
57
|
"typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.runtime.json",
|
|
58
58
|
"verify:package": "node scripts/verify-package.mjs"
|
|
59
59
|
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"hanzio": "^3.1.0",
|
|
62
|
+
"zod": "^4.3.6"
|
|
63
|
+
},
|
|
60
64
|
"peerDependencies": {
|
|
61
65
|
"expo-device": ">=55",
|
|
62
66
|
"expo-notifications": ">=55",
|
package/src/args.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { CommandContext } from 'hanzio/cli'
|
|
2
|
+
import { z } from 'zod'
|
|
2
3
|
|
|
3
4
|
export const DEFAULT_IMPACT_PATHS = ['apps/expo/'] as const
|
|
4
5
|
|
|
@@ -39,76 +40,133 @@ export type ReleaseOptions = {
|
|
|
39
40
|
dryRun: boolean
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
43
|
+
// Every flag falls back to an EXPO_RELEASE_* variable: the composite action passes its inputs that way.
|
|
44
|
+
const text = (env: string) =>
|
|
45
|
+
z
|
|
46
|
+
.string()
|
|
47
|
+
.transform((value) => value.trim() || undefined)
|
|
48
|
+
.optional()
|
|
49
|
+
.meta({ env: `EXPO_RELEASE_${env}` })
|
|
50
|
+
|
|
51
|
+
// `--auto-submit false` takes a value, so these are not boolean switches.
|
|
52
|
+
const trueFalse = (env: string, fallback: 'true' | 'false') =>
|
|
53
|
+
z
|
|
54
|
+
.enum(['true', 'false'])
|
|
55
|
+
.default(fallback)
|
|
56
|
+
.transform((value) => value === 'true')
|
|
57
|
+
.meta({ env: `EXPO_RELEASE_${env}` })
|
|
58
|
+
|
|
59
|
+
export const releaseFlags = z
|
|
60
|
+
.object({
|
|
61
|
+
profile: z
|
|
62
|
+
.string()
|
|
63
|
+
.trim()
|
|
64
|
+
.min(1)
|
|
65
|
+
.meta({ env: 'EXPO_RELEASE_PROFILE' })
|
|
66
|
+
.describe('EAS build profile, also the default update channel'),
|
|
67
|
+
base: text('BASE').describe(
|
|
68
|
+
'Git base SHA for the impact diff (falls back to GITHUB_EVENT_BEFORE)'
|
|
69
|
+
),
|
|
70
|
+
head: text('HEAD').describe(
|
|
71
|
+
'Git head SHA for the impact diff (falls back to GITHUB_SHA)'
|
|
72
|
+
),
|
|
73
|
+
platform: z
|
|
74
|
+
.enum(['ios', 'android'])
|
|
75
|
+
.default('ios')
|
|
76
|
+
.meta({ env: 'EXPO_RELEASE_PLATFORM' })
|
|
77
|
+
.describe('Native platform'),
|
|
78
|
+
impactPaths: z
|
|
79
|
+
.string()
|
|
80
|
+
.default(DEFAULT_IMPACT_PATHS.join(','))
|
|
81
|
+
.transform((value, ctx) => {
|
|
82
|
+
const paths = value
|
|
83
|
+
.split(',')
|
|
84
|
+
.map((path) => path.trim())
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
if (paths.length === 0)
|
|
87
|
+
ctx.addIssue({
|
|
88
|
+
code: 'custom',
|
|
89
|
+
message: 'At least one --impact-paths entry is required'
|
|
90
|
+
})
|
|
91
|
+
return paths
|
|
92
|
+
})
|
|
93
|
+
.meta({ env: 'EXPO_RELEASE_IMPACT_PATHS' })
|
|
94
|
+
.describe('Comma-separated repo-root paths that count as an Expo change'),
|
|
95
|
+
ignoreExpoImpact: trueFalse('IGNORE_EXPO_IMPACT', 'false').describe(
|
|
96
|
+
'Check the fingerprint even when no impact path changed'
|
|
97
|
+
),
|
|
98
|
+
operation: z
|
|
99
|
+
.enum([
|
|
100
|
+
'deploy',
|
|
101
|
+
'force-native',
|
|
102
|
+
'retry-ota',
|
|
103
|
+
'republish',
|
|
104
|
+
'rollback-embedded'
|
|
105
|
+
])
|
|
106
|
+
.default('deploy')
|
|
107
|
+
.meta({ env: 'EXPO_RELEASE_OPERATION' })
|
|
108
|
+
.describe('What to run'),
|
|
109
|
+
deployType: z
|
|
110
|
+
.enum(['none', 'ota', 'native'])
|
|
111
|
+
.default('none')
|
|
112
|
+
.meta({ env: 'EXPO_RELEASE_DEPLOY_TYPE' })
|
|
113
|
+
.describe('Decision type for operate'),
|
|
114
|
+
channel: text('CHANNEL').describe(
|
|
115
|
+
'EAS Update channel (default: the profile)'
|
|
116
|
+
),
|
|
117
|
+
environment: text('ENVIRONMENT').describe(
|
|
118
|
+
'EAS environment for eas update (required for Expo SDK 55+)'
|
|
119
|
+
),
|
|
120
|
+
autoSubmit: trueFalse('AUTO_SUBMIT', 'true').describe(
|
|
121
|
+
'Pass --auto-submit on native builds'
|
|
122
|
+
),
|
|
123
|
+
preUpdate: text('PRE_UPDATE').describe(
|
|
124
|
+
'Shell command run before eas update'
|
|
125
|
+
),
|
|
126
|
+
postUpdate: text('POST_UPDATE').describe(
|
|
127
|
+
'Shell command run after a successful eas update'
|
|
128
|
+
),
|
|
129
|
+
updateExtraArgs: text('UPDATE_EXTRA_ARGS').describe(
|
|
130
|
+
'Extra tokens appended to eas update'
|
|
131
|
+
),
|
|
132
|
+
exportSourceMaps: trueFalse('EXPORT_SOURCE_MAPS', 'false').describe(
|
|
133
|
+
'On OTA, expo export with external source maps, then publish dist'
|
|
134
|
+
),
|
|
135
|
+
group: text('GROUP').describe('Update group ID for --operation republish'),
|
|
136
|
+
runtimeVersion: text('RUNTIME_VERSION').describe(
|
|
137
|
+
'Runtime version for rollback-embedded (default: the fingerprint)'
|
|
138
|
+
),
|
|
139
|
+
message: text('MESSAGE').describe('Override the eas update message'),
|
|
140
|
+
fingerprint: text('FINGERPRINT').describe(
|
|
141
|
+
'Fingerprint hash to use instead of generating one'
|
|
142
|
+
),
|
|
143
|
+
dryRun: z
|
|
144
|
+
.boolean()
|
|
145
|
+
.meta({ env: 'EXPO_RELEASE_DRY_RUN' })
|
|
146
|
+
.describe('Print mutating EAS commands without running them')
|
|
147
|
+
})
|
|
148
|
+
.superRefine((flags, ctx) => {
|
|
149
|
+
if (flags.operation === 'republish' && !flags.group)
|
|
150
|
+
ctx.addIssue({
|
|
151
|
+
code: 'custom',
|
|
152
|
+
path: ['group'],
|
|
153
|
+
message: 'required by --operation republish'
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
export type ReleaseFlags = z.output<typeof releaseFlags>
|
|
158
|
+
|
|
159
|
+
export function toReleaseOptions(
|
|
160
|
+
command: CommandName,
|
|
161
|
+
{ context: flags, env }: CommandContext<ReleaseFlags>
|
|
162
|
+
): ReleaseOptions {
|
|
163
|
+
return {
|
|
164
|
+
...flags,
|
|
165
|
+
command,
|
|
166
|
+
base: flags.base ?? env.GITHUB_EVENT_BEFORE,
|
|
167
|
+
head: flags.head ?? env.GITHUB_SHA,
|
|
168
|
+
channel: flags.channel ?? flags.profile
|
|
110
169
|
}
|
|
111
|
-
throw new Error(USAGE)
|
|
112
170
|
}
|
|
113
171
|
|
|
114
172
|
export function needsDecide(operation: Operation): boolean {
|
|
@@ -137,192 +195,3 @@ export function shouldIgnoreExpoImpact(
|
|
|
137
195
|
options.operation === 'deploy'
|
|
138
196
|
)
|
|
139
197
|
}
|
|
140
|
-
|
|
141
|
-
export function parseReleaseOptions(
|
|
142
|
-
args: string[],
|
|
143
|
-
env: NodeJS.ProcessEnv = process.env
|
|
144
|
-
): ReleaseOptions {
|
|
145
|
-
let values: {
|
|
146
|
-
profile?: string
|
|
147
|
-
base?: string
|
|
148
|
-
head?: string
|
|
149
|
-
platform?: string
|
|
150
|
-
'impact-paths'?: string
|
|
151
|
-
'ignore-expo-impact'?: string
|
|
152
|
-
operation?: string
|
|
153
|
-
'deploy-type'?: string
|
|
154
|
-
channel?: string
|
|
155
|
-
environment?: string
|
|
156
|
-
'auto-submit'?: string
|
|
157
|
-
'pre-update'?: string
|
|
158
|
-
'post-update'?: string
|
|
159
|
-
'update-extra-args'?: string
|
|
160
|
-
'export-source-maps'?: string
|
|
161
|
-
group?: string
|
|
162
|
-
'runtime-version'?: string
|
|
163
|
-
message?: string
|
|
164
|
-
fingerprint?: string
|
|
165
|
-
'dry-run'?: boolean
|
|
166
|
-
}
|
|
167
|
-
let positionals: string[]
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
const parsed = parseArgs({
|
|
171
|
-
args,
|
|
172
|
-
allowPositionals: true,
|
|
173
|
-
strict: true,
|
|
174
|
-
options: {
|
|
175
|
-
profile: { type: 'string' },
|
|
176
|
-
base: { type: 'string' },
|
|
177
|
-
head: { type: 'string' },
|
|
178
|
-
platform: { type: 'string' },
|
|
179
|
-
'impact-paths': { type: 'string' },
|
|
180
|
-
'ignore-expo-impact': { type: 'string' },
|
|
181
|
-
operation: { type: 'string' },
|
|
182
|
-
'deploy-type': { type: 'string' },
|
|
183
|
-
channel: { type: 'string' },
|
|
184
|
-
environment: { type: 'string' },
|
|
185
|
-
'auto-submit': { type: 'string' },
|
|
186
|
-
'pre-update': { type: 'string' },
|
|
187
|
-
'post-update': { type: 'string' },
|
|
188
|
-
'update-extra-args': { type: 'string' },
|
|
189
|
-
'export-source-maps': { type: 'string' },
|
|
190
|
-
group: { type: 'string' },
|
|
191
|
-
'runtime-version': { type: 'string' },
|
|
192
|
-
message: { type: 'string' },
|
|
193
|
-
fingerprint: { type: 'string' },
|
|
194
|
-
'dry-run': { type: 'boolean', default: false }
|
|
195
|
-
}
|
|
196
|
-
})
|
|
197
|
-
values = parsed.values
|
|
198
|
-
positionals = parsed.positionals
|
|
199
|
-
} catch (error) {
|
|
200
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
201
|
-
throw new Error(`${USAGE}\n${message}`)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (positionals.length > 1) {
|
|
205
|
-
throw new Error(USAGE)
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const command = parseCommandName(
|
|
209
|
-
positionals[0] ?? fromEnv(env, 'EXPO_RELEASE_COMMAND')
|
|
210
|
-
)
|
|
211
|
-
const profile = firstDefined(
|
|
212
|
-
values.profile,
|
|
213
|
-
fromEnv(env, 'EXPO_RELEASE_PROFILE')
|
|
214
|
-
)
|
|
215
|
-
if (!profile) {
|
|
216
|
-
throw new Error(USAGE)
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const operation = parseOperation(
|
|
220
|
-
firstDefined(values.operation, fromEnv(env, 'EXPO_RELEASE_OPERATION'))
|
|
221
|
-
)
|
|
222
|
-
const group = optionalString(
|
|
223
|
-
firstDefined(values.group, fromEnv(env, 'EXPO_RELEASE_GROUP'))
|
|
224
|
-
)
|
|
225
|
-
if (operation === 'republish' && !group) {
|
|
226
|
-
throw new Error(
|
|
227
|
-
'Usage: --operation republish requires --group <update-group-id>'
|
|
228
|
-
)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const channel =
|
|
232
|
-
firstDefined(values.channel, fromEnv(env, 'EXPO_RELEASE_CHANNEL')) ??
|
|
233
|
-
profile
|
|
234
|
-
|
|
235
|
-
return {
|
|
236
|
-
command,
|
|
237
|
-
profile,
|
|
238
|
-
base: optionalString(
|
|
239
|
-
firstDefined(
|
|
240
|
-
values.base,
|
|
241
|
-
fromEnv(env, 'EXPO_RELEASE_BASE'),
|
|
242
|
-
fromEnv(env, 'GITHUB_EVENT_BEFORE')
|
|
243
|
-
)
|
|
244
|
-
),
|
|
245
|
-
head: optionalString(
|
|
246
|
-
firstDefined(
|
|
247
|
-
values.head,
|
|
248
|
-
fromEnv(env, 'EXPO_RELEASE_HEAD'),
|
|
249
|
-
fromEnv(env, 'GITHUB_SHA')
|
|
250
|
-
)
|
|
251
|
-
),
|
|
252
|
-
platform: parsePlatform(
|
|
253
|
-
firstDefined(values.platform, fromEnv(env, 'EXPO_RELEASE_PLATFORM'))
|
|
254
|
-
),
|
|
255
|
-
impactPaths: parseImpactPaths(
|
|
256
|
-
firstDefined(
|
|
257
|
-
values['impact-paths'],
|
|
258
|
-
fromEnv(env, 'EXPO_RELEASE_IMPACT_PATHS')
|
|
259
|
-
)
|
|
260
|
-
),
|
|
261
|
-
ignoreExpoImpact: parseBoolean(
|
|
262
|
-
firstDefined(
|
|
263
|
-
values['ignore-expo-impact'],
|
|
264
|
-
fromEnv(env, 'EXPO_RELEASE_IGNORE_EXPO_IMPACT')
|
|
265
|
-
),
|
|
266
|
-
false
|
|
267
|
-
),
|
|
268
|
-
operation,
|
|
269
|
-
deployType: parseDeployType(
|
|
270
|
-
firstDefined(
|
|
271
|
-
values['deploy-type'],
|
|
272
|
-
fromEnv(env, 'EXPO_RELEASE_DEPLOY_TYPE')
|
|
273
|
-
)
|
|
274
|
-
),
|
|
275
|
-
channel,
|
|
276
|
-
environment: optionalString(
|
|
277
|
-
firstDefined(values.environment, fromEnv(env, 'EXPO_RELEASE_ENVIRONMENT'))
|
|
278
|
-
),
|
|
279
|
-
autoSubmit: parseBoolean(
|
|
280
|
-
firstDefined(
|
|
281
|
-
values['auto-submit'],
|
|
282
|
-
fromEnv(env, 'EXPO_RELEASE_AUTO_SUBMIT')
|
|
283
|
-
),
|
|
284
|
-
true
|
|
285
|
-
),
|
|
286
|
-
preUpdate: optionalString(
|
|
287
|
-
firstDefined(
|
|
288
|
-
values['pre-update'],
|
|
289
|
-
fromEnv(env, 'EXPO_RELEASE_PRE_UPDATE')
|
|
290
|
-
)
|
|
291
|
-
),
|
|
292
|
-
postUpdate: optionalString(
|
|
293
|
-
firstDefined(
|
|
294
|
-
values['post-update'],
|
|
295
|
-
fromEnv(env, 'EXPO_RELEASE_POST_UPDATE')
|
|
296
|
-
)
|
|
297
|
-
),
|
|
298
|
-
updateExtraArgs: optionalString(
|
|
299
|
-
firstDefined(
|
|
300
|
-
values['update-extra-args'],
|
|
301
|
-
fromEnv(env, 'EXPO_RELEASE_UPDATE_EXTRA_ARGS')
|
|
302
|
-
)
|
|
303
|
-
),
|
|
304
|
-
exportSourceMaps: parseBoolean(
|
|
305
|
-
firstDefined(
|
|
306
|
-
values['export-source-maps'],
|
|
307
|
-
fromEnv(env, 'EXPO_RELEASE_EXPORT_SOURCE_MAPS')
|
|
308
|
-
),
|
|
309
|
-
false
|
|
310
|
-
),
|
|
311
|
-
group,
|
|
312
|
-
runtimeVersion: optionalString(
|
|
313
|
-
firstDefined(
|
|
314
|
-
values['runtime-version'],
|
|
315
|
-
fromEnv(env, 'EXPO_RELEASE_RUNTIME_VERSION')
|
|
316
|
-
)
|
|
317
|
-
),
|
|
318
|
-
message: optionalString(
|
|
319
|
-
firstDefined(values.message, fromEnv(env, 'EXPO_RELEASE_MESSAGE'))
|
|
320
|
-
),
|
|
321
|
-
fingerprint: optionalString(
|
|
322
|
-
firstDefined(values.fingerprint, fromEnv(env, 'EXPO_RELEASE_FINGERPRINT'))
|
|
323
|
-
),
|
|
324
|
-
dryRun:
|
|
325
|
-
values['dry-run'] === true ||
|
|
326
|
-
parseBoolean(fromEnv(env, 'EXPO_RELEASE_DRY_RUN'), false)
|
|
327
|
-
}
|
|
328
|
-
}
|