diff --git a/lib/docker.js b/lib/docker.js index 74316283..8fba07ca 100644 --- a/lib/docker.js +++ b/lib/docker.js @@ -101,4 +101,16 @@ function getBindPath(servicePath) { throw new Error('Unable to find good bind path format'); }; -module.exports = {buildImage, getBindPath}; +/** + * Find out what uid the docker machine is using + * @param {string} bindPath + * @return {boolean} + */ +function getDockerUid(bindPath) { + const options = ['run', '--rm', '-v', `${bindPath}:/test`, + 'alpine', 'stat', '-c', '%u', '/test/.serverless']; + const ps = dockerCommand(options); + return ps.stdout.trim(); +}; + +module.exports = {buildImage, getBindPath, getDockerUid}; diff --git a/lib/pip.js b/lib/pip.js index 5f8d5266..062713b4 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -3,9 +3,8 @@ const path = require('path'); const get = require('lodash.get'); const set = require('lodash.set'); const {spawnSync} = require('child_process'); -const {quote} = require('shell-quote'); const values = require('lodash.values'); -const {buildImage, getBindPath} = require('./docker'); +const {buildImage, getBindPath, getDockerUid} = require('./docker'); /** * Install requirements described in requirementsPath to targetPath @@ -82,19 +81,17 @@ function installRequirements(requirementsPath, targetFolder, serverless, service cmdOptions.push('-e', 'SSH_AUTH_SOCK=/tmp/ssh_sock'); } if (process.platform === 'linux') { - // Set the ownership of the .serverless/requirements folder to current user - pipCmd = quote(pipCmd); - const chownCmd = quote([ - 'chown', '-R', `${process.getuid()}:${process.getgid()}`, - targetRequirementsFolder, - ]); - pipCmd = ['/bin/bash', '-c', '"' + pipCmd + ' && ' + chownCmd + '"']; + // Use same user so requirements folder is not root and so --cache-dir works + cmdOptions.push('-u', `${process.getuid()}`); // const stripCmd = quote([ // 'find', targetRequirementsFolder, // '-name', '"*.so"', // '-exec', 'strip', '{}', '\;', // ]); // pipCmd = ['/bin/bash', '-c', '"' + pipCmd + ' && ' + stripCmd + ' && ' + chownCmd + '"']; + } else { + // Use same user so --cache-dir works + cmdOptions.push('-u', getDockerUid(bindPath)); } cmdOptions.push(dockerImage); cmdOptions.push(...pipCmd); diff --git a/package.json b/package.json index 430ac6eb..0a580265 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", "lodash.values": "^4.3.0", - "shell-quote": "^1.6.1", "zip-local": "^0.3.4" } } diff --git a/test.bats b/test.bats index b297e188..0cdda0ee 100755 --- a/test.bats +++ b/test.bats @@ -3,6 +3,10 @@ setup() { export SLS_DEBUG=t + if ! [ -z "$CI" ]; then + export LC_ALL=C.UTF-8 + export LANG=C.UTF-8 + fi cd test @@ -11,7 +15,7 @@ setup() { teardown() { sls requirements clean - rm -rf puck puck2 puck3 node_modules .serverless + rm -rf puck puck2 puck3 node_modules .serverless .requirements.zip .requirements-cache if [ -f serverless.yml.bak ]; then mv serverless.yml.bak serverless.yml; fi } @@ -58,6 +62,14 @@ teardown() { ls puck/flask } +@test "py3.6 uses cache with dockerizePip option" { + [ -z "$CIRCLE_BRANCH" ] || skip "Volumes are weird in CircleCI https://circleci.com/docs/2.0/building-docker-images/#mounting-folders" + ! uname -sm|grep Linux || groups|grep docker || id -u|egrep '^0$' || skip "can't dockerize on linux if not root & not in docker group" + perl -p -i'.bak' -e 's/(pythonRequirements:$)/\1\n pipCmdExtraArgs: ["--cache-dir", ".requirements-cache"]/' serverless.yml + sls --dockerizePip=true package + ls .requirements-cache/http +} + @test "py2.7 can package flask with default options" { sls --runtime=python2.7 package unzip .serverless/sls-py-req-test.zip -d puck