rman 2.15.0 → 2.15.1

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.
@@ -286,7 +286,8 @@ function printApplied(result) {
286
286
  console.log(`${colors.green('tags')} ${release.map(label).join(', ')} ${colors.gray('(repository release)')}`);
287
287
  }
288
288
  console.log(result.pushed
289
- ? `${colors.green('push')} pushed, with tags`
289
+ ? `${colors.green('push')} pushed the branch` +
290
+ (result.tags.length ? ` and ${result.tags.map(t => t.name).join(', ')}, together` : '')
290
291
  : `${colors.gray('push')} ${colors.gray('not pushed - run with --push, or push it yourself')}`);
291
292
  }
292
293
  function printPlan(entries) {
package/constants.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.15.0';
1
+ export const version = '2.15.1';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rman",
3
3
  "description": "Repository manager",
4
- "version": "2.15.0",
4
+ "version": "2.15.1",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
@@ -254,8 +254,21 @@ export class VersionService extends Service {
254
254
  tagged.push({ name: releaseTag, created: true, release: true });
255
255
  }
256
256
  const pushed = !!options.push && bumped.length > 0;
257
- if (pushed)
258
- await git.push();
257
+ if (pushed) {
258
+ /** Every tag this release names, including one that was already there: it is this release's
259
+ * tag all the same, and a remote that lacks it is missing part of the release. */
260
+ const tags = tagged.map(t => t.name);
261
+ try {
262
+ await git.push({ tags });
263
+ }
264
+ catch (e) {
265
+ throw new Error(`${e.message}\n\n` +
266
+ ` Nothing was pushed - the branch and its tags go up together or not at all. Left behind ` +
267
+ `locally: ${commits.map(c => c.sha).join(', ') || 'no commit'}` +
268
+ (tags.length ? `, tagged ${tags.join(', ')}` : '') +
269
+ `.\n Once the cause is fixed, push them with: git push --atomic origin HEAD ${tags.join(' ')}`.trimEnd(), { cause: e });
270
+ }
271
+ }
259
272
  return { entries: plan, updated: bumped, commits, tags: tagged, pushed };
260
273
  }
261
274
  }
package/utils/git.d.ts CHANGED
@@ -121,11 +121,13 @@ export declare class GitHelper {
121
121
  * Throws if a tag with that name already exists - callers wanting idempotent tagging should
122
122
  * check `tagExists` first. */
123
123
  createTag(name: string, message?: string): Promise<void>;
124
- /** Pushes the current branch to `remote` (default `"origin"`), and its tags too unless
125
- * `options.tags` is `false`. */
124
+ /**
125
+ * Pushes the current branch to `remote` (default `"origin"`) together with `options.tags`, by
126
+ * name, in **one atomic push**: the branch and every tag go up, or none of them does.
127
+ */
126
128
  push(options?: {
127
129
  remote?: string;
128
- tags?: boolean;
130
+ tags?: readonly string[];
129
131
  }): Promise<void>;
130
132
  /** `git remote get-url <remote>` (default `"origin"`) - `undefined` when that remote isn't
131
133
  * configured at all, which is a perfectly normal state for a local-only repository. */
package/utils/git.js CHANGED
@@ -394,14 +394,24 @@ export class GitHelper {
394
394
  throw new Error(`Unable to create tag "${name}": ${e.message}`, { cause: e });
395
395
  }
396
396
  }
397
- /** Pushes the current branch to `remote` (default `"origin"`), and its tags too unless
398
- * `options.tags` is `false`. */
397
+ /**
398
+ * Pushes the current branch to `remote` (default `"origin"`) together with `options.tags`, by
399
+ * name, in **one atomic push**: the branch and every tag go up, or none of them does.
400
+ */
401
+ /* **Named tags, not `--tags`.** `git push <remote> --tags` sends every tag in the clone, so one
402
+ * stale tag from months ago - re-created locally, diverged from the remote's - failed every
403
+ * release from then on, and failed it *after* the new tag had already gone up. Measured on
404
+ * `postgrejs` with 2.15.0: `v3.13.0` pushed, `v3.10.0` rejected, exit 1, and in CI the publish
405
+ * step after it never ran.
406
+ *
407
+ * **One command, `--atomic`.** The branch used to go first and the tags second, so a failure in
408
+ * the second - GitHub's own `fatal error in commit_refs` did it to the same repository an hour
409
+ * earlier - left a pushed version bump with no tag, which a re-run cannot repair because it has
410
+ * nothing left to bump. Atomic, a failure leaves the remote exactly as it was. */
399
411
  async push(options) {
400
412
  const remote = options?.remote ?? 'origin';
401
413
  try {
402
- await execFileAsync('git', ['push', remote], { cwd: this.cwd });
403
- if (options?.tags !== false)
404
- await execFileAsync('git', ['push', remote, '--tags'], { cwd: this.cwd });
414
+ await execFileAsync('git', ['push', '--atomic', remote, 'HEAD', ...(options?.tags ?? [])], { cwd: this.cwd });
405
415
  }
406
416
  catch (e) {
407
417
  throw new Error(`Unable to push to "${remote}": ${e.message}`, { cause: e });