Skip to content

Commit

Permalink
refactor(eas-cli): always default to static uploads and add modified …
Browse files Browse the repository at this point in the history
…time (#2565)

* refactor(eas-cli): always default to static uploads and add modified time

* docs(eas-cli): add changelog entry

* chore(eas-cli): resolve linting issue

* fix(eas-cli): error when there are no deployments

* fix(eas-cli): remove unused `exp` var

* fix(eas-cli): fix json output again
  • Loading branch information
byCedric authored Sep 14, 2024
1 parent 539ef8f commit 7847b52
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 54 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ This is the log of notable changes to EAS CLI and related packages.
- Unify both `worker` and `worker:alias` command output. ([#2558](https://github.com/expo/eas-cli/pull/2558) by [@byCedric](https://github.com/byCedric)))
- Share similar table/json output in both `worker` and `worker:alias` command outputs. ([#2563](https://github.com/expo/eas-cli/pull/2563) by [@byCedric](https://github.com/byCedric)))
- Polish the project URL prompt when setting up new projects. ([#2564](https://github.com/expo/eas-cli/pull/2564) by [@byCedric](https://github.com/byCedric)))
- Always assume `static` exports in `eas deploy` and add modified time. ([#2565](https://github.com/expo/eas-cli/pull/2565) by [@byCedric](https://github.com/byCedric)))

## [12.3.0](https://github.com/expo/eas-cli/releases/tag/v12.3.0) - 2024-09-09

Expand Down
8 changes: 7 additions & 1 deletion packages/eas-cli/src/commands/worker/alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,5 +227,11 @@ async function resolveDeploymentIdAsync({
selectTitle: chalk`deployment to assign the {underline ${aliasName}} alias`,
});

return deployment?.deploymentIdentifier;
if (!deployment) {
throw new Error(
'No deployments found for this project, create a new deployment with "eas deploy"'
);
}

return deployment.deploymentIdentifier;
}
88 changes: 47 additions & 41 deletions packages/eas-cli/src/commands/worker/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { format as formatTimeAgo } from '@expo/timeago.js';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import fs from 'node:fs';
Expand Down Expand Up @@ -111,49 +112,21 @@ export default class WorkerDeploy extends EasCommand {
projectDir,
} = await this.getContextAsync(WorkerDeploy, flags);

const { projectId, exp } = await getDynamicPrivateProjectConfigAsync();
const distPath = path.join(projectDir, flags.exportDir);
const [{ projectId }, projectDist] = await Promise.all([
getDynamicPrivateProjectConfigAsync(),
resolveExportedProjectAsync(flags, projectDir),
]);

let distServerPath: string | null;
let distClientPath: string;
if (exp.web?.output === 'static') {
distClientPath = distPath;
distServerPath = null;
if (!(await isDirectory(distClientPath))) {
throw new Error(
`No "${flags.exportDir}/" folder found. Prepare your project for deployment with "npx expo export"`
);
}

logDeploymentType('static');
} else if (exp.web?.output === 'server') {
distClientPath = path.join(distPath, 'client');
distServerPath = path.join(distPath, 'server');
if (!(await isDirectory(distClientPath))) {
throw new Error(
`No "${flags.exportDir}/client/" folder found. Prepare your project for deployment with "npx expo export"`
);
} else if (!(await isDirectory(distServerPath))) {
throw new Error(
`No "${flags.exportDir}/server/" folder found. Prepare your project for deployment with "npx expo export"`
);
}

logDeploymentType('server');
} else {
throw new Error(
`Single-page apps are not supported. Ensure that app.json key "expo.web.output" is set to "server" or "static".`
);
}
logExportedProjectInfo(projectDist);

async function* emitWorkerTarballAsync(params: {
assetMap: WorkerAssets.AssetMap;
manifest: WorkerAssets.Manifest;
}): AsyncGenerator<WorkerAssets.FileEntry> {
yield ['assets.json', JSON.stringify(params.assetMap)];
yield ['manifest.json', JSON.stringify(params.manifest)];
if (distServerPath) {
const workerFiles = WorkerAssets.listWorkerFilesAsync(distServerPath);
if (projectDist.type === 'server' && projectDist.serverPath) {
const workerFiles = WorkerAssets.listWorkerFilesAsync(projectDist.serverPath);
for await (const workerFile of workerFiles) {
yield [`server/${workerFile.normalizedPath}`, workerFile.data];
}
Expand All @@ -172,7 +145,10 @@ export default class WorkerDeploy extends EasCommand {
if (response.status === 413) {
throw new Error(
'Upload failed! (Payload too large)\n' +
`The files in "dist/server/" (at: ${distServerPath}) exceed the maximum file size (10MB gzip).`
`The files in "${path.relative(
projectDir,
projectDist.path
)}" (at: ${projectDir}) exceed the maximum file size (10MB gzip).`
);
} else if (!response.ok) {
throw new Error(`Upload failed! (${response.statusText})`);
Expand All @@ -195,7 +171,8 @@ export default class WorkerDeploy extends EasCommand {

// TODO(@kitten): Batch and upload multiple files in parallel
const uploadParams: UploadParams[] = [];
for await (const asset of WorkerAssets.listAssetMapFilesAsync(distClientPath, assetMap)) {
const assetPath = projectDist.type === 'server' ? projectDist.clientPath : projectDist.path;
for await (const asset of WorkerAssets.listAssetMapFilesAsync(assetPath, assetMap)) {
const uploadURL = uploads[asset.normalizedPath];
if (uploadURL) {
uploadParams.push({ url: uploadURL, filePath: asset.path });
Expand Down Expand Up @@ -252,7 +229,9 @@ export default class WorkerDeploy extends EasCommand {
},
graphqlClient
);
assetMap = await WorkerAssets.createAssetMapAsync(distClientPath);
assetMap = await WorkerAssets.createAssetMapAsync(
projectDist.type === 'server' ? projectDist.clientPath : projectDist.path
);
tarPath = await WorkerAssets.packFilesIterableAsync(
emitWorkerTarballAsync({
assetMap,
Expand All @@ -266,6 +245,7 @@ export default class WorkerDeploy extends EasCommand {
// NOTE(cedric): this function might ask the user for a dev-domain name,
// when that happens, no ora spinner should be running.
onSetupDevDomain: () => progress.stop(),
nonInteractive: flags.nonInteractive,
});

progress.start('Creating deployment');
Expand Down Expand Up @@ -376,7 +356,33 @@ export default class WorkerDeploy extends EasCommand {
}
}

/** Log the detected of Expo export */
function logDeploymentType(type: 'static' | 'server'): void {
Log.log(chalk`{dim > output: ${type}}`);
async function resolveExportedProjectAsync(
flags: DeployFlags,
projectDir: string
): Promise<
| { type: 'static'; modifiedAt: Date; path: string }
| { type: 'server'; modifiedAt: Date; path: string; serverPath: string; clientPath: string }
> {
const exportPath = path.join(projectDir, flags.exportDir);
const serverPath = path.join(exportPath, 'server');
const clientPath = path.join(exportPath, 'client');

const [hasServerPath, hasClientPath, modifiedAt] = await Promise.all([
isDirectory(serverPath),
isDirectory(clientPath),
fs.promises.stat(exportPath).then(stat => stat.mtime),
]);

if (hasServerPath && hasClientPath) {
return { type: 'server', path: exportPath, modifiedAt, serverPath, clientPath };
}

return { type: 'static', path: exportPath, modifiedAt };
}

function logExportedProjectInfo(
project: Awaited<ReturnType<typeof resolveExportedProjectAsync>>
): void {
const modifiedAgo = formatTimeAgo(project.modifiedAt);
Log.log(chalk`{dim > Project export: ${project.type} - created ${modifiedAgo}}`);
}
31 changes: 19 additions & 12 deletions packages/eas-cli/src/worker/deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ import { selectPaginatedAsync } from '../utils/relay';

export async function getSignedDeploymentUrlAsync(
graphqlClient: ExpoGraphqlClient,
deploymentVariables: {
options: {
appId: string;
deploymentIdentifier?: string | null;
/** Callback which is invoked when the project is going to setup the dev domain */
onSetupDevDomain?: () => any;
/** If the terminal is running in non interactive mode or not */
nonInteractive?: boolean;
}
): Promise<string> {
try {
return await DeploymentsMutation.createSignedDeploymentUrlAsync(
graphqlClient,
deploymentVariables
);
return await DeploymentsMutation.createSignedDeploymentUrlAsync(graphqlClient, {
appId: options.appId,
deploymentIdentifier: options.deploymentIdentifier,
});
} catch (error: any) {
const isMissingDevDomain = (error as GraphqlError)?.graphQLErrors?.some(e =>
['APP_NO_DEV_DOMAIN_NAME'].includes(e?.extensions?.errorCode as string)
Expand All @@ -33,24 +35,29 @@ export async function getSignedDeploymentUrlAsync(
if (!isMissingDevDomain) {
throw error;
}
if (options.nonInteractive) {
throw new Error(
'The project URL needs to be set up, but the terminal is running in non-interactive mode.'
);
}

const suggestedDevDomainName = await DeploymentsQuery.getSuggestedDevDomainByAppIdAsync(
graphqlClient,
{ appId: deploymentVariables.appId }
{ appId: options.appId }
);

deploymentVariables.onSetupDevDomain?.();
options.onSetupDevDomain?.();

await chooseDevDomainNameAsync({
graphqlClient,
appId: deploymentVariables.appId,
appId: options.appId,
initial: suggestedDevDomainName,
});

return await DeploymentsMutation.createSignedDeploymentUrlAsync(
graphqlClient,
deploymentVariables
);
return await DeploymentsMutation.createSignedDeploymentUrlAsync(graphqlClient, {
appId: options.appId,
deploymentIdentifier: options.deploymentIdentifier,
});
}
}

Expand Down

0 comments on commit 7847b52

Please sign in to comment.