@codedependant/semantic-release-docker 4.2.0 → 4.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Semantic Release Docker
2
2
 
3
+ # [4.4.0](https://github.com/esatterwhite/semantic-release-docker/compare/v4.3.0...v4.4.0) (2023-07-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * **image**: add support for cache-from build flag [efae32d](https://github.com/esatterwhite/semantic-release-docker/commit/efae32d760bc0ef1978ad97f834b4cb034199ace) - Eric Satterwhite, closes: [#35](https://github.com/esatterwhite/semantic-release-docker/issues/35)
9
+ * **image**: support arbitrary build flags [d7e875d](https://github.com/esatterwhite/semantic-release-docker/commit/d7e875d2b0b8de58e57c56e9ba24bf3c2db5df84) - Eric Satterwhite
10
+
11
+ # [4.3.0](https://github.com/esatterwhite/semantic-release-docker/compare/v4.2.0...v4.3.0) (2022-12-29)
12
+
13
+
14
+ ### Features
15
+
16
+ * **config**: add option to disable post publish image removal [0c03cbd](https://github.com/esatterwhite/semantic-release-docker/commit/0c03cbd16bf7aa34cb435e3782492ad294e9bdfd) - Eric Satterwhite, closes: [#28](https://github.com/esatterwhite/semantic-release-docker/issues/28)
17
+
3
18
  # [4.2.0](https://github.com/esatterwhite/semantic-release-docker/compare/v4.1.0...v4.2.0) (2022-12-19)
4
19
 
5
20
 
package/README.md CHANGED
@@ -42,19 +42,22 @@ omitted, it is assumed the docker daemon is already authenticated with the targe
42
42
 
43
43
  ### Options
44
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
- | `dockerVerifyCmd` | _Optional_. If specified, during the verify stage, the specified command will execute in a container of the build image. If the command errors, the release will fail. | `false` |
57
- | `dockerNetwork` | _Optional_. Specify the Docker network to use while the image is building. | `default` |
45
+ | Option | Description | Type | Default |
46
+ |------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|---------------------------------------------------------------|
47
+ | `dockerTags` | _Optional_. An array of strings allowing to specify additional tags to apply to the image. Supports templating | [Array][]<[String][]> | [`latest`, `{{major}}-latest`, `{{version}}`] |
48
+ | `dockerImage` | _Optional_. The name of the image to release. | [String][] | 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 | [String][] | `null` (dockerhub) |
50
+ | `dockerProject` | _Optional_. The project or repository name to publish the image to | [String][] | 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 | [String][] | `Dockerfile` |
52
+ | `dockerContext` | _Optional_. A path, relative to `$PWD` to use as the build context A | [String][] | `.` |
53
+ | `dockerLogin` | _Optional_. Set to false it by pass docker login if the docker daemon is already authorized | [String][] | `true` |
54
+ | `dockerArgs` | _Optional_. Include additional values for docker's `build-arg`. Supports templating | [Object][] | |
55
+ | `dockerPublish` | _Optional_. Automatically push image tags during the publish phase. | [Boolean][] | `true` |
56
+ | `dockerVerifyCmd` | _Optional_. If specified, during the verify stage, the specified command will execute in a container of the build image. If the command errors, the release will fail. | [String][] | `false` |
57
+ | `dockerNetwork` | _Optional_. Specify the Docker network to use while the image is building. | [String][] | `default` |
58
+ | `dockerAutoClean` | _Optional_. If set to true | [Boolean][] | `true` |
59
+ | `dockerBuildFlags` | _Optional_. An object containing additional flags to the `docker build` command. Values can be strings or an array of strings | [Object][] | `{}` |
60
+ | `dockerBuildCacheFrom` | _Optional_. A list of external cache sources. See [--cache-from][] | [String][] | [Array][]<[String][]> | |
58
61
 
59
62
  ### Build Arguments
60
63
 
@@ -79,22 +82,22 @@ secrets and other sensitive information
79
82
 
80
83
  String template will be passed these
81
84
 
82
- | Variable name | Description | Type |
83
- |----------------|--------------------------------------------------------------------|-----------------|
84
- | `git_sha` | The commit SHA of the current release | `String` |
85
- | `git_tag` | The git tag of the current release | `String` |
86
- | `release_type` | The severity type of the current build (`major`, `minor`, `patch`) | `String` |
87
- | `relase_notes` | The release notes blob associated with the release | `String` |
88
- | `next` | Semver object representing the next release | `Object` |
89
- | `previous` | Semver object representing the previous release | `Object` |
90
- | `major` | The major version of the next release | `Number` |
91
- | `minor` | The minor version of the next release | `Number` |
92
- | `patch` | The patch version of the next release | `Number` |
93
- | `prerelease` | The prerelease versions of the next release | `Array<Number>` |
94
- | `env` | Environment variables that were set at build time | `Object` |
95
- | `pkg` | Values parsed from `package.json` | `Object` |
96
- | `build` | A Random build hash representing the current execution context | `String` |
97
- | `now` | Current timestamp is ISO 8601 format | `String` |
85
+ | Variable name | Description | Type |
86
+ |----------------|--------------------------------------------------------------------|-----------------------------|
87
+ | `git_sha` | The commit SHA of the current release | [String][] |
88
+ | `git_tag` | The git tag of the current release | [String][] |
89
+ | `release_type` | The severity type of the current build (`major`, `minor`, `patch`) | [String][] |
90
+ | `relase_notes` | The release notes blob associated with the release | [String][] |
91
+ | `next` | Semver object representing the next release | [Object][] |
92
+ | `previous` | Semver object representing the previous release | [Object][] |
93
+ | `major` | The major version of the next release | [Number][] |
94
+ | `minor` | The minor version of the next release | [Number][] |
95
+ | `patch` | The patch version of the next release | [Number][] |
96
+ | `prerelease` | The prerelease versions of the next release | [Array][]&lt;[Number][]&gt; |
97
+ | `env` | Environment variables that were set at build time | [Object][] |
98
+ | `pkg` | Values parsed from `package.json` | [Object][] |
99
+ | `build` | A Random build hash representing the current execution context | [String][] |
100
+ | `now` | Current timestamp is ISO 8601 format | [String][] |
98
101
 
99
102
  ### Template Helpers
100
103
 
@@ -116,7 +119,50 @@ The following handlebars template helpers are pre installed
116
119
  | `startswith` | returns true if a string starts with another | [Boolean][] | <pre lang="hbs">{{#if (starts myvar 'foo')}}{{ othervar }}{{/if}}</pre> |
117
120
  | `upper` | returns the upper cased varient of the input string | [String][] | <pre lang="hbs">{{upper my_var}}</pre> |
118
121
 
122
+ ### Build Flags
119
123
 
124
+ Using the `dockerBuildFlags` option allows you to pass arbitrary flags to the build command.
125
+ If the standardized options are not sufficient, `dockerBuildFlags` is a perfect workaround
126
+ until first class support can be added. This is considered and advanced feature, and you should
127
+ know what you intend to do before using. There is no validation, and any configuration of the
128
+ docker daemon required is expected to be done before hand.
129
+
130
+ Keys found in `dockerBuildFlags` are normalized as command line flags in the following manner:
131
+
132
+ * If the key does not start with a `-` it will be prepended
133
+ * all occurences of `_` will be re-written as `-`
134
+ * Single letter keys are considered shorthands e.g. `p` becomes `-p`
135
+ * Multi letter keys are considered long form e.g. `foo_bar` becomes `--foo-bar`
136
+ * If the value is an array, the flag is repeated for each occurance
137
+ * A `null` value may be used to omit the value and only inject the flag itself
138
+
139
+ #### Example
140
+
141
+ ```javascript
142
+ {
143
+ plugins: [
144
+ ['@codedependant/semantic-release-docker', {
145
+ dockerImage: 'my-image',
146
+ dockerRegistry: 'quay.io',
147
+ dockerProject: 'codedependant',
148
+ dockerCacheFrom: 'myname/myapp'
149
+ dockerBuildFlags: {
150
+ pull: null
151
+ , target: 'release'
152
+ },
153
+ dockerArgs: {
154
+ GITHUB_TOKEN: null
155
+ }
156
+ }]
157
+ ]
158
+ }
159
+ ```
160
+
161
+ This configuration, will generate the following build command
162
+
163
+ ```bash
164
+ > docker build --network=default --quiet --tag quay.io/codedependant/my-image:abc123 --cache-from myname/myapp --build-arg GITHUB_TOKEN --pull --target release -f path/to/repo/Dockerfile /path/to/repo
165
+ ```
120
166
 
121
167
  ## Usage
122
168
 
@@ -134,8 +180,12 @@ module.exports = {
134
180
  dockerFile: 'Dockerfile',
135
181
  dockerRegistry: 'quay.io',
136
182
  dockerProject: 'codedependant',
183
+ dockerBuildFlags: {
184
+ pull: null
185
+ , target: 'release'
186
+ },
137
187
  dockerArgs: {
138
- API_TOKEN: true
188
+ API_TOKEN: null
139
189
  , RELEASE_DATE: new Date().toISOString()
140
190
  , RELEASE_VERSION: '{{next.version}}'
141
191
  }
@@ -230,3 +280,6 @@ $ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout server.key -o
230
280
  [Boolean]: https://mdn.io/boolean
231
281
  [String]: https://mdn.io/string
232
282
  [Array]: https://mdn.io/array
283
+ [Object]: https://mdn.io/object
284
+ [Number]: https://mdn.io/number
285
+ [--cache-from]: https://docs.docker.com/engine/reference/commandline/build/#cache-from
package/index.js CHANGED
@@ -5,13 +5,21 @@ const dockerPrepare = require('./lib/prepare.js')
5
5
  const dockerVerify = require('./lib/verify.js')
6
6
  const dockerPublish = require('./lib/publish.js')
7
7
  const buildConfig = require('./lib/build-config.js')
8
+ const dockerSuccess = require('./lib/success.js')
9
+ const dockerFail = require('./lib/fail.js')
8
10
  const build_id = crypto.randomBytes(10).toString('hex')
9
11
 
10
12
  module.exports = {
11
- prepare
13
+ buildConfig
14
+ , fail
15
+ , prepare
12
16
  , publish
17
+ , success
13
18
  , verifyConditions
14
- , buildConfig
19
+ }
20
+
21
+ async function fail(config, context) {
22
+ return dockerFail(await buildConfig(build_id, config, context), context)
15
23
  }
16
24
 
17
25
  async function prepare(config, context) {
@@ -22,7 +30,10 @@ async function publish(config, context) {
22
30
  return dockerPublish(await buildConfig(build_id, config, context), context)
23
31
  }
24
32
 
33
+ async function success(config, context) {
34
+ return dockerSuccess(await buildConfig(build_id, config, context), context)
35
+ }
36
+
25
37
  async function verifyConditions(config, context) {
26
38
  return dockerVerify(await buildConfig(build_id, config, context), context)
27
39
  }
28
-
@@ -5,6 +5,7 @@ const readPkg = require('./read-pkg.js')
5
5
  const object = require('./lang/object/index.js')
6
6
  const array = require('./lang/array/index.js')
7
7
  const parsePkgName = require('./parse-pkg-name.js')
8
+ const typeCast = require('./lang/string/typecast.js')
8
9
  const PWD = '.'
9
10
 
10
11
  module.exports = buildConfig
@@ -15,6 +16,8 @@ async function buildConfig(build_id, config, context) {
15
16
  , dockerNoCache: nocache = false
16
17
  , dockerTags: tags = ['latest', '{{major}}-latest', '{{version}}']
17
18
  , dockerArgs: args = {}
19
+ , dockerBuildFlags: build_flags = {}
20
+ , dockerBuildCacheFrom: cache_from
18
21
  , dockerRegistry: registry = null
19
22
  , dockerLogin: login = true
20
23
  , dockerImage: image
@@ -22,6 +25,7 @@ async function buildConfig(build_id, config, context) {
22
25
  , dockerContext = '.'
23
26
  , dockerVerifyCmd: verifycmd = null
24
27
  , dockerNetwork: network = 'default'
28
+ , dockerAutoClean: clean = true
25
29
  } = config
26
30
 
27
31
  let name = null
@@ -38,10 +42,14 @@ async function buildConfig(build_id, config, context) {
38
42
  const root = object.get(context, 'options.root')
39
43
  const target = path.relative(root || context.cwd, context.cwd) || PWD
40
44
  const {nextRelease = {}} = context
45
+
46
+ if (cache_from) build_flags.cache_from = array.toArray(cache_from)
47
+
41
48
  return {
42
49
  registry
43
50
  , dockerfile
44
51
  , nocache
52
+ , build_flags
45
53
  , pkg
46
54
  , project
47
55
  , publish
@@ -64,5 +72,6 @@ async function buildConfig(build_id, config, context) {
64
72
  , env: context.env
65
73
  , context: dockerContext
66
74
  , network: network
75
+ , clean: typeCast(clean) === true
67
76
  }
68
77
  }
@@ -26,15 +26,16 @@ class Image {
26
26
 
27
27
  this.sha = sha
28
28
  this.opts = {
29
- args: new Map()
30
- , registry: registry
31
- , project: project
32
- , name: name
33
- , build_id: build_id
34
- , dockerfile: dockerfile
29
+ build_id: build_id
30
+ , args: new Map()
35
31
  , context: context
36
32
  , cwd: cwd
33
+ , dockerfile: dockerfile
34
+ , flags: new Map()
35
+ , name: name
37
36
  , network: network
37
+ , project: project
38
+ , registry: registry
38
39
  }
39
40
  }
40
41
 
@@ -72,27 +73,66 @@ class Image {
72
73
  return this.opts.network
73
74
  }
74
75
 
75
- arg(key, val) {
76
+ get flags() {
77
+ const output = []
78
+
79
+ for (const [key, value] of this.opts.flags.entries()) {
80
+ let normalized = key
81
+ if (!key.startsWith('-')) {
82
+ normalized = (key.length === 1 ? `-${key}` : `--${key}`)
83
+ .toLowerCase()
84
+ .replace(/_/g, '-')
85
+ }
86
+
87
+ if (value === null) {
88
+ output.push(normalized)
89
+ continue
90
+ }
91
+
92
+ for (const item of value) {
93
+ output.push(normalized, item)
94
+ }
95
+ }
96
+
97
+ return output
98
+ }
99
+
100
+ arg(key, val = null) {
101
+ if (val === true || val == null) { // eslint-disable-line no-eq-null
102
+ this.flag('build-arg', key)
103
+ } else {
104
+ this.flag('build-arg', `${key}=${val}`)
105
+ }
76
106
  this.opts.args.set(key, val)
77
107
  return this
78
108
  }
79
109
 
80
- get build_cmd() {
81
- const args = []
82
- for (const [name, value] of this.opts.args.entries()) {
83
- if (value === true || value == null) { // eslint-disable-line no-eq-null
84
- args.push('--build-arg', name)
85
- } else {
86
- args.push('--build-arg', `${name}=${value}`)
87
- }
110
+ flag(key, val) {
111
+ if (val === null) {
112
+ this.opts.flags.set(key, val)
113
+ return this
114
+ }
115
+
116
+ let value = this.opts.flags.get(key) || []
117
+
118
+ if (Array.isArray(val)) {
119
+ value = value.concat(val)
120
+ } else {
121
+ value.push(val)
88
122
  }
123
+
124
+ this.opts.flags.set(key, value)
125
+ return this
126
+ }
127
+
128
+ get build_cmd() {
89
129
  return [
90
130
  'build'
91
131
  , `--network=${this.network}`
92
132
  , '--quiet'
93
133
  , '--tag'
94
134
  , this.name
95
- , ...args
135
+ , ...this.flags
96
136
  , '-f'
97
137
  , this.dockerfile
98
138
  , this.context
package/lib/fail.js ADDED
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const postPublish = require('./post-publish.js')
4
+
5
+ module.exports = fail
6
+
7
+ function fail(opts, context) {
8
+ return postPublish(opts, context)
9
+ }
@@ -2,4 +2,5 @@
2
2
 
3
3
  module.exports = {
4
4
  template: require('./template.js')
5
+ , typecast: require('./typecast.js')
5
6
  }
@@ -0,0 +1,32 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @module lib/string/typecast
5
+ * @author Eric Satterwhite
6
+ **/
7
+
8
+ module.exports = function typecast(value) {
9
+ if (value === 'null' || value === null) return null
10
+ if (value === 'undefined' || value === undefined) return undefined
11
+ if (value === 'true' || value === true) return true
12
+ if (value === 'false' || value === false) return false
13
+ if (value === '' || isNaN(value)) return value
14
+ if (isFinite(value)) return parseFloat(value)
15
+ return value
16
+ }
17
+
18
+ /**
19
+ * Best effort to cast a string to its native couter part where possible
20
+ * Supported casts are booleans, numbers, null and undefined
21
+ * @function module:lib/string/typecast
22
+ * @param {String} str The string value to typecast
23
+ * @return {*} The coerced value
24
+ * @example
25
+ * typecast('null') // null
26
+ * @example
27
+ * typecast('true') // true
28
+ * @example
29
+ * typecast('10.01') // 10.01
30
+ * @example
31
+ * typecast({}) // {}
32
+ **/
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ const docker = require('./docker/index.js')
4
+
5
+ module.exports = postPublish
6
+
7
+ async function postPublish(opts, context) {
8
+ const {cwd, logger} = context
9
+ const image = new docker.Image({
10
+ registry: opts.registry
11
+ , project: opts.project
12
+ , name: opts.name
13
+ , dockerfile: opts.dockerfile
14
+ , build_id: opts.build
15
+ , cwd: cwd
16
+ , context: opts.context
17
+ })
18
+
19
+ if (!opts.clean) return
20
+
21
+ logger.info(`removing images for ${image.repo}`)
22
+ await image.clean()
23
+ }
package/lib/prepare.js CHANGED
@@ -20,15 +20,25 @@ async function dockerPrepare(opts, context) {
20
20
  , network: opts.network
21
21
  })
22
22
 
23
- const args = {
24
- ...opts.args
23
+ const vars = buildTemplateVars(opts, context)
24
+
25
+ function render(item, vars) {
26
+ if (Array.isArray(item)) {
27
+ return item.map((element) => {
28
+ return string.template(element)(vars)
29
+ })
30
+ }
31
+ return string.template(item)(vars)
25
32
  }
26
33
 
27
- const vars = buildTemplateVars(opts, context)
28
- for (const [key, value] of Object.entries(args)) {
34
+ for (const [key, value] of Object.entries(opts.args)) {
29
35
  image.arg(key, string.template(value)(vars))
30
36
  }
31
37
 
38
+ for (const [key, value] of Object.entries(opts.build_flags)) {
39
+ image.flag(key, render(value, vars))
40
+ }
41
+
32
42
  context.logger.info('building image', image.name)
33
43
  context.logger.debug('build command: docker %s', image.build_cmd.join(' '))
34
44
  await image.build(path.join(cwd, opts.context))
package/lib/publish.js CHANGED
@@ -28,6 +28,4 @@ async function publish(opts, context) {
28
28
  logger.info(`pushing image: ${image.repo} tag: ${tag}`)
29
29
  await image.tag(tag, opts.publish)
30
30
  }
31
-
32
- await image.clean()
33
31
  }
package/lib/success.js ADDED
@@ -0,0 +1,9 @@
1
+ 'use strict'
2
+
3
+ const postPublish = require('./post-publish.js')
4
+
5
+ module.exports = success
6
+
7
+ function success(opts, context) {
8
+ return postPublish(opts, context)
9
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codedependant/semantic-release-docker",
3
3
  "private": false,
4
- "version": "4.2.0",
4
+ "version": "4.4.0",
5
5
  "description": "docker package",
6
6
  "main": "index.js",
7
7
  "files": [