diff --git a/lib/pip.js b/lib/pip.js index f1d3b1fb..caf48f3e 100644 --- a/lib/pip.js +++ b/lib/pip.js @@ -338,6 +338,28 @@ function dockerPathForWin(path) { return path; } } + +/** + * get requirements from requirements.txt + * @param {string} source + * @return {string[]} + */ +function getRequirements(source) { + const requirements = fse + .readFileSync(source, { encoding: 'utf-8' }) + .replace(/\\\n/g, ' ') + .split(/\r?\n/); + + return requirements.reduce((acc, req) => { + req = req.trim(); + if (!req.startsWith('-r')) { + return [...acc, req]; + } + source = path.join(path.dirname(source), req.replace(/^-r\s+/, '')); + return [...acc, ...getRequirements(source)]; + }, []); +} + /** create a filtered requirements.txt without anything from noDeploy * then remove all comments and empty lines, and sort the list which * assist with matching the static cache. The sorting will skip any @@ -351,10 +373,7 @@ function dockerPathForWin(path) { */ function filterRequirementsFile(source, target, options) { const noDeploy = new Set(options.noDeploy || []); - const requirements = fse - .readFileSync(source, { encoding: 'utf-8' }) - .replace(/\\\n/g, ' ') - .split(/\r?\n/); + const requirements = getRequirements(source); var prepend = []; const filteredRequirements = requirements.filter(req => { req = req.trim(); diff --git a/test.js b/test.js index d28daa6e..d9a6279d 100644 --- a/test.js +++ b/test.js @@ -158,6 +158,21 @@ test('py3.6 can package flask with hashes', t => { t.end(); }); +test('py3.6 can package flask with nested', t => { + process.chdir('tests/base'); + const path = npm(['pack', '../..']); + npm(['i', path]); + sls([ + `--pythonBin=${getPythonBin(3)}`, + '--fileName=requirements-w-nested.txt', + 'package' + ]); + const zipfiles = listZipFiles('.serverless/sls-py-req-test.zip'); + t.true(zipfiles.includes(`flask${sep}__init__.py`), 'flask is packaged'); + t.true(zipfiles.includes(`boto3${sep}__init__.py`), 'boto3 is packaged'); + t.end(); +}); + test('py3.6 can package flask with zip option', t => { process.chdir('tests/base'); const path = npm(['pack', '../..']); @@ -1459,6 +1474,10 @@ test('py3.6 can package only requirements of module', t => { zipfiles_hello.includes(`pyaml${sep}__init__.py`), 'pyaml is packaged in function hello1' ); + t.true( + zipfiles_hello.includes(`boto3${sep}__init__.py`), + 'boto3 is packaged in function hello1' + ); t.false( zipfiles_hello.includes(`flask${sep}__init__.py`), 'flask is NOT packaged in function hello1' @@ -1479,6 +1498,10 @@ test('py3.6 can package only requirements of module', t => { zipfiles_hello2.includes(`pyaml${sep}__init__.py`), 'pyaml is NOT packaged in function hello2' ); + t.false( + zipfiles_hello2.includes(`boto3${sep}__init__.py`), + 'boto3 is NOT packaged in function hello2' + ); t.true( zipfiles_hello2.includes(`flask${sep}__init__.py`), 'flask is packaged in function hello2' diff --git a/tests/base/requirements-common.txt b/tests/base/requirements-common.txt new file mode 100644 index 00000000..30ddf823 --- /dev/null +++ b/tests/base/requirements-common.txt @@ -0,0 +1 @@ +boto3 diff --git a/tests/base/requirements-w-nested.txt b/tests/base/requirements-w-nested.txt new file mode 100644 index 00000000..4d73c837 --- /dev/null +++ b/tests/base/requirements-w-nested.txt @@ -0,0 +1,3 @@ +flask +bottle +-r requirements-common.txt diff --git a/tests/individually/module1/requirements.txt b/tests/individually/module1/requirements.txt index 2e64be1f..9b7a216a 100644 --- a/tests/individually/module1/requirements.txt +++ b/tests/individually/module1/requirements.txt @@ -1 +1,2 @@ +-r ../requirements-common.txt pyaml diff --git a/tests/individually/requirements-common.txt b/tests/individually/requirements-common.txt new file mode 100644 index 00000000..30ddf823 --- /dev/null +++ b/tests/individually/requirements-common.txt @@ -0,0 +1 @@ +boto3