diff --git a/circle.yml b/circle.yml index 87341cbe..3957bc4f 100644 --- a/circle.yml +++ b/circle.yml @@ -23,6 +23,8 @@ jobs: sudo ./install.sh /usr/local # other deps - run: sudo apt -y update && sudo apt -y install python-pip python2.7 curl unzip + # upgrade python3.6 pip to latest + - run: sudo python3.6 -m pip install -U pip # instal pipenv - run: sudo python3.6 -m pip install pipenv pip-tools # install poetry diff --git a/lib/inject.js b/lib/inject.js index 1abbb531..dc30be79 100644 --- a/lib/inject.js +++ b/lib/inject.js @@ -37,7 +37,12 @@ function injectRequirements(requirementsPath, packagePath, options) { !noDeploy.has(relativeFile.split(/([-\\/]|\.py$|\.pyc$)/, 1)[0]) ) .map(([file, relativeFile]) => - zipFile(zip, relativeFile, fse.readFileAsync(file)) + Promise.all([file, relativeFile, fse.statAsync(file)]) + ) + .map(([file, relativeFile, fileStat]) => + zipFile(zip, relativeFile, fse.readFileAsync(file), { + unixPermissions: fileStat.mode + }) ) .then(() => writeZip(zip, packagePath)) ); diff --git a/lib/zipTree.js b/lib/zipTree.js index ea7a9df0..d45aded0 100644 --- a/lib/zipTree.js +++ b/lib/zipTree.js @@ -60,14 +60,23 @@ function writeZip(zip, targetPath) { * @param {string} zipPath the target path in the zip. * @param {Promise} bufferPromise a promise providing a nodebuffer. * @return {Promise} a promise providing the JSZip object. + * @param {object} fileOpts an object with the opts to save for the file in the zip. */ -function zipFile(zip, zipPath, bufferPromise) { +function zipFile(zip, zipPath, bufferPromise, fileOpts) { return bufferPromise .then(buffer => - zip.file(zipPath, buffer, { - // necessary to get the same hash when zipping the same content - date: new Date(0) - }) + zip.file( + zipPath, + buffer, + Object.assign( + {}, + { + // necessary to get the same hash when zipping the same content + date: new Date(0) + }, + fileOpts + ) + ) ) .then(() => zip); } diff --git a/test.js b/test.js index 2ec71976..78f60aad 100644 --- a/test.js +++ b/test.js @@ -9,6 +9,7 @@ const { readFileSync, copySync, writeFileSync, + statSync, pathExistsSync } = require('fs-extra'); const { quote } = require('shell-quote'); @@ -875,6 +876,12 @@ test( 'foobar has retained its executable file permissions' ); + const flaskPerm = statSync('.serverless/requirements/bin/flask').mode; + t.true( + zipfiles_with_metadata['bin/flask'].unixPermissions === flaskPerm, + 'bin/flask has retained its executable file permissions' + ); + t.end(); }, { skip: process.platform === 'win32' } @@ -1566,15 +1573,23 @@ test( npm(['i', path]); sls(['package']); - const zipfiles_hello = listZipFilesWithMetaData('.serverless/hello1.zip'); + const zipfiles_hello1 = listZipFilesWithMetaData('.serverless/hello1.zip'); t.true( - zipfiles_hello['module1/foobar'].unixPermissions + zipfiles_hello1['module1/foobar'].unixPermissions .toString(8) .slice(3, 6) === perm, 'foobar has retained its executable file permissions' ); + const zipfiles_hello2 = listZipFilesWithMetaData('.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip'); + const flaskPerm = statSync('.serverless/module2/requirements/bin/flask').mode; + + t.true( + zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, + 'bin/flask has retained its executable file permissions' + ); + t.end(); }, { skip: process.platform === 'win32' } @@ -1601,6 +1616,14 @@ test( 'foobar has retained its executable file permissions' ); + const zipfiles_hello2 = listZipFilesWithMetaData('.serverless/module2-sls-py-req-test-indiv-dev-hello2.zip'); + const flaskPerm = statSync('.serverless/module2/requirements/bin/flask').mode; + + t.true( + zipfiles_hello2['bin/flask'].unixPermissions === flaskPerm, + 'bin/flask has retained its executable file permissions' + ); + t.end(); }, { skip: !canUseDocker() || process.platform === 'win32' }