eas-cli 2.5.1 → 2.7.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.
Files changed (150) hide show
  1. package/README.md +8 -1095
  2. package/build/branch/queries.d.ts +8 -1
  3. package/build/branch/queries.js +53 -23
  4. package/build/build/android/build.js +1 -0
  5. package/build/build/ios/build.js +1 -0
  6. package/build/build/local.js +1 -1
  7. package/build/build/queries.d.ts +11 -1
  8. package/build/build/queries.js +39 -1
  9. package/build/build/validate.d.ts +1 -0
  10. package/build/build/validate.js +121 -1
  11. package/build/channel/queries.d.ts +11 -0
  12. package/build/channel/queries.js +51 -9
  13. package/build/channel/utils.js +22 -22
  14. package/build/commandUtils/context/LoggedInContextField.d.ts +3 -0
  15. package/build/commandUtils/context/LoggedInContextField.js +9 -1
  16. package/build/commandUtils/context/MaybeLoggedInContextField.d.ts +3 -0
  17. package/build/commandUtils/context/MaybeLoggedInContextField.js +9 -1
  18. package/build/commandUtils/context/contextUtils/createGraphqlClient.d.ts +2 -2
  19. package/build/commandUtils/context/contextUtils/getProjectIdAsync.js +1 -1
  20. package/build/commandUtils/gating/FeatureGateEnvOverrides.d.ts +7 -0
  21. package/build/commandUtils/gating/FeatureGateEnvOverrides.js +35 -0
  22. package/build/commandUtils/gating/FeatureGateKey.d.ts +4 -0
  23. package/build/commandUtils/gating/FeatureGateKey.js +11 -0
  24. package/build/commandUtils/gating/FeatureGateTestOverrides.d.ts +5 -0
  25. package/build/commandUtils/gating/FeatureGateTestOverrides.js +17 -0
  26. package/build/commandUtils/gating/FeatureGating.d.ts +16 -0
  27. package/build/commandUtils/gating/FeatureGating.js +55 -0
  28. package/build/commandUtils/pagination.d.ts +4 -3
  29. package/build/commands/branch/create.d.ts +0 -3
  30. package/build/commands/branch/create.js +2 -27
  31. package/build/commands/branch/list.d.ts +1 -1
  32. package/build/commands/branch/view.d.ts +1 -1
  33. package/build/commands/build/configure.d.ts +1 -1
  34. package/build/commands/build/index.d.ts +2 -2
  35. package/build/commands/build/list.d.ts +4 -4
  36. package/build/commands/build/list.js +2 -15
  37. package/build/commands/build/run.d.ts +21 -0
  38. package/build/commands/build/run.js +149 -0
  39. package/build/commands/build/version/set.d.ts +1 -1
  40. package/build/commands/build/version/sync.d.ts +1 -1
  41. package/build/commands/channel/create.d.ts +0 -7
  42. package/build/commands/channel/create.js +4 -31
  43. package/build/commands/channel/list.d.ts +1 -1
  44. package/build/commands/channel/view.d.ts +1 -1
  45. package/build/commands/config.d.ts +1 -1
  46. package/build/commands/credentials.d.ts +1 -1
  47. package/build/commands/device/list.d.ts +1 -1
  48. package/build/commands/metadata/lint.d.ts +12 -0
  49. package/build/commands/metadata/lint.js +72 -0
  50. package/build/commands/metadata/pull.js +20 -9
  51. package/build/commands/metadata/push.js +20 -9
  52. package/build/commands/secret/create.d.ts +1 -1
  53. package/build/commands/secret/list.js +12 -17
  54. package/build/commands/submit.d.ts +1 -1
  55. package/build/commands/update/configure.d.ts +1 -0
  56. package/build/commands/update/configure.js +10 -216
  57. package/build/commands/update/index.d.ts +3 -9
  58. package/build/commands/update/index.js +136 -143
  59. package/build/commands/update/list.d.ts +1 -1
  60. package/build/commands/webhook/create.d.ts +1 -1
  61. package/build/commands/webhook/list.d.ts +1 -1
  62. package/build/commands/webhook/update.d.ts +1 -1
  63. package/build/devices/actions/create/inputMethod.js +2 -15
  64. package/build/devices/utils/formatDevice.d.ts +2 -0
  65. package/build/devices/utils/formatDevice.js +32 -7
  66. package/build/env.d.ts +8 -0
  67. package/build/env.js +8 -0
  68. package/build/graphql/generated.d.ts +100 -23
  69. package/build/graphql/generated.js +10 -2
  70. package/build/graphql/mutations/KeystoreGenerationUrlMutation.js +1 -1
  71. package/build/graphql/queries/UserQuery.js +2 -2
  72. package/build/graphql/types/Build.js +2 -0
  73. package/build/log.d.ts +1 -0
  74. package/build/log.js +3 -0
  75. package/build/metadata/apple/rules/index.d.ts +1 -0
  76. package/build/metadata/apple/rules/index.js +6 -0
  77. package/build/metadata/apple/rules/infoKeywordLength.d.ts +6 -0
  78. package/build/metadata/apple/rules/infoKeywordLength.js +35 -0
  79. package/build/metadata/apple/rules/infoRestrictedWords.d.ts +6 -0
  80. package/build/metadata/apple/rules/infoRestrictedWords.js +57 -0
  81. package/build/metadata/apple/tasks/index.d.ts +1 -2
  82. package/build/metadata/apple/tasks/index.js +1 -1
  83. package/build/metadata/auth.d.ts +21 -0
  84. package/build/metadata/auth.js +33 -0
  85. package/build/metadata/config/issue.d.ts +21 -0
  86. package/build/metadata/config/issue.js +9 -0
  87. package/build/metadata/config/resolve.d.ts +27 -0
  88. package/build/metadata/{config.js → config/resolve.js} +24 -25
  89. package/build/metadata/config/schema.d.ts +7 -0
  90. package/build/metadata/config/schema.js +2 -0
  91. package/build/metadata/config/validate.d.ts +3 -0
  92. package/build/metadata/config/validate.js +47 -0
  93. package/build/metadata/download.d.ts +11 -2
  94. package/build/metadata/download.js +14 -9
  95. package/build/metadata/errors.d.ts +3 -3
  96. package/build/metadata/errors.js +3 -1
  97. package/build/metadata/upload.d.ts +11 -2
  98. package/build/metadata/upload.js +16 -11
  99. package/build/metadata/utils/ajv.d.ts +10 -0
  100. package/build/metadata/utils/ajv.js +30 -0
  101. package/build/metadata/utils/telemetry.js +6 -6
  102. package/build/project/projectUtils.d.ts +3 -1
  103. package/build/project/projectUtils.js +10 -3
  104. package/build/project/publish.d.ts +13 -10
  105. package/build/project/publish.js +68 -38
  106. package/build/project/workflow.d.ts +1 -0
  107. package/build/project/workflow.js +9 -1
  108. package/build/run/android/run.d.ts +1 -0
  109. package/build/run/android/run.js +9 -0
  110. package/build/run/ios/run.d.ts +1 -0
  111. package/build/run/ios/run.js +31 -0
  112. package/build/run/ios/simctl.d.ts +2 -0
  113. package/build/run/ios/simctl.js +8 -0
  114. package/build/run/ios/simulator.d.ts +21 -0
  115. package/build/run/ios/simulator.js +161 -0
  116. package/build/run/ios/systemRequirements.d.ts +1 -0
  117. package/build/run/ios/systemRequirements.js +82 -0
  118. package/build/run/ios/xcode.d.ts +4 -0
  119. package/build/run/ios/xcode.js +41 -0
  120. package/build/run/ios/xcrun.d.ts +4 -0
  121. package/build/run/ios/xcrun.js +68 -0
  122. package/build/run/run.d.ts +8 -0
  123. package/build/run/run.js +15 -0
  124. package/build/submit/ArchiveSource.js +12 -16
  125. package/build/submit/utils/summary.d.ts +1 -1
  126. package/build/update/configure.d.ts +22 -0
  127. package/build/update/configure.js +200 -0
  128. package/build/update/queries.js +30 -39
  129. package/build/update/utils.d.ts +8 -1
  130. package/build/update/utils.js +35 -1
  131. package/build/utils/buildDistribution.d.ts +3 -0
  132. package/build/utils/buildDistribution.js +20 -0
  133. package/build/utils/download.d.ts +2 -0
  134. package/build/utils/download.js +114 -0
  135. package/build/utils/expoCli.d.ts +6 -0
  136. package/build/utils/expoCli.js +46 -1
  137. package/build/utils/expodash/filter.d.ts +2 -0
  138. package/build/utils/expodash/filter.js +8 -0
  139. package/build/utils/expodash/memoize.d.ts +2 -0
  140. package/build/utils/expodash/memoize.js +17 -0
  141. package/build/utils/formatFields.d.ts +3 -2
  142. package/build/utils/image.d.ts +6 -0
  143. package/build/utils/image.js +107 -0
  144. package/build/utils/statuspageService.js +1 -0
  145. package/oclif.manifest.json +1 -1
  146. package/package.json +36 -30
  147. package/schema/metadata-0.json +1 -1
  148. package/build/metadata/config.d.ts +0 -41
  149. package/build/metadata/context.d.ts +0 -50
  150. package/build/metadata/context.js +0 -47
