eas-cli 16.19.3 → 16.20.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.
Files changed (40) hide show
  1. package/README.md +85 -85
  2. package/build/api.d.ts +1 -0
  3. package/build/api.js +5 -1
  4. package/build/build/android/build.js +1 -3
  5. package/build/build/build.d.ts +3 -3
  6. package/build/build/build.js +11 -4
  7. package/build/build/graphql.d.ts +4 -2
  8. package/build/build/graphql.js +3 -14
  9. package/build/build/ios/build.js +1 -3
  10. package/build/build/metadata.d.ts +1 -2
  11. package/build/build/metadata.js +0 -1
  12. package/build/commandUtils/workflow/validation.d.ts +6 -0
  13. package/build/commandUtils/workflow/validation.js +160 -0
  14. package/build/commands/build/configure.js +2 -19
  15. package/build/commands/env/create.js +0 -1
  16. package/build/commands/project/new.d.ts +33 -0
  17. package/build/commands/project/new.js +349 -0
  18. package/build/commands/project/onboarding.js +1 -1
  19. package/build/commands/update/index.js +1 -5
  20. package/build/commands/workflow/run.js +0 -2
  21. package/build/commands/workflow/validate.d.ts +1 -0
  22. package/build/commands/workflow/validate.js +8 -39
  23. package/build/credentials/ios/appstore/bundleIdCapabilities.js +1 -1
  24. package/build/credentials/ios/appstore/capabilityList.js +18 -8
  25. package/build/graphql/generated.d.ts +0 -6
  26. package/build/graphql/mutations/EnvironmentVariableMutation.d.ts +5 -24
  27. package/build/graphql/mutations/FingerprintMutation.d.ts +2 -3
  28. package/build/log.js +2 -1
  29. package/build/onboarding/installDependencies.d.ts +4 -1
  30. package/build/onboarding/installDependencies.js +13 -5
  31. package/build/project/maybeUploadFingerprintAsync.d.ts +2 -2
  32. package/build/project/maybeUploadFingerprintAsync.js +7 -12
  33. package/build/project/publish.d.ts +5 -5
  34. package/build/project/publish.js +1 -1
  35. package/build/utils/prompts.d.ts +4 -0
  36. package/build/utils/prompts.js +25 -1
  37. package/build/utils/workflowFile.d.ts +1 -3
  38. package/build/utils/workflowFile.js +2 -8
  39. package/oclif.manifest.json +23 -1
  40. package/package.json +6 -6
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatScriptCommand = exports.initializeGitRepositoryAsync = exports.mergeReadmeAsync = exports.copyProjectTemplatesAsync = exports.updatePackageJsonAsync = exports.generateEasConfigAsync = exports.generateAppConfigAsync = exports.generateConfigFilesAsync = exports.stripInvalidCharactersForBundleIdentifier = exports.createProjectAsync = exports.getAccountChoices = exports.installProjectDependenciesAsync = exports.cloneTemplateAsync = exports.promptForTargetDirectoryAsync = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const eas_json_1 = require("@expo/eas-json");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
8
+ const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
9
+ const path_1 = tslib_1.__importDefault(require("path"));
10
+ const ts_deepmerge_1 = tslib_1.__importDefault(require("ts-deepmerge"));
11
+ const api_1 = require("../../api");
12
+ const url_1 = require("../../build/utils/url");
13
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
14
+ const generated_1 = require("../../graphql/generated");
15
+ const AppMutation_1 = require("../../graphql/mutations/AppMutation");
16
+ const AppQuery_1 = require("../../graphql/queries/AppQuery");
17
+ const log_1 = tslib_1.__importStar(require("../../log"));
18
+ const git_1 = require("../../onboarding/git");
19
+ const installDependencies_1 = require("../../onboarding/installDependencies");
20
+ const runCommand_1 = require("../../onboarding/runCommand");
21
+ const ora_1 = require("../../ora");
22
+ const expoConfig_1 = require("../../project/expoConfig");
23
+ const fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync_1 = require("../../project/fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync");
24
+ const prompts_1 = require("../../prompts");
25
+ const User_1 = require("../../user/User");
26
+ const easCli_1 = require("../../utils/easCli");
27
+ async function promptForTargetDirectoryAsync(targetProjectDirFromArgs) {
28
+ log_1.default.log(`🚚 Let's start by cloning the default Expo template project from GitHub and installing dependencies.`);
29
+ log_1.default.newLine();
30
+ if (targetProjectDirFromArgs) {
31
+ return targetProjectDirFromArgs;
32
+ }
33
+ const result = await (0, prompts_1.promptAsync)({
34
+ type: 'text',
35
+ name: 'targetProjectDir',
36
+ message: 'Where would you like to create your new project directory?',
37
+ initial: path_1.default.join(process.cwd(), 'new-expo-project'),
38
+ });
39
+ return result.targetProjectDir;
40
+ }
41
+ exports.promptForTargetDirectoryAsync = promptForTargetDirectoryAsync;
42
+ async function cloneTemplateAsync(targetProjectDir) {
43
+ const githubUsername = 'expo';
44
+ const githubRepositoryName = 'expo-template-default';
45
+ log_1.default.log(`📂 Cloning the project to ${targetProjectDir}`);
46
+ log_1.default.newLine();
47
+ const cloneMethod = (await (0, git_1.canAccessRepositoryUsingSshAsync)({
48
+ githubUsername,
49
+ githubRepositoryName,
50
+ }))
51
+ ? 'ssh'
52
+ : 'https';
53
+ log_1.default.log(chalk_1.default.dim(`We detected that ${cloneMethod} is your preferred git clone method`));
54
+ log_1.default.newLine();
55
+ const { targetProjectDir: finalTargetProjectDirectory } = await (0, git_1.runGitCloneAsync)({
56
+ githubUsername,
57
+ githubRepositoryName,
58
+ targetProjectDir,
59
+ cloneMethod,
60
+ });
61
+ return finalTargetProjectDirectory;
62
+ }
63
+ exports.cloneTemplateAsync = cloneTemplateAsync;
64
+ async function installProjectDependenciesAsync(projectDir) {
65
+ const packageManager = await (0, installDependencies_1.promptForPackageManagerAsync)();
66
+ await (0, installDependencies_1.installDependenciesAsync)({
67
+ projectDir,
68
+ packageManager,
69
+ });
70
+ const dependencies = ['expo-updates', '@expo/metro-runtime'];
71
+ for (const dependency of dependencies) {
72
+ await (0, runCommand_1.runCommandAsync)({
73
+ cwd: projectDir,
74
+ command: 'npx',
75
+ args: ['expo', 'install', dependency],
76
+ });
77
+ }
78
+ return packageManager;
79
+ }
80
+ exports.installProjectDependenciesAsync = installProjectDependenciesAsync;
81
+ function getAccountChoices(actor, namesWithSufficientPermissions) {
82
+ const sortedAccounts = actor.accounts.sort((a, _b) => actor.__typename === 'User' ? (a.name === actor.username ? -1 : 1) : 0);
83
+ return sortedAccounts.map(account => {
84
+ const isPersonalAccount = actor.__typename === 'User' && account.name === actor.username;
85
+ const accountDisplayName = isPersonalAccount
86
+ ? `${account.name} (personal account)`
87
+ : account.name;
88
+ const disabled = !namesWithSufficientPermissions.has(account.name);
89
+ return {
90
+ title: accountDisplayName,
91
+ value: { name: account.name },
92
+ ...(disabled && {
93
+ disabled: true,
94
+ description: 'You do not have the required permissions to create projects on this account.',
95
+ }),
96
+ };
97
+ });
98
+ }
99
+ exports.getAccountChoices = getAccountChoices;
100
+ async function createProjectAsync(graphqlClient, actor, projectDir) {
101
+ const allAccounts = actor.accounts;
102
+ const accountNamesWhereUserHasSufficientPermissionsToCreateApp = new Set(allAccounts
103
+ .filter(a => a.users.find(it => it.actor.id === actor.id)?.role !== generated_1.Role.ViewOnly)
104
+ .map(it => it.name));
105
+ let accountName = allAccounts[0].name;
106
+ if (allAccounts.length > 1) {
107
+ const choices = getAccountChoices(actor, accountNamesWhereUserHasSufficientPermissionsToCreateApp);
108
+ accountName = (await (0, prompts_1.promptAsync)({
109
+ type: 'select',
110
+ name: 'account',
111
+ message: 'Which account should own this project?',
112
+ choices,
113
+ })).account.name;
114
+ }
115
+ const projectName = (0, User_1.getActorUsername)(actor) + '-app';
116
+ const projectFullName = `@${accountName}/${projectName}`;
117
+ const existingProjectIdOnServer = await (0, fetchOrCreateProjectIDForWriteToConfigWithConfirmationAsync_1.findProjectIdByAccountNameAndSlugNullableAsync)(graphqlClient, accountName, projectName);
118
+ if (existingProjectIdOnServer) {
119
+ throw new Error(`Existing project found: ${projectFullName} (ID: ${existingProjectIdOnServer}). Project ID configuration canceled. Re-run the command to select a different account/project.`);
120
+ }
121
+ if (!accountNamesWhereUserHasSufficientPermissionsToCreateApp.has(accountName)) {
122
+ throw new Error(`You don't have permission to create a new project on the ${accountName} account and no matching project already exists on the account.`);
123
+ }
124
+ const projectDashboardUrl = (0, url_1.getProjectDashboardUrl)(accountName, projectName);
125
+ const projectLink = (0, log_1.link)(projectDashboardUrl, { text: projectFullName });
126
+ const account = (0, nullthrows_1.default)(allAccounts.find(a => a.name === accountName));
127
+ const spinner = (0, ora_1.ora)(`Creating ${chalk_1.default.bold(projectFullName)}`).start();
128
+ let projectId;
129
+ try {
130
+ projectId = await AppMutation_1.AppMutation.createAppAsync(graphqlClient, {
131
+ accountId: account.id,
132
+ projectName,
133
+ });
134
+ spinner.succeed(`Created ${chalk_1.default.bold(projectLink)}`);
135
+ }
136
+ catch (err) {
137
+ spinner.fail();
138
+ throw err;
139
+ }
140
+ const exp = await (0, expoConfig_1.getPrivateExpoConfigAsync)(projectDir, { skipPlugins: true });
141
+ await (0, expoConfig_1.createOrModifyExpoConfigAsync)(projectDir, {
142
+ extra: { ...exp.extra, eas: { ...exp.extra?.eas, projectId } },
143
+ }, { skipSDKVersionRequirement: true });
144
+ log_1.default.withTick(`Project successfully linked (ID: ${chalk_1.default.bold(projectId)}) (modified app.json)`);
145
+ return projectId;
146
+ }
147
+ exports.createProjectAsync = createProjectAsync;
148
+ function stripInvalidCharactersForBundleIdentifier(string) {
149
+ return string.replaceAll(/[^A-Za-z0-9]/g, '');
150
+ }
151
+ exports.stripInvalidCharactersForBundleIdentifier = stripInvalidCharactersForBundleIdentifier;
152
+ async function generateConfigFilesAsync(projectDir, app) {
153
+ await generateAppConfigAsync(projectDir, app);
154
+ await generateEasConfigAsync(projectDir);
155
+ await updatePackageJsonAsync(projectDir);
156
+ await copyProjectTemplatesAsync(projectDir);
157
+ await mergeReadmeAsync(projectDir);
158
+ }
159
+ exports.generateConfigFilesAsync = generateConfigFilesAsync;
160
+ async function generateAppConfigAsync(projectDir, app) {
161
+ // Android package name requires each component to start with a lowercase letter.
162
+ const isUsernameValidSegment = /^[^a-z]/.test(app.ownerAccount.name);
163
+ const userPrefix = isUsernameValidSegment ? 'user' : '';
164
+ const isSlugValidSegment = /^[^a-z]/.test(app.slug);
165
+ const slugPrefix = isSlugValidSegment ? 'app' : '';
166
+ const bundleIdentifier = `com.${userPrefix}${stripInvalidCharactersForBundleIdentifier(app.ownerAccount.name)}.${slugPrefix}${stripInvalidCharactersForBundleIdentifier(app.slug)}`;
167
+ const updateUrl = (0, api_1.getEASUpdateURL)(app.id, /* manifestHostOverride */ null);
168
+ const { expo: baseExpoConfig } = await fs_extra_1.default.readJson(path_1.default.join(projectDir, 'app.json'));
169
+ const expoConfig = {
170
+ name: app.name ?? app.slug,
171
+ slug: app.slug,
172
+ scheme: stripInvalidCharactersForBundleIdentifier(app.name ?? app.slug),
173
+ extra: {
174
+ eas: {
175
+ projectId: app.id,
176
+ },
177
+ },
178
+ owner: app.ownerAccount.name,
179
+ updates: {
180
+ url: updateUrl,
181
+ },
182
+ runtimeVersion: {
183
+ policy: 'appVersion',
184
+ },
185
+ ios: {
186
+ bundleIdentifier,
187
+ },
188
+ android: {
189
+ package: bundleIdentifier,
190
+ },
191
+ };
192
+ const mergedConfig = (0, ts_deepmerge_1.default)(baseExpoConfig, expoConfig);
193
+ const appJsonPath = path_1.default.join(projectDir, 'app.json');
194
+ await fs_extra_1.default.writeJson(appJsonPath, { expo: mergedConfig }, { spaces: 2 });
195
+ log_1.default.withTick(`Generated ${chalk_1.default.bold('app.json')}. ${(0, log_1.learnMore)('https://docs.expo.dev/versions/latest/config/app/')}`);
196
+ log_1.default.log();
197
+ }
198
+ exports.generateAppConfigAsync = generateAppConfigAsync;
199
+ async function generateEasConfigAsync(projectDir) {
200
+ const easBuildGitHubConfig = {
201
+ android: {
202
+ image: 'latest',
203
+ },
204
+ ios: {
205
+ image: 'latest',
206
+ },
207
+ };
208
+ const easJson = {
209
+ cli: {
210
+ version: `>= ${easCli_1.easCliVersion}`,
211
+ appVersionSource: eas_json_1.AppVersionSource.REMOTE,
212
+ },
213
+ build: {
214
+ development: {
215
+ developmentClient: true,
216
+ distribution: 'internal',
217
+ ...easBuildGitHubConfig,
218
+ },
219
+ 'development-simulator': {
220
+ extends: 'development',
221
+ ios: {
222
+ simulator: true,
223
+ },
224
+ },
225
+ preview: {
226
+ distribution: 'internal',
227
+ channel: 'main',
228
+ ...easBuildGitHubConfig,
229
+ },
230
+ production: {
231
+ channel: 'production',
232
+ autoIncrement: true,
233
+ ...easBuildGitHubConfig,
234
+ },
235
+ },
236
+ submit: {
237
+ production: {},
238
+ },
239
+ };
240
+ const easJsonPath = path_1.default.join(projectDir, 'eas.json');
241
+ await fs_extra_1.default.writeJson(easJsonPath, easJson, { spaces: 2 });
242
+ log_1.default.withTick(`Generated ${chalk_1.default.bold('eas.json')}. ${(0, log_1.learnMore)('https://docs.expo.dev/build-reference/eas-json/')}`);
243
+ log_1.default.log();
244
+ }
245
+ exports.generateEasConfigAsync = generateEasConfigAsync;
246
+ async function updatePackageJsonAsync(projectDir) {
247
+ const packageJsonPath = path_1.default.join(projectDir, 'package.json');
248
+ const packageJson = await fs_extra_1.default.readJson(packageJsonPath);
249
+ if (!packageJson.scripts) {
250
+ packageJson.scripts = {};
251
+ }
252
+ packageJson.scripts.preview = 'npx eas-cli@latest workflow:run publish-preview-update.yml';
253
+ packageJson.scripts['development-builds'] =
254
+ 'npx eas-cli@latest workflow:run create-development-builds.yml';
255
+ packageJson.scripts.deploy = 'npx eas-cli@latest workflow:run deploy-to-production.yml';
256
+ await fs_extra_1.default.writeJson(packageJsonPath, packageJson, { spaces: 2 });
257
+ log_1.default.withTick('Updated package.json with scripts');
258
+ log_1.default.log();
259
+ }
260
+ exports.updatePackageJsonAsync = updatePackageJsonAsync;
261
+ async function copyProjectTemplatesAsync(projectDir) {
262
+ const templatesSourceDir = path_1.default.join(__dirname, 'templates', '.eas', 'workflows');
263
+ const easWorkflowsTargetDir = path_1.default.join(projectDir, '.eas', 'workflows');
264
+ await fs_extra_1.default.copy(templatesSourceDir, easWorkflowsTargetDir, {
265
+ overwrite: true,
266
+ errorOnExist: false,
267
+ });
268
+ log_1.default.withTick('Created EAS workflow files');
269
+ log_1.default.log();
270
+ }
271
+ exports.copyProjectTemplatesAsync = copyProjectTemplatesAsync;
272
+ async function mergeReadmeAsync(projectDir) {
273
+ const readmeTemplatePath = path_1.default.join(__dirname, 'templates', 'readme-additions.md');
274
+ const projectReadmePath = path_1.default.join(projectDir, 'README.md');
275
+ const readmeAdditions = await fs_extra_1.default.readFile(readmeTemplatePath, 'utf8');
276
+ const existingReadme = await fs_extra_1.default.readFile(projectReadmePath, 'utf8');
277
+ const targetSection = '## Get a fresh project';
278
+ const sectionIndex = existingReadme.indexOf(targetSection);
279
+ let mergedReadme;
280
+ if (sectionIndex !== -1) {
281
+ // Insert before "## Get a fresh project" section
282
+ const beforeSection = existingReadme.substring(0, sectionIndex).trim();
283
+ const afterSection = existingReadme.substring(sectionIndex);
284
+ mergedReadme = beforeSection + '\n\n' + readmeAdditions.trim() + '\n\n' + afterSection;
285
+ }
286
+ else {
287
+ // Append to the end if section doesn't exist
288
+ mergedReadme = existingReadme.trim() + '\n\n' + readmeAdditions.trim() + '\n';
289
+ }
290
+ await fs_extra_1.default.writeFile(projectReadmePath, mergedReadme);
291
+ log_1.default.withTick('Updated README.md with EAS configuration details');
292
+ log_1.default.log();
293
+ }
294
+ exports.mergeReadmeAsync = mergeReadmeAsync;
295
+ async function initializeGitRepositoryAsync(projectDir) {
296
+ await fs_extra_1.default.remove(path_1.default.join(projectDir, '.git'));
297
+ const commands = [['init'], ['add', '.'], ['commit', '-m', 'Initial commit']];
298
+ for (const args of commands) {
299
+ await (0, runCommand_1.runCommandAsync)({
300
+ cwd: projectDir,
301
+ command: 'git',
302
+ args,
303
+ });
304
+ log_1.default.log();
305
+ }
306
+ }
307
+ exports.initializeGitRepositoryAsync = initializeGitRepositoryAsync;
308
+ const formatScriptCommand = (script, packageManager) => {
309
+ if (packageManager === 'npm') {
310
+ return `npm run ${script}`;
311
+ }
312
+ return `${packageManager} ${script}`;
313
+ };
314
+ exports.formatScriptCommand = formatScriptCommand;
315
+ class New extends EasCommand_1.default {
316
+ static aliases = ['new'];
317
+ static description = "create a new project set up with Expo's services.";
318
+ static flags = {};
319
+ static hidden = true;
320
+ static args = [{ name: 'TARGET_PROJECT_DIRECTORY' }];
321
+ static contextDefinition = {
322
+ ...this.ContextOptions.LoggedIn,
323
+ };
324
+ async runAsync() {
325
+ const { args } = await this.parse(New);
326
+ const { loggedIn: { actor, graphqlClient }, } = await this.getContextAsync(New, { nonInteractive: false });
327
+ if (actor.__typename === 'Robot') {
328
+ throw new Error('This command is not available for robot users. Make sure you are not using a robot token and try again.');
329
+ }
330
+ log_1.default.warn('This command is not yet implemented. It will create a new project, but it will not be fully configured.');
331
+ log_1.default.log(`👋 Welcome to Expo, ${actor.username}!`);
332
+ log_1.default.newLine();
333
+ const targetProjectDirectory = await promptForTargetDirectoryAsync(args.TARGET_PROJECT_DIRECTORY);
334
+ const projectDirectory = await cloneTemplateAsync(targetProjectDirectory);
335
+ const packageManager = await installProjectDependenciesAsync(projectDirectory);
336
+ const projectId = await createProjectAsync(graphqlClient, actor, projectDirectory);
337
+ const app = await AppQuery_1.AppQuery.byIdAsync(graphqlClient, projectId);
338
+ await generateConfigFilesAsync(projectDirectory, app);
339
+ await initializeGitRepositoryAsync(projectDirectory);
340
+ log_1.default.log('🎉 We finished creating your new project.');
341
+ log_1.default.log('Next steps:');
342
+ log_1.default.withInfo(`Run \`cd ${projectDirectory}\` to navigate to your project.`);
343
+ log_1.default.withInfo(`Run \`${(0, exports.formatScriptCommand)('preview', packageManager)}\` to create a preview build on EAS. ${(0, log_1.learnMore)('https://docs.expo.dev/eas/workflows/examples/publish-preview-update/')}`);
344
+ log_1.default.withInfo(`Run \`${(0, exports.formatScriptCommand)('start', packageManager)}\` to start developing locally. ${(0, log_1.learnMore)('https://docs.expo.dev/get-started/start-developing/')}`);
345
+ log_1.default.withInfo(`See the README.md for more information about your project.`);
346
+ log_1.default.newLine();
347
+ }
348
+ }
349
+ exports.default = New;
@@ -100,7 +100,7 @@ class Onboarding extends EasCommand_1.default {
100
100
  }))
