genbumppush 0.0.3 → 0.0.5
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/README.md +117 -46
- package/dist/bin.mjs +15 -1
- package/dist/index.d.mts +462 -0
- package/dist/index.mjs +1 -1
- package/dist/{release-CVQ5WSKG.mjs → release-BdWrsEaL.mjs} +281 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ genbumppush --retry-gitlab v1.2.4 # retry provider rel
|
|
|
66
66
|
| `--yes`, `-y` | Skip confirmation |
|
|
67
67
|
| `--help`, `-h` | Print help |
|
|
68
68
|
|
|
69
|
-
Values are resolved in this order: CLI, config file, the `genbumppush` key in `package.json`, then defaults. A CLI flag such as `--no-push` overrides
|
|
69
|
+
Values are resolved in this order: CLI flags, C12 config file, the `"genbumppush"` key in `package.json`, then defaults. A CLI flag such as `--no-push` overrides both config surfaces.
|
|
70
70
|
|
|
71
71
|
## Commit detection
|
|
72
72
|
|
|
@@ -84,7 +84,7 @@ chore(deps): update vite -> excluded by default
|
|
|
84
84
|
|
|
85
85
|
## Configuration
|
|
86
86
|
|
|
87
|
-
Create `genbumppush.config.ts`:
|
|
87
|
+
Prefer a dedicated C12 file so the config stays typed, commented, and out of dependency diffs. Create `genbumppush.config.ts`:
|
|
88
88
|
|
|
89
89
|
```ts
|
|
90
90
|
import { defineConfig } from 'genbumppush';
|
|
@@ -113,35 +113,85 @@ export default defineConfig({
|
|
|
113
113
|
});
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
116
|
+
JavaScript C12 files are also supported. Use `--config <path>` to load a file at another location.
|
|
117
|
+
|
|
118
|
+
### Config in `package.json`
|
|
119
|
+
|
|
120
|
+
For simple setups, the same object can live under the exact top-level key `"genbumppush"` in `package.json`. Other keys are ignored.
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"name": "my-app",
|
|
125
|
+
"version": "1.2.3",
|
|
126
|
+
"genbumppush": {
|
|
127
|
+
"changelog": "CHANGELOG.md",
|
|
128
|
+
"files": ["package.json"],
|
|
129
|
+
"git": {
|
|
130
|
+
"remote": "origin",
|
|
131
|
+
"push": true,
|
|
132
|
+
"tagName": "v{{version}}"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
JSON has no comments and no `defineConfig` typing, so move to `genbumppush.config.ts` once the release config grows nested `github`/`gitlab` blocks or custom hooks. A config file always wins over the `package.json` key.
|
|
139
|
+
|
|
140
|
+
**Never put secrets in either surface.** Tokens, host credentials, and project IDs with credentials belong in the process environment or an uncommitted `.env`. Config may only name which env var to read (for example `tokenEnv`).
|
|
141
|
+
|
|
142
|
+
| Key | Type | Default | Behavior |
|
|
143
|
+
| -------------------------- | ------------------------- | ------------------------------ | --------------------------------------- |
|
|
144
|
+
| `release` | release type | detected | Force release type |
|
|
145
|
+
| `preid` | string | `beta` | Prerelease channel |
|
|
146
|
+
| `changelog` | `false \| true \| string` | `CHANGELOG.md` | Disable, default, or custom path |
|
|
147
|
+
| `excludeDependencyCommits` | boolean | `true` | Ignore non-breaking dependency commits |
|
|
148
|
+
| `files` | string[] | `['package.json']` | Version files |
|
|
149
|
+
| `recursive` | boolean | `false` | Discover nested manifests |
|
|
150
|
+
| `git.remote` | string | `origin` | Remote for checks and push |
|
|
151
|
+
| `git.push` | boolean | `true` | Push branch and tag |
|
|
152
|
+
| `git.sign` | boolean | `false` | Sign commit and tag |
|
|
153
|
+
| `git.requireClean` | boolean | `true` | Reject uncommitted changes |
|
|
154
|
+
| `git.requireUpstream` | boolean | `true` | Require upstream before pushing |
|
|
155
|
+
| `git.commitMessage` | string | `chore(release): v{{version}}` | Commit template |
|
|
156
|
+
| `git.tagName` | string | `v{{version}}` | Tag template |
|
|
157
|
+
| `git.tagMessage` | string | `v{{version}}` | Annotated tag template |
|
|
158
|
+
| `hooks.before` | string or string[] | unset | Commands before file changes |
|
|
159
|
+
| `hooks.after` | string or string[] | unset | Commands after tag/push |
|
|
160
|
+
| `github.enabled` | boolean | `false` | Create a GitHub release after push |
|
|
161
|
+
| `github.host` | string | `github.com` | github.com or GHES host |
|
|
162
|
+
| `github.repo` | string | env / remote | `owner/name` |
|
|
163
|
+
| `github.remote` | string | unset | Extra Git remote for dual-host GitHub |
|
|
164
|
+
| `github.tokenEnv` | string | auto | Exact env var name (disables fallbacks) |
|
|
165
|
+
| `github.releaseName` | string | tag | Release title template |
|
|
166
|
+
| `gitlab.enabled` | boolean | `false` | Create a GitLab release after push |
|
|
167
|
+
| `gitlab.remote` | string | unset | Extra Git remote for dual-host GitLab |
|
|
142
168
|
|
|
143
169
|
`{{version}}` is replaced in commit and tag templates. Hooks run through the shell in the repository directory; only use trusted configuration.
|
|
144
170
|
|
|
171
|
+
## Environment variables and `.env`
|
|
172
|
+
|
|
173
|
+
Config never stores secrets — not in `genbumppush.config.ts`, not in `"genbumppush"` inside `package.json`. Those files are committed; tokens must not be. Provider credentials are read from the process environment. On every run, genbumppush loads `.env` from the working directory (same behavior as changelogen). Values already set in the real environment win over `.env`.
|
|
174
|
+
|
|
175
|
+
Preferred names use the `GENBUMPPUSH_` prefix. Legacy provider variables still work as fallbacks:
|
|
176
|
+
|
|
177
|
+
| Purpose | Preferred | Fallbacks |
|
|
178
|
+
| ----------------- | ------------------------------- | ------------------------------------------------------- |
|
|
179
|
+
| GitHub token | `GENBUMPPUSH_GITHUB_TOKEN` | `GITHUB_TOKEN`, `GH_TOKEN`, `CHANGELOGEN_TOKENS_GITHUB` |
|
|
180
|
+
| GitHub host | `GENBUMPPUSH_GITHUB_HOST` | `GITHUB_API_URL` |
|
|
181
|
+
| GitHub repository | `GENBUMPPUSH_GITHUB_REPOSITORY` | `GITHUB_REPOSITORY` |
|
|
182
|
+
| GitLab token | `GENBUMPPUSH_GITLAB_TOKEN` | `GITLAB_TOKEN` |
|
|
183
|
+
| GitLab host | `GENBUMPPUSH_GITLAB_HOST` | `GITLAB_HOST` |
|
|
184
|
+
| GitLab project | `GENBUMPPUSH_GITLAB_PROJECT` | `GITLAB_PROJECT` |
|
|
185
|
+
|
|
186
|
+
When `tokenEnv` is set in config, only that exact variable name is read; the fallback chain is skipped. Leave `tokenEnv` unset to use the preferred/fallback chain above.
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# .env (do not commit)
|
|
190
|
+
GENBUMPPUSH_GITHUB_TOKEN=ghp_...
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`.env` is optional. Add it to `.gitignore`. CI should inject the same variables through the job environment instead of a file.
|
|
194
|
+
|
|
145
195
|
## Version-file adapters
|
|
146
196
|
|
|
147
197
|
Every configured file is validated before writes begin. Structured files must agree with the root version; mismatches fail without partial updates.
|
|
@@ -216,8 +266,8 @@ export default defineConfig({
|
|
|
216
266
|
github: {
|
|
217
267
|
enabled: true,
|
|
218
268
|
// host: 'github.com', // or a GHES host
|
|
219
|
-
// repo: 'group/project', // or GITHUB_REPOSITORY / package.json
|
|
220
|
-
tokenEnv
|
|
269
|
+
// repo: 'group/project', // or GENBUMPPUSH_GITHUB_REPOSITORY / GITHUB_REPOSITORY / package.json
|
|
270
|
+
// omit tokenEnv to use GENBUMPPUSH_GITHUB_TOKEN, then GITHUB_TOKEN / GH_TOKEN / CHANGELOGEN_TOKENS_GITHUB
|
|
221
271
|
releaseName: 'v{{version}}',
|
|
222
272
|
},
|
|
223
273
|
});
|
|
@@ -234,17 +284,33 @@ export default defineConfig({
|
|
|
234
284
|
enabled: true,
|
|
235
285
|
host: 'https://gitlab.com',
|
|
236
286
|
project: 'group/project',
|
|
237
|
-
tokenEnv: 'GITLAB_TOKEN',
|
|
238
287
|
releaseName: 'v{{version}}',
|
|
239
288
|
},
|
|
240
289
|
});
|
|
241
290
|
```
|
|
242
291
|
|
|
243
|
-
`GITLAB_HOST` and `GITLAB_PROJECT`
|
|
244
|
-
must be available as the configured `tokenEnv` (default `GITLAB_TOKEN`). GitLab receives
|
|
292
|
+
Without `tokenEnv`, GitLab tokens resolve from `GENBUMPPUSH_GITLAB_TOKEN`, then `GITLAB_TOKEN`. `GENBUMPPUSH_GITLAB_HOST` / `GITLAB_HOST` and `GENBUMPPUSH_GITLAB_PROJECT` / `GITLAB_PROJECT` cover host and project fallbacks. GitLab receives
|
|
245
293
|
the matching `CHANGELOG.md` section as the release description. If `git.push` is false,
|
|
246
294
|
the provider is rejected because GitLab cannot create a release for an unpublished tag.
|
|
247
295
|
|
|
296
|
+
### Dual-host releases (GitHub + GitLab)
|
|
297
|
+
|
|
298
|
+
GitLab's release API requires `tag_name` to already exist on that project. When `origin` is GitHub and GitLab is a separate remote, set `gitlab.remote` so genbumppush pushes the release branch and tag to GitLab before calling the API:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
export default defineConfig({
|
|
302
|
+
git: { push: true, remote: 'origin' }, // origin → GitHub
|
|
303
|
+
github: { enabled: true },
|
|
304
|
+
gitlab: {
|
|
305
|
+
enabled: true,
|
|
306
|
+
project: 'group/project',
|
|
307
|
+
remote: 'gitlab', // git remote that points at the GitLab project
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
`github.remote` works the same way when GitHub is not the primary remote. Provider remotes must already exist (`git remote add gitlab <url>`); genbumppush fails fast before commit/tag if a configured provider remote is missing. Leave `gitlab.remote` unset when the primary remote already receives the tag (same remote, or a mirror GitLab already has). `--retry-gitlab` / `--retry-github` check the provider remote when one is configured.
|
|
313
|
+
|
|
248
314
|
```yaml
|
|
249
315
|
release:
|
|
250
316
|
image: node:20
|
|
@@ -263,19 +329,20 @@ Keep artifact publication and GitLab release creation in protected, tag-triggere
|
|
|
263
329
|
|
|
264
330
|
## Scenario guide
|
|
265
331
|
|
|
266
|
-
| Scenario
|
|
267
|
-
|
|
|
268
|
-
| Local release
|
|
269
|
-
| CI release
|
|
270
|
-
| Preview only
|
|
271
|
-
| Local commit/tag only
|
|
272
|
-
| Nuxt or Node package
|
|
273
|
-
| npm lockfile
|
|
274
|
-
| Fixed-version monorepo
|
|
275
|
-
| Tauri
|
|
276
|
-
| Custom VERSION file
|
|
277
|
-
| GitHub/npm publication
|
|
278
|
-
| GitLab release
|
|
332
|
+
| Scenario | Recommended setup |
|
|
333
|
+
| ----------------------- | --------------------------------------------------- |
|
|
334
|
+
| Local release | `npm run release`, confirm interactively |
|
|
335
|
+
| CI release | `genbumppush --yes` with protected Git credentials |
|
|
336
|
+
| Preview only | `genbumppush --dry-run --yes` |
|
|
337
|
+
| Local commit/tag only | `genbumppush patch --no-push --yes` |
|
|
338
|
+
| Nuxt or Node package | Default `package.json` adapter |
|
|
339
|
+
| npm lockfile | Add `package-lock.json` explicitly |
|
|
340
|
+
| Fixed-version monorepo | `recursive: true` |
|
|
341
|
+
| Tauri | Explicit JSON, Cargo.toml, and Cargo.lock files |
|
|
342
|
+
| Custom VERSION file | Add only if the old version occurs once |
|
|
343
|
+
| GitHub/npm publication | Push `v*`; let workflows publish |
|
|
344
|
+
| GitLab release | Run downstream jobs on `$CI_COMMIT_TAG` |
|
|
345
|
+
| Dual-host GitHub+GitLab | Set `gitlab.remote` (and `github.remote` if needed) |
|
|
279
346
|
|
|
280
347
|
## Troubleshooting
|
|
281
348
|
|
|
@@ -320,6 +387,10 @@ vp pack
|
|
|
320
387
|
|
|
321
388
|
The test suite covers CLI parsing, C12 configuration, SemVer edges, JSON/npm/Cargo adapters, recursive workspaces, dry runs, rollback, hooks, tag collisions, detached HEAD, upstream checks, and a real atomic push to a temporary bare Git remote.
|
|
322
389
|
|
|
390
|
+
### Agent skill
|
|
391
|
+
|
|
392
|
+
This repository ships a project skill at `skills/genbumppush/`. Agents working in this checkout can load it for configuration recipes, CLI and error recovery, CI patterns, and library development notes. New conversations that open this worktree pick it up automatically; it is not published on npm.
|
|
393
|
+
|
|
323
394
|
## License
|
|
324
395
|
|
|
325
396
|
MIT
|
package/dist/bin.mjs
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { n as ReleaseError, t as runRelease } from "./release-
|
|
2
|
+
import { n as ReleaseError, t as runRelease } from "./release-BdWrsEaL.mjs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
//#region src/types.ts
|
|
5
|
+
/**
|
|
6
|
+
* Every release type genbumppush can apply.
|
|
7
|
+
*
|
|
8
|
+
* The first seven values match Semantic Versioning. The `pre*` variants
|
|
9
|
+
* enter a prerelease channel (`beta` by default); `prerelease` continues
|
|
10
|
+
* an existing one.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import type { ReleaseType } from 'genbumppush';
|
|
15
|
+
*
|
|
16
|
+
* const forced: ReleaseType = 'minor';
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
5
19
|
const RELEASE_TYPES = [
|
|
6
20
|
"major",
|
|
7
21
|
"premajor",
|
package/dist/index.d.mts
CHANGED
|
@@ -1,81 +1,543 @@
|
|
|
1
1
|
//#region src/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Every release type genbumppush can apply.
|
|
4
|
+
*
|
|
5
|
+
* The first seven values match Semantic Versioning. The `pre*` variants
|
|
6
|
+
* enter a prerelease channel (`beta` by default); `prerelease` continues
|
|
7
|
+
* an existing one.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import type { ReleaseType } from 'genbumppush';
|
|
12
|
+
*
|
|
13
|
+
* const forced: ReleaseType = 'minor';
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
2
16
|
declare const RELEASE_TYPES: readonly ["major", "premajor", "minor", "preminor", "patch", "prepatch", "prerelease"];
|
|
17
|
+
/** One of {@link RELEASE_TYPES}. */
|
|
3
18
|
type ReleaseType = (typeof RELEASE_TYPES)[number];
|
|
19
|
+
/**
|
|
20
|
+
* How genbumppush talks to Git: remote name, commit/tag templates,
|
|
21
|
+
* and the safety checks run before anything is written.
|
|
22
|
+
*/
|
|
4
23
|
type GitOptions = {
|
|
24
|
+
/** Remote used for tag collision checks and `git push`. Default: `'origin'`. */
|
|
5
25
|
remote?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Push the release commit and tag together with `git push --atomic`.
|
|
28
|
+
* Set to `false` for a local-only rehearsal.
|
|
29
|
+
* @defaultValue true
|
|
30
|
+
*/
|
|
6
31
|
push?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Sign the release commit (`git commit -S`) and annotated tag (`git tag -s`).
|
|
34
|
+
* @defaultValue false
|
|
35
|
+
*/
|
|
7
36
|
sign?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Refuse to release when the worktree has uncommitted changes.
|
|
39
|
+
* @defaultValue true
|
|
40
|
+
*/
|
|
8
41
|
requireClean?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Require the current branch to have an upstream before pushing.
|
|
44
|
+
* Ignored when {@link GitOptions.push} is `false`.
|
|
45
|
+
* @defaultValue true
|
|
46
|
+
*/
|
|
9
47
|
requireUpstream?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Release commit subject. `{{version}}` is replaced with the new version
|
|
50
|
+
* (without a leading `v` unless the template adds one).
|
|
51
|
+
* @defaultValue `'chore(release): v{{version}}'`
|
|
52
|
+
* @example `'release: v{{version}}'`
|
|
53
|
+
*/
|
|
10
54
|
commitMessage?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Tag name template. `{{version}}` is replaced with the new version.
|
|
57
|
+
* @defaultValue `'v{{version}}'`
|
|
58
|
+
*/
|
|
11
59
|
tagName?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Annotated tag message template. `{{version}}` is replaced with the new version.
|
|
62
|
+
* @defaultValue `'v{{version}}'`
|
|
63
|
+
*/
|
|
12
64
|
tagMessage?: string;
|
|
13
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* Shell commands run around the release.
|
|
68
|
+
*
|
|
69
|
+
* Commands run in the repository root with a shell. Use them for gates you
|
|
70
|
+
* want on every release (checks, tests), not for storing secrets.
|
|
71
|
+
*/
|
|
14
72
|
type HookOptions = {
|
|
73
|
+
/**
|
|
74
|
+
* Runs after validation and before version files or the changelog change.
|
|
75
|
+
* A failing hook aborts the release with nothing written.
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* hooks: {
|
|
79
|
+
* before: ['npm run check', 'npm test'],
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
15
83
|
before?: string | string[];
|
|
84
|
+
/**
|
|
85
|
+
* Runs after the commit, tag, and optional provider release succeed.
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* hooks: {
|
|
89
|
+
* after: 'echo "Shipped {{version}}"',
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
16
93
|
after?: string | string[];
|
|
17
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Optional GitLab release creation after a successful Git push.
|
|
97
|
+
*
|
|
98
|
+
* Credentials come from the environment (`GENBUMPPUSH_GITLAB_TOKEN` or
|
|
99
|
+
* `GITLAB_TOKEN`). Never put the token itself in this object.
|
|
100
|
+
*/
|
|
18
101
|
type GitLabOptions = {
|
|
102
|
+
/**
|
|
103
|
+
* Create a GitLab release for the new tag.
|
|
104
|
+
* Requires {@link GitOptions.push} to stay enabled and a token in the environment.
|
|
105
|
+
* @defaultValue false
|
|
106
|
+
*/
|
|
19
107
|
enabled?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* GitLab base URL, including protocol when not on gitlab.com.
|
|
110
|
+
* @defaultValue `'https://gitlab.com'`
|
|
111
|
+
* @example `'https://gitlab.example.com'`
|
|
112
|
+
*/
|
|
20
113
|
host?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Project path or numeric ID, as used by the GitLab API.
|
|
116
|
+
* Falls back to `GENBUMPPUSH_GITLAB_PROJECT` or `GITLAB_PROJECT`.
|
|
117
|
+
* @example `'group/subgroup/project'`
|
|
118
|
+
*/
|
|
21
119
|
project?: string;
|
|
120
|
+
/**
|
|
121
|
+
* Git remote that points at the GitLab project.
|
|
122
|
+
*
|
|
123
|
+
* Set this in dual-host setups where `git.remote` (for example `origin`)
|
|
124
|
+
* is GitHub and GitLab is a separate remote. When the value differs from
|
|
125
|
+
* `git.remote`, genbumppush atomically pushes the release branch and tag
|
|
126
|
+
* to this remote before calling the GitLab release API. Leave unset when
|
|
127
|
+
* the primary remote already receives the tag (same remote, or a mirror
|
|
128
|
+
* that GitLab already has).
|
|
129
|
+
*
|
|
130
|
+
* @example `'gitlab'`
|
|
131
|
+
*/
|
|
132
|
+
remote?: string;
|
|
133
|
+
/**
|
|
134
|
+
* Exact environment variable name to read the token from.
|
|
135
|
+
* When set, the usual fallback chain is skipped.
|
|
136
|
+
* @example `'CI_JOB_TOKEN'`
|
|
137
|
+
*/
|
|
22
138
|
tokenEnv?: string;
|
|
139
|
+
/**
|
|
140
|
+
* Release title template. `{{version}}` is replaced with the new version.
|
|
141
|
+
* @defaultValue the tag name (for example `v1.2.3`)
|
|
142
|
+
*/
|
|
23
143
|
releaseName?: string;
|
|
24
144
|
};
|
|
145
|
+
/**
|
|
146
|
+
* Optional GitHub (or GitHub Enterprise Server) release after a successful Git push.
|
|
147
|
+
*
|
|
148
|
+
* Credentials come from the environment (`GENBUMPPUSH_GITHUB_TOKEN`,
|
|
149
|
+
* `GITHUB_TOKEN`, or `GH_TOKEN`). Never put the token itself in this object.
|
|
150
|
+
*/
|
|
25
151
|
type GitHubOptions = {
|
|
152
|
+
/**
|
|
153
|
+
* Create a GitHub release for the new tag.
|
|
154
|
+
* Requires {@link GitOptions.push} to stay enabled and a token in the environment.
|
|
155
|
+
* @defaultValue false
|
|
156
|
+
*/
|
|
26
157
|
enabled?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* GitHub host. `github.com` uses the public API; any other host is treated
|
|
160
|
+
* as GitHub Enterprise Server (`https://<host>/api/v3`).
|
|
161
|
+
* @defaultValue `'github.com'`
|
|
162
|
+
*/
|
|
27
163
|
host?: string;
|
|
164
|
+
/**
|
|
165
|
+
* Repository as `owner/name`. Usually inferred from the Git remote.
|
|
166
|
+
* Override when inference is wrong or unavailable.
|
|
167
|
+
* @example `'xcvzmoon/genbumppush'`
|
|
168
|
+
*/
|
|
28
169
|
repo?: string;
|
|
170
|
+
/**
|
|
171
|
+
* Git remote that points at the GitHub repository.
|
|
172
|
+
*
|
|
173
|
+
* Set this in dual-host setups where `git.remote` is not GitHub. When the
|
|
174
|
+
* value differs from `git.remote`, genbumppush atomically pushes the
|
|
175
|
+
* release branch and tag to this remote before calling the GitHub release
|
|
176
|
+
* API. Leave unset when the primary remote already receives the tag.
|
|
177
|
+
*
|
|
178
|
+
* @example `'github'`
|
|
179
|
+
*/
|
|
180
|
+
remote?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Exact environment variable name to read the token from.
|
|
183
|
+
* When set, the usual fallback chain is skipped.
|
|
184
|
+
*/
|
|
29
185
|
tokenEnv?: string;
|
|
186
|
+
/**
|
|
187
|
+
* Release title template. `{{version}}` is replaced with the new version.
|
|
188
|
+
* @defaultValue the tag name (for example `v1.2.3`)
|
|
189
|
+
*/
|
|
30
190
|
releaseName?: string;
|
|
31
191
|
};
|
|
192
|
+
/**
|
|
193
|
+
* Full genbumppush configuration.
|
|
194
|
+
*
|
|
195
|
+
* Every field is optional. Missing values fall back to the built-in defaults
|
|
196
|
+
* documented on each property. Prefer {@link defineConfig} in a config file
|
|
197
|
+
* so your editor can check the shape.
|
|
198
|
+
*
|
|
199
|
+
* @example Minimal config in `genbumppush.config.ts`
|
|
200
|
+
* ```ts
|
|
201
|
+
* import { defineConfig } from 'genbumppush';
|
|
202
|
+
*
|
|
203
|
+
* export default defineConfig({
|
|
204
|
+
* files: ['package.json', 'package-lock.json'],
|
|
205
|
+
* git: { tagName: 'v{{version}}' },
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @example Same object under `"genbumppush"` in `package.json`
|
|
210
|
+
* ```json
|
|
211
|
+
* {
|
|
212
|
+
* "genbumppush": {
|
|
213
|
+
* "changelog": false,
|
|
214
|
+
* "git": { "push": false }
|
|
215
|
+
* }
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
32
219
|
type GenBumpPushConfig = {
|
|
220
|
+
/**
|
|
221
|
+
* Force a release type instead of detecting it from Conventional Commits.
|
|
222
|
+
* Leave unset for automatic detection.
|
|
223
|
+
* @example `'patch'`
|
|
224
|
+
*/
|
|
33
225
|
release?: ReleaseType;
|
|
226
|
+
/**
|
|
227
|
+
* Prerelease identifier used by `premajor`, `preminor`, `prepatch`,
|
|
228
|
+
* and `prerelease`.
|
|
229
|
+
* @defaultValue `'beta'`
|
|
230
|
+
* @example `'rc'` produces `1.2.4-rc.0` from `1.2.3`
|
|
231
|
+
*/
|
|
34
232
|
preid?: string;
|
|
233
|
+
/**
|
|
234
|
+
* Files whose version strings are updated for the release.
|
|
235
|
+
* Each path is relative to the repository root and must stay inside it.
|
|
236
|
+
* @defaultValue `['package.json']`
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* files: [
|
|
240
|
+
* 'package.json',
|
|
241
|
+
* 'src-tauri/tauri.conf.json',
|
|
242
|
+
* 'src-tauri/Cargo.toml',
|
|
243
|
+
* ]
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
35
246
|
files?: string[];
|
|
247
|
+
/**
|
|
248
|
+
* Also update every nested `package.json` under the repository.
|
|
249
|
+
* Meant for fixed-version monorepos that share one version number.
|
|
250
|
+
* @defaultValue false
|
|
251
|
+
*/
|
|
36
252
|
recursive?: boolean;
|
|
253
|
+
/**
|
|
254
|
+
* Changelog behavior:
|
|
255
|
+
* - `false` — do not write a changelog
|
|
256
|
+
* - `true` — write `CHANGELOG.md`
|
|
257
|
+
* - `string` — write that path
|
|
258
|
+
* @defaultValue `'CHANGELOG.md'`
|
|
259
|
+
* @example `'docs/RELEASES.md'`
|
|
260
|
+
*/
|
|
37
261
|
changelog?: boolean | string;
|
|
262
|
+
/**
|
|
263
|
+
* Ignore non-breaking `chore(deps): …` commits when detecting the next
|
|
264
|
+
* version and building the changelog.
|
|
265
|
+
* @defaultValue true
|
|
266
|
+
*/
|
|
38
267
|
excludeDependencyCommits?: boolean;
|
|
268
|
+
/** Git commit, tag, and push behavior. See {@link GitOptions}. */
|
|
39
269
|
git?: GitOptions;
|
|
270
|
+
/** Optional GitLab release after push. See {@link GitLabOptions}. */
|
|
40
271
|
gitlab?: GitLabOptions;
|
|
272
|
+
/** Optional GitHub release after push. See {@link GitHubOptions}. */
|
|
41
273
|
github?: GitHubOptions;
|
|
274
|
+
/** Shell commands run before and after the release. See {@link HookOptions}. */
|
|
42
275
|
hooks?: HookOptions;
|
|
43
276
|
};
|
|
277
|
+
/**
|
|
278
|
+
* Parsed CLI arguments for {@link runRelease}.
|
|
279
|
+
*
|
|
280
|
+
* You usually receive this from the binary rather than building it by hand.
|
|
281
|
+
* When embedding genbumppush, the minimum object is `{ cwd, dryRun: false, yes: true }`
|
|
282
|
+
* (plus `help: false` if you want a complete {@link CliOptions}).
|
|
283
|
+
*
|
|
284
|
+
* @example Non-interactive patch release in another directory
|
|
285
|
+
* ```ts
|
|
286
|
+
* import { runRelease } from 'genbumppush';
|
|
287
|
+
*
|
|
288
|
+
* await runRelease({
|
|
289
|
+
* cwd: '/path/to/repo',
|
|
290
|
+
* dryRun: false,
|
|
291
|
+
* yes: true,
|
|
292
|
+
* help: false,
|
|
293
|
+
* release: 'patch',
|
|
294
|
+
* });
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
44
297
|
type CliOptions = {
|
|
298
|
+
/** Absolute path to the Git repository to release. Defaults to `process.cwd()` in the CLI. */
|
|
45
299
|
cwd: string;
|
|
300
|
+
/** Explicit C12 config file path. Overrides discovery and the `package.json` key. */
|
|
46
301
|
configFile?: string;
|
|
302
|
+
/** Retry only GitLab release creation for a tag that already exists on the remote. */
|
|
47
303
|
gitlabRetryTag?: string;
|
|
304
|
+
/** Retry only GitHub release creation for a tag that already exists on the remote. */
|
|
48
305
|
githubRetryTag?: string;
|
|
306
|
+
/** Force a release type; otherwise it is detected from commits (or config). */
|
|
49
307
|
release?: ReleaseType;
|
|
308
|
+
/** Prerelease identifier; overrides the value from config when set. */
|
|
50
309
|
preid?: string;
|
|
310
|
+
/** Preview the release without changing files, Git, or remotes. */
|
|
51
311
|
dryRun: boolean;
|
|
312
|
+
/** `false` keeps the commit and tag local. Unset means “use config”. */
|
|
52
313
|
push?: boolean;
|
|
314
|
+
/** Skip the interactive `Create a … release?` confirmation. */
|
|
53
315
|
yes: boolean;
|
|
316
|
+
/** Print CLI help and exit without running a release. */
|
|
54
317
|
help: boolean;
|
|
55
318
|
};
|
|
319
|
+
/**
|
|
320
|
+
* What {@link runRelease} did (or would do, for a dry run).
|
|
321
|
+
*
|
|
322
|
+
* When there are no releasable commits, `releaseType`, `newVersion`, and
|
|
323
|
+
* `tag` stay `undefined` and `pushed` is `false`. That is a successful no-op.
|
|
324
|
+
*
|
|
325
|
+
* @example Inspect a dry run
|
|
326
|
+
* ```ts
|
|
327
|
+
* import { runRelease } from 'genbumppush';
|
|
328
|
+
*
|
|
329
|
+
* const result = await runRelease({ cwd: process.cwd(), dryRun: true, yes: true, help: false });
|
|
330
|
+
* if (result.releaseType === undefined) {
|
|
331
|
+
* console.log('Nothing to release');
|
|
332
|
+
* } else {
|
|
333
|
+
* console.log(`${result.currentVersion} → ${result.newVersion} as ${result.tag}`);
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
56
337
|
type ReleaseResult = {
|
|
338
|
+
/** Version before this run (from the root `package.json`). */
|
|
57
339
|
currentVersion: string;
|
|
340
|
+
/** Version after the bump. Absent on dry-run skips and “no releasable commits”. */
|
|
58
341
|
newVersion?: string;
|
|
342
|
+
/** Release type that was applied (or planned, for a dry run). */
|
|
59
343
|
releaseType?: ReleaseType;
|
|
344
|
+
/** Tag name created or planned, after `{{version}}` substitution. */
|
|
60
345
|
tag?: string;
|
|
346
|
+
/** `true` only when the branch and tag were pushed to the remote. */
|
|
61
347
|
pushed: boolean;
|
|
348
|
+
/** `true` when the run was a dry run and nothing was written. */
|
|
62
349
|
dryRun: boolean;
|
|
350
|
+
/** Number of Conventional Commits that fed the release decision. */
|
|
63
351
|
commitCount: number;
|
|
352
|
+
/** `true` when a GitLab release was created after the Git push. */
|
|
64
353
|
gitlabReleaseCreated?: boolean;
|
|
354
|
+
/** `true` when a GitHub release was created after the Git push. */
|
|
65
355
|
githubReleaseCreated?: boolean;
|
|
66
356
|
};
|
|
67
357
|
//#endregion
|
|
68
358
|
//#region src/config.d.ts
|
|
359
|
+
/**
|
|
360
|
+
* Identity helper that types a release config for your editor.
|
|
361
|
+
*
|
|
362
|
+
* It does not change the object at runtime — it only enables autocomplete
|
|
363
|
+
* and catches typos inside `defineConfig({ … })`.
|
|
364
|
+
*
|
|
365
|
+
* @typeParam Config - Config object shape; defaults to {@link GenBumpPushConfig}.
|
|
366
|
+
* @returns The same object you passed in.
|
|
367
|
+
*
|
|
368
|
+
* @example `genbumppush.config.ts`
|
|
369
|
+
* ```ts
|
|
370
|
+
* import { defineConfig } from 'genbumppush';
|
|
371
|
+
*
|
|
372
|
+
* export default defineConfig({
|
|
373
|
+
* preid: 'beta',
|
|
374
|
+
* files: ['package.json'],
|
|
375
|
+
* git: {
|
|
376
|
+
* commitMessage: 'chore(release): v{{version}}',
|
|
377
|
+
* },
|
|
378
|
+
* hooks: {
|
|
379
|
+
* before: ['npm run check', 'npm test'],
|
|
380
|
+
* },
|
|
381
|
+
* });
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
69
384
|
export declare const defineConfig: import("c12").DefineConfig<GenBumpPushConfig, import("c12").ConfigLayerMeta>;
|
|
385
|
+
/**
|
|
386
|
+
* Load the effective release config for a repository.
|
|
387
|
+
*
|
|
388
|
+
* Resolution order (later wins):
|
|
389
|
+
* 1. {@link defaults}
|
|
390
|
+
* 2. `"genbumppush"` key in that directory’s `package.json`
|
|
391
|
+
* 3. C12 config file (`genbumppush.config.ts` / `.js`, or `configFile` when given)
|
|
392
|
+
* 4. `overrides` you pass here (CLI flags in the binary go through this)
|
|
393
|
+
*
|
|
394
|
+
* Also loads `.env` from `cwd` into `process.env` without overwriting
|
|
395
|
+
* variables that are already set. Secrets still belong in the environment —
|
|
396
|
+
* not in config files.
|
|
397
|
+
*
|
|
398
|
+
* @param cwd - Repository root to load config from.
|
|
399
|
+
* @param configFile - Optional explicit path to a C12 config file.
|
|
400
|
+
* @param overrides - Highest-priority values (for example CLI flags).
|
|
401
|
+
* @returns The fully merged config object.
|
|
402
|
+
* @throws May reject if the config file throws or cannot be loaded.
|
|
403
|
+
*
|
|
404
|
+
* @example Read what a repo already configured
|
|
405
|
+
* ```ts
|
|
406
|
+
* import { loadReleaseConfig } from 'genbumppush';
|
|
407
|
+
*
|
|
408
|
+
* const config = await loadReleaseConfig(process.cwd());
|
|
409
|
+
* console.log(config.git?.remote ?? 'origin');
|
|
410
|
+
* ```
|
|
411
|
+
*
|
|
412
|
+
* @example Force a dry-run-style push disable from a script
|
|
413
|
+
* ```ts
|
|
414
|
+
* import { loadReleaseConfig } from 'genbumppush';
|
|
415
|
+
*
|
|
416
|
+
* const config = await loadReleaseConfig(process.cwd(), undefined, {
|
|
417
|
+
* git: { push: false },
|
|
418
|
+
* });
|
|
419
|
+
* // config.git.push === false even if the file enabled push
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
70
422
|
export declare function loadReleaseConfig(cwd: string, configFile?: string, overrides?: GenBumpPushConfig): Promise<GenBumpPushConfig>;
|
|
71
423
|
//#endregion
|
|
72
424
|
//#region src/error.d.ts
|
|
425
|
+
/**
|
|
426
|
+
* Error thrown by genbumppush for expected release failures.
|
|
427
|
+
*
|
|
428
|
+
* Unlike a generic `Error`, every {@link ReleaseError} carries a stable
|
|
429
|
+
* {@link ReleaseError.code} so scripts and the CLI can branch on the reason
|
|
430
|
+
* (`DIRTY_WORKTREE`, `TAG_EXISTS`, `CANCELLED`, …) without parsing messages.
|
|
431
|
+
*
|
|
432
|
+
* @example Branch on the failure reason
|
|
433
|
+
* ```ts
|
|
434
|
+
* import { runRelease, ReleaseError } from 'genbumppush';
|
|
435
|
+
*
|
|
436
|
+
* try {
|
|
437
|
+
* await runRelease({ cwd: process.cwd(), dryRun: false, yes: true, help: false });
|
|
438
|
+
* } catch (error) {
|
|
439
|
+
* if (error instanceof ReleaseError && error.code === 'CANCELLED') {
|
|
440
|
+
* process.exit(0);
|
|
441
|
+
* }
|
|
442
|
+
* throw error;
|
|
443
|
+
* }
|
|
444
|
+
* ```
|
|
445
|
+
*
|
|
446
|
+
* @example Print code and message the same way the CLI does
|
|
447
|
+
* ```ts
|
|
448
|
+
* if (error instanceof ReleaseError) {
|
|
449
|
+
* console.error(`[${error.code}] ${error.message}`);
|
|
450
|
+
* }
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
73
453
|
export declare class ReleaseError extends Error {
|
|
454
|
+
/** Machine-readable reason, for example `'DIRTY_WORKTREE'` or `'TAG_EXISTS'`. */
|
|
74
455
|
readonly code: string;
|
|
456
|
+
/**
|
|
457
|
+
* @param code - Stable machine-readable reason.
|
|
458
|
+
* @param message - Human-readable explanation shown to the user.
|
|
459
|
+
* @param options - Optional `cause` when wrapping an underlying error.
|
|
460
|
+
*/
|
|
75
461
|
constructor(code: string, message: string, options?: ErrorOptions);
|
|
76
462
|
}
|
|
77
463
|
//#endregion
|
|
78
464
|
//#region src/release.d.ts
|
|
465
|
+
/**
|
|
466
|
+
* Run a full release (or a dry run / provider retry) for one repository.
|
|
467
|
+
*
|
|
468
|
+
* Typical flow:
|
|
469
|
+
* 1. Load config and `.env`
|
|
470
|
+
* 2. Verify the worktree is a clean Git repo on a branch
|
|
471
|
+
* 3. Detect (or force) a release type from Conventional Commits
|
|
472
|
+
* 4. Confirm, run `before` hooks, update version files and the changelog
|
|
473
|
+
* 5. Commit, tag, and atomically push branch + tag (plus provider remotes when set)
|
|
474
|
+
* 6. Optionally create a GitHub/GitLab release, then run `after` hooks
|
|
475
|
+
*
|
|
476
|
+
* Version-file or commit failures restore files and the index. A failed tag
|
|
477
|
+
* or network push can leave the release commit locally so you can inspect
|
|
478
|
+
* and retry — see the returned {@link ReleaseResult} and any thrown
|
|
479
|
+
* {@link ReleaseError}.
|
|
480
|
+
*
|
|
481
|
+
* Prefer the `genbumppush` CLI for day-to-day use. Call this directly when
|
|
482
|
+
* embedding releases in a Node script or CI job that already builds
|
|
483
|
+
* {@link CliOptions}.
|
|
484
|
+
*
|
|
485
|
+
* @param options - Parsed CLI options. `cwd`, `dryRun`, `yes`, and `help` are required;
|
|
486
|
+
* the rest override config when set.
|
|
487
|
+
* @returns A summary of what happened. Never mutates when `dryRun` is `true`.
|
|
488
|
+
* @throws {@link ReleaseError} for expected failures (dirty worktree, existing tag,
|
|
489
|
+
* cancelled confirmation, Git/provider errors). Unexpected errors may also throw.
|
|
490
|
+
*
|
|
491
|
+
* @example Dry run in the current directory
|
|
492
|
+
* ```ts
|
|
493
|
+
* import { runRelease } from 'genbumppush';
|
|
494
|
+
*
|
|
495
|
+
* const result = await runRelease({
|
|
496
|
+
* cwd: process.cwd(),
|
|
497
|
+
* dryRun: true,
|
|
498
|
+
* yes: true,
|
|
499
|
+
* help: false,
|
|
500
|
+
* });
|
|
501
|
+
*
|
|
502
|
+
* console.log(result);
|
|
503
|
+
* // {
|
|
504
|
+
* // currentVersion: '1.2.3',
|
|
505
|
+
* // newVersion: '1.3.0',
|
|
506
|
+
* // releaseType: 'minor',
|
|
507
|
+
* // tag: 'v1.3.0',
|
|
508
|
+
* // pushed: false,
|
|
509
|
+
* // dryRun: true,
|
|
510
|
+
* // commitCount: 4
|
|
511
|
+
* // }
|
|
512
|
+
* ```
|
|
513
|
+
*
|
|
514
|
+
* @example Non-interactive patch release that stays local
|
|
515
|
+
* ```ts
|
|
516
|
+
* import { runRelease } from 'genbumppush';
|
|
517
|
+
*
|
|
518
|
+
* await runRelease({
|
|
519
|
+
* cwd: process.cwd(),
|
|
520
|
+
* dryRun: false,
|
|
521
|
+
* yes: true,
|
|
522
|
+
* help: false,
|
|
523
|
+
* release: 'patch',
|
|
524
|
+
* push: false,
|
|
525
|
+
* });
|
|
526
|
+
* ```
|
|
527
|
+
*
|
|
528
|
+
* @example Retry only the GitLab release after Git already succeeded
|
|
529
|
+
* ```ts
|
|
530
|
+
* import { runRelease } from 'genbumppush';
|
|
531
|
+
*
|
|
532
|
+
* await runRelease({
|
|
533
|
+
* cwd: process.cwd(),
|
|
534
|
+
* dryRun: false,
|
|
535
|
+
* yes: true,
|
|
536
|
+
* help: false,
|
|
537
|
+
* gitlabRetryTag: 'v1.3.0',
|
|
538
|
+
* });
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
79
541
|
export declare function runRelease(options: CliOptions): Promise<ReleaseResult>;
|
|
80
542
|
//#endregion
|
|
81
543
|
export type { CliOptions, GenBumpPushConfig, GitHubOptions, GitLabOptions, GitOptions, HookOptions, ReleaseResult, ReleaseType };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as loadReleaseConfig, n as ReleaseError, r as defineConfig, t as runRelease } from "./release-
|
|
1
|
+
import { i as loadReleaseConfig, n as ReleaseError, r as defineConfig, t as runRelease } from "./release-BdWrsEaL.mjs";
|
|
2
2
|
export { ReleaseError, defineConfig, loadReleaseConfig, runRelease };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDefineConfig, loadConfig } from "c12";
|
|
1
|
+
import { createDefineConfig, loadConfig, setupDotenv } from "c12";
|
|
2
2
|
import { determineSemverChange, generateMarkDown, getGitDiff, loadChangelogConfig, parseCommits, resolveRepoConfig } from "changelogen";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { readFile, readdir, realpath, unlink, writeFile } from "node:fs/promises";
|
|
@@ -6,7 +6,38 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:pat
|
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
8
8
|
//#region src/config.ts
|
|
9
|
+
/**
|
|
10
|
+
* Identity helper that types a release config for your editor.
|
|
11
|
+
*
|
|
12
|
+
* It does not change the object at runtime — it only enables autocomplete
|
|
13
|
+
* and catches typos inside `defineConfig({ … })`.
|
|
14
|
+
*
|
|
15
|
+
* @typeParam Config - Config object shape; defaults to {@link GenBumpPushConfig}.
|
|
16
|
+
* @returns The same object you passed in.
|
|
17
|
+
*
|
|
18
|
+
* @example `genbumppush.config.ts`
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { defineConfig } from 'genbumppush';
|
|
21
|
+
*
|
|
22
|
+
* export default defineConfig({
|
|
23
|
+
* preid: 'beta',
|
|
24
|
+
* files: ['package.json'],
|
|
25
|
+
* git: {
|
|
26
|
+
* commitMessage: 'chore(release): v{{version}}',
|
|
27
|
+
* },
|
|
28
|
+
* hooks: {
|
|
29
|
+
* before: ['npm run check', 'npm test'],
|
|
30
|
+
* },
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
9
34
|
const defineConfig = createDefineConfig();
|
|
35
|
+
/**
|
|
36
|
+
* Built-in defaults used when neither the CLI, a config file, nor
|
|
37
|
+
* `package.json` sets a field.
|
|
38
|
+
*
|
|
39
|
+
* Useful if you want to document or assert what an empty config resolves to.
|
|
40
|
+
*/
|
|
10
41
|
const defaults = {
|
|
11
42
|
changelog: "CHANGELOG.md",
|
|
12
43
|
excludeDependencyCommits: true,
|
|
@@ -22,7 +53,45 @@ const defaults = {
|
|
|
22
53
|
tagMessage: "v{{version}}"
|
|
23
54
|
}
|
|
24
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Load the effective release config for a repository.
|
|
58
|
+
*
|
|
59
|
+
* Resolution order (later wins):
|
|
60
|
+
* 1. {@link defaults}
|
|
61
|
+
* 2. `"genbumppush"` key in that directory’s `package.json`
|
|
62
|
+
* 3. C12 config file (`genbumppush.config.ts` / `.js`, or `configFile` when given)
|
|
63
|
+
* 4. `overrides` you pass here (CLI flags in the binary go through this)
|
|
64
|
+
*
|
|
65
|
+
* Also loads `.env` from `cwd` into `process.env` without overwriting
|
|
66
|
+
* variables that are already set. Secrets still belong in the environment —
|
|
67
|
+
* not in config files.
|
|
68
|
+
*
|
|
69
|
+
* @param cwd - Repository root to load config from.
|
|
70
|
+
* @param configFile - Optional explicit path to a C12 config file.
|
|
71
|
+
* @param overrides - Highest-priority values (for example CLI flags).
|
|
72
|
+
* @returns The fully merged config object.
|
|
73
|
+
* @throws May reject if the config file throws or cannot be loaded.
|
|
74
|
+
*
|
|
75
|
+
* @example Read what a repo already configured
|
|
76
|
+
* ```ts
|
|
77
|
+
* import { loadReleaseConfig } from 'genbumppush';
|
|
78
|
+
*
|
|
79
|
+
* const config = await loadReleaseConfig(process.cwd());
|
|
80
|
+
* console.log(config.git?.remote ?? 'origin');
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* @example Force a dry-run-style push disable from a script
|
|
84
|
+
* ```ts
|
|
85
|
+
* import { loadReleaseConfig } from 'genbumppush';
|
|
86
|
+
*
|
|
87
|
+
* const config = await loadReleaseConfig(process.cwd(), undefined, {
|
|
88
|
+
* git: { push: false },
|
|
89
|
+
* });
|
|
90
|
+
* // config.git.push === false even if the file enabled push
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
25
93
|
async function loadReleaseConfig(cwd, configFile, overrides) {
|
|
94
|
+
await setupDotenv({ cwd });
|
|
26
95
|
return (await loadConfig({
|
|
27
96
|
name: "genbumppush",
|
|
28
97
|
cwd,
|
|
@@ -36,8 +105,42 @@ async function loadReleaseConfig(cwd, configFile, overrides) {
|
|
|
36
105
|
}
|
|
37
106
|
//#endregion
|
|
38
107
|
//#region src/error.ts
|
|
108
|
+
/**
|
|
109
|
+
* Error thrown by genbumppush for expected release failures.
|
|
110
|
+
*
|
|
111
|
+
* Unlike a generic `Error`, every {@link ReleaseError} carries a stable
|
|
112
|
+
* {@link ReleaseError.code} so scripts and the CLI can branch on the reason
|
|
113
|
+
* (`DIRTY_WORKTREE`, `TAG_EXISTS`, `CANCELLED`, …) without parsing messages.
|
|
114
|
+
*
|
|
115
|
+
* @example Branch on the failure reason
|
|
116
|
+
* ```ts
|
|
117
|
+
* import { runRelease, ReleaseError } from 'genbumppush';
|
|
118
|
+
*
|
|
119
|
+
* try {
|
|
120
|
+
* await runRelease({ cwd: process.cwd(), dryRun: false, yes: true, help: false });
|
|
121
|
+
* } catch (error) {
|
|
122
|
+
* if (error instanceof ReleaseError && error.code === 'CANCELLED') {
|
|
123
|
+
* process.exit(0);
|
|
124
|
+
* }
|
|
125
|
+
* throw error;
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @example Print code and message the same way the CLI does
|
|
130
|
+
* ```ts
|
|
131
|
+
* if (error instanceof ReleaseError) {
|
|
132
|
+
* console.error(`[${error.code}] ${error.message}`);
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
39
136
|
var ReleaseError = class extends Error {
|
|
137
|
+
/** Machine-readable reason, for example `'DIRTY_WORKTREE'` or `'TAG_EXISTS'`. */
|
|
40
138
|
code;
|
|
139
|
+
/**
|
|
140
|
+
* @param code - Stable machine-readable reason.
|
|
141
|
+
* @param message - Human-readable explanation shown to the user.
|
|
142
|
+
* @param options - Optional `cause` when wrapping an underlying error.
|
|
143
|
+
*/
|
|
41
144
|
constructor(code, message, options) {
|
|
42
145
|
super(message, options);
|
|
43
146
|
this.name = "ReleaseError";
|
|
@@ -85,6 +188,16 @@ function isGitRepository(cwd) {
|
|
|
85
188
|
if (result.error || result.status !== 0 || typeof result.stdout !== "string") return false;
|
|
86
189
|
return result.stdout.trim() === "true";
|
|
87
190
|
}
|
|
191
|
+
function remoteExists(cwd, remote) {
|
|
192
|
+
return spawnSync("git", [
|
|
193
|
+
"remote",
|
|
194
|
+
"get-url",
|
|
195
|
+
remote
|
|
196
|
+
], {
|
|
197
|
+
cwd,
|
|
198
|
+
stdio: "ignore"
|
|
199
|
+
}).status === 0;
|
|
200
|
+
}
|
|
88
201
|
function remoteTagExists(cwd, remote, tag) {
|
|
89
202
|
return git([
|
|
90
203
|
"ls-remote",
|
|
@@ -101,6 +214,26 @@ function runHook(command, cwd) {
|
|
|
101
214
|
}).status !== 0) throw new ReleaseError("HOOK_FAILED", `Hook failed: ${command}`);
|
|
102
215
|
}
|
|
103
216
|
//#endregion
|
|
217
|
+
//#region src/env.ts
|
|
218
|
+
const ENV = {
|
|
219
|
+
GITHUB_TOKEN: "GENBUMPPUSH_GITHUB_TOKEN",
|
|
220
|
+
GITHUB_HOST: "GENBUMPPUSH_GITHUB_HOST",
|
|
221
|
+
GITHUB_REPOSITORY: "GENBUMPPUSH_GITHUB_REPOSITORY",
|
|
222
|
+
GITLAB_TOKEN: "GENBUMPPUSH_GITLAB_TOKEN",
|
|
223
|
+
GITLAB_HOST: "GENBUMPPUSH_GITLAB_HOST",
|
|
224
|
+
GITLAB_PROJECT: "GENBUMPPUSH_GITLAB_PROJECT"
|
|
225
|
+
};
|
|
226
|
+
function readEnv(name) {
|
|
227
|
+
const value = process.env[name];
|
|
228
|
+
return value !== void 0 && value.length > 0 ? value : void 0;
|
|
229
|
+
}
|
|
230
|
+
function readEnvFirst(...names) {
|
|
231
|
+
for (const name of names) {
|
|
232
|
+
const value = readEnv(name);
|
|
233
|
+
if (value !== void 0) return value;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
104
237
|
//#region src/github.ts
|
|
105
238
|
function normalizeHost(host) {
|
|
106
239
|
return host.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
@@ -113,23 +246,22 @@ function apiBase(host) {
|
|
|
113
246
|
function encodeRepoPath(repo) {
|
|
114
247
|
return repo.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
115
248
|
}
|
|
249
|
+
const GITHUB_TOKEN_FALLBACKS = [
|
|
250
|
+
ENV.GITHUB_TOKEN,
|
|
251
|
+
"GITHUB_TOKEN",
|
|
252
|
+
"GH_TOKEN",
|
|
253
|
+
"CHANGELOGEN_TOKENS_GITHUB"
|
|
254
|
+
];
|
|
255
|
+
function githubTokenEnvLabel(tokenEnv) {
|
|
256
|
+
return tokenEnv ?? `${ENV.GITHUB_TOKEN} (or GITHUB_TOKEN, GH_TOKEN, CHANGELOGEN_TOKENS_GITHUB)`;
|
|
257
|
+
}
|
|
116
258
|
function resolveGitHubToken(tokenEnv) {
|
|
117
|
-
if (tokenEnv !== void 0)
|
|
118
|
-
|
|
119
|
-
return token !== void 0 && token.length > 0 ? token : void 0;
|
|
120
|
-
}
|
|
121
|
-
for (const name of [
|
|
122
|
-
"GITHUB_TOKEN",
|
|
123
|
-
"GH_TOKEN",
|
|
124
|
-
"CHANGELOGEN_TOKENS_GITHUB"
|
|
125
|
-
]) {
|
|
126
|
-
const token = process.env[name];
|
|
127
|
-
if (token !== void 0 && token.length > 0) return token;
|
|
128
|
-
}
|
|
259
|
+
if (tokenEnv !== void 0) return readEnv(tokenEnv);
|
|
260
|
+
return readEnvFirst(...GITHUB_TOKEN_FALLBACKS);
|
|
129
261
|
}
|
|
130
262
|
async function resolveGitHubRepo(cwd, source) {
|
|
131
|
-
const host = normalizeHost(source.host ??
|
|
132
|
-
const explicit = source.repo ??
|
|
263
|
+
const host = normalizeHost(source.host ?? readEnv(ENV.GITHUB_HOST) ?? readEnv("GITHUB_API_URL") ?? "github.com");
|
|
264
|
+
const explicit = source.repo ?? readEnvFirst(ENV.GITHUB_REPOSITORY, "GITHUB_REPOSITORY");
|
|
133
265
|
if (explicit !== void 0 && explicit.length > 0) return {
|
|
134
266
|
host,
|
|
135
267
|
repo: explicit
|
|
@@ -139,7 +271,7 @@ async function resolveGitHubRepo(cwd, source) {
|
|
|
139
271
|
host: normalizeHost(resolved.domain ?? host),
|
|
140
272
|
repo: resolved.repo
|
|
141
273
|
};
|
|
142
|
-
throw new ReleaseError("GITHUB_RELEASE_FAILED",
|
|
274
|
+
throw new ReleaseError("GITHUB_RELEASE_FAILED", `Set github.repo or ${ENV.GITHUB_REPOSITORY} (or GITHUB_REPOSITORY) to create a GitHub release.`);
|
|
143
275
|
}
|
|
144
276
|
async function githubFetch(options, path, init) {
|
|
145
277
|
const url = `${apiBase(options.host)}/repos/${encodeRepoPath(options.repo)}${path}`;
|
|
@@ -201,6 +333,20 @@ async function createGitHubRelease(options) {
|
|
|
201
333
|
}
|
|
202
334
|
//#endregion
|
|
203
335
|
//#region src/gitlab.ts
|
|
336
|
+
const GITLAB_TOKEN_FALLBACKS = [ENV.GITLAB_TOKEN, "GITLAB_TOKEN"];
|
|
337
|
+
function gitLabTokenEnvLabel(tokenEnv) {
|
|
338
|
+
return tokenEnv ?? `${ENV.GITLAB_TOKEN} (or GITLAB_TOKEN)`;
|
|
339
|
+
}
|
|
340
|
+
function resolveGitLabToken(tokenEnv) {
|
|
341
|
+
if (tokenEnv !== void 0) return readEnv(tokenEnv);
|
|
342
|
+
return readEnvFirst(...GITLAB_TOKEN_FALLBACKS);
|
|
343
|
+
}
|
|
344
|
+
function resolveGitLabHost(host) {
|
|
345
|
+
return host ?? readEnv(ENV.GITLAB_HOST) ?? readEnv("GITLAB_HOST") ?? "https://gitlab.com";
|
|
346
|
+
}
|
|
347
|
+
function resolveGitLabProject(project) {
|
|
348
|
+
return project ?? readEnvFirst(ENV.GITLAB_PROJECT, "GITLAB_PROJECT");
|
|
349
|
+
}
|
|
204
350
|
function releaseNotes(changelog, tag) {
|
|
205
351
|
const lines = changelog.split("\n");
|
|
206
352
|
const heading = `## ${tag}`;
|
|
@@ -618,15 +764,31 @@ function isObject(value) {
|
|
|
618
764
|
function isString(value) {
|
|
619
765
|
return Object.prototype.toString.call(value) === "[object String]";
|
|
620
766
|
}
|
|
767
|
+
function requireRemote(cwd, remote, code, provider) {
|
|
768
|
+
if (remoteExists(cwd, remote)) return;
|
|
769
|
+
throw new ReleaseError(code, `${provider} release is configured with remote "${remote}", but that remote does not exist. Add it with: git remote add ${remote} <url>`);
|
|
770
|
+
}
|
|
771
|
+
function providerRemote(configured, primaryRemote) {
|
|
772
|
+
if (configured === void 0 || configured === primaryRemote) return void 0;
|
|
773
|
+
return configured;
|
|
774
|
+
}
|
|
775
|
+
function pushRelease(cwd, remote, branch, tag) {
|
|
776
|
+
git([
|
|
777
|
+
"push",
|
|
778
|
+
"--atomic",
|
|
779
|
+
remote,
|
|
780
|
+
`HEAD:${branch}`,
|
|
781
|
+
`refs/tags/${tag}`
|
|
782
|
+
], cwd);
|
|
783
|
+
}
|
|
621
784
|
function gitLabContext(config) {
|
|
622
785
|
if (config?.enabled !== true) throw new ReleaseError("GITLAB_RELEASE_FAILED", "Enable gitlab before creating a release.");
|
|
623
|
-
const
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
if (
|
|
627
|
-
if (project === void 0 || project.length === 0) throw new ReleaseError("GITLAB_RELEASE_FAILED", "Set gitlab.project or GITLAB_PROJECT to create a GitLab release.");
|
|
786
|
+
const token = resolveGitLabToken(config.tokenEnv);
|
|
787
|
+
const project = resolveGitLabProject(config.project);
|
|
788
|
+
if (token === void 0) throw new ReleaseError("GITLAB_RELEASE_FAILED", `Set ${gitLabTokenEnvLabel(config.tokenEnv)} to create a GitLab release.`);
|
|
789
|
+
if (project === void 0) throw new ReleaseError("GITLAB_RELEASE_FAILED", "Set gitlab.project or GENBUMPPUSH_GITLAB_PROJECT (or GITLAB_PROJECT) to create a GitLab release.");
|
|
628
790
|
const context = {
|
|
629
|
-
host: config.host
|
|
791
|
+
host: resolveGitLabHost(config.host),
|
|
630
792
|
project,
|
|
631
793
|
token
|
|
632
794
|
};
|
|
@@ -636,7 +798,7 @@ function gitLabContext(config) {
|
|
|
636
798
|
function resolveGitHubContext(config, cwd) {
|
|
637
799
|
if (config?.enabled !== true) throw new ReleaseError("GITHUB_RELEASE_FAILED", "Enable github before creating a release.");
|
|
638
800
|
const token = resolveGitHubToken(config.tokenEnv);
|
|
639
|
-
if (token === void 0) throw new ReleaseError("GITHUB_RELEASE_FAILED", `Set ${config.tokenEnv
|
|
801
|
+
if (token === void 0) throw new ReleaseError("GITHUB_RELEASE_FAILED", `Set ${githubTokenEnvLabel(config.tokenEnv)} to create a GitHub release.`);
|
|
640
802
|
return resolveGitHubRepo(cwd, {
|
|
641
803
|
host: config.host,
|
|
642
804
|
repo: config.repo
|
|
@@ -714,6 +876,82 @@ function packageVersion(cwd) {
|
|
|
714
876
|
if (!isObject(data) || !("version" in data) || !isString(data.version)) throw new ReleaseError("INVALID_PACKAGE", "package.json must contain a version string.");
|
|
715
877
|
return data.version;
|
|
716
878
|
}
|
|
879
|
+
/**
|
|
880
|
+
* Run a full release (or a dry run / provider retry) for one repository.
|
|
881
|
+
*
|
|
882
|
+
* Typical flow:
|
|
883
|
+
* 1. Load config and `.env`
|
|
884
|
+
* 2. Verify the worktree is a clean Git repo on a branch
|
|
885
|
+
* 3. Detect (or force) a release type from Conventional Commits
|
|
886
|
+
* 4. Confirm, run `before` hooks, update version files and the changelog
|
|
887
|
+
* 5. Commit, tag, and atomically push branch + tag (plus provider remotes when set)
|
|
888
|
+
* 6. Optionally create a GitHub/GitLab release, then run `after` hooks
|
|
889
|
+
*
|
|
890
|
+
* Version-file or commit failures restore files and the index. A failed tag
|
|
891
|
+
* or network push can leave the release commit locally so you can inspect
|
|
892
|
+
* and retry — see the returned {@link ReleaseResult} and any thrown
|
|
893
|
+
* {@link ReleaseError}.
|
|
894
|
+
*
|
|
895
|
+
* Prefer the `genbumppush` CLI for day-to-day use. Call this directly when
|
|
896
|
+
* embedding releases in a Node script or CI job that already builds
|
|
897
|
+
* {@link CliOptions}.
|
|
898
|
+
*
|
|
899
|
+
* @param options - Parsed CLI options. `cwd`, `dryRun`, `yes`, and `help` are required;
|
|
900
|
+
* the rest override config when set.
|
|
901
|
+
* @returns A summary of what happened. Never mutates when `dryRun` is `true`.
|
|
902
|
+
* @throws {@link ReleaseError} for expected failures (dirty worktree, existing tag,
|
|
903
|
+
* cancelled confirmation, Git/provider errors). Unexpected errors may also throw.
|
|
904
|
+
*
|
|
905
|
+
* @example Dry run in the current directory
|
|
906
|
+
* ```ts
|
|
907
|
+
* import { runRelease } from 'genbumppush';
|
|
908
|
+
*
|
|
909
|
+
* const result = await runRelease({
|
|
910
|
+
* cwd: process.cwd(),
|
|
911
|
+
* dryRun: true,
|
|
912
|
+
* yes: true,
|
|
913
|
+
* help: false,
|
|
914
|
+
* });
|
|
915
|
+
*
|
|
916
|
+
* console.log(result);
|
|
917
|
+
* // {
|
|
918
|
+
* // currentVersion: '1.2.3',
|
|
919
|
+
* // newVersion: '1.3.0',
|
|
920
|
+
* // releaseType: 'minor',
|
|
921
|
+
* // tag: 'v1.3.0',
|
|
922
|
+
* // pushed: false,
|
|
923
|
+
* // dryRun: true,
|
|
924
|
+
* // commitCount: 4
|
|
925
|
+
* // }
|
|
926
|
+
* ```
|
|
927
|
+
*
|
|
928
|
+
* @example Non-interactive patch release that stays local
|
|
929
|
+
* ```ts
|
|
930
|
+
* import { runRelease } from 'genbumppush';
|
|
931
|
+
*
|
|
932
|
+
* await runRelease({
|
|
933
|
+
* cwd: process.cwd(),
|
|
934
|
+
* dryRun: false,
|
|
935
|
+
* yes: true,
|
|
936
|
+
* help: false,
|
|
937
|
+
* release: 'patch',
|
|
938
|
+
* push: false,
|
|
939
|
+
* });
|
|
940
|
+
* ```
|
|
941
|
+
*
|
|
942
|
+
* @example Retry only the GitLab release after Git already succeeded
|
|
943
|
+
* ```ts
|
|
944
|
+
* import { runRelease } from 'genbumppush';
|
|
945
|
+
*
|
|
946
|
+
* await runRelease({
|
|
947
|
+
* cwd: process.cwd(),
|
|
948
|
+
* dryRun: false,
|
|
949
|
+
* yes: true,
|
|
950
|
+
* help: false,
|
|
951
|
+
* gitlabRetryTag: 'v1.3.0',
|
|
952
|
+
* });
|
|
953
|
+
* ```
|
|
954
|
+
*/
|
|
717
955
|
async function runRelease(options) {
|
|
718
956
|
const overrides = {};
|
|
719
957
|
if (options.release !== void 0) overrides.release = options.release;
|
|
@@ -725,7 +963,8 @@ async function runRelease(options) {
|
|
|
725
963
|
const currentVersion = packageVersion(cwd);
|
|
726
964
|
if (options.gitlabRetryTag !== void 0) {
|
|
727
965
|
const context = gitLabContext(config.gitlab);
|
|
728
|
-
const remote = config.git?.remote ?? "origin";
|
|
966
|
+
const remote = config.gitlab?.remote ?? config.git?.remote ?? "origin";
|
|
967
|
+
requireRemote(cwd, remote, "GITLAB_RELEASE_FAILED", "GitLab");
|
|
729
968
|
if (!remoteTagExists(cwd, remote, options.gitlabRetryTag)) throw new ReleaseError("GITLAB_RELEASE_FAILED", `Tag ${options.gitlabRetryTag} does not exist on ${remote}.`);
|
|
730
969
|
await publishGitLab(context, cwd, config, options.gitlabRetryTag, currentVersion);
|
|
731
970
|
return {
|
|
@@ -739,7 +978,8 @@ async function runRelease(options) {
|
|
|
739
978
|
}
|
|
740
979
|
if (options.githubRetryTag !== void 0) {
|
|
741
980
|
const context = await resolveGitHubContext(config.github, cwd);
|
|
742
|
-
const remote = config.git?.remote ?? "origin";
|
|
981
|
+
const remote = config.github?.remote ?? config.git?.remote ?? "origin";
|
|
982
|
+
requireRemote(cwd, remote, "GITHUB_RELEASE_FAILED", "GitHub");
|
|
743
983
|
if (!remoteTagExists(cwd, remote, options.githubRetryTag)) throw new ReleaseError("GITHUB_RELEASE_FAILED", `Tag ${options.githubRetryTag} does not exist on ${remote}.`);
|
|
744
984
|
await publishGitHub(context, cwd, config, options.githubRetryTag, currentVersion);
|
|
745
985
|
return {
|
|
@@ -787,14 +1027,20 @@ async function runRelease(options) {
|
|
|
787
1027
|
};
|
|
788
1028
|
}
|
|
789
1029
|
let gitlab;
|
|
1030
|
+
let gitlabRemote;
|
|
790
1031
|
if (config.gitlab?.enabled === true) {
|
|
791
1032
|
if (!push) throw new ReleaseError("GITLAB_RELEASE_FAILED", "GitLab release creation requires git.push to be enabled.");
|
|
792
1033
|
gitlab = gitLabContext(config.gitlab);
|
|
1034
|
+
gitlabRemote = providerRemote(config.gitlab.remote, config.git?.remote ?? "origin");
|
|
1035
|
+
if (gitlabRemote !== void 0) requireRemote(cwd, gitlabRemote, "GITLAB_RELEASE_FAILED", "GitLab");
|
|
793
1036
|
}
|
|
794
1037
|
let github;
|
|
1038
|
+
let githubRemote;
|
|
795
1039
|
if (config.github?.enabled === true) {
|
|
796
1040
|
if (!push) throw new ReleaseError("GITHUB_RELEASE_FAILED", "GitHub release creation requires git.push to be enabled.");
|
|
797
1041
|
github = await resolveGitHubContext(config.github, cwd);
|
|
1042
|
+
githubRemote = providerRemote(config.github.remote, config.git?.remote ?? "origin");
|
|
1043
|
+
if (githubRemote !== void 0) requireRemote(cwd, githubRemote, "GITHUB_RELEASE_FAILED", "GitHub");
|
|
798
1044
|
}
|
|
799
1045
|
if (!options.yes && !await confirm(`Create a ${releaseType} release?`)) throw new ReleaseError("CANCELLED", "Release cancelled.");
|
|
800
1046
|
const version = plannedVersion;
|
|
@@ -842,19 +1088,21 @@ async function runRelease(options) {
|
|
|
842
1088
|
if (config.git?.sign === true) tagArgs.push("-s");
|
|
843
1089
|
tagArgs.push(tag, "-m", render(config.git?.tagMessage ?? "v{{version}}", version));
|
|
844
1090
|
git(tagArgs, cwd);
|
|
845
|
-
if (push)
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
1091
|
+
if (push) {
|
|
1092
|
+
pushRelease(cwd, remote, branch, tag);
|
|
1093
|
+
const extraRemotes = [...new Set([gitlabRemote, githubRemote].filter((value) => value !== void 0))];
|
|
1094
|
+
for (const extraRemote of extraRemotes) try {
|
|
1095
|
+
pushRelease(cwd, extraRemote, branch, tag);
|
|
1096
|
+
} catch (error) {
|
|
1097
|
+
throw new ReleaseError(extraRemote === gitlabRemote ? "RELEASE_PUBLISHED_GITLAB_FAILED" : "RELEASE_PUBLISHED_GITHUB_FAILED", `Git release ${tag} was pushed to ${remote}, but push to ${extraRemote} failed. Fix that remote, then run: git push --atomic ${extraRemote} HEAD:${branch} refs/tags/${tag}`, { cause: error });
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
852
1100
|
let gitlabReleaseCreated = false;
|
|
853
1101
|
if (gitlab !== void 0) try {
|
|
854
1102
|
await publishGitLab(gitlab, cwd, config, tag, version);
|
|
855
1103
|
gitlabReleaseCreated = true;
|
|
856
1104
|
} catch (error) {
|
|
857
|
-
throw new ReleaseError("RELEASE_PUBLISHED_GITLAB_FAILED", `Git release ${tag} was pushed, but GitLab release creation failed. Retry with: genbumppush --retry-gitlab ${tag}`, { cause: error });
|
|
1105
|
+
throw new ReleaseError("RELEASE_PUBLISHED_GITLAB_FAILED", `Git release ${tag} was pushed, but GitLab release creation failed.${config.gitlab?.remote === void 0 ? " If GitLab is a separate remote from git.remote, set gitlab.remote so the tag is pushed there first." : ""} Retry with: genbumppush --retry-gitlab ${tag}`, { cause: error });
|
|
858
1106
|
}
|
|
859
1107
|
let githubReleaseCreated = false;
|
|
860
1108
|
if (github !== void 0) try {
|