genbumppush 0.0.4 → 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 CHANGED
@@ -160,9 +160,11 @@ JSON has no comments and no `defineConfig` typing, so move to `genbumppush.confi
160
160
  | `github.enabled` | boolean | `false` | Create a GitHub release after push |
161
161
  | `github.host` | string | `github.com` | github.com or GHES host |
162
162
  | `github.repo` | string | env / remote | `owner/name` |
163
+ | `github.remote` | string | unset | Extra Git remote for dual-host GitHub |
163
164
  | `github.tokenEnv` | string | auto | Exact env var name (disables fallbacks) |
164
165
  | `github.releaseName` | string | tag | Release title template |
165
166
  | `gitlab.enabled` | boolean | `false` | Create a GitLab release after push |
167
+ | `gitlab.remote` | string | unset | Extra Git remote for dual-host GitLab |
166
168
 
167
169
  `{{version}}` is replaced in commit and tag templates. Hooks run through the shell in the repository directory; only use trusted configuration.
168
170
 
@@ -291,6 +293,24 @@ Without `tokenEnv`, GitLab tokens resolve from `GENBUMPPUSH_GITLAB_TOKEN`, then
291
293
  the matching `CHANGELOG.md` section as the release description. If `git.push` is false,
292
294
  the provider is rejected because GitLab cannot create a release for an unpublished tag.
293
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
+
294
314
  ```yaml
295
315
  release:
296
316
  image: node:20
@@ -309,19 +329,20 @@ Keep artifact publication and GitLab release creation in protected, tag-triggere
309
329
 
310
330
  ## Scenario guide
311
331
 
312
- | Scenario | Recommended setup |
313
- | ---------------------- | -------------------------------------------------- |
314
- | Local release | `npm run release`, confirm interactively |
315
- | CI release | `genbumppush --yes` with protected Git credentials |
316
- | Preview only | `genbumppush --dry-run --yes` |
317
- | Local commit/tag only | `genbumppush patch --no-push --yes` |
318
- | Nuxt or Node package | Default `package.json` adapter |
319
- | npm lockfile | Add `package-lock.json` explicitly |
320
- | Fixed-version monorepo | `recursive: true` |
321
- | Tauri | Explicit JSON, Cargo.toml, and Cargo.lock files |
322
- | Custom VERSION file | Add only if the old version occurs once |
323
- | GitHub/npm publication | Push `v*`; let workflows publish |
324
- | GitLab release | Run downstream jobs on `$CI_COMMIT_TAG` |
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) |
325
346
 
326
347
  ## Troubleshooting
327
348
 
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as ReleaseError, t as runRelease } from "./release-gwyqzR0C.mjs";
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
5
  /**
package/dist/index.d.mts CHANGED
@@ -117,6 +117,19 @@ type GitLabOptions = {
117
117
  * @example `'group/subgroup/project'`
118
118
  */
119
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;
120
133
  /**
121
134
  * Exact environment variable name to read the token from.
122
135
  * When set, the usual fallback chain is skipped.
@@ -154,6 +167,17 @@ type GitHubOptions = {
154
167
  * @example `'xcvzmoon/genbumppush'`
155
168
  */
