presidium 4.0.10 → 4.1.1

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 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` - filepaths or filenames to ignore when bundling files and directories for the build context.
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
- const pack = Archive.tar(path, {
405
- base: options.archive,
406
- ignore: options.ignore ?? ['node_modules', '.git', '.nyc_output'],
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
- const compressed = pack.pipe(zlib.createGzip())
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: compressed,
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.10",
3
+ "version": "4.1.1",
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
- "tar-stream": "^3.1.7"
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": "^3.848.0",
78
- "eslint": "^7.13.0",
79
- "fast-crc32c": "^2.0.0",
80
- "mocha": "^10.8.2",
81
- "nyc": "^17.1.0",
82
- "thunk-test": "^1.3.9",
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
  }
@@ -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
@@ -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