From 9604815a39d04cb0af22b3bca69723c0f29a0753 Mon Sep 17 00:00:00 2001 From: Zitao Xiong Date: Thu, 16 Jan 2025 16:20:52 +0800 Subject: [PATCH] use biome --- .envrc | 7 +- deploy/biome.jsonc | 7 +- deploy/bun.lockb | Bin 121291 -> 124793 bytes deploy/package.json | 1 + deploy/src/gen-config.js | 83 ++++++++----- deploy/src/index.ts | 250 +++++++++++++++++++-------------------- deploy/src/keys.ts | 28 ++--- deploy/src/utils.ts | 101 ++++++++-------- deploy/tsconfig.json | 30 ++--- 9 files changed, 262 insertions(+), 245 deletions(-) diff --git a/.envrc b/.envrc index 73981c3..c37e21b 100644 --- a/.envrc +++ b/.envrc @@ -9,6 +9,7 @@ export BITCOIND_IMAGE=caoer/bitcoind export WORKSPACE_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" PATH_add $WORKSPACE_ROOT/tools/bin +PATH_add $WORKSPACE_ROOT/deploy/node_modules/.bin export OPI_PG_DATA_PATH="${WORKSPACE_ROOT}/data/opi/postgres-data" export OPI_BITCOIND_PATH="${WORKSPACE_ROOT}/data/bitcoind" @@ -26,13 +27,13 @@ export API_TRUSTED_PROXY_CNT="0" export DB_METAPROTOCOL_USER="postgres" export DB_METAPROTOCOL_HOST="postgres-opi-server" -export DB_METAPROTOCOL_PORT="5432" +export DB_METAPROTOCOL_PORT="5432" export DB_METAPROTOCOL_DATABASE="db_opi" export DB_METAPROTOCOL_PASSWD="passwd_123!@#" export API_HOST="127.0.0.1" export API_PORT="8001" -export API_TRUSTED_PROXY_CNT="0" +export API_TRUSTED_PROXY_CNT="0" export NETWORK_TYPE="mainnet" @@ -77,4 +78,4 @@ if [[ -f .envrc.override ]]; then source_env .envrc.override fi -configs/gen.sh \ No newline at end of file +configs/gen.sh diff --git a/deploy/biome.jsonc b/deploy/biome.jsonc index 63ac31d..5139393 100644 --- a/deploy/biome.jsonc +++ b/deploy/biome.jsonc @@ -7,12 +7,7 @@ }, "files": { "ignoreUnknown": false, - "ignore": [ - "data/**/*", - ".cursorrules", - "**/dist/**", - "**/node_modules/**" - ] + "ignore": ["data/**/*", ".cursorrules", "**/dist/**", "**/node_modules/**"] }, "formatter": { "enabled": true, diff --git a/deploy/bun.lockb b/deploy/bun.lockb index 8f996d9c132ad030528bfcb1bed0d4d7a3e527a4..752d4e9237c4c95c46d315cd6c7c9b981db7261f 100755 GIT binary patch delta 19438 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 { - const { serviceName, region, size } = answers; - const outputTemplate = ` +inquirer + .prompt([questionService, questionRegion, questionSize]) + .then((answers) => { + const { serviceName, region, size } = answers; + const outputTemplate = ` services: ${serviceName}: region: '${region}' size: '${size}' `; - const outputPath = path.resolve(__dirname, 'config.user.yaml'); - fs.writeFileSync(outputPath, outputTemplate); - console.log(`file generated at: ${path.resolve(outputPath)}`); - }); - - \ No newline at end of file + const outputPath = path.resolve(__dirname, 'config.user.yaml'); + fs.writeFileSync(outputPath, outputTemplate); + console.log(`file generated at: ${path.resolve(outputPath)}`); + }); diff --git a/deploy/src/index.ts b/deploy/src/index.ts index 13b0695..8d8868e 100644 --- a/deploy/src/index.ts +++ b/deploy/src/index.ts @@ -1,4 +1,11 @@ -import * as digitalocean from '@pulumi/digitalocean' +import { createHash } from 'node:crypto'; +import fs from 'node:fs'; +import { local, remote } from '@pulumi/command'; +import type { types } from '@pulumi/command'; +import * as digitalocean from '@pulumi/digitalocean'; +import * as pulumi from '@pulumi/pulumi'; +import * as time from '@pulumiverse/time'; +import YAML from 'yaml'; import { generateDirectoryHash, getScript, @@ -6,28 +13,22 @@ import { root$, transformFile, unroot, -} from './utils' -import * as pulumi from '@pulumi/pulumi' -import fs from 'fs' -import { local, remote, types } from '@pulumi/command' -import { createHash } from 'crypto' -import * as time from '@pulumiverse/time' -import YAML from 'yaml' +} from './utils'; -import { getPrivateKey, sshKey } from './keys' +import { getPrivateKey, sshKey } from './keys'; export function provisionInstance(params: { - name: string - connection: types.input.remote.ConnectionArgs + name: string; + connection: types.input.remote.ConnectionArgs; }) { - const { connection, name } = params + const { connection, name } = params; const setupCommands = execScriptsOnRemote(name, connection, [ root('deploy/src/provision/configure-apt-mock.sh'), root('deploy/src/provision/configure-apt.sh'), root('deploy/src/provision/setup.sh'), root('deploy/src/provision/pull.sh'), - ]) + ]); const reboot = new remote.Command( `${name}:reboot`, @@ -37,7 +38,7 @@ export function provisionInstance(params: { environment: { DEBIAN_FRONTEND: 'noninteractive' }, }, { dependsOn: setupCommands }, - ) + ); const wait = new time.Sleep( `${name}:wait60Seconds`, @@ -45,7 +46,7 @@ export function provisionInstance(params: { { dependsOn: [reboot], }, - ) + ); return execScriptOnRemote( name, @@ -54,7 +55,7 @@ export function provisionInstance(params: { { commandOpts: { dependsOn: [wait] }, }, - ) + ); } export function execScriptsOnRemote( @@ -62,23 +63,23 @@ export function execScriptsOnRemote( connection: types.input.remote.ConnectionArgs, locations: string[], ) { - let command: remote.Command | null = null - const commands: remote.Command[] = [] + let command: remote.Command | null = null; + const commands: remote.Command[] = []; for (const loc of locations) { if (command == null) { - command = execScriptOnRemote(name, connection, loc) + command = execScriptOnRemote(name, connection, loc); } else { command = execScriptOnRemote(name, connection, loc, { commandOpts: { dependsOn: [command], }, - }) + }); } - commands.push(command) + commands.push(command); } - return commands + return commands; } export function execScriptOnRemote( @@ -86,15 +87,14 @@ export function execScriptOnRemote( connection: types.input.remote.ConnectionArgs, loc: string, options: { - cwd?: pulumi.Output - commandOpts?: pulumi.CustomResourceOptions + cwd?: pulumi.Output; + commandOpts?: pulumi.CustomResourceOptions; } = {}, ) { - // cwd is the CWD - const createContent = fs.readFileSync(loc, 'utf-8') + const createContent = fs.readFileSync(loc, 'utf-8'); const createContentHash = createHash('md5') .update(createContent) - .digest('hex') + .digest('hex'); if (options.cwd) { return new remote.Command( @@ -110,31 +110,31 @@ export function execScriptOnRemote( customTimeouts: { create: '240m' }, ...options.commandOpts, }, - ) - } else { - return new remote.Command( - `${name}:run:remote: ${unroot(loc)}`, - { - connection, - create: createContent, - triggers: [createContentHash, loc], - }, - { - customTimeouts: { create: '240m' }, - ...options.commandOpts, - }, - ) + ); } + + return new remote.Command( + `${name}:run:remote: ${unroot(loc)}`, + { + connection, + create: createContent, + triggers: [createContentHash, loc], + }, + { + customTimeouts: { create: '240m' }, + ...options.commandOpts, + }, + ); } -const image = 'ubuntu-22-04-x64' +const image = 'ubuntu-22-04-x64'; export function create(params: { name: string; region: string; size: string }) { - const { region, size, name } = params + const { region, size, name } = params; const snapshotId = (() => { - const id = process.env['OPI_VOLUME_SNAPSHOT_ID'] - return id?.length == 0 ? undefined : id - })() + const id = process.env.OPI_VOLUME_SNAPSHOT_ID; + return id?.length === 0 ? undefined : id; + })(); // create instance @@ -143,47 +143,47 @@ export function create(params: { name: string; region: string; size: string }) { region, size, sshKeys: [sshKey.id], - }) - const privateKey = getPrivateKey() + }); + const privateKey = getPrivateKey(); const connection: types.input.remote.ConnectionArgs = { host: droplet.ipv4Address, user: 'root', privateKey, dialErrorLimit: 50, - } + }; - const provision = provisionInstance({ name, connection }) + const provision = provisionInstance({ name, connection }); const copyConfigDir = (loc: string, remotePath: pulumi.Output) => { if (!fs.existsSync(loc)) { - throw new Error(`not found: ${loc}`) + throw new Error(`not found: ${loc}`); } - const hash = generateDirectoryHash(loc).slice(0, 5) + const hash = generateDirectoryHash(loc).slice(0, 5); return new local.Command(`${name}:copyFiles ${unroot(loc)}`, { - create: pulumi.interpolate`rsync -avP -e "ssh -i ${process.env['PRIVATE_KEY_PATH']}" ${loc} ${connection.user}@${droplet.ipv4Address}:${remotePath}`, + create: pulumi.interpolate`rsync -avP -e "ssh -i ${process.env.PRIVATE_KEY_PATH}" ${loc} ${connection.user}@${droplet.ipv4Address}:${remotePath}`, triggers: [hash, loc, remotePath], - }) - } + }); + }; const volume = new digitalocean.Volume( `${name}volume`, { region, - size: parseInt(process.env['OPI_VOLUME_SIZE'] ?? '1000', 10), + size: Number.parseInt(process.env.OPI_VOLUME_SIZE ?? '1000', 10), initialFilesystemType: 'ext4', snapshotId, }, { dependsOn: [provision, droplet] }, - ) + ); // mount disk const volumeAttachment = new digitalocean.VolumeAttachment( `${name}-volume-attachment`, { - dropletId: droplet.id.apply((id) => parseInt(id, 10)), + dropletId: droplet.id.apply((id) => Number.parseInt(id, 10)), volumeId: volume.id, }, - ) + ); const volumePathPrint = new remote.Command( `${name}-read-volume-path`, @@ -195,7 +195,7 @@ export function create(params: { name: string; region: string; size: string }) { dependsOn: [droplet, volumeAttachment, volume], customTimeouts: { create: '5m' }, }, - ) + ); // cp restore files const cpRestoreDockerCompose = volumePathPrint.stdout.apply((volumeName) => { @@ -204,41 +204,36 @@ export function create(params: { name: string; region: string; size: string }) { './src/docker-composes/restore.docker-compose.yaml', [ ['${OPI_PG_DATA_PATH}', `${volumeName}/pg_data`], - ['${OPI_IMAGE}', process.env['OPI_IMAGE']!], - // DB_USER - ['${DB_USER}', process.env['DB_USER']!], - // DB_PASSWORD - ['${DB_PASSWD}', process.env['DB_PASSWD']!], - // DB_DATABASE - ['${DB_DATABASE}', process.env['DB_DATABASE']!], - // WORKSPACE_ROOT + ['${OPI_IMAGE}', process.env.OPI_IMAGE!], + ['${DB_USER}', process.env.DB_USER!], + ['${DB_PASSWD}', process.env.DB_PASSWD!], + ['${DB_DATABASE}', process.env.DB_DATABASE!], ['${WORKSPACE_ROOT}', volumeName], - // ORD_DATADIR ['${ORD_DATADIR}', `${volumeName}/ord_data`], ], - ) + ); - const file = fs.readFileSync(localPath, 'utf-8') - const hash = createHash('md5').update(file).digest('hex').slice(0, 5) - const remotePath = `${volumeName}/restore.docker-compose.yaml` + const file = fs.readFileSync(localPath, 'utf-8'); + const hash = createHash('md5').update(file).digest('hex').slice(0, 5); + const remotePath = `${volumeName}/restore.docker-compose.yaml`; return new remote.CopyFile(`${name}:restore`, { connection, localPath, remotePath, triggers: [hash, localPath], - }) - }) + }); + }); const cpConfig = copyConfigDir( root('configs'), pulumi.interpolate`${volumePathPrint.stdout}`, - ) + ); // create swap space execScriptOnRemote(name, connection, root('deploy/src/scripts/mkswap.sh'), { commandOpts: { dependsOn: [provision] }, - }) + }); // restore pg database and ord_data const restore = execScriptOnRemote( @@ -251,7 +246,7 @@ export function create(params: { name: string; region: string; size: string }) { dependsOn: [cpConfig, cpRestoreDockerCompose], }, }, - ) + ); // cp service docker-compose file /** @@ -266,30 +261,21 @@ export function create(params: { name: string; region: string; size: string }) { [ ['${OPI_PG_DATA_PATH}', `${volumeName}/pg_data`], ['${OPI_BITCOIND_PATH}', `${volumeName}/bitcoind_data`], - ['${OPI_IMAGE}', process.env['OPI_IMAGE']!], - ['${BITCOIND_IMAGE}', process.env['BITCOIND_IMAGE']!], - // DB_USER - ['${DB_USER}', process.env['DB_USER']!], - // DB_PASSWORD - ['${DB_PASSWD}', process.env['DB_PASSWD']!], - // DB_DATABASE - ['${DB_DATABASE}', process.env['DB_DATABASE']!], - // WORKSPACE_ROOT + ['${OPI_IMAGE}', process.env.OPI_IMAGE!], + ['${BITCOIND_IMAGE}', process.env.BITCOIND_IMAGE!], + ['${DB_USER}', process.env.DB_USER!], + ['${DB_PASSWD}', process.env.DB_PASSWD!], + ['${DB_DATABASE}', process.env.DB_DATABASE!], ['${WORKSPACE_ROOT}', volumeName], - // BITCOIN_RPC_USER - ['${BITCOIN_RPC_USER}', process.env['BITCOIN_RPC_USER']!], - // BITCOIN_RPC_PASSWD - ['${BITCOIN_RPC_PASSWD}', process.env['BITCOIN_RPC_PASSWD']!], - // BITCOIN_RPC_POR - ['${BITCOIN_RPC_PORT}', process.env['BITCOIN_RPC_PORT']!], - // ORD_DATADIR + ['${BITCOIN_RPC_USER}', process.env.BITCOIN_RPC_USER!], + ['${BITCOIN_RPC_PASSWD}', process.env.BITCOIN_RPC_PASSWD!], + ['${BITCOIN_RPC_PORT}', process.env.BITCOIN_RPC_PORT!], ['${ORD_DATADIR}', `${volumeName}/ord_data`], - // BITCOIN_CHAIN_FOLDER ['${BITCOIN_CHAIN_FOLDER}', `${volumeName}/bitcoind_data/datadir`], ], - ) - const file = fs.readFileSync(localPath, 'utf-8') - const hash = createHash('md5').update(file).digest('hex').slice(0, 5) + ); + const file = fs.readFileSync(localPath, 'utf-8'); + const hash = createHash('md5').update(file).digest('hex').slice(0, 5); const cpDockerCompose = new remote.CopyFile( `${name}:cp:opi.docker-compose. -> ${volumeName}`, @@ -300,7 +286,7 @@ export function create(params: { name: string; region: string; size: string }) { triggers: [hash, localPath], }, { dependsOn: [restore] }, - ) + ); // start opi new remote.Command( @@ -311,67 +297,69 @@ export function create(params: { name: string; region: string; size: string }) { triggers: [hash], }, { dependsOn: [cpDockerCompose] }, - ) - }) + ); + }); - exports[`ip_${name}`] = droplet.ipv4Address - exports[`name_${name}`] = droplet.name - exports[`volume_id_${name}`] = volume.id - exports[`volume_attachment_id_${name}`] = volumeAttachment.id - exports[`volume_path_${name}`] = volumePathPrint.stdout + exports[`ip_${name}`] = droplet.ipv4Address; + exports[`name_${name}`] = droplet.name; + exports[`volume_id_${name}`] = volume.id; + exports[`volume_attachment_id_${name}`] = volumeAttachment.id; + exports[`volume_path_${name}`] = volumePathPrint.stdout; - return { droplet, volume, name } + return { droplet, volume, name }; } -type Instance = { - name: string - region: string - size: string +interface Instance { + name: string; + region: string; + size: string; } -function validateInstance(instance: any): instance is Instance { +function validateInstance(instance: unknown): instance is Instance { return ( - typeof instance.name === 'string' && - typeof instance.region === 'string' && - typeof instance.size === 'string' - ) + typeof instance === 'object' && + instance !== null && + 'name' in instance && + 'region' in instance && + 'size' in instance && + typeof (instance as Instance).name === 'string' && + typeof (instance as Instance).region === 'string' && + typeof (instance as Instance).size === 'string' + ); } function readYamlAndCreateInstance() { // read yaml file - const file = (() => { if (fs.existsSync(root$('deploy/src/config.user.yaml'))) { - return fs.readFileSync(root('deploy/src/config.user.yaml'), 'utf8') + return fs.readFileSync(root('deploy/src/config.user.yaml'), 'utf8'); } - - return fs.readFileSync(root('deploy/src/config.yaml'), 'utf8') - })() + return fs.readFileSync(root('deploy/src/config.yaml'), 'utf8'); + })(); // parse yaml file - const data = YAML.parse(file) + const data = YAML.parse(file); + const instances = []; - let instances = [] - - for (let serviceName in data.services) { + for (const serviceName in data.services) { // validate required fields const instance = { name: serviceName, region: data.services[serviceName].region, size: data.services[serviceName].size, - } + }; if (validateInstance(instance)) { // create instance and push to instances array - instances.push(create(instance)) + instances.push(create(instance)); } else { - throw new Error(`Invalid instance data '${JSON.stringify(instance)}'`) + throw new Error(`Invalid instance data '${JSON.stringify(instance)}'`); } } - return instances + return instances; } -const instances = readYamlAndCreateInstance() +const instances = readYamlAndCreateInstance(); -console.log(`created: ${instances.length} instances`) +console.log(`created: ${instances.length} instances`); diff --git a/deploy/src/keys.ts b/deploy/src/keys.ts index 59c7745..73a4be4 100644 --- a/deploy/src/keys.ts +++ b/deploy/src/keys.ts @@ -1,27 +1,27 @@ -import * as digitalocean from '@pulumi/digitalocean' -import path from 'path' -import os from 'os' -import fs from 'fs' +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import * as digitalocean from '@pulumi/digitalocean'; -export const id = process.env['DIGITAL_OCEAN_SSH_KEY_ID']! -export const name = process.env['DIGITAL_OCEAN_SSH_KEY_NAME']! +export const id = process.env.DIGITAL_OCEAN_SSH_KEY_ID!; +export const name = process.env.DIGITAL_OCEAN_SSH_KEY_NAME!; -export const sshKey = digitalocean.SshKey.get(name, id) +export const sshKey = digitalocean.SshKey.get(name, id); export const getPrivateKey = () => { // Assuming your environment variable is named 'PRIVATE_KEY_PATH' - const privateKeyPath = process.env['PRIVATE_KEY_PATH'] + const privateKeyPath = process.env.PRIVATE_KEY_PATH; if (!privateKeyPath) { - console.error('The environment variable PRIVATE_KEY_PATH is not set.') - process.exit(1) // Exit with an error code + console.error('The environment variable PRIVATE_KEY_PATH is not set.'); + process.exit(1); // Exit with an error code } // Handles the tilde by replacing it with the user's home directory const resolvedPrivateKeyPath = privateKeyPath.startsWith('~') ? path.join(os.homedir(), privateKeyPath.slice(1)) - : path.resolve(privateKeyPath) + : path.resolve(privateKeyPath); - const key = fs.readFileSync(resolvedPrivateKeyPath, 'utf-8') + const key = fs.readFileSync(resolvedPrivateKeyPath, 'utf-8'); - return key -} + return key; +}; diff --git a/deploy/src/utils.ts b/deploy/src/utils.ts index 4c15c55..0fb1116 100644 --- a/deploy/src/utils.ts +++ b/deploy/src/utils.ts @@ -1,45 +1,50 @@ -import assert from 'assert' -import path, { join } from 'path' -import fs, { readdirSync, readFileSync, statSync } from 'fs' -import { id, name } from './keys' -import { createHash } from 'crypto' -import * as digitalocean from '@pulumi/digitalocean' +import assert from 'node:assert'; +import { createHash } from 'node:crypto'; +import fs, { readdirSync, readFileSync, statSync } from 'node:fs'; +import path, { join } from 'node:path'; +import * as digitalocean from '@pulumi/digitalocean'; +import { id, name } from './keys'; export function root(filePath: string) { - const p = path.resolve(__dirname, `../../${filePath}`) + const p = path.resolve(__dirname, `../../${filePath}`); if (fs.existsSync(p)) { - return p + return p; } - throw new Error(`File not found: ${p}`) + throw new Error(`File not found: ${p}`); } export function root$(filePath: string) { - return path.resolve(__dirname, `../../${filePath}`) + return path.resolve(__dirname, `../../${filePath}`); } // convert the absolute path from root(filePath: string) to relative path // example: unroot(root(a)) === a export function unroot(filePath: string) { - return filePath.replace(root('') + '/', '') + return filePath.replace(root('') + '/', ''); } -assert(id, 'DIGITAL_OCEAN_SSH_KEY_ID is required') -assert(name, 'DIGITAL_OCEAN_SSH_KEY_NAME is required') +assert(id, 'DIGITAL_OCEAN_SSH_KEY_ID is required'); +assert(name, 'DIGITAL_OCEAN_SSH_KEY_NAME is required'); export function getScript(scriptName: string) { - return fs.readFileSync(`./src/scripts/${scriptName}`, 'utf-8') -} // write takeSnapshot function which input is the output of function create. -function takeSnapshot(params: { name: string; volume: digitalocean.Volume }) { - const { name, volume } = params + return fs.readFileSync(`./src/scripts/${scriptName}`, 'utf-8'); +} + +// write takeSnapshot function which input is the output of function create. +export function takeSnapshot(params: { + name: string; + volume: digitalocean.Volume; +}) { + const { name, volume } = params; const createSnapshot = new digitalocean.VolumeSnapshot(`${name}-snapshot`, { volumeId: volume.id, name: `${name}-snapshot`, - }) + }); - exports[`volume_snapshot_${name}`] = createSnapshot.id + exports[`volume_snapshot_${name}`] = createSnapshot.id; - return { createSnapshot } + return { createSnapshot }; } export function transformFile( @@ -48,62 +53,62 @@ export function transformFile( transforms: string[][], ): string { // Read the content of the source file - const content = fs.readFileSync(filePath, 'utf8') + const content = fs.readFileSync(filePath, 'utf8'); // Apply all transformations - let transformedContent = content + let transformedContent = content; for (const transform of transforms) { - const [original, replacement] = transform - transformedContent = transformedContent.split(original).join(replacement) + const [original, replacement] = transform; + transformedContent = transformedContent.split(original).join(replacement); } // Create a temp file in a random location - const tempDir = createTmpDirFromSeed(filePath + seed) - const tempFilePath = path.join(tempDir, path.basename(filePath)) + const tempDir = createTmpDirFromSeed(filePath + seed); + const tempFilePath = path.join(tempDir, path.basename(filePath)); // Write the transformed content to the temp file - fs.writeFileSync(tempFilePath, transformedContent) + fs.writeFileSync(tempFilePath, transformedContent); // Return the path of the temp file - return tempFilePath + return tempFilePath; } const createTmpDirFromSeed = (seed: string): string => { - const hash = createHash('sha256').update(seed).digest('hex') - const tmpBaseDir = '/tmp' - const dirPath = join(tmpBaseDir, hash) + const hash = createHash('sha256').update(seed).digest('hex'); + const tmpBaseDir = '/tmp'; + const dirPath = join(tmpBaseDir, hash); try { - fs.mkdirSync(dirPath, { recursive: true }) - return dirPath + fs.mkdirSync(dirPath, { recursive: true }); + return dirPath; } catch (error) { - throw new Error(`Failed to create temp directory: ${error}`) + throw new Error(`Failed to create temp directory: ${error}`); } -} +}; export function hashFile(filePath: string): string { - const fileBuffer = readFileSync(filePath) - const hashSum = createHash('sha256') - hashSum.update(fileBuffer) - return hashSum.digest('hex') + const fileBuffer = readFileSync(filePath); + const hashSum = createHash('sha256'); + hashSum.update(fileBuffer); + return hashSum.digest('hex'); } export function generateDirectoryHash(dirPath: string): string { - let hashString = '' + let hashString = ''; - const files = readdirSync(dirPath) + const files = readdirSync(dirPath); for (const file of files) { - const filePath = join(dirPath, file) - const fileStat = statSync(filePath) + const filePath = join(dirPath, file); + const fileStat = statSync(filePath); if (fileStat.isDirectory()) { - hashString += `${file}:${generateDirectoryHash(filePath)}` + hashString += `${file}:${generateDirectoryHash(filePath)}`; } else { - hashString += `${file}:${hashFile(filePath)}` + hashString += `${file}:${hashFile(filePath)}`; } } - const hashSum = createHash('sha256') - hashSum.update(hashString) - return hashSum.digest('hex') + const hashSum = createHash('sha256'); + hashSum.update(hashString); + return hashSum.digest('hex'); } diff --git a/deploy/tsconfig.json b/deploy/tsconfig.json index 165fbba..d7de85e 100644 --- a/deploy/tsconfig.json +++ b/deploy/tsconfig.json @@ -1,17 +1,17 @@ { - "compilerOptions": { - "strict": true, - "outDir": "bin", - "target": "ES2020", - "module": "commonjs", - "moduleResolution": "node", - "sourceMap": true, - "experimentalDecorators": true, - "esModuleInterop": true, - "pretty": true, - "noFallthroughCasesInSwitch": true, - "noImplicitReturns": true, - "forceConsistentCasingInFileNames": true - }, - "include": ["src"] + "compilerOptions": { + "strict": true, + "outDir": "bin", + "target": "ES2020", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "pretty": true, + "noFallthroughCasesInSwitch": true, + "noImplicitReturns": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] }