156
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;
157
181
  /**
158
182
  * Exact environment variable name to read the token from.
159
183
  * When set, the usual fallback chain is skipped.
@@ -446,7 +470,7 @@ export declare class ReleaseError extends Error {
446
470
  * 2. Verify the worktree is a clean Git repo on a branch
447
471
  * 3. Detect (or force) a release type from Conventional Commits
448
472
  * 4. Confirm, run `before` hooks, update version files and the changelog
449
- * 5. Commit, tag, and atomically push branch + tag
473
+ * 5. Commit, tag, and atomically push branch + tag (plus provider remotes when set)
450
474
  * 6. Optionally create a GitHub/GitLab release, then run `after` hooks
451
475
  *
452
476
  * Version-file or commit failures restore files and the index. A failed tag
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-gwyqzR0C.mjs";
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 };
@@ -188,6 +188,16 @@ function isGitRepository(cwd) {
188
188
  if (result.error || result.status !== 0 || typeof result.stdout !== "string") return false;
189
189
  return result.stdout.trim() === "true";
190
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
+ }
191
201
  function remoteTagExists(cwd, remote, tag) {
192
202
  return git([
193
203
  "ls-remote",
@@ -754,6 +764,23 @@ function isObject(value) {
754
764
  function isString(value) {
755
765
  return Object.prototype.toString.call(value) === "[object String]";
756
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
+ }
757
784
  function gitLabContext(config) {
758
785
  if (config?.enabled !== true) throw new ReleaseError("GITLAB_RELEASE_FAILED", "Enable gitlab before creating a release.");
759
786
  const token = resolveGitLabToken(config.tokenEnv);
@@ -857,7 +884,7 @@ function packageVersion(cwd) {
857
884
  * 2. Verify the worktree is a clean Git repo on a branch
858
885
  * 3. Detect (or force) a release type from Conventional Commits
859
886
  * 4. Confirm, run `before` hooks, update version files and the changelog
860
- * 5. Commit, tag, and atomically push branch + tag
887
+ * 5. Commit, tag, and atomically push branch + tag (plus provider remotes when set)
861
888
  * 6. Optionally create a GitHub/GitLab release, then run `after` hooks
862
889
  *
863
890
  * Version-file or commit failures restore files and the index. A failed tag
@@ -936,7 +963,8 @@ async function runRelease(options) {
936
963
  const currentVersion = packageVersion(cwd);
937
964
  if (options.gitlabRetryTag !== void 0) {
938
965
  const context = gitLabContext(config.gitlab);
939
- const remote = config.git?.remote ?? "origin";
966
+ const remote = config.gitlab?.remote ?? config.git?.remote ?? "origin";
967
+ requireRemote(cwd, remote, "GITLAB_RELEASE_FAILED", "GitLab");
940
968
  if (!remoteTagExists(cwd, remote, options.gitlabRetryTag)) throw new ReleaseError("GITLAB_RELEASE_FAILED", `Tag ${options.gitlabRetryTag} does not exist on ${remote}.`);
941
969
  await publishGitLab(context, cwd, config, options.gitlabRetryTag, currentVersion);
942
970
  return {
@@ -950,7 +978,8 @@ async function runRelease(options) {
950
978
  }
951
979
  if (options.githubRetryTag !== void 0) {
952
980
  const context = await resolveGitHubContext(config.github, cwd);
953
- const remote = config.git?.remote ?? "origin";
981
+ const remote = config.github?.remote ?? config.git?.remote ?? "origin";
982
+ requireRemote(cwd, remote, "GITHUB_RELEASE_FAILED", "GitHub");
954
983
  if (!remoteTagExists(cwd, remote, options.githubRetryTag)) throw new ReleaseError("GITHUB_RELEASE_FAILED", `Tag ${options.githubRetryTag} does not exist on ${remote}.`);
955
984
  await publishGitHub(context, cwd, config, options.githubRetryTag, currentVersion);
956
985
  return {
@@ -998,14 +1027,20 @@ async function runRelease(options) {
998
1027
  };
999
1028
  }
1000
1029
  let gitlab;
1030
+ let gitlabRemote;
1001
1031
  if (config.gitlab?.enabled === true) {
1002
1032
  if (!push) throw new ReleaseError("GITLAB_RELEASE_FAILED", "GitLab release creation requires git.push to be enabled.");
1003
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");
1004
1036
  }
1005
1037
  let github;
1038
+ let githubRemote;
1006
1039
  if (config.github?.enabled === true) {
1007
1040
  if (!push) throw new ReleaseError("GITHUB_RELEASE_FAILED", "GitHub release creation requires git.push to be enabled.");
1008
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");
1009
1044
  }
1010
1045
  if (!options.yes && !await confirm(`Create a ${releaseType} release?`)) throw new ReleaseError("CANCELLED", "Release cancelled.");
1011
1046
  const version = plannedVersion;
@@ -1053,19 +1088,21 @@ async function runRelease(options) {
1053
1088
  if (config.git?.sign === true) tagArgs.push("-s");
1054
1089
  tagArgs.push(tag, "-m", render(config.git?.tagMessage ?? "v{{version}}", version));
1055
1090
  git(tagArgs, cwd);
1056
- if (push) git([
1057
- "push",
1058
- "--atomic",
1059
- remote,
1060
- `HEAD:${branch}`,
1061
- `refs/tags/${tag}`
1062
- ], cwd);
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
+ }
1063
1100
  let gitlabReleaseCreated = false;
1064
1101
  if (gitlab !== void 0) try {
1065
1102
  await publishGitLab(gitlab, cwd, config, tag, version);
1066
1103
  gitlabReleaseCreated = true;
1067
1104
  } catch (error) {
1068
- 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 });
1069
1106
  }
1070
1107
  let githubReleaseCreated = false;
1071
1108
  if (github !== void 0) try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "genbumppush",
3
3
  "description": "Generate changelog, bump version, then push",
4
- "version": "0.0.4",
4
+ "version": "0.0.5",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "license": "MIT",