@codedependant/semantic-release-docker 3.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/LICENSE +21 -0
  3. package/README.md +184 -0
  4. package/index.js +28 -0
  5. package/lib/build-config.js +63 -0
  6. package/lib/build-template-vars.js +28 -0
  7. package/lib/docker/image.js +127 -0
  8. package/lib/docker/index.js +5 -0
  9. package/lib/lang/array/index.js +5 -0
  10. package/lib/lang/array/to-array.js +9 -0
  11. package/lib/lang/object/get.js +18 -0
  12. package/lib/lang/object/has.js +8 -0
  13. package/lib/lang/object/index.js +6 -0
  14. package/lib/lang/string/index.js +5 -0
  15. package/lib/lang/string/template.js +14 -0
  16. package/lib/parse-pkg-name.js +12 -0
  17. package/lib/prepare.js +34 -0
  18. package/lib/publish.js +32 -0
  19. package/lib/read-pkg.js +12 -0
  20. package/lib/verify.js +95 -0
  21. package/package.json +98 -0
  22. package/test/common/git/add.js +9 -0
  23. package/test/common/git/commit.js +11 -0
  24. package/test/common/git/head.js +10 -0
  25. package/test/common/git/index.js +13 -0
  26. package/test/common/git/init-origin.js +13 -0
  27. package/test/common/git/init-remote.js +16 -0
  28. package/test/common/git/init.js +20 -0
  29. package/test/common/git/push.js +9 -0
  30. package/test/common/git/tag.js +13 -0
  31. package/test/common/git/tags.js +14 -0
  32. package/test/fixture/docker/Dockerfile.prepare +4 -0
  33. package/test/fixture/docker/Dockerfile.publish +4 -0
  34. package/test/fixture/docker/Dockerfile.test +4 -0
  35. package/test/fixture/package.json +4 -0
  36. package/test/fixture/pkg/one/package.json +5 -0
  37. package/test/fixture/pkg/two/package.json +0 -0
  38. package/test/integration/prepare.js +74 -0
  39. package/test/integration/publish.js +60 -0
  40. package/test/integration/release.js +92 -0
  41. package/test/integration/verify.js +205 -0
  42. package/test/unit/build-config.js +167 -0
  43. package/test/unit/build-template-vars.js +63 -0
  44. package/test/unit/docker/image.js +289 -0
  45. package/test/unit/lang/array.js +34 -0
  46. package/test/unit/lang/object.js +45 -0
  47. package/test/unit/lang/string.js +37 -0
  48. package/test/unit/parse-pkg-name.js +27 -0
  49. package/test/unit/read-pkg.js +36 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,90 @@
