@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
@@ -0,0 +1,205 @@
1
+ 'use strict'
2
+
3
+ const crypto = require('crypto')
4
+ const sinon = require('sinon')
5
+ const {test, threw} = require('tap')
6
+
7
+ const buildConfig = require('../../lib/build-config.js')
8
+ const verify = require('../../lib/verify.js')
9
+ const DOCKER_REGISTRY_HOST = process.env.TEST_DOCKER_REGISTRY || 'localhost:5000'
10
+
11
+ test('steps::verify', async (t) => {
12
+ const build_id = crypto.randomBytes(5).toString('hex')
13
+ t.test('docker password, no username', async (tt) => {
14
+ const context = {
15
+ env: {
16
+ ...process.env
17
+ , DOCKER_REGISTRY_PASSWORD: 'abc123'
18
+ }
19
+ , cwd: process.cwd()
20
+ , logger: {
21
+ success: sinon.stub()
22
+ , info: sinon.stub()
23
+ }
24
+ }
25
+ const config = await buildConfig(build_id, {}, context)
26
+ await tt.rejects(
27
+ verify(config, context)
28
+ , {
29
+ message: /docker authentication failed/i
30
+ , code: 'EAUTH'
31
+ , details: /DOCKER_REGISTRY_USER AND DOCKER_REGISTRY_PASSWORD must be set/ig
32
+ }
33
+ )
34
+ })
35
+
36
+ t.test('docker password, no username', async (tt) => {
37
+ const context = {
38
+ env: {
39
+ ...process.env
40
+ , DOCKER_REGISTRY_USER: 'abc123'
41
+ }
42
+ , cwd: process.cwd()
43
+ , logger: {
44
+ success: sinon.stub()
45
+ , info: sinon.stub()
46
+ }
47
+ }
48
+ const config = await buildConfig(build_id, {}, context)
49
+ await tt.rejects(
50
+ verify(config, context)
51
+ , {
52
+ message: /docker authentication failed/i
53
+ , code: 'EAUTH'
54
+ , details: /DOCKER_REGISTRY_USER AND DOCKER_REGISTRY_PASSWORD must be set/ig
55
+ }
56
+ )
57
+ })
58
+
59
+ t.test('docker USER/PASS succeeds', async (tt) => {
60
+ const context = {
61
+ env: {
62
+ ...process.env
63
+ , DOCKER_REGISTRY_USER: 'iamweasel'
64
+ , DOCKER_REGISTRY_PASSWORD: 'secretsquirrel'
65
+ }
66
+ , cwd: process.cwd()
67
+ , logger: {
68
+ success: sinon.stub()
69
+ , info: sinon.stub()
70
+ }
71
+ }
72
+ const config = await buildConfig(build_id, {
73
+ dockerRegistry: DOCKER_REGISTRY_HOST
74
+ }, context)
75
+ tt.resolves(verify(config, context))
76
+ })
77
+
78
+ t.test('docker USER / GITHUB_TOKEN succeeds', async (tt) => {
79
+ const context = {
80
+ env: {
81
+ ...process.env
82
+ , DOCKER_REGISTRY_USER: 'iamweasel'
83
+ , GITHUB_TOKEN: 'secretsquirrel'
84
+ }
85
+ , cwd: process.cwd()
86
+ , logger: {
87
+ success: sinon.stub()
88
+ , info: sinon.stub()
89
+ }
90
+ }
91
+ const config = await buildConfig(build_id, {
92
+ dockerRegistry: DOCKER_REGISTRY_HOST
93
+ }, context)
94
+ tt.resolves(verify(config, context))
95
+ })
96
+
97
+ t.test('docker no login', async (tt) => {
98
+ const context = {
99
+ env: {
100
+ ...process.env
101
+ , DOCKER_REGISTRY_USER: 'iamweasel'
102
+ }
103
+ , cwd: process.cwd()
104
+ , logger: {
105
+ success: sinon.stub()
106
+ , info: sinon.stub()
107
+ }
108
+ }
109
+ const config = await buildConfig(build_id, {
110
+ dockerRegistry: DOCKER_REGISTRY_HOST
111
+ , dockerLogin: false
112
+ }, context)
113
+ tt.resolves(verify(config, context))
114
+ })
115
+
116
+ t.test('docker password, no username', async (tt) => {
117
+ const context = {
118
+ env: {
119
+ ...process.env
120
+ , DOCKER_REGISTRY_PASSWORD: 'abc123'
121
+ , DOCKER_REGISTRY_USER: 'abc123'
122
+ }
123
+ , cwd: process.cwd()
124
+ , logger: {
125
+ success: sinon.stub()
126
+ , info: sinon.stub()
127
+ }
128
+ }
129
+ const config = await buildConfig(build_id, {}, context)
130
+ tt.rejects(
131
+ verify(config, context)
132
+ , {
133
+ message: /docker authentication failed/i
134
+ , code: 'EAUTH'
135
+ , details: /authentication to dockerhub failed/ig
136
+ }
137
+ )
138
+ })
139
+
140
+ t.test('No auth provided succeed', async (tt) => {
141
+ const context = {
142
+ env: {
143
+ ...process.env
144
+ }
145
+ , cwd: process.cwd()
146
+ , logger: {
147
+ success: sinon.stub()
148
+ , info: sinon.stub()
149
+ }
150
+ }
151
+
152
+ const config = await buildConfig(build_id, {}, context)
153
+ tt.strictEqual(await verify(config, context), false, 'auth step skipped')
154
+ })
155
+
156
+ t.test('unable to collect image name', async (tt) => {
157
+ const context = {
158
+ env: {
159
+ ...process.env
160
+ }
161
+ , cwd: __dirname
162
+ , logger: {
163
+ success: sinon.stub()
164
+ , info: sinon.stub()
165
+ }
166
+ }
167
+
168
+ const config = await buildConfig(build_id, {
169
+ dockerRegistry: DOCKER_REGISTRY_HOST
170
+ }, context)
171
+ tt.rejects(verify(config, context), {
172
+ code: 'EINVAL'
173
+ , name: 'SemanticReleaseError'
174
+ , details: new RegExp(
175
+ 'image name parsed from package.json name if possible. or via the "image" option'
176
+ , 'gi'
177
+ )
178
+ })
179
+ })
180
+
181
+ t.test('invalid docker file location', async (tt) => {
182
+ const context = {
183
+ env: {
184
+ ...process.env
185
+ }
186
+ , cwd: process.cwd()
187
+ , logger: {
188
+ success: sinon.stub()
189
+ , info: sinon.stub()
190
+ }
191
+ }
192
+
193
+ const config = await buildConfig(build_id, {
194
+ dockerRegistry: DOCKER_REGISTRY_HOST
195
+ , dockerFile: 'Notafile'
196
+ }, context)
197
+
198
+ await tt.rejects(verify(config, context), {
199
+ code: 'ENOENT'
200
+ , name: 'SemanticReleaseError'
201
+ , details: /relative to pwd/gi
202
+ })
203
+
204
+ })
205
+ }).catch(threw)
@@ -0,0 +1,167 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+ const {test, threw} = require('tap')
5
+ const buildConfig = require('../../lib/build-config.js')
6
+
7
+ test('build-config', async (t) => {
8
+ t.testdir({
9
+ standard: {
10
+ 'package.json': JSON.stringify({name: 'this-is-not-scoped'})
11
+ }
12
+ , scoped: {
13
+ 'package.json': JSON.stringify({name: '@scope/this-is-scoped'})
14
+ }
15
+ , workspace: {
16
+ one: {
17
+ 'package.json': JSON.stringify({name: '@internal/package'})
18
+ }
19
+ }
20
+ })
21
+
22
+ t.test('standard package', async (tt) => {
23
+ const config = await buildConfig('id', {
24
+ }, {
25
+ cwd: path.join(t.testdirName, 'standard')
26
+ })
27
+ tt.match(config, {
28
+ dockerfile: 'Dockerfile'
29
+ , publish: true
30
+ , nocache: false
31
+ , tags: ['latest', '{major}-latest', '{version}']
32
+ , args: {
33
+ SRC_DIRECTORY: 'standard'
34
+ , TARGET_PATH: '.'
35
+ , NPM_PACKAGE_NAME: 'this-is-not-scoped'
36
+ , NPM_PACKAGE_SCOPE: null
37
+ , CONFIG_NAME: 'this-is-not-scoped'
38
+ , CONFIG_PROJECT: null
39
+ }
40
+ , pkg: Object
41
+ , registry: null
42
+ , name: 'this-is-not-scoped'
43
+ , project: null
44
+ , build: 'id'
45
+ , context: '.'
46
+ })
47
+ })
48
+
49
+ t.test('nested workspace: target resolution', async (tt) => {
50
+ const config = await buildConfig('id', {
51
+ }, {
52
+ options: {
53
+ root: t.testdirName
54
+ }
55
+ , cwd: path.join(t.testdirName, 'workspace', 'one')
56
+ })
57
+ tt.match(config, {
58
+ dockerfile: 'Dockerfile'
59
+ , nocache: false
60
+ , publish: true
61
+ , tags: ['latest', '{major}-latest', '{version}']
62
+ , args: {
63
+ SRC_DIRECTORY: 'one'
64
+ , TARGET_PATH: 'workspace/one'
65
+ , NPM_PACKAGE_NAME: 'package'
66
+ , NPM_PACKAGE_SCOPE: 'internal'
67
+ , CONFIG_NAME: 'package'
68
+ , CONFIG_PROJECT: 'internal'
69
+ }
70
+ , pkg: Object
71
+ , registry: null
72
+ , name: 'package'
73
+ , project: 'internal'
74
+ , build: 'id'
75
+ , context: '.'
76
+ })
77
+ })
78
+
79
+ t.test('scoped package', async (tt) => {
80
+ {
81
+ const config = await buildConfig('id', {
82
+ }, {
83
+ cwd: path.join(t.testdirName, 'scoped')
84
+ })
85
+ tt.match(config, {
86
+ dockerfile: 'Dockerfile'
87
+ , nocache: false
88
+ , tags: ['latest', '{major}-latest', '{version}']
89
+ , args: {
90
+ SRC_DIRECTORY: 'scoped'
91
+ , TARGET_PATH: '.'
92
+ , NPM_PACKAGE_NAME: '@scope/this-is-scoped'
93
+ , NPM_PACKAGE_SCOPE: 'scope'
94
+ , CONFIG_NAME: 'this-is-scoped'
95
+ , CONFIG_PROJECT: 'scope'
96
+ }
97
+ , pkg: Object
98
+ , registry: null
99
+ , name: 'this-is-scoped'
100
+ , project: 'scope'
101
+ , build: 'id'
102
+ , context: '.'
103
+ })
104
+ }
105
+
106
+ {
107
+ const config = await buildConfig('id', {
108
+ dockerProject: 'kittens'
109
+ , dockerImage: 'override'
110
+ , dockerFile: 'Dockerfile.test'
111
+ , dockerPublish: false
112
+ }, {
113
+ cwd: path.join(t.testdirName, 'scoped')
114
+ })
115
+ tt.match(config, {
116
+ dockerfile: 'Dockerfile.test'
117
+ , publish: false
118
+ , nocache: false
119
+ , tags: ['latest', '{major}-latest', '{version}']
120
+ , args: {
121
+ SRC_DIRECTORY: 'scoped'
122
+ , TARGET_PATH: '.'
123
+ , NPM_PACKAGE_NAME: '@scope/this-is-scoped'
124
+ , NPM_PACKAGE_SCOPE: 'scope'
125
+ , CONFIG_NAME: 'override'
126
+ , CONFIG_PROJECT: 'kittens'
127
+ }
128
+ , pkg: Object
129
+ , registry: null
130
+ , name: 'override'
131
+ , project: 'kittens'
132
+ , build: 'id'
133
+ , context: '.'
134
+ })
135
+ }
136
+
137
+ {
138
+ const config = await buildConfig('id', {
139
+ dockerProject: null
140
+ , dockerImage: 'override'
141
+ , dockerFile: 'Dockerfile.test'
142
+ , dockerTags: 'latest,{major}-latest , fake, {version}'
143
+ }, {
144
+ cwd: path.join(t.testdirName, 'scoped')
145
+ })
146
+ tt.match(config, {
147
+ dockerfile: 'Dockerfile.test'
148
+ , nocache: false
149
+ , tags: ['latest', '{major}-latest', 'fake', '{version}']
150
+ , args: {
151
+ SRC_DIRECTORY: 'scoped'
152
+ , TARGET_PATH: '.'
153
+ , NPM_PACKAGE_NAME: '@scope/this-is-scoped'
154
+ , NPM_PACKAGE_SCOPE: 'scope'
155
+ , CONFIG_NAME: 'override'
156
+ , CONFIG_PROJECT: null
157
+ }
158
+ , pkg: Object
159
+ , registry: null
160
+ , name: 'override'
161
+ , project: null
162
+ , build: 'id'
163
+ , context: '.'
164
+ })
165
+ }
166
+ })
167
+ }).catch(threw)
@@ -0,0 +1,63 @@
1
+ 'use strict'
2
+
3
+ const {test, threw} = require('tap')
4
+ const buildConfig = require('../../lib/build-config.js')
5
+ const buildTemplateVars = require('../../lib/build-template-vars.js')
6
+
7
+ test('buildTemplateVars', async (t) => {
8
+ const cwd = t.testdir({
9
+ 'package.json': JSON.stringify({
10
+ name: 'template-vars'
11
+ })
12
+ })
13
+ const context = {
14
+ cwd
15
+ , nextRelease: {
16
+ version: '1.0.0'
17
+ , gitTag: 'v1.0.0'
18
+ , gitHead: 'abcdefgh'
19
+ , type: 'major'
20
+ , notes: 'test it'
21
+ }
22
+ }
23
+ const opts = await buildConfig('abacadaba', {
24
+ dockerArgs: {
25
+ TEMPLATE_VALUE: '{type}.{version}'
26
+ , BOOLEAN_VALUE: true
27
+ , NULL_VALUE: null
28
+ }
29
+ }, context)
30
+
31
+ const vars = buildTemplateVars(opts, context)
32
+ t.match(vars, {
33
+ release_type: 'major'
34
+ , release_notes: 'test it'
35
+ , version: '1.0.0'
36
+ , git_sha: 'abcdefgh'
37
+ , git_tag: 'v1.0.0'
38
+ , pkg: {name: 'template-vars'}
39
+ , major: 1
40
+ , minor: 0
41
+ , patch: 0
42
+ , version: '1.0.0'
43
+ , next: {
44
+ major: 1
45
+ , minor: 0
46
+ , patch: 0
47
+ , version: '1.0.0'
48
+ }
49
+ , args: {
50
+ SRC_DIRECTORY: String
51
+ , TARGET_PATH: String
52
+ , NPM_PACKAGE_NAME: 'template-vars'
53
+ , NPM_PACKAGE_SCOPE: null
54
+ , CONFIG_NAME: String
55
+ , CONFIG_PROJECT: null
56
+ , GIT_SHA: 'abcdefgh'
57
+ , GIT_TAG: 'v1.0.0'
58
+ , TEMPLATE_VALUE: '{type}.{version}'
59
+ , BOOLEAN_VALUE: true
60
+ , NULL_VALUE: null
61
+ }
62
+ }, 'expected template values')
63
+ }).catch(threw)