Add command to update serverless layer (#11500)

New command to add packages to serverless -> Run into your terminal
`npx nx run twenty-server:command serverless:add-packages -p
stripe@17.0.0,@slack/web-api,@slack/oauth`

Will create a new layer folder, add packages in it and update the new
last_layer_version

<img width="1468" alt="image"
src="https://github.com/user-attachments/assets/da8ee77f-e512-4ceb-95bc-cfa979f3439c"
/>

Before
<img width="877" alt="image"
src="https://github.com/user-attachments/assets/35f6bb6c-2ebf-46fe-bd15-4e6352901fc8"
/>

After
<img width="938" alt="image"
src="https://github.com/user-attachments/assets/785af132-5e8c-4a9a-bc57-dffdcaaeec02"
/>
This commit is contained in:
martmull
2025-04-10 14:53:59 +02:00
committed by GitHub
parent 1802c30995
commit 4e4c0ddd3c
2 changed files with 137 additions and 1 deletions

View File

@ -0,0 +1,135 @@
import { Logger } from '@nestjs/common';
import * as fs from 'fs/promises';
import { resolve } from 'path';
import { promisify } from 'util';
import { exec } from 'child_process';
import { Command, CommandRunner, Option } from 'nest-commander';
const execPromise = promisify(exec);
@Command({
name: 'serverless:add-packages',
description:
'Create a new serverless layer version and install packages in it',
})
export class AddPackagesCommand extends CommandRunner {
private readonly logger = new Logger(AddPackagesCommand.name);
@Option({
flags: '-p, --packages <packages>',
description: 'comma separated packages (eg: axios,uuid@9.0.1)',
required: true,
})
parsePackages(val: string): string[] {
return val.split(',');
}
async run(
passedParams: string[],
options: Record<string, any>,
): Promise<void> {
this.logger.log('---------------------------------------');
this.logger.warn('This command should be run locally only');
this.logger.log('');
const layersFolder = this.getAbsoluteFilePath(
`src/engine/core-modules/serverless/drivers/layers`,
);
const currentVersion = await this.getLastLayerVersion();
const newVersion = currentVersion + 1;
const currentVersionFolder = `${layersFolder}/${currentVersion}`;
const newVersionFolder = `${layersFolder}/${newVersion}`;
await fs.cp(currentVersionFolder, newVersionFolder, { recursive: true });
// Install each package
this.logger.log('Installing packages');
await this.installPackages(options.packages, newVersionFolder);
this.logger.log('Cleaning');
await this.cleanPackageInstallation(newVersionFolder);
this.logger.log('Updating last layer version');
await this.updateLastLayerVersion(newVersion);
this.logger.log('Add changes to git');
await this.addToGit(layersFolder);
this.logger.log('');
this.logger.log(
`New packages '${options.packages.join("', '")}' installed in new layer version '${newVersion}' `,
);
this.logger.log('Please commit your changes');
this.logger.log('---------------------------------------');
}
private getAbsoluteFilePath(path: string) {
const rootPath = process.cwd();
return resolve(rootPath, path);
}
private async addToGit(folderPath: string) {
await execPromise(`git add ${folderPath}`);
}
private async cleanPackageInstallation(folderPath: string) {
await fs.rm(folderPath + '/node_modules', {
recursive: true,
force: true,
});
await fs.rm(folderPath + '/.yarn', {
recursive: true,
force: true,
});
}
private async installPackages(packages: string[], folderPath: string) {
if (packages?.length) {
for (const packageName of packages) {
this.logger.log(`- adding '${packageName}'...`);
try {
await execPromise(`yarn add ${packageName}`, {
cwd: folderPath,
});
} catch (error) {
this.logger.error(
`Failed to install ${packageName}: ${(error as Error).message}`,
);
}
}
}
}
private async getLastLayerVersion() {
const filePath = this.getAbsoluteFilePath(
'src/engine/core-modules/serverless/drivers/layers/last-layer-version.ts',
);
const content = await fs.readFile(filePath, 'utf8');
const match = content.match(/export const LAST_LAYER_VERSION = (\d+);/);
if (!match) {
throw new Error('LAST_LAYER_VERSION not found');
}
return parseInt(match[1], 10);
}
private async updateLastLayerVersion(newVersion: number) {
const filePath = this.getAbsoluteFilePath(
'src/engine/core-modules/serverless/drivers/layers/last-layer-version.ts',
);
await fs.writeFile(
filePath,
`export const LAST_LAYER_VERSION = ${newVersion};\n`,
'utf8',
);
}
}

View File

@ -8,6 +8,7 @@ import {
ServerlessModuleAsyncOptions,
} from 'src/engine/core-modules/serverless/serverless.interface';
import { ServerlessService } from 'src/engine/core-modules/serverless/serverless.service';
import { AddPackagesCommand } from 'src/engine/core-modules/serverless/commands/add-packages.command';
@Global()
export class ServerlessModule {
@ -27,7 +28,7 @@ export class ServerlessModule {
return {
module: ServerlessModule,
imports: options.imports || [],
providers: [ServerlessService, provider],
providers: [ServerlessService, provider, AddPackagesCommand],
exports: [ServerlessService],
};
}