Skip to content

Commit 95333b9

Browse files
authored
Merge pull request #147 from kichik/inject
Inject files into package instead of creating symlinks
2 parents 9790c62 + bb7d493 commit 95333b9

File tree

5 files changed

+133
-192
lines changed

5 files changed

+133
-192
lines changed

index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
const BbPromise = require('bluebird');
55
const fse = require('fs-extra');
66
const {addVendorHelper, removeVendorHelper, packRequirements} = require('./lib/zip');
7+
const {injectAllRequirements} = require('./lib/inject');
78
const {installAllRequirements} = require('./lib/pip');
89
const {pipfileToRequirements} = require('./lib/pipenv');
9-
const {linkAllRequirements, unlinkAllRequirements} = require('./lib/link');
1010
const {cleanup} = require('./lib/clean');
1111

1212
BbPromise.promisifyAll(fse);
@@ -101,12 +101,11 @@ class ServerlessPythonRequirements {
101101
.then(pipfileToRequirements)
102102
.then(addVendorHelper)
103103
.then(installAllRequirements)
104-
.then(packRequirements)
105-
.then(linkAllRequirements);
104+
.then(packRequirements);
106105

107106
const after = () => BbPromise.bind(this)
108107
.then(removeVendorHelper)
109-
.then(unlinkAllRequirements);
108+
.then(injectAllRequirements);
110109

111110
const invalidateCaches = () => {
112111
if (this.options.invalidateCaches) {

lib/inject.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
const fse = require('fs-extra');
2+
const glob = require('glob-all');
3+
const get = require('lodash.get');
4+
const set = require('lodash.set');
5+
const path = require('path');
6+
const values = require('lodash.values');
7+
const zipper = require('zip-local');
8+
const JSZip = require('jszip');
9+
10+
/**
11+
* write zip contents to a file
12+
* @param {Object} zip
13+
* @param {string} path
14+
*/
15+
function writeZip(zip, path) {
16+
const buff = zip.generate({
17+
type: 'nodebuffer',
18+
compression: 'DEFLATE',
19+
});
20+
21+
fse.writeFileSync(path, buff);
22+
}
23+
24+
/**
25+
* add a new file to a zip file from a buffer
26+
* @param {Object} zip
27+
* @param {string} path path to put in zip
28+
* @param {string} buffer file contents
29+
*/
30+
function zipFile(zip, path, buffer) {
31+
zip.file(path, buffer, {
32+
date: new Date(0), // necessary to get the same hash when zipping the same content
33+
});
34+
}
35+
36+
/**
37+
* inject requirements into packaged application
38+
* @param {string} requirementsPath requirements folder path
39+
* @param {string} packagePath target package path
40+
* @param {Object} options our options object
41+
*/
42+
function injectRequirements(requirementsPath, packagePath, options) {
43+
const noDeploy = new Set(options.noDeploy || []);
44+
45+
const zip = zipper.sync.unzip(packagePath).lowLevel();
46+
47+
glob.sync([path.join(requirementsPath, '**')], {mark: true, dot: true}).forEach((file) => {
48+
if (file.endsWith('/')) {
49+
return;
50+
}
51+
52+
const relativeFile = path.relative(requirementsPath, file);
53+
54+
if (relativeFile.match(/^__pycache__[\\\/]/)) {
55+
return;
56+
}
57+
if (noDeploy.has(relativeFile.split(/([-\\\/]|\.py$|\.pyc$)/, 1)[0])) {
58+
return;
59+
}
60+
61+
zipFile(zip, relativeFile, fse.readFileSync(file));
62+
});
63+
64+
writeZip(zip, packagePath);
65+
}
66+
67+
/**
68+
* remove all modules but the selected module from a package
69+
* @param {string} source original package
70+
* @param {string} target result package
71+
* @param {string} module module to keep
72+
*/
73+
function moveModuleUp(source, target, module) {
74+
const sourceZip = zipper.sync.unzip(source).memory();
75+
const targetZip = JSZip.make();
76+
77+
sourceZip.contents().forEach((file) => {
78+
if (!file.startsWith(module + '/')) {
79+
return;
80+
}
81+
zipFile(targetZip, file.replace(module + '/', ''), sourceZip.read(file, 'buffer'));
82+
});
83+
84+
writeZip(targetZip, target);
85+
}
86+
87+
/**
88+
* inject requirements into packaged application
89+
*/
90+
function injectAllRequirements() {
91+
this.serverless.cli.log('Injecting required Python packages to package...');
92+
93+
if (this.options.zip) {
94+
return;
95+
}
96+
97+
if (this.serverless.service.package.individually) {
98+
values(this.serverless.service.functions)
99+
.forEach((f) => {
100+
if (!(f.runtime || this.serverless.service.provider.runtime).match(/^python.*/)) {
101+
return;
102+
}
103+
if (!get(f, 'module')) {
104+
set(f, ['module'], '.');
105+
}
106+
if (f.module !== '.') {
107+
const artifactPath = path.join('.serverless', `${f.module}.zip`);
108+
moveModuleUp(f.package.artifact, artifactPath, f.module);
109+
f.package.artifact = artifactPath;
110+
}
111+
injectRequirements(
112+
path.join('.serverless', f.module, 'requirements'),
113+
f.package.artifact,
114+
this.options
115+
);
116+
});
117+
} else {
118+
injectRequirements(
119+
path.join('.serverless', 'requirements'),
120+
this.serverless.service.package.artifact,
121+
this.options
122+
);
123+
}
124+
}
125+
126+
module.exports = {injectAllRequirements};

lib/link.js

Lines changed: 0 additions & 187 deletions
This file was deleted.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,12 @@
4848
"dependencies": {
4949
"bluebird": "^3.0.6",
5050
"fs-extra": "^3.0.1",
51+
"glob-all": "^3.1.0",
5152
"is-wsl": "^1.1.0",
53+
"jszip": "^2.5.0",
5254
"lodash.get": "^4.4.2",
5355
"lodash.set": "^4.3.2",
5456
"lodash.values": "^4.3.0",
55-
"rimraf": "^2.6.2",
5657
"shell-quote": "^1.6.1",
5758
"zip-local": "^0.3.4"
5859
}

test.bats

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ teardown() {
2727
sls --zip=true package
2828
unzip .serverless/sls-py-req-test.zip -d puck
2929
ls puck/.requirements.zip puck/unzip_requirements.py
30+
! ls puck/flask
3031
}
3132

3233
@test "py3.6 doesn't package boto3 by default" {
@@ -40,6 +41,7 @@ teardown() {
4041
sls package
4142
unzip .serverless/sls-py-req-test.zip -d puck
4243
! ls puck/bottle.py
44+
! ls puck/__pycache__/bottle.cpython-36.pyc
4345
}
4446

4547
@test "py3.6 can package flask with zip & dockerizePip option" {

0 commit comments

Comments
 (0)