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:
@ -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',
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user