eas-cli 16.20.4 → 16.22.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.
@@ -2,28 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WorkflowCreate = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
5
6
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
7
  const promises_1 = tslib_1.__importDefault(require("fs/promises"));
7
8
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
8
9
  const path_1 = tslib_1.__importDefault(require("path"));
9
- const prompts_1 = tslib_1.__importDefault(require("prompts"));
10
10
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
11
- const flags_1 = require("../../commandUtils/flags");
11
+ const buildProfileUtils_1 = require("../../commandUtils/workflow/buildProfileUtils");
12
+ const creation_1 = require("../../commandUtils/workflow/creation");
13
+ const validation_1 = require("../../commandUtils/workflow/validation");
12
14
  const log_1 = tslib_1.__importDefault(require("../../log"));
15
+ const prompts_1 = require("../../prompts");
16
+ const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
13
17
  const workflowFile_1 = require("../../utils/workflowFile");
14
- const DEFAULT_WORKFLOW_NAME = 'workflow.yml';
15
- const HELLO_WORLD_TEMPLATE = `name: Hello World
16
-
17
- on:
18
- push:
19
- branches: ['*']
20
-
21
- jobs:
22
- hello_world:
23
- steps:
24
- - uses: eas/checkout
25
- - run: echo "Hello, World"
26
- `;
27
18
  class WorkflowCreate extends EasCommand_1.default {
28
19
  static description = 'create a new workflow configuration YAML file';
29
20
  static args = [
@@ -34,65 +25,148 @@ class WorkflowCreate extends EasCommand_1.default {
34
25
  },
35
26
  ];
36
27
  static flags = {
37
- ...flags_1.EASNonInteractiveFlag,
28
+ 'skip-validation': core_1.Flags.boolean({
29
+ description: 'If set, the workflow file will not be validated before being created',
30
+ default: false,
31
+ }),
38
32
  };
39
33
  static contextDefinition = {
34
+ ...this.ContextOptions.DynamicProjectConfig,
40
35
  ...this.ContextOptions.ProjectDir,
36
+ ...this.ContextOptions.LoggedIn,
41
37
  };
42
38
  async runAsync() {
43
39
  const { args: { name: argFileName }, flags, } = await this.parse(WorkflowCreate);
44
- const { projectDir } = await this.getContextAsync(WorkflowCreate, {
45
- nonInteractive: flags['non-interactive'],
46
- });
47
- let fileName = argFileName;
48
- if (!fileName) {
49
- const response = await (0, prompts_1.default)({
50
- type: 'text',
51
- name: 'fileName',
52
- message: 'What would you like to name your workflow file?',
53
- initial: DEFAULT_WORKFLOW_NAME,
54
- validate: value => {
55
- try {
56
- workflowFile_1.WorkflowFile.validateYamlExtension(value);
57
- return true;
40
+ try {
41
+ const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, projectDir, } = await this.getContextAsync(WorkflowCreate, {
42
+ nonInteractive: false,
43
+ withServerSideEnvironment: null,
44
+ });
45
+ const { exp: originalExpoConfig, projectId } = await getDynamicPrivateProjectConfigAsync();
46
+ let expoConfig = originalExpoConfig;
47
+ let workflowStarter;
48
+ while (!workflowStarter) {
49
+ workflowStarter = await chooseTemplateAsync();
50
+ switch (workflowStarter.name) {
51
+ case creation_1.WorkflowStarterName.BUILD:
52
+ case creation_1.WorkflowStarterName.DEPLOY:
53
+ case creation_1.WorkflowStarterName.UPDATE: {
54
+ const shouldProceed = await (0, buildProfileUtils_1.runBuildConfigureIfNeededAsync)({
55
+ projectDir,
56
+ expoConfig,
57
+ });
58
+ if (!shouldProceed) {
59
+ workflowStarter = undefined;
60
+ continue;
61
+ }
62
+ break;
58
63
  }
59
- catch (error) {
60
- return error instanceof Error ? error.message : 'Invalid file name';
64
+ default:
65
+ break;
66
+ }
67
+ switch (workflowStarter.name) {
68
+ case creation_1.WorkflowStarterName.DEPLOY:
69
+ case creation_1.WorkflowStarterName.UPDATE: {
70
+ const shouldProceed = await (0, buildProfileUtils_1.runUpdateConfigureIfNeededAsync)({
71
+ projectDir,
72
+ expoConfig,
73
+ });
74
+ if (!shouldProceed) {
75
+ workflowStarter = undefined;
76
+ continue;
77
+ }
78
+ // Need to refetch the Expo config because it may have changed
79
+ expoConfig = (await getDynamicPrivateProjectConfigAsync()).exp;
80
+ break;
61
81
  }
62
- },
63
- });
64
- if (!response.fileName) {
65
- log_1.default.warn('Workflow creation cancelled.');
66
- process.exit(0);
82
+ default:
83
+ break;
84
+ }
85
+ }
86
+ const { fileName, filePath } = await chooseFileNameAsync(argFileName, projectDir, workflowStarter);
87
+ // Customize the template if needed
88
+ workflowStarter = await (0, creation_1.customizeTemplateIfNeededAsync)(workflowStarter, projectDir, expoConfig);
89
+ log_1.default.debug(`Creating workflow file ${fileName} from template ${workflowStarter.name}`);
90
+ const yamlString = [
91
+ workflowStarter.header,
92
+ (0, validation_1.workflowContentsFromParsedYaml)(workflowStarter.template),
93
+ ].join('\n');
94
+ if (!flags['skip-validation']) {
95
+ await (0, validation_1.validateWorkflowFileAsync)({ yamlConfig: yamlString, filePath: fileName }, projectDir, graphqlClient, projectId);
96
+ }
97
+ await ensureWorkflowsDirectoryExistsAsync({ projectDir });
98
+ filePath && (await promises_1.default.writeFile(filePath, yamlString));
99
+ log_1.default.withTick(`Created ${chalk_1.default.bold(filePath)}`);
100
+ log_1.default.addNewLineIfNone();
101
+ log_1.default.log((0, creation_1.howToRunWorkflow)(fileName, workflowStarter));
102
+ // Next steps
103
+ if (workflowStarter.nextSteps && workflowStarter.nextSteps.length > 0) {
104
+ log_1.default.addNewLineIfNone();
105
+ log_1.default.log('Next steps:');
106
+ log_1.default.addNewLineIfNone();
107
+ log_1.default.log((0, formatFields_1.default)(workflowStarter.nextSteps.map((step, index) => ({
108
+ label: `${index + 1}.`,
109
+ value: step,
110
+ }))));
67
111
  }
68
- fileName = response.fileName;
69
- }
70
- try {
71
- await this.ensureWorkflowsDirectoryExistsAsync({ projectDir });
72
- await this.createWorkflowFileAsync({ fileName, projectDir });
73
112
  }
74
113
  catch (error) {
114
+ (0, validation_1.logWorkflowValidationErrors)(error);
75
115
  log_1.default.error('Failed to create workflow file.');
76
- throw error;
77
116
  }
78
117
  }
79
- async ensureWorkflowsDirectoryExistsAsync({ projectDir, }) {
118
+ }
119
+ exports.WorkflowCreate = WorkflowCreate;
120
+ async function ensureWorkflowsDirectoryExistsAsync({ projectDir, }) {
121
+ try {
122
+ await promises_1.default.access(path_1.default.join(projectDir, '.eas', 'workflows'));
123
+ }
124
+ catch {
125
+ await promises_1.default.mkdir(path_1.default.join(projectDir, '.eas', 'workflows'), { recursive: true });
126
+ log_1.default.withTick(`Created directory ${chalk_1.default.bold(path_1.default.join(projectDir, '.eas', 'workflows'))}`);
127
+ }
128
+ }
129
+ async function chooseTemplateAsync() {
130
+ const workflowStarter = (await (0, prompts_1.promptAsync)({
131
+ type: 'select',
132
+ name: 'starter',
133
+ message: 'Select a workflow template:',
134
+ choices: creation_1.workflowStarters.map(starter => ({
135
+ title: starter.displayName,
136
+ value: starter,
137
+ })),
138
+ })).starter;
139
+ return workflowStarter;
140
+ }
141
+ async function chooseFileNameAsync(initialValue, projectDir, workflowStarter) {
142
+ let fileName = initialValue;
143
+ let filePath = '';
144
+ while ((fileName?.length ?? 0) === 0) {
145
+ fileName = (await (0, prompts_1.promptAsync)({
146
+ type: 'text',
147
+ name: 'fileName',
148
+ message: 'What would you like to name your workflow file?',
149
+ initial: workflowStarter.defaultFileName,
150
+ })).fileName;
151
+ if (!fileName) {
152
+ fileName = undefined;
153
+ continue;
154
+ }
80
155
  try {
81
- await promises_1.default.access(path_1.default.join(projectDir, '.eas', 'workflows'));
156
+ workflowFile_1.WorkflowFile.validateYamlExtension(fileName);
82
157
  }
83
- catch {
84
- await promises_1.default.mkdir(path_1.default.join(projectDir, '.eas', 'workflows'), { recursive: true });
85
- log_1.default.withTick(`Created directory ${chalk_1.default.bold(path_1.default.join(projectDir, '.eas', 'workflows'))}`);
158
+ catch (error) {
159
+ log_1.default.error(error instanceof Error ? error.message : 'Invalid YAML file name extension');
160
+ fileName = undefined;
161
+ continue;
86
162
  }
87
- }
88
- async createWorkflowFileAsync({ fileName, projectDir, }) {
89
- workflowFile_1.WorkflowFile.validateYamlExtension(fileName);
90
- const filePath = path_1.default.join(projectDir, '.eas', 'workflows', fileName);
163
+ filePath = path_1.default.join(projectDir, '.eas', 'workflows', fileName);
91
164
  if (await fs_extra_1.default.pathExists(filePath)) {
92
- throw new Error(`Workflow file already exists: ${filePath}`);
165
+ log_1.default.error(`Workflow file already exists: ${filePath}`);
166
+ log_1.default.error('Please choose a different file name.');
167
+ log_1.default.newLine();
168
+ fileName = undefined;
93
169
  }
94
- await promises_1.default.writeFile(filePath, HELLO_WORLD_TEMPLATE);
95
- log_1.default.withTick(`Created ${chalk_1.default.bold(filePath)}`);
96
170
  }
171
+ return { fileName: fileName ?? '', filePath };
97
172
  }
98
- exports.WorkflowCreate = WorkflowCreate;
@@ -166,6 +166,7 @@ export type Account = {
166
166
  displayName?: Maybe<Scalars['String']['output']>;
167
167
  /** Environment secrets for an account */
168
168
  environmentSecrets: Array<EnvironmentSecret>;
169
+ environmentVariableEnvironments: Array<Scalars['EnvironmentVariableEnvironment']['output']>;
169
170
  /** Environment variables for an account */
170
171
  environmentVariables: Array<EnvironmentVariable>;
171
172
  /** Environment variables for an account with decrypted secret values */
@@ -1139,6 +1140,7 @@ export type App = Project & {
1139
1140
  devDomainName?: Maybe<AppDevDomainName>;
1140
1141
  /** Environment secrets for an app */
1141
1142
  environmentSecrets: Array<EnvironmentSecret>;
1143
+ environmentVariableEnvironments: Array<Scalars['EnvironmentVariableEnvironment']['output']>;
1142
1144
  /** Environment variables for an app */
1143
1145
  environmentVariables: Array<EnvironmentVariable>;
1144
1146
  /** Environment variables for an app with decrypted secret values */
@@ -12704,6 +12706,20 @@ export type AppByIdWorkflowsQuery = {
12704
12706
  fileName: string;
12705
12707
  createdAt: any;
12706
12708
  updatedAt: any;
12709
+ revisionsPaginated: {
12710
+ __typename?: 'WorkflowRevisionsConnection';
12711
+ edges: Array<{
12712
+ __typename?: 'WorkflowRevisionEdge';
12713
+ node: {
12714
+ __typename?: 'WorkflowRevision';
12715
+ id: string;
12716
+ blobSha: string;
12717
+ commitSha?: string | null;
12718
+ createdAt: any;
12719
+ yamlConfig: string;
12720
+ };
12721
+ }>;
12722
+ };
12707
12723
  }>;
12708
12724
  };
12709
12725
  };
@@ -15670,6 +15686,20 @@ export type WorkflowFragment = {
15670
15686
  fileName: string;
15671
15687
  createdAt: any;
15672
15688
  updatedAt: any;
15689
+ revisionsPaginated: {
15690
+ __typename?: 'WorkflowRevisionsConnection';
15691
+ edges: Array<{
15692
+ __typename?: 'WorkflowRevisionEdge';
15693
+ node: {
15694
+ __typename?: 'WorkflowRevision';
15695
+ id: string;
15696
+ blobSha: string;
15697
+ commitSha?: string | null;
15698
+ createdAt: any;
15699
+ yamlConfig: string;
15700
+ };
15701
+ }>;
15702
+ };
15673
15703
  };
15674
15704
  export type WorkflowJobFragment = {
15675
15705
  __typename?: 'WorkflowJob';
@@ -10,5 +10,16 @@ exports.WorkflowFragmentNode = (0, graphql_tag_1.default) `
10
10
  fileName
11
11
  createdAt
12
12
  updatedAt
13
+ revisionsPaginated(first: 1) {
14
+ edges {
15
+ node {
16
+ id
17
+ blobSha
18
+ commitSha
19
+ createdAt
20
+ yamlConfig
21
+ }
22
+ }
23
+ }
13
24
  }
14
25
  `;
@@ -14,6 +14,11 @@ export interface AssetFileEntry {
14
14
  }
15
15
  /** Collects assets from a given target path */
16
16
  export declare function collectAssetsAsync(assetPath: string | undefined, options: AssetMapOptions): Promise<AssetFileEntry[]>;
17
+ export interface RoutesConfigEntry {
18
+ headers?: Record<string, unknown>;
19
+ redirects?: Record<string, unknown>[];
20
+ }
21
+ export declare function getRoutesConfigAsync(assetPath: string | undefined): Promise<RoutesConfigEntry | null>;
17
22
  /** Mapping of normalized file paths to a SHA512 hash */
18
23
  export type AssetMap = Record<string, string | {
19
24
  sha512: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.packFilesIterableAsync = exports.listWorkerFilesAsync = exports.createManifestAsync = exports.assetsToAssetsMap = exports.collectAssetsAsync = void 0;
3
+ exports.packFilesIterableAsync = exports.listWorkerFilesAsync = exports.createManifestAsync = exports.assetsToAssetsMap = exports.getRoutesConfigAsync = exports.collectAssetsAsync = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const env_1 = require("@expo/env");
6
6
  const mime_1 = tslib_1.__importDefault(require("mime"));
@@ -12,6 +12,7 @@ const node_path_1 = tslib_1.__importDefault(require("node:path"));
12
12
  const promises_1 = require("node:stream/promises");
13
13
  const tar_stream_1 = require("tar-stream");
14
14
  const EnvironmentVariablesQuery_1 = require("../graphql/queries/EnvironmentVariablesQuery");
15
+ const EXPO_ROUTES_PATHS = new Set(['_expo/routes.json', '_expo/.routes.json', '.expo-routes.json']);
15
16
  /** Returns whether a file or folder is ignored */
16
17
  function isIgnoredName(name) {
17
18
  switch (name) {
@@ -89,6 +90,9 @@ async function collectAssetsAsync(assetPath, options) {
89
90
  if (file.size > options.maxFileSize) {
90
91
  throw new Error(`Upload of "${file.normalizedPath}" aborted: File size is greater than the upload limit (>500MB)`);
91
92
  }
93
+ else if (EXPO_ROUTES_PATHS.has(file.normalizedPath)) {
94
+ continue;
95
+ }
92
96
  const sha512$ = computeSha512HashAsync(file.path);
93
97
  const contentType$ = determineMimeTypeAsync(file.path);
94
98
  assets.push({
@@ -103,6 +107,25 @@ async function collectAssetsAsync(assetPath, options) {
103
107
  return assets;
104
108
  }
105
109
  exports.collectAssetsAsync = collectAssetsAsync;
110
+ async function getRoutesConfigAsync(assetPath) {
111
+ if (assetPath) {
112
+ for (const candidatePath of EXPO_ROUTES_PATHS) {
113
+ const targetPath = node_path_1.default.resolve(assetPath, candidatePath);
114
+ let json;
115
+ try {
116
+ json = JSON.parse(await node_fs_1.default.promises.readFile(targetPath, 'utf8'));
117
+ }
118
+ catch {
119
+ continue;
120
+ }
121
+ if (typeof json === 'object' && json) {
122
+ return json;
123
+ }
124
+ }
125
+ }
126
+ return null;
127
+ }
128
+ exports.getRoutesConfigAsync = getRoutesConfigAsync;
106
129
  /** Converts array of asset entries into AssetMap (as sent to deployment-api) */
107
130
  function assetsToAssetsMap(assets) {
108
131
  return assets.reduce((map, entry) => {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "16.20.4",
2
+ "version": "16.22.0",
3
3
  "commands": {
4
4
  "analytics": {
5
5
  "id": "analytics",
@@ -4153,10 +4153,10 @@
4153
4153
  "pluginType": "core",
4154
4154
  "aliases": [],
4155
4155
  "flags": {
4156
- "non-interactive": {
4157
- "name": "non-interactive",
4156
+ "skip-validation": {
4157
+ "name": "skip-validation",
4158
4158
  "type": "boolean",
4159
- "description": "Run the command in non-interactive mode.",
4159
+ "description": "If set, the workflow file will not be validated before being created",
4160
4160
  "allowNo": false
4161
4161
  }
4162
4162
  },
@@ -4168,7 +4168,10 @@
4168
4168
  }
4169
4169
  },
4170
4170
  "contextDefinition": {
4171
- "projectDir": {}
4171
+ "getDynamicPublicProjectConfigAsync": {},
4172
+ "getDynamicPrivateProjectConfigAsync": {},
4173
+ "projectDir": {},
4174
+ "loggedIn": {}
4172
4175
  }
4173
4176
  },
4174
4177
  "workflow:list": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eas-cli",
3
3
  "description": "EAS command line tool",
4
- "version": "16.20.4",
4
+ "version": "16.22.0",
5
5
  "author": "Expo <support@expo.dev>",
6
6
  "bin": {
7
7
  "eas": "./bin/run"
@@ -241,5 +241,5 @@
241
241
  "node": "20.11.0",
242
242
  "yarn": "1.22.21"
243
243
  },
244
- "gitHead": "5b9734b60b61012d458ade299f9f4447ad8fa376"
244
+ "gitHead": "72cff1df1a5db334d2b12461532f483a7a35e2fa"
245
245
  }