zx-bulk-release 2.2.6 → 2.2.8
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 +11 -0
- package/README.md +12 -7
- package/package.json +1 -1
- package/src/main/js/analyze.js +1 -1
- package/src/main/js/config.js +7 -10
- package/src/main/js/git.js +25 -24
- package/src/main/js/log.js +18 -24
- package/src/main/js/processor.js +39 -41
- package/src/main/js/util.js +0 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## [2.2.8](https://github.com/semrel-extra/zx-bulk-release/compare/v2.2.7...v2.2.8) (2023-03-24)
|
|
2
|
+
|
|
3
|
+
### Fixes & improvements
|
|
4
|
+
* fix: fix git-push-rebase-retry hook ([30df2f1](https://github.com/semrel-extra/zx-bulk-release/commit/30df2f1d25d43145343e47f2f96d4a00955d6b76))
|
|
5
|
+
|
|
6
|
+
## [2.2.7](https://github.com/semrel-extra/zx-bulk-release/compare/v2.2.6...v2.2.7) (2023-03-24)
|
|
7
|
+
|
|
8
|
+
### Fixes & improvements
|
|
9
|
+
* docs: describe more CLI flags ([6f7a053](https://github.com/semrel-extra/zx-bulk-release/commit/6f7a053ae2fe2da8fd71858b132ef4ac4d1456ad))
|
|
10
|
+
* refactor: move `topo` to ctx builder ([6fec83c](https://github.com/semrel-extra/zx-bulk-release/commit/6fec83c10dd76feb93a3148eb2c032c6eaa902f1))
|
|
11
|
+
|
|
1
12
|
## [2.2.6](https://github.com/semrel-extra/zx-bulk-release/compare/v2.2.5...v2.2.6) (2023-03-24)
|
|
2
13
|
|
|
3
14
|
### Fixes & improvements
|
package/README.md
CHANGED
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
* No extra builds. The required deps are fetched from the pkg registry (`npmFetch` config opt).
|
|
19
19
|
|
|
20
20
|
## Roadmap
|
|
21
|
+
* [x] Store release metrics to `meta`.
|
|
22
|
+
* [ ] ~~Self-repair. Restore broken/missing metadata from external registries (npm, pypi, m2)~~. Tags should be the only source of truth
|
|
21
23
|
* [ ] Multistack. Add support for java/kt/py.
|
|
22
|
-
* [ ] Self-repair. Restore broken/missing metadata from external registries (npm, pypi, m2).
|
|
23
24
|
* [ ] Semaphore. Let several release agents to serve the monorepo at the same time.
|
|
24
|
-
* [ ] Stats. Store release metrics to `meta`.
|
|
25
25
|
|
|
26
26
|
## Requirements
|
|
27
27
|
* macOS / linux
|
|
@@ -37,11 +37,16 @@ yarn add zx-bulk-release
|
|
|
37
37
|
```shell
|
|
38
38
|
GH_TOKEN=ghtoken GH_USER=username NPM_TOKEN=npmtoken npx zx-bulk-release [opts]
|
|
39
39
|
```
|
|
40
|
-
| Flag
|
|
41
|
-
|
|
42
|
-
| `--
|
|
43
|
-
| `--
|
|
44
|
-
| `--
|
|
40
|
+
| Flag | Description | Default |
|
|
41
|
+
|------------------------------|----------------------------------------------------------------|------------------|
|
|
42
|
+
| `--ignore` | Packages to ignore: `a, b` | |
|
|
43
|
+
| `--include-private` | Include `private` packages | `false` |
|
|
44
|
+
| `--concurrency` | `build/publish` threads limit | `os.cpus.length` |
|
|
45
|
+
| `--no-build` | Skip `buildCmd` invoke | |
|
|
46
|
+
| `--no-npm-fetch` | Disable npm artifacts fetching | |
|
|
47
|
+
| `--dry-run` / `--no-publish` | Disable any publish logic | |
|
|
48
|
+
| `--report` | Persist release state to file | |
|
|
49
|
+
| `--debug` | Enable [zx](https://github.com/google/zx#verbose) verbose mode | |
|
|
45
50
|
|
|
46
51
|
### JS API
|
|
47
52
|
```js
|
package/package.json
CHANGED
package/src/main/js/analyze.js
CHANGED
|
@@ -6,7 +6,7 @@ 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)
|
|
9
|
-
const depsChanges = await updateDeps(pkg, packages)
|
|
9
|
+
const depsChanges = await updateDeps(pkg, pkg.context.packages)
|
|
10
10
|
const changes = [...semanticChanges, ...depsChanges]
|
|
11
11
|
const releaseType = getNextReleaseType(changes)
|
|
12
12
|
|
package/src/main/js/config.js
CHANGED
|
@@ -15,10 +15,10 @@ const CONFIG_FILES = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
export const defaultConfig = {
|
|
18
|
-
cmd:
|
|
19
|
-
changelog:
|
|
20
|
-
npmFetch:
|
|
21
|
-
ghRelease:
|
|
18
|
+
cmd: 'yarn && yarn build && yarn test',
|
|
19
|
+
changelog: 'changelog',
|
|
20
|
+
npmFetch: true,
|
|
21
|
+
ghRelease: true,
|
|
22
22
|
// npmPublish: true,
|
|
23
23
|
// ghPages: 'gh-pages'
|
|
24
24
|
}
|
|
@@ -38,10 +38,8 @@ export const normalizePkgConfig = (config, env) => ({
|
|
|
38
38
|
}
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
export const parseEnv = (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return {
|
|
41
|
+
export const parseEnv = ({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} = process.env) =>
|
|
42
|
+
({
|
|
45
43
|
ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME,
|
|
46
44
|
ghToken: GH_TOKEN || GITHUB_TOKEN,
|
|
47
45
|
npmConfig: NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
|
|
@@ -49,7 +47,6 @@ export const parseEnv = (env = process.env) => {
|
|
|
49
47
|
npmRegistry: NPM_REGISTRY || 'https://registry.npmjs.org',
|
|
50
48
|
gitCommitterName: GIT_COMMITTER_NAME || 'Semrel Extra Bot',
|
|
51
49
|
gitCommitterEmail: GIT_COMMITTER_EMAIL || 'semrel-extra-bot@hotmail.com',
|
|
52
|
-
}
|
|
53
|
-
}
|
|
50
|
+
})
|
|
54
51
|
|
|
55
52
|
export const normalizeFlags = (flags = {}) => Object.entries(flags).reduce((acc, [k, v]) => ({...acc, [camelize(k)]: v}), {})
|
package/src/main/js/git.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {$, ctx, fs, path, tempy, copy} from 'zx-extra'
|
|
2
2
|
import {log} from './log.js'
|
|
3
|
-
import {
|
|
3
|
+
import {memoizeBy} from './util.js'
|
|
4
|
+
import {queuefy} from 'queuefy'
|
|
4
5
|
|
|
5
6
|
export const fetchRepo = memoizeBy(async ({cwd: _cwd, branch, origin: _origin, basicAuth}) => ctx(async ($) => {
|
|
6
7
|
const origin = _origin || (await getRepo(_cwd, {basicAuth})).repoAuthedUrl
|
|
@@ -17,32 +18,30 @@ export const fetchRepo = memoizeBy(async ({cwd: _cwd, branch, origin: _origin, b
|
|
|
17
18
|
return cwd
|
|
18
19
|
}), async ({cwd, branch}) => `${await getRoot(cwd)}:${branch}`)
|
|
19
20
|
|
|
20
|
-
export const pushCommit = async ({cwd, from, to, branch, origin, msg, ignoreFiles, files = [], basicAuth, gitCommitterEmail, gitCommitterName}) => ctx(async ($) => {
|
|
21
|
+
export const pushCommit = queuefy(async ({cwd, from, to, branch, origin, msg, ignoreFiles, files = [], basicAuth, gitCommitterEmail, gitCommitterName}) => ctx(async ($) => {
|
|
21
22
|
let retries = 3
|
|
22
|
-
let _cwd
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
_cwd = await fetchRepo({cwd, branch, origin, basicAuth})
|
|
24
|
+
const _cwd = await fetchRepo({cwd, branch, origin, basicAuth})
|
|
25
|
+
$.cwd = _cwd
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return
|
|
44
|
-
}
|
|
27
|
+
for (let {relpath, contents} of files) {
|
|
28
|
+
const _contents = typeof contents === 'string' ? contents : JSON.stringify(contents, null, 2)
|
|
29
|
+
await fs.outputFile(path.resolve(_cwd, to, relpath), _contents)
|
|
30
|
+
}
|
|
31
|
+
if (from) await copy({baseFrom: cwd, from, baseTo: _cwd, to, ignoreFiles, cwd})
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await $`git config user.name ${gitCommitterName} &&
|
|
35
|
+
git config user.email ${gitCommitterEmail} &&
|
|
36
|
+
git add . &&
|
|
37
|
+
git commit -m ${msg}`
|
|
38
|
+
} catch {
|
|
39
|
+
log({level: 'warn'})(`no changes to commit to ${branch}`)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
45
42
|
|
|
43
|
+
while (retries > 0) {
|
|
44
|
+
try {
|
|
46
45
|
return await $.raw`git push origin HEAD:refs/heads/${branch}`
|
|
47
46
|
} catch (e) {
|
|
48
47
|
retries -= 1
|
|
@@ -51,9 +50,11 @@ export const pushCommit = async ({cwd, from, to, branch, origin, msg, ignoreFile
|
|
|
51
50
|
if (retries === 0) {
|
|
52
51
|
throw e
|
|
53
52
|
}
|
|
53
|
+
|
|
54
|
+
await $`git pull --rebase origin ${branch}`
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
|
-
})
|
|
57
|
+
}))
|
|
57
58
|
|
|
58
59
|
export const getRoot = async (cwd) => (await $.o({cwd})`git rev-parse --show-toplevel`).toString().trim()
|
|
59
60
|
|
package/src/main/js/log.js
CHANGED
|
@@ -1,34 +1,28 @@
|
|
|
1
1
|
import {$, fs} from 'zx-extra'
|
|
2
2
|
import {get, set, tpl} from './util.js'
|
|
3
3
|
|
|
4
|
-
export const log = (ctx) =>
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return console.log
|
|
10
|
-
}
|
|
4
|
+
export const log = (ctx) =>
|
|
5
|
+
$.report
|
|
6
|
+
? $.report.log(ctx)
|
|
7
|
+
: console.log
|
|
11
8
|
|
|
12
|
-
export const createReport = ({logger = console,
|
|
9
|
+
export const createReport = ({logger = console, packages = {}, queue = [], flags} = {}) => ({
|
|
13
10
|
logger,
|
|
14
|
-
|
|
11
|
+
flags,
|
|
12
|
+
file: flags.report || flags.file,
|
|
15
13
|
status: 'initial',
|
|
16
|
-
queue: [],
|
|
17
|
-
packages: {},
|
|
18
14
|
events: [],
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return this
|
|
31
|
-
},
|
|
15
|
+
queue,
|
|
16
|
+
packages: Object.entries(packages).reduce((acc, [name, {manifest: {version}, absPath, relPath}]) => {
|
|
17
|
+
acc[name] = {
|
|
18
|
+
status: 'initial',
|
|
19
|
+
name,
|
|
20
|
+
version,
|
|
21
|
+
path: absPath,
|
|
22
|
+
relPath
|
|
23
|
+
}
|
|
24
|
+
return acc
|
|
25
|
+
}, {}),
|
|
32
26
|
get(key, pkgName) {
|
|
33
27
|
return get(
|
|
34
28
|
pkgName ? this.packages[pkgName] : this,
|
package/src/main/js/processor.js
CHANGED
|
@@ -13,23 +13,21 @@ import {fetchPkg, npmPublish} from './npm.js'
|
|
|
13
13
|
import {memoizeBy, tpl} from './util.js'
|
|
14
14
|
|
|
15
15
|
export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within(async () => {
|
|
16
|
-
const
|
|
16
|
+
const context = await createContext({flags, env, cwd})
|
|
17
|
+
const {report, packages, queue, prev, graphs} = context
|
|
18
|
+
const _runCmd = queuefy(runCmd, flags.concurrency || os.cpus().length)
|
|
17
19
|
|
|
18
|
-
report
|
|
20
|
+
report
|
|
21
|
+
.log()('zx-bulk-release')
|
|
22
|
+
.log()('queue:', queue)
|
|
23
|
+
.log()('graphs', graphs)
|
|
19
24
|
|
|
20
25
|
try {
|
|
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
|
-
.setPackages(packages)
|
|
27
|
-
|
|
28
26
|
await traverseQueue({queue, prev, async cb(name) {
|
|
29
27
|
report.setStatus('analyzing', name)
|
|
30
28
|
const pkg = packages[name]
|
|
31
|
-
await contextify(pkg,
|
|
32
|
-
await analyze(pkg
|
|
29
|
+
await contextify(pkg, context)
|
|
30
|
+
await analyze(pkg)
|
|
33
31
|
report
|
|
34
32
|
.set('config', pkg.config, name)
|
|
35
33
|
.set('version', pkg.version, name)
|
|
@@ -47,17 +45,14 @@ export const run = async ({cwd = process.cwd(), env, flags = {}} = {}) => within
|
|
|
47
45
|
report.setStatus('skipped', name)
|
|
48
46
|
return
|
|
49
47
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (flags.dryRun) {
|
|
55
|
-
report.setStatus('
|
|
56
|
-
|
|
48
|
+
if (!flags.noBuild) {
|
|
49
|
+
report.setStatus('building', name)
|
|
50
|
+
await build(pkg, _runCmd)
|
|
51
|
+
}
|
|
52
|
+
if (!flags.dryRun && !flags.noPublish) {
|
|
53
|
+
report.setStatus('publishing', name)
|
|
54
|
+
await publish(pkg, _runCmd)
|
|
57
55
|
}
|
|
58
|
-
|
|
59
|
-
report.setStatus('publishing', name)
|
|
60
|
-
await publish(pkg)
|
|
61
56
|
|
|
62
57
|
report.setStatus('success', name)
|
|
63
58
|
}})
|
|
@@ -78,30 +73,31 @@ export const runCmd = async (pkg, name) => {
|
|
|
78
73
|
|
|
79
74
|
if (cmd) {
|
|
80
75
|
log({pkg})(`run ${name} '${cmd}'`)
|
|
81
|
-
|
|
76
|
+
return $.o({cwd: pkg.absPath, quote: v => v, preferLocal: true})`${cmd}`
|
|
82
77
|
}
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
const createContext = (flags, env) => {
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const _build = memoizeBy((pkg, packages) => build(pkg, packages, _runCmd, _build))
|
|
89
|
-
const _publish = memoizeBy((pkg) => publish(pkg, _runCmd))
|
|
80
|
+
const createContext = async ({flags, env, cwd}) => {
|
|
81
|
+
const { packages, queue, root, prev, graphs } = await topo({cwd, flags})
|
|
82
|
+
const report = createReport({packages, queue, flags})
|
|
90
83
|
|
|
91
|
-
$.report =
|
|
92
|
-
$.env =
|
|
93
|
-
$.verbose =
|
|
84
|
+
$.report = report
|
|
85
|
+
$.env = {...process.env, ...env}
|
|
86
|
+
$.verbose = !!(flags.debug || $.env.DEBUG ) || $.verbose
|
|
94
87
|
|
|
95
88
|
return {
|
|
96
89
|
report,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
packages,
|
|
91
|
+
root,
|
|
92
|
+
queue,
|
|
93
|
+
prev,
|
|
94
|
+
graphs,
|
|
95
|
+
flags
|
|
100
96
|
}
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
// Inspired by https://docs.github.com/en/actions/learn-github-actions/contexts
|
|
104
|
-
const contextify = async (pkg, packages, root) => {
|
|
100
|
+
const contextify = async (pkg, {packages, root}) => {
|
|
105
101
|
pkg.config = await getPkgConfig(pkg.absPath, root.absPath)
|
|
106
102
|
pkg.latest = await getLatest(pkg)
|
|
107
103
|
pkg.context = {
|
|
@@ -114,13 +110,12 @@ const contextify = async (pkg, packages, root) => {
|
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
const build = async (pkg,
|
|
113
|
+
const build = memoizeBy(async (pkg, run = runCmd, flags = {}, self = build) => within(async () => {
|
|
118
114
|
$.scope = pkg.name
|
|
119
|
-
if (pkg.built) return
|
|
120
115
|
|
|
121
116
|
await Promise.all([
|
|
122
|
-
traverseDeps(pkg, packages, async (_, {pkg}) => self(pkg,
|
|
123
|
-
pkg.changes.length === 0 && pkg.config.npmFetch
|
|
117
|
+
traverseDeps(pkg, pkg.context.packages, async (_, {pkg}) => self(pkg, run, flags, self)),
|
|
118
|
+
pkg.changes.length === 0 && pkg.config.npmFetch && !flags.noNpmFetch
|
|
124
119
|
? fetchPkg(pkg)
|
|
125
120
|
: Promise.resolve()
|
|
126
121
|
])
|
|
@@ -131,10 +126,11 @@ const build = async (pkg, packages, run = runCmd, self = build) => within(async
|
|
|
131
126
|
}
|
|
132
127
|
|
|
133
128
|
pkg.built = true
|
|
134
|
-
})
|
|
129
|
+
}))
|
|
135
130
|
|
|
136
|
-
const publish = async (pkg, run = runCmd) => within(async () => {
|
|
131
|
+
const publish = memoizeBy(async (pkg, run = runCmd) => within(async () => {
|
|
137
132
|
$.scope = pkg.name
|
|
133
|
+
|
|
138
134
|
await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
|
|
139
135
|
await pushTag(pkg)
|
|
140
136
|
|
|
@@ -146,4 +142,6 @@ const publish = async (pkg, run = runCmd) => within(async () => {
|
|
|
146
142
|
ghPages(pkg),
|
|
147
143
|
run(pkg, 'publishCmd')
|
|
148
144
|
])
|
|
149
|
-
|
|
145
|
+
|
|
146
|
+
pkg.published = true
|
|
147
|
+
}))
|
package/src/main/js/util.js
CHANGED
|
@@ -30,19 +30,6 @@ export const set = (obj, path, value) => {
|
|
|
30
30
|
|
|
31
31
|
export const msgJoin = (rest, context, def) => tpl(rest.filter(Boolean).join(' ') || def, context)
|
|
32
32
|
|
|
33
|
-
export const getPromise = () => {
|
|
34
|
-
let resolve, reject
|
|
35
|
-
const promise = new Promise((...args) => {
|
|
36
|
-
[resolve, reject] = args
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
resolve,
|
|
41
|
-
reject,
|
|
42
|
-
promise,
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
33
|
export const keyByValue = (obj, value) => Object.keys(obj).find((key) => obj[key] === value)
|
|
47
34
|
|
|
48
35
|
export const memoizeBy = (fn, getKey = v => v, memo = new Map()) => async (...args) => {
|