diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 670fc55..bc19af5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: - run: yarn install --frozen-lockfile - run: yarn lint - run: yarn build + - run: yarn test - run: yarn verify || true - uses: actions/upload-artifact@v2 with: diff --git a/.gitignore b/.gitignore index b2eba31..f3a0c45 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ lerna-debug.log* /reports /.nyc_output test-report.html +test-folder # IDEs and editors .idea diff --git a/README.md b/README.md index d9b763b..87390ed 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,15 @@ -# Zambda: zipping things for AWS Lambda +# Zambda: zipping things for AWS Lambda +[![npm version](https://badge.fury.io/js/zambda.svg)](https://badge.fury.io/js/zambda) [![Build Status](https://github.com/bhoudu/zambda/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/bhoudu/zambda/actions?query=branch%3Adevelop) Zambda is a basic CLI to package files in a zip based on JSON file. -## Usage - Installation - yarn install zambda + yarn install -g zambda -Configuration example file +You then write a JSON zambda configuration file `lambda_handler.zambda.json` for instance. { "workDir": "zambda-dist", @@ -34,8 +33,8 @@ Configuration example file ] } -Example of command +You run zambda with your conf file as parameter. zambda lambda_handler.zambda.json -This will procuce a zip file `my_lambda.zip` in `zambda-dist` folder. \ No newline at end of file +This will procuce a zip file `my_lambda.zip` in `zambda-dist` folder as a result. \ No newline at end of file diff --git a/release.config.js b/release.config.js index 80a70b1..a68a789 100644 --- a/release.config.js +++ b/release.config.js @@ -28,7 +28,6 @@ module.exports = { ':zap:', ':fire:', ':alembic:', - ':tada:', ':rocket:', ], }, @@ -43,8 +42,12 @@ module.exports = { ':package:', ':recycle:', ':abc:', + ':pencil:', + ':recycle:', ":bento:", - ":arrow_up:" + ":arrow_up:", + ':wrench:', + ':robot:' ] } } @@ -67,7 +70,7 @@ module.exports = { 'README.md', 'package.json' ], - message: 'chore(release): Release <%= nextRelease.version %> - <%= new Date().toLocaleDateString(\'en-US\', {year: \'numeric\', month: \'short\', day: \'numeric\', hour: \'numeric\', minute: \'numeric\' }) %> [skip ci]\\n\\n<%= nextRelease.notes %>' + message: ':tada: Release <%= nextRelease.version %> - <%= new Date().toLocaleDateString(\'en-US\', {year: \'numeric\', month: \'short\', day: \'numeric\', hour: \'numeric\', minute: \'numeric\' }) %> [skip ci]\\n\\n<%= nextRelease.notes %>' } ], '@semantic-release/npm', diff --git a/src/zambda.ts b/src/zambda.ts index 30d0234..9030862 100644 --- a/src/zambda.ts +++ b/src/zambda.ts @@ -1,94 +1,6 @@ #!/usr/bin/env node -import fs from 'fs'; -import archiver from 'archiver'; - -interface ZambdaFile { - source: string; - destination: string; -} - -type ZambdaFolder = string | ZambdaFile; - -interface ZambdaZip { - name: string; - folders: ZambdaFolder[]; - files: ZambdaFile[]; -} - -interface ZambdaConfig { - workDir: string; - zip: ZambdaZip; -} - -async function zipLambda( - configFilePath: string, -): Promise { - const configJson: string = fs.readFileSync(configFilePath, { - encoding: 'UTF-8', - }); - if (!configJson) { - throw new Error('JSON file: ' + configFilePath + ' cannot be read!'); - } - - const zambdaConfig = JSON.parse(configJson) as ZambdaConfig; - const workPath = zambdaConfig.workDir.endsWith('/') ? zambdaConfig.workDir : zambdaConfig.workDir + '/'; - return new Promise((resolve, reject) => { - const destinationPath = workPath + zambdaConfig.zip.name; - - // Init archive - const output = fs.createWriteStream(destinationPath); - const archive = archiver('zip', { - zlib: { level: 9 }, // Sets the compression level. - }); - - // Listeners on zip - output.on('close', function close(): void { - console.log(archive.pointer() + ' total bytes'); - resolve(true); - }); - output.on('end', reject); - - // Listeners on archive - archive.on('warning', function warn(err): void { - if (err.code === 'ENOENT') { - // log warning - console.warn('Error found: ' + err); - return; - } - reject(err); - }); - archive.on('error', reject); - archive.pipe(output); - - // Append files - zambdaConfig.zip?.files.forEach(f => { - const filePath = f.source; - const fileName = !!f.destination ? f.destination : f.source; - archive.file(filePath, { name: fileName }); - }); - zambdaConfig.zip?.folders.forEach(f => { - const isString = typeof f === 'string' || f instanceof String; - if (isString) { - const folder = f as string; - const directory = folder.endsWith('/') ? f : f + '/'; - const destination = folder.endsWith('/') ? folder.substring(0, folder.length - 1) : folder; - archive.directory(directory, destination); - } else { - const zambdaFile = f as ZambdaFile; - const source = zambdaFile.source.endsWith('/') ? zambdaFile.source - : zambdaFile.source + '/'; - const destination = zambdaFile.destination.endsWith('/') ? - zambdaFile.destination.substring(0, zambdaFile.destination.length - 1) - : zambdaFile.destination; - archive.directory(source, destination); - } - }); - - // Do the archive! - archive.finalize(); - }); -} +import { zip } from "./zip"; const isOK = process.argv.length > 2; if (!isOK) { @@ -96,11 +8,14 @@ if (!isOK) { process.exit(1); } -// Generate package +// Zip! const confFile = process.argv[2]; -zipLambda(confFile).then(function handler(): void { - console.log('Zambda archive for conf: ' + confFile + ' has been generated!'); -}).catch(e => { - console.error(e + ''); - process.exit(1); -}); +zip(confFile) + .then(function handler(): void { + console.log('Zambda archive for conf: ' + confFile + ' has been generated!'); + process.exit(0); + }) + .catch(e => { + console.error(e + ''); + process.exit(1); + }); diff --git a/src/zip.spec.ts b/src/zip.spec.ts new file mode 100644 index 0000000..207e0c6 --- /dev/null +++ b/src/zip.spec.ts @@ -0,0 +1,29 @@ +import { zipWithConf } from "./zip"; + +describe('zip.ts', () => { + it('should zip file from given json conf', async () => { + const result = await zipWithConf({ + workDir: 'test-folder', + zip: { + name: 'test.zip', + files: [ + { + source: 'README.md' + }, + { + source: 'package.json', + destination: 'subpath/package.json', + } + ], + folders: [ + 'resources', + { + source: 'src', + destination: 'subpath/src', + } + ], + } + }); + expect(result).toBeTruthy(); + }, 10000); +}); diff --git a/src/zip.ts b/src/zip.ts new file mode 100644 index 0000000..39792ea --- /dev/null +++ b/src/zip.ts @@ -0,0 +1,108 @@ +import fs from "fs"; +import archiver from 'archiver'; +import * as mkdirp from "mkdirp"; + +interface ZambdaFile { + source: string; + destination?: string; +} + +type ZambdaFolder = string | ZambdaFile; + +interface ZambdaZip { + name: string; + folders: ZambdaFolder[]; + files: ZambdaFile[]; +} + +interface ZambdaConfig { + workDir: string; + zip: ZambdaZip; +} + +export function zipWithConf( + zambdaConfig: ZambdaConfig, +): Promise { + // Define workPath + const workPath = zambdaConfig.workDir.endsWith('/') ? zambdaConfig.workDir : zambdaConfig.workDir + '/'; + return new Promise((resolve, reject) => { + // Create work dir + mkdirp.sync(workPath); + + // Define target zip file + const destinationPath = workPath + zambdaConfig.zip.name; + + // Init archive + const output = fs.createWriteStream(destinationPath); + const archive = archiver('zip', { + zlib: { level: 9 }, // Sets the compression level. + }); + + // Listeners on zip + output.on('close', function close(): void { + console.log(archive.pointer() + ' total bytes'); + resolve(true); + }); + output.on('end', reject); + + // Listeners on archive + archive.on('warning', function warn(err): void { + if (err.code === 'ENOENT') { + // log warning + console.warn('Error found: ' + err); + return; + } + reject(err); + }); + archive.on('error', reject); + + // Append files + zambdaConfig.zip?.files.forEach(f => { + const filePath = f.source; + const fileName = !!f.destination ? f.destination : f.source; + archive.file(filePath, { name: fileName }); + }); + + // Append folders + zambdaConfig.zip?.folders.forEach(f => { + const isString = typeof f === 'string' || f instanceof String; + if (isString) { + const folder = f as string; + const directory = folder.endsWith('/') ? f : f + '/'; + const destination = folder.endsWith('/') ? folder.substring(0, folder.length - 1) : folder; + archive.directory(directory, destination); + } else { + const zambdaFile = f as ZambdaFile; + const source = zambdaFile.source.endsWith('/') ? zambdaFile.source + : zambdaFile.source + '/'; + const destination = zambdaFile.destination.endsWith('/') ? + zambdaFile.destination.substring(0, zambdaFile.destination.length - 1) + : zambdaFile.destination; + archive.directory(source, destination); + } + }); + + // Pipe all the files + archive.pipe(output); + + // Do the archive! + archive.finalize(); + }); +} + +export async function zip( + configFilePath: string, +): Promise { + const configJson: string = fs.readFileSync(configFilePath, { + encoding: 'UTF-8', + }); + if (!configJson) { + throw new Error('JSON file: ' + configFilePath + ' cannot be read!'); + } + // Parse configuration + const zambdaConfig = JSON.parse(configJson) as ZambdaConfig; + return zipWithConf(zambdaConfig).then(() => { + console.log('gfg'); + return true; + }); +}