diff --git a/README.md b/README.md index 11e8ae5d..34878bf2 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,9 @@ the following packages are omitted as they are already installed on Lambda: * s3transfer * setuptools * six + +Alternatively the `extendNoDeploy` option allows you to omit additional packages +as well as those on teh default list. This example makes it instead omit pytest: ```yaml @@ -228,6 +231,14 @@ custom: - pytest ``` +This example makes it omit numpy in addition to the above list: +```yaml +custom: + pythonRequirements: + extendNoDeploy: + - numpy +``` + To include the default omitted packages, set the `noDeploy` option to an empty list: ```yaml diff --git a/index.js b/index.js index b5200285..f2950e7e 100644 --- a/index.js +++ b/index.js @@ -64,6 +64,7 @@ class ServerlessPythonRequirements { 'pip', 'setuptools' ], + extendNoDeploy: [], vendor: '' }, (this.serverless.service.custom && @@ -104,6 +105,9 @@ class ServerlessPythonRequirements { options.layer = {}; } } + if (options.extendNoDeploy.length > 0) { + options.noDeploy.push(...options.extendNoDeploy); + } return options; } diff --git a/lib/inject.js b/lib/inject.js index dc30be79..374a84b7 100644 --- a/lib/inject.js +++ b/lib/inject.js @@ -9,6 +9,45 @@ const { writeZip, zipFile } = require('./zipTree'); BbPromise.promisifyAll(fse); +/** + * Inject requirements into JSZip object + * @param {JSZip} zip js + * @param {string} requirementsPath requirements folder path + * @param {Object} options our options object + * @return {Promise} the JSZip object constructed. + */ +function injectRequirementsToZip(zip, requirementsPath, options) { + const noDeploy = new Set(options.noDeploy || []); + const runtimePath = 'python'; + + return BbPromise.resolve( + glob.sync([path.join(requirementsPath, '**')], { + mark: true, + dot: true + }) + ) + .map(file => [file, path.relative(requirementsPath, file)]) + .filter( + ([file, relativeFile]) => + !file.endsWith('/') && + !relativeFile.match(/^__pycache__[\\/]/) && + !noDeploy.has(relativeFile.split(/([-\\/]|\.py$|\.pyc$)/, 1)[0]) + ) + .map(([file, relativeFile]) => + Promise.all([ + file, + options.layer ? path.join(runtimePath, relativeFile) : relativeFile, + fse.statAsync(file) + ]) + ) + .map(([file, relativeFile, fileStat]) => + zipFile(zip, relativeFile, fse.readFileAsync(file), { + unixPermissions: fileStat.mode + }) + ) + .then(() => zip); +} + /** * Inject requirements into packaged application. * @param {string} requirementsPath requirements folder path @@ -17,35 +56,11 @@ BbPromise.promisifyAll(fse); * @return {Promise} the JSZip object constructed. */ function injectRequirements(requirementsPath, packagePath, options) { - const noDeploy = new Set(options.noDeploy || []); - return fse .readFileAsync(packagePath) .then(buffer => JSZip.loadAsync(buffer)) - .then(zip => - BbPromise.resolve( - glob.sync([path.join(requirementsPath, '**')], { - mark: true, - dot: true - }) - ) - .map(file => [file, path.relative(requirementsPath, file)]) - .filter( - ([file, relativeFile]) => - !file.endsWith('/') && - !relativeFile.match(/^__pycache__[\\/]/) && - !noDeploy.has(relativeFile.split(/([-\\/]|\.py$|\.pyc$)/, 1)[0]) - ) - .map(([file, relativeFile]) => - Promise.all([file, relativeFile, fse.statAsync(file)]) - ) - .map(([file, relativeFile, fileStat]) => - zipFile(zip, relativeFile, fse.readFileAsync(file), { - unixPermissions: fileStat.mode - }) - ) - .then(() => writeZip(zip, packagePath)) - ); + .then(zip => injectRequirementsToZip(zip, requirementsPath, options)) + .then(zip => writeZip(zip, packagePath)); } /** @@ -130,4 +145,7 @@ function injectAllRequirements(funcArtifact) { } } -module.exports = { injectAllRequirements }; +module.exports = { + injectAllRequirements, + injectRequirementsToZip +}; diff --git a/lib/layer.js b/lib/layer.js index 06c70871..19bc5e7c 100644 --- a/lib/layer.js +++ b/lib/layer.js @@ -2,7 +2,8 @@ const BbPromise = require('bluebird'); const fse = require('fs-extra'); const path = require('path'); const JSZip = require('jszip'); -const { writeZip, addTree } = require('./zipTree'); +const { writeZip } = require('./zipTree'); +const { injectRequirementsToZip } = require('./inject'); BbPromise.promisifyAll(fse); @@ -13,10 +14,9 @@ BbPromise.promisifyAll(fse); function zipRequirements() { const rootZip = new JSZip(); const src = path.join('.serverless', 'requirements'); - const runtimepath = 'python'; - return addTree(rootZip.folder(runtimepath), src).then(() => - writeZip(rootZip, path.join('.serverless', 'pythonRequirements.zip')) + return injectRequirementsToZip(rootZip, src, this.options).then(zip => + writeZip(zip, path.join('.serverless', 'pythonRequirements.zip')) ); }