Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 81 additions & 45 deletions packages/lambda/src/api/deploy-site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,56 +78,92 @@ const mandatoryDeploySite = async ({
}): DeploySiteOutput => {
let generatedBundleDir: string | null = null;

const result = await deploySiteWithBundle({
bucketName,
region,
siteName,
options,
privacy,
throwIfSiteExists,
providerSpecifics,
forcePathStyle,
fullClientSpecifics,
requestHandler,
getBundle: async () => {
generatedBundleDir = await fullClientSpecifics.bundleSite({
publicPath: `/${getSitesKey(siteName)}/`,
bundlerOverride: options.bundlerOverride ?? ((f) => f),
rspackOverride: options.rspackOverride ?? ((f) => f),
webpackOverride: options.webpackOverride ?? ((f) => f),
enableCaching: options.enableCaching ?? true,
publicDir: options.publicDir ?? null,
rootDir: options.rootDir ?? null,
ignoreRegisterRootWarning: options.ignoreRegisterRootWarning ?? false,
onProgress: options.onBundleProgress ?? (() => undefined),
entryPoint,
gitSource,
bufferStateDelayInMilliseconds: null,
maxTimelineTracks: null,
onDirectoryCreated: () => undefined,
onPublicDirCopyProgress: () => undefined,
onSymlinkDetected: () => undefined,
outDir: null,
askAIEnabled: options.askAIEnabled ?? true,
interactivityEnabled: options.interactivityEnabled ?? true,
audioLatencyHint: null,
keyboardShortcutsEnabled: options.keyboardShortcutsEnabled ?? true,
renderDefaults: null,
rspack: options.rspack ?? false,
symlinkPublicDir: false,
});
let deploymentOutcome:
| {type: 'success'; result: Awaited<DeploySiteOutput>}
| {type: 'failure'; error: unknown};

return generatedBundleDir;
},
});
try {
const result = await deploySiteWithBundle({
bucketName,
region,
siteName,
options,
privacy,
throwIfSiteExists,
providerSpecifics,
forcePathStyle,
fullClientSpecifics,
requestHandler,
getBundle: async () => {
const bundleDir = await fullClientSpecifics.bundleSite({
publicPath: `/${getSitesKey(siteName)}/`,
bundlerOverride: options.bundlerOverride ?? ((f) => f),
rspackOverride: options.rspackOverride ?? ((f) => f),
webpackOverride: options.webpackOverride ?? ((f) => f),
enableCaching: options.enableCaching ?? true,
publicDir: options.publicDir ?? null,
rootDir: options.rootDir ?? null,
ignoreRegisterRootWarning: options.ignoreRegisterRootWarning ?? false,
onProgress: options.onBundleProgress ?? (() => undefined),
entryPoint,
gitSource,
bufferStateDelayInMilliseconds: null,
maxTimelineTracks: null,
onDirectoryCreated: (directory) => {
generatedBundleDir = directory;
},
onPublicDirCopyProgress: () => undefined,
onSymlinkDetected: () => undefined,
outDir: null,
askAIEnabled: options.askAIEnabled ?? true,
interactivityEnabled: options.interactivityEnabled ?? true,
audioLatencyHint: null,
keyboardShortcutsEnabled: options.keyboardShortcutsEnabled ?? true,
renderDefaults: null,
rspack: options.rspack ?? false,
symlinkPublicDir: false,
});

if (generatedBundleDir && fs.existsSync(generatedBundleDir)) {
fs.rmSync(generatedBundleDir, {
recursive: true,
generatedBundleDir = bundleDir;
return bundleDir;
},
});
deploymentOutcome = {type: 'success', result};
} catch (error) {
deploymentOutcome = {type: 'failure', error};
}

let cleanupFailed = false;
let cleanupError: unknown;
if (generatedBundleDir !== null) {
try {
fs.rmSync(generatedBundleDir, {
force: true,
recursive: true,
});
} catch (error) {
cleanupFailed = true;
cleanupError = error;
}
}

if (deploymentOutcome.type === 'failure') {
if (cleanupFailed) {
throw new AggregateError(
[deploymentOutcome.error, cleanupError],
'Deploying the site failed, and removing the generated bundle also failed.',
{cause: deploymentOutcome.error},
);
}

throw deploymentOutcome.error;
}

if (cleanupFailed) {
throw cleanupError;
}

return result;
return deploymentOutcome.result;
};

export type InternalDeploySiteInput = MandatoryParameters &
Expand Down
22 changes: 12 additions & 10 deletions packages/lambda/src/api/upload-dir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {Privacy, UploadDirProgress} from '@remotion/serverless';
import mimeTypes from 'mime-types';
import {makeS3Key} from '../shared/make-s3-key';
import {multipartUploadPartSize} from '../shared/multipart-upload-part-size';
import {waitForPromisesToFinish} from '../shared/wait-for-promises-to-finish';

type FileInfo = {
name: string;
Expand Down Expand Up @@ -141,14 +142,11 @@ export const uploadDir = async ({
}
};

const uploadAll = (async () => {
const uploads = files.map((filePath) =>
limit(async () => {
await uploadWithRetry(filePath);
}),
);
await Promise.all(uploads);
})();
const uploads = files.map((filePath) =>
limit(async () => {
await uploadWithRetry(filePath);
}),
);

const interval = setInterval(() => {
onProgress({
Expand All @@ -158,6 +156,10 @@ export const uploadDir = async ({
filesUploaded: files.filter((f) => progresses[f.name] === f.size).length,
});
}, 1000);
await uploadAll;
clearInterval(interval);

try {
await waitForPromisesToFinish(uploads);
} finally {
clearInterval(interval);
}
};
53 changes: 28 additions & 25 deletions packages/lambda/src/shared/deploy-site-with-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
import {validateBucketName, validatePrivacy} from '@remotion/serverless';
import {getS3DiffOperations} from './get-s3-operations';
import {validateSiteName} from './validate-site-name';
import {waitForPromisesToFinish} from './wait-for-promises-to-finish';

export type DeploySiteOutput = Promise<{
serveUrl: string;
Expand Down Expand Up @@ -87,18 +88,20 @@ export const deploySiteWithBundle: (

const subFolder = getSitesKey(siteName);

const [files, bundleDir] = await Promise.all([
providerSpecifics.listObjects({
bucketName,
expectedBucketOwner: accountId,
region,
// The `/` is important to not accidentally delete sites with the same name but containing a suffix.
prefix: `${subFolder}/`,
forcePathStyle,
requestHandler,
}),
getBundle(),
]);
const filesPromise = providerSpecifics.listObjects({
bucketName,
expectedBucketOwner: accountId,
region,
// The `/` is important to not accidentally delete sites with the same name but containing a suffix.
prefix: `${subFolder}/`,
forcePathStyle,
requestHandler,
});
const bundlePromise = getBundle();
const [files, bundleDir] = await waitForPromisesToFinish([
filesPromise,
bundlePromise,
] as const);

if (throwIfSiteExists && files.length > 0) {
throw new Error(
Expand Down Expand Up @@ -152,20 +155,20 @@ export const deploySiteWithBundle: (
await upload(indexHtmlToUpload);

const limit = LambdaClientInternals.pLimit(deleteConcurrency);
await Promise.all(
toDelete.map((d) => {
return limit(() => {
return providerSpecifics.deleteFile({
bucketName,
customCredentials: null,
key: d.Key as string,
region,
forcePathStyle,
requestHandler,
});
const deletePromises = toDelete.map((d) => {
return limit(() => {
return providerSpecifics.deleteFile({
bucketName,
customCredentials: null,
key: d.Key as string,
region,
forcePathStyle,
requestHandler,
});
}),
);
});
});

await waitForPromisesToFinish(deletePromises);

return {
serveUrl: LambdaClientInternals.makeS3ServeUrl({
Expand Down
12 changes: 12 additions & 0 deletions packages/lambda/src/shared/wait-for-promises-to-finish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const waitForPromisesToFinish = async <
T extends readonly PromiseLike<unknown>[],
>(
promises: T,
) => {
try {
return await Promise.all(promises);
} catch (error) {
await Promise.allSettled(promises);
throw error;
}
};
Loading
Loading