1
+ # Semantic Release Docker
2
+
3
+ ## [3.1.1](https://github.com/esatterwhite/semantic-release-docker/compare/v3.1.0...v3.1.1) (2021-09-18)
4
+
5
+
6
+ ### Documentation
7
+
8
+ * corrected the parameter - dockerFile [81e9319](https://github.com/esatterwhite/semantic-release-docker/commit/81e9319e7dae9905cf098a5b2c3a9837d4f5d1d9) - Eric Satterwhite
9
+
10
+ # [3.1.0](https://github.com/esatterwhite/semantic-release-docker/compare/v3.0.1...v3.1.0) (2021-04-12)
11
+
12
+
13
+ ### Features
14
+
15
+ * **lib**: parse tags array from string [27d078b](https://github.com/esatterwhite/semantic-release-docker/commit/27d078b2bbd1f8881f2a4c390b50ac6d384e40f4) - Eric Satterwhite
16
+
17
+ ## Changelog
18
+
19
+ ## [3.0.1](https://github.com/esatterwhite/semantic-release-docker/compare/v3.0.0...v3.0.1) (2021-04-09)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **docker**: honor absolute paths to docker file [723257b](https://github.com/esatterwhite/semantic-release-docker/commit/723257b705e83ed8d951673e5ab5f4ef7d75b437) - Eric Satterwhite
25
+
26
+ # [3.0.0](https://github.com/esatterwhite/semantic-release-docker/compare/v2.2.0...v3.0.0) (2021-04-08)
27
+
28
+
29
+ ### Chores
30
+
31
+ * **deps**: eslint-config-codedependant [dbe464e](https://github.com/esatterwhite/semantic-release-docker/commit/dbe464ebf7ca571ffe0430f67085d30a291f828d) - Eric Satterwhite
32
+ * **deps**: release-config-npm@1.0.1 [98b1a88](https://github.com/esatterwhite/semantic-release-docker/commit/98b1a880ef213abb49af9f9bd5fcff6d13729c21) - Eric Satterwhite
33
+
34
+
35
+ ### Features
36
+
37
+ * **config**: allow templated build arguments [7167dcf](https://github.com/esatterwhite/semantic-release-docker/commit/7167dcf9aba8f554e2ccb9d42209fce3ec86d3e3) - Eric Satterwhite
38
+ * **config**: unnest config values from docker property [2087683](https://github.com/esatterwhite/semantic-release-docker/commit/2087683edfaf66825544ba2eb95eb8c6be533658) - Eric Satterwhite
39
+
40
+
41
+ ### **BREAKING CHANGES**
42
+
43
+ * **config:** Flatten the docker config option for semantic relase into the root
44
+ config. Semantic release doesn't do a very good job of merging options
45
+ that are coming from a sharable configuration. This makes it easier to
46
+ utilize overrides when using a sharable config. Options are camel cased
47
+ using the docker root word prefix
48
+ `docker.args` -> `dockerArgs`
49
+ `docker.login` -> `dockerLogin`
50
+
51
+ # Semantic Release Docker
52
+
53
+ # [2.2.0](https://github.com/esatterwhite/semantic-release-docker/compare/v2.1.0...v2.2.0) (2020-12-21)
54
+
55
+
56
+ ### Features
57
+
58
+ * **verify:** add option to opt out of docker login ([de17169](https://github.com/esatterwhite/semantic-release-docker/commit/de17169897965d197ed51b0aeff2e06d29157c99))
59
+
60
+ # [2.1.0](https://github.com/esatterwhite/semantic-release-docker/compare/v2.0.2...v2.1.0) (2020-08-06)
61
+
62
+
63
+ ### Features
64
+
65
+ * **docker:** Load default build args into docker build ([0760d5e](https://github.com/esatterwhite/semantic-release-docker/commit/0760d5e73560a4bddbadb5a849f7574522c503fc))
66
+
67
+ ## [2.0.2](https://github.com/esatterwhite/semantic-release-docker/compare/v2.0.1...v2.0.2) (2020-08-05)
68
+
69
+ ## [2.0.1](https://github.com/esatterwhite/semantic-release-docker/compare/v2.0.0...v2.0.1) (2020-07-28)
70
+
71
+ # [2.0.0](https://github.com/esatterwhite/semantic-release-docker/compare/v1.0.1...v2.0.0) (2020-07-28)
72
+
73
+
74
+ ### Features
75
+
76
+ * **pkg:** update to trigger a release ([acefb13](https://github.com/esatterwhite/semantic-release-docker/commit/acefb13697ca64723efd90ea0c7f3b5e5a8a5106))
77
+
78
+ ## [1.0.1](https://github.com/esatterwhite/semantic-release-docker/compare/v1.0.0...v1.0.1) (2020-07-26)
79
+
80
+
81
+ ### Bug Fixes
82
+
83
+ * **lib:** fixes a bug in build config that would miss project config ([51b60c1](https://github.com/esatterwhite/semantic-release-docker/commit/51b60c12f8954c2cb59bb78a276529acc08fb8ea))
84
+
85
+ # 1.0.0 (2020-07-24)
86
+
87
+
88
+ ### Features
89
+
90
+ * **pkg:** initial implementation ([4a4dead](https://github.com/esatterwhite/semantic-release-docker/commit/4a4dead685892ecf89e900f3b6f7979c69fc440e))
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Eric Satterwhite
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # @codedependant/semantic-release-docker
2
+
3
+ [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
4
+ [![MIT license](https://img.shields.io/npm/l/@codedependant/semantic-release-docker.svg)](https://www.npmjs.com/package/@codedependant/semantic-release-docker)
5
+
6
+ A [semantic-release](https://github.com/semantic-release/semantic-release) plugin to use semantic versioning for docker images.
7
+
8
+ ## Supported Steps
9
+
10
+ ### verifyConditions
11
+
12
+ verifies that environment variables for authentication via username and password are set.
13
+ It uses a registry server provided via config or environment variable (preferred) or defaults to docker hub if none is given.
14
+ It also verifies that the credentials are correct by logging in to the given registry.
15
+
16
+ ### prepare
17
+
18
+ builds a an image using the specified docker file and context. This image will be used to create tags in later steps
19
+
20
+ ### publish
21
+
22
+ pushes the tags Images with specified tags and pushes them to the registry.
23
+ Tags support simple templating. Values enclosed in braces `{}` will be subsititued with release context information
24
+
25
+ ## Installation
26
+
27
+ Run `npm i --save-dev @codedependant/semantic-release-docker` to install this semantic-release plugin.
28
+
29
+ ## Configuration
30
+
31
+ ### Docker registry authentication
32
+
33
+ The `docker registry` authentication set via environment variables. It is not required, and if
34
+ omitted, it is assumed the docker daemon is already authenticated with the targe registry.
35
+
36
+ ### Environment variables
37
+
38
+ | Variable | Description |
39
+ | ------------------------ | ----------------------------------------------------------------------------------------- |
40
+ | DOCKER_REGISTRY_USER | The user name to authenticate with at the registry. |
41
+ | DOCKER_REGISTRY_PASSWORD | The password used for authentication at the registry. |
42
+
43
+ ### Options
44
+
45
+ | Option | Description | Default
46
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------
47
+ | `dockerTags` | _Optional_. An array of strings allowing to specify additional tags to apply to the image. Supports templating | [`latest`, `{major}-latest`, `{version}`] |
48
+ | `dockerImage` | _Optional_. The name of the image to release. | Parsed from package.json `name` property |
49
+ | `dockerRegistry` | _Optional_. The hostname and port used by the the registry in format `hostname[:port]`. Omit the port if the registry uses the default port | `null` (dockerhub) |
50
+ | `dockerProject` | _Optional_. The project or repository name to publish the image to | For scoped packages, the scope will be used, otherwise `null` |
51
+ | `dockerFile` | _Optional_. The path, relative to `$PWD` to a Docker file to build the target image with | `Dockerfile` |
52
+ | `dockerContext` | _Optional_. A path, relative to `$PWD` to use as the build context A | `.` |
53
+ | `dockerLogin` | _Optional_. Set to false it by pass docker login if the docker daemon is already authorized | `true` |
54
+ | `dockerArgs` | _Optional_. Include additional values for docker's `build-arg`. Supports templating | |
55
+ | `dockerPublish` | _Optional_. Automatically push image tags during the publish phase. | `true` |
56
+
57
+ ### Build Arguments
58
+
59
+ By default several build arguments will be included when the docker images is being built.
60
+ Build arguments can be templated in the same fashion as docker tags. If the value for a
61
+ build argument is explictly `true`, the value will be omitted and the value from
62
+ a matching environment variable will be utilized instead. This can be usefule when trying to include
63
+ secrets and other sensitive information
64
+
65
+ | Argument Name | Description | Default |
66
+ |---------------------|--------------------------------------------------------------------------------------------|------------------------------|
67
+ | `SRC_DIRECTORY` | The of the directory the build was triggered from | The directory name of CWD |
68
+ | `TARGET_PATH` | Path relative to the execution root. Usefule for Sharing a Single Docker file in monorepos | |
69
+ | `NPM_PACKAGE_NAME` | The `name` property extracted from `package.json` - if present | |
70
+ | `NPM_PACKAGE_SCOPE` | The parsed scope from the `name` property from `package.json` - sans `@` | |
71
+ | `CONFIG_NAME` | The configured name of the docker image. | The parsed package name |
72
+ | `CONFIG_PROJECT` | The configured docker repo project name | The package scope if present |
73
+ | `GIT_SHA` | The commit SHA of the current release | |
74
+ | `GIT_TAG` | The git tag of the current release | |
75
+
76
+ ### Template Variables
77
+
78
+ String template will be passed these
79
+
80
+ | Variable name | Description | Type |
81
+ |----------------|--------------------------------------------------------------------|----------|
82
+ | `git_sha` | The commit SHA of the current release | `String` |
83
+ | `git_tag` | The git tag of the current release | `String` |
84
+ | `release_type` | The severity type of the current build (`major`, `minor`, `patch`) | `String` |
85
+ | `relase_notes` | The release notes blob associated with the release | `String` |
86
+ | `next` | Semver object representing the next release | `Object` |
87
+ | `previous` | Semver object representing the previous release | `Object` |
88
+ | `major` | The major version of the next release | `Number` |
89
+ | `minor` | The minor version of the next release | `Number` |
90
+ | `patch` | The patch version of the next release | `Number` |
91
+ | `env` | Environment variables that were set at build time | `Object` |
92
+ | `pkg` | Values parsed from `package.json` | `Object` |
93
+ | `build` | A Random build hash representing the current execution context | `String` |
94
+ | `now` | Current timestamp is ISO 8601 format | `String` |
95
+
96
+ ## Usage
97
+
98
+ **full configuration**:
99
+
100
+ ```javascript
101
+ // release.config.js
102
+
103
+ module.exports = {
104
+ branches: ['main']
105
+ plugins: [
106
+ ['@codedependant/semantic-release-docker', {
107
+ dockerTags: ['latest', '{version}', '{major}-latest', '{major}.{minor}'],
108
+ dockerImage: 'my-image',
109
+ dockerFile: 'Dockerfile',
110
+ dockerRegistry: 'quay.io',
111
+ dockerProject: 'codedependant',
112
+ dockerArgs: {
113
+ API_TOKEN: true
114
+ , RELEASE_DATE: new Date().toISOString()
115
+ , RELEASE_VERSION: '{next.version}'
116
+ }
117
+ }]
118
+ ]
119
+ }
120
+ ```
121
+
122
+ results in `quay.io/codedependant/my-image` with tags `latest`, `1.0.0`, `1-latest` and the `1.0` determined by `semantic-release`.
123
+
124
+ Alternatively, using global options w/ root configuration
125
+ ```json5
126
+ // package.json
127
+ {
128
+ "name": "@codedependant/test-project"
129
+ "version": "1.0.0"
130
+ "release": {
131
+ "extends": "@internal/release-config-docker",
132
+ "dockerTags": ["latest", "{version}", "{major}-latest", "{major}.{minor}"],
133
+ "dockerImage": "my-image",
134
+ "dockerFile": "Dockerfile",
135
+ "dockerRegistry": "quay.io",
136
+ "dockerArgs": {
137
+ "GITHUB_TOKEN": true
138
+ , "SOME_VALUE": '{git_sha}'
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ This would generate the following for a `1.2.0` build
145
+
146
+ ```shell
147
+ $ docker build -t quay.io/codedependant/my-image --build-arg GITHUB_TOKEN --build-arg SOME_VALUE=6eada70 -f Dockerfile .
148
+ $ docker tag <SHA1> latest
149
+ $ docker tag <SHA1> 1.2.0
150
+ $ docker tag <SHA1> 1.2
151
+ $ docker tag <SHA1> 1-latest
152
+ $ docker push quay.io/codedependant/my-image
153
+ ```
154
+
155
+ **minimum configuration**:
156
+
157
+ ```json
158
+ {
159
+ "release": {
160
+ "plugins": [
161
+ "@codedependant/semantic-release-docker"
162
+ ]
163
+ }
164
+ }
165
+ ```
166
+
167
+ * A package name `@codedependant/test-project` results in docker project name`codedependant` and image name `test-project`
168
+ * A package name `test-project` results in a docker image name `test-project`
169
+
170
+ the default docker image tags for the 1.0.0 release would be `1.0.0`, `1-latest`, `latest`
171
+
172
+ ## Development
173
+
174
+ ### Docker Registry
175
+
176
+ To be able to push to the local registry with auth credentials, ssl certificates are required.
177
+ This project uses self signed certs. To regenerate the certs run the following:
178
+
179
+ ```bash
180
+ $ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout server.key -out server.crt
181
+ ```
182
+
183
+ **NOTE**: When prompted for an FQDN it must be `registry:5000`
184
+ **NOTE**: The credentials for the local registry are **user:** `iamweasel`, **pass:** `secretsquirrel`
package/index.js ADDED
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const crypto = require('crypto')
4
+ const dockerPrepare = require('./lib/prepare.js')
5
+ const dockerVerify = require('./lib/verify.js')
6
+ const dockerPublish = require('./lib/publish.js')
7
+ const buildConfig = require('./lib/build-config.js')
8
+ const build_id = crypto.randomBytes(10).toString('hex')
9
+
10
+ module.exports = {
11
+ prepare
12
+ , publish
13
+ , verifyConditions
14
+ , buildConfig
15
+ }
16
+
17
+ async function prepare(config, context) {
18
+ return dockerPrepare(await buildConfig(build_id, config, context), context)
19
+ }
20
+
21
+ async function publish(config, context) {
22
+ return dockerPublish(await buildConfig(build_id, config, context), context)
23
+ }
24
+
25
+ async function verifyConditions(config, context) {
26
+ return dockerVerify(await buildConfig(build_id, config, context), context)
27
+ }
28
+
@@ -0,0 +1,63 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+ const readPkg = require('./read-pkg.js')
5
+ const object = require('./lang/object/index.js')
6
+ const array = require('./lang/array/index.js')
7
+ const parsePkgName = require('./parse-pkg-name.js')
8
+ const PWD = '.'
9
+
10
+ module.exports = buildConfig
11
+
12
+ async function buildConfig(build_id, config, context) {
13
+ const {
14
+ dockerFile: dockerfile = 'Dockerfile'
15
+ , dockerNoCache: nocache = false
16
+ , dockerTags: tags = ['latest', '{major}-latest', '{version}']
17
+ , dockerArgs: args = {}
18
+ , dockerRegistry: registry = null
19
+ , dockerLogin: login = true
20
+ , dockerImage: image
21
+ , dockerPublish: publish = true
22
+ } = config
23
+
24
+ let name = null
25
+ let scope = null
26
+ let pkg = {}
27
+ try {
28
+ pkg = await readPkg({cwd: context.cwd})
29
+ const parsed = parsePkgName(pkg.name)
30
+ name = parsed.name
31
+ scope = parsed.scope
32
+ } catch (_) {}
33
+
34
+ const project = object.has(config, 'dockerProject') ? config.dockerProject : scope
35
+ const root = object.get(context, 'options.root')
36
+ const target = path.relative(root || context.cwd, context.cwd) || PWD
37
+ const {nextRelease = {}} = context
38
+ return {
39
+ registry
40
+ , dockerfile
41
+ , nocache
42
+ , pkg
43
+ , project
44
+ , publish
45
+ , tags: array.toArray(tags)
46
+ , args: {
47
+ SRC_DIRECTORY: path.basename(context.cwd)
48
+ , TARGET_PATH: target
49
+ , NPM_PACKAGE_NAME: object.get(pkg, 'name')
50
+ , NPM_PACKAGE_SCOPE: scope
51
+ , CONFIG_NAME: image || name
52
+ , CONFIG_PROJECT: project
53
+ , GIT_SHA: nextRelease.gitHead || ''
54
+ , GIT_TAG: nextRelease.gitTag || ''
55
+ , ...(args || {})
56
+ }
57
+ , name: image || name
58
+ , build: build_id
59
+ , login: login
60
+ , env: context.env
61
+ , context: config.context || '.'
62
+ }
63
+ }
@@ -0,0 +1,28 @@
1
+ 'use strict'
2
+
3
+ const semver = require('semver')
4
+ const now = new Date().toISOString()
5
+
6
+ module.exports = buildTemplateVars
7
+
8
+ function buildTemplateVars(opts, context) {
9
+ const {nextRelease = {}, lastRelease = {}} = context
10
+
11
+ const versions = {
12
+ next: semver.parse(nextRelease.version) || {}
13
+ , previous: semver.parse(lastRelease.version) || {}
14
+ }
15
+
16
+ const {tags: _, ...rest} = opts
17
+ return {
18
+ ...versions.next
19
+ , ...versions
20
+ , ...nextRelease
21
+ , ...rest
22
+ , git_tag: nextRelease.gitTag
23
+ , git_sha: nextRelease.gitHead
24
+ , release_type: nextRelease.type
25
+ , release_notes: nextRelease.notes
26
+ , now: now
27
+ }
28
+ }
@@ -0,0 +1,127 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+ const crypto = require('crypto')
5
+ const execa = require('execa')
6
+
7
+ class Image {
8
+ constructor(opts) {
9
+ const {
10
+ registry = null
11
+ , project = null
12
+ , name = null
13
+ , sha = null
14
+ , build_id = crypto.randomBytes(10).toString('hex')
15
+ , dockerfile = 'Dockerfile'
16
+ , cwd = process.cwd()
17
+ , context = '.'
18
+ } = opts || {}
19
+
20
+ if (!name || typeof name !== 'string') {
21
+ const error = new TypeError('Docker Image "name" is required and must be a string')
22
+ throw error
23
+ }
24
+
25
+ this.sha = sha
26
+ this.opts = {
27
+ args: new Map()
28
+ , registry: registry
29
+ , project: project
30
+ , name: name
31
+ , build_id: build_id
32
+ , dockerfile: dockerfile
33
+ , context: context
34
+ , cwd: cwd
35
+ }
36
+ }
37
+
38
+ get id() {
39
+ if (this.sha) return this.sha
40
+ return `${this.opts.name}:${this.opts.build_id}`
41
+ }
42
+
43
+ get repo() {
44
+ const parts = []
45
+ if (this.opts.registry) parts.push(this.opts.registry)
46
+ if (this.opts.project) parts.push(this.opts.project)
47
+ parts.push(this.opts.name)
48
+ return parts.join('/')
49
+ }
50
+
51
+ get name() {
52
+ return `${this.repo}:${this.opts.build_id}`
53
+ }
54
+
55
+ get context() {
56
+ return this.opts.context
57
+ }
58
+
59
+ set context(ctx) {
60
+ this.opts.context = ctx
61
+ return this.opts.context
62
+ }
63
+
64
+ get dockerfile() {
65
+ return path.resolve(this.opts.cwd, this.opts.dockerfile)
66
+ }
67
+
68
+ arg(key, val) {
69
+ this.opts.args.set(key, val)
70
+ return this
71
+ }
72
+
73
+ get build_cmd() {
74
+ const args = []
75
+ for (const [name, value] of this.opts.args.entries()) {
76
+ if (value === true || value == null) { // eslint-disable-line no-eq-null
77
+ args.push('--build-arg', name)
78
+ } else {
79
+ args.push('--build-arg', `${name}=${value}`)
80
+ }
81
+ }
82
+ const cmd = [
83
+ 'build'
84
+ , '--quiet'
85
+ , '--tag'
86
+ , this.name
87
+ , ...args
88
+ , '-f'
89
+ , this.dockerfile
90
+ , this.context
91
+ ]
92
+ return cmd
93
+ }
94
+
95
+ async build() {
96
+ const stream = execa('docker', this.build_cmd)
97
+ stream.stdout.pipe(process.stdout)
98
+ stream.stderr.pipe(process.stderr)
99
+ const {stdout} = await stream
100
+ const [_, sha] = stdout.split(':')
101
+ this.sha = sha.substring(0, 12)
102
+ return this.sha
103
+ }
104
+
105
+ async tag(tag, push = true) {
106
+ await execa('docker', ['tag', this.name, `${this.repo}:${tag}`])
107
+ if (!push) return
108
+
109
+ const stream = execa('docker', ['push', `${this.repo}:${tag}`])
110
+ stream.stdout.pipe(process.stdout)
111
+ stream.stderr.pipe(process.stderr)
112
+ await stream
113
+ }
114
+
115
+ async push() {
116
+ await execa('docker', ['push', this.repo])
117
+ }
118
+
119
+ async clean() {
120
+ const images = execa('docker', ['images', this.repo, '-q'])
121
+ const rm = execa('xargs', ['docker', 'rmi', '-f'])
122
+ images.stdout.pipe(rm.stdin)
123
+ return rm
124
+ }
125
+ }
126
+
127
+ module.exports = Image
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ Image: require('./image.js')
5
+ }
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ toArray: require('./to-array.js')
5
+ }
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const CSV_SEP_EXP = /\s*,\s*/
4
+ module.exports = function toArray(item, sep = CSV_SEP_EXP) {
5
+ if (!item) return []
6
+ if (item instanceof Set) return Array.from(item)
7
+ if (Array.isArray(item)) return item
8
+ return typeof item === 'string' ? item.split(sep) : [item]
9
+ }
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ module.exports = getProperty
4
+
5
+ function getProperty(obj, str, sep = '.') {
6
+ if (!obj || !str) return null
7
+ const parts = str.split(sep)
8
+ let ret = obj
9
+ const last = parts.pop()
10
+ let prop
11
+
12
+ /* eslint-disable no-cond-assign */
13
+ while (prop = parts.shift()) {
14
+ ret = ret[prop]
15
+ if (ret === null || ret === undefined) return ret
16
+ }
17
+ return ret[last]
18
+ }
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ module.exports = hasProperty
4
+
5
+ function hasProperty(obj, prop) {
6
+ if (!obj) return false
7
+ return Object.prototype.hasOwnProperty.call(obj, prop)
8
+ }
@@ -0,0 +1,6 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ get: require('./get.js')
5
+ , has: require('./has.js')
6
+ }
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ template: require('./template.js')
5
+ }
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+ const object = require('../object/index.js')
3
+
4
+ module.exports = template
5
+
6
+ function template(str) {
7
+ return function interpolate(values) {
8
+ if (typeof str !== 'string') return str
9
+ return str.replace(/{([^{}]*)}/g, function(original, parsed) {
10
+ const result = object.get(values, parsed)
11
+ return typeof result === 'string' || typeof result === 'number' ? result : original
12
+ })
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ 'use strict'
2
+
3
+ const NAME_EXP = /^(?:@([^/]+?)[/])?([^/]+?)$/
4
+
5
+ module.exports = parsePkgName
6
+
7
+ function parsePkgName(pkgname) {
8
+ if (!pkgname) return {scope: null, name: null}
9
+ const [_, scope = null, name = null] = (NAME_EXP.exec(pkgname) || [])
10
+ return {scope, name}
11
+ }
12
+
package/lib/prepare.js ADDED
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+ const docker = require('./docker/index.js')
5
+ const buildTemplateVars = require('./build-template-vars.js')
6
+ const string = require('./lang/string/index.js')
7
+
8
+ module.exports = dockerPrepare
9
+
10
+ async function dockerPrepare(opts, context) {
11
+ const {cwd} = context
12
+ const image = new docker.Image({
13
+ registry: opts.registry
14
+ , project: opts.project
15
+ , name: opts.name
16
+ , dockerfile: opts.dockerfile
17
+ , build_id: opts.build
18
+ , cwd: cwd
19
+ })
20
+
21
+ const args = {
22
+ ...opts.args
23
+ }
24
+
25
+ const vars = buildTemplateVars(opts, context)
26
+ for (const [key, value] of Object.entries(args)) {
27
+ image.arg(key, string.template(value)(vars))
28
+ }
29
+
30
+ context.logger.info('building image', image.name)
31
+ context.logger.debug('build command: docker %s', image.build_cmd.join(' '))
32
+ await image.build(path.join(cwd, opts.context))
33
+ return image
34
+ }