eas-cli 12.2.0 → 12.4.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.
@@ -10,7 +10,7 @@ const EnvironmentVariablesQuery_1 = require("../../graphql/queries/EnvironmentVa
10
10
  const log_1 = tslib_1.__importDefault(require("../../log"));
11
11
  const prompts_1 = require("../../prompts");
12
12
  const prompts_2 = require("../../utils/prompts");
13
- class EnvironmentValuePull extends EasCommand_1.default {
13
+ class EnvironmentVariablePull extends EasCommand_1.default {
14
14
  async runAsync() {
15
15
  let { flags: { environment, path: targetPath, 'non-interactive': nonInteractive }, } = await this.parse(_a);
16
16
  if (!environment) {
@@ -43,17 +43,24 @@ class EnvironmentValuePull extends EasCommand_1.default {
43
43
  })
44
44
  .join('\n');
45
45
  await fs.writeFile(targetPath, filePrefix + envFileContent);
46
- log_1.default.log(`Pulled environment variables from ${environment} environment to ${targetPath}.`);
46
+ const secretEnvVariables = environmentVariables.filter((variable) => variable.value === null);
47
+ if (secretEnvVariables.length > 0) {
48
+ log_1.default.warn(`The eas env:pull command tried to pull environment variables with "secret" visibility. The variables with "secret" visibility are not available for reading, therefore thet were marked as "*****" in the generated .env file. Provide values for these manually in ${targetPath} if needed. Skipped variables: ${secretEnvVariables
49
+ .map(v => v.name)
50
+ .join('\n')}`);
51
+ log_1.default.warn();
52
+ }
53
+ log_1.default.log(`Pulled environment variables from ${environment.toLowerCase()} environment to ${targetPath}.`);
47
54
  }
48
55
  }
49
- _a = EnvironmentValuePull;
50
- EnvironmentValuePull.description = 'pull env file';
51
- EnvironmentValuePull.hidden = true;
52
- EnvironmentValuePull.contextDefinition = {
56
+ _a = EnvironmentVariablePull;
57
+ EnvironmentVariablePull.description = 'pull env file';
58
+ EnvironmentVariablePull.hidden = true;
59
+ EnvironmentVariablePull.contextDefinition = {
53
60
  ..._a.ContextOptions.ProjectConfig,
54
61
  ..._a.ContextOptions.LoggedIn,
55
62
  };
56
- EnvironmentValuePull.flags = {
63
+ EnvironmentVariablePull.flags = {
57
64
  ...flags_1.EASEnvironmentFlag,
58
65
  ...flags_1.EASNonInteractiveFlag,
59
66
  path: core_1.Flags.string({
@@ -61,4 +68,4 @@ EnvironmentValuePull.flags = {
61
68
  default: '.env.local',
62
69
  }),
63
70
  };
64
- exports.default = EnvironmentValuePull;
71
+ exports.default = EnvironmentVariablePull;
@@ -0,0 +1,22 @@
1
+ import EasCommand from '../../commandUtils/EasCommand';
2
+ export default class WorkerAlias extends EasCommand {
3
+ static description: string;
4
+ static aliases: string[];
5
+ static hidden: boolean;
6
+ static state: string;
7
+ static flags: {
8
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ prod: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ alias: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
12
+ id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
13
+ };
14
+ static contextDefinition: {
15
+ loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
16
+ projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
17
+ getDynamicPublicProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPublicProjectConfigContextField;
18
+ getDynamicPrivateProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPrivateProjectConfigContextField;
19
+ };
20
+ runAsync(): Promise<void>;
21
+ private sanitizeFlags;
22
+ }
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
+ const flags_1 = require("../../commandUtils/flags");
9
+ const log_1 = tslib_1.__importDefault(require("../../log"));
10
+ const ora_1 = require("../../ora");
11
+ const prompts_1 = require("../../prompts");
12
+ const json_1 = require("../../utils/json");
13
+ const deployment_1 = require("../../worker/deployment");
14
+ const logs_1 = require("../../worker/utils/logs");
15
+ class WorkerAlias extends EasCommand_1.default {
16
+ async runAsync() {
17
+ // NOTE(cedric): `Log.warn` uses `console.log`, which is incorrect when running with `--json`
18
+ // eslint-disable-next-line no-console
19
+ console.warn(chalk_1.default.yellow('EAS Worker Deployments are in beta and subject to breaking changes.'));
20
+ const { flags: rawFlags } = await this.parse(_a);
21
+ const flags = this.sanitizeFlags(rawFlags);
22
+ if (flags.json) {
23
+ (0, json_1.enableJsonOutput)();
24
+ }
25
+ const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(_a, {
26
+ nonInteractive: true,
27
+ });
28
+ const { projectId } = await getDynamicPrivateProjectConfigAsync();
29
+ const aliasName = await resolveDeploymentAliasAsync(flags);
30
+ const deploymentId = await resolveDeploymentIdAsync({
31
+ ...flags,
32
+ graphqlClient,
33
+ projectId,
34
+ aliasName,
35
+ });
36
+ let progress = null;
37
+ let deploymentAlias = null;
38
+ if (aliasName) {
39
+ try {
40
+ progress = (0, ora_1.ora)((0, chalk_1.default) `Assigning alias {bold ${aliasName}} to deployment`).start();
41
+ deploymentAlias = await (0, deployment_1.assignWorkerDeploymentAliasAsync)({
42
+ graphqlClient,
43
+ appId: projectId,
44
+ deploymentId,
45
+ aliasName,
46
+ });
47
+ progress.text = (0, chalk_1.default) `Assigned alias {bold ${aliasName}} to deployment`;
48
+ }
49
+ catch (error) {
50
+ progress?.fail((0, chalk_1.default) `Failed to assign {bold ${aliasName}} alias to deployment`);
51
+ throw error;
52
+ }
53
+ }
54
+ let deploymentProdAlias = null;
55
+ if (flags.isProduction) {
56
+ try {
57
+ progress = (progress ?? (0, ora_1.ora)()).start((0, chalk_1.default) `Promoting deployment to {bold production}`);
58
+ deploymentProdAlias = await (0, deployment_1.assignWorkerDeploymentProductionAsync)({
59
+ graphqlClient,
60
+ appId: projectId,
61
+ deploymentId,
62
+ });
63
+ progress.text = (0, chalk_1.default) `Promoted deployment to {bold production}`;
64
+ }
65
+ catch (error) {
66
+ progress?.fail((0, chalk_1.default) `Failed to promote deployment to {bold production}`);
67
+ throw error;
68
+ }
69
+ }
70
+ progress?.succeed(!deploymentAlias
71
+ ? (0, chalk_1.default) `Promoted deployment to {bold production}`
72
+ : (0, chalk_1.default) `Promoted deployment to {bold production} with alias {bold ${deploymentAlias.aliasName}}`);
73
+ // Either use the alias, or production deployment information
74
+ const deployment = deploymentAlias?.workerDeployment ?? deploymentProdAlias?.workerDeployment;
75
+ if (flags.json) {
76
+ (0, json_1.printJsonOnlyOutput)((0, logs_1.formatWorkerDeploymentJson)({
77
+ projectId,
78
+ deployment: deployment,
79
+ aliases: [deploymentAlias].filter(Boolean),
80
+ production: deploymentProdAlias,
81
+ }));
82
+ return;
83
+ }
84
+ log_1.default.addNewLineIfNone();
85
+ log_1.default.log(`🎉 Your deployment is modified`);
86
+ log_1.default.addNewLineIfNone();
87
+ log_1.default.log((0, logs_1.formatWorkerDeploymentTable)({
88
+ projectId,
89
+ deployment: deployment,
90
+ aliases: [deploymentAlias].filter(Boolean),
91
+ production: deploymentProdAlias,
92
+ }));
93
+ }
94
+ sanitizeFlags(flags) {
95
+ return {
96
+ nonInteractive: flags['non-interactive'],
97
+ json: flags['json'],
98
+ aliasName: flags.alias?.trim().toLowerCase(),
99
+ deploymentIdentifier: flags.id?.trim().toLowerCase(),
100
+ isProduction: flags.prod,
101
+ };
102
+ }
103
+ }
104
+ _a = WorkerAlias;
105
+ WorkerAlias.description = 'Assign deployment aliases';
106
+ WorkerAlias.aliases = ['deploy:alias', 'deploy:promote'];
107
+ // TODO(@kitten): Keep command hidden until worker deployments are live
108
+ WorkerAlias.hidden = true;
109
+ WorkerAlias.state = 'beta';
110
+ WorkerAlias.flags = {
111
+ prod: core_1.Flags.boolean({
112
+ aliases: ['production'],
113
+ description: 'Promote an existing deployment to production',
114
+ default: false,
115
+ }),
116
+ alias: core_1.Flags.string({
117
+ description: 'Custom alias to assign to the existing deployment',
118
+ helpValue: 'name',
119
+ required: false,
120
+ }),
121
+ id: core_1.Flags.string({
122
+ description: 'Unique identifier of an existing deployment',
123
+ helpValue: 'xyz123',
124
+ required: false,
125
+ }),
126
+ ...flags_1.EasNonInteractiveAndJsonFlags,
127
+ };
128
+ WorkerAlias.contextDefinition = {
129
+ ..._a.ContextOptions.DynamicProjectConfig,
130
+ ..._a.ContextOptions.ProjectDir,
131
+ ..._a.ContextOptions.LoggedIn,
132
+ };
133
+ exports.default = WorkerAlias;
134
+ async function resolveDeploymentAliasAsync(flags) {
135
+ if (flags.aliasName?.trim()) {
136
+ return flags.aliasName.trim().toLowerCase();
137
+ }
138
+ // Skip alias prompt when promoting deployments to prod
139
+ if (flags.isProduction) {
140
+ return null;
141
+ }
142
+ if (flags.nonInteractive) {
143
+ throw new Error('The `--alias` flag must be set when running in `--non-interactive` mode.');
144
+ }
145
+ const { alias: aliasName } = await (0, prompts_1.promptAsync)({
146
+ type: 'text',
147
+ name: 'alias',
148
+ message: 'Enter the alias to assign to a deployment',
149
+ validate: (value) => !!value.trim(),
150
+ hint: 'The alias name is case insensitive and must be URL safe',
151
+ });
152
+ return aliasName.trim().toLowerCase();
153
+ }
154
+ async function resolveDeploymentIdAsync({ graphqlClient, deploymentIdentifier, aliasName, projectId, nonInteractive, }) {
155
+ if (deploymentIdentifier) {
156
+ return deploymentIdentifier;
157
+ }
158
+ if (nonInteractive) {
159
+ throw new Error('The `--id` flag must be set when running in `--non-interactive` mode.');
160
+ }
161
+ const deployment = await (0, deployment_1.selectWorkerDeploymentOnAppAsync)({
162
+ graphqlClient,
163
+ appId: projectId,
164
+ selectTitle: (0, chalk_1.default) `deployment to assign the {underline ${aliasName}} alias`,
165
+ });
166
+ if (!deployment) {
167
+ throw new Error('No deployments found for this project, create a new deployment with "eas deploy"');
168
+ }
169
+ return deployment.deploymentIdentifier;
170
+ }
@@ -1,10 +1,20 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
+ import { EnvironmentVariableEnvironment } from '../../graphql/generated';
2
3
  export default class WorkerDeploy extends EasCommand {
3
4
  static description: string;
4
5
  static aliases: string[];
6
+ static usage: string[];
5
7
  static hidden: boolean;
6
8
  static state: string;
7
- static flags: {};
9
+ static flags: {
10
+ environment: import("@oclif/core/lib/interfaces").OptionFlag<EnvironmentVariableEnvironment | undefined>;
11
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
+ prod: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
+ alias: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
15
+ id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
16
+ 'export-dir': import("@oclif/core/lib/interfaces").OptionFlag<string>;
17
+ };
8
18
  static contextDefinition: {
9
19
  loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
10
20
  projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
@@ -12,4 +22,5 @@ export default class WorkerDeploy extends EasCommand {
12
22
  getDynamicPrivateProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPrivateProjectConfigContextField;
13
23
  };
14
24
  runAsync(): Promise<void>;
25
+ private sanitizeFlags;
15
26
  }
@@ -2,68 +2,52 @@
2
2
  var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
+ const timeago_js_1 = require("@expo/timeago.js");
6
+ const core_1 = require("@oclif/core");
5
7
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
8
  const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
7
9
  const path = tslib_1.__importStar(require("node:path"));
8
10
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
11
+ const flags_1 = require("../../commandUtils/flags");
9
12
  const log_1 = tslib_1.__importDefault(require("../../log"));
10
13
  const ora_1 = require("../../ora");
14
+ const json_1 = require("../../utils/json");
11
15
  const progress_1 = require("../../utils/progress");
12
16
  const WorkerAssets = tslib_1.__importStar(require("../../worker/assets"));
13
17
  const deployment_1 = require("../../worker/deployment");
14
18
  const upload_1 = require("../../worker/upload");
19
+ const logs_1 = require("../../worker/utils/logs");
15
20
  const isDirectory = (directoryPath) => node_fs_1.default.promises
16
21
  .stat(directoryPath)
17
22
  .then(stat => stat.isDirectory())
18
23
  .catch(() => false);
19
24
  class WorkerDeploy extends EasCommand_1.default {
20
25
  async runAsync() {
21
- log_1.default.warn('EAS Worker Deployments are in beta and subject to breaking changes.');
22
- const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(_a, {
23
- nonInteractive: true,
24
- });
25
- const { projectId, projectDir, exp } = await getDynamicPrivateProjectConfigAsync();
26
- const distPath = path.resolve(projectDir, 'dist');
27
- let distServerPath;
28
- let distClientPath;
29
- if (exp.web?.output === 'static') {
30
- distClientPath = distPath;
31
- distServerPath = null;
32
- if (!(await isDirectory(distClientPath))) {
33
- throw new Error(`No "dist/" folder found. Prepare your project for deployment with "npx expo export"`);
34
- }
35
- log_1.default.log('Detected "static" worker deployment');
36
- }
37
- else if (exp.web?.output === 'server') {
38
- distClientPath = path.resolve(distPath, 'client');
39
- distServerPath = path.resolve(distPath, 'server');
40
- if (!(await isDirectory(distClientPath))) {
41
- throw new Error(`No "dist/client/" folder found. Prepare your project for deployment with "npx expo export"`);
42
- }
43
- else if (!(await isDirectory(distServerPath))) {
44
- throw new Error(`No "dist/server/" folder found. Prepare your project for deployment with "npx expo export"`);
45
- }
46
- log_1.default.log('Detected "server" worker deployment');
47
- }
48
- else {
49
- throw new Error(`Single-page apps are not supported. Ensure that app.json key "expo.web.output" is set to "server" or "static".`);
26
+ // NOTE(cedric): `Log.warn` uses `console.log`, which is incorrect when running with `--json`
27
+ // eslint-disable-next-line no-console
28
+ console.warn(chalk_1.default.yellow('EAS Worker Deployments are in beta and subject to breaking changes.'));
29
+ const { flags: rawFlags } = await this.parse(_a);
30
+ const flags = this.sanitizeFlags(rawFlags);
31
+ if (flags.json) {
32
+ (0, json_1.enableJsonOutput)();
50
33
  }
51
- async function* emitWorkerTarballAsync(assetMap) {
52
- yield ['assets.json', JSON.stringify(assetMap)];
53
- // TODO: Create manifest from user configuration
54
- const manifest = { env: {} };
55
- yield ['manifest.json', JSON.stringify(manifest)];
56
- if (distServerPath) {
57
- const workerFiles = WorkerAssets.listWorkerFilesAsync(distServerPath);
34
+ const { getDynamicPrivateProjectConfigAsync, loggedIn: { graphqlClient }, projectDir, } = await this.getContextAsync(_a, flags);
35
+ const [{ projectId }, projectDist] = await Promise.all([
36
+ getDynamicPrivateProjectConfigAsync(),
37
+ resolveExportedProjectAsync(flags, projectDir),
38
+ ]);
39
+ logExportedProjectInfo(projectDist);
40
+ async function* emitWorkerTarballAsync(params) {
41
+ yield ['assets.json', JSON.stringify(params.assetMap)];
42
+ yield ['manifest.json', JSON.stringify(params.manifest)];
43
+ if (projectDist.type === 'server' && projectDist.serverPath) {
44
+ const workerFiles = WorkerAssets.listWorkerFilesAsync(projectDist.serverPath);
58
45
  for await (const workerFile of workerFiles) {
59
46
  yield [`server/${workerFile.normalizedPath}`, workerFile.data];
60
47
  }
61
48
  }
62
49
  }
63
- async function uploadTarballAsync(tarPath) {
64
- const uploadUrl = await (0, deployment_1.getSignedDeploymentUrlAsync)(graphqlClient, exp, {
65
- appId: projectId,
66
- });
50
+ async function uploadTarballAsync(tarPath, uploadUrl) {
67
51
  const { response } = await (0, upload_1.uploadAsync)({
68
52
  url: uploadUrl,
69
53
  filePath: tarPath,
@@ -74,7 +58,7 @@ class WorkerDeploy extends EasCommand_1.default {
74
58
  });
75
59
  if (response.status === 413) {
76
60
  throw new Error('Upload failed! (Payload too large)\n' +
77
- `The files in "dist/server/" (at: ${distServerPath}) exceed the maximum file size (10MB gzip).`);
61
+ `The files in "${path.relative(projectDir, projectDist.path)}" (at: ${projectDir}) exceed the maximum file size (10MB gzip).`);
78
62
  }
79
63
  else if (!response.ok) {
80
64
  throw new Error(`Upload failed! (${response.statusText})`);
@@ -93,7 +77,8 @@ class WorkerDeploy extends EasCommand_1.default {
93
77
  }
94
78
  // TODO(@kitten): Batch and upload multiple files in parallel
95
79
  const uploadParams = [];
96
- for await (const asset of WorkerAssets.listAssetMapFilesAsync(distClientPath, assetMap)) {
80
+ const assetPath = projectDist.type === 'server' ? projectDist.clientPath : projectDist.path;
81
+ for await (const asset of WorkerAssets.listAssetMapFilesAsync(assetPath, assetMap)) {
97
82
  const uploadURL = uploads[asset.normalizedPath];
98
83
  if (uploadURL) {
99
84
  uploadParams.push({ url: uploadURL, filePath: asset.path });
@@ -110,9 +95,9 @@ class WorkerDeploy extends EasCommand_1.default {
110
95
  message(ratio) {
111
96
  const percent = `${Math.floor(ratio * 100)}`;
112
97
  const details = chalk_1.default.dim(`(${progress.pending} Pending, ${progress.transferred} Completed, ${progress.total} Total)`);
113
- return `Uploading client assets: ${percent.padStart(3)}% ${details}`;
98
+ return `Uploading assets: ${percent.padStart(3)}% ${details}`;
114
99
  },
115
- completedMessage: 'Uploaded assets for serverless deployment',
100
+ completedMessage: 'Uploaded assets',
116
101
  });
117
102
  try {
118
103
  for await (const signal of (0, upload_1.batchUploadAsync)(uploadParams)) {
@@ -132,46 +117,151 @@ class WorkerDeploy extends EasCommand_1.default {
132
117
  }
133
118
  updateProgress({ isComplete: true });
134
119
  }
135
- let progress = (0, ora_1.ora)('Preparing worker upload');
136
120
  let assetMap;
137
121
  let tarPath;
138
- try {
139
- assetMap = await WorkerAssets.createAssetMapAsync(distClientPath);
140
- tarPath = await WorkerAssets.packFilesIterableAsync(emitWorkerTarballAsync(assetMap));
141
- }
142
- catch (error) {
143
- progress.fail('Failed to prepare worker upload');
144
- throw error;
145
- }
146
- progress.succeed('Prepared worker upload');
147
- progress = (0, ora_1.ora)('Creating worker deployment');
148
122
  let deployResult;
123
+ let progress = (0, ora_1.ora)('Preparing project').start();
149
124
  try {
150
- deployResult = await uploadTarballAsync(tarPath);
125
+ const manifest = await WorkerAssets.createManifestAsync({
126
+ environment: flags.environment,
127
+ projectDir,
128
+ projectId,
129
+ }, graphqlClient);
130
+ assetMap = await WorkerAssets.createAssetMapAsync(projectDist.type === 'server' ? projectDist.clientPath : projectDist.path);
131
+ tarPath = await WorkerAssets.packFilesIterableAsync(emitWorkerTarballAsync({
132
+ assetMap,
133
+ manifest,
134
+ }));
135
+ const uploadUrl = await (0, deployment_1.getSignedDeploymentUrlAsync)(graphqlClient, {
136
+ appId: projectId,
137
+ deploymentIdentifier: flags.deploymentIdentifier,
138
+ // NOTE(cedric): this function might ask the user for a dev-domain name,
139
+ // when that happens, no ora spinner should be running.
140
+ onSetupDevDomain: () => progress.stop(),
141
+ nonInteractive: flags.nonInteractive,
142
+ });
143
+ progress.start('Creating deployment');
144
+ deployResult = await uploadTarballAsync(tarPath, uploadUrl);
145
+ progress.succeed('Created deployment');
151
146
  }
152
147
  catch (error) {
153
- progress.fail('Failed to create worker deployment');
148
+ progress.fail('Failed to create deployment');
154
149
  throw error;
155
150
  }
156
- progress.succeed('Created worker deployment');
157
151
  await uploadAssetsAsync(assetMap, deployResult.uploads);
158
- const baseDomain = process.env.EXPO_STAGING ? 'staging.expo' : 'expo';
159
- const deploymentURL = `https://${deployResult.fullName}.${baseDomain}.app`;
160
- const deploymentsUrl = `https://${baseDomain}.dev/accounts/${exp.owner}/projects/${deployResult.name}/serverless/deployments`;
152
+ let deploymentAlias = null;
153
+ if (flags.aliasName) {
154
+ progress = (0, ora_1.ora)((0, chalk_1.default) `Assigning alias {bold ${flags.aliasName}} to deployment`).start();
155
+ try {
156
+ deploymentAlias = await (0, deployment_1.assignWorkerDeploymentAliasAsync)({
157
+ graphqlClient,
158
+ appId: projectId,
159
+ deploymentId: deployResult.id,
160
+ aliasName: flags.aliasName,
161
+ });
162
+ // Only stop the spinner when not promoting to production
163
+ if (!flags.isProduction) {
164
+ progress.succeed((0, chalk_1.default) `Assigned alias {bold ${flags.aliasName}} to deployment`);
165
+ }
166
+ }
167
+ catch (error) {
168
+ progress.fail((0, chalk_1.default) `Failed to assign {bold ${flags.aliasName}} alias to deployment`);
169
+ throw error;
170
+ }
171
+ }
172
+ let deploymentProdAlias = null;
173
+ if (flags.isProduction) {
174
+ try {
175
+ if (!flags.aliasName) {
176
+ progress = (0, ora_1.ora)((0, chalk_1.default) `Promoting deployment to {bold production}`).start();
177
+ }
178
+ else {
179
+ progress.text = (0, chalk_1.default) `Promoting deployment to {bold production}`;
180
+ }
181
+ deploymentProdAlias = await (0, deployment_1.assignWorkerDeploymentProductionAsync)({
182
+ graphqlClient,
183
+ appId: projectId,
184
+ deploymentId: deployResult.id,
185
+ });
186
+ progress.succeed(!flags.aliasName
187
+ ? (0, chalk_1.default) `Promoted deployment to {bold production}`
188
+ : (0, chalk_1.default) `Promoted deployment to {bold production} with alias {bold ${flags.aliasName}}`);
189
+ }
190
+ catch (error) {
191
+ progress.fail('Failed to promote deployment to production');
192
+ throw error;
193
+ }
194
+ }
195
+ if (flags.json) {
196
+ (0, json_1.printJsonOnlyOutput)((0, logs_1.formatWorkerDeploymentJson)({
197
+ projectId,
198
+ deployment: {
199
+ deploymentIdentifier: deployResult.id,
200
+ url: (0, logs_1.getDeploymentUrlFromFullName)(deployResult.fullName),
201
+ },
202
+ aliases: [deploymentAlias].filter(Boolean),
203
+ production: deploymentProdAlias,
204
+ }));
205
+ return;
206
+ }
161
207
  log_1.default.addNewLineIfNone();
162
- log_1.default.log(`🎉 Your worker deployment is ready: ${deploymentURL}`);
208
+ log_1.default.log(`🎉 Your deployment is ready`);
163
209
  log_1.default.addNewLineIfNone();
164
- log_1.default.log(`🔗 Manage on EAS: ${deploymentsUrl}`);
210
+ log_1.default.log((0, logs_1.formatWorkerDeploymentTable)({
211
+ projectId,
212
+ deployment: {
213
+ deploymentIdentifier: deployResult.id,
214
+ url: (0, logs_1.getDeploymentUrlFromFullName)(deployResult.fullName),
215
+ },
216
+ aliases: [deploymentAlias].filter(Boolean),
217
+ production: deploymentProdAlias,
218
+ }));
219
+ if (!deploymentProdAlias) {
220
+ log_1.default.addNewLineIfNone();
221
+ log_1.default.log('🚀 When you are ready to deploy to production:');
222
+ log_1.default.log((0, chalk_1.default) ` $ eas deploy {bold --prod}`);
223
+ }
224
+ }
225
+ sanitizeFlags(flags) {
226
+ return {
227
+ nonInteractive: flags['non-interactive'],
228
+ json: flags['json'],
229
+ isProduction: !!flags.prod,
230
+ aliasName: flags.alias?.trim().toLowerCase(),
231
+ deploymentIdentifier: flags.id?.trim(),
232
+ exportDir: flags['export-dir'],
233
+ };
165
234
  }
166
235
  }
167
236
  _a = WorkerDeploy;
168
- WorkerDeploy.description = 'deploy an Expo web build';
237
+ WorkerDeploy.description = 'Deploy your Expo web build';
169
238
  WorkerDeploy.aliases = ['deploy'];
239
+ WorkerDeploy.usage = [(0, chalk_1.default) `deploy {dim [options]}`, `deploy --prod`];
170
240
  // TODO(@kitten): Keep command hidden until worker deployments are live
171
241
  WorkerDeploy.hidden = true;
172
242
  WorkerDeploy.state = 'beta';
173
243
  WorkerDeploy.flags = {
174
- // TODO(@kitten): Allow deployment identifier to be specified
244
+ prod: core_1.Flags.boolean({
245
+ aliases: ['production'],
246
+ description: 'Create a new production deployment',
247
+ default: false,
248
+ }),
249
+ alias: core_1.Flags.string({
250
+ description: 'Custom alias to assign to the new deployment',
251
+ helpValue: 'name',
252
+ }),
253
+ id: core_1.Flags.string({
254
+ description: 'Custom unique identifier for the new deployment',
255
+ helpValue: 'xyz123',
256
+ }),
257
+ 'export-dir': core_1.Flags.string({
258
+ description: 'Directory where the Expo project was exported',
259
+ helpValue: 'dir',
260
+ default: 'dist',
261
+ }),
262
+ // TODO(@kitten): Allow deployment identifier to be specified
263
+ ...flags_1.EasNonInteractiveAndJsonFlags,
264
+ ...flags_1.EASEnvironmentFlag,
175
265
  };
176
266
  WorkerDeploy.contextDefinition = {
177
267
  ..._a.ContextOptions.DynamicProjectConfig,
@@ -179,3 +269,21 @@ WorkerDeploy.contextDefinition = {
179
269
  ..._a.ContextOptions.LoggedIn,
180
270
  };
181
271
  exports.default = WorkerDeploy;
272
+ async function resolveExportedProjectAsync(flags, projectDir) {
273
+ const exportPath = path.join(projectDir, flags.exportDir);
274
+ const serverPath = path.join(exportPath, 'server');
275
+ const clientPath = path.join(exportPath, 'client');
276
+ const [hasServerPath, hasClientPath, modifiedAt] = await Promise.all([
277
+ isDirectory(serverPath),
278
+ isDirectory(clientPath),
279
+ node_fs_1.default.promises.stat(exportPath).then(stat => stat.mtime),
280
+ ]);
281
+ if (hasServerPath && hasClientPath) {
282
+ return { type: 'server', path: exportPath, modifiedAt, serverPath, clientPath };
283
+ }
284
+ return { type: 'static', path: exportPath, modifiedAt };
285
+ }
286
+ function logExportedProjectInfo(project) {
287
+ const modifiedAgo = (0, timeago_js_1.format)(project.modifiedAt);
288
+ log_1.default.log((0, chalk_1.default) `{dim > Project export: ${project.type} - created ${modifiedAgo}}`);
289
+ }