zx-bulk-release 2.19.1 → 2.20.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 +5 -0
- package/README.md +25 -3
- package/package.json +1 -1
- package/src/main/js/api/changelog.js +17 -4
- package/src/main/js/api/gh.js +4 -4
- package/src/main/js/config.js +12 -3
- package/src/main/js/processor/meta.js +2 -2
- package/src/main/js/steps/contextify.js +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## [2.20.0](https://github.com/semrel-extra/zx-bulk-release/compare/v2.19.1...v2.20.0) (2026-04-05)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
* feat: support custom ghe urls (#89) ([e33d53c](https://github.com/semrel-extra/zx-bulk-release/commit/e33d53cfe4ac15d8fd66785622d9df428a15d8e6))
|
|
5
|
+
|
|
1
6
|
## [2.19.1](https://github.com/semrel-extra/zx-bulk-release/compare/v2.19.0...v2.19.1) (2026-04-05)
|
|
2
7
|
|
|
3
8
|
### Fixes & improvements
|
package/README.md
CHANGED
|
@@ -77,18 +77,40 @@ Any [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) compliant format
|
|
|
77
77
|
"cmd": "yarn && yarn build && yarn test",
|
|
78
78
|
"npmFetch": true,
|
|
79
79
|
"changelog": "changelog",
|
|
80
|
-
"ghPages": "gh-pages"
|
|
80
|
+
"ghPages": "gh-pages",
|
|
81
|
+
"diffTagUrl": "${repoPublicUrl}/compare/${prevTag}...${newTag}",
|
|
82
|
+
"diffCommitUrl": "${repoPublicUrl}/commit/${hash}"
|
|
81
83
|
}
|
|
82
84
|
```
|
|
83
85
|
|
|
86
|
+
#### Changelog diff URLs
|
|
87
|
+
By default, changelog entries link to GitHub compare/commit pages. Override `diffTagUrl` and `diffCommitUrl` to customize for other platforms (e.g. Gerrit):
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"diffTagUrl": "https://gerrit.foo.com/plugins/gitiles/${repoName}/+/refs/tags/${newTag}",
|
|
91
|
+
"diffCommitUrl": "https://gerrit.foo.com/plugins/gitiles/${repoName}/+/${hash}%5E%21"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
Available variables: `repoName`, `repoPublicUrl`, `prevTag`, `newTag`, `name`, `version`, `hash`, `short`.
|
|
95
|
+
|
|
96
|
+
#### GitHub Enterprise
|
|
97
|
+
Set `ghUrl` to point to your GHE instance. API URL (`ghApiUrl`) is derived automatically.
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"ghUrl": "https://ghe.corp.com"
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
Or via env: `GH_URL=https://ghe.corp.com` / `GITHUB_URL=https://ghe.corp.com`.
|
|
104
|
+
|
|
84
105
|
### env vars
|
|
85
106
|
```js
|
|
86
107
|
export const parseEnv = (env = process.env) => {
|
|
87
|
-
const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, NPM_TOKEN, NPM_REGISTRY, NPMRC, NPM_USERCONFIG, NPM_CONFIG_USERCONFIG, NPM_PROVENANCE, NPM_OIDC, ACTIONS_ID_TOKEN_REQUEST_URL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env
|
|
108
|
+
const {GH_USER, GH_USERNAME, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, GH_URL, GITHUB_URL, NPM_TOKEN, NPM_REGISTRY, NPMRC, NPM_USERCONFIG, NPM_CONFIG_USERCONFIG, NPM_PROVENANCE, NPM_OIDC, ACTIONS_ID_TOKEN_REQUEST_URL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = env
|
|
88
109
|
|
|
89
110
|
return {
|
|
90
|
-
ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME,
|
|
111
|
+
ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME || 'x-access-token',
|
|
91
112
|
ghToken: GH_TOKEN || GITHUB_TOKEN,
|
|
113
|
+
ghUrl: GH_URL || GITHUB_URL || 'https://github.com',
|
|
92
114
|
npmToken: NPM_TOKEN,
|
|
93
115
|
// npmConfig suppresses npmToken
|
|
94
116
|
npmConfig: NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
|
package/package.json
CHANGED
|
@@ -21,14 +21,27 @@ export const pushChangelog = queuefy(async (pkg) => {
|
|
|
21
21
|
await pushCommit({cwd, branch, msg, gitCommitterEmail, gitCommitterName, basicAuth})
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
+
export const DIFF_TAG_URL = '${repoPublicUrl}/compare/${prevTag}...${newTag}'
|
|
25
|
+
export const DIFF_COMMIT_URL = '${repoPublicUrl}/commit/${hash}'
|
|
26
|
+
|
|
27
|
+
export const interpolate = (template, vars) => {
|
|
28
|
+
const result = template.replace(/\$\{(\w+)}/g, (_, key) => vars[key] ?? '')
|
|
29
|
+
try { new URL(result) } catch { throw new Error(`invalid URL after interpolation: '${result}' (template: '${template}')`) }
|
|
30
|
+
return result
|
|
31
|
+
}
|
|
32
|
+
|
|
24
33
|
export const formatReleaseNotes = async (pkg) => {
|
|
25
|
-
const {name, version, tag = formatTag({name, version}), absPath: cwd, config: {ghBasicAuth: basicAuth}} = pkg
|
|
26
|
-
const {repoPublicUrl} = await getRepo(cwd, {basicAuth})
|
|
27
|
-
const
|
|
34
|
+
const {name, version, tag = formatTag({name, version}), absPath: cwd, config: {ghBasicAuth: basicAuth, diffTagUrl = DIFF_TAG_URL, diffCommitUrl = DIFF_COMMIT_URL}} = pkg
|
|
35
|
+
const {repoPublicUrl, repoName} = await getRepo(cwd, {basicAuth})
|
|
36
|
+
const prevTag = pkg.latest.tag?.ref
|
|
37
|
+
const vars = {repoName, repoPublicUrl, prevTag, newTag: tag, name, version}
|
|
38
|
+
const diffUrl = interpolate(diffTagUrl, vars)
|
|
39
|
+
const releaseDiffRef = `## [${name}@${version}](${diffUrl}) (${new Date().toISOString().slice(0, 10)})`
|
|
28
40
|
const releaseDetails = Object.values(pkg.changes
|
|
29
41
|
.reduce((acc, {group, subj, short, hash}) => {
|
|
30
42
|
const {commits} = acc[group] || (acc[group] = {commits: [], group})
|
|
31
|
-
const
|
|
43
|
+
const commitUrl = interpolate(diffCommitUrl, {...vars, hash, short})
|
|
44
|
+
const commitRef = `* ${subj}${short ? ` [${short}](${commitUrl})` : ''}`
|
|
32
45
|
|
|
33
46
|
commits.push(commitRef)
|
|
34
47
|
|
package/src/main/js/api/gh.js
CHANGED
|
@@ -8,7 +8,7 @@ import {asArray, getCommonPath, msgJoin} from '../util.js'
|
|
|
8
8
|
|
|
9
9
|
// https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release
|
|
10
10
|
export const ghRelease = async (pkg) => {
|
|
11
|
-
const {ghBasicAuth: basicAuth, ghToken, ghAssets} = pkg.config
|
|
11
|
+
const {ghBasicAuth: basicAuth, ghToken, ghAssets, ghApiUrl} = pkg.config
|
|
12
12
|
if (!ghToken) return null
|
|
13
13
|
|
|
14
14
|
log({pkg})('create gh release')
|
|
@@ -23,7 +23,7 @@ export const ghRelease = async (pkg) => {
|
|
|
23
23
|
body: releaseNotes
|
|
24
24
|
})
|
|
25
25
|
|
|
26
|
-
const res = await (await fetch(
|
|
26
|
+
const res = await (await fetch(`${ghApiUrl}/repos/${repoName}/releases`, {
|
|
27
27
|
method: 'POST',
|
|
28
28
|
headers: {
|
|
29
29
|
Accept: 'application/vnd.github.v3+json',
|
|
@@ -122,8 +122,8 @@ export const ghUploadAssets = async ({ghToken, ghAssets, uploadUrl, cwd}) => {
|
|
|
122
122
|
}))
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
export const ghGetAsset = async ({repoName, tag, name}) => {
|
|
126
|
-
return (await fetch(
|
|
125
|
+
export const ghGetAsset = async ({repoName, tag, name, ghUrl}) => {
|
|
126
|
+
return (await fetch(`${ghUrl || 'https://github.com'}/${repoName}/releases/download/${tag.ref || tag}/${name}`, {
|
|
127
127
|
headers: {
|
|
128
128
|
// Accept: 'application/vnd.github.v3+json'
|
|
129
129
|
}
|
package/src/main/js/config.js
CHANGED
|
@@ -58,10 +58,18 @@ export const normalizeMetaConfig = (meta) =>
|
|
|
58
58
|
? { type: meta } // 'commit' | 'asset' | 'tag'
|
|
59
59
|
: { type: null }
|
|
60
60
|
|
|
61
|
-
export const
|
|
62
|
-
|
|
61
|
+
export const GH_URL = 'https://github.com'
|
|
62
|
+
|
|
63
|
+
const resolveGhApiUrl = (ghUrl) =>
|
|
64
|
+
ghUrl === GH_URL ? 'https://api.github.com' : `${ghUrl.replace(/\/$/, '')}/api/v3`
|
|
65
|
+
|
|
66
|
+
export const parseEnv = ({GH_USER, GH_USERNAME, GH_META, GH_URL: _GH_URL, GITHUB_URL, GITHUB_USER, GITHUB_USERNAME, GH_TOKEN, GITHUB_TOKEN, NPM_TOKEN, NPM_REGISTRY, NPMRC, NPM_USERCONFIG, NPM_CONFIG_USERCONFIG, NPM_PROVENANCE, NPM_OIDC, ACTIONS_ID_TOKEN_REQUEST_URL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL} = process.env) => {
|
|
67
|
+
const ghUrl = _GH_URL || GITHUB_URL || GH_URL
|
|
68
|
+
return {
|
|
63
69
|
ghUser: GH_USER || GH_USERNAME || GITHUB_USER || GITHUB_USERNAME || ((GH_TOKEN || GITHUB_TOKEN) ? 'x-access-token' : undefined),
|
|
64
70
|
ghToken: GH_TOKEN || GITHUB_TOKEN,
|
|
71
|
+
ghUrl,
|
|
72
|
+
ghApiUrl: resolveGhApiUrl(ghUrl),
|
|
65
73
|
ghMeta: GH_META,
|
|
66
74
|
npmConfig: NPMRC || NPM_USERCONFIG || NPM_CONFIG_USERCONFIG,
|
|
67
75
|
npmToken: NPM_TOKEN,
|
|
@@ -70,6 +78,7 @@ export const parseEnv = ({GH_USER, GH_USERNAME, GH_META, GITHUB_USER, GITHUB_USE
|
|
|
70
78
|
npmRegistry: NPM_REGISTRY || 'https://registry.npmjs.org',
|
|
71
79
|
gitCommitterName: GIT_COMMITTER_NAME || 'Semrel Extra Bot',
|
|
72
80
|
gitCommitterEmail: GIT_COMMITTER_EMAIL || 'semrel-extra-bot@hotmail.com',
|
|
73
|
-
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
74
83
|
|
|
75
84
|
export const normalizeFlags = (flags = {}) => Object.entries(flags).reduce((acc, [k, v]) => ({...acc, [camelize(k)]: v}), {})
|
|
@@ -217,11 +217,11 @@ export const getArtifactPath = (tag) => tag.toLowerCase().replace(/[^a-z0-9-]/g,
|
|
|
217
217
|
|
|
218
218
|
export const getLatestMeta = async (pkg, tag) => {
|
|
219
219
|
if (tag) {
|
|
220
|
-
const {absPath: cwd, config: {ghBasicAuth: basicAuth}} = pkg
|
|
220
|
+
const {absPath: cwd, config: {ghBasicAuth: basicAuth, ghUrl}} = pkg
|
|
221
221
|
const {repoName} = await getRepo(cwd, {basicAuth})
|
|
222
222
|
|
|
223
223
|
try {
|
|
224
|
-
return JSON.parse(await ghGetAsset({repoName, tag, name: 'meta.json'}))
|
|
224
|
+
return JSON.parse(await ghGetAsset({repoName, tag, name: 'meta.json', ghUrl}))
|
|
225
225
|
} catch {}
|
|
226
226
|
|
|
227
227
|
try {
|
|
@@ -28,7 +28,7 @@ export const rollbackRelease = async (pkg) => {
|
|
|
28
28
|
if (!tag) return
|
|
29
29
|
|
|
30
30
|
const cwd = pkg.context.git.root
|
|
31
|
-
const {ghBasicAuth: basicAuth, ghToken, gitCommitterName, gitCommitterEmail} = pkg.config
|
|
31
|
+
const {ghBasicAuth: basicAuth, ghToken, ghApiUrl, gitCommitterName, gitCommitterEmail} = pkg.config
|
|
32
32
|
if (!basicAuth) throw new Error('rollback requires git credentials (GH_TOKEN)')
|
|
33
33
|
const {repoName} = await getRepo(cwd, {basicAuth})
|
|
34
34
|
|
|
@@ -37,12 +37,12 @@ export const rollbackRelease = async (pkg) => {
|
|
|
37
37
|
// 1. Delete GitHub release
|
|
38
38
|
if (ghToken) {
|
|
39
39
|
try {
|
|
40
|
-
const res = await fetch(
|
|
40
|
+
const res = await fetch(`${ghApiUrl}/repos/${repoName}/releases/tags/${tag}`, {
|
|
41
41
|
headers: {Authorization: `token ${ghToken}`, 'X-GitHub-Api-Version': '2022-11-28'}
|
|
42
42
|
})
|
|
43
43
|
if (res.ok) {
|
|
44
44
|
const {id} = await res.json()
|
|
45
|
-
await fetch(
|
|
45
|
+
await fetch(`${ghApiUrl}/repos/${repoName}/releases/${id}`, {
|
|
46
46
|
method: 'DELETE',
|
|
47
47
|
headers: {Authorization: `token ${ghToken}`, 'X-GitHub-Api-Version': '2022-11-28'}
|
|
48
48
|
})
|
|
@@ -98,7 +98,7 @@ export const recover = async (pkg) => {
|
|
|
98
98
|
if (manifest) return false
|
|
99
99
|
|
|
100
100
|
const cwd = await getRoot(pkg.absPath)
|
|
101
|
-
const {ghBasicAuth: basicAuth, ghToken, gitCommitterName, gitCommitterEmail} = pkg.config
|
|
101
|
+
const {ghBasicAuth: basicAuth, ghToken, ghApiUrl, gitCommitterName, gitCommitterEmail} = pkg.config
|
|
102
102
|
if (!basicAuth) throw new Error('recover requires git credentials (GH_TOKEN)')
|
|
103
103
|
const {repoName} = await getRepo(cwd, {basicAuth})
|
|
104
104
|
|
|
@@ -107,12 +107,12 @@ export const recover = async (pkg) => {
|
|
|
107
107
|
// 1. Delete GitHub release (also removes attached meta assets)
|
|
108
108
|
if (ghToken) {
|
|
109
109
|
try {
|
|
110
|
-
const res = await fetch(
|
|
110
|
+
const res = await fetch(`${ghApiUrl}/repos/${repoName}/releases/tags/${tag.ref}`, {
|
|
111
111
|
headers: {Authorization: `token ${ghToken}`, 'X-GitHub-Api-Version': '2022-11-28'}
|
|
112
112
|
})
|
|
113
113
|
if (res.ok) {
|
|
114
114
|
const {id} = await res.json()
|
|
115
|
-
await fetch(
|
|
115
|
+
await fetch(`${ghApiUrl}/repos/${repoName}/releases/${id}`, {
|
|
116
116
|
method: 'DELETE',
|
|
117
117
|
headers: {Authorization: `token ${ghToken}`, 'X-GitHub-Api-Version': '2022-11-28'}
|
|
118
118
|
})
|