zx-bulk-release 1.4.1 → 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 +19 -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/main/js/publish.js +102 -86
- package/src/test/js/integration.test.js +48 -4
- package/src/test/js/test-utils.js +23 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
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
|
+
|
|
10
|
+
## [1.5.0](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.2...v1.5.0) (2022-06-21)
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
* feat: add commit-ish to meta ([9092a4d](https://github.com/semrel-extra/zx-bulk-release/commit/9092a4d41f51fb58a9d7cb1696bb9ee3f07c9133))
|
|
14
|
+
|
|
15
|
+
## [1.4.2](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.1...v1.4.2) (2022-06-21)
|
|
16
|
+
|
|
17
|
+
### Fixes & improvements
|
|
18
|
+
* refactor: use common iface for push internals ([e614e42](https://github.com/semrel-extra/zx-bulk-release/commit/e614e42d8b323749082b94611f4552fdacebe1f5))
|
|
19
|
+
|
|
1
20
|
## [1.4.1](https://github.com/semrel-extra/zx-bulk-release/compare/v1.4.0...v1.4.1) (2022-06-21)
|
|
2
21
|
|
|
3
22
|
### Fixes & improvements
|
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})
|
package/src/main/js/publish.js
CHANGED
|
@@ -2,8 +2,101 @@ import {formatTag, getLatestTag} from './tag.js'
|
|
|
2
2
|
import {tempy, ctx, fs, path} from 'zx-extra'
|
|
3
3
|
import {copydir} from 'git-glob-cp'
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
5
|
+
export const publish = async (pkg) => {
|
|
6
|
+
await pushTag(pkg)
|
|
7
|
+
await pushMeta(pkg)
|
|
8
|
+
await npmPublish(pkg)
|
|
9
|
+
await createGhRelease(pkg)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const pushTag = (pkg) => ctx(async ($) => {
|
|
13
|
+
const {absPath: cwd, name, version} = pkg
|
|
14
|
+
const tag = formatTag({name, version})
|
|
15
|
+
const {gitCommitterEmail, gitCommitterName} = parseEnv($.env)
|
|
16
|
+
$.cwd = cwd
|
|
17
|
+
|
|
18
|
+
console.log(`push release tag ${tag}`)
|
|
19
|
+
|
|
20
|
+
await $`git config user.name ${gitCommitterName}`
|
|
21
|
+
await $`git config user.email ${gitCommitterEmail}`
|
|
22
|
+
await $`git tag -m ${tag} ${tag}`
|
|
23
|
+
await $`git push origin ${tag}`
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export const pushMeta = (pkg) => ctx(async ($) => {
|
|
27
|
+
console.log('push artifact to branch `meta`')
|
|
28
|
+
|
|
29
|
+
const cwd = pkg.absPath
|
|
30
|
+
const {name, version} = pkg
|
|
31
|
+
const tag = formatTag({name, version})
|
|
32
|
+
const to = '.'
|
|
33
|
+
const branch = 'meta'
|
|
34
|
+
const msg = `chore: release meta ${name} ${version}`
|
|
35
|
+
|
|
36
|
+
$.cwd = cwd
|
|
37
|
+
const hash = (await $`git rev-parse HEAD`).toString().trim()
|
|
38
|
+
const meta = {
|
|
39
|
+
META_VERSION: '1',
|
|
40
|
+
name: pkg.name,
|
|
41
|
+
hash,
|
|
42
|
+
version: pkg.version,
|
|
43
|
+
dependencies: pkg.dependencies,
|
|
44
|
+
devDependencies: pkg.devDependencies,
|
|
45
|
+
peerDependencies: pkg.peerDependencies,
|
|
46
|
+
optionalDependencies: pkg.optionalDependencies,
|
|
47
|
+
}
|
|
48
|
+
const files = [{relpath: `${getArtifactPath(tag)}.json`, contents: meta}]
|
|
49
|
+
|
|
50
|
+
await push({cwd, to, branch, msg, files})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
export const npmPublish = ({absPath: cwd}) => ctx(async ($) => {
|
|
54
|
+
const {npmRegistry, npmToken, npmConfig} = parseEnv($.env)
|
|
55
|
+
const npmrc = npmConfig ? npmConfig : path.resolve(cwd, '.npmrc')
|
|
56
|
+
|
|
57
|
+
console.log(`publish npm package to ${npmRegistry}`)
|
|
58
|
+
$.cwd = cwd
|
|
59
|
+
if (!npmConfig) {
|
|
60
|
+
await $.raw`echo ${npmRegistry.replace(/https?:/, '')}/:_authToken=${npmToken} >> ${npmrc}`
|
|
61
|
+
}
|
|
62
|
+
await $`npm publish --no-git-tag-version --registry=${npmRegistry} --userconfig ${npmrc} --no-workspaces`
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
export const createGhRelease = (pkg) => ctx(async ($) => {
|
|
66
|
+
console.log('create gh release')
|
|
67
|
+
|
|
68
|
+
const cwd = pkg.absPath
|
|
69
|
+
const {name, version} = pkg
|
|
70
|
+
const {ghUser, ghToken} = parseEnv($.env)
|
|
71
|
+
const {repoName, repoPublicUrl} = await parseRepo(cwd)
|
|
72
|
+
|
|
73
|
+
if (!ghToken || !ghUser) return null
|
|
74
|
+
|
|
75
|
+
const tag = formatTag({name, version})
|
|
76
|
+
const releaseDiffRef = `## [${name}@${version}](${repoPublicUrl}/compare/${pkg.latest.tag?.ref}...${tag}) (${new Date().toISOString().slice(0, 10)})`
|
|
77
|
+
const releaseDetails = Object.values(pkg.changes
|
|
78
|
+
.reduce((acc, {group, subj, short, hash}) => {
|
|
79
|
+
const {commits} = acc[group] || (acc[group] = {commits: [], group})
|
|
80
|
+
const commitRef = `* ${subj}${short ? ` [${short}](${repoPublicUrl}/commit/${hash})` : ''}`
|
|
81
|
+
|
|
82
|
+
commits.push(commitRef)
|
|
83
|
+
|
|
84
|
+
return acc
|
|
85
|
+
}, {}))
|
|
86
|
+
.map(({group, commits}) => `
|
|
87
|
+
### ${group}
|
|
88
|
+
${commits.join('\n')}`).join('\n')
|
|
89
|
+
|
|
90
|
+
const releaseNotes = releaseDiffRef + '\n' + releaseDetails + '\n'
|
|
91
|
+
const releaseData = JSON.stringify({
|
|
92
|
+
name: tag,
|
|
93
|
+
tag_name: tag,
|
|
94
|
+
body: releaseNotes
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
$.cwd = cwd
|
|
98
|
+
await $`curl -u ${ghUser}:${ghToken} -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
|
|
99
|
+
})
|
|
7
100
|
|
|
8
101
|
export const getOrigin = (cwd) => ctx(async ($) => {
|
|
9
102
|
$.cwd = cwd
|
|
@@ -16,6 +109,7 @@ export const getOrigin = (cwd) => ctx(async ($) => {
|
|
|
16
109
|
: originUrl
|
|
17
110
|
})
|
|
18
111
|
|
|
112
|
+
const branches = {}
|
|
19
113
|
export const fetch = async ({cwd: _cwd, branch, origin: _origin}) => ctx(async ($) => {
|
|
20
114
|
let cwd = branches[branch]
|
|
21
115
|
if (cwd) return cwd
|
|
@@ -55,33 +149,6 @@ export const push = async ({cwd, from, to, branch, origin, msg, ignoreFiles, fil
|
|
|
55
149
|
await $.raw`git push origin HEAD:refs/heads/${branch}`
|
|
56
150
|
})
|
|
57
151
|
|
|
58
|
-
export const publish = async (pkg, env) => ctx(async ($) => {
|
|
59
|
-
const {name, version} = pkg.manifest
|
|
60
|
-
const tag = formatTag({name, version})
|
|
61
|
-
const cwd = pkg.absPath
|
|
62
|
-
const {gitCommitterEmail, gitCommitterName, npmRegistry} = parseEnv(env)
|
|
63
|
-
|
|
64
|
-
$.cwd = cwd
|
|
65
|
-
$.env = env
|
|
66
|
-
|
|
67
|
-
console.log(`push release tag ${tag}`)
|
|
68
|
-
await $`git config user.name ${gitCommitterName}`
|
|
69
|
-
await $`git config user.email ${gitCommitterEmail}`
|
|
70
|
-
await $`git tag -m ${tag} ${tag}`
|
|
71
|
-
await $`git push origin ${tag}`
|
|
72
|
-
|
|
73
|
-
console.log('push artifact to branch `meta`')
|
|
74
|
-
await pushMeta(pkg)
|
|
75
|
-
|
|
76
|
-
console.log(`publish npm package to ${npmRegistry}`)
|
|
77
|
-
const npmrc = path.resolve(cwd, '.npmrc')
|
|
78
|
-
await $.raw`echo ${npmRegistry.replace(/https?:/, '')}/:_authToken=${$.env.NPM_TOKEN} >> ${npmrc}`
|
|
79
|
-
await $`npm publish --no-git-tag-version --registry=${npmRegistry} --userconfig ${npmrc} --no-workspaces`
|
|
80
|
-
|
|
81
|
-
console.log('create gh release')
|
|
82
|
-
await createGhRelease(pkg)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
152
|
export const getArtifactPath = (tag) => tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')
|
|
86
153
|
|
|
87
154
|
export const getLatestMeta = async (cwd, tag) => {
|
|
@@ -89,33 +156,15 @@ export const getLatestMeta = async (cwd, tag) => {
|
|
|
89
156
|
|
|
90
157
|
try {
|
|
91
158
|
const _cwd = await fetch({cwd, branch: 'meta'})
|
|
92
|
-
return await
|
|
159
|
+
return await Promise.any([
|
|
160
|
+
fs.readJson(path.resolve(_cwd, `${getArtifactPath(tag)}.json`)),
|
|
161
|
+
fs.readJson(path.resolve(_cwd, getArtifactPath(tag), 'meta.json'))
|
|
162
|
+
])
|
|
93
163
|
} catch {}
|
|
94
164
|
|
|
95
165
|
return null
|
|
96
166
|
}
|
|
97
167
|
|
|
98
|
-
export const pushMeta = async (pkg) => {
|
|
99
|
-
const cwd = pkg.absPath
|
|
100
|
-
const {name, version} = pkg
|
|
101
|
-
const tag = formatTag({name, version})
|
|
102
|
-
const to = getArtifactPath(tag)
|
|
103
|
-
const branch = 'meta'
|
|
104
|
-
const msg = `chore: release meta ${name} ${version}`
|
|
105
|
-
const meta = {
|
|
106
|
-
META_VERSION,
|
|
107
|
-
name: pkg.name,
|
|
108
|
-
version: pkg.version,
|
|
109
|
-
dependencies: pkg.dependencies,
|
|
110
|
-
devDependencies: pkg.devDependencies,
|
|
111
|
-
peerDependencies: pkg.peerDependencies,
|
|
112
|
-
optionalDependencies: pkg.optionalDependencies,
|
|
113
|
-
}
|
|
114
|
-
const files = [{relpath: 'meta.json', contents: meta}]
|
|
115
|
-
|
|
116
|
-
await push({cwd, to, branch, msg, files})
|
|
117
|
-
}
|
|
118
|
-
|
|
119
168
|
export const getLatest = async (cwd, name) => {
|
|
120
169
|
const tag = await getLatestTag(cwd, name)
|
|
121
170
|
const meta = await getLatestMeta(cwd, tag?.ref)
|
|
@@ -127,11 +176,12 @@ export const getLatest = async (cwd, name) => {
|
|
|
127
176
|
}
|
|
128
177
|
|
|
129
178
|
export const parseEnv = (env = process.env) => {
|
|
130
|
-
const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, NPM_TOKEN, NPM_REGISTRY, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env
|
|
179
|
+
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
|
|
131
180
|
|
|
132
181
|
return {
|
|
133
182
|
ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME,
|
|
134
183
|
ghToken: GH_TOKEN || GITHUB_TOKEN,
|
|
184
|
+
npmConfig: NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
|
|
135
185
|
npmToken: NPM_TOKEN,
|
|
136
186
|
npmRegistry: NPM_REGISTRY || 'https://registry.npmjs.org',
|
|
137
187
|
gitCommitterName: GIT_COMMITTER_NAME || 'Semrel Extra Bot',
|
|
@@ -151,37 +201,3 @@ export const parseRepo = (cwd, origin) => ctx(async ($) => {
|
|
|
151
201
|
repoPublicUrl
|
|
152
202
|
}
|
|
153
203
|
})
|
|
154
|
-
|
|
155
|
-
export const createGhRelease = (pkg) => ctx(async ($) => {
|
|
156
|
-
const cwd = pkg.absPath
|
|
157
|
-
const {name, version} = pkg
|
|
158
|
-
const {ghUser, ghToken} = parseEnv($.env)
|
|
159
|
-
const {repoName, repoPublicUrl} = await parseRepo(cwd)
|
|
160
|
-
|
|
161
|
-
if (!ghToken || !ghUser) return null
|
|
162
|
-
|
|
163
|
-
const tag = formatTag({name, version})
|
|
164
|
-
const releaseDiffRef = `## [${name}@${version}](${repoPublicUrl}/compare/${pkg.latest.tag?.ref}...${tag}) (${new Date().toISOString().slice(0, 10)})`
|
|
165
|
-
const releaseDetails = Object.values(pkg.changes
|
|
166
|
-
.reduce((acc, {group, subj, short, hash}) => {
|
|
167
|
-
const {commits} = acc[group] || (acc[group] = {commits: [], group})
|
|
168
|
-
const commitRef = `* ${subj}${short ? ` [${short}](${repoPublicUrl}/commit/${hash})` : ''}`
|
|
169
|
-
|
|
170
|
-
commits.push(commitRef)
|
|
171
|
-
|
|
172
|
-
return acc
|
|
173
|
-
}, {}))
|
|
174
|
-
.map(({group, commits}) => `
|
|
175
|
-
### ${group}
|
|
176
|
-
${commits.join('\n')}`).join('\n')
|
|
177
|
-
|
|
178
|
-
const releaseNotes = releaseDiffRef + '\n' + releaseDetails + '\n'
|
|
179
|
-
const releaseData = JSON.stringify({
|
|
180
|
-
name: tag,
|
|
181
|
-
tag_name: tag,
|
|
182
|
-
body: releaseNotes
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
$.cwd = cwd
|
|
186
|
-
await $`curl -u ${ghUser}:${ghToken} -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${repoName}/releases -d ${releaseData}`
|
|
187
|
-
})
|
|
@@ -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
|
|
|
@@ -121,7 +131,41 @@ test('run()', async () => {
|
|
|
121
131
|
const meta = tempy.temporaryDirectory()
|
|
122
132
|
|
|
123
133
|
await $`git clone --single-branch --branch meta --depth 1 ${origin} ${meta}`
|
|
124
|
-
assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}
|
|
134
|
+
assert.is((await fs.readJson(`${meta}/${tag.toLowerCase().replace(/[^a-z0-9-]/g, '-')}.json`)).version, '1.0.1')
|
|
135
|
+
})
|
|
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')
|
|
125
169
|
})
|
|
126
170
|
|
|
127
171
|
await registry.stop()
|
|
@@ -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
|
+
})
|