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.
- package/README.md +87 -87
- package/build/commandUtils/workflow/buildProfileUtils.d.ts +36 -0
- package/build/commandUtils/workflow/buildProfileUtils.js +210 -0
- package/build/commandUtils/workflow/creation.d.ts +20 -0
- package/build/commandUtils/workflow/creation.js +462 -0
- package/build/commandUtils/workflow/validation.d.ts +4 -0
- package/build/commandUtils/workflow/validation.js +8 -4
- package/build/commands/deploy/index.js +8 -1
- package/build/commands/workflow/create.d.ts +4 -3
- package/build/commands/workflow/create.js +130 -56
- package/build/graphql/generated.d.ts +30 -0
- package/build/graphql/types/Workflow.js +11 -0
- package/build/worker/assets.d.ts +5 -0
- package/build/worker/assets.js +24 -1
- package/oclif.manifest.json +8 -5
- package/package.json +2 -2
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
156
|
+
workflowFile_1.WorkflowFile.validateYamlExtension(fileName);
|
|
82
157
|
}
|
|
83
|
-
catch {
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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';
|
package/build/worker/assets.d.ts
CHANGED
|
@@ -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;
|
package/build/worker/assets.js
CHANGED
|
@@ -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) => {
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "16.
|
|
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
|
-
"
|
|
4157
|
-
"name": "
|
|
4156
|
+
"skip-validation": {
|
|
4157
|
+
"name": "skip-validation",
|
|
4158
4158
|
"type": "boolean",
|
|
4159
|
-
"description": "
|
|
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
|
-
"
|
|
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.
|
|
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": "
|
|
244
|
+
"gitHead": "72cff1df1a5db334d2b12461532f483a7a35e2fa"
|
|
245
245
|
}
|