zx-bulk-release 2.2.0 → 2.2.2

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,3 +1,14 @@
1
+ ## [2.2.2](https://github.com/semrel-extra/zx-bulk-release/compare/v2.2.1...v2.2.2) (2023-03-24)
2
+
3
+ ### Fixes & improvements
4
+ * fix: fix gitPush retry ([268da72](https://github.com/semrel-extra/zx-bulk-release/commit/268da727036b0d3bf20b1ef10053440c5f56dad0))
5
+
6
+ ## [2.2.1](https://github.com/semrel-extra/zx-bulk-release/compare/v2.2.0...v2.2.1) (2023-03-23)
7
+
8
+ ### Fixes & improvements
9
+ * perf: make parallel internal `publish` tasks ([87f388e](https://github.com/semrel-extra/zx-bulk-release/commit/87f388ea2ff5cce96cca600903d80260f13f0908))
10
+ * refactor: improve internal reporter ([cc62df7](https://github.com/semrel-extra/zx-bulk-release/commit/cc62df762c0581db159ddd2fae8d90b085911884))
11
+
1
12
  ## [2.2.0](https://github.com/semrel-extra/zx-bulk-release/compare/v2.1.6...v2.2.0) (2023-03-23)
2
13
 
3
14
  ### Fixes & improvements
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zx-bulk-release",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "zx-based alternative for multi-semantic-release",
5
5
  "type": "module",
6
6
  "exports": {
@@ -2,7 +2,7 @@ import {semver} from 'zx-extra'
2
2
  import {updateDeps} from './deps.js'
3
3
  import {formatTag} from './meta.js';
4
4
  import {log} from './log.js'
5
- import {getCommits} from './repo.js'
5
+ import {getCommits} from './git.js'
6
6
 
7
7
  export const analyze = async (pkg, packages) => {
8
8
  const semanticChanges = await getSemanticChanges(pkg.absPath, pkg.latest.tag?.ref)
@@ -1,6 +1,6 @@
1
1
  import {$} from 'zx-extra'
2
2
  import {log} from './log.js'
3
- import {fetchRepo, getRepo, pushCommit} from './repo.js'
3
+ import {fetchRepo, getRepo, pushCommit} from './git.js'
4
4
  import {msgJoin} from './util.js'
5
5
  import {formatTag} from './meta.js'
6
6
 
@@ -1,23 +1,9 @@
1
- import {fs, path, $} from 'zx-extra'
2
- import {formatTag, pushTag} from './meta.js'
3
- import {pushCommit, getRepo} from './repo.js'
4
- import {npmPublish} from './npm.js'
5
- import {pushChangelog, formatReleaseNotes} from './changelog.js'
6
- import {msgJoin} from './util.js'
7
- import {runCmd} from './processor.js'
8
1
  import {log} from './log.js'
9
- import {pushMeta} from './meta.js'
10
-
11
- export const publish = async (pkg, run = runCmd) => {
12
- await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
13
- await pushTag(pkg)
14
- await pushMeta(pkg)
15
- await pushChangelog(pkg)
16
- await npmPublish(pkg)
17
- await ghRelease(pkg)
18
- await ghPages(pkg)
19
- await run(pkg, 'publishCmd')
20
- }
2
+ import {getRepo, pushCommit} from './git.js'
3
+ import {formatTag} from './meta.js'
4
+ import {formatReleaseNotes} from './changelog.js'
5
+ import {$, path} from 'zx-extra'
6
+ import {msgJoin} from './util.js'
21
7
 
22
8
  export const ghRelease = async (pkg) => {
23
9
  log({pkg})(`create gh release`)
@@ -35,10 +21,10 @@ export const ghRelease = async (pkg) => {
35
21
  body: releaseNotes
36
22
  })
37
23
 
38
- await $.o({cwd})`curl -H "Authorization: token ${ghToken}" -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
24
+ await $.o({cwd})`curl -H 'Authorization: token ${ghToken}' -H 'Accept: application/vnd.github.v3+json' https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
39
25
  }
40
26
 
41
- const ghPages = async (pkg) => {
27
+ export const ghPages = async (pkg) => {
42
28
  const {config: {ghPages: opts, gitCommitterEmail, gitCommitterName, ghBasicAuth: basicAuth}} = pkg
43
29
  if (!opts) return
44
30
 
@@ -30,7 +30,6 @@ export const pushCommit = async ({cwd, from, to, branch, origin, msg, ignoreFile
30
30
 
31
31
  while (retries > 0) {
32
32
  try {
33
-
34
33
  _cwd = await fetchRepo({cwd, branch, origin, basicAuth})
35
34
 
36
35
  for (let {relpath, contents} of files) {
@@ -54,8 +53,12 @@ export const pushCommit = async ({cwd, from, to, branch, origin, msg, ignoreFile
54
53
  return await $.raw`git push origin HEAD:refs/heads/${branch}`
55
54
  } catch (e) {
56
55
  retries -= 1
56
+ log({level: 'error'})('git push failed', 'branch', branch, 'retries left', retries, e)
57
57
  branches[keyByValue(branches, _cwd)] = null
58
- console.warn('git push failed', 'retries left', retries, e)
58
+
59
+ if (retries === 0) {
60
+ throw e
61
+ }
59
62
  }
60
63
  }
61
64
  })
@@ -65,6 +68,7 @@ export const getRepo = async (_cwd, {basicAuth} = {}) => {
65
68
  const cwd = await getRoot(_cwd)
66
69
  if (repos[cwd]) return repos[cwd]
67
70
 
71
+ console.log('!!! has basic auth', !!basicAuth)
68
72
  const originUrl = await getOrigin(cwd)
69
73
  const [, , repoHost, repoName] = originUrl.replace(':', '/').replace(/\.git/, '').match(/.+(@|\/\/)([^/]+)\/(.+)$/) || []
70
74
  const repoPublicUrl = `https://${repoHost}/${repoName}`
@@ -109,3 +113,8 @@ export const getCommits = async (cwd, from, to = 'HEAD') => ctx(async ($) => {
109
113
  return {subj, body, short, hash}
110
114
  })
111
115
  })
116
+
117
+ export const getTags = async (cwd, ref = '') =>
118
+ (await $.o({cwd})`git tag -l ${ref}`)
119
+ .toString()
120
+ .split('\n')
@@ -1,9 +1,69 @@
1
- import {$} from 'zx-extra'
1
+ import {$, fs} from 'zx-extra'
2
+ import {get, set, tpl} from './util.js'
2
3
 
3
4
  export const log = (ctx) => {
4
- if ($.state) {
5
- return $.state.log(ctx)
5
+ if ($.report) {
6
+ return $.report.log(ctx)
6
7
  }
7
8
 
8
9
  return console.log
9
10
  }
11
+
12
+ export const createReport = ({logger = console, file = null} = {}) => ({
13
+ logger,
14
+ file,
15
+ status: 'initial',
16
+ queue: [],
17
+ packages: {},
18
+ events: [],
19
+ setPackages(packages) {
20
+ this.packages = Object.entries(packages).reduce((acc, [name, {manifest: {version}, absPath, relPath}]) => {
21
+ acc[name] = {
22
+ status: 'initial',
23
+ name,
24
+ version,
25
+ path: absPath,
26
+ relPath
27
+ }
28
+ return acc
29
+ }, {})
30
+ return this
31
+ },
32
+ get(key, pkgName) {
33
+ return get(
34
+ pkgName ? this.packages[pkgName] : this,
35
+ key
36
+ )
37
+ },
38
+ set(key, value, pkgName) {
39
+ set(
40
+ pkgName ? this.packages[pkgName] : this,
41
+ key,
42
+ value
43
+ )
44
+ return this
45
+ },
46
+ setStatus(status, name) {
47
+ this.set('status', status, name)
48
+ this.save()
49
+ return this
50
+ },
51
+ getStatus(status, name) {
52
+ return this.get('status', name)
53
+ },
54
+ log(ctx = {}) {
55
+ return function (...chunks) {
56
+ const {pkg, scope = pkg?.name || $.scope || '~', level = 'info'} = ctx
57
+ const msg = chunks.map(c => typeof c === 'string' ? tpl(c, ctx) : c)
58
+ const event = {msg, scope, date: Date.now(), level}
59
+ this.events.push(event)
60
+ logger[level](`[${scope}]`, ...msg)
61
+
62
+ return this
63
+ }.bind(this)
64
+ },
65
+ save() {
66
+ this.file && fs.outputJsonSync(this.file, this)
67
+ return this
68
+ }
69
+ })
@@ -3,7 +3,7 @@
3
3
  import {ctx, semver, $, fs, path} from 'zx-extra'
4
4
  import {Buffer} from 'buffer'
5
5
  import {log} from './log.js'
6
- import {fetchRepo, pushCommit} from './repo.js'
6
+ import {fetchRepo, pushCommit, getTags as getGitTags} from './git.js'
7
7
  import {fetchManifest} from './npm.js'
8
8
 
9
9
  export const pushTag = (pkg) => ctx(async ($) => {
@@ -138,8 +138,7 @@ export const parseTag = (tag) => f0.parse(tag) || f1.parse(tag) || lerna.parse(t
138
138
  export const formatTag = (tag) => f0.format(tag) || f1.format(tag) || null
139
139
 
140
140
  export const getTags = async (cwd, ref = '') =>
141
- (await $.o({cwd})`git tag -l ${ref}`).toString()
142
- .split('\n')
141
+ (await getGitTags(cwd, ref))
143
142
  .map(tag => parseTag(tag.trim()))
144
143
  .filter(Boolean)
145
144
  .sort((a, b) => semver.rcompare(a.version, b.version))
@@ -2,35 +2,35 @@ import os from 'node:os'
2
2
  import {$, fs, within} from 'zx-extra'
3
3
  import {queuefy} from 'queuefy'
4
4
  import {analyze} from './analyze.js'
5
- import {build} from './build.js'
5
+ import {pushChangelog} from './changelog.js'
6
6
  import {getPkgConfig} from './config.js'
7
- import {topo, traverseQueue} from './deps.js'
8
- import {log} from './log.js'
9
- import {getLatest} from './meta.js'
10
- import {publish} from './publish.js'
11
- import {getRoot, getSha} from './repo.js'
12
- import {get, memoizeBy, set, tpl} from './util.js'
7
+ import {topo, traverseDeps, traverseQueue} from './deps.js'
8
+ import {ghPages, ghRelease} from './gh.js'
9
+ import {getRoot, getSha} from './git.js'
10
+ import {log, createReport} from './log.js'
11
+ import {getLatest, pushMeta, pushTag} from './meta.js'
12
+ import {fetchPkg, npmPublish} from './npm.js'
13
+ import {memoizeBy, tpl} from './util.js'
13
14
 
14
15
  export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within(async () => {
15
- const {state, build, publish} = createContext(flags, env)
16
+ const {report, build, publish} = createContext(flags, env)
16
17
 
17
- log()('zx-bulk-release')
18
+ report.log()('zx-bulk-release')
18
19
 
19
20
  try {
20
- const {packages, queue, root, sources, next, prev, nodes, graphs, edges} = await topo({cwd, flags})
21
- log()('queue:', queue)
22
- log()('graphs', graphs)
23
-
24
- state
25
- .setQueue(queue)
21
+ const {packages, queue, root, prev, graphs} = await topo({cwd, flags})
22
+ report
23
+ .log()('queue:', queue)
24
+ .log()('graphs', graphs)
25
+ .set('queue', queue)
26
26
  .setPackages(packages)
27
27
 
28
28
  await traverseQueue({queue, prev, async cb(name) {
29
- state.setStatus('analyzing', name)
29
+ report.setStatus('analyzing', name)
30
30
  const pkg = packages[name]
31
31
  await contextify(pkg, packages, root)
32
32
  await analyze(pkg, packages)
33
- state
33
+ report
34
34
  .set('config', pkg.config, name)
35
35
  .set('version', pkg.version, name)
36
36
  .set('prevVersion', pkg.latest.tag?.version || pkg.manifest.version, name)
@@ -38,38 +38,39 @@ export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within
38
38
  .set('tag', pkg.tag, name)
39
39
  }})
40
40
 
41
- state.setStatus('pending')
41
+ report.setStatus('pending')
42
42
 
43
43
  await traverseQueue({queue, prev, async cb(name) {
44
44
  const pkg = packages[name]
45
45
 
46
46
  if (!pkg.releaseType) {
47
- state.setStatus('skipped', name)
47
+ report.setStatus('skipped', name)
48
48
  return
49
49
  }
50
50
 
51
- state.setStatus('building', name)
51
+ report.setStatus('building', name)
52
52
  await build(pkg, packages)
53
53
 
54
54
  if (flags.dryRun) {
55
- state.setStatus('success', name)
55
+ report.setStatus('success', name)
56
56
  return
57
57
  }
58
58
 
59
- state.setStatus('publishing', name)
59
+ report.setStatus('publishing', name)
60
60
  await publish(pkg)
61
61
 
62
- state.setStatus('success', name)
62
+ report.setStatus('success', name)
63
63
  }})
64
64
  } catch (e) {
65
- log({level: 'error'})(e)
66
- state
65
+ report
66
+ .log({level: 'error'})(e)
67
67
  .set('error', e)
68
68
  .setStatus('failure')
69
69
  throw e
70
70
  }
71
- state.setStatus('success')
72
- log()('Great success!')
71
+ report
72
+ .setStatus('success')
73
+ .log()('Great success!')
73
74
  })
74
75
 
75
76
  export const runCmd = async (pkg, name) => {
@@ -82,17 +83,17 @@ export const runCmd = async (pkg, name) => {
82
83
  }
83
84
 
84
85
  const createContext = (flags, env) => {
85
- const state = createState({file: flags.report})
86
+ const report = createReport({file: flags.report})
86
87
  const _runCmd = queuefy(runCmd, flags.concurrency || os.cpus().length)
87
88
  const _build = memoizeBy((pkg, packages) => build(pkg, packages, _runCmd, _build))
88
89
  const _publish = memoizeBy((pkg) => publish(pkg, _runCmd))
89
90
 
90
- $.state = state
91
+ $.report = report
91
92
  $.env = {...process.env, ...env}
92
93
  $.verbose = !!(flags.debug || $.env.DEBUG ) || $.verbose
93
94
 
94
95
  return {
95
- state,
96
+ report,
96
97
  runCmd: _runCmd,
97
98
  build: _build,
98
99
  publish: _publish
@@ -100,7 +101,7 @@ const createContext = (flags, env) => {
100
101
  }
101
102
 
102
103
  // Inspired by https://docs.github.com/en/actions/learn-github-actions/contexts
103
- export const contextify = async (pkg, packages, root) => {
104
+ const contextify = async (pkg, packages, root) => {
104
105
  pkg.config = await getPkgConfig(pkg.absPath, root.absPath)
105
106
  pkg.latest = await getLatest(pkg)
106
107
  pkg.context = {
@@ -113,63 +114,36 @@ export const contextify = async (pkg, packages, root) => {
113
114
  }
114
115
  }
115
116
 
116
- export const createState = ({logger = console, file = null} = {}) => ({
117
- logger,
118
- file,
119
- status: 'initial',
120
- queue: [],
121
- packages: {},
122
- events: [],
123
- setQueue(queue) {
124
- this.queue = queue
125
- return this
126
- },
127
- setPackages(packages) {
128
- this.packages = Object.entries(packages).reduce((acc, [name, {manifest: {version}, absPath, relPath}]) => {
129
- acc[name] = {
130
- status: 'initial',
131
- name,
132
- version,
133
- path: absPath,
134
- relPath
135
- }
136
- return acc
137
- }, {})
138
- return this
139
- },
140
- get(key, pkgName) {
141
- return get(
142
- pkgName ? this.packages[pkgName] : this,
143
- key
144
- )
145
- },
146
- set(key, value, pkgName) {
147
- set(
148
- pkgName ? this.packages[pkgName] : this,
149
- key,
150
- value
151
- )
152
- return this
153
- },
154
- setStatus(status, name) {
155
- this.set('status', status, name)
156
- this.save()
157
- return this
158
- },
159
- getStatus(status, name) {
160
- return this.get('status', name)
161
- },
162
- log(ctx = {}) {
163
- return function (...chunks) {
164
- const {pkg, scope = pkg?.name || '~', level = 'info'} = ctx
165
- const msg = chunks.map(c => typeof c === 'string' ? tpl(c, ctx) : c)
166
- const event = {msg, scope, date: Date.now(), level}
167
- this.events.push(event)
168
- logger[level](`[${scope}]`, ...msg)
169
- }.bind(this)
170
- },
171
- save() {
172
- this.file && fs.outputJsonSync(this.file, this)
173
- return this
117
+ const build = async (pkg, packages, run = runCmd, self = build) => within(async () => {
118
+ $.scope = pkg.name
119
+ if (pkg.built) return
120
+
121
+ await Promise.all([
122
+ traverseDeps(pkg, packages, async (_, {pkg}) => self(pkg, packages, run, self)),
123
+ pkg.changes.length === 0 && pkg.config.npmFetch
124
+ ? fetchPkg(pkg)
125
+ : Promise.resolve()
126
+ ])
127
+
128
+ if (!pkg.fetched) {
129
+ await run(pkg, 'buildCmd')
130
+ await run(pkg, 'testCmd')
174
131
  }
132
+
133
+ pkg.built = true
134
+ })
135
+
136
+ const publish = async (pkg, run = runCmd) => within(async () => {
137
+ $.scope = pkg.name
138
+ await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
139
+ await pushTag(pkg)
140
+
141
+ await Promise.all([
142
+ pushMeta(pkg),
143
+ pushChangelog(pkg),
144
+ npmPublish(pkg),
145
+ ghRelease(pkg),
146
+ ghPages(pkg),
147
+ run(pkg, 'publishCmd')
148
+ ])
175
149
  })
@@ -1,21 +0,0 @@
1
- import {traverseDeps} from './deps.js'
2
- import {fetchPkg} from './npm.js'
3
- import {runCmd} from './processor.js'
4
-
5
- export const build = async (pkg, packages, run = runCmd, self = build) => {
6
- if (pkg.built) return true
7
-
8
- await traverseDeps(pkg, packages, async (_, {pkg}) => self(pkg, packages, run, self))
9
-
10
- if (pkg.changes.length === 0 && pkg.config.npmFetch) {
11
- await fetchPkg(pkg)
12
- }
13
-
14
- if (!pkg.fetched) {
15
- await run(pkg, 'buildCmd')
16
- await run(pkg, 'testCmd')
17
- }
18
-
19
- pkg.built = true
20
- return true
21
- }