eas-cli 0.58.0 → 1.0.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 +84 -44
- package/build/build/android/version.js +6 -6
- package/build/build/build.d.ts +1 -2
- package/build/build/build.js +2 -7
- package/build/build/ios/version.js +6 -6
- package/build/build/local.js +1 -1
- package/build/build/runBuildAndSubmit.js +5 -1
- package/build/commands/metadata/pull.js +2 -2
- package/build/graphql/generated.d.ts +45 -4
- 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/upload.js +18 -21
- package/build/ora.js +1 -1
- package/build/project/projectUtils.d.ts +4 -0
- package/build/project/projectUtils.js +20 -11
- package/build/project/publish.js +8 -2
- package/build/submit/utils/wait.js +1 -7
- 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 +33 -28
|
@@ -24,13 +24,9 @@ export declare type Scalars = {
|
|
|
24
24
|
Boolean: boolean;
|
|
25
25
|
Int: number;
|
|
26
26
|
Float: number;
|
|
27
|
-
/** Date custom scalar type */
|
|
28
27
|
DateTime: any;
|
|
29
|
-
/** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
|
30
28
|
JSON: any;
|
|
31
|
-
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
|
|
32
29
|
JSONObject: any;
|
|
33
|
-
/** The `Upload` scalar type represents a file upload. */
|
|
34
30
|
Upload: any;
|
|
35
31
|
};
|
|
36
32
|
export declare type AcceptUserInvitationResult = {
|
|
@@ -106,6 +102,8 @@ export declare type Account = {
|
|
|
106
102
|
createdAt: Scalars['DateTime'];
|
|
107
103
|
/** Environment secrets for an account */
|
|
108
104
|
environmentSecrets: Array<EnvironmentSecret>;
|
|
105
|
+
/** GitHub App installations for an account */
|
|
106
|
+
githubAppInstallations: Array<GitHubAppInstallation>;
|
|
109
107
|
/** Android credentials for account */
|
|
110
108
|
googleServiceAccountKeys: Array<GoogleServiceAccountKey>;
|
|
111
109
|
id: Scalars['ID'];
|
|
@@ -1819,6 +1817,10 @@ export declare type CreateEnvironmentSecretInput = {
|
|
|
1819
1817
|
name: Scalars['String'];
|
|
1820
1818
|
value: Scalars['String'];
|
|
1821
1819
|
};
|
|
1820
|
+
export declare type CreateGitHubAppInstallationInput = {
|
|
1821
|
+
accountId: Scalars['ID'];
|
|
1822
|
+
installationIdentifier: Scalars['Int'];
|
|
1823
|
+
};
|
|
1822
1824
|
export declare type CreateIosSubmissionInput = {
|
|
1823
1825
|
appId: Scalars['ID'];
|
|
1824
1826
|
archiveUrl?: InputMaybe<Scalars['String']>;
|
|
@@ -2015,6 +2017,31 @@ export declare type GetSignedAssetUploadSpecificationsResult = {
|
|
|
2015
2017
|
__typename?: 'GetSignedAssetUploadSpecificationsResult';
|
|
2016
2018
|
specifications: Array<Scalars['String']>;
|
|
2017
2019
|
};
|
|
2020
|
+
export declare type GitHubAppInstallation = {
|
|
2021
|
+
__typename?: 'GitHubAppInstallation';
|
|
2022
|
+
account: Account;
|
|
2023
|
+
actor?: Maybe<Actor>;
|
|
2024
|
+
id: Scalars['ID'];
|
|
2025
|
+
installationIdentifier: Scalars['Int'];
|
|
2026
|
+
};
|
|
2027
|
+
export declare type GitHubAppInstallationMutation = {
|
|
2028
|
+
__typename?: 'GitHubAppInstallationMutation';
|
|
2029
|
+
/** Create a GitHub App installation for an Account */
|
|
2030
|
+
createGitHubAppInstallationForAccount: GitHubAppInstallation;
|
|
2031
|
+
/** Delete a GitHub App installation by ID */
|
|
2032
|
+
deleteGitHubAppInstallation: GitHubAppInstallation;
|
|
2033
|
+
};
|
|
2034
|
+
export declare type GitHubAppInstallationMutationCreateGitHubAppInstallationForAccountArgs = {
|
|
2035
|
+
githubAppInstallationData: CreateGitHubAppInstallationInput;
|
|
2036
|
+
};
|
|
2037
|
+
export declare type GitHubAppInstallationMutationDeleteGitHubAppInstallationArgs = {
|
|
2038
|
+
githubAppInstallationId: Scalars['ID'];
|
|
2039
|
+
};
|
|
2040
|
+
export declare type GitHubAppQuery = {
|
|
2041
|
+
__typename?: 'GitHubAppQuery';
|
|
2042
|
+
appIdentifier: Scalars['String'];
|
|
2043
|
+
clientIdentifier: Scalars['String'];
|
|
2044
|
+
};
|
|
2018
2045
|
export declare type GoogleServiceAccountKey = {
|
|
2019
2046
|
__typename?: 'GoogleServiceAccountKey';
|
|
2020
2047
|
account: Account;
|
|
@@ -2615,6 +2642,8 @@ export declare type RootMutation = {
|
|
|
2615
2642
|
emailSubscription: EmailSubscriptionMutation;
|
|
2616
2643
|
/** Mutations that create and delete EnvironmentSecrets */
|
|
2617
2644
|
environmentSecret: EnvironmentSecretMutation;
|
|
2645
|
+
/** Mutations for GitHub App installations */
|
|
2646
|
+
githubAppInstallation: GitHubAppInstallationMutation;
|
|
2618
2647
|
/** Mutations that modify a Google Service Account Key */
|
|
2619
2648
|
googleServiceAccountKey: GoogleServiceAccountKeyMutation;
|
|
2620
2649
|
/** Mutations that modify the build credentials for an iOS app */
|
|
@@ -2652,6 +2681,8 @@ export declare type RootMutationBuildJobArgs = {
|
|
|
2652
2681
|
};
|
|
2653
2682
|
export declare type RootQuery = {
|
|
2654
2683
|
__typename?: 'RootQuery';
|
|
2684
|
+
/** Top-level query object for querying GitHub App information and resources it has access to. */
|
|
2685
|
+
GitHubApp: GitHubAppQuery;
|
|
2655
2686
|
/**
|
|
2656
2687
|
* This is a placeholder field
|
|
2657
2688
|
* @deprecated Not used.
|
|
@@ -2831,6 +2862,15 @@ export declare enum StandardOffer {
|
|
|
2831
2862
|
/** $348 USD per year, 30 day trial */
|
|
2832
2863
|
YearlySub = "YEARLY_SUB"
|
|
2833
2864
|
}
|
|
2865
|
+
export declare type StripeCoupon = {
|
|
2866
|
+
__typename?: 'StripeCoupon';
|
|
2867
|
+
amountOff?: Maybe<Scalars['String']>;
|
|
2868
|
+
appliesTo?: Maybe<Scalars['String']>;
|
|
2869
|
+
id: Scalars['ID'];
|
|
2870
|
+
name: Scalars['String'];
|
|
2871
|
+
percentOff?: Maybe<Scalars['Float']>;
|
|
2872
|
+
valid: Scalars['Boolean'];
|
|
2873
|
+
};
|
|
2834
2874
|
/** Represents an EAS Submission */
|
|
2835
2875
|
export declare type Submission = ActivityTimelineProjectActivity & {
|
|
2836
2876
|
__typename?: 'Submission';
|
|
@@ -2926,6 +2966,7 @@ export declare type SubscriptionDetails = {
|
|
|
2926
2966
|
addons: Array<AddonDetails>;
|
|
2927
2967
|
cancelledAt?: Maybe<Scalars['DateTime']>;
|
|
2928
2968
|
concurrencies?: Maybe<Concurrencies>;
|
|
2969
|
+
coupon?: Maybe<StripeCoupon>;
|
|
2929
2970
|
endedAt?: Maybe<Scalars['DateTime']>;
|
|
2930
2971
|
futureSubscription?: Maybe<FutureSubscription>;
|
|
2931
2972
|
id: Scalars['ID'];
|
|
@@ -8,6 +8,25 @@ export interface MetadataConfig {
|
|
|
8
8
|
/** All App Store related configuration */
|
|
9
9
|
apple?: AppleMetadata;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Get the static configuration file path, based on the metadata context.
|
|
13
|
+
* This uses any custom name provided, but swaps out the extension for `.json`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getStaticConfigFilePath({ projectDir, metadataPath, }: {
|
|
16
|
+
projectDir: string;
|
|
17
|
+
metadataPath: string;
|
|
18
|
+
}): string;
|
|
19
|
+
/**
|
|
20
|
+
* Load the store configuration from a metadata context.
|
|
21
|
+
* This can load `.json` and `.js` config files, using `require`.
|
|
22
|
+
* It throws MetadataValidationErrors when the file doesn't exist, or contains errors.
|
|
23
|
+
* The user is prompted to try anyway when errors are found.
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadConfigAsync({ projectDir, metadataPath, skipValidation, }: {
|
|
26
|
+
projectDir: string;
|
|
27
|
+
metadataPath: string;
|
|
28
|
+
skipValidation?: boolean;
|
|
29
|
+
}): Promise<MetadataConfig>;
|
|
11
30
|
/**
|
|
12
31
|
* Run the JSON Schema validation to normalize defaults and flag early config errors.
|
|
13
32
|
* This includes validating the known store limitations for every configurable property.
|
package/build/metadata/config.js
CHANGED
|
@@ -1,11 +1,55 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createAppleWriter = exports.createAppleReader = exports.validateConfig = void 0;
|
|
3
|
+
exports.createAppleWriter = exports.createAppleReader = exports.validateConfig = exports.loadConfigAsync = exports.getStaticConfigFilePath = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const ajv_1 = tslib_1.__importDefault(require("ajv"));
|
|
6
6
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
7
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
9
|
const reader_1 = require("./apple/config/reader");
|
|
8
10
|
const writer_1 = require("./apple/config/writer");
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the dynamic config from the user.
|
|
14
|
+
* It supports methods, async methods, or objects (json).
|
|
15
|
+
*/
|
|
16
|
+
async function resolveDynamicConfigAsync(configFile) {
|
|
17
|
+
const userConfigOrFunction = await Promise.resolve().then(() => tslib_1.__importStar(require(configFile))).then(file => { var _a; return (_a = file.default) !== null && _a !== void 0 ? _a : file; });
|
|
18
|
+
return typeof userConfigOrFunction === 'function'
|
|
19
|
+
? await userConfigOrFunction()
|
|
20
|
+
: userConfigOrFunction;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get the static configuration file path, based on the metadata context.
|
|
24
|
+
* This uses any custom name provided, but swaps out the extension for `.json`.
|
|
25
|
+
*/
|
|
26
|
+
function getStaticConfigFilePath({ projectDir, metadataPath, }) {
|
|
27
|
+
const configFile = path_1.default.join(projectDir, metadataPath);
|
|
28
|
+
const configExtension = path_1.default.extname(configFile);
|
|
29
|
+
return path_1.default.join(projectDir, `${path_1.default.basename(configFile, configExtension)}.json`);
|
|
30
|
+
}
|
|
31
|
+
exports.getStaticConfigFilePath = getStaticConfigFilePath;
|
|
32
|
+
/**
|
|
33
|
+
* Load the store configuration from a metadata context.
|
|
34
|
+
* This can load `.json` and `.js` config files, using `require`.
|
|
35
|
+
* It throws MetadataValidationErrors when the file doesn't exist, or contains errors.
|
|
36
|
+
* The user is prompted to try anyway when errors are found.
|
|
37
|
+
*/
|
|
38
|
+
async function loadConfigAsync({ projectDir, metadataPath, skipValidation = false, }) {
|
|
39
|
+
const configFile = path_1.default.join(projectDir, metadataPath);
|
|
40
|
+
if (!(await fs_extra_1.default.pathExists(configFile))) {
|
|
41
|
+
throw new errors_1.MetadataValidationError(`Metadata store config file not found: "${configFile}"`);
|
|
42
|
+
}
|
|
43
|
+
const configData = await resolveDynamicConfigAsync(configFile);
|
|
44
|
+
if (!skipValidation) {
|
|
45
|
+
const { valid, errors: validationErrors } = validateConfig(configData);
|
|
46
|
+
if (!valid) {
|
|
47
|
+
throw new errors_1.MetadataValidationError(`Metadata store config errors found`, validationErrors);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return configData;
|
|
51
|
+
}
|
|
52
|
+
exports.loadConfigAsync = loadConfigAsync;
|
|
9
53
|
/**
|
|
10
54
|
* Run the JSON Schema validation to normalize defaults and flag early config errors.
|
|
11
55
|
* This includes validating the known store limitations for every configurable property.
|
|
@@ -17,20 +17,21 @@ const telemetry_1 = require("./utils/telemetry");
|
|
|
17
17
|
* Note, only App Store is supported at this time.
|
|
18
18
|
*/
|
|
19
19
|
async function downloadMetadataAsync(metadataCtx) {
|
|
20
|
-
const filePath =
|
|
20
|
+
const filePath = (0, config_1.getStaticConfigFilePath)(metadataCtx);
|
|
21
21
|
const fileExists = await fs_extra_1.default.pathExists(filePath);
|
|
22
22
|
if (fileExists) {
|
|
23
|
+
const filePathRelative = path_1.default.relative(metadataCtx.projectDir, filePath);
|
|
23
24
|
const overwrite = await (0, prompts_1.confirmAsync)({
|
|
24
|
-
message: `Do you want to overwrite the existing
|
|
25
|
+
message: `Do you want to overwrite the existing "${filePathRelative}"?`,
|
|
25
26
|
});
|
|
26
27
|
if (!overwrite) {
|
|
27
|
-
throw new errors_1.MetadataValidationError(`Store
|
|
28
|
+
throw new errors_1.MetadataValidationError(`Store config already exists at "${filePath}"`);
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
const { app, auth } = await (0, context_1.ensureMetadataAppStoreAuthenticatedAsync)(metadataCtx);
|
|
31
32
|
const { unsubscribeTelemetry, executionId } = (0, telemetry_1.subscribeTelemetry)(events_1.MetadataEvent.APPLE_METADATA_DOWNLOAD, { app, auth });
|
|
32
33
|
log_1.default.addNewLineIfNone();
|
|
33
|
-
log_1.default.log('Downloading App Store
|
|
34
|
+
log_1.default.log('Downloading App Store config...');
|
|
34
35
|
const errors = [];
|
|
35
36
|
const config = (0, config_1.createAppleWriter)();
|
|
36
37
|
const tasks = (0, tasks_1.createAppleTasks)(metadataCtx);
|
|
@@ -52,7 +53,7 @@ async function downloadMetadataAsync(metadataCtx) {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
try {
|
|
55
|
-
await fs_extra_1.default.
|
|
56
|
+
await fs_extra_1.default.writeJSON(filePath, config.toSchema(), { spaces: 2 });
|
|
56
57
|
}
|
|
57
58
|
finally {
|
|
58
59
|
unsubscribeTelemetry();
|
package/build/metadata/upload.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadMetadataAsync = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
6
|
-
const path_1 = tslib_1.__importDefault(require("path"));
|
|
7
5
|
const events_1 = require("../analytics/events");
|
|
8
6
|
const log_1 = tslib_1.__importDefault(require("../log"));
|
|
9
7
|
const prompts_1 = require("../prompts");
|
|
@@ -18,30 +16,13 @@ const telemetry_1 = require("./utils/telemetry");
|
|
|
18
16
|
*/
|
|
19
17
|
async function uploadMetadataAsync(metadataCtx) {
|
|
20
18
|
var _a;
|
|
21
|
-
const
|
|
22
|
-
if (!(await fs_extra_1.default.pathExists(filePath))) {
|
|
23
|
-
throw new errors_1.MetadataValidationError(`Store configuration file not found "${filePath}"`);
|
|
24
|
-
}
|
|
25
|
-
const fileData = await fs_extra_1.default.readJson(filePath);
|
|
26
|
-
const { valid, errors: validationErrors } = (0, config_1.validateConfig)(fileData);
|
|
27
|
-
if (!valid) {
|
|
28
|
-
const error = new errors_1.MetadataValidationError(`Store configuration errors found`, validationErrors);
|
|
29
|
-
(0, errors_1.logMetadataValidationError)(error);
|
|
30
|
-
log_1.default.newLine();
|
|
31
|
-
log_1.default.warn('Without further updates, the current store configuration may fail to be synchronized with the App Store or pass App Store review.');
|
|
32
|
-
const attempt = await (0, prompts_1.confirmAsync)({
|
|
33
|
-
message: 'Do you still want to push the store configuration?',
|
|
34
|
-
});
|
|
35
|
-
if (!attempt) {
|
|
36
|
-
throw error;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
19
|
+
const storeConfig = await loadConfigWithValidationPromptAsync(metadataCtx);
|
|
39
20
|
const { app, auth } = await (0, context_1.ensureMetadataAppStoreAuthenticatedAsync)(metadataCtx);
|
|
40
21
|
const { unsubscribeTelemetry, executionId } = (0, telemetry_1.subscribeTelemetry)(events_1.MetadataEvent.APPLE_METADATA_UPLOAD, { app, auth });
|
|
41
22
|
log_1.default.addNewLineIfNone();
|
|
42
23
|
log_1.default.log('Uploading App Store configuration...');
|
|
43
24
|
const errors = [];
|
|
44
|
-
const config = (0, config_1.createAppleReader)(
|
|
25
|
+
const config = (0, config_1.createAppleReader)(storeConfig);
|
|
45
26
|
const tasks = (0, tasks_1.createAppleTasks)(metadataCtx, {
|
|
46
27
|
// We need to resolve a different version as soon as possible.
|
|
47
28
|
// This version is the parent model of all changes we are going to push.
|
|
@@ -71,3 +52,19 @@ async function uploadMetadataAsync(metadataCtx) {
|
|
|
71
52
|
return { appleLink: `https://appstoreconnect.apple.com/apps/${app.id}/appstore` };
|
|
72
53
|
}
|
|
73
54
|
exports.uploadMetadataAsync = uploadMetadataAsync;
|
|
55
|
+
async function loadConfigWithValidationPromptAsync(metadataCtx) {
|
|
56
|
+
try {
|
|
57
|
+
return await (0, config_1.loadConfigAsync)(metadataCtx);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error instanceof errors_1.MetadataValidationError) {
|
|
61
|
+
(0, errors_1.logMetadataValidationError)(error);
|
|
62
|
+
log_1.default.newLine();
|
|
63
|
+
log_1.default.warn('Without further updates, the current store configuration can fail to be synchronized with the App Store or pass App Store review.');
|
|
64
|
+
if (await (0, prompts_1.confirmAsync)({ message: 'Do you still want to push the store configuration?' })) {
|
|
65
|
+
return await (0, config_1.loadConfigAsync)({ ...metadataCtx, skipValidation: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
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
|
|
@@ -13,6 +13,9 @@ export declare function setProjectIdAsync(projectDir: string, options?: {
|
|
|
13
13
|
}): Promise<ExpoConfig | undefined>;
|
|
14
14
|
export declare function getProjectIdAsync(exp: ExpoConfig, options?: {
|
|
15
15
|
env?: Env;
|
|
16
|
+
}, findProjectRootOptions?: {
|
|
17
|
+
cwd?: string;
|
|
18
|
+
defaultToProcessCwd?: boolean;
|
|
16
19
|
}): Promise<string>;
|
|
17
20
|
export declare function getProjectFullNameAsync(exp: ExpoConfig): Promise<string>;
|
|
18
21
|
/**
|
|
@@ -26,4 +29,5 @@ export declare function getProjectConfigDescription(projectDir: string): string;
|
|
|
26
29
|
export declare function promptToCreateProjectIfNotExistsAsync(exp: ExpoConfig): Promise<string | null>;
|
|
27
30
|
export declare function isExpoUpdatesInstalled(projectDir: string): boolean;
|
|
28
31
|
export declare function isExpoUpdatesInstalledOrAvailable(projectDir: string, sdkVersion?: string): boolean;
|
|
32
|
+
export declare function validateAppVersionRuntimePolicySupportAsync(projectDir: string, exp: ExpoConfig): Promise<void>;
|
|
29
33
|
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.setProjectIdAsync = exports.findProjectRootAsync = exports.getProjectAccountNameAsync = exports.getUsername = exports.getProjectAccountName = void 0;
|
|
3
|
+
exports.installExpoUpdatesAsync = exports.validateAppVersionRuntimePolicySupportAsync = exports.isExpoUpdatesInstalledOrAvailable = exports.isExpoUpdatesInstalled = exports.promptToCreateProjectIfNotExistsAsync = exports.getProjectConfigDescription = exports.getProjectFullNameAsync = exports.getProjectIdAsync = exports.setProjectIdAsync = 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"));
|
|
@@ -101,22 +103,14 @@ async function setProjectIdAsync(projectDir, options = {}) {
|
|
|
101
103
|
return (_b = result.config) === null || _b === void 0 ? void 0 : _b.expo;
|
|
102
104
|
}
|
|
103
105
|
exports.setProjectIdAsync = setProjectIdAsync;
|
|
104
|
-
async function getProjectIdAsync(exp, options = {}) {
|
|
106
|
+
async function getProjectIdAsync(exp, options = {}, findProjectRootOptions = {}) {
|
|
105
107
|
var _a, _b, _c, _d;
|
|
106
|
-
if (!process.env.EAS_ENABLE_PROJECT_ID) {
|
|
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
108
|
const localProjectId = (_b = (_a = exp.extra) === null || _a === void 0 ? void 0 : _a.eas) === null || _b === void 0 ? void 0 : _b.projectId;
|
|
115
109
|
if (localProjectId) {
|
|
116
110
|
return localProjectId;
|
|
117
111
|
}
|
|
118
112
|
// Set the project ID if it is missing.
|
|
119
|
-
const projectDir = await findProjectRootAsync();
|
|
113
|
+
const projectDir = await findProjectRootAsync(findProjectRootOptions);
|
|
120
114
|
if (!projectDir) {
|
|
121
115
|
throw new Error('Please run this command inside a project directory.');
|
|
122
116
|
}
|
|
@@ -202,6 +196,21 @@ function isExpoUpdatesInstalledOrAvailable(projectDir, sdkVersion) {
|
|
|
202
196
|
return isExpoUpdatesInstalled(projectDir);
|
|
203
197
|
}
|
|
204
198
|
exports.isExpoUpdatesInstalledOrAvailable = isExpoUpdatesInstalledOrAvailable;
|
|
199
|
+
async function validateAppVersionRuntimePolicySupportAsync(projectDir, exp) {
|
|
200
|
+
var _a;
|
|
201
|
+
if (typeof exp.runtimeVersion !== 'object' || ((_a = exp.runtimeVersion) === null || _a === void 0 ? void 0 : _a.policy) !== 'appVersion') {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const maybePackageJson = resolve_from_1.default.silent(projectDir, 'expo-updates/package.json');
|
|
205
|
+
if (maybePackageJson) {
|
|
206
|
+
const { version } = await fs_extra_1.default.readJson(maybePackageJson);
|
|
207
|
+
if (semver_1.default.gte(version, '0.14.4')) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
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.`);
|
|
212
|
+
}
|
|
213
|
+
exports.validateAppVersionRuntimePolicySupportAsync = validateAppVersionRuntimePolicySupportAsync;
|
|
205
214
|
async function installExpoUpdatesAsync(projectDir) {
|
|
206
215
|
log_1.default.newLine();
|
|
207
216
|
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
|
]);
|
|
@@ -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/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',
|