zx-bulk-release 1.22.3 → 1.23.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.
@@ -101,7 +101,7 @@ jobs:
101
101
  run: yarn docs
102
102
 
103
103
  - name: Codeclimate
104
- uses: paambaati/codeclimate-action@v3.0.0
104
+ uses: paambaati/codeclimate-action@v3.1.0
105
105
  env:
106
106
  CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
107
107
  with:
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [1.23.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.22.3...v1.23.0) (2022-10-14)
2
+
3
+ ### Features
4
+ * feat: introduce reports ([443102c](https://github.com/semrel-extra/zx-bulk-release/commit/443102c6395adabef63e433ec1136d30dbe8fff4))
5
+
6
+ ### Fixes & improvements
7
+ * refactor: introduce internal logger ([ede8d6d](https://github.com/semrel-extra/zx-bulk-release/commit/ede8d6df3612654f3d6e9d5997f1b579c8149f2b))
8
+ * perf: up depx ([372bab2](https://github.com/semrel-extra/zx-bulk-release/commit/372bab2639d8feabaf1153e2cb0576838edb556f))
9
+
1
10
  ## [1.22.3](https://github.com/semrel-extra/zx-bulk-release/compare/v1.22.2...v1.22.3) (2022-08-25)
2
11
 
3
12
  ### Fixes & improvements
package/README.md CHANGED
@@ -203,6 +203,7 @@ Note, [npm-package-name charset](https://www.npmjs.com/package/validate-npm-pack
203
203
  ```
204
204
 
205
205
  ### env vars
206
+
206
207
  ```js
207
208
  export const parseEnv = (env = process.env) => {
208
209
  const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, NPM_TOKEN, NPM_REGISTRY, NPMRC, NPM_USERCONFIG, NPM_CONFIG_USERCONFIG, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env
@@ -222,7 +223,7 @@ export const parseEnv = (env = process.env) => {
222
223
 
223
224
  ### Meta
224
225
 
225
- Each release projects its result into the `meta` branch.
226
+ Each release stores its result into the `meta` branch.
226
227
  `2022-6-26-semrel-extra-zxbr-test-c-1-3-1-f0.json`
227
228
  ```json
228
229
  {
@@ -237,6 +238,29 @@ Each release projects its result into the `meta` branch.
237
238
  }
238
239
  ```
239
240
 
241
+ ### Report
242
+
243
+ Release process state is reported to the console and `release-report.json` if `report` flag set to `true`.
244
+ ```json
245
+ {
246
+ status: 'success' | 'failure' | 'pending',
247
+ error: string | null
248
+ queue: ['a', 'b', 'c', 'd'],
249
+ packages: [
250
+ name: string,
251
+ version: string,
252
+ path: string
253
+ config: {},
254
+ changes: [],
255
+ tag: string | null,
256
+ version: string,
257
+ releaseType: string | null,
258
+ prevVersion: string | null,
259
+ }
260
+ }]
261
+ }
262
+ ```
263
+
240
264
  ## References
241
265
  * [semrel-extra/zx-semrel](https://github.com/semrel-extra/zx-semrel)
242
266
  * [dhoulb/multi-semantic-release](https://github.com/dhoulb/multi-semantic-release)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zx-bulk-release",
3
- "version": "1.22.3",
3
+ "version": "1.23.0",
4
4
  "description": "zx-based alternative for multi-semantic-release",
5
5
  "type": "module",
6
6
  "exports": "./src/main/js/index.js",
@@ -13,12 +13,12 @@
13
13
  "dependencies": {
14
14
  "@semrel-extra/topo": "^1.4.3",
15
15
  "cosmiconfig": "^7.0.1",
16
- "zx-extra": "^2.5.1"
16
+ "zx-extra": "^2.5.2"
17
17
  },
18
18
  "devDependencies": {
19
19
  "c8": "^7.12.0",
20
20
  "uvu": "^0.5.6",
21
- "verdaccio": "^5.14.0"
21
+ "verdaccio": "^5.15.4"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public"
@@ -1,16 +1,19 @@
1
1
  import {ctx, semver} from 'zx-extra'
2
2
  import {updateDeps} from './deps.js'
3
+ import {log} from './util.js'
3
4
 
4
5
  export const analyze = async (pkg, packages) => {
5
6
  const semanticChanges = await getSemanticChanges(pkg.absPath, pkg.latest.tag?.ref)
6
7
  const depsChanges = await updateDeps(pkg, packages)
7
8
  const changes = [...semanticChanges, ...depsChanges]
9
+ const releaseType = getNextReleaseType(changes)
8
10
 
9
11
  pkg.changes = changes
10
- pkg.version = resolvePkgVersion(changes, pkg.latest.tag?.version || pkg.manifest.version)
12
+ pkg.releaseType = releaseType
13
+ pkg.version = resolvePkgVersion(releaseType, pkg.latest.tag?.version || pkg.manifest.version)
11
14
  pkg.manifest.version = pkg.version
12
15
 
13
- console.log(`[${pkg.name}] semantic changes`, changes)
16
+ log({pkg})('semantic changes', changes)
14
17
  }
15
18
 
16
19
  export const releaseSeverityOrder = ['major', 'minor', 'patch']
@@ -61,17 +64,17 @@ export const getSemanticChanges = async (cwd, since) => {
61
64
  return semanticChanges
62
65
  }
63
66
 
64
- export const getNextReleaseType = (changes) => releaseSeverityOrder.find(type => changes.find(({releaseType}) => type === releaseType))
67
+ export const getNextReleaseType = (changes) => changes.length
68
+ ? releaseSeverityOrder.find(type => changes.find(({releaseType}) => type === releaseType))
69
+ : null
65
70
 
66
- export const getNextVersion = (changes, prevVersion) => {
71
+ export const getNextVersion = (releaseType, prevVersion) => {
67
72
  if (!prevVersion) return '1.0.0'
68
73
 
69
- const releaseType = getNextReleaseType(changes)
70
-
71
74
  return semver.inc(prevVersion, releaseType)
72
75
  }
73
76
 
74
- export const resolvePkgVersion = (changes, prevVersion) =>
75
- changes.length > 0
76
- ? getNextVersion(changes, prevVersion)
77
+ export const resolvePkgVersion = (releaseType, prevVersion) =>
78
+ releaseType
79
+ ? getNextVersion(releaseType, prevVersion)
77
80
  : prevVersion || null
@@ -12,7 +12,7 @@ export const build = async (pkg, packages) => {
12
12
  if (pkg.changes.length === 0 && config.npmFetch) await fetchPkg(pkg)
13
13
 
14
14
  if (!pkg.fetched && config.buildCmd) {
15
- await await runHook(pkg, 'buildCmd')
15
+ await runHook(pkg, 'buildCmd')
16
16
  }
17
17
 
18
18
  pkg.built = true
@@ -1,36 +1,60 @@
1
+ import {within, $} from 'zx-extra'
1
2
  import {analyze} from './analyze.js'
2
3
  import {publish} from './publish.js'
3
4
  import {build} from './build.js'
4
5
  import {contextify} from './contextify.js'
5
6
  import {topo} from './topo.js'
6
- import {within, $} from 'zx-extra'
7
+ import {createReporter, log} from './util.js';
7
8
 
8
9
  export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within(async () => {
9
- console.log('zx-bulk-release')
10
+ const reporter = $.r = createReporter(flags.report)
10
11
  $.env = {...process.env, ...env}
11
12
  $.verbose = !!(flags.debug || $.env.DEBUG ) || $.verbose
13
+ log()('zx-bulk-release')
12
14
 
13
15
  try {
14
16
  const {packages, queue, root} = await topo({cwd, flags})
15
- console.log('queue:', queue)
17
+ log()('queue:', queue)
18
+
19
+ reporter.setQueue(queue, packages)
20
+ reporter.setStatus('pending')
16
21
 
17
22
  for (let name of queue) {
18
23
  const pkg = packages[name]
19
24
 
25
+ reporter.setStatus('analyzing', name)
20
26
  await contextify(pkg, packages, root)
21
27
  await analyze(pkg, packages)
28
+ reporter.setState('config', pkg.config, name)
29
+ reporter.setState('version', pkg.version, name)
30
+ reporter.setState('prevVersion', pkg.latest.tag?.version || pkg.manifest.version, name)
31
+ reporter.setState('releaseType', pkg.releaseType, name)
22
32
 
23
- if (pkg.changes.length === 0) continue
33
+ if (!pkg.releaseType) {
34
+ reporter.setStatus('skipped', name)
35
+ continue
36
+ }
24
37
 
38
+ reporter.setStatus('building', name)
25
39
  await build(pkg, packages)
40
+ reporter.setStatus('')
26
41
 
27
- if (flags.dryRun) continue
42
+ if (flags.dryRun) {
43
+ reporter.setStatus('success', name)
44
+ continue
45
+ }
28
46
 
47
+ reporter.setStatus('publishing', name)
29
48
  await publish(pkg)
49
+
50
+ reporter.setStatus('success', name)
30
51
  }
31
52
  } catch (e) {
32
- console.error(e)
53
+ log({level: 'error'})(e)
54
+ reporter.setState('error', e)
55
+ reporter.setStatus('failure')
33
56
  throw e
34
57
  }
35
- console.log('Great success!')
58
+ reporter.setStatus('success')
59
+ log()('Great success!')
36
60
  })
@@ -1,4 +1,5 @@
1
1
  import {parseEnv} from './config.js'
2
+ import {log} from './util.js'
2
3
  import {$, ctx, fs, path, tempy, copy, INI, fetch} from 'zx-extra'
3
4
 
4
5
  export const fetchPkg = async (pkg, {env = $.env} = {}) => {
@@ -13,9 +14,9 @@ export const fetchPkg = async (pkg, {env = $.env} = {}) => {
13
14
  await copy({from: ['**/*', '!package.json'], to: cwd, baseFrom: `${temp}/package`})
14
15
 
15
16
  pkg.fetched = true
16
- console.log(`[${pkg.name}] fetched '${pkg.name}@${pkg.version}'`)
17
+ log({pkg})(`fetched '${pkg.name}@${pkg.version}'`)
17
18
  } catch (e) {
18
- console.log(`[${pkg.name}] fetching '${pkg.name}@${pkg.version}' failed`, e)
19
+ log({pkg})(`fetching '${pkg.name}@${pkg.version}' failed`, e)
19
20
  }
20
21
  }
21
22
 
@@ -41,7 +42,7 @@ export const npmPublish = (pkg) => ctx(async ($) => {
41
42
  const {npmRegistry, npmToken, npmConfig} = parseEnv($.env)
42
43
  const npmrc = npmConfig ? npmConfig : path.resolve(cwd, '.npmrc')
43
44
 
44
- console.log(`[${name}] publish npm package ${name} ${version} to ${npmRegistry}`)
45
+ log({pkg})(`publish npm package ${name} ${version} to ${npmRegistry}`)
45
46
  $.cwd = cwd
46
47
  if (!npmConfig) {
47
48
  await $.raw`echo ${npmRegistry.replace(/https?:/, '')}/:_authToken=${npmToken} >> ${npmrc}`
@@ -3,7 +3,7 @@ import {fs, path, $} from 'zx-extra'
3
3
  import {push, fetch, parseRepo} from './repo.js'
4
4
  import {parseEnv} from './config.js'
5
5
  import {fetchManifest, npmPublish} from './npm.js'
6
- import {restJoin, runHook} from './util.js'
6
+ import {restJoin, runHook, log} from './util.js'
7
7
 
8
8
  export const publish = async (pkg) => {
9
9
  await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
@@ -17,7 +17,7 @@ export const publish = async (pkg) => {
17
17
  }
18
18
 
19
19
  export const pushMeta = async (pkg) => {
20
- console.log(`[${pkg.name}] push artifact to branch 'meta'`)
20
+ log({pkg})('push artifact to branch \'meta\'')
21
21
 
22
22
  const {name, version, absPath: cwd} = pkg
23
23
  const tag = formatTag({name, version})
@@ -41,7 +41,7 @@ export const pushMeta = async (pkg) => {
41
41
  }
42
42
 
43
43
  export const ghRelease = async (pkg) => {
44
- console.log(`[${pkg.name}] create gh release`)
44
+ log({pkg})(`create gh release`)
45
45
 
46
46
  const {ghToken} = parseEnv($.env)
47
47
  if (!ghToken) return null
@@ -63,7 +63,7 @@ const pushChangelog = async (pkg) => {
63
63
  const {config: {changelog: opts}} = pkg
64
64
  if (!opts) return
65
65
 
66
- console.log(`[${pkg.name}] push changelog`)
66
+ log({pkg})('push changelog')
67
67
  const [branch = 'changelog', file = `${pkg.name.replace(/[^a-z0-9-]/ig, '')}-changelog.md`, ..._msg] = typeof opts === 'string'
68
68
  ? opts.split(' ')
69
69
  : [opts.branch, opts.file, opts.msg]
@@ -105,7 +105,7 @@ const ghPages = async (pkg) => {
105
105
  : [opts.branch, opts.from, opts.to, opts.msg]
106
106
  const msg = restJoin(_msg, pkg, 'docs: update docs ${{name}} ${{version}}')
107
107
 
108
- console.log(`[${pkg.name}] publish docs to ${branch}`)
108
+ log({pkg})(`publish docs to ${branch}`)
109
109
 
110
110
  await push({
111
111
  cwd: path.join(pkg.absPath, from),
@@ -1,5 +1,6 @@
1
1
  import {$, ctx, fs, path, tempy, copy} from 'zx-extra'
2
2
  import {parseEnv} from './config.js'
3
+ import {log} from './util.js'
3
4
 
4
5
  const branches = {}
5
6
  export const fetch = async ({cwd: _cwd, branch, origin: _origin}) => ctx(async ($) => {
@@ -14,7 +15,7 @@ export const fetch = async ({cwd: _cwd, branch, origin: _origin}) => ctx(async (
14
15
  try {
15
16
  await $`git clone --single-branch --branch ${branch} --depth 1 ${origin} .`
16
17
  } catch (e) {
17
- console.warn(`ref '${branch}' does not exist in ${origin}`)
18
+ log({level: 'warn'})(`ref '${branch}' does not exist in ${origin}`)
18
19
  await $`git init .`
19
20
  await $`git remote add origin ${origin}`
20
21
  }
@@ -41,7 +42,7 @@ export const push = async ({cwd, from, to, branch, origin, msg, ignoreFiles, fil
41
42
  try {
42
43
  await $`git commit -m ${msg}`
43
44
  } catch {
44
- console.warn(`no changes to commit to ${branch}`)
45
+ log({level: 'warn'})(`no changes to commit to ${branch}`)
45
46
  return
46
47
  }
47
48
 
@@ -3,6 +3,7 @@
3
3
  import {ctx, semver, $} from 'zx-extra'
4
4
  import {Buffer} from 'buffer'
5
5
  import {parseEnv} from './config.js'
6
+ import {log} from './util.js'
6
7
 
7
8
  export const pushTag = (pkg) => ctx(async ($) => {
8
9
  const {absPath: cwd, name, version} = pkg
@@ -10,7 +11,7 @@ export const pushTag = (pkg) => ctx(async ($) => {
10
11
  const {gitCommitterEmail, gitCommitterName} = parseEnv($.env)
11
12
 
12
13
  pkg.context.git.tag = tag
13
- console.log(`[${name}] push release tag ${tag}`)
14
+ log({pkg})(`push release tag ${tag}`)
14
15
 
15
16
  $.cwd = cwd
16
17
  await $`git config user.name ${gitCommitterName}`
@@ -1,4 +1,4 @@
1
- import {$} from 'zx-extra'
1
+ import {$, fs} from 'zx-extra'
2
2
 
3
3
  export const tpl = (str, context) =>
4
4
  str?.replace(/\$\{\{\s*([.a-z0-9]+)\s*}}/gi, (matched, key) => get(context, key) ?? '')
@@ -14,13 +14,88 @@ export const get = (obj, path = '.') => {
14
14
  return result
15
15
  }
16
16
 
17
+ export const set = (obj, path, value) => {
18
+ const chunks = path.split('.').filter(Boolean)
19
+ let result = obj
20
+
21
+ for (let i = 0, len = chunks.length; i < len && result !== undefined && result !== null; i++) {
22
+ if (i === len - 1) {
23
+ result[chunks[i]] = value
24
+ } else {
25
+ result[chunks[i]] = result[chunks[i]] || {}
26
+ result = result[chunks[i]]
27
+ }
28
+ }
29
+
30
+ return result
31
+ }
32
+
17
33
  export const runHook = async (pkg, name) => {
18
34
  const cmd = tpl(pkg.config[name], {...pkg, ...pkg.context})
19
35
 
20
36
  if (cmd) {
21
- console.log(`[${pkg.name}] run ${name} '${cmd}'`)
37
+ log({pkg})(`run ${name} '${cmd}'`)
22
38
  await $.o({cwd: pkg.absPath, quote: v => v})`${cmd}`
23
39
  }
24
40
  }
25
41
 
26
42
  export const restJoin = (rest, context, def) => tpl(rest.filter(Boolean).join(' ') || def, context)
43
+
44
+ export const createReporter = (file, logger = console) => {
45
+ const state = {
46
+ status: 'initial',
47
+ queue: [],
48
+ packages: [],
49
+ events: [],
50
+ }
51
+
52
+ return {
53
+ setQueue(queue, packages) {
54
+ state.queue = queue
55
+ state.packages = queue.map(name => {
56
+ const {manifest: {version}, absPath} = packages[name]
57
+ return {
58
+ status: 'initial',
59
+ name,
60
+ version,
61
+ path: absPath
62
+ }
63
+ })
64
+ },
65
+ getState(key, pkgName) {
66
+ const _state = pkgName ? state.packages.find(({name}) => name === pkgName) : state
67
+ return get(_state, key)
68
+ },
69
+ setState(key, value, pkgName) {
70
+ const _state = pkgName ? state.packages.find(({name}) => name === pkgName) : state
71
+ set(_state, key, value)
72
+ },
73
+ setStatus(status, name) {
74
+ this.setState('status', status, name)
75
+
76
+ this.persistState()
77
+ },
78
+ getStatus (status, name) {
79
+ return this.getState('status', name)
80
+ },
81
+ log(ctx = {}) { return (...chunks) => {
82
+ const {pkg, scope = pkg?.name || '~', level = 'info'} = ctx
83
+ const msg = chunks.map(c => typeof c === 'string' ? tpl(c, ctx) : c)
84
+ const event = {msg, scope, date: Date.now(), level}
85
+ state.events.push(event)
86
+ logger[level](`[${scope}]`, ...msg)
87
+ }},
88
+ persistState() {
89
+ file && fs.writeJsonSync(file, JSON.stringify(state))
90
+ }
91
+ }
92
+ }
93
+
94
+ export const log = (ctx) => {
95
+ if (!$.r) {
96
+ $.r = createReporter()
97
+ }
98
+
99
+ return $.r.log(ctx)
100
+ }
101
+
@@ -7,6 +7,7 @@ import {formatTag} from '../../main/js/tag.js'
7
7
  import {addCommits, createFakeRepo, createNpmRegistry, fixtures} from './test-utils.js'
8
8
 
9
9
  const test = suite('integration')
10
+ const report = tempy.temporaryFile()
10
11
 
11
12
  const cwd = await createFakeRepo({
12
13
  commits: [
@@ -162,7 +163,25 @@ $.cwd = cwd
162
163
  await $`yarn install`
163
164
 
164
165
  test('run() dry-run', async () => {
165
- await run({cwd, flags: {dryRun: true}})
166
+ await run({cwd, flags: {dryRun: true, report}})
167
+
168
+ const r = JSON.parse(await fs.readJson(report))
169
+ const [a, b] = r.packages
170
+
171
+ assert.equal(r.queue, ['a', 'b'])
172
+ assert.equal(r.status, 'success')
173
+
174
+ assert.equal(a.name, 'a')
175
+ assert.equal(a.version, '1.0.1')
176
+ assert.equal(a.prevVersion, 'v1.0.0')
177
+ assert.equal(a.releaseType, 'patch')
178
+ assert.equal(a.status, 'success')
179
+
180
+ assert.equal(b.name, 'b')
181
+ assert.equal(b.version, '1.0.0')
182
+ assert.equal(b.prevVersion, '1.0.0')
183
+ assert.equal(b.releaseType, 'minor')
184
+ assert.equal(b.status, 'success')
166
185
  })
167
186
 
168
187
  test('run()', async () => {
@@ -1,6 +1,6 @@
1
1
  import {suite} from 'uvu'
2
2
  import * as assert from 'uvu/assert'
3
- import {tpl} from '../../main/js/util.js'
3
+ import {tpl, get, set} from '../../main/js/util.js'
4
4
 
5
5
  const test = suite('util')
6
6
 
@@ -30,5 +30,16 @@ test('tpl()', () => {
30
30
  })
31
31
  })
32
32
 
33
+ test('set/get()', () => {
34
+ const obj = {arr: [{}]}
35
+
36
+ set(obj, 'foo.bar', 'baz')
37
+ set(obj, 'arr.0.name', 'name')
38
+ assert.equal(get(obj, 'foo.bar'), 'baz')
39
+ assert.equal(get(obj, 'arr.0.name'), 'name')
40
+ assert.equal(get(obj, '.'), obj)
41
+ assert.equal(get(obj), obj)
42
+ })
43
+
33
44
  test.run()
34
45