zx-bulk-release 1.16.0 → 1.17.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 +19 -0
- package/README.md +62 -8
- package/package.json +2 -2
- package/src/main/js/analyze.js +18 -0
- package/src/main/js/cli.js +2 -1
- package/src/main/js/config.js +3 -0
- package/src/main/js/index.js +9 -24
- package/src/main/js/npm.js +4 -4
- package/src/main/js/publish.js +3 -17
- package/src/main/js/tag.js +19 -7
- package/src/main/js/topo.js +17 -0
- package/src/test/js/topo.test.js +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## [1.17.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.16.2...v1.17.0) (2022-06-28)
|
|
2
|
+
|
|
3
|
+
### Fixes & improvements
|
|
4
|
+
* docs: describe CLI options ([e744e97](https://github.com/semrel-extra/zx-bulk-release/commit/e744e97e8e5451ffff217283ad4e552f05073e7c))
|
|
5
|
+
* docs: describe internal release flow ([e29bfc9](https://github.com/semrel-extra/zx-bulk-release/commit/e29bfc968a519d7c48b0475ad30c826bef17fb96))
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
* feat: introduce `--include-private` and `ignore` flags ([d029b62](https://github.com/semrel-extra/zx-bulk-release/commit/d029b6242da2e3c7fabaa5e24d61709b47e42b94))
|
|
9
|
+
|
|
10
|
+
## [1.16.2](https://github.com/semrel-extra/zx-bulk-release/compare/v1.16.1...v1.16.2) (2022-06-28)
|
|
11
|
+
|
|
12
|
+
### Fixes & improvements
|
|
13
|
+
* refactor: separate `analyze` helper ([1f76993](https://github.com/semrel-extra/zx-bulk-release/commit/1f76993d3d9539a02d516c0cd757713964f6f523))
|
|
14
|
+
|
|
15
|
+
## [1.16.1](https://github.com/semrel-extra/zx-bulk-release/compare/v1.16.0...v1.16.1) (2022-06-28)
|
|
16
|
+
|
|
17
|
+
### Fixes & improvements
|
|
18
|
+
* refactor: extract tag utils from publish.js ([82b3ef0](https://github.com/semrel-extra/zx-bulk-release/commit/82b3ef02affb65bb79eda5f54f32b0378e7083f2))
|
|
19
|
+
|
|
1
20
|
## [1.16.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.15.0...v1.16.0) (2022-06-28)
|
|
2
21
|
|
|
3
22
|
### Features
|
package/README.md
CHANGED
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
```shell
|
|
27
27
|
GH_TOKEN=ghtoken GH_USER=username NPM_TOKEN=npmtoken npx zx-bulk-release [opts]
|
|
28
28
|
```
|
|
29
|
+
| Flag | Description | Default |
|
|
30
|
+
|---------------------|----------------------------|----------|
|
|
31
|
+
| `--dry-run` | Dry run mode | `false` |
|
|
32
|
+
| `--ignore` | Packages to ignore: `a, b` | |
|
|
33
|
+
| `--include-private` | Include private pkgs | `false` |
|
|
29
34
|
|
|
30
35
|
### JS API
|
|
31
36
|
```js
|
|
@@ -59,7 +64,7 @@ Any [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) compliant format
|
|
|
59
64
|
|
|
60
65
|
### Output
|
|
61
66
|
|
|
62
|
-
[
|
|
67
|
+
[Compact and clear logs](https://github.com/semrel-extra/demo-zx-bulk-release/runs/7090161341?check_suite_focus=true#step:6:1)
|
|
63
68
|
|
|
64
69
|
```shell
|
|
65
70
|
Run npm_config_yes=true npx zx-bulk-release
|
|
@@ -99,6 +104,55 @@ zx-bulk-release
|
|
|
99
104
|
```
|
|
100
105
|
|
|
101
106
|
## Implementation notes
|
|
107
|
+
### Flow
|
|
108
|
+
```js
|
|
109
|
+
try {
|
|
110
|
+
const {packages, queue, root} = await topo({cwd, flags})
|
|
111
|
+
console.log('queue:', queue)
|
|
112
|
+
|
|
113
|
+
for (let name of queue) {
|
|
114
|
+
const pkg = packages[name]
|
|
115
|
+
|
|
116
|
+
await analyze(pkg, packages, root)
|
|
117
|
+
|
|
118
|
+
if (pkg.changes.length === 0) continue
|
|
119
|
+
|
|
120
|
+
await build(pkg, packages)
|
|
121
|
+
|
|
122
|
+
if (flags.dryRun) continue
|
|
123
|
+
|
|
124
|
+
await publish(pkg)
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(e)
|
|
128
|
+
throw e
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `topo`
|
|
133
|
+
[Toposort](https://github.com/semrel-extra/topo) is used to order the pkgs to be released.
|
|
134
|
+
By default, it omits the packages marked as `private`. You can override this by setting the `--include-private` flag.
|
|
135
|
+
|
|
136
|
+
### `analyze`
|
|
137
|
+
Determines pkg changes, release type, next version etc.
|
|
138
|
+
|
|
139
|
+
### `build`
|
|
140
|
+
Building pkg assets: bundles, docs, etc.
|
|
141
|
+
```js
|
|
142
|
+
export const publish = async (pkg) => {
|
|
143
|
+
await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
|
|
144
|
+
await pushTag(pkg)
|
|
145
|
+
await pushMeta(pkg)
|
|
146
|
+
await pushChangelog(pkg)
|
|
147
|
+
await npmPublish(pkg)
|
|
148
|
+
await ghRelease(pkg)
|
|
149
|
+
await ghPages(pkg)
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `publish`
|
|
154
|
+
Publish the pkg to git, npm, gh-pages, gh-release, etc.
|
|
155
|
+
|
|
102
156
|
### Tags
|
|
103
157
|
[Lerna](https://github.com/lerna/lerna) tags (like `@pkg/name@v1.0.0-beta.0`) are suitable for monorepos, but they don’t follow [semver spec](https://semver.org/). Therefore, we propose another contract:
|
|
104
158
|
```js
|
|
@@ -117,14 +171,14 @@ export const parseEnv = (env = process.env) => {
|
|
|
117
171
|
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
|
|
118
172
|
|
|
119
173
|
return {
|
|
120
|
-
ghUser:
|
|
121
|
-
ghToken:
|
|
122
|
-
npmToken:
|
|
174
|
+
ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME,
|
|
175
|
+
ghToken: GH_TOKEN || GITHUB_TOKEN,
|
|
176
|
+
npmToken: NPM_TOKEN,
|
|
123
177
|
// npmConfig suppresses npmToken
|
|
124
|
-
npmConfig:
|
|
125
|
-
npmRegistry:
|
|
126
|
-
gitCommitterName:
|
|
127
|
-
gitCommitterEmail:
|
|
178
|
+
npmConfig: NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
|
|
179
|
+
npmRegistry: NPM_REGISTRY || 'https://registry.npmjs.org',
|
|
180
|
+
gitCommitterName: GIT_COMMITTER_NAME || 'Semrel Extra Bot',
|
|
181
|
+
gitCommitterEmail: GIT_COMMITTER_EMAIL || 'semrel-extra-bot@hotmail.com',
|
|
128
182
|
}
|
|
129
183
|
}
|
|
130
184
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zx-bulk-release",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "zx-based alternative for multi-semantic-release",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./src/main/js/index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@semrel-extra/topo": "^1.4.1",
|
|
15
15
|
"cosmiconfig": "^7.0.1",
|
|
16
|
-
"git-glob-cp": "^1.7.
|
|
16
|
+
"git-glob-cp": "^1.7.2",
|
|
17
17
|
"ini": "^3.0.0",
|
|
18
18
|
"zx-extra": "^1.7.1"
|
|
19
19
|
},
|
package/src/main/js/analyze.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
import {ctx, semver} from 'zx-extra'
|
|
2
|
+
import {getConfig} from './config.js'
|
|
3
|
+
import {getLatest} from './publish.js'
|
|
4
|
+
import {updateDeps} from './deps.js'
|
|
5
|
+
|
|
6
|
+
export const analyze = async (pkg, packages, root) => {
|
|
7
|
+
pkg.config = await getConfig(pkg.absPath, root.absPath)
|
|
8
|
+
pkg.latest = await getLatest(pkg)
|
|
9
|
+
|
|
10
|
+
const semanticChanges = await getSemanticChanges(pkg.absPath, pkg.latest.tag?.ref)
|
|
11
|
+
const depsChanges = await updateDeps(pkg, packages)
|
|
12
|
+
const changes = [...semanticChanges, ...depsChanges]
|
|
13
|
+
|
|
14
|
+
pkg.changes = changes
|
|
15
|
+
pkg.version = resolvePkgVersion(changes, pkg.latest.tag?.version || pkg.manifest.version)
|
|
16
|
+
pkg.manifest.version = pkg.version
|
|
17
|
+
|
|
18
|
+
console.log(`[${pkg.name}] semantic changes`, changes)
|
|
19
|
+
}
|
|
2
20
|
|
|
3
21
|
export const releaseSeverityOrder = ['major', 'minor', 'patch']
|
|
4
22
|
export const semanticRules = [
|
package/src/main/js/cli.js
CHANGED
package/src/main/js/config.js
CHANGED
|
@@ -46,3 +46,6 @@ export const parseEnv = (env = process.env) => {
|
|
|
46
46
|
gitCommitterEmail: GIT_COMMITTER_EMAIL || 'semrel-extra-bot@hotmail.com',
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
const camelize = s => s.replace(/-./g, x=>x[1].toUpperCase())
|
|
51
|
+
export const normalizeFlags = (flags = {}) => Object.entries(flags).reduce((acc, [k, v]) => ({...acc, [camelize(k)]: v}), {})
|
package/src/main/js/index.js
CHANGED
|
@@ -1,42 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {updateDeps} from './deps.js'
|
|
4
|
-
import {getSemanticChanges, resolvePkgVersion} from './analyze.js'
|
|
5
|
-
import {publish, getLatest} from './publish.js'
|
|
1
|
+
import {analyze} from './analyze.js'
|
|
2
|
+
import {publish} from './publish.js'
|
|
6
3
|
import {build} from './build.js'
|
|
7
|
-
import {
|
|
4
|
+
import {topo} from './topo.js'
|
|
8
5
|
|
|
9
6
|
export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} = {}) => {
|
|
10
7
|
console.log('zx-bulk-release')
|
|
11
8
|
|
|
12
9
|
try {
|
|
13
|
-
const {packages, queue, root} = await topo({cwd})
|
|
14
|
-
const dryRun = flags['dry-run'] || flags.dryRun
|
|
15
|
-
|
|
10
|
+
const {packages, queue, root} = await topo({cwd, flags})
|
|
16
11
|
console.log('queue:', queue)
|
|
17
12
|
|
|
18
13
|
for (let name of queue) {
|
|
19
14
|
const pkg = packages[name]
|
|
20
|
-
pkg.config = await getConfig(pkg.absPath, root.absPath)
|
|
21
|
-
pkg.latest = await getLatest(pkg)
|
|
22
|
-
|
|
23
|
-
const semanticChanges = await getSemanticChanges(pkg.absPath, pkg.latest.tag?.ref)
|
|
24
|
-
const depsChanges = await updateDeps(pkg, packages)
|
|
25
|
-
const changes = [...semanticChanges, ...depsChanges]
|
|
26
15
|
|
|
27
|
-
pkg
|
|
28
|
-
pkg.version = resolvePkgVersion(changes, pkg.latest.tag?.version || pkg.manifest.version)
|
|
29
|
-
pkg.manifest.version = pkg.version
|
|
16
|
+
await analyze(pkg, packages, root)
|
|
30
17
|
|
|
31
|
-
if (changes.length === 0) continue
|
|
32
|
-
console.log(`[${name}] semantic changes`, changes)
|
|
18
|
+
if (pkg.changes.length === 0) continue
|
|
33
19
|
|
|
34
|
-
await build(pkg, packages
|
|
20
|
+
await build(pkg, packages)
|
|
35
21
|
|
|
36
|
-
if (dryRun) continue
|
|
22
|
+
if (flags.dryRun) continue
|
|
37
23
|
|
|
38
|
-
await
|
|
39
|
-
await publish(pkg, env)
|
|
24
|
+
await publish(pkg)
|
|
40
25
|
}
|
|
41
26
|
} catch (e) {
|
|
42
27
|
console.error(e)
|
package/src/main/js/npm.js
CHANGED
|
@@ -50,10 +50,6 @@ export const npmPublish = (pkg) => ctx(async ($) => {
|
|
|
50
50
|
await $`npm publish --no-git-tag-version --registry=${npmRegistry} --userconfig ${npmrc} --no-workspaces`
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
// NOTE registry-auth-token does not work with localhost:4873
|
|
54
|
-
export const getAuthToken = (registry, npmrc) =>
|
|
55
|
-
(Object.entries(npmrc).find(([reg]) => reg.startsWith(registry.replace(/^https?/, ''))) || [])[1]
|
|
56
|
-
|
|
57
53
|
// $`npm view ${name}@${version} dist.tarball`
|
|
58
54
|
export const getTarballUrl = (registry, name, version) => `${registry}/${name}/-/${name.replace(/^.+(%2f|\/)/,'')}-${version}.tgz`
|
|
59
55
|
|
|
@@ -65,3 +61,7 @@ export const getBearerToken = async (npmRegistry, npmToken, npmConfig) => {
|
|
|
65
61
|
: npmToken
|
|
66
62
|
return `Bearer ${token}`
|
|
67
63
|
}
|
|
64
|
+
|
|
65
|
+
// NOTE registry-auth-token does not work with localhost:4873
|
|
66
|
+
export const getAuthToken = (registry, npmrc) =>
|
|
67
|
+
(Object.entries(npmrc).find(([reg]) => reg.startsWith(registry.replace(/^https?/, ''))) || [])[1]
|
package/src/main/js/publish.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {formatTag, getLatestTag} from './tag.js'
|
|
1
|
+
import {formatTag, getLatestTag, pushTag} from './tag.js'
|
|
2
2
|
import {ctx, 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
6
|
|
|
7
7
|
export const publish = async (pkg) => {
|
|
8
|
+
await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
|
|
8
9
|
await pushTag(pkg)
|
|
9
10
|
await pushMeta(pkg)
|
|
10
11
|
await pushChangelog(pkg)
|
|
@@ -13,25 +14,10 @@ export const publish = async (pkg) => {
|
|
|
13
14
|
await ghPages(pkg)
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
export const pushTag = (pkg) => ctx(async ($) => {
|
|
17
|
-
const {absPath: cwd, name, version} = pkg
|
|
18
|
-
const tag = formatTag({name, version})
|
|
19
|
-
const {gitCommitterEmail, gitCommitterName} = parseEnv($.env)
|
|
20
|
-
|
|
21
|
-
console.log(`[${name}] push release tag ${tag}`)
|
|
22
|
-
|
|
23
|
-
$.cwd = cwd
|
|
24
|
-
await $`git config user.name ${gitCommitterName}`
|
|
25
|
-
await $`git config user.email ${gitCommitterEmail}`
|
|
26
|
-
await $`git tag -m ${tag} ${tag}`
|
|
27
|
-
await $`git push origin ${tag}`
|
|
28
|
-
})
|
|
29
|
-
|
|
30
17
|
export const pushMeta = async (pkg) => {
|
|
31
18
|
console.log(`[${pkg.name}] push artifact to branch 'meta'`)
|
|
32
19
|
|
|
33
|
-
const cwd = pkg
|
|
34
|
-
const {name, version} = pkg
|
|
20
|
+
const {name, version, absPath: cwd} = pkg
|
|
35
21
|
const tag = formatTag({name, version})
|
|
36
22
|
const to = '.'
|
|
37
23
|
const branch = 'meta'
|
package/src/main/js/tag.js
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
// Semantic tags
|
|
1
|
+
// Semantic tags processing
|
|
2
2
|
|
|
3
|
-
import {ctx, semver} from 'zx-extra'
|
|
3
|
+
import {ctx, semver, $} from 'zx-extra'
|
|
4
4
|
import {Buffer} from 'buffer'
|
|
5
|
+
import {parseEnv} from './config.js'
|
|
6
|
+
|
|
7
|
+
export const pushTag = (pkg) => ctx(async ($) => {
|
|
8
|
+
const {absPath: cwd, name, version} = pkg
|
|
9
|
+
const tag = formatTag({name, version})
|
|
10
|
+
const {gitCommitterEmail, gitCommitterName} = parseEnv($.env)
|
|
11
|
+
|
|
12
|
+
console.log(`[${name}] push release tag ${tag}`)
|
|
13
|
+
|
|
14
|
+
$.cwd = cwd
|
|
15
|
+
await $`git config user.name ${gitCommitterName}`
|
|
16
|
+
await $`git config user.email ${gitCommitterEmail}`
|
|
17
|
+
await $`git tag -m ${tag} ${tag}`
|
|
18
|
+
await $`git push origin ${tag}`
|
|
19
|
+
})
|
|
5
20
|
|
|
6
21
|
const f0 = {
|
|
7
22
|
parse(tag) {
|
|
@@ -85,15 +100,12 @@ export const parseTag = (tag) => f0.parse(tag) || f1.parse(tag) || lerna.parse(t
|
|
|
85
100
|
|
|
86
101
|
export const formatTag = (tag) => f0.format(tag) || f1.format(tag) || null
|
|
87
102
|
|
|
88
|
-
export const getTags =
|
|
89
|
-
$.cwd
|
|
90
|
-
|
|
91
|
-
return (await $`git tag -l`).toString()
|
|
103
|
+
export const getTags = async (cwd) =>
|
|
104
|
+
(await $.o({cwd})`git tag -l`).toString()
|
|
92
105
|
.split('\n')
|
|
93
106
|
.map(tag => parseTag(tag.trim()))
|
|
94
107
|
.filter(Boolean)
|
|
95
108
|
.sort((a, b) => semver.rcompare(a.version, b.version))
|
|
96
|
-
})
|
|
97
109
|
|
|
98
110
|
export const getLatestTag = async (cwd, name) =>
|
|
99
111
|
(await getTags(cwd)).find(tag => tag.name === name) || null
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {topo as _topo} from '@semrel-extra/topo'
|
|
2
|
+
|
|
3
|
+
export const topo = async ({flags = {}, cwd} = {}) => {
|
|
4
|
+
const ignore = typeof flags.ignore === 'string'
|
|
5
|
+
? flags.ignore.split(/\s*,\s*/)
|
|
6
|
+
: Array.isArray(flags.ignore)
|
|
7
|
+
? flags.ignore
|
|
8
|
+
: []
|
|
9
|
+
|
|
10
|
+
const filter = flags.includePrivate
|
|
11
|
+
? () => true
|
|
12
|
+
: ({manifest: {private: _private, name}}) =>
|
|
13
|
+
flags.includePrivate ? true : !_private &&
|
|
14
|
+
!ignore.includes(name)
|
|
15
|
+
|
|
16
|
+
return _topo({cwd, filter})
|
|
17
|
+
}
|
package/src/test/js/topo.test.js
CHANGED
|
@@ -2,7 +2,7 @@ import {suite} from 'uvu'
|
|
|
2
2
|
import * as assert from 'uvu/assert'
|
|
3
3
|
import {fileURLToPath} from "node:url"
|
|
4
4
|
import path from 'node:path'
|
|
5
|
-
import {topo} from '
|
|
5
|
+
import {topo} from '../../main/js/topo.js'
|
|
6
6
|
|
|
7
7
|
const test = suite('topo')
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
@@ -14,4 +14,9 @@ test('topo returns pkg info and release queue', async () => {
|
|
|
14
14
|
assert.equal(queue, ['a', 'b', 'd', 'c'])
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
+
test('topo applies pkg filter', async () => {
|
|
18
|
+
const {nodes, queue} = await topo({cwd: path.resolve(fixtures, 'regular-monorepo'), flags: {ignore: 'b,c'}})
|
|
19
|
+
assert.equal(queue, ['a', 'd'])
|
|
20
|
+
})
|
|
21
|
+
|
|
17
22
|
test.run()
|