presidium 4.0.10 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Docker.js +64 -8
- package/package.json +8 -9
- package/internal/Archive.js +0 -115
- package/internal/Archive.test.js +0 -78
package/Docker.js
CHANGED
|
@@ -11,11 +11,13 @@ const identity = require('rubico/x/identity')
|
|
|
11
11
|
const size = require('rubico/x/size')
|
|
12
12
|
const defaultsDeep = require('rubico/x/defaultsDeep')
|
|
13
13
|
const Transducer = require('rubico/Transducer')
|
|
14
|
-
const zlib = require('zlib')
|
|
15
14
|
const http = require('http')
|
|
15
|
+
const fs = require('fs')
|
|
16
|
+
const zlib = require('zlib')
|
|
17
|
+
const uniqid = require('uniqid')
|
|
18
|
+
const { spawn } = require('child_process')
|
|
16
19
|
const Readable = require('./Readable')
|
|
17
20
|
const HTTP = require('./HTTP')
|
|
18
|
-
const Archive = require('./internal/Archive')
|
|
19
21
|
const querystring = require('querystring')
|
|
20
22
|
const split = require('./internal/split')
|
|
21
23
|
const join = require('./internal/join')
|
|
@@ -43,6 +45,12 @@ const handleDockerHTTPResponse = require('./internal/handleDockerHTTPResponse')
|
|
|
43
45
|
* Arguments:
|
|
44
46
|
* * `options`
|
|
45
47
|
* * `apiVersion` - the version of the Docker API. Defaults to `'1.48'`.
|
|
48
|
+
*
|
|
49
|
+
* Supported platforms:
|
|
50
|
+
* * `linux64`
|
|
51
|
+
*
|
|
52
|
+
* System dependencies:
|
|
53
|
+
* * `tar`
|
|
46
54
|
*/
|
|
47
55
|
class Docker {
|
|
48
56
|
constructor(options = {}) {
|
|
@@ -341,7 +349,7 @@ class Docker {
|
|
|
341
349
|
* * `path` - parent directory of the build context.
|
|
342
350
|
* * `options`
|
|
343
351
|
* * `image` - the name and optional tag of the image. If no tag is present, `'LATEST'` is assumed as the value for the tag.
|
|
344
|
-
* * `ignore` -
|
|
352
|
+
* * `ignore` - patterns to ignore when bundling files and directories for the build context.
|
|
345
353
|
* * `archive` - an object of filenames and file contents that will be present in the build context.
|
|
346
354
|
* * `archiveDockerfile` - the filepath including filename of the Dockerfile, e.g. `'Dockerfiles/Dockerfile2'`. Defaults to `'Dockerfile'`.
|
|
347
355
|
* * `platform` - target platform for the build, e.g. `'linux/arm64'`.
|
|
@@ -401,12 +409,58 @@ class Docker {
|
|
|
401
409
|
* * [Dockerfile](https://docs.docker.com/engine/reference/builder/)
|
|
402
410
|
*/
|
|
403
411
|
async buildImage(path, options = {}) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
412
|
+
path = path.endsWith('/') ? path : `${path}/`
|
|
413
|
+
|
|
414
|
+
const archiveDir = `/tmp/presidium/Docker-buildImage/${uniqid()}`
|
|
415
|
+
|
|
416
|
+
await fs.promises.mkdir(archiveDir, { recursive: true })
|
|
417
|
+
|
|
418
|
+
const archiveFilename = `${archiveDir}/archive.tar`
|
|
419
|
+
|
|
420
|
+
const cmdArgs = [
|
|
421
|
+
'-cf',
|
|
422
|
+
archiveFilename,
|
|
423
|
+
'-C',
|
|
424
|
+
path,
|
|
425
|
+
]
|
|
426
|
+
|
|
427
|
+
if (options.ignore) {
|
|
428
|
+
for (const pattern of options.ignore) {
|
|
429
|
+
cmdArgs.push(`--exclude=${pattern}`)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
cmdArgs.push('.')
|
|
434
|
+
|
|
435
|
+
const cmd = spawn('tar', cmdArgs, { stdio: 'inherit' })
|
|
436
|
+
|
|
437
|
+
await new Promise(resolve => {
|
|
438
|
+
cmd.on('close', resolve)
|
|
407
439
|
})
|
|
408
440
|
|
|
409
|
-
|
|
441
|
+
if (options.archive) {
|
|
442
|
+
for (const filename in options.archive) {
|
|
443
|
+
await fs.promises.writeFile(
|
|
444
|
+
`${archiveDir}/${filename}`, options.archive[filename]
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
const cmdArgs = [
|
|
448
|
+
'-rf',
|
|
449
|
+
archiveFilename,
|
|
450
|
+
`--directory=${archiveDir}`,
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
cmdArgs.push(filename)
|
|
454
|
+
|
|
455
|
+
const cmd = spawn('tar', cmdArgs, { stdio: 'inherit' })
|
|
456
|
+
|
|
457
|
+
await new Promise(resolve => {
|
|
458
|
+
cmd.on('close', resolve)
|
|
459
|
+
})
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const archive = fs.createReadStream(archiveFilename).pipe(zlib.createGzip())
|
|
410
464
|
|
|
411
465
|
const response = await this.http.post(`/build?${querystring.stringify({
|
|
412
466
|
dockerfile: options.archiveDockerfile ?? 'Dockerfile',
|
|
@@ -414,12 +468,14 @@ class Docker {
|
|
|
414
468
|
forcerm: true,
|
|
415
469
|
platform: options.platform ?? '',
|
|
416
470
|
})}`, {
|
|
417
|
-
body:
|
|
471
|
+
body: archive,
|
|
418
472
|
headers: {
|
|
419
473
|
'Content-Type': 'application/x-tar',
|
|
420
474
|
},
|
|
421
475
|
})
|
|
422
476
|
|
|
477
|
+
await fs.promises.rm(archiveDir, { recursive: true })
|
|
478
|
+
|
|
423
479
|
const dataStream = await handleDockerHTTPResponse(response, { stream: true })
|
|
424
480
|
return dataStream
|
|
425
481
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "presidium",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "The Presidium library.",
|
|
5
5
|
"author": "Richard Tong",
|
|
6
6
|
"license": "CFOSS",
|
|
@@ -67,19 +67,18 @@
|
|
|
67
67
|
"presidium-db": ">=3.10.0",
|
|
68
68
|
"presidium-websocket": ">=3.2.1",
|
|
69
69
|
"rubico": "^2.7.7",
|
|
70
|
-
"
|
|
70
|
+
"uniqid": "^5.4.0"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"test": "node test.js && mocha **/*.test.js *.test.js",
|
|
74
74
|
"lint": "eslint ."
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@aws-sdk/crc64-nvme-crt": "
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"wavefile": "^11.0.0"
|
|
77
|
+
"@aws-sdk/crc64-nvme-crt": "latest",
|
|
78
|
+
"fast-crc32c": "latest",
|
|
79
|
+
"mocha": "12.0.0-beta-3",
|
|
80
|
+
"nyc": "latest",
|
|
81
|
+
"thunk-test": "latest",
|
|
82
|
+
"wavefile": "latest"
|
|
84
83
|
}
|
|
85
84
|
}
|
package/internal/Archive.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
require('rubico/global')
|
|
2
|
-
const tarStream = require('tar-stream')
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const walk = require('./walk')
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* @name Archive
|
|
8
|
-
*/
|
|
9
|
-
const Archive = {}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @name Archive.tar
|
|
13
|
-
*
|
|
14
|
-
* @docs
|
|
15
|
-
* ```coffeescript [specscript]
|
|
16
|
-
* module tar 'https://github.com/mafintosh/tar-stream'
|
|
17
|
-
*
|
|
18
|
-
* Archive.tar(path string, options {
|
|
19
|
-
* ignore: Array<string>,
|
|
20
|
-
* base: Object<string>,
|
|
21
|
-
* }) -> pack tar.Pack
|
|
22
|
-
* ```
|
|
23
|
-
*
|
|
24
|
-
* Bundle multiple files and directories from a parent directory into a tarball.
|
|
25
|
-
*
|
|
26
|
-
* Arguments:
|
|
27
|
-
* * `path` - the path to the parent directory which contains the files and directories to bundle.
|
|
28
|
-
* * `options`
|
|
29
|
-
* * `ignore` - filepaths, filenames, or patterns to ignore.
|
|
30
|
-
*/
|
|
31
|
-
Archive.tar = function tar(path, options) {
|
|
32
|
-
path = path.endsWith('/') ? path : `${path}/`
|
|
33
|
-
const ignore = get(options, 'ignore')
|
|
34
|
-
const pack = tarStream.pack()
|
|
35
|
-
|
|
36
|
-
pipe(path, [
|
|
37
|
-
walk,
|
|
38
|
-
filter(path => {
|
|
39
|
-
for (const part of ignore) {
|
|
40
|
-
if (path.includes(part)) {
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return true
|
|
45
|
-
}),
|
|
46
|
-
|
|
47
|
-
reduce(async (pack, filepath) => {
|
|
48
|
-
const stats = await pipe(filepath, [
|
|
49
|
-
fs.promises.stat,
|
|
50
|
-
pick(['size', 'mode', 'mtime', 'uid', 'gid']),
|
|
51
|
-
])
|
|
52
|
-
|
|
53
|
-
const entry = pack.entry({ ...stats, name: filepath.replace(path, '') })
|
|
54
|
-
|
|
55
|
-
const fileStream = fs.createReadStream(filepath)
|
|
56
|
-
|
|
57
|
-
fileStream.pipe(entry)
|
|
58
|
-
|
|
59
|
-
await new Promise(resolve => {
|
|
60
|
-
fileStream.on('end', resolve)
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
return pack
|
|
64
|
-
}, pack),
|
|
65
|
-
|
|
66
|
-
tap(pack => {
|
|
67
|
-
const base = options.base ?? {}
|
|
68
|
-
for (const filename in base) {
|
|
69
|
-
pack.entry({ name: filename }, base[filename])
|
|
70
|
-
}
|
|
71
|
-
pack.finalize()
|
|
72
|
-
}),
|
|
73
|
-
])
|
|
74
|
-
|
|
75
|
-
return pack
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @name untar
|
|
80
|
-
*
|
|
81
|
-
* @docs
|
|
82
|
-
* ```coffeescript [specscript]
|
|
83
|
-
* module tar 'https://github.com/mafintosh/tar-stream'
|
|
84
|
-
* module presidium 'https://presidium.services/docs'
|
|
85
|
-
*
|
|
86
|
-
* Archive.untar(pack tar.Pack) ->
|
|
87
|
-
* contentMap Promise<Map<(filepath string)=>(content presidium.Readable)> >
|
|
88
|
-
* ```
|
|
89
|
-
*
|
|
90
|
-
* Extract a tarball into a map of file paths to content streams.
|
|
91
|
-
*
|
|
92
|
-
* Arguments:
|
|
93
|
-
* * `pack` - a tarball represented by the `tar.Pack` type. Returned by the [tar](#tar) method.
|
|
94
|
-
*/
|
|
95
|
-
Archive.untar = function untar(pack) {
|
|
96
|
-
const extractStream = tarStream.extract()
|
|
97
|
-
const extracted = new Map()
|
|
98
|
-
return new Promise((resolve, reject) => {
|
|
99
|
-
extractStream.on('entry', (header, stream, next) => {
|
|
100
|
-
stream.header = header
|
|
101
|
-
extracted.set(header.name, stream)
|
|
102
|
-
next()
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
extractStream.on('finish', () => {
|
|
106
|
-
resolve(extracted)
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
extractStream.on('error', reject)
|
|
110
|
-
|
|
111
|
-
pack.pipe(extractStream)
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
module.exports = Archive
|
package/internal/Archive.test.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const assert = require('assert')
|
|
2
|
-
const stream = require('stream')
|
|
3
|
-
const fs = require('fs/promises')
|
|
4
|
-
const Test = require('thunk-test')
|
|
5
|
-
const Archive = require('./Archive')
|
|
6
|
-
const resolvePath = require('./resolvePath')
|
|
7
|
-
const map = require('rubico/map')
|
|
8
|
-
const reduce = require('rubico/reduce')
|
|
9
|
-
const Readable = require('../Readable')
|
|
10
|
-
|
|
11
|
-
const test = new Test('Archive', async function integration() {
|
|
12
|
-
|
|
13
|
-
{
|
|
14
|
-
const pack = await Archive.tar(__dirname, {
|
|
15
|
-
ignore: ['Dockerfile', 'node_modules', '.git', '.nyc_output'],
|
|
16
|
-
})
|
|
17
|
-
const extracted = await Archive.untar(pack)
|
|
18
|
-
assert(extracted.size > 0)
|
|
19
|
-
for (const [path, entry] of extracted) {
|
|
20
|
-
assert('header' in entry)
|
|
21
|
-
assert(!path.startsWith('/'))
|
|
22
|
-
assert.equal(typeof path, 'string')
|
|
23
|
-
assert.equal(typeof entry[Symbol.asyncIterator], 'function')
|
|
24
|
-
assert(entry.readable)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
const base = {
|
|
30
|
-
Dockerfile: 'FROM node:15-alpine'
|
|
31
|
-
}
|
|
32
|
-
const ignore = ['fixtures', 'tmp', 'google-chrome-for-testing']
|
|
33
|
-
const pack = await Archive.tar(__dirname, { ignore, base })
|
|
34
|
-
const extracted = await Archive.untar(pack)
|
|
35
|
-
const dir = await fs.readdir(__dirname)
|
|
36
|
-
.then(filter(filepath => {
|
|
37
|
-
for (const part of ignore) {
|
|
38
|
-
if (filepath.includes(part)) {
|
|
39
|
-
return false
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return true
|
|
43
|
-
}))
|
|
44
|
-
const extractedKeys = [...extracted.keys()]
|
|
45
|
-
assert.equal(extracted.size, dir.length + 1) // extra Dockerfile
|
|
46
|
-
assert(extracted.has('Dockerfile'))
|
|
47
|
-
assert.equal(
|
|
48
|
-
await reduce(extracted.get('Dockerfile'), (a, b) => a + b, ''),
|
|
49
|
-
'FROM node:15-alpine'
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
{
|
|
54
|
-
const base = {
|
|
55
|
-
Dockerfile: 'FROM busybox:1.32',
|
|
56
|
-
'.aws/credentials': '[presidium]\naccessKeyId\nsecretAccessKey',
|
|
57
|
-
}
|
|
58
|
-
const pack = await Archive.tar(`${__dirname}/`, {
|
|
59
|
-
ignore: ['hashJSON.js'],
|
|
60
|
-
base,
|
|
61
|
-
})
|
|
62
|
-
const extracted = await Archive.untar(pack)
|
|
63
|
-
assert(extracted.size > 0)
|
|
64
|
-
assert(extracted.has('Dockerfile'))
|
|
65
|
-
assert(extracted.has('.aws/credentials'))
|
|
66
|
-
assert.equal(
|
|
67
|
-
await reduce(extracted.get('Dockerfile'), (a, b) => a + b, ''),
|
|
68
|
-
'FROM busybox:1.32'
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
}).case()
|
|
73
|
-
|
|
74
|
-
if (process.argv[1] == __filename) {
|
|
75
|
-
test()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
module.exports = test
|