eas-cli 0.59.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -47
- package/build/build/android/build.js +1 -1
- package/build/build/build.d.ts +1 -2
- package/build/build/build.js +6 -11
- package/build/build/runBuildAndSubmit.js +2 -0
- package/build/build/utils/printBuildInfo.js +2 -2
- package/build/build/utils/repository.js +4 -4
- package/build/commands/branch/create.js +1 -1
- package/build/commands/branch/delete.js +1 -1
- package/build/commands/branch/rename.js +2 -2
- package/build/commands/channel/create.js +1 -1
- package/build/commands/channel/delete.js +1 -1
- package/build/commands/channel/edit.js +3 -3
- package/build/commands/channel/rollout.js +3 -3
- package/build/commands/channel/view.js +1 -1
- package/build/commands/credentials.d.ts +3 -0
- package/build/commands/credentials.js +6 -1
- package/build/commands/metadata/pull.js +2 -2
- package/build/commands/project/init.js +3 -1
- package/build/commands/secret/list.js +1 -1
- package/build/commands/update/index.js +4 -4
- package/build/commands/update/list.js +1 -1
- package/build/credentials/android/actions/RemoveKeystore.js +1 -1
- package/build/credentials/errors.js +1 -1
- package/build/credentials/ios/actions/DistributionCertificateUtils.js +2 -2
- package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +1 -1
- package/build/credentials/ios/actions/SetUpInternalProvisioningProfile.js +2 -2
- package/build/credentials/ios/actions/SetUpProvisioningProfile.js +1 -1
- package/build/credentials/ios/actions/SetUpSubmissionCredentials.js +1 -1
- package/build/credentials/ios/appstore/contractMessages.js +1 -1
- package/build/credentials/ios/appstore/ensureAppExists.js +3 -3
- package/build/credentials/ios/appstore/pushKey.js +2 -2
- package/build/credentials/manager/SelectIosDistributionTypeGraphqlFromBuildProfile.js +1 -1
- package/build/credentials/manager/SelectPlatform.d.ts +2 -0
- package/build/credentials/manager/SelectPlatform.js +7 -12
- package/build/credentials/utils/promptForCredentials.js +1 -1
- package/build/devices/manager.js +1 -1
- package/build/graphql/client.js +1 -1
- package/build/graphql/generated.d.ts +35 -0
- package/build/metadata/config.d.ts +19 -0
- package/build/metadata/config.js +45 -1
- package/build/metadata/download.js +6 -5
- package/build/metadata/errors.js +2 -2
- package/build/metadata/upload.js +18 -21
- package/build/ora.js +1 -1
- package/build/project/metroConfig.js +1 -1
- package/build/project/projectUtils.d.ts +20 -2
- package/build/project/projectUtils.js +58 -32
- package/build/project/publish.js +8 -2
- package/build/submit/utils/errors.js +9 -9
- package/build/submit/utils/wait.js +1 -7
- package/build/update/utils.js +1 -1
- package/build/uploads.d.ts +1 -1
- package/build/uploads.js +64 -25
- package/build/vcs/clients/git.js +6 -2
- package/oclif.manifest.json +1 -1
- package/package.json +15 -14
package/build/ora.js
CHANGED
|
@@ -51,7 +51,7 @@ function ora(options) {
|
|
|
51
51
|
// eslint-disable-next-line no-console
|
|
52
52
|
console.log = logReal;
|
|
53
53
|
// eslint-disable-next-line no-console
|
|
54
|
-
console.info =
|
|
54
|
+
console.info = infoReal;
|
|
55
55
|
// eslint-disable-next-line no-console
|
|
56
56
|
console.warn = warnReal;
|
|
57
57
|
// eslint-disable-next-line no-console
|
|
@@ -42,7 +42,7 @@ function importMetroConfigFromProject(projectDir) {
|
|
|
42
42
|
if (!resolvedPath) {
|
|
43
43
|
throw new MetroConfigPackageMissingError('Missing package "metro-config" in the project. ' +
|
|
44
44
|
'This usually means `react-native` is not installed. ' +
|
|
45
|
-
'
|
|
45
|
+
'Verify that dependencies in package.json include "react-native" ' +
|
|
46
46
|
'and run `yarn` or `npm install`.');
|
|
47
47
|
}
|
|
48
48
|
return require(resolvedPath);
|
|
@@ -8,11 +8,28 @@ export declare function findProjectRootAsync({ cwd, defaultToProcessCwd, }?: {
|
|
|
8
8
|
cwd?: string;
|
|
9
9
|
defaultToProcessCwd?: boolean;
|
|
10
10
|
}): Promise<string>;
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Save an EAS project ID to the appropriate field in the app config.
|
|
13
|
+
*/
|
|
14
|
+
export declare function saveProjectIdToAppConfigAsync(projectDir: string, projectId: string, options?: {
|
|
12
15
|
env?: Env;
|
|
13
|
-
}): Promise<
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Use the owner/slug to identify an EAS project on the server.
|
|
19
|
+
*
|
|
20
|
+
* @returns the EAS project ID from the server
|
|
21
|
+
*/
|
|
22
|
+
export declare function fetchProjectIdFromServerAsync(exp: ExpoConfig): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Get the EAS project ID from the app config. If the project ID is not set in the config.
|
|
25
|
+
* use the owner/slug to identify an EAS project on the server, and attempt to save the
|
|
26
|
+
* EAS project ID to the appropriate field in the app config.
|
|
27
|
+
*/
|
|
14
28
|
export declare function getProjectIdAsync(exp: ExpoConfig, options?: {
|
|
15
29
|
env?: Env;
|
|
30
|
+
}, findProjectRootOptions?: {
|
|
31
|
+
cwd?: string;
|
|
32
|
+
defaultToProcessCwd?: boolean;
|
|
16
33
|
}): Promise<string>;
|
|
17
34
|
export declare function getProjectFullNameAsync(exp: ExpoConfig): Promise<string>;
|
|
18
35
|
/**
|
|
@@ -26,4 +43,5 @@ export declare function getProjectConfigDescription(projectDir: string): string;
|
|
|
26
43
|
export declare function promptToCreateProjectIfNotExistsAsync(exp: ExpoConfig): Promise<string | null>;
|
|
27
44
|
export declare function isExpoUpdatesInstalled(projectDir: string): boolean;
|
|
28
45
|
export declare function isExpoUpdatesInstalledOrAvailable(projectDir: string, sdkVersion?: string): boolean;
|
|
46
|
+
export declare function validateAppVersionRuntimePolicySupportAsync(projectDir: string, exp: ExpoConfig): Promise<void>;
|
|
29
47
|
export declare function installExpoUpdatesAsync(projectDir: string): Promise<void>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.installExpoUpdatesAsync = exports.isExpoUpdatesInstalledOrAvailable = exports.isExpoUpdatesInstalled = exports.promptToCreateProjectIfNotExistsAsync = exports.getProjectConfigDescription = exports.getProjectFullNameAsync = exports.getProjectIdAsync = exports.
|
|
3
|
+
exports.installExpoUpdatesAsync = exports.validateAppVersionRuntimePolicySupportAsync = exports.isExpoUpdatesInstalledOrAvailable = exports.isExpoUpdatesInstalled = exports.promptToCreateProjectIfNotExistsAsync = exports.getProjectConfigDescription = exports.getProjectFullNameAsync = exports.getProjectIdAsync = exports.fetchProjectIdFromServerAsync = exports.saveProjectIdToAppConfigAsync = exports.findProjectRootAsync = exports.getProjectAccountNameAsync = exports.getUsername = exports.getProjectAccountName = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const config_1 = require("@expo/config");
|
|
6
6
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
7
8
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
8
9
|
const pkg_dir_1 = tslib_1.__importDefault(require("pkg-dir"));
|
|
10
|
+
const resolve_from_1 = tslib_1.__importDefault(require("resolve-from"));
|
|
9
11
|
const semver_1 = tslib_1.__importDefault(require("semver"));
|
|
10
12
|
const generated_1 = require("../graphql/generated");
|
|
11
13
|
const log_1 = tslib_1.__importDefault(require("../log"));
|
|
@@ -50,7 +52,7 @@ async function findProjectRootAsync({ cwd, defaultToProcessCwd = false, } = {})
|
|
|
50
52
|
const projectRootDir = await (0, pkg_dir_1.default)(cwd);
|
|
51
53
|
if (!projectRootDir) {
|
|
52
54
|
if (!defaultToProcessCwd) {
|
|
53
|
-
throw new Error('
|
|
55
|
+
throw new Error('Run this command inside a project directory.');
|
|
54
56
|
}
|
|
55
57
|
else {
|
|
56
58
|
return process.cwd();
|
|
@@ -69,15 +71,12 @@ async function findProjectRootAsync({ cwd, defaultToProcessCwd = false, } = {})
|
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
exports.findProjectRootAsync = findProjectRootAsync;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Save an EAS project ID to the appropriate field in the app config.
|
|
76
|
+
*/
|
|
77
|
+
async function saveProjectIdToAppConfigAsync(projectDir, projectId, options = {}) {
|
|
78
|
+
var _a;
|
|
74
79
|
const exp = (0, expoConfig_1.getExpoConfig)(projectDir, options);
|
|
75
|
-
const privacy = toAppPrivacy(exp.privacy);
|
|
76
|
-
const projectId = await (0, ensureProjectExists_1.ensureProjectExistsAsync)({
|
|
77
|
-
accountName: getProjectAccountName(exp, await (0, actions_1.ensureLoggedInAsync)()),
|
|
78
|
-
projectName: exp.slug,
|
|
79
|
-
privacy,
|
|
80
|
-
});
|
|
81
80
|
const result = await (0, config_1.modifyConfigAsync)(projectDir, {
|
|
82
81
|
extra: { ...exp.extra, eas: { ...(_a = exp.extra) === null || _a === void 0 ? void 0 : _a.eas, projectId } },
|
|
83
82
|
});
|
|
@@ -97,36 +96,48 @@ async function setProjectIdAsync(projectDir, options = {}) {
|
|
|
97
96
|
default:
|
|
98
97
|
throw new Error('Unexpected result type from modifyConfigAsync');
|
|
99
98
|
}
|
|
100
|
-
log_1.default.withTick(`Linked app.json to project with ID ${chalk_1.default.bold(projectId)}`);
|
|
101
|
-
return (_b = result.config) === null || _b === void 0 ? void 0 : _b.expo;
|
|
102
99
|
}
|
|
103
|
-
exports.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
exports.saveProjectIdToAppConfigAsync = saveProjectIdToAppConfigAsync;
|
|
101
|
+
/**
|
|
102
|
+
* Use the owner/slug to identify an EAS project on the server.
|
|
103
|
+
*
|
|
104
|
+
* @returns the EAS project ID from the server
|
|
105
|
+
*/
|
|
106
|
+
async function fetchProjectIdFromServerAsync(exp) {
|
|
107
|
+
const privacy = toAppPrivacy(exp.privacy);
|
|
108
|
+
return await (0, ensureProjectExists_1.ensureProjectExistsAsync)({
|
|
109
|
+
accountName: getProjectAccountName(exp, await (0, actions_1.ensureLoggedInAsync)()),
|
|
110
|
+
projectName: exp.slug,
|
|
111
|
+
privacy,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
exports.fetchProjectIdFromServerAsync = fetchProjectIdFromServerAsync;
|
|
115
|
+
/**
|
|
116
|
+
* Get the EAS project ID from the app config. If the project ID is not set in the config.
|
|
117
|
+
* use the owner/slug to identify an EAS project on the server, and attempt to save the
|
|
118
|
+
* EAS project ID to the appropriate field in the app config.
|
|
119
|
+
*/
|
|
120
|
+
async function getProjectIdAsync(exp, options = {}, findProjectRootOptions = {}) {
|
|
121
|
+
var _a, _b;
|
|
114
122
|
const localProjectId = (_b = (_a = exp.extra) === null || _a === void 0 ? void 0 : _a.eas) === null || _b === void 0 ? void 0 : _b.projectId;
|
|
115
123
|
if (localProjectId) {
|
|
116
124
|
return localProjectId;
|
|
117
125
|
}
|
|
118
|
-
|
|
119
|
-
const projectDir = await findProjectRootAsync();
|
|
126
|
+
const projectDir = await findProjectRootAsync(findProjectRootOptions);
|
|
120
127
|
if (!projectDir) {
|
|
121
|
-
throw new Error('
|
|
128
|
+
throw new Error('Run this command inside a project directory.');
|
|
129
|
+
}
|
|
130
|
+
const projectId = await fetchProjectIdFromServerAsync(exp);
|
|
131
|
+
try {
|
|
132
|
+
await saveProjectIdToAppConfigAsync(projectDir, projectId, options);
|
|
122
133
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
|
|
134
|
+
catch (e) {
|
|
135
|
+
// saveProjectIdToAppConfigAsync already printed out a set of detailed errors and
|
|
136
|
+
// instructions on how to fix it. To mimic throwing the error but not halting
|
|
137
|
+
// execution, just warn here with the error message.
|
|
138
|
+
log_1.default.warn(e.message);
|
|
128
139
|
}
|
|
129
|
-
return
|
|
140
|
+
return projectId;
|
|
130
141
|
}
|
|
131
142
|
exports.getProjectIdAsync = getProjectIdAsync;
|
|
132
143
|
const toAppPrivacy = (privacy) => {
|
|
@@ -202,6 +213,21 @@ function isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion) {
|
|
|
202
213
|
return isExpoUpdatesInstalled(projectDir);
|
|
203
214
|
}
|
|
204
215
|
exports.isExpoUpdatesInstalledOrAvailable = isExpoUpdatesInstalledOrAvailable;
|
|
216
|
+
async function validateAppVersionRuntimePolicySupportAsync(projectDir, exp) {
|
|
217
|
+
var _a;
|
|
218
|
+
if (typeof exp.runtimeVersion !== 'object' || ((_a = exp.runtimeVersion) === null || _a === void 0 ? void 0 : _a.policy) !== 'appVersion') {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const maybePackageJson = resolve_from_1.default.silent(projectDir, 'expo-updates/package.json');
|
|
222
|
+
if (maybePackageJson) {
|
|
223
|
+
const { version } = await fs_extra_1.default.readJson(maybePackageJson);
|
|
224
|
+
if (semver_1.default.gte(version, '0.14.4')) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
log_1.default.warn(`You need to be on SDK 46 or higher, and use expo-updates >= 0.14.4 to use appVersion runtime policy.`);
|
|
229
|
+
}
|
|
230
|
+
exports.validateAppVersionRuntimePolicySupportAsync = validateAppVersionRuntimePolicySupportAsync;
|
|
205
231
|
async function installExpoUpdatesAsync(projectDir) {
|
|
206
232
|
log_1.default.newLine();
|
|
207
233
|
log_1.default.log(`Running ${chalk_1.default.bold('expo install expo-updates')}`);
|
package/build/project/publish.js
CHANGED
|
@@ -14,6 +14,7 @@ const PublishMutation_1 = require("../graphql/mutations/PublishMutation");
|
|
|
14
14
|
const PublishQuery_1 = require("../graphql/queries/PublishQuery");
|
|
15
15
|
const uploads_1 = require("../uploads");
|
|
16
16
|
const expoCli_1 = require("../utils/expoCli");
|
|
17
|
+
const chunk_1 = tslib_1.__importDefault(require("../utils/expodash/chunk"));
|
|
17
18
|
const uniqBy_1 = tslib_1.__importDefault(require("../utils/expodash/uniqBy"));
|
|
18
19
|
const fileMetadataJoi = joi_1.default.object({
|
|
19
20
|
assets: joi_1.default.array()
|
|
@@ -205,7 +206,12 @@ async function uploadAssetsAsync(assetsForUpdateInfoGroup, projectId, updateSpin
|
|
|
205
206
|
updateSpinnerText === null || updateSpinnerText === void 0 ? void 0 : updateSpinnerText(totalAssets, totalAssets);
|
|
206
207
|
let missingAssets = await filterOutAssetsThatAlreadyExistAsync(uniqueAssets);
|
|
207
208
|
const uniqueUploadedAssetCount = missingAssets.length;
|
|
208
|
-
const
|
|
209
|
+
const missingAssetChunks = (0, chunk_1.default)(missingAssets, 100);
|
|
210
|
+
const specifications = [];
|
|
211
|
+
for (const missingAssets of missingAssetChunks) {
|
|
212
|
+
const { specifications: chunkSpecifications } = await PublishMutation_1.PublishMutation.getUploadURLsAsync(missingAssets.map(ma => ma.contentType));
|
|
213
|
+
specifications.push(...chunkSpecifications);
|
|
214
|
+
}
|
|
209
215
|
updateSpinnerText === null || updateSpinnerText === void 0 ? void 0 : updateSpinnerText(totalAssets, missingAssets.length);
|
|
210
216
|
const assetUploadPromiseLimit = (0, promise_limit_1.default)(15);
|
|
211
217
|
const [assetLimitPerUpdateGroup] = await Promise.all([
|
|
@@ -213,7 +219,7 @@ async function uploadAssetsAsync(assetsForUpdateInfoGroup, projectId, updateSpin
|
|
|
213
219
|
missingAssets.map((missingAsset, i) => {
|
|
214
220
|
assetUploadPromiseLimit(async () => {
|
|
215
221
|
const presignedPost = JSON.parse(specifications[i]);
|
|
216
|
-
await (0, uploads_1.
|
|
222
|
+
await (0, uploads_1.uploadWithPresignedPostWithRetryAsync)(missingAsset.path, presignedPost);
|
|
217
223
|
});
|
|
218
224
|
}),
|
|
219
225
|
]);
|
|
@@ -24,12 +24,12 @@ var SubmissionErrorCode;
|
|
|
24
24
|
SubmissionErrorCode["IOS_INVALID_PROVISIONING_PROFILE_SIGNATURE"] = "SUBMISSION_SERVICE_IOS_INVALID_PROVISIONING_PROFILE_SIGNATURE";
|
|
25
25
|
})(SubmissionErrorCode || (SubmissionErrorCode = {}));
|
|
26
26
|
const SubmissionErrorMessages = {
|
|
27
|
-
[SubmissionErrorCode.ARCHIVE_DOWNLOAD_NOT_FOUND_ERROR]: "Failed to download the archive file (Response code: 404 Not Found).
|
|
27
|
+
[SubmissionErrorCode.ARCHIVE_DOWNLOAD_NOT_FOUND_ERROR]: "Failed to download the archive file (Response code: 404 Not Found). Make sure the URL you've provided is correct.",
|
|
28
28
|
[SubmissionErrorCode.ARCHIVE_DOWNLOAD_FORBIDDEN_ERROR]: 'Failed to download the archive file (Response code: 403 Forbidden). This is most probably caused by trying to upload an expired build artifact. All EAS build artifacts expire after 30 days.',
|
|
29
29
|
[SubmissionErrorCode.ARCHIVE_EXTRACT_CORRUPT_ARCHIVE_ERROR]: 'The compressed archive is corrupt, in an unsupported format, or contains an invalid application format. Supported files include .apk, .aab, and .ipa files and one of these files compressed into a .tar.gz archive.',
|
|
30
30
|
[SubmissionErrorCode.ARCHIVE_EXTRACT_NO_FILES_FOUND_ERROR]: "EAS Submit couldn't find a valid build artifact within provided compressed archive.\n" +
|
|
31
31
|
'If you provide a tar.gz archive, it should contain at least one .apk/.aab/.ipa file, depending on the submission platform.',
|
|
32
|
-
[SubmissionErrorCode.ANDROID_UNKNOWN_ERROR]: "We couldn't figure out what went wrong.
|
|
32
|
+
[SubmissionErrorCode.ANDROID_UNKNOWN_ERROR]: "We couldn't figure out what went wrong. See logs to learn more.",
|
|
33
33
|
[SubmissionErrorCode.ANDROID_FIRST_UPLOAD_ERROR]: "You haven't submitted this app to Google Play Store yet. The first submission of the app needs to be performed manually.\n" +
|
|
34
34
|
`${(0, log_1.learnMore)('https://expo.fyi/first-android-submission')}.`,
|
|
35
35
|
[SubmissionErrorCode.ANDROID_OLD_VERSION_CODE_ERROR]: "You've already submitted this version of the app.\n" +
|
|
@@ -42,22 +42,22 @@ const SubmissionErrorMessages = {
|
|
|
42
42
|
'Versions are identified by Build Numbers (expo.ios.buildNumber in app.json).\n' +
|
|
43
43
|
"If you're submitting an Expo project built with EAS Build, increment the build number in app.json and build the project again.\n" +
|
|
44
44
|
`${(0, log_1.learnMore)('https://expo.fyi/bumping-ios-build-number')}.`,
|
|
45
|
-
[SubmissionErrorCode.IOS_UNKNOWN_ERROR]: "We couldn't figure out what went wrong.
|
|
45
|
+
[SubmissionErrorCode.IOS_UNKNOWN_ERROR]: "We couldn't figure out what went wrong. See logs to learn more.",
|
|
46
46
|
[SubmissionErrorCode.IOS_MISSING_APP_ICON]: 'Your iOS app icon is missing or is an invalid format. The icon must be a 1024x1024 PNG image with no transparency.\n' +
|
|
47
|
-
'
|
|
47
|
+
'Check your icon image and icon configuration in app.json.\n' +
|
|
48
48
|
`${(0, log_1.learnMore)('https://docs.expo.dev/guides/app-icons/')}`,
|
|
49
49
|
[SubmissionErrorCode.IOS_INVALID_SIGNATURE]: 'Your app signature seems to be invalid.\n' +
|
|
50
|
-
"
|
|
50
|
+
"Check your iOS Distribution Certificate and your app's Provisioning Profile.\n" +
|
|
51
51
|
`${(0, log_1.learnMore)('https://docs.expo.dev/distribution/app-signing')}`,
|
|
52
|
-
[SubmissionErrorCode.IOS_INCORRECT_CREDENTIALS]: 'Your Apple ID or app-specific password is incorrect.
|
|
52
|
+
[SubmissionErrorCode.IOS_INCORRECT_CREDENTIALS]: 'Your Apple ID or app-specific password is incorrect. Verify that you entered them correctly and try again.',
|
|
53
53
|
[SubmissionErrorCode.IOS_IPAD_INVALID_ORIENTATION]: "Your app doesn't support iPad multitasking and has to require full screen.\n" +
|
|
54
54
|
"If you're submitting a managed Expo project, set the `expo.ios.requireFullScreen` to true in app.json and build the project again.\n" +
|
|
55
55
|
`${(0, log_1.learnMore)('https://expo.fyi/ipad-requires-fullscreen')}`,
|
|
56
|
-
[SubmissionErrorCode.IOS_APPLE_MAINTENANCE]: 'It looks like Apple servers are undergoing an unscheduled maintenance.
|
|
56
|
+
[SubmissionErrorCode.IOS_APPLE_MAINTENANCE]: 'It looks like Apple servers are undergoing an unscheduled maintenance. Try again later.',
|
|
57
57
|
[SubmissionErrorCode.IOS_INVALID_PROVISIONING_PROFILE_SIGNATURE]: 'Invalid Provisioning Profile Signature (ITMS-90165)\n' +
|
|
58
58
|
"Some of Apple's certificates have expired.\n" +
|
|
59
|
-
'
|
|
60
|
-
[SubmissionErrorCode.UPLOAD_TAKING_TOO_LONG_ERROR]: 'Submission has reached the timeout limit.
|
|
59
|
+
'Delete your Provisioning Profile from your account. Then rebuild the app interactively to generate a new one, and try submitting it to the App Store again.',
|
|
60
|
+
[SubmissionErrorCode.UPLOAD_TAKING_TOO_LONG_ERROR]: 'Submission has reached the timeout limit. Try again.',
|
|
61
61
|
};
|
|
62
62
|
function printSubmissionError(error) {
|
|
63
63
|
if (error.errorCode &&
|
|
@@ -12,14 +12,11 @@ const APP_STORE_NAMES = {
|
|
|
12
12
|
[generated_1.AppPlatform.Android]: 'Google Play Store',
|
|
13
13
|
[generated_1.AppPlatform.Ios]: 'Apple App Store Connect',
|
|
14
14
|
};
|
|
15
|
-
const CHECK_TIMEOUT_MS = 3600000;
|
|
16
15
|
const CHECK_INTERVAL_MS = 5000;
|
|
17
16
|
async function waitForSubmissionsEndAsync(initialSubmissions) {
|
|
18
17
|
log_1.default.log(`Waiting for submission${initialSubmissions.length > 1 ? 's' : ''} to complete. You can press Ctrl+C to exit.`);
|
|
19
18
|
const spinner = (0, ora_1.ora)(`Submitting`).start();
|
|
20
|
-
|
|
21
|
-
const timeoutTime = time + CHECK_TIMEOUT_MS;
|
|
22
|
-
while (time <= timeoutTime) {
|
|
19
|
+
while (true) {
|
|
23
20
|
const submissions = await Promise.all(initialSubmissions.map(({ id }) => {
|
|
24
21
|
try {
|
|
25
22
|
return SubmissionQuery_1.SubmissionQuery.byIdAsync(id, { useCache: false });
|
|
@@ -69,11 +66,8 @@ async function waitForSubmissionsEndAsync(initialSubmissions) {
|
|
|
69
66
|
return submissions.map(s => (0, nullthrows_1.default)(s));
|
|
70
67
|
}
|
|
71
68
|
}
|
|
72
|
-
time = new Date().getTime();
|
|
73
69
|
await (0, promise_1.sleepAsync)(CHECK_INTERVAL_MS);
|
|
74
70
|
}
|
|
75
|
-
spinner.warn('Timed out');
|
|
76
|
-
throw new Error('Timeout reached! It is taking longer than expected to complete, aborting...');
|
|
77
71
|
}
|
|
78
72
|
exports.waitForSubmissionsEndAsync = waitForSubmissionsEndAsync;
|
|
79
73
|
function getSingleSpinnerText(submission) {
|
package/build/update/utils.js
CHANGED
|
@@ -52,7 +52,7 @@ function formatUpdate(update) {
|
|
|
52
52
|
exports.formatUpdate = formatUpdate;
|
|
53
53
|
function ensureValidVersions(exp, platform) {
|
|
54
54
|
var _a, _b;
|
|
55
|
-
const error = new Error(`Couldn't find either ${chalk_1.default.bold('runtimeVersion')} or ${chalk_1.default.bold('sdkVersion')} to configure ${chalk_1.default.bold('expo-updates')}.
|
|
55
|
+
const error = new Error(`Couldn't find either ${chalk_1.default.bold('runtimeVersion')} or ${chalk_1.default.bold('sdkVersion')} to configure ${chalk_1.default.bold('expo-updates')}. Specify at least one of these properties under the ${chalk_1.default.bold('expo')} key in ${chalk_1.default.bold('app.json')}. ${(0, log_1.learnMore)('https://docs.expo.dev/eas-update/runtime-versions/')}`);
|
|
56
56
|
if ([platform_1.RequestedPlatform.Android, platform_1.RequestedPlatform.All].includes(platform) &&
|
|
57
57
|
!(((_a = exp.android) === null || _a === void 0 ? void 0 : _a.runtimeVersion) || exp.runtimeVersion) &&
|
|
58
58
|
!exp.sdkVersion) {
|
package/build/uploads.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare function uploadFileAtPathToS3Async(type: UploadSessionType, path:
|
|
|
6
6
|
url: string;
|
|
7
7
|
bucketKey: string;
|
|
8
8
|
}>;
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function uploadWithPresignedPostWithRetryAsync(file: string, presignedPost: PresignedPost): Promise<Response>;
|
|
10
10
|
/**
|
|
11
11
|
* S3 returns broken URLs, sth like:
|
|
12
12
|
* https://submission-service-archives.s3.amazonaws.com/production%2Fdc98ca84-1473-4cb3-ae81-8c7b291cb27e%2F4424aa95-b985-4e2f-8755-9507b1037c1c
|
package/build/uploads.js
CHANGED
|
@@ -1,24 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.fixS3Url = exports.
|
|
3
|
+
exports.fixS3Url = exports.uploadWithPresignedPostWithRetryAsync = exports.uploadFileAtPathToS3Async = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
6
|
const form_data_1 = tslib_1.__importDefault(require("form-data"));
|
|
7
7
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
8
8
|
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
9
|
+
const promise_retry_1 = tslib_1.__importDefault(require("promise-retry"));
|
|
9
10
|
const url_1 = require("url");
|
|
10
11
|
const fetch_1 = tslib_1.__importDefault(require("./fetch"));
|
|
11
12
|
const UploadSessionMutation_1 = require("./graphql/mutations/UploadSessionMutation");
|
|
12
13
|
async function uploadFileAtPathToS3Async(type, path, handleProgressEvent) {
|
|
13
14
|
const presignedPost = await UploadSessionMutation_1.UploadSessionMutation.createUploadSessionAsync(type);
|
|
14
15
|
(0, assert_1.default)(presignedPost.fields.key, 'key is not specified in in presigned post');
|
|
15
|
-
const response = await
|
|
16
|
+
const response = await uploadWithPresignedPostWithProgressAsync(path, presignedPost, handleProgressEvent);
|
|
16
17
|
const location = (0, nullthrows_1.default)(response.headers.get('location'), `location does not exist in response headers (make sure you're uploading to AWS S3)`);
|
|
17
18
|
const url = fixS3Url(location);
|
|
18
19
|
return { url, bucketKey: presignedPost.fields.key };
|
|
19
20
|
}
|
|
20
21
|
exports.uploadFileAtPathToS3Async = uploadFileAtPathToS3Async;
|
|
21
|
-
async function
|
|
22
|
+
async function uploadWithPresignedPostWithRetryAsync(file, presignedPost) {
|
|
23
|
+
return await (0, promise_retry_1.default)(async (retry) => {
|
|
24
|
+
// retry fetch errors (usually connection or DNS errors)
|
|
25
|
+
let response;
|
|
26
|
+
try {
|
|
27
|
+
response = await uploadWithPresignedPostAsync(file, presignedPost);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return retry(e);
|
|
31
|
+
}
|
|
32
|
+
// retry 408, 429, 5xx as suggested by google
|
|
33
|
+
if (response.status === 408 ||
|
|
34
|
+
response.status === 429 ||
|
|
35
|
+
(response.status >= 500 && response.status <= 599)) {
|
|
36
|
+
return retry(new Error(`Presigned upload responded with a ${response.status} status`));
|
|
37
|
+
}
|
|
38
|
+
// don't retry other errors
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`Presigned upload responded with a ${response.status} status`);
|
|
41
|
+
}
|
|
42
|
+
return response;
|
|
43
|
+
},
|
|
44
|
+
// retry parameters match google suggested defaults: https://cloud.google.com/storage/docs/retry-strategy#node.js
|
|
45
|
+
{
|
|
46
|
+
retries: 3,
|
|
47
|
+
factor: 2,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
exports.uploadWithPresignedPostWithRetryAsync = uploadWithPresignedPostWithRetryAsync;
|
|
51
|
+
async function createPresignedPostFormDataAsync(file, presignedPost) {
|
|
22
52
|
const fileStat = await fs_extra_1.default.stat(file);
|
|
23
53
|
const fileSize = fileStat.size;
|
|
24
54
|
const form = new form_data_1.default();
|
|
@@ -26,6 +56,21 @@ async function uploadWithPresignedPostAsync(file, presignedPost, handleProgressE
|
|
|
26
56
|
form.append(fieldKey, fieldValue);
|
|
27
57
|
}
|
|
28
58
|
form.append('file', fs_extra_1.default.createReadStream(file), { knownLength: fileSize });
|
|
59
|
+
return { form, fileSize };
|
|
60
|
+
}
|
|
61
|
+
async function uploadWithPresignedPostAsync(file, presignedPost) {
|
|
62
|
+
const { form } = await createPresignedPostFormDataAsync(file, presignedPost);
|
|
63
|
+
const formHeaders = form.getHeaders();
|
|
64
|
+
return await (0, fetch_1.default)(presignedPost.url, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
body: form,
|
|
67
|
+
headers: {
|
|
68
|
+
...formHeaders,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function uploadWithPresignedPostWithProgressAsync(file, presignedPost, handleProgressEvent) {
|
|
73
|
+
const { form, fileSize } = await createPresignedPostFormDataAsync(file, presignedPost);
|
|
29
74
|
const formHeaders = form.getHeaders();
|
|
30
75
|
const uploadPromise = (0, fetch_1.default)(presignedPost.url, {
|
|
31
76
|
method: 'POST',
|
|
@@ -35,32 +80,26 @@ async function uploadWithPresignedPostAsync(file, presignedPost, handleProgressE
|
|
|
35
80
|
},
|
|
36
81
|
});
|
|
37
82
|
let currentSize = 0;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
});
|
|
83
|
+
form.addListener('data', (chunk) => {
|
|
84
|
+
currentSize += Buffer.byteLength(chunk);
|
|
85
|
+
handleProgressEvent({
|
|
86
|
+
progress: {
|
|
87
|
+
total: fileSize,
|
|
88
|
+
percent: currentSize / fileSize,
|
|
89
|
+
transferred: currentSize,
|
|
90
|
+
},
|
|
48
91
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
catch (error) {
|
|
55
|
-
handleProgressEvent({ isComplete: true, error });
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
92
|
+
});
|
|
93
|
+
try {
|
|
94
|
+
const response = await uploadPromise;
|
|
95
|
+
handleProgressEvent({ isComplete: true });
|
|
96
|
+
return response;
|
|
58
97
|
}
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
catch (error) {
|
|
99
|
+
handleProgressEvent({ isComplete: true, error });
|
|
100
|
+
throw error;
|
|
61
101
|
}
|
|
62
102
|
}
|
|
63
|
-
exports.uploadWithPresignedPostAsync = uploadWithPresignedPostAsync;
|
|
64
103
|
/**
|
|
65
104
|
* S3 returns broken URLs, sth like:
|
|
66
105
|
* https://submission-service-archives.s3.amazonaws.com/production%2Fdc98ca84-1473-4cb3-ae81-8c7b291cb27e%2F4424aa95-b985-4e2f-8755-9507b1037c1c
|
package/build/vcs/clients/git.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isGitCaseSensitiveAsync = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const PackageManagerUtils = tslib_1.__importStar(require("@expo/package-manager"));
|
|
5
6
|
const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
|
|
6
7
|
const core_1 = require("@oclif/core");
|
|
7
8
|
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
@@ -12,6 +13,7 @@ const git_1 = require("../git");
|
|
|
12
13
|
const vcs_1 = require("../vcs");
|
|
13
14
|
class GitClient extends vcs_1.Client {
|
|
14
15
|
async ensureRepoExistsAsync() {
|
|
16
|
+
var _a;
|
|
15
17
|
if (!(await (0, git_1.isGitInstalledAsync)())) {
|
|
16
18
|
log_1.default.error(`${chalk_1.default.bold('git')} command not found. Install it before proceeding or set ${chalk_1.default.bold('EAS_NO_VCS=1')} to use EAS CLI without Git (or any other version control system).`);
|
|
17
19
|
log_1.default.error((0, log_1.learnMore)('https://expo.fyi/eas-vcs-workflow'));
|
|
@@ -22,13 +24,15 @@ class GitClient extends vcs_1.Client {
|
|
|
22
24
|
}
|
|
23
25
|
log_1.default.warn("It looks like you haven't initialized the git repository yet.");
|
|
24
26
|
log_1.default.warn('EAS Build requires you to use a git repository for your project.');
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
const repoRoot = (_a = PackageManagerUtils.findWorkspaceRoot(cwd)) !== null && _a !== void 0 ? _a : cwd;
|
|
25
29
|
const confirmInit = await (0, prompts_1.confirmAsync)({
|
|
26
|
-
message: `Would you like us to run 'git init' in
|
|
30
|
+
message: `Would you like us to run 'git init' in ${repoRoot} for you?`,
|
|
27
31
|
});
|
|
28
32
|
if (!confirmInit) {
|
|
29
33
|
throw new Error('A git repository is required for building your project. Initialize it and run this command again.');
|
|
30
34
|
}
|
|
31
|
-
await (0, spawn_async_1.default)('git', ['init']);
|
|
35
|
+
await (0, spawn_async_1.default)('git', ['init'], { cwd: repoRoot });
|
|
32
36
|
log_1.default.log("We're going to make an initial commit for your repository.");
|
|
33
37
|
const { message } = await (0, prompts_1.promptAsync)({
|
|
34
38
|
type: 'text',
|