eas-cli 0.51.0 → 0.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -44
- package/build/analytics/events.d.ts +5 -1
- package/build/analytics/events.js +6 -1
- package/build/build/android/version.js +1 -1
- package/build/build/build.js +38 -27
- package/build/build/ios/build.js +3 -1
- package/build/build/ios/credentials.js +2 -6
- package/build/build/local.js +1 -1
- package/build/commands/branch/create.js +2 -2
- package/build/commands/branch/delete.js +2 -2
- package/build/commands/branch/list.js +2 -2
- package/build/commands/branch/rename.js +2 -2
- package/build/commands/branch/view.js +2 -2
- package/build/commands/build/cancel.js +3 -3
- package/build/commands/build/configure.js +2 -2
- package/build/commands/build/inspect.js +1 -1
- package/build/commands/build/list.js +2 -2
- package/build/commands/build/view.js +2 -2
- package/build/commands/channel/create.js +2 -2
- package/build/commands/channel/delete.js +2 -2
- package/build/commands/channel/edit.js +2 -2
- package/build/commands/channel/list.js +2 -2
- package/build/commands/channel/rollout.js +2 -2
- package/build/commands/channel/view.js +3 -3
- package/build/commands/device/delete.js +2 -2
- package/build/commands/device/list.js +2 -2
- package/build/commands/device/view.js +2 -2
- package/build/commands/project/info.js +2 -2
- package/build/commands/project/init.js +2 -2
- package/build/commands/secret/create.js +2 -2
- package/build/commands/secret/delete.js +2 -2
- package/build/commands/secret/list.js +2 -2
- package/build/commands/submit.js +2 -2
- package/build/commands/update/configure.js +2 -3
- package/build/commands/update/index.js +3 -5
- package/build/commands/update/list.js +2 -2
- package/build/commands/webhook/create.js +2 -2
- package/build/commands/webhook/delete.js +2 -2
- package/build/commands/webhook/list.js +2 -2
- package/build/credentials/android/utils/googleServiceAccountKey.js +1 -1
- package/build/credentials/android/utils/keystore.js +1 -1
- package/build/credentials/android/utils/keystoreNew.js +2 -2
- package/build/credentials/context.js +2 -3
- package/build/credentials/credentialsJson/read.js +1 -1
- package/build/credentials/ios/IosCredentialsProvider.d.ts +0 -2
- package/build/credentials/ios/IosCredentialsProvider.js +0 -1
- package/build/credentials/ios/actions/SetUpBuildCredentials.d.ts +0 -2
- package/build/credentials/ios/actions/SetUpBuildCredentials.js +3 -1
- package/build/credentials/ios/actions/SetUpTargetBuildCredentials.d.ts +2 -2
- package/build/credentials/ios/actions/SetUpTargetBuildCredentials.js +2 -2
- package/build/credentials/ios/types.d.ts +2 -0
- package/build/credentials/ios/utils/printCredentials.d.ts +1 -1
- package/build/credentials/ios/utils/provisioningProfile.js +1 -1
- package/build/credentials/ios/validators/validateProvisioningProfile.js +1 -1
- package/build/credentials/manager/ManageIos.js +8 -7
- package/build/graphql/generated.d.ts +66 -21
- package/build/graphql/generated.js +5 -0
- package/build/graphql/queries/UpdateQuery.js +1 -1
- package/build/metadata/apple/config/reader.d.ts +25 -0
- package/build/metadata/apple/config/reader.js +94 -0
- package/build/metadata/apple/config/writer.d.ts +23 -0
- package/build/metadata/apple/config/writer.js +85 -0
- package/build/metadata/apple/data.d.ts +20 -0
- package/build/metadata/apple/data.js +2 -0
- package/build/metadata/apple/task.d.ts +24 -0
- package/build/metadata/apple/task.js +6 -0
- package/build/metadata/apple/tasks/age-rating.d.ts +13 -0
- package/build/metadata/apple/tasks/age-rating.js +39 -0
- package/build/metadata/apple/tasks/app-info.d.ts +15 -0
- package/build/metadata/apple/tasks/app-info.js +71 -0
- package/build/metadata/apple/tasks/app-version.d.ts +27 -0
- package/build/metadata/apple/tasks/app-version.js +100 -0
- package/build/metadata/apple/tasks/index.d.ts +6 -0
- package/build/metadata/apple/tasks/index.js +13 -0
- package/build/metadata/apple/types.d.ts +50 -0
- package/build/metadata/apple/types.js +2 -0
- package/build/metadata/config.d.ts +22 -0
- package/build/metadata/config.js +32 -0
- package/build/metadata/context.d.ts +46 -0
- package/build/metadata/context.js +55 -0
- package/build/metadata/download.d.ts +6 -0
- package/build/metadata/download.js +58 -0
- package/build/metadata/errors.d.ts +37 -0
- package/build/metadata/errors.js +67 -0
- package/build/metadata/upload.d.ts +6 -0
- package/build/metadata/upload.js +54 -0
- package/build/metadata/utils/array.d.ts +1 -0
- package/build/metadata/utils/array.js +8 -0
- package/build/metadata/utils/asc.d.ts +4 -0
- package/build/metadata/utils/asc.js +2 -0
- package/build/metadata/utils/date.d.ts +12 -0
- package/build/metadata/utils/date.js +30 -0
- package/build/metadata/utils/log.d.ts +16 -0
- package/build/metadata/utils/log.js +23 -0
- package/build/metadata/utils/retry.d.ts +8 -0
- package/build/metadata/utils/retry.js +22 -0
- package/build/metadata/utils/telemetry.d.ts +20 -0
- package/build/metadata/utils/telemetry.js +87 -0
- package/build/project/android/applicationId.js +1 -1
- package/build/project/ios/bundleIdentifier.js +1 -1
- package/build/project/ios/entitlements.d.ts +8 -0
- package/build/{credentials/ios/appstore → project/ios}/entitlements.js +7 -19
- package/build/project/ios/target.d.ts +9 -3
- package/build/project/ios/target.js +68 -47
- package/build/submit/ArchiveSource.js +1 -1
- package/build/submit/submit.js +1 -1
- package/build/submit/utils/errors.js +2 -0
- package/build/submit/utils/files.js +1 -1
- package/build/submit/utils/logs.js +1 -1
- package/build/update/android/UpdatesModule.js +2 -2
- package/build/update/ios/UpdatesModule.js +2 -2
- package/build/user/actions.js +1 -1
- package/build/vcs/clients/git.js +5 -5
- package/build/vcs/git.js +1 -1
- package/build/webhooks/input.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +39 -38
- package/build/credentials/ios/appstore/entitlements.d.ts +0 -4
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAppleWriter = exports.createAppleReader = exports.validateConfig = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const ajv_1 = tslib_1.__importDefault(require("ajv"));
|
|
6
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
7
|
+
const reader_1 = require("./apple/config/reader");
|
|
8
|
+
const writer_1 = require("./apple/config/writer");
|
|
9
|
+
/**
|
|
10
|
+
* Run the JSON Schema validation to normalize defaults and flag early config errors.
|
|
11
|
+
* This includes validating the known store limitations for every configurable property.
|
|
12
|
+
*/
|
|
13
|
+
function validateConfig(config) {
|
|
14
|
+
const validator = new ajv_1.default({ allErrors: true, useDefaults: true })
|
|
15
|
+
.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'))
|
|
16
|
+
.compile(require('../../schema/metadata-0.json'));
|
|
17
|
+
const valid = validator(config);
|
|
18
|
+
return { valid, errors: validator.errors || [] };
|
|
19
|
+
}
|
|
20
|
+
exports.validateConfig = validateConfig;
|
|
21
|
+
/** Create a versioned deserializer to fetch App Store data from the store configuration. */
|
|
22
|
+
function createAppleReader(config) {
|
|
23
|
+
(0, assert_1.default)(config.configVersion === 0, 'Unsupported store configuration version');
|
|
24
|
+
(0, assert_1.default)(config.apple, 'No apple configuration found');
|
|
25
|
+
return new reader_1.AppleConfigReader(config.apple);
|
|
26
|
+
}
|
|
27
|
+
exports.createAppleReader = createAppleReader;
|
|
28
|
+
/** Create the serializer to write the App Store to the store configuration. */
|
|
29
|
+
function createAppleWriter() {
|
|
30
|
+
return new writer_1.AppleConfigWriter();
|
|
31
|
+
}
|
|
32
|
+
exports.createAppleWriter = createAppleWriter;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/// <reference types="@expo/apple-utils/ts-declarations/expo__app-store" />
|
|
2
|
+
import { App, Session } from '@expo/apple-utils';
|
|
3
|
+
import { ExpoConfig } from '@expo/config';
|
|
4
|
+
import { Platform } from '@expo/eas-build-job';
|
|
5
|
+
import { SubmitProfile } from '@expo/eas-json';
|
|
6
|
+
import { CredentialsContext } from '../credentials/context';
|
|
7
|
+
import { Actor } from '../user/User';
|
|
8
|
+
export declare type MetadataContext = {
|
|
9
|
+
/** Submission profile platform to use */
|
|
10
|
+
platform: Platform.IOS;
|
|
11
|
+
/** Resolved submission profile configuration */
|
|
12
|
+
profile: SubmitProfile<Platform.IOS>;
|
|
13
|
+
/** Configured store config path, relative from projectDir (defaults to store.config.json) */
|
|
14
|
+
metadataPath: string;
|
|
15
|
+
/** Authenticated Expo account */
|
|
16
|
+
user: Actor;
|
|
17
|
+
/** The store credentials manager */
|
|
18
|
+
credentialsCtx: CredentialsContext;
|
|
19
|
+
/** The app bundle identifier to use for the store */
|
|
20
|
+
bundleIdentifier: string;
|
|
21
|
+
/** Root of the Expo project directory */
|
|
22
|
+
projectDir: string;
|
|
23
|
+
/** Resolved Expo app configuration */
|
|
24
|
+
exp: ExpoConfig;
|
|
25
|
+
};
|
|
26
|
+
export declare type MetadataAppStoreAuthentication = {
|
|
27
|
+
/** The root entity of the App store */
|
|
28
|
+
app: App;
|
|
29
|
+
/** The authentication state we used to fetch the root entity */
|
|
30
|
+
auth: Partial<Session.AuthState>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Metadata uses the submission profile to find the configured metadata filename.
|
|
34
|
+
* Note, only iOS is supported for metadata right now.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMetadataContextAsync(params: {
|
|
37
|
+
projectDir: string;
|
|
38
|
+
credentialsCtx: CredentialsContext;
|
|
39
|
+
exp?: ExpoConfig;
|
|
40
|
+
profileName?: string;
|
|
41
|
+
}): Promise<MetadataContext>;
|
|
42
|
+
/**
|
|
43
|
+
* To start syncing ASC entities, we need access to the apple utils App instance.
|
|
44
|
+
* This ensures we are authenticated and have the app instance ready to use.
|
|
45
|
+
*/
|
|
46
|
+
export declare function ensureMetadataAppStoreAuthenticatedAsync({ credentialsCtx, bundleIdentifier, }: MetadataContext): Promise<MetadataAppStoreAuthentication>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureMetadataAppStoreAuthenticatedAsync = exports.createMetadataContextAsync = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const apple_utils_1 = require("@expo/apple-utils");
|
|
6
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
7
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
8
|
+
const authenticate_1 = require("../credentials/ios/appstore/authenticate");
|
|
9
|
+
const expoConfig_1 = require("../project/expoConfig");
|
|
10
|
+
const bundleIdentifier_1 = require("../project/ios/bundleIdentifier");
|
|
11
|
+
const actions_1 = require("../user/actions");
|
|
12
|
+
const profiles_1 = require("../utils/profiles");
|
|
13
|
+
/**
|
|
14
|
+
* Metadata uses the submission profile to find the configured metadata filename.
|
|
15
|
+
* Note, only iOS is supported for metadata right now.
|
|
16
|
+
*/
|
|
17
|
+
async function createMetadataContextAsync(params) {
|
|
18
|
+
var _a, _b;
|
|
19
|
+
const submissionProfiles = await (0, profiles_1.getProfilesAsync)({
|
|
20
|
+
type: 'submit',
|
|
21
|
+
platforms: [eas_build_job_1.Platform.IOS],
|
|
22
|
+
projectDir: params.projectDir,
|
|
23
|
+
profileName: params.profileName,
|
|
24
|
+
});
|
|
25
|
+
const submissionProfile = submissionProfiles.find(profile => profile.platform === eas_build_job_1.Platform.IOS);
|
|
26
|
+
(0, assert_1.default)(submissionProfile, 'Could not resolve the iOS submission profile, only iOS is supported for metadata');
|
|
27
|
+
const iosSubmissionProfile = submissionProfile.profile;
|
|
28
|
+
const exp = (_a = params.exp) !== null && _a !== void 0 ? _a : (0, expoConfig_1.getExpoConfig)(params.projectDir);
|
|
29
|
+
const user = await (0, actions_1.ensureLoggedInAsync)();
|
|
30
|
+
const bundleIdentifier = await (0, bundleIdentifier_1.getBundleIdentifierAsync)(params.projectDir, exp);
|
|
31
|
+
return {
|
|
32
|
+
platform: eas_build_job_1.Platform.IOS,
|
|
33
|
+
profile: iosSubmissionProfile,
|
|
34
|
+
metadataPath: (_b = iosSubmissionProfile.metadataPath) !== null && _b !== void 0 ? _b : 'store.config.json',
|
|
35
|
+
user,
|
|
36
|
+
credentialsCtx: params.credentialsCtx,
|
|
37
|
+
bundleIdentifier,
|
|
38
|
+
projectDir: params.projectDir,
|
|
39
|
+
exp,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
exports.createMetadataContextAsync = createMetadataContextAsync;
|
|
43
|
+
/**
|
|
44
|
+
* To start syncing ASC entities, we need access to the apple utils App instance.
|
|
45
|
+
* This ensures we are authenticated and have the app instance ready to use.
|
|
46
|
+
*/
|
|
47
|
+
async function ensureMetadataAppStoreAuthenticatedAsync({ credentialsCtx, bundleIdentifier, }) {
|
|
48
|
+
const authCtx = await credentialsCtx.appStore.ensureAuthenticatedAsync();
|
|
49
|
+
(0, assert_1.default)(authCtx.authState, 'Failed to authenticate with App Store Connect');
|
|
50
|
+
// TODO: improve error handling by mentioning possible configuration errors
|
|
51
|
+
const app = await apple_utils_1.App.findAsync((0, authenticate_1.getRequestContext)(authCtx), { bundleId: bundleIdentifier });
|
|
52
|
+
(0, assert_1.default)(app, `Failed to load app "${bundleIdentifier}" from App Store Connect`);
|
|
53
|
+
return { app, auth: authCtx.authState };
|
|
54
|
+
}
|
|
55
|
+
exports.ensureMetadataAppStoreAuthenticatedAsync = ensureMetadataAppStoreAuthenticatedAsync;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.downloadMetadataAsync = void 0;
|
|
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
|
+
const events_1 = require("../analytics/events");
|
|
8
|
+
const prompts_1 = require("../prompts");
|
|
9
|
+
const tasks_1 = require("./apple/tasks");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
const context_1 = require("./context");
|
|
12
|
+
const errors_1 = require("./errors");
|
|
13
|
+
const telemetry_1 = require("./utils/telemetry");
|
|
14
|
+
/**
|
|
15
|
+
* Generate a local store configuration from the stores.
|
|
16
|
+
* Note, only App Store is supported at this time.
|
|
17
|
+
*/
|
|
18
|
+
async function downloadMetadataAsync(metadataCtx) {
|
|
19
|
+
const filePath = path_1.default.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
|
|
20
|
+
const fileExists = await fs_extra_1.default.pathExists(filePath);
|
|
21
|
+
if (fileExists) {
|
|
22
|
+
const overwrite = await (0, prompts_1.confirmAsync)({
|
|
23
|
+
message: `Do you want to overwrite the existing store configuration "${metadataCtx.metadataPath}"?`,
|
|
24
|
+
});
|
|
25
|
+
if (!overwrite) {
|
|
26
|
+
throw new errors_1.MetadataValidationError(`Store configuration already exists at "${filePath}"`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const { app, auth } = await (0, context_1.ensureMetadataAppStoreAuthenticatedAsync)(metadataCtx);
|
|
30
|
+
const { unsubscribeTelemetry, executionId } = (0, telemetry_1.subscribeTelemetry)(events_1.MetadataEvent.APPLE_METADATA_DOWNLOAD, { app, auth });
|
|
31
|
+
const errors = [];
|
|
32
|
+
const config = (0, config_1.createAppleWriter)();
|
|
33
|
+
const tasks = (0, tasks_1.createAppleTasks)(metadataCtx);
|
|
34
|
+
const taskCtx = { app };
|
|
35
|
+
for (const task of tasks) {
|
|
36
|
+
try {
|
|
37
|
+
await task.prepareAsync({ context: taskCtx });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
errors.push(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const task of tasks) {
|
|
44
|
+
try {
|
|
45
|
+
await task.downloadAsync({ config, context: taskCtx });
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
errors.push(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
await fs_extra_1.default.writeJson(filePath, config.toSchema(), { spaces: 2 });
|
|
52
|
+
unsubscribeTelemetry();
|
|
53
|
+
if (errors.length > 0) {
|
|
54
|
+
throw new errors_1.MetadataDownloadError(errors, executionId);
|
|
55
|
+
}
|
|
56
|
+
return filePath;
|
|
57
|
+
}
|
|
58
|
+
exports.downloadMetadataAsync = downloadMetadataAsync;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ErrorObject } from 'ajv';
|
|
2
|
+
/**
|
|
3
|
+
* Before syncing data to the ASC API, we need to validate the metadata config.
|
|
4
|
+
* This error represents unrecoverable issues before syncing that data,
|
|
5
|
+
* and should contain useful information for the user to solve before trying again.
|
|
6
|
+
*/
|
|
7
|
+
export declare class MetadataValidationError extends Error {
|
|
8
|
+
readonly errors?: ErrorObject[] | undefined;
|
|
9
|
+
constructor(message?: string, errors?: ErrorObject[] | undefined);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* If a single entity failed to update, we don't block the other entities from uploading.
|
|
13
|
+
* We still attempt to update the data in the stores as much as possible.
|
|
14
|
+
* Because of that, we keep track of any errors encountered and throw this generic error.
|
|
15
|
+
* It contains that list of encountered errors to present to the user.
|
|
16
|
+
*/
|
|
17
|
+
export declare class MetadataUploadError extends Error {
|
|
18
|
+
readonly errors: Error[];
|
|
19
|
+
readonly executionId: string;
|
|
20
|
+
constructor(errors: Error[], executionId: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* If a single entity failed to download, we don't block the other entities from downloading.
|
|
24
|
+
* We sill attempt to pull in the data from the stores as much as possible.
|
|
25
|
+
* Because of that, we keep track of any errors envountered and throw this generic error.
|
|
26
|
+
* It contains that list of encountered errors to present to the user.
|
|
27
|
+
*/
|
|
28
|
+
export declare class MetadataDownloadError extends Error {
|
|
29
|
+
readonly errors: Error[];
|
|
30
|
+
readonly executionId: string;
|
|
31
|
+
constructor(errors: Error[], executionId: string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Handle a thrown metadata error by informing the user what went wrong.
|
|
35
|
+
* If a normal error is thrown, this method will re-throw that error to avoid consuming it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function handleMetadataError(error: Error): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleMetadataError = exports.MetadataDownloadError = exports.MetadataUploadError = exports.MetadataValidationError = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const log_1 = tslib_1.__importStar(require("../log"));
|
|
6
|
+
/**
|
|
7
|
+
* Before syncing data to the ASC API, we need to validate the metadata config.
|
|
8
|
+
* This error represents unrecoverable issues before syncing that data,
|
|
9
|
+
* and should contain useful information for the user to solve before trying again.
|
|
10
|
+
*/
|
|
11
|
+
class MetadataValidationError extends Error {
|
|
12
|
+
constructor(message, errors) {
|
|
13
|
+
super(message !== null && message !== void 0 ? message : 'Store configuration validation failed');
|
|
14
|
+
this.errors = errors;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.MetadataValidationError = MetadataValidationError;
|
|
18
|
+
/**
|
|
19
|
+
* If a single entity failed to update, we don't block the other entities from uploading.
|
|
20
|
+
* We still attempt to update the data in the stores as much as possible.
|
|
21
|
+
* Because of that, we keep track of any errors encountered and throw this generic error.
|
|
22
|
+
* It contains that list of encountered errors to present to the user.
|
|
23
|
+
*/
|
|
24
|
+
class MetadataUploadError extends Error {
|
|
25
|
+
constructor(errors, executionId) {
|
|
26
|
+
super(`Store configuration upload encountered ${errors.length === 1 ? 'an error' : `${errors.length} errors`}.`);
|
|
27
|
+
this.errors = errors;
|
|
28
|
+
this.executionId = executionId;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.MetadataUploadError = MetadataUploadError;
|
|
32
|
+
/**
|
|
33
|
+
* If a single entity failed to download, we don't block the other entities from downloading.
|
|
34
|
+
* We sill attempt to pull in the data from the stores as much as possible.
|
|
35
|
+
* Because of that, we keep track of any errors envountered and throw this generic error.
|
|
36
|
+
* It contains that list of encountered errors to present to the user.
|
|
37
|
+
*/
|
|
38
|
+
class MetadataDownloadError extends Error {
|
|
39
|
+
constructor(errors, executionId) {
|
|
40
|
+
super(`Store configuration download encountered ${errors.length === 1 ? 'an error' : `${errors.length} errors`}.`);
|
|
41
|
+
this.errors = errors;
|
|
42
|
+
this.executionId = executionId;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.MetadataDownloadError = MetadataDownloadError;
|
|
46
|
+
/**
|
|
47
|
+
* Handle a thrown metadata error by informing the user what went wrong.
|
|
48
|
+
* If a normal error is thrown, this method will re-throw that error to avoid consuming it.
|
|
49
|
+
*/
|
|
50
|
+
function handleMetadataError(error) {
|
|
51
|
+
var _a;
|
|
52
|
+
if (error instanceof MetadataValidationError) {
|
|
53
|
+
log_1.default.error(error.message);
|
|
54
|
+
log_1.default.log((_a = error.errors) === null || _a === void 0 ? void 0 : _a.map(err => ` - ${err.dataPath} ${err.message}`).join('\n'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (error instanceof MetadataDownloadError || error instanceof MetadataUploadError) {
|
|
58
|
+
log_1.default.error(error.message);
|
|
59
|
+
log_1.default.log('Please check the logs for any configuration issues.');
|
|
60
|
+
log_1.default.log('If this issue persists, please open a new issue at:');
|
|
61
|
+
// TODO: add execution ID to the issue template link
|
|
62
|
+
log_1.default.log((0, log_1.link)('https://github.com/expo/eas-cli'));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
exports.handleMetadataError = handleMetadataError;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.uploadMetadataAsync = void 0;
|
|
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
|
+
const events_1 = require("../analytics/events");
|
|
8
|
+
const tasks_1 = require("./apple/tasks");
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
const context_1 = require("./context");
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
const telemetry_1 = require("./utils/telemetry");
|
|
13
|
+
/**
|
|
14
|
+
* Sync a local store configuration with the stores.
|
|
15
|
+
* Note, only App Store is supported at this time.
|
|
16
|
+
*/
|
|
17
|
+
async function uploadMetadataAsync(metadataCtx) {
|
|
18
|
+
const filePath = path_1.default.resolve(metadataCtx.projectDir, metadataCtx.metadataPath);
|
|
19
|
+
if (!(await fs_extra_1.default.pathExists(filePath))) {
|
|
20
|
+
throw new errors_1.MetadataValidationError(`Store configuration file not found "${filePath}"`);
|
|
21
|
+
}
|
|
22
|
+
const { app, auth } = await (0, context_1.ensureMetadataAppStoreAuthenticatedAsync)(metadataCtx);
|
|
23
|
+
const { unsubscribeTelemetry, executionId } = (0, telemetry_1.subscribeTelemetry)(events_1.MetadataEvent.APPLE_METADATA_UPLOAD, { app, auth });
|
|
24
|
+
const fileData = await fs_extra_1.default.readJson(filePath);
|
|
25
|
+
const { valid, errors: validationErrors } = (0, config_1.validateConfig)(fileData);
|
|
26
|
+
if (!valid) {
|
|
27
|
+
throw new errors_1.MetadataValidationError(`Store configuration errors found`, validationErrors);
|
|
28
|
+
}
|
|
29
|
+
const errors = [];
|
|
30
|
+
const config = (0, config_1.createAppleReader)(fileData);
|
|
31
|
+
const tasks = (0, tasks_1.createAppleTasks)(metadataCtx);
|
|
32
|
+
const taskCtx = { app };
|
|
33
|
+
for (const task of tasks) {
|
|
34
|
+
try {
|
|
35
|
+
await task.prepareAsync({ context: taskCtx });
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
errors.push(error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const task of tasks) {
|
|
42
|
+
try {
|
|
43
|
+
await task.uploadAsync({ config, context: taskCtx });
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
errors.push(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
unsubscribeTelemetry();
|
|
50
|
+
if (errors.length > 0) {
|
|
51
|
+
throw new errors_1.MetadataUploadError(errors, executionId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.uploadMetadataAsync = uploadMetadataAsync;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function unique<T = any>(items: T[]): T[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove time precision from a date to avoid potential errors with the App Store.
|
|
3
|
+
*
|
|
4
|
+
* "status": "409",
|
|
5
|
+
* "code": "ENTITY_ERROR.ATTRIBUTE.INVALID",
|
|
6
|
+
* "title": "An attribute value is invalid.",
|
|
7
|
+
* "detail": "The attribute 'earliestReleaseDate' only allows hour precision",
|
|
8
|
+
* "source": {
|
|
9
|
+
* "pointer": "/data/attributes/earliestReleaseDate"
|
|
10
|
+
* }
|
|
11
|
+
*/
|
|
12
|
+
export declare function removeDatePrecision(date: any): null | Date;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeDatePrecision = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Remove time precision from a date to avoid potential errors with the App Store.
|
|
6
|
+
*
|
|
7
|
+
* "status": "409",
|
|
8
|
+
* "code": "ENTITY_ERROR.ATTRIBUTE.INVALID",
|
|
9
|
+
* "title": "An attribute value is invalid.",
|
|
10
|
+
* "detail": "The attribute 'earliestReleaseDate' only allows hour precision",
|
|
11
|
+
* "source": {
|
|
12
|
+
* "pointer": "/data/attributes/earliestReleaseDate"
|
|
13
|
+
* }
|
|
14
|
+
*/
|
|
15
|
+
function removeDatePrecision(date) {
|
|
16
|
+
if (date) {
|
|
17
|
+
try {
|
|
18
|
+
const result = new Date(date);
|
|
19
|
+
result.setMinutes(0);
|
|
20
|
+
result.setSeconds(0);
|
|
21
|
+
result.setMilliseconds(0);
|
|
22
|
+
if (!isNaN(result.getTime())) {
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
exports.removeDatePrecision = removeDatePrecision;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Ora } from '../../ora';
|
|
2
|
+
declare type LogAsyncOptions = {
|
|
3
|
+
/** If the spinner representing the async action should be hidden, e.g. for JSON output */
|
|
4
|
+
hidden?: boolean;
|
|
5
|
+
/** The message to display when the action is pending */
|
|
6
|
+
pending: string;
|
|
7
|
+
/** The message to display when the action succeeded */
|
|
8
|
+
success: string;
|
|
9
|
+
/** The message to display when the action failed */
|
|
10
|
+
failure: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Log an asynchronous action using a spinner.
|
|
14
|
+
*/
|
|
15
|
+
export declare function logAsync<T>(action: (spinner?: Ora) => Promise<T>, { hidden, ...message }: LogAsyncOptions): Promise<T>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logAsync = void 0;
|
|
4
|
+
const ora_1 = require("../../ora");
|
|
5
|
+
/**
|
|
6
|
+
* Log an asynchronous action using a spinner.
|
|
7
|
+
*/
|
|
8
|
+
async function logAsync(action, { hidden, ...message }) {
|
|
9
|
+
if (hidden) {
|
|
10
|
+
return action();
|
|
11
|
+
}
|
|
12
|
+
const spinner = (0, ora_1.ora)(message.pending).start();
|
|
13
|
+
try {
|
|
14
|
+
const result = await action(spinner);
|
|
15
|
+
spinner.succeed(message.success);
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
spinner.fail(message.failure);
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.logAsync = logAsync;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function waitAsync(duration: number): Promise<void>;
|
|
2
|
+
declare type WithRetryOptions = {
|
|
3
|
+
tries?: number;
|
|
4
|
+
delay?: number;
|
|
5
|
+
onRetry?: (triesLeft: number) => void;
|
|
6
|
+
};
|
|
7
|
+
export declare function retryIfNullAsync<T>(method: () => Promise<T | null>, options?: WithRetryOptions): Promise<T | null>;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.retryIfNullAsync = exports.waitAsync = void 0;
|
|
4
|
+
async function waitAsync(duration) {
|
|
5
|
+
return new Promise(resolve => setTimeout(resolve, duration));
|
|
6
|
+
}
|
|
7
|
+
exports.waitAsync = waitAsync;
|
|
8
|
+
async function retryIfNullAsync(method, options = {}) {
|
|
9
|
+
var _a;
|
|
10
|
+
let { tries = 5, delay = 1000 } = options;
|
|
11
|
+
while (tries > 0) {
|
|
12
|
+
const value = await method();
|
|
13
|
+
if (value !== null) {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
tries--;
|
|
17
|
+
(_a = options.onRetry) === null || _a === void 0 ? void 0 : _a.call(options, tries);
|
|
18
|
+
await waitAsync(delay);
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
exports.retryIfNullAsync = retryIfNullAsync;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="@expo/apple-utils/ts-declarations/expo__app-store" />
|
|
2
|
+
import { App, Session } from '@expo/apple-utils';
|
|
3
|
+
import { MetadataEvent } from '../../analytics/events';
|
|
4
|
+
export declare type TelemetryContext = {
|
|
5
|
+
app: App;
|
|
6
|
+
auth: Partial<Session.AuthState>;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Subscribe the telemetry to the ongoing metadata requests and responses.
|
|
10
|
+
* When providing the app and auth info, we can scrub that data from the telemetry.
|
|
11
|
+
* Returns an execution ID to group all events of a single run together, and a unsubscribe function.
|
|
12
|
+
*/
|
|
13
|
+
export declare function subscribeTelemetry(event: MetadataEvent, options: TelemetryContext): {
|
|
14
|
+
/** Unsubscribe the telemetry from all apple-utils events */
|
|
15
|
+
unsubscribeTelemetry: () => void;
|
|
16
|
+
/** The unique id added to all telemetry events from a single execution */
|
|
17
|
+
executionId: string;
|
|
18
|
+
};
|
|
19
|
+
/** Exposed for testing */
|
|
20
|
+
export declare function makeDataScrubber({ app, auth }: TelemetryContext): <T>(data: T) => string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeDataScrubber = exports.subscribeTelemetry = void 0;
|
|
4
|
+
const apple_utils_1 = require("@expo/apple-utils");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
const events_1 = require("../../analytics/events");
|
|
7
|
+
/**
|
|
8
|
+
* Subscribe the telemetry to the ongoing metadata requests and responses.
|
|
9
|
+
* When providing the app and auth info, we can scrub that data from the telemetry.
|
|
10
|
+
* Returns an execution ID to group all events of a single run together, and a unsubscribe function.
|
|
11
|
+
*/
|
|
12
|
+
function subscribeTelemetry(event, options) {
|
|
13
|
+
const executionId = (0, uuid_1.v4)();
|
|
14
|
+
const scrubber = makeDataScrubber(options);
|
|
15
|
+
const { interceptors } = (0, apple_utils_1.getRequestClient)();
|
|
16
|
+
const responseInterceptorId = interceptors.response.use(response => {
|
|
17
|
+
events_1.Analytics.logEvent(event, {
|
|
18
|
+
executionId,
|
|
19
|
+
type: 'response',
|
|
20
|
+
phase: 'resolved',
|
|
21
|
+
method: response.request.method.toUpperCase(),
|
|
22
|
+
url: scrubber(response.request.path),
|
|
23
|
+
status: String(response.status),
|
|
24
|
+
statusText: scrubber(response.statusText),
|
|
25
|
+
});
|
|
26
|
+
return response;
|
|
27
|
+
}, (error) => {
|
|
28
|
+
var _a, _b, _c;
|
|
29
|
+
events_1.Analytics.logEvent(event, {
|
|
30
|
+
executionId,
|
|
31
|
+
type: 'response',
|
|
32
|
+
phase: 'rejected',
|
|
33
|
+
method: error.request.method.toUpperCase(),
|
|
34
|
+
url: scrubber(error.config.url),
|
|
35
|
+
error: scrubber(error.message),
|
|
36
|
+
status: String((_a = error.response) === null || _a === void 0 ? void 0 : _a.status),
|
|
37
|
+
statusText: scrubber((_b = error.response) === null || _b === void 0 ? void 0 : _b.statusText),
|
|
38
|
+
input: scrubber(error.config.data),
|
|
39
|
+
output: scrubber((_c = error.response) === null || _c === void 0 ? void 0 : _c.data),
|
|
40
|
+
});
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
43
|
+
function unsubscribeTelemetry() {
|
|
44
|
+
interceptors.response.eject(responseInterceptorId);
|
|
45
|
+
}
|
|
46
|
+
return { unsubscribeTelemetry, executionId };
|
|
47
|
+
}
|
|
48
|
+
exports.subscribeTelemetry = subscribeTelemetry;
|
|
49
|
+
/** Exposed for testing */
|
|
50
|
+
function makeDataScrubber({ app, auth }) {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
const token = getAuthTokenString(auth);
|
|
53
|
+
const patterns = {
|
|
54
|
+
APPLE_APP_ID: new RegExp(app.id, 'gi'),
|
|
55
|
+
APPLE_USERNAME: auth.username ? new RegExp(auth.username, 'gi') : null,
|
|
56
|
+
APPLE_PASSWORD: auth.password ? new RegExp(auth.password, 'gi') : null,
|
|
57
|
+
APPLE_TOKEN: token ? new RegExp(token, 'gi') : null,
|
|
58
|
+
APPLE_TEAM_ID: ((_a = auth.context) === null || _a === void 0 ? void 0 : _a.teamId) ? new RegExp(auth.context.teamId, 'gi') : null,
|
|
59
|
+
APPLE_PROVIDER_ID: ((_b = auth.context) === null || _b === void 0 ? void 0 : _b.providerId)
|
|
60
|
+
? new RegExp(String(auth.context.providerId), 'gi')
|
|
61
|
+
: null,
|
|
62
|
+
};
|
|
63
|
+
const iterator = Object.entries(patterns);
|
|
64
|
+
return function scrubber(data) {
|
|
65
|
+
if (!data) {
|
|
66
|
+
return String(data);
|
|
67
|
+
}
|
|
68
|
+
let value = typeof data === 'object' ? JSON.stringify(data) : String(data);
|
|
69
|
+
for (const [replacement, pattern] of iterator) {
|
|
70
|
+
if (pattern) {
|
|
71
|
+
value = value.replace(pattern, `{${replacement}}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
exports.makeDataScrubber = makeDataScrubber;
|
|
78
|
+
function getAuthTokenString(auth) {
|
|
79
|
+
var _a;
|
|
80
|
+
if (!((_a = auth.context) === null || _a === void 0 ? void 0 : _a.token)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (typeof auth.context.token === 'object') {
|
|
84
|
+
return auth.context.token.getToken();
|
|
85
|
+
}
|
|
86
|
+
return auth.context.token;
|
|
87
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { JSONObject } from '@expo/json-file';
|
|
2
|
+
interface Target {
|
|
3
|
+
buildConfiguration?: string;
|
|
4
|
+
targetName: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getManagedApplicationTargetEntitlementsAsync(projectDir: string, env: Record<string, string>): Promise<JSONObject>;
|
|
7
|
+
export declare function getNativeTargetEntitlementsAsync(projectDir: string, target: Target): Promise<JSONObject | null>;
|
|
8
|
+
export {};
|