diff --git a/README.md b/README.md index 1e543ab0..dd50f8f4 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,16 @@ custom: pythonBin: /opt/python3.6/bin/python ``` +### Run Custom extra packaging scripts +As a convenience method for if you need to install system dependencies for your python package you +can specify a command to do so. It honors the `dockerizePip` command so you can `yum install` +anything available in Amazon Linux if using that option. Example config: +```yaml +custom: + pythonRequirements: + prereqCmd: bash -c "yum install mysql-devel && cp /usr/lib64/mysql/libmysqlclient.so.18.0.0 ./libmysqlclient.so.18" +``` + ## Manual invocations The `.requirements` and `requirements.zip`(if using zip support) files are left diff --git a/example/prereq b/example/prereq new file mode 100755 index 00000000..033ce159 --- /dev/null +++ b/example/prereq @@ -0,0 +1,2 @@ +#!/bin/bash +echo foobar > foobar diff --git a/example/serverless.yml b/example/serverless.yml index 5062d1fa..81c4a5c8 100644 --- a/example/serverless.yml +++ b/example/serverless.yml @@ -11,6 +11,7 @@ custom: zip: false cleanupZipHelper: true dockerizePip: false + prereqCmd: null package: exclude: diff --git a/index.js b/index.js index c9429cdc..ec2f2051 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ const BbPromise = require('bluebird'); const fse = require('fs-extra'); const {addVendorHelper, removeVendorHelper, packRequirements} = require('./lib/zip'); const {installRequirements} = require('./lib/pip'); +const {runPrereqCmd} = require('./lib/prereqCmd'); const {pipfileToRequirements} = require('./lib/pipenv'); const {linkRequirements, unlinkRequirements} = require('./lib/link'); const {cleanup} = require('./lib/clean'); @@ -30,6 +31,7 @@ class ServerlessPythonRequirements { dockerizePip: false, dockerImage: `lambci/lambda:build-${this.serverless.service.provider.runtime}`, pipCmdExtraArgs: [], + prereqCmd: null, noDeploy: [ 'boto3', 'botocore', @@ -80,6 +82,7 @@ class ServerlessPythonRequirements { .then(pipfileToRequirements) .then(addVendorHelper) .then(installRequirements) + .then(runPrereqCmd) .then(packRequirements) .then(linkRequirements); @@ -105,6 +108,7 @@ class ServerlessPythonRequirements { 'requirements:install:install': () => BbPromise.bind(this) .then(pipfileToRequirements) .then(addVendorHelper) + .then(runPrereqCmd) .then(installRequirements) .then(packRequirements), 'requirements:clean:clean': () => BbPromise.bind(this) diff --git a/lib/prereqCmd.js b/lib/prereqCmd.js new file mode 100644 index 00000000..b460d111 --- /dev/null +++ b/lib/prereqCmd.js @@ -0,0 +1,44 @@ +const fse = require('fs-extra'); +const path = require('path'); +const {spawnSync} = require('child_process'); + +/** + * run a prerequisite script if provided + * @return {Promise} + */ +function runPrereqCmd() { + const {prereqCmd} = this.options; + if (!prereqCmd) + return; + this.serverless.cli.log(`Running prerequisite script: ${prereqCmd}`); + let options = []; + if (this.options.dockerizePip) { + cmd = 'docker'; + options = [ + 'run', '--rm', + '-v', `${this.servicePath}:/var/task:z`, + prereqCmd, + ]; + if (process.platform === 'linux') + options.push('-u', `${process.getuid()}:${process.getgid()}`); + options.push(this.options.dockerImage); + options.push(...pipCmd); + } else { + cmd = prereqCmd; + } + const res = spawnSync(`${cmd} ${options.join(' ')}`, {shell: true, cwd: this.servicePath}); + if (res.error) { + if (res.error.code === 'ENOENT') { + if (this.options.dockerizePip) { + throw new Error('docker not found! Please install it.'); + } else { + throw new Error(`${prereqCmd} not found!`); + } + } + throw new Error(res.error); + } + if (res.status != 0) + throw new Error(res.stderr); +}; + +module.exports = {runPrereqCmd}; diff --git a/test.bats b/test.bats index e0593a6e..e7b2cb7f 100755 --- a/test.bats +++ b/test.bats @@ -14,6 +14,7 @@ setup() { teardown() { sls requirements clean rm -rf puck node_modules + rm -f foobar if [ -f serverless.yml.bak ]; then mv serverless.yml.bak serverless.yml; fi } @@ -148,3 +149,17 @@ teardown() { unzip .serverless/sls-py-req-test.zip -d puck ls puck/.requirements.zip puck/unzip_requirements.py } + +@test "py3.6 runs prereq script" { + sed -i'.bak' -e 's;prereqCmd: *null;prereqCmd: ./prereq;' serverless.yml + sls package + unzip .serverless/sls-py-req-test.zip -d puck + ls foobar +} + +@test "py3.6 runs prereq command" { + sed -i'.bak' -e 's;prereqCmd: *null;prereqCmd: touch foobar;' serverless.yml + sls package + unzip .serverless/sls-py-req-test.zip -d puck + ls foobar +}