101
101
  ? 'ssh'
102
102
  : 'https';
103
- log_1.default.log(chalk_1.default.dim(`We detected that ${cloneMethod} is your preffered git clone method`));
103
+ log_1.default.log(chalk_1.default.dim(`We detected that ${cloneMethod} is your preferred git clone method`));
104
104
  log_1.default.log();
105
105
  const { targetProjectDir: finalTargetProjectDirectory } = await (0, git_1.runGitCloneAsync)({
106
106
  githubUsername,
@@ -8,7 +8,6 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
8
8
  const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
9
9
  // import { getExpoWebsiteBaseUrl } from '../../api';
10
10
  const queries_1 = require("../../branch/queries");
11
- const graphql_1 = require("../../build/graphql");
12
11
  const repository_1 = require("../../build/utils/repository");
13
12
  const url_1 = require("../../build/utils/url");
14
13
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
@@ -324,10 +323,7 @@ class UpdatePublish extends EasCommand_1.default {
324
323
  const transformedFingerprintInfoGroup = Object.entries(fingerprintInfoGroup).reduce((prev, [platform, fingerprintInfo]) => {
325
324
  return {
326
325
  ...prev,
327
- [platform]: {
328
- ...fingerprintInfo,
329
- fingerprintSource: (0, graphql_1.transformFingerprintSource)(fingerprintInfo.fingerprintSource),
330
- },
326
+ [platform]: fingerprintInfo,
331
327
  };
332
328
  }, {});
333
329
  const assetMapGroup = assetMapSource
@@ -132,8 +132,6 @@ class WorkflowRun extends EasCommand_1.default {
132
132
  if (error instanceof core_2.CombinedError) {
133
133
  workflowFile_1.WorkflowFile.maybePrintWorkflowFileValidationErrors({
134
134
  error,
135
- accountName: account.name,
136
- projectName,
137
135
  });
138
136
  throw error;
139
137
  }
@@ -11,6 +11,7 @@ export declare class WorkflowValidate extends EasCommand {
11
11
  };
12
12
  static contextDefinition: {
13
13
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
14
+ projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
14
15
  projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
15
16
  getDynamicPublicProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPublicProjectConfigContextField;
16
17
  getDynamicPrivateProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPrivateProjectConfigContextField;
@@ -2,14 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WorkflowValidate = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const core_1 = require("@urql/core");
6
- const YAML = tslib_1.__importStar(require("yaml"));
7
5
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
6
  const flags_1 = require("../../commandUtils/flags");
9
- const WorkflowRevisionMutation_1 = require("../../graphql/mutations/WorkflowRevisionMutation");
7
+ const validation_1 = require("../../commandUtils/workflow/validation");
10
8
  const log_1 = tslib_1.__importDefault(require("../../log"));
11
9
  const ora_1 = require("../../ora");
12
- const projectUtils_1 = require("../../project/projectUtils");
13
10
  const workflowFile_1 = require("../../utils/workflowFile");
14
11
  class WorkflowValidate extends EasCommand_1.default {
15
12
  static description = 'validate a workflow configuration yaml file';
@@ -26,56 +23,28 @@ class WorkflowValidate extends EasCommand_1.default {
26
23
  static contextDefinition = {
27
24
  ...this.ContextOptions.DynamicProjectConfig,
28
25
  ...this.ContextOptions.ProjectDir,
26
+ ...this.ContextOptions.ProjectId,
29
27
  ...this.ContextOptions.LoggedIn,
30
28
  };
31
29
  async runAsync() {
32
30
  const { args: { path: filePath }, flags, } = await this.parse(WorkflowValidate);
33
- const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, projectDir, } = await this.getContextAsync(WorkflowValidate, {
34
- nonInteractive: flags['non-interactive'],
35
- withServerSideEnvironment: null,
36
- });
37
- const { projectId, exp: { slug: projectName }, } = await getDynamicPrivateProjectConfigAsync();
38
- const account = await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId);
39
31
  const spinner = (0, ora_1.ora)().start('Validating the workflow YAML file…');
40
32
  try {
33
+ const { loggedIn: { graphqlClient }, projectDir, projectId, } = await this.getContextAsync(WorkflowValidate, {
34
+ nonInteractive: flags['non-interactive'],
35
+ withServerSideEnvironment: null,
36
+ });
41
37
  const workflowFileContents = await workflowFile_1.WorkflowFile.readWorkflowFileContentsAsync({
42
38
  projectDir,
43
39
  filePath,
44
40
  });
45
41
  log_1.default.log(`Using workflow file from ${workflowFileContents.filePath}`);
46
- const parsedYaml = YAML.parse(workflowFileContents.yamlConfig);
47
- // Check if the parsed result is empty or null
48
- if (parsedYaml === null ||
49
- parsedYaml === undefined ||
50
- (typeof parsedYaml === 'object' && Object.keys(parsedYaml).length === 0)) {
51
- throw new Error('YAML file is empty or contains only comments.');
52
- }
53
- await WorkflowRevisionMutation_1.WorkflowRevisionMutation.validateWorkflowYamlConfigAsync(graphqlClient, {
54
- appId: projectId,
55
- yamlConfig: workflowFileContents.yamlConfig,
56
- });
42
+ await (0, validation_1.validateWorkflowFileAsync)(workflowFileContents, projectDir, graphqlClient, projectId);
57
43
  spinner.succeed('Workflow configuration YAML is valid.');
58
44
  }
59
45
  catch (error) {
60
46
  spinner.fail('Workflow configuration YAML is not valid.');
61
- if (error instanceof YAML.YAMLParseError) {
62
- log_1.default.error(`YAML syntax error: ${error.message}`);
63
- }
64
- else if (error instanceof core_1.CombinedError) {
65
- workflowFile_1.WorkflowFile.maybePrintWorkflowFileValidationErrors({
66
- error,
67
- accountName: account.name,
68
- projectName,
69
- });
70
- throw error;
71
- }
72
- else if (error instanceof Error) {
73
- log_1.default.error(error.message);
74
- }
75
- else {
76
- log_1.default.error(`Unexpected error: ${String(error)}`);
77
- }
78
- throw error;
47
+ (0, validation_1.logWorkflowValidationErrors)(error);
79
48
  }
80
49
  }
81
50
  }
@@ -73,7 +73,7 @@ function getCapabilitiesToEnable(currentRemoteCapabilities, entitlements, additi
73
73
  });
74
74
  const { op } = operation;
75
75
  if (log_1.default.isDebug) {
76
- log_1.default.log(`Will ${op} remote capability: ${key} (${staticCapabilityInfo.name}.`);
76
+ log_1.default.log(`Will ${op} remote capability: ${key} (${staticCapabilityInfo.name}).`);
77
77
  }
78
78
  if (op === 'enable') {
79
79
  enabledCapabilityNames.push(staticCapabilityInfo.name);
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const apple_utils_1 = require("@expo/apple-utils");
6
6
  const invariant_1 = require("graphql/jsutils/invariant");
7
7
  const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
8
+ const log_1 = tslib_1.__importDefault(require("../../../log"));
8
9
  const validateBooleanOptions = (options) => {
9
10
  return typeof options === 'boolean';
10
11
  };
@@ -51,16 +52,25 @@ const capabilityWithSettingsSyncOperation = ({ existingRemote, entitlementValue,
51
52
  if (!existingRemote) {
52
53
  return enableOp;
53
54
  }
54
- (0, invariant_1.invariant)('enabled' in existingRemote.attributes, `Expected "enabled" attribute in ${existingRemote.id}`);
55
- const existingEnabled = existingRemote.attributes.enabled === true;
56
- const newOption = entitlementValue ? apple_utils_1.CapabilityTypeOption.ON : apple_utils_1.CapabilityTypeOption.OFF;
57
- // If both are enabled and the existing one has settings, skip the update
58
- if (existingEnabled && entitlementValue && existingRemote.attributes.settings) {
55
+ const { attributes, id } = existingRemote;
56
+ if ('enabled' in attributes) {
57
+ // the `enabled` field should be available as per https://developer.apple.com/documentation/appstoreconnectapi/capabilitysetting
58
+ const existingEnabled = attributes.enabled === true;
59
+ // If both are enabled and the existing one has settings, skip the update
60
+ if (existingEnabled && entitlementValue && attributes.settings) {
61
+ return skipOp;
62
+ }
63
+ const newOption = entitlementValue ? apple_utils_1.CapabilityTypeOption.ON : apple_utils_1.CapabilityTypeOption.OFF;
64
+ // If the states don't match, we need to update
65
+ const newEnabled = newOption === apple_utils_1.CapabilityTypeOption.ON;
66
+ return existingEnabled === newEnabled ? skipOp : { op: 'enable', option: newOption };
67
+ }
68
+ else {
69
+ if (log_1.default.isDebug) {
70
+ log_1.default.log(`Expected the "enabled" attribute in ${id} but it was not present (attributes: ${JSON.stringify(attributes, null, 2)}). Will skip syncing this capability.`);
71
+ }
59
72
  return skipOp;
60
73
  }
61
- // If the states don't match, we need to update
62
- const newEnabled = newOption === apple_utils_1.CapabilityTypeOption.ON;
63
- return existingEnabled === newEnabled ? skipOp : { op: 'enable', option: newOption };
64
74
  };
65
75
  // NOTE(Bacon): From manually toggling values in Xcode and checking the git diff and network requests.
66
76
  // Last Updated: July 22nd, 2021
@@ -1093,7 +1093,6 @@ export type AndroidSubmissionConfig = {
1093
1093
  };
1094
1094
  export type AndroidSubmissionConfigInput = {
1095
1095
  applicationIdentifier?: InputMaybe<Scalars['String']['input']>;
1096
- archiveUrl?: InputMaybe<Scalars['String']['input']>;
1097
1096
  changelog?: InputMaybe<Scalars['String']['input']>;
1098
1097
  changesNotSentForReview?: InputMaybe<Scalars['Boolean']['input']>;
1099
1098
  googleServiceAccountKeyId?: InputMaybe<Scalars['String']['input']>;
@@ -3028,7 +3027,6 @@ export type CreateAndConfigureRepositoryInput = {
3028
3027
  export type CreateAndroidSubmissionInput = {
3029
3028
  appId: Scalars['ID']['input'];
3030
3029
  archiveSource?: InputMaybe<SubmissionArchiveSourceInput>;
3031
- archiveUrl?: InputMaybe<Scalars['String']['input']>;
3032
3030
  config: AndroidSubmissionConfigInput;
3033
3031
  submittedBuildId?: InputMaybe<Scalars['ID']['input']>;
3034
3032
  };
@@ -3096,7 +3094,6 @@ export type CreateGitHubRepositorySettingsInput = {
3096
3094
  export type CreateIosSubmissionInput = {
3097
3095
  appId: Scalars['ID']['input'];
3098
3096
  archiveSource?: InputMaybe<SubmissionArchiveSourceInput>;
3099
- archiveUrl?: InputMaybe<Scalars['String']['input']>;
3100
3097
  config: IosSubmissionConfigInput;
3101
3098
  submittedBuildId?: InputMaybe<Scalars['ID']['input']>;
3102
3099
  };
@@ -3108,7 +3105,6 @@ export type CreateSentryProjectInput = {
3108
3105
  export type CreateSharedEnvironmentVariableInput = {
3109
3106
  environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>;
3110
3107
  fileName?: InputMaybe<Scalars['String']['input']>;
3111
- isGlobal?: InputMaybe<Scalars['Boolean']['input']>;
3112
3108
  name: Scalars['String']['input'];
3113
3109
  overwrite?: InputMaybe<Scalars['Boolean']['input']>;
3114
3110
  type?: InputMaybe<EnvironmentSecretType>;
@@ -4476,7 +4472,6 @@ export type IosSubmissionConfig = {
4476
4472
  export type IosSubmissionConfigInput = {
4477
4473
  appleAppSpecificPassword?: InputMaybe<Scalars['String']['input']>;
4478
4474
  appleIdUsername?: InputMaybe<Scalars['String']['input']>;
4479
- archiveUrl?: InputMaybe<Scalars['String']['input']>;
4480
4475
  ascApiKey?: InputMaybe<AscApiKeyInput>;
4481
4476
  ascApiKeyId?: InputMaybe<Scalars['String']['input']>;
4482
4477
  ascAppIdentifier: Scalars['String']['input'];
@@ -6193,7 +6188,6 @@ export type UpdateEnvironmentVariableInput = {
6193
6188
  environments?: InputMaybe<Array<EnvironmentVariableEnvironment>>;
6194
6189
  fileName?: InputMaybe<Scalars['String']['input']>;
6195
6190
  id: Scalars['ID']['input'];
6196
- isGlobal?: InputMaybe<Scalars['Boolean']['input']>;
6197
6191
  name?: InputMaybe<Scalars['String']['input']>;
6198
6192
  type?: InputMaybe<EnvironmentSecretType>;
6199
6193
  value?: InputMaybe<Scalars['String']['input']>;
@@ -1,30 +1,11 @@
1
1
  import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
2
- import { EnvironmentSecretType, EnvironmentVariableEnvironment, EnvironmentVariableFragment, EnvironmentVariableVisibility } from '../generated';
3
- type CreateVariableArgs = {
4
- value: string;
5
- name: string;
6
- visibility: EnvironmentVariableVisibility;
7
- environments: EnvironmentVariableEnvironment[];
8
- type: EnvironmentSecretType;
9
- isGlobal?: boolean;
10
- fileName?: string;
11
- };
12
- export type EnvironmentVariablePushInput = {
13
- name: string;
14
- value: string;
15
- environments: EnvironmentVariableEnvironment[];
16
- visibility: EnvironmentVariableVisibility;
17
- overwrite?: boolean;
18
- };
2
+ import { CreateEnvironmentVariableInput, CreateSharedEnvironmentVariableInput, EnvironmentVariableFragment, UpdateEnvironmentVariableInput } from '../generated';
19
3
  export declare const EnvironmentVariableMutation: {
20
- createSharedVariableAsync(graphqlClient: ExpoGraphqlClient, input: CreateVariableArgs, accountId: string): Promise<EnvironmentVariableFragment>;
21
- createForAppAsync(graphqlClient: ExpoGraphqlClient, input: CreateVariableArgs, appId: string): Promise<EnvironmentVariableFragment>;
22
- updateAsync(graphqlClient: ExpoGraphqlClient, input: Partial<CreateVariableArgs> & {
23
- id: string;
24
- }): Promise<EnvironmentVariableFragment>;
4
+ createSharedVariableAsync(graphqlClient: ExpoGraphqlClient, input: CreateSharedEnvironmentVariableInput, accountId: string): Promise<EnvironmentVariableFragment>;
5
+ createForAppAsync(graphqlClient: ExpoGraphqlClient, input: CreateEnvironmentVariableInput, appId: string): Promise<EnvironmentVariableFragment>;
6
+ updateAsync(graphqlClient: ExpoGraphqlClient, input: UpdateEnvironmentVariableInput): Promise<EnvironmentVariableFragment>;
25
7
  deleteAsync(graphqlClient: ExpoGraphqlClient, id: string): Promise<{
26
8
  id: string;
27
9
  }>;
28
- createBulkEnvironmentVariablesForAppAsync(graphqlClient: ExpoGraphqlClient, input: EnvironmentVariablePushInput[], appId: string): Promise<boolean>;
10
+ createBulkEnvironmentVariablesForAppAsync(graphqlClient: ExpoGraphqlClient, input: CreateEnvironmentVariableInput[], appId: string): Promise<boolean>;
29
11
  };
30
- export {};
@@ -1,9 +1,8 @@
1
- import { FingerprintSource } from '@expo/eas-build-job';
2
1
  import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
3
- import { FingerprintFragment } from '../generated';
2
+ import { FingerprintFragment, FingerprintSourceInput } from '../generated';
4
3
  export declare const FingerprintMutation: {
5
4
  createFingerprintAsync(graphqlClient: ExpoGraphqlClient, appId: string, fingerprintData: {
6
5
  hash: string;
7
- source?: FingerprintSource;
6
+ source?: FingerprintSourceInput;
8
7
  }): Promise<FingerprintFragment>;
9
8
  };
package/build/log.js CHANGED
@@ -33,7 +33,8 @@ class Log {
33
33
  Log.consoleLog(...args);
34
34
  }
35
35
  else {
36
- nodeDebug(args);
36
+ Log.updateIsLastLineNewLine(args);
37
+ nodeDebug(...args);
37
38
  }
38
39
  }
39
40
  static gray(...args) {
@@ -1,3 +1,6 @@
1
- export declare function installDependenciesAsync({ projectDir, }: {
1
+ export type PackageManager = 'npm' | 'yarn' | 'pnpm';
2
+ export declare function promptForPackageManagerAsync(): Promise<PackageManager>;
3
+ export declare function installDependenciesAsync({ projectDir, packageManager, }: {
2
4
  projectDir: string;
5
+ packageManager?: PackageManager;
3
6
  }): Promise<void>;