zx-bulk-release 1.5.0 → 1.6.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 +9 -0
- package/package.json +6 -4
- package/src/main/js/build.js +52 -0
- package/src/main/js/deps.js +23 -16
- package/src/main/js/index.js +3 -0
- package/src/test/js/integration.test.js +47 -3
- package/src/test/js/test-utils.js +23 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [1.6.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.5.0...v1.6.0) (2022-06-25)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
* feat: add lazy builder ([4c59a0e](https://github.com/semrel-extra/zx-bulk-release/commit/4c59a0e61aea19cccd66228f34100d733bc3d3d8))
|
|
5
|
+
* feat: provide pkg building ([04ac6ce](https://github.com/semrel-extra/zx-bulk-release/commit/04ac6cee878aa8cfa329b16348735ba809544bea))
|
|
6
|
+
|
|
7
|
+
### Fixes & improvements
|
|
8
|
+
* refactor: reuse `traverseDeps` ([626de1c](https://github.com/semrel-extra/zx-bulk-release/commit/626de1c9250bce831aa9ec6298077bd5b91e06b0))
|
|
9
|
+
|
|
1
10
|
## [1.5.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.2...v1.5.0) (2022-06-21)
|
|
2
11
|
|
|
3
12
|
### Features
|
package/package.json
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zx-bulk-release",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "zx-based alternative for multi-semantic-release",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./src/main/js/index.js",
|
|
7
7
|
"bin": "./src/main/js/cli.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"test": "NPM_REGISTRY='http://localhost:4873' NPM_TOKEN='mRv6eIuiaggXGb9ZDFCtBA==' c8 uvu ./src/test -i fixtures -i utils && c8 report -r lcov",
|
|
10
|
+
"test:it": "NPM_REGISTRY='http://localhost:4873' NPM_TOKEN='mRv6eIuiaggXGb9ZDFCtBA==' node ./src/test/js/integration.test.js",
|
|
10
11
|
"docs": "mkdir -p docs && cp ./README.md ./docs/README.md"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"@semrel-extra/topo": "^1.4.1",
|
|
14
|
-
"git-glob-cp": "^1.
|
|
15
|
+
"git-glob-cp": "^1.7.1",
|
|
16
|
+
"ini": "^3.0.0",
|
|
15
17
|
"zx-extra": "^1.7.0"
|
|
16
18
|
},
|
|
17
19
|
"devDependencies": {
|
|
18
20
|
"c8": "^7.11.3",
|
|
19
|
-
"uvu": "^0.5.
|
|
20
|
-
"verdaccio": "^5.13.
|
|
21
|
+
"uvu": "^0.5.4",
|
|
22
|
+
"verdaccio": "^5.13.1"
|
|
21
23
|
},
|
|
22
24
|
"publishConfig": {
|
|
23
25
|
"access": "public"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {ctx, tempy, fs} from 'zx-extra'
|
|
2
|
+
import {copy} from 'git-glob-cp'
|
|
3
|
+
import ini from 'ini'
|
|
4
|
+
import {traverseDeps} from './deps.js'
|
|
5
|
+
import {parseEnv} from './publish.js'
|
|
6
|
+
|
|
7
|
+
export const build = (pkg, packages) => ctx(async ($) => {
|
|
8
|
+
if (pkg.built) return true
|
|
9
|
+
|
|
10
|
+
await traverseDeps(pkg, packages, async (_, {pkg}) => build(pkg, packages))
|
|
11
|
+
|
|
12
|
+
if (pkg.manifest.scripts?.build && pkg.manifest?.release?.build) {
|
|
13
|
+
if (pkg.changes?.length === 0 && pkg.manifest?.release?.fetch) await fetchPkg(pkg)
|
|
14
|
+
|
|
15
|
+
if (!pkg.fetched) {
|
|
16
|
+
$.cwd = pkg.absPath
|
|
17
|
+
await $`npm run build`
|
|
18
|
+
console.log(`built '${pkg.name}'`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pkg.built = true
|
|
23
|
+
|
|
24
|
+
return true
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const fetchPkg = (pkg) => ctx(async ($) => {
|
|
28
|
+
try {
|
|
29
|
+
const cwd = pkg.absPath
|
|
30
|
+
const {npmRegistry} = parseEnv($.env)
|
|
31
|
+
const temp = tempy.temporaryDirectory()
|
|
32
|
+
const npmrc = ini.parse(await fs.readFile(`${cwd}/.npmrc`, 'utf8'))
|
|
33
|
+
const token = getAuthToken(npmRegistry, npmrc)
|
|
34
|
+
const auth = `Authorization: Bearer ${token}`
|
|
35
|
+
const tarball = getTarballUrl(npmRegistry, pkg.name, pkg.version)
|
|
36
|
+
|
|
37
|
+
await $`wget --header=${auth} -qO- ${tarball} | tar xvz -C ${temp}`
|
|
38
|
+
await copy({from: ['**/*', '!package.json'], to: cwd, cwd: `${temp}/package`})
|
|
39
|
+
|
|
40
|
+
pkg.fetched = true
|
|
41
|
+
console.log(`fetched '${pkg.name}@${pkg.version}'`)
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.log(`fetching '${pkg.name}@${pkg.version}' failed`, e)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// NOTE registry-auth-token does not work with localhost:4873
|
|
48
|
+
const getAuthToken = (registry, npmrc) =>
|
|
49
|
+
(Object.entries(npmrc).find(([reg]) => reg.startsWith(registry.replace(/^https?/, ''))) || [])[1]
|
|
50
|
+
|
|
51
|
+
// $`npm view ${name}@${version} dist.tarball`
|
|
52
|
+
const getTarballUrl = (registry, name, version) => `${registry}/${name}/-/${name.replace(/^.+(%2f|\/)/,'')}-${version}.tgz`
|
package/src/main/js/deps.js
CHANGED
|
@@ -2,9 +2,8 @@ import {semver} from 'zx-extra'
|
|
|
2
2
|
|
|
3
3
|
export const depScopes = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
|
|
4
4
|
|
|
5
|
-
export const
|
|
5
|
+
export const traverseDeps = async (pkg, packages, fn) => {
|
|
6
6
|
const {manifest} = pkg
|
|
7
|
-
const changes = []
|
|
8
7
|
|
|
9
8
|
for (let scope of depScopes) {
|
|
10
9
|
const deps = manifest[scope]
|
|
@@ -13,24 +12,32 @@ export const updateDeps = async (pkg, packages) => {
|
|
|
13
12
|
for (let [name, version] of Object.entries(deps)) {
|
|
14
13
|
if (!packages[name]) continue
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
await fn(pkg, {name, version, deps, scope, pkg: packages[name]})
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
export const updateDeps = async (pkg, packages) => {
|
|
21
|
+
const changes = []
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
await traverseDeps(pkg, packages, async (_, {name, version, deps, scope, pkg: dep}) => {
|
|
24
|
+
const prev = pkg.latest.meta?.[scope]?.[name]
|
|
25
|
+
const actual = dep?.version
|
|
26
|
+
const next = resolveVersion(version, actual, prev)
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
pkg[scope] = {...pkg[scope], [name]: next || version}
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
if (!next) return
|
|
31
|
+
|
|
32
|
+
deps[name] = next
|
|
33
|
+
|
|
34
|
+
changes.push({
|
|
35
|
+
group: 'Dependencies',
|
|
36
|
+
releaseType: 'patch',
|
|
37
|
+
change: 'perf',
|
|
38
|
+
subj: `perf: ${name} updated to ${next}`,
|
|
39
|
+
})
|
|
40
|
+
})
|
|
34
41
|
|
|
35
42
|
return changes
|
|
36
43
|
}
|
package/src/main/js/index.js
CHANGED
|
@@ -3,8 +3,10 @@ import {topo} from '@semrel-extra/topo'
|
|
|
3
3
|
import {updateDeps} from './deps.js'
|
|
4
4
|
import {getSemanticChanges, resolvePkgVersion} from './analyze.js'
|
|
5
5
|
import {publish, getLatest} from './publish.js'
|
|
6
|
+
import {build} from './build.js'
|
|
6
7
|
|
|
7
8
|
export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} = {}) => {
|
|
9
|
+
console.log('Run zx bulk release')
|
|
8
10
|
const {packages, queue} = await topo({cwd})
|
|
9
11
|
const dryRun = flags['dry-run'] || flags.dryRun
|
|
10
12
|
|
|
@@ -23,6 +25,7 @@ export const run = async ({cwd = process.cwd(), env = process.env, flags = {}} =
|
|
|
23
25
|
if (changes.length === 0) continue
|
|
24
26
|
console.log(`semantic changes of '${name}'`, changes)
|
|
25
27
|
|
|
28
|
+
pkg.build = await build(pkg, packages)
|
|
26
29
|
if (dryRun) continue
|
|
27
30
|
|
|
28
31
|
await fs.writeJson(pkg.manifestPath, pkg.manifest, {spaces: 2})
|
|
@@ -4,7 +4,7 @@ import * as assert from 'uvu/assert'
|
|
|
4
4
|
import {ctx, tempy, fs} from 'zx-extra'
|
|
5
5
|
import {run} from '../../main/js/index.js'
|
|
6
6
|
import {formatTag} from '../../main/js/tag.js'
|
|
7
|
-
import {createFakeRepo, createNpmRegistry} from './test-utils.js'
|
|
7
|
+
import {addCommits, createFakeRepo, createNpmRegistry} from './test-utils.js'
|
|
8
8
|
|
|
9
9
|
const test = suite('integration')
|
|
10
10
|
|
|
@@ -32,7 +32,14 @@ const cwd = await createFakeRepo({
|
|
|
32
32
|
{
|
|
33
33
|
relpath: './packages/a/package.json',
|
|
34
34
|
contents: {
|
|
35
|
-
name: 'a'
|
|
35
|
+
name: 'a',
|
|
36
|
+
scripts: {
|
|
37
|
+
build: 'echo "building a"'
|
|
38
|
+
},
|
|
39
|
+
release: {
|
|
40
|
+
build: true,
|
|
41
|
+
fetch: true
|
|
42
|
+
}
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
]
|
|
@@ -74,6 +81,10 @@ const cwd = await createFakeRepo({
|
|
|
74
81
|
name: 'b',
|
|
75
82
|
dependencies: {
|
|
76
83
|
a: '1.0.0'
|
|
84
|
+
},
|
|
85
|
+
release: {
|
|
86
|
+
build: true,
|
|
87
|
+
fetch: true
|
|
77
88
|
}
|
|
78
89
|
}
|
|
79
90
|
}
|
|
@@ -110,7 +121,6 @@ test('run()', async () => {
|
|
|
110
121
|
|
|
111
122
|
assert.is(digestA['dist-tags'].latest, '1.0.1')
|
|
112
123
|
assert.is(digestA.dist.tarball, 'http://localhost:4873/a/-/a-1.0.1.tgz')
|
|
113
|
-
|
|
114
124
|
assert.is(digestB['dist-tags'].latest, '1.0.0')
|
|
115
125
|
assert.is(digestB.dist.tarball, 'http://localhost:4873/b/-/b-1.0.0.tgz')
|
|
116
126
|
|
|
@@ -124,6 +134,40 @@ test('run()', async () => {
|
|
|
124
134
|
assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.0.1')
|
|
125
135
|
})
|
|
126
136
|
|
|
137
|
+
await addCommits({cwd, commits: [
|
|
138
|
+
{
|
|
139
|
+
msg: 'feat(b): add feat',
|
|
140
|
+
files: [
|
|
141
|
+
{
|
|
142
|
+
relpath: './packages/b/feat.txt',
|
|
143
|
+
contents: 'new feat'
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
]})
|
|
148
|
+
|
|
149
|
+
await run({cwd})
|
|
150
|
+
|
|
151
|
+
await ctx(async ($) => {
|
|
152
|
+
$.cwd = cwd
|
|
153
|
+
const digestA = JSON.parse((await $`npm view a --registry=${registry.address} --json`).toString())
|
|
154
|
+
const digestB = JSON.parse((await $`npm view b --registry=${registry.address} --json`).toString())
|
|
155
|
+
|
|
156
|
+
assert.is(digestA['dist-tags'].latest, '1.0.1')
|
|
157
|
+
assert.is(digestA.dist.tarball, 'http://localhost:4873/a/-/a-1.0.1.tgz')
|
|
158
|
+
assert.is(digestB['dist-tags'].latest, '1.1.0')
|
|
159
|
+
assert.is(digestB.dist.tarball, 'http://localhost:4873/b/-/b-1.1.0.tgz')
|
|
160
|
+
|
|
161
|
+
const tag = formatTag({name: 'b', version: '1.1.0'})
|
|
162
|
+
assert.is((await $`git for-each-ref refs/tags/${tag} --format='%(contents)'`).toString().trim(), tag)
|
|
163
|
+
|
|
164
|
+
const origin = (await $`git remote get-url origin`).toString().trim()
|
|
165
|
+
const meta = tempy.temporaryDirectory()
|
|
166
|
+
|
|
167
|
+
await $`git clone --single-branch --branch meta --depth 1 ${origin} ${meta}`
|
|
168
|
+
assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.1.0')
|
|
169
|
+
})
|
|
170
|
+
|
|
127
171
|
await registry.stop()
|
|
128
172
|
})
|
|
129
173
|
|
|
@@ -30,20 +30,7 @@ export const createFakeRepo = async ({cwd = tempy.temporaryDirectory(), commits
|
|
|
30
30
|
$.cwd = cwd
|
|
31
31
|
await $`git init`
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
await $`git config user.name ${name}`
|
|
35
|
-
await $`git config user.email ${email}`
|
|
36
|
-
for (let {relpath, contents} of files) {
|
|
37
|
-
const _contents = typeof contents === 'string' ? contents : JSON.stringify(contents, null, 2)
|
|
38
|
-
await fs.outputFile(path.resolve(cwd, relpath), _contents)
|
|
39
|
-
}
|
|
40
|
-
await $`git add .`
|
|
41
|
-
await $`git commit -m ${msg}`
|
|
42
|
-
|
|
43
|
-
for (let tag of tags) {
|
|
44
|
-
await $`git tag ${tag} -m ${tag}`
|
|
45
|
-
}
|
|
46
|
-
}
|
|
33
|
+
await addCommits({cwd, commits})
|
|
47
34
|
|
|
48
35
|
const bare = tempy.temporaryDirectory()
|
|
49
36
|
await $`git init --bare ${bare}`
|
|
@@ -51,3 +38,25 @@ export const createFakeRepo = async ({cwd = tempy.temporaryDirectory(), commits
|
|
|
51
38
|
|
|
52
39
|
return cwd
|
|
53
40
|
})
|
|
41
|
+
|
|
42
|
+
export const addCommits = async ({cwd, commits = []}) => ctx(async ($) => {
|
|
43
|
+
$.cwd = cwd
|
|
44
|
+
|
|
45
|
+
for (let {msg, files, name = 'Semrel-extra Bot', email = 'semrel-extra-bot@hotmail.com', tags = []} of commits) {
|
|
46
|
+
await $`git config user.name ${name}`
|
|
47
|
+
await $`git config user.email ${email}`
|
|
48
|
+
for (let {relpath, contents} of files) {
|
|
49
|
+
const _contents = typeof contents === 'string' ? contents : JSON.stringify(contents, null, 2)
|
|
50
|
+
const file = path.resolve(cwd, relpath)
|
|
51
|
+
await fs.outputFile(file, _contents)
|
|
52
|
+
|
|
53
|
+
await $`git add ${file}`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await $`git commit -m ${msg}`
|
|
57
|
+
|
|
58
|
+
for (let tag of tags) {
|
|
59
|
+
await $`git tag ${tag} -m ${tag}`
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})
|