@@ -1,6 +1,6 @@
1
1
  import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
2
2
  import { PaginatedQueryOptions } from '../commandUtils/pagination';
3
- import { UpdateBranchFragment } from '../graphql/generated';
3
+ import { CreateUpdateBranchForAppMutationVariables, UpdateBranch, UpdateBranchFragment } from '../graphql/generated';
4
4
  export declare const BRANCHES_LIMIT = 50;
5
5
  export declare function selectBranchOnAppAsync(graphqlClient: ExpoGraphqlClient, { projectId, promptTitle, displayTextForListItem, paginatedQueryOptions, }: {
6
6
  projectId: string;
@@ -12,3 +12,10 @@ export declare function listAndRenderBranchesOnAppAsync(graphqlClient: ExpoGraph
12
12
  projectId: string;
13
13
  paginatedQueryOptions: PaginatedQueryOptions;
14
14
  }): Promise<void>;
15
+ export declare function createUpdateBranchOnAppAsync(graphqlClient: ExpoGraphqlClient, { appId, name }: CreateUpdateBranchForAppMutationVariables): Promise<Pick<UpdateBranch, 'id' | 'name'>>;
16
+ export declare function ensureBranchExistsAsync(graphqlClient: ExpoGraphqlClient, { appId, branchName, }: {
17
+ appId: string;
18
+ branchName: string;
19
+ }): Promise<{
20
+ branchId: string;
21
+ }>;
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listAndRenderBranchesOnAppAsync = exports.selectBranchOnAppAsync = exports.BRANCHES_LIMIT = void 0;
3
+ exports.ensureBranchExistsAsync = exports.createUpdateBranchOnAppAsync = exports.listAndRenderBranchesOnAppAsync = exports.selectBranchOnAppAsync = exports.BRANCHES_LIMIT = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
- const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
6
+ const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
7
+ const client_1 = require("../graphql/client");
7
8
  const BranchQuery_1 = require("../graphql/queries/BranchQuery");
8
9
  const log_1 = tslib_1.__importDefault(require("../log"));
9
10
  const utils_1 = require("../update/utils");
10
11
  const json_1 = require("../utils/json");
11
12
  const queries_1 = require("../utils/queries");
13
+ const utils_2 = require("./utils");
12
14
  exports.BRANCHES_LIMIT = 50;
13
15
  async function selectBranchOnAppAsync(graphqlClient, { projectId, promptTitle, displayTextForListItem, paginatedQueryOptions, }) {
14
16
  var _a;
@@ -62,29 +64,57 @@ function renderPageOfBranches(currentPage, { json }) {
62
64
  (0, json_1.printJsonOnlyOutput)(currentPage);
63
65
  }
64
66
  else {
65
- const table = new cli_table3_1.default({
66
- head: ['Branch', ...utils_1.UPDATE_COLUMNS],
67
- wordWrap: true,
68
- });
69
- table.push(...currentPage.map(branch => {
70
- if (branch.updates.length === 0) {
71
- return [branch.name, 'N/A', 'N/A', 'N/A', 'N/A'];
72
- }
73
- const latestUpdateOnBranch = branch.updates[0];
74
- return [
75
- branch.name,
76
- (0, utils_1.formatUpdateMessage)(latestUpdateOnBranch),
77
- latestUpdateOnBranch.runtimeVersion,
78
- latestUpdateOnBranch.group,
79
- (0, utils_1.getPlatformsForGroup)({
80
- group: latestUpdateOnBranch.group,
81
- updates: branch.updates,
82
- }),
83
- ];
84
- }));
85
67
  log_1.default.addNewLineIfNone();
86
68
  log_1.default.log(chalk_1.default.bold('Branches:'));
87
69
  log_1.default.addNewLineIfNone();
88
- log_1.default.log(table.toString());
70
+ log_1.default.log(currentPage
71
+ .map(branch => (0, utils_1.formatBranch)((0, utils_1.getBranchDescription)(branch)))
72
+ .join(`\n\n${chalk_1.default.dim('———')}\n\n`));
73
+ }
74
+ }
75
+ async function createUpdateBranchOnAppAsync(graphqlClient, { appId, name }) {
76
+ const result = await (0, client_1.withErrorHandlingAsync)(graphqlClient
77
+ .mutation((0, graphql_tag_1.default) `
78
+ mutation CreateUpdateBranchForApp($appId: ID!, $name: String!) {
79
+ updateBranch {
80
+ createUpdateBranchForApp(appId: $appId, name: $name) {
81
+ id
82
+ name
83
+ }
84
+ }
85
+ }
86
+ `, {
87
+ appId,
88
+ name,
89
+ })
90
+ .toPromise());
91
+ const newBranch = result.updateBranch.createUpdateBranchForApp;
92
+ if (!newBranch) {
93
+ throw new Error(`Could not create branch ${name}.`);
94
+ }
95
+ return newBranch;
96
+ }
97
+ exports.createUpdateBranchOnAppAsync = createUpdateBranchOnAppAsync;
98
+ async function ensureBranchExistsAsync(graphqlClient, { appId, branchName, }) {
99
+ try {
100
+ const updateBranch = await BranchQuery_1.BranchQuery.getBranchByNameAsync(graphqlClient, {
101
+ appId,
102
+ name: branchName,
103
+ });
104
+ const { id } = updateBranch;
105
+ return { branchId: id };
106
+ }
107
+ catch (error) {
108
+ if (error instanceof utils_2.BranchNotFoundError) {
109
+ const newUpdateBranch = await createUpdateBranchOnAppAsync(graphqlClient, {
110
+ appId,
111
+ name: branchName,
112
+ });
113
+ return { branchId: newUpdateBranch.id };
114
+ }
115
+ else {
116
+ throw error;
117
+ }
89
118
  }
90
119
  }
120
+ exports.ensureBranchExistsAsync = ensureBranchExistsAsync;
@@ -36,6 +36,7 @@ This means that it will most likely produce an AAB and you will not be able to i
36
36
  }
37
37
  (0, validate_1.checkNodeEnvVariable)(ctx);
38
38
  await (0, validate_1.checkGoogleServicesFileAsync)(ctx);
39
+ await (0, validate_1.validatePNGsForManagedProjectAsync)(ctx);
39
40
  const gradleContext = await (0, gradle_1.resolveGradleBuildContextAsync)(ctx.projectDir, buildProfile);
40
41
  if (ctx.workflow === eas_build_job_1.Workflow.MANAGED) {
41
42
  await (0, applicationId_1.ensureApplicationIdIsDefinedForManagedProjectAsync)(ctx.projectDir, ctx.exp, ctx.user);
@@ -23,6 +23,7 @@ async function createIosContextAsync(ctx) {
23
23
  }
24
24
  (0, validate_1.checkNodeEnvVariable)(ctx);
25
25
  await (0, validate_1.checkGoogleServicesFileAsync)(ctx);
26
+ await (0, validate_1.validatePNGsForManagedProjectAsync)(ctx);
26
27
  const xcodeBuildContext = await (0, scheme_1.resolveXcodeBuildContextAsync)({
27
28
  projectDir: ctx.projectDir,
28
29
  nonInteractive: ctx.nonInteractive,
@@ -6,7 +6,7 @@ const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
6
6
  const semver_1 = tslib_1.__importDefault(require("semver"));
7
7
  const ora_1 = require("../ora");
8
8
  const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
9
- const PLUGIN_PACKAGE_VERSION = '0.0.115';
9
+ const PLUGIN_PACKAGE_VERSION = '0.0.118';
10
10
  async function runLocalBuildAsync(job, metadata, options) {
11
11
  var _a;
12
12
  const { command, args } = await getCommandAndArgsAsync(job, metadata);
@@ -1,6 +1,6 @@
1
1
  import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
2
2
  import { PaginatedQueryOptions } from '../commandUtils/pagination';
3
- import { BuildFilter } from '../graphql/generated';
3
+ import { BuildFilter, BuildFragment } from '../graphql/generated';
4
4
  export declare const BUILDS_LIMIT = 50;
5
5
  export declare function listAndRenderBuildsOnAppAsync(graphqlClient: ExpoGraphqlClient, { projectId, projectDisplayName, filter, paginatedQueryOptions, }: {
6
6
  projectId: string;
@@ -8,3 +8,13 @@ export declare function listAndRenderBuildsOnAppAsync(graphqlClient: ExpoGraphql
8
8
  filter?: BuildFilter;
9
9
  paginatedQueryOptions: PaginatedQueryOptions;
10
10
  }): Promise<void>;
11
+ export declare function listAndSelectBuildsOnAppAsync(graphqlClient: ExpoGraphqlClient, { projectId, projectDisplayName, filter, queryOptions, }: {
12
+ projectId: string;
13
+ projectDisplayName: string;
14
+ filter?: BuildFilter;
15
+ queryOptions: PaginatedQueryOptions;
16
+ }): Promise<BuildFragment>;
17
+ export declare function getLatestBuildAsync(graphqlClient: ExpoGraphqlClient, { projectId, filter, }: {
18
+ projectId: string;
19
+ filter?: BuildFilter;
20
+ }): Promise<BuildFragment>;
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listAndRenderBuildsOnAppAsync = exports.BUILDS_LIMIT = void 0;
3
+ exports.getLatestBuildAsync = exports.listAndSelectBuildsOnAppAsync = exports.listAndRenderBuildsOnAppAsync = exports.BUILDS_LIMIT = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
6
  const BuildQuery_1 = require("../graphql/queries/BuildQuery");
7
7
  const log_1 = tslib_1.__importDefault(require("../log"));
8
+ const platform_1 = require("../platform");
9
+ const prompts_1 = require("../prompts");
8
10
  const json_1 = require("../utils/json");
9
11
  const queries_1 = require("../utils/queries");
10
12
  const formatBuild_1 = require("./utils/formatBuild");
@@ -38,6 +40,42 @@ async function listAndRenderBuildsOnAppAsync(graphqlClient, { projectId, project
38
40
  }
39
41
  }
40
42
  exports.listAndRenderBuildsOnAppAsync = listAndRenderBuildsOnAppAsync;
43
+ async function listAndSelectBuildsOnAppAsync(graphqlClient, { projectId, projectDisplayName, filter, queryOptions, }) {
44
+ var _a;
45
+ const builds = await BuildQuery_1.BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
46
+ appId: projectId,
47
+ limit: (_a = queryOptions.limit) !== null && _a !== void 0 ? _a : exports.BUILDS_LIMIT,
48
+ offset: queryOptions.offset,
49
+ filter,
50
+ });
51
+ if (builds.length === 0) {
52
+ throw new Error('Found no builds matching the provided criteria.');
53
+ }
54
+ const { selectedSimulatorBuild } = await (0, prompts_1.promptAsync)({
55
+ type: 'select',
56
+ message: `Select simulator build to run for ${projectDisplayName} app`,
57
+ name: 'selectedSimulatorBuild',
58
+ choices: builds.map(build => ({
59
+ title: `id: ${build.id}, platform: ${platform_1.appPlatformDisplayNames[build.platform]}, version: ${build.appVersion}, build number: ${build.appBuildVersion}`,
60
+ value: build,
61
+ })),
62
+ });
63
+ return selectedSimulatorBuild;
64
+ }
65
+ exports.listAndSelectBuildsOnAppAsync = listAndSelectBuildsOnAppAsync;
66
+ async function getLatestBuildAsync(graphqlClient, { projectId, filter, }) {
67
+ const builds = await BuildQuery_1.BuildQuery.viewBuildsOnAppAsync(graphqlClient, {
68
+ appId: projectId,
69
+ limit: 1,
70
+ offset: 0,
71
+ filter,
72
+ });
73
+ if (builds.length === 0) {
74
+ throw new Error('Found no build matching the provided criteria.');
75
+ }
76
+ return builds[0];
77
+ }
78
+ exports.getLatestBuildAsync = getLatestBuildAsync;
41
79
  function renderPageOfBuilds({ builds, projectDisplayName, paginatedQueryOptions, }) {
42
80
  if (paginatedQueryOptions.json) {
43
81
  (0, json_1.printJsonOnlyOutput)(builds);
@@ -2,3 +2,4 @@ import { Platform } from '@expo/eas-build-job';
2
2
  import { CommonContext } from './context';
3
3
  export declare function checkNodeEnvVariable(ctx: CommonContext<Platform>): void;
4
4
  export declare function checkGoogleServicesFileAsync<T extends Platform>(ctx: CommonContext<T>): Promise<void>;
5
+ export declare function validatePNGsForManagedProjectAsync<T extends Platform>(ctx: CommonContext<T>): Promise<void>;
@@ -1,11 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkGoogleServicesFileAsync = exports.checkNodeEnvVariable = void 0;
3
+ exports.validatePNGsForManagedProjectAsync = exports.checkGoogleServicesFileAsync = exports.checkNodeEnvVariable = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const eas_build_job_1 = require("@expo/eas-build-job");
6
+ const core_1 = require("@oclif/core");
6
7
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
7
8
  const path_1 = tslib_1.__importDefault(require("path"));
9
+ const semver_1 = tslib_1.__importDefault(require("semver"));
8
10
  const log_1 = tslib_1.__importStar(require("../log"));
11
+ const image_1 = require("../utils/image");
9
12
  const vcs_1 = require("../vcs");
10
13
  function checkNodeEnvVariable(ctx) {
11
14
  var _a;
@@ -39,3 +42,120 @@ exports.checkGoogleServicesFileAsync = checkGoogleServicesFileAsync;
39
42
  function isInsideDirectory(file, directory) {
40
43
  return file.startsWith(directory);
41
44
  }
45
+ async function validatePNGsForManagedProjectAsync(ctx) {
46
+ if (ctx.workflow !== eas_build_job_1.Workflow.MANAGED) {
47
+ return;
48
+ }
49
+ // don't run PNG checks on SDK 47 and newer
50
+ // see https://github.com/expo/eas-cli/pull/1477#issuecomment-1293914917
51
+ if (!ctx.exp.sdkVersion || semver_1.default.satisfies(ctx.exp.sdkVersion, '>= 47.0.0')) {
52
+ return;
53
+ }
54
+ if (ctx.platform === eas_build_job_1.Platform.ANDROID) {
55
+ await validateAndroidPNGsAsync(ctx);
56
+ }
57
+ // Validating iOS PNGs is currently disabled
58
+ // See https://github.com/expo/eas-cli/pull/1477 for context
59
+ //
60
+ // else {
61
+ // await validateIosPNGsAsync(ctx as CommonContext<Platform.IOS>);
62
+ // }
63
+ }
64
+ exports.validatePNGsForManagedProjectAsync = validatePNGsForManagedProjectAsync;
65
+ async function validateAndroidPNGsAsync(ctx) {
66
+ var _a, _b, _c, _d, _e, _f, _g;
67
+ const pngs = [
68
+ {
69
+ configPath: 'exp.icon',
70
+ pngPath: ctx.exp.icon,
71
+ },
72
+ {
73
+ configPath: 'exp.android.icon',
74
+ pngPath: (_a = ctx.exp.android) === null || _a === void 0 ? void 0 : _a.icon,
75
+ },
76
+ {
77
+ configPath: 'exp.android.adaptiveIcon.foregroundImage',
78
+ pngPath: (_c = (_b = ctx.exp.android) === null || _b === void 0 ? void 0 : _b.adaptiveIcon) === null || _c === void 0 ? void 0 : _c.foregroundImage,
79
+ },
80
+ {
81
+ configPath: 'exp.android.adaptiveIcon.backgroundImage',
82
+ pngPath: (_e = (_d = ctx.exp.android) === null || _d === void 0 ? void 0 : _d.adaptiveIcon) === null || _e === void 0 ? void 0 : _e.backgroundImage,
83
+ },
84
+ {
85
+ configPath: 'exp.splash.image',
86
+ pngPath: (_f = ctx.exp.splash) === null || _f === void 0 ? void 0 : _f.image,
87
+ },
88
+ {
89
+ configPath: 'exp.notification.icon',
90
+ pngPath: (_g = ctx.exp.notification) === null || _g === void 0 ? void 0 : _g.icon,
91
+ },
92
+ ];
93
+ await validatePNGsAsync(pngs);
94
+ }
95
+ // Validating iOS PNGs is currently disabled
96
+ // See https://github.com/expo/eas-cli/pull/1477 for context
97
+ //
98
+ // async function validateIosPNGsAsync(ctx: CommonContext<Platform.IOS>): Promise<void> {
99
+ // const pngs: ConfigPng[] = [
100
+ // {
101
+ // configPath: 'exp.icon',
102
+ // pngPath: ctx.exp.icon,
103
+ // },
104
+ // {
105
+ // configPath: 'exp.ios.icon',
106
+ // pngPath: ctx.exp.ios?.icon,
107
+ // },
108
+ // {
109
+ // configPath: 'exp.splash.image',
110
+ // pngPath: ctx.exp.splash?.image,
111
+ // },
112
+ // {
113
+ // configPath: 'exp.notification.icon',
114
+ // pngPath: ctx.exp.notification?.icon,
115
+ // },
116
+ // ];
117
+ // await validatePNGsAsync(pngs);
118
+ // const icon = ctx.exp.ios?.icon ?? ctx.exp.icon;
119
+ // if (!icon) {
120
+ // return;
121
+ // }
122
+ // const iconConfigPath = `expo${ctx.exp.ios?.icon ? '.ios' : ''}.icon`;
123
+ // try {
124
+ // await ensurePNGIsNotTransparentAsync(icon);
125
+ // } catch (err: any) {
126
+ // if (err instanceof ImageTransparencyError) {
127
+ // Log.error(
128
+ // `Your iOS app icon (${iconConfigPath}) can't have transparency if you wish to upload your app to the Apple App Store.`
129
+ // );
130
+ // Log.error(learnMore('https://expo.fyi/remove-alpha-channel', { dim: false }));
131
+ // Errors.exit(1);
132
+ // } else {
133
+ // throw err;
134
+ // }
135
+ // }
136
+ // }
137
+ async function validatePNGsAsync(configPngs) {
138
+ const validationPromises = configPngs.map(configPng => validatePNGAsync(configPng));
139
+ const validationResults = await Promise.allSettled(validationPromises);
140
+ const failedValidations = validationResults.filter((result) => result.status === 'rejected');
141
+ if (failedValidations.length === 0) {
142
+ return;
143
+ }
144
+ log_1.default.error('PNG images validation failed:');
145
+ for (const { reason } of failedValidations) {
146
+ const error = reason;
147
+ log_1.default.error(`- ${error.message}`);
148
+ }
149
+ core_1.Errors.exit(1);
150
+ }
151
+ async function validatePNGAsync({ configPath, pngPath }) {
152
+ if (!pngPath) {
153
+ return;
154
+ }
155
+ if (!pngPath.endsWith('.png')) {
156
+ throw new Error(`"${configPath}" is not a PNG file`);
157
+ }
158
+ if (!(await (0, image_1.isPNGAsync)(pngPath))) {
159
+ throw new Error(`"${configPath}" is not valid PNG`);
160
+ }
161
+ }
@@ -1,5 +1,6 @@
1
1
  import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGraphqlClient';
2
2
  import { PaginatedQueryOptions } from '../commandUtils/pagination';
3
+ import { CreateUpdateChannelOnAppMutation } from '../graphql/generated';
3
4
  import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
4
5
  export declare const CHANNELS_LIMIT = 25;
5
6
  export declare function selectChannelOnAppAsync(graphqlClient: ExpoGraphqlClient, { projectId, selectionPromptTitle, paginatedQueryOptions, }: {
@@ -16,3 +17,13 @@ export declare function listAndRenderBranchesAndUpdatesOnChannelAsync(graphqlCli
16
17
  channelName: string;
17
18
  paginatedQueryOptions: PaginatedQueryOptions;
18
19
  }): Promise<void>;
20
+ export declare function createChannelOnAppAsync(graphqlClient: ExpoGraphqlClient, { appId, branchId, channelName, }: {
21
+ appId: string;
22
+ branchId: string;
23
+ channelName: string;
24
+ }): Promise<CreateUpdateChannelOnAppMutation>;
25
+ export declare function ensureChannelExistsAsync(graphqlClient: ExpoGraphqlClient, { appId, branchId, channelName }: {
26
+ appId: string;
27
+ branchId: string;
28
+ channelName: string;
29
+ }): Promise<void>;
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listAndRenderBranchesAndUpdatesOnChannelAsync = exports.listAndRenderChannelsOnAppAsync = exports.selectChannelOnAppAsync = exports.CHANNELS_LIMIT = void 0;
3
+ exports.ensureChannelExistsAsync = exports.createChannelOnAppAsync = exports.listAndRenderBranchesAndUpdatesOnChannelAsync = exports.listAndRenderChannelsOnAppAsync = exports.selectChannelOnAppAsync = exports.CHANNELS_LIMIT = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
+ const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
7
+ const client_1 = require("../graphql/client");
6
8
  const BranchQuery_1 = require("../graphql/queries/BranchQuery");
7
9
  const ChannelQuery_1 = require("../graphql/queries/ChannelQuery");
8
10
  const log_1 = tslib_1.__importDefault(require("../log"));
@@ -102,12 +104,12 @@ function renderPageOfChannels(currentPage, { json }) {
102
104
  }
103
105
  else {
104
106
  for (const channel of currentPage) {
107
+ renderChannelHeaderContent({ channelName: channel.name, channelId: channel.id });
105
108
  log_1.default.addNewLineIfNone();
106
- log_1.default.log((0, formatFields_1.default)([
107
- { label: 'Name', value: channel.name },
108
- { label: 'ID', value: channel.id },
109
- ]));
110
109
  (0, utils_1.logChannelDetails)(channel);
110
+ if (currentPage.indexOf(channel) < currentPage.length - 1) {
111
+ log_1.default.log(`\n${chalk_1.default.dim('———')}\n`);
112
+ }
111
113
  }
112
114
  }
113
115
  }
@@ -117,11 +119,8 @@ function renderPageOfBranchesOnChannel(channel, currentPage, { json }) {
117
119
  (0, json_1.printJsonOnlyOutput)({ currentPage: channelWithNewBranches });
118
120
  }
119
121
  else {
122
+ // The channel details contain both the branch and latest update group
120
123
  log_1.default.addNewLineIfNone();
121
- log_1.default.log((0, formatFields_1.default)([
122
- { label: 'Name', value: channel.name },
123
- { label: 'ID', value: channel.id },
124
- ]));
125
124
  (0, utils_1.logChannelDetails)(channelWithNewBranches);
126
125
  }
127
126
  }
@@ -135,3 +134,46 @@ function renderChannelHeaderContent({ channelName, channelId, }) {
135
134
  log_1.default.addNewLineIfNone();
136
135
  log_1.default.log((0, chalk_1.default) `{bold Branches pointed at this channel and their most recent update group:}`);
137
136
  }
137
+ async function createChannelOnAppAsync(graphqlClient, { appId, branchId, channelName, }) {
138
+ // Point the new channel at a branch with its same name.
139
+ const branchMapping = JSON.stringify({
140
+ data: [{ branchId, branchMappingLogic: 'true' }],
141
+ version: 0,
142
+ });
143
+ return await (0, client_1.withErrorHandlingAsync)(graphqlClient
144
+ .mutation((0, graphql_tag_1.default) `
145
+ mutation CreateUpdateChannelOnApp($appId: ID!, $name: String!, $branchMapping: String!) {
146
+ updateChannel {
147
+ createUpdateChannelForApp(appId: $appId, name: $name, branchMapping: $branchMapping) {
148
+ id
149
+ name
150
+ branchMapping
151
+ }
152
+ }
153
+ }
154
+ `, {
155
+ appId,
156
+ name: channelName,
157
+ branchMapping,
158
+ })
159
+ .toPromise());
160
+ }
161
+ exports.createChannelOnAppAsync = createChannelOnAppAsync;
162
+ async function ensureChannelExistsAsync(graphqlClient, { appId, branchId, channelName }) {
163
+ var _a;
164
+ try {
165
+ await createChannelOnAppAsync(graphqlClient, {
166
+ appId,
167
+ channelName,
168
+ branchId,
169
+ });
170
+ }
171
+ catch (e) {
172
+ const isIgnorableError = ((_a = e.graphQLErrors) === null || _a === void 0 ? void 0 : _a.length) === 1 &&
173
+ e.graphQLErrors[0].extensions.errorCode === 'CHANNEL_ALREADY_EXISTS';
174
+ if (!isIgnorableError) {
175
+ throw e;
176
+ }
177
+ }
178
+ }
179
+ exports.ensureChannelExistsAsync = ensureChannelExistsAsync;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.logChannelDetails = exports.getBranchMapping = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const assert_1 = tslib_1.__importDefault(require("assert"));
6
- const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
7
  const log_1 = tslib_1.__importDefault(require("../log"));
8
8
  const utils_1 = require("../update/utils");
9
9
  /**
@@ -57,31 +57,31 @@ function getBranchMapping(branchMappingString) {
57
57
  exports.getBranchMapping = getBranchMapping;
58
58
  function logChannelDetails(channel) {
59
59
  const { branchMapping, isRollout, rolloutPercent } = getBranchMapping(channel.branchMapping);
60
- const updateChannelsTable = new cli_table3_1.default({
61
- head: ['branch', ...(isRollout ? ['rollout percent'] : []), ...utils_1.UPDATE_COLUMNS],
62
- wordWrap: true,
63
- });
64
60
  if (branchMapping.data.length > 2) {
65
61
  throw new Error('Branch Mapping data must have length less than or equal to 2.');
66
62
  }
67
63
  const rolloutBranchIds = branchMapping.data.map(data => data.branchId);
68
- for (const currentBranch of channel.updateBranches) {
69
- const updateGroupDescriptions = (0, utils_1.getUpdateGroupDescriptionsWithBranch)(currentBranch.updateGroups);
70
- const isRolloutBranch = isRollout && rolloutBranchIds.includes(currentBranch.id);
71
- const isBaseBranch = rolloutBranchIds.length > 0 && rolloutBranchIds[0] === currentBranch.id;
72
- updateGroupDescriptions.forEach(({ branch, message, runtimeVersion, group, platforms }) => {
73
- updateChannelsTable.push([
74
- branch,
75
- ...(isRolloutBranch
76
- ? [isBaseBranch ? `${rolloutPercent * 100}%` : `${(1 - rolloutPercent) * 100}%`]
77
- : []),
78
- message,
79
- runtimeVersion,
80
- group,
81
- platforms,
82
- ]);
83
- });
64
+ const branchDescription = channel.updateBranches.flatMap(branch => {
65
+ const updateGroupWithBranchDescriptions = (0, utils_1.getUpdateGroupDescriptionsWithBranch)(branch.updateGroups);
66
+ const isRolloutBranch = isRollout && rolloutBranchIds.includes(branch.id);
67
+ const isBaseBranch = rolloutBranchIds.length > 0 && rolloutBranchIds[0] === branch.id;
68
+ let rolloutPercentNumber = undefined;
69
+ if (isRolloutBranch) {
70
+ rolloutPercentNumber = isBaseBranch ? rolloutPercent * 100 : (1 - rolloutPercent) * 100;
71
+ }
72
+ return updateGroupWithBranchDescriptions.map(({ branch, ...updateGroup }) => ({
73
+ branch,
74
+ branchRolloutPercentage: rolloutPercentNumber,
75
+ update: updateGroup,
76
+ }));
77
+ });
78
+ if (branchDescription.length === 0) {
79
+ log_1.default.log(chalk_1.default.dim('No branches are pointed to this channel.'));
80
+ }
81
+ else {
82
+ log_1.default.log(branchDescription
83
+ .map(description => (0, utils_1.formatBranch)(description))
84
+ .join(`\n\n${chalk_1.default.dim('———')}\n\n`));
84
85
  }
85
- log_1.default.log(updateChannelsTable.toString());
86
86
  }
87
87
  exports.logChannelDetails = logChannelDetails;
@@ -1,12 +1,15 @@
1
1
  import { Actor } from '../../user/User';
2
+ import FeatureGating from '../gating/FeatureGating';
2
3
  import ContextField, { ContextOptions } from './ContextField';
3
4
  import { ExpoGraphqlClient } from './contextUtils/createGraphqlClient';
4
5
  export default class LoggedInContextField extends ContextField<{
5
6
  actor: Actor;
7
+ featureGating: FeatureGating;
6
8
  graphqlClient: ExpoGraphqlClient;
7
9
  }> {
8
10
  getValueAsync({ nonInteractive, sessionManager }: ContextOptions): Promise<{
9
11
  actor: Actor;
12
+ featureGating: FeatureGating;
10
13
  graphqlClient: ExpoGraphqlClient;
11
14
  }>;
12
15
  }
@@ -1,15 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ const FeatureGateEnvOverrides_1 = tslib_1.__importDefault(require("../gating/FeatureGateEnvOverrides"));
5
+ const FeatureGating_1 = tslib_1.__importDefault(require("../gating/FeatureGating"));
4
6
  const ContextField_1 = tslib_1.__importDefault(require("./ContextField"));
5
7
  const createGraphqlClient_1 = require("./contextUtils/createGraphqlClient");
6
8
  class LoggedInContextField extends ContextField_1.default {
7
9
  async getValueAsync({ nonInteractive, sessionManager }) {
10
+ var _a;
8
11
  const { actor, authenticationInfo } = await sessionManager.ensureLoggedInAsync({
9
12
  nonInteractive,
10
13
  });
14
+ const featureGateServerValues = (_a = actor === null || actor === void 0 ? void 0 : actor.featureGates) !== null && _a !== void 0 ? _a : {};
11
15
  const graphqlClient = (0, createGraphqlClient_1.createGraphqlClient)(authenticationInfo);
12
- return { actor, graphqlClient };
16
+ return {
17
+ actor,
18
+ featureGating: new FeatureGating_1.default(featureGateServerValues, new FeatureGateEnvOverrides_1.default()),
19
+ graphqlClient,
20
+ };
13
21
  }
14
22
  }
15
23
  exports.default = LoggedInContextField;
@@ -1,12 +1,15 @@
1
1
  import { Actor } from '../../user/User';
2
+ import FeatureGating from '../gating/FeatureGating';
2
3
  import ContextField, { ContextOptions } from './ContextField';
3
4
  import { ExpoGraphqlClient } from './contextUtils/createGraphqlClient';
4
5
  export default class MaybeLoggedInContextField extends ContextField<{
5
6
  actor: Actor | null;
7
+ featureGating: FeatureGating;
6
8
  graphqlClient: ExpoGraphqlClient;
7
9
  }> {
8
10
  getValueAsync({ sessionManager }: ContextOptions): Promise<{
9
11
  actor: Actor | null;
12
+ featureGating: FeatureGating;
10
13
  graphqlClient: ExpoGraphqlClient;
11
14
  }>;
12
15
  }
@@ -1,17 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ const FeatureGateEnvOverrides_1 = tslib_1.__importDefault(require("../gating/FeatureGateEnvOverrides"));
5
+ const FeatureGating_1 = tslib_1.__importDefault(require("../gating/FeatureGating"));
4
6
  const ContextField_1 = tslib_1.__importDefault(require("./ContextField"));
5
7
  const createGraphqlClient_1 = require("./contextUtils/createGraphqlClient");
6
8
  class MaybeLoggedInContextField extends ContextField_1.default {
7
9
  async getValueAsync({ sessionManager }) {
10
+ var _a;
8
11
  const authenticationInfo = {
9
12
  accessToken: sessionManager.getAccessToken(),
10
13
  sessionSecret: sessionManager.getSessionSecret(),
11
14
  };
12
15
  const graphqlClient = (0, createGraphqlClient_1.createGraphqlClient)(authenticationInfo);
13
16
  const actor = await sessionManager.getUserAsync();
14
- return { actor: actor !== null && actor !== void 0 ? actor : null, graphqlClient };
17
+ const featureGateServerValues = (_a = actor === null || actor === void 0 ? void 0 : actor.featureGates) !== null && _a !== void 0 ? _a : {};
18
+ return {
19
+ actor: actor !== null && actor !== void 0 ? actor : null,
20
+ featureGating: new FeatureGating_1.default(featureGateServerValues, new FeatureGateEnvOverrides_1.default()),
21
+ graphqlClient,
22
+ };
15
23
  }
16
24
  }
17
25
  exports.default = MaybeLoggedInContextField;
@@ -1,7 +1,7 @@
1
- import { Client, OperationContext, OperationResult, PromisifiedSource, TypedDocumentNode } from '@urql/core';
1
+ import { AnyVariables, Client, OperationContext, OperationResult, PromisifiedSource, TypedDocumentNode } from '@urql/core';
2
2
  import { DocumentNode } from 'graphql';
3
3
  export interface ExpoGraphqlClient extends Client {
4
- query<Data = any, Variables extends object = object>(query: DocumentNode | TypedDocumentNode<Data, Variables> | string, variables: Variables | undefined, context: Partial<OperationContext> & {
4
+ query<Data = any, Variables extends AnyVariables = AnyVariables>(query: DocumentNode | TypedDocumentNode<Data, Variables> | string, variables: Variables, context: Partial<OperationContext> & {
5
5
  additionalTypenames: string[];
6
6
  }): PromisifiedSource<OperationResult<Data, Variables>>;
7
7
  }
@@ -22,7 +22,7 @@ async function saveProjectIdToAppConfigAsync(projectDir, projectId, options = {}
22
22
  const exp = (0, expoConfig_1.getExpoConfig)(projectDir, options);
23
23
  const result = await (0, config_1.modifyConfigAsync)(projectDir, {
24
24
  extra: { ...exp.extra, eas: { ...(_a = exp.extra) === null || _a === void 0 ? void 0 : _a.eas, projectId } },
25
- });
25
+ }, { skipSDKVersionRequirement: true });
26
26
  switch (result.type) {
27
27
  case 'success':
28
28
  break;
@@ -0,0 +1,7 @@
1
+ import { FeatureGateKey } from './FeatureGateKey';
2
+ export default class FeatureGateEnvOverrides {
3
+ private readonly map;
4
+ constructor();
5
+ isOverridden(key: FeatureGateKey): boolean;
6
+ getOverride(key: FeatureGateKey): boolean;
7
+ }