Skip to content
This repository was archived by the owner on Jun 28, 2022. It is now read-only.

Commit

Permalink
feat: deploy static app edge working
Browse files Browse the repository at this point in the history
  • Loading branch information
arantespp committed Aug 23, 2020
1 parent 48c0b0e commit da5a284
Show file tree
Hide file tree
Showing 17 changed files with 539 additions and 274 deletions.
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"change-case": "^4.1.1",
"deepmerge": "^4.2.2",
"find-up": "^5.0.0",
"glob": "^7.1.6",
"js-yaml": "^3.14.0",
"npmlog": "^4.1.2",
"rollup": "^2.26.4",
Expand Down
124 changes: 54 additions & 70 deletions packages/cli/src/deploy/s3.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-await-in-loop */
import { S3 } from 'aws-sdk';
import fs from 'fs';
import glob from 'glob';
import log from 'npmlog';
import path from 'path';

Expand All @@ -26,7 +29,9 @@ type ContentType =

type ContentEncoding = 'utf8' | 'base64' | 'binary';

export const getContentMetadata = (file: string) => {
export const getContentMetadata = (
file: string,
): { encoding: ContentEncoding; type: ContentType } => {
const contentMetadata: {
[key: string]: { encoding: ContentEncoding; type: ContentType };
} = {
Expand All @@ -52,7 +57,8 @@ export const getContentMetadata = (file: string) => {
const extension = file.split('.').pop();

if (!extension || !contentMetadata[extension]) {
throw new Error(`Content metadata for file ${file} does not exist.`);
log.warn(logPrefix, `Content metadata for file ${file} does not exist.`);
return { type: 'text/plain', encoding: 'utf8' };
}

return contentMetadata[extension];
Expand Down Expand Up @@ -85,7 +91,7 @@ export const uploadFileToS3 = async ({
throw new Error('file or filePath must be defined');
}

log.info(logPrefix, `Uploading files to ${bucket}/${key}`);
log.info(logPrefix, `Uploading file to ${bucket}/${key}...`);

const params: S3.PutObjectRequest = {
Bucket: bucket,
Expand Down Expand Up @@ -124,63 +130,31 @@ export const uploadDirectoryToS3 = async ({
}) => {
log.info(
logPrefix,
`Uploading ${directory} files to ${bucket}/${bucketKey}...`,
`Uploading directory ${directory} to ${bucket}/${bucketKey}...`,
);

/**
* Iterates over a folder and upload files if item is file or call it again
* if is folder.
* Get all files and directories inside ${directory}.
*/
const uploadDirFilesToS3 = async (currentDir: string) => {
const itemPath = (item: string) => path.resolve(currentDir, item);
const isDir = (item: string) => fs.lstatSync(itemPath(item)).isDirectory();
const dir = await fs.promises.readdir(currentDir);

await dir.reduce<Promise<S3.ManagedUpload.SendData | void>>(
async (acc, item) => {
try {
if (isDir(item)) {
await uploadDirFilesToS3(itemPath(item));
return acc;
}

const { type, encoding } = getContentMetadata(item);

const file = await fs.promises.readFile(itemPath(item), encoding);

const key = path.relative(
directory,
path.resolve(bucketKey, currentDir, item),
);

const params = {
Bucket: bucket,
ContentType: type,
ContentEncoding: encoding,
Key: key,
Body: Buffer.from(file, encoding),
};

log.info(logPrefix, `Uploading ${params.Key}...`);

await s3.upload(params).promise();

log.info(logPrefix, `Upload of ${params.Key} finished.`);
const allFilesAndDirectories = await new Promise<string[]>(
(resolve, reject) => {
glob(`${directory}/**/*`, (err, matches) => {
return err ? reject(err) : resolve(matches);
});
},
);

return acc;
} catch (err) {
log.error(logPrefix, item, err.message);
return process.exit(1);
}
},
Promise.resolve(),
);
};
const allFiles = allFilesAndDirectories.filter((item) =>
fs.lstatSync(item).isFile(),
);

try {
await uploadDirFilesToS3(directory);
} catch (err) {
log.error(logPrefix, 'Error on uploadDirectoryToS3');
throw err;
for (const [index, file] of allFiles.entries()) {
log.info(logPrefix, `Upload ${index + 1}/${allFiles.length}`);
await uploadFileToS3({
bucket,
key: path.relative(directory, file),
filePath: file,
});
}
};

Expand All @@ -204,37 +178,47 @@ export const emptyS3Directory = async ({
/**
* Get object versions
*/
const objects = await Contents.reduce<Promise<S3.ObjectIdentifierList>>(
async (promiseAcc, { Key }): Promise<S3.ObjectIdentifierList> => {
const acc = await promiseAcc;

if (!Key) {
return acc;
}

const objectsPromises = Contents.filter(({ Key }) => !!Key).map(
async ({ Key }) => {
const { Versions = [] } = await s3
.listObjectVersions({
Bucket: bucket,
Prefix: Key,
})
.promise();

return Promise.resolve([
...acc,
...Versions.map(({ VersionId }) => ({ Key, VersionId })),
]);
return {
Key: Key as string,
Versions: Versions.map(({ VersionId }) => VersionId || undefined),
};
},
Promise.resolve([]),
);

const objects = await Promise.all(objectsPromises);

const objectsWithVersionsIds = objects.reduce<
Array<{
Key: string;
VersionId?: string;
}>
>((acc, { Key, Versions }) => {
const objectWithVersionsIds = Versions.map((VersionId) => ({
Key,
VersionId,
}));
return [...acc, ...objectWithVersionsIds];
}, []);

await s3
.deleteObjects({
Bucket: bucket,
Delete: { Objects: objects },
Delete: { Objects: objectsWithVersionsIds },
})
.promise();
}

/**
* Truncated is files that exists but weren't listed from S3 API.
*/
if (IsTruncated) {
await emptyS3Directory({ bucket, directory });
}
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/deploy/staticApp/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ export const deployStaticAppCommand: CommandModule = {
require: false,
type: 'boolean',
},
edge: {
spa: {
alias: ['single-page-application'],
default: false,
describe:
'This option enables Lambda@Edge. This is used with apps that is built with Next.js or Gatsby.',
'This option enables CloudFront to serve a single page application (SPA).',
require: false,
type: 'boolean',
},
Expand All @@ -55,8 +56,8 @@ export const deployStaticAppCommand: CommandModule = {
},
})
.middleware((argv) => {
const { acmArn, aliases, edge } = argv;
if (acmArn || acmArn || aliases || edge) {
const { acmArn, acmArnExportedName, aliases, spa } = argv;
if (acmArn || acmArnExportedName || aliases || spa) {
argv.cloudfront = true;
}
})
Expand Down
Loading

0 comments on commit da5a284

Please sign in to comment.