Skip to content
This repository was archived by the owner on Oct 10, 2022. It is now read-only.

Commit 34bf3ae

Browse files
authored
Merge pull request #34 from netlify/zip_it_and_ship_it
Zip it and ship it
2 parents f242511 + 4c24571 commit 34bf3ae

File tree

4 files changed

+24
-100
lines changed

4 files changed

+24
-100
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
],
3636
"dependencies": {
3737
"@netlify/open-api": "^0.5.0",
38-
"archiver": "^3.0.0",
3938
"backoff": "^2.5.0",
4039
"clean-deep": "^3.0.2",
4140
"debug": "^4.1.1",
@@ -59,7 +58,7 @@
5958
"through2-filter": "^3.0.0",
6059
"through2-map": "^3.0.0",
6160
"util.promisify": "^1.0.0",
62-
"util.promisify-all": "^1.0.2"
61+
"@netlify/zip-it-and-ship-it": "^0.2.0"
6362
},
6463
"devDependencies": {
6564
"@babel/core": "^7.1.2",

src/deploy/hash-fns.js

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
const promisifyAll = require('util.promisify-all')
21
const promisify = require('util.promisify')
32
const pump = promisify(require('pump'))
4-
const fs = promisifyAll(require('fs'))
53
const fromArray = require('from2-array')
4+
const zipIt = require('@netlify/zip-it-and-ship-it')
5+
const path = require('path')
66

7-
const { hasherCtor, manifestCollectorCtor, fnStatCtor, fnFilterCtor } = require('./hasher-segments')
7+
const { hasherCtor, manifestCollectorCtor } = require('./hasher-segments')
88

99
module.exports = hashFns
1010
async function hashFns(dir, opts) {
@@ -14,32 +14,38 @@ async function hashFns(dir, opts) {
1414
assetType: 'function',
1515
hashAlgorithm: 'sha256',
1616
// tmpDir,
17-
statusCb: () => { }
17+
statusCb: () => {}
1818
},
1919
opts
2020
)
2121
// early out if the functions dir is omitted
2222
if (!dir) return { functions: {}, shaMap: {} }
23-
if (!opts.filter) throw new Error('Missing required filter function')
2423
if (!opts.tmpDir) throw new Error('Missing tmpDir directory for zipping files')
2524

26-
const fileList = await fs.readdir(dir).then(files => files.filter(opts.filter))
27-
const fileStream = fromArray.obj(fileList)
25+
const functionZips = await zipIt.zipFunctions(dir, opts.tmpDir)
26+
27+
const fileObjs = functionZips.map(({ path: functionPath, runtime }) => ({
28+
filepath: functionPath,
29+
root: opts.tmpDir,
30+
relname: path.relative(opts.tmpDir, functionPath),
31+
basename: path.basename(functionPath),
32+
extname: path.extname(functionPath),
33+
type: 'file',
34+
assetType: 'function',
35+
normalizedPath: path.basename(functionPath, path.extname(functionPath)),
36+
runtime
37+
}))
38+
39+
const functionStream = fromArray.obj(fileObjs)
2840

29-
const fnStat = fnStatCtor({ root: dir, concurrentStat: opts.concurrentHash, tmpDir: opts.tmpDir })
30-
const fnFilter = fnFilterCtor()
3141
const hasher = hasherCtor(opts)
3242

3343
// Written to by manifestCollector
3444
const functions = {} // normalizedPath: hash (wanted by deploy API)
3545
const fnShaMap = {} //hash: [fileObj, fileObj, fileObj]
3646
const manifestCollector = manifestCollectorCtor(functions, fnShaMap, opts)
3747

38-
// TODO: Zip up functions, hash then send.
39-
// This is totally wrong right now.
40-
// See https://github.com/netlify/open-api/blob/master/go/porcelain/deploy.go#L544
41-
42-
await pump(fileStream, fnStat, fnFilter, hasher, manifestCollector)
48+
await pump(functionStream, hasher, manifestCollector)
4349

4450
return { functions, fnShaMap }
4551
}

src/deploy/hasher-segments.js

Lines changed: 3 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
const objFilterCtor = require('through2-filter').objCtor
22
const objWriter = require('flush-write-stream').obj
3-
const { isExe, normalizePath } = require('./util')
3+
const { normalizePath } = require('./util')
44
const transform = require('parallel-transform')
55
const hasha = require('hasha')
6-
const path = require('path')
7-
const fs = require('fs')
86
const map = require('through2-map').obj
9-
const pump = require('pump')
10-
const archiver = require('archiver')
117

128
// a parallel transform stream segment ctor that hashes fileObj's created by folder-walker
139
exports.hasherCtor = ({ concurrentHash, hashAlgorithm = 'sha1' }) => {
10+
const hashaOpts = { algorithm: hashAlgorithm }
1411
if (!concurrentHash) throw new Error('Missing required opts')
1512
return transform(concurrentHash, { objectMode: true }, (fileObj, cb) => {
1613
hasha
17-
.fromFile(fileObj.filepath, { algorithm: hashAlgorithm })
14+
.fromFile(fileObj.filepath, hashaOpts)
1815
// insert hash and asset type to file obj
1916
.then(hash => cb(null, Object.assign({}, fileObj, { hash })))
2017
.catch(err => cb(err))
@@ -56,67 +53,3 @@ exports.manifestCollectorCtor = (filesObj, shaMap, { statusCb, assetType }) => {
5653
exports.fileFilterCtor = objFilterCtor(fileObj => {
5754
return fileObj.type === 'file'
5855
})
59-
60-
exports.fnFilterCtor = objFilterCtor(fileObj => {
61-
// filter additional files out of our fn pipeline
62-
return fileObj.type === 'file' && !!fileObj.runtime
63-
})
64-
65-
// Zip a file into a temporary directory
66-
function zipFunction(item, tmpDir, cb) {
67-
const zipPath = path.join(tmpDir, item.normalizedPath + '.zip')
68-
const output = fs.createWriteStream(zipPath)
69-
const archive = archiver('zip')
70-
71-
archive.file(item.filepath, { name: item.basename })
72-
archive.finalize()
73-
74-
pump(archive, output, err => {
75-
if (err) return cb(err)
76-
77-
item.filepath = zipPath
78-
cb(null, item)
79-
})
80-
}
81-
82-
// parallel stream ctor similar to folder-walker but specialized for netlify functions
83-
// Stream in names of files that may be functions, and this will stat the file and return a fileObj
84-
exports.fnStatCtor = ({ root, concurrentStat, tmpDir }) => {
85-
if (!concurrentStat || !root || !tmpDir) throw new Error('Missing required opts')
86-
return transform(concurrentStat, { objectMode: true }, (name, cb) => {
87-
const filepath = path.join(root, name)
88-
fs.stat(filepath, (err, stat) => {
89-
if (err) return cb(err)
90-
91-
const item = {
92-
root,
93-
filepath,
94-
stat,
95-
relname: path.relative(root, filepath),
96-
basename: path.basename(name),
97-
extname: path.extname(name),
98-
type: stat.isFile() ? 'file' : stat.isDirectory() ? 'directory' : null,
99-
assetType: 'function',
100-
normalizedPath: path.basename(name, path.extname(name))
101-
}
102-
103-
if (['.zip'].some(ext => item.extname === ext)) {
104-
item.runtime = 'js'
105-
return cb(null, item)
106-
}
107-
108-
if (['.js'].some(ext => item.extname === ext)) {
109-
item.runtime = 'js'
110-
111-
return zipFunction(item, tmpDir, cb)
112-
}
113-
114-
if (isExe(item.stat)) {
115-
item.runtime = 'go'
116-
return zipFunction(item, tmpDir, cb)
117-
}
118-
119-
return cb(null, item)
120-
})
121-
})
122-
}

src/deploy/util.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,4 @@ function getUploadList(required, shaMap) {
115115
return flatten(required.map(sha => shaMap[sha]))
116116
}
117117

118-
// given a Stat object, return if its executable or not
119-
// https://github.com/jokeyrhyme/is-executable.js without the io
120-
exports.isExe = stat => {
121-
const { mode, gid, uid } = stat
122-
if (process.platform === 'win32') {
123-
return true
124-
}
125-
126-
const isGroup = gid ? process.getgid && gid === process.getgid() : true
127-
const isUser = uid ? process.getuid && uid === process.getuid() : true
128-
129-
return Boolean(mode & 0o0001 || (mode & 0o0010 && isGroup) || (mode & 0o0100 && isUser))
130-
}
131-
132118
exports.retry = async (fn, errHandler, opts) => {}

0 commit comments

Comments
 (0)