vouchington-tooling 0.8.0 → 0.8.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.
@@ -1,5 +1,5 @@
1
1
  export { createCommandRunner, runGh, runGit } from './exec.mts';
2
2
  export type { ExecFileText, RunTextCommand } from './exec.mts';
3
3
  export { getDiffAgainstBase } from './diff.mts';
4
- export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, resolveHeadBranch, } from './pr-create.mts';
4
+ export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './pr-create.mts';
5
5
  export type { AssertHeadPushedOptions, BuildGhPrCreateArgsOptions, CreatePullRequestExecutors, CreatePullRequestOptions, } from './pr-create.mts';
@@ -1,3 +1,3 @@
1
1
  export { createCommandRunner, runGh, runGit } from './exec.mjs';
2
2
  export { getDiffAgainstBase } from './diff.mjs';
3
- export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, resolveHeadBranch, } from './pr-create.mjs';
3
+ export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './pr-create.mjs';
@@ -7,6 +7,13 @@ export declare class DetachedHeadError extends Error {
7
7
  export declare class HeadNotPushedError extends Error {
8
8
  constructor(branch: string, remote: string);
9
9
  }
10
+ /**
11
+ * Thrown by {@link assertHeadPushed} when `branch` exists on `remote` but at a different commit
12
+ * than the local branch — creating the pull request now would silently use the stale remote tip.
13
+ */
14
+ export declare class HeadOutOfDateError extends Error {
15
+ constructor(branch: string, remote: string);
16
+ }
10
17
  /** Resolves the current branch via `git branch --show-current`, trimmed. */
11
18
  export declare function resolveHeadBranch(runGit: RunTextCommand): Promise<string>;
12
19
  export type AssertHeadPushedOptions = {
@@ -14,9 +21,16 @@ export type AssertHeadPushedOptions = {
14
21
  remote?: string;
15
22
  };
16
23
  /**
17
- * Confirms `branch` has a matching ref on `remote` (default `origin`) via
18
- * `git ls-remote --heads`. `ls-remote --heads` exits `0` with empty stdout when there is no
19
- * match, so this checks stdout rather than the exit code.
24
+ * Confirms `branch` has a matching, up-to-date ref on `remote` (default `origin`) via
25
+ * `git ls-remote --heads` and `git rev-parse`. Both use the full `refs/heads/<branch>` path
26
+ * rather than the bare branch name: `ls-remote`'s pattern otherwise matches any ref whose name
27
+ * ends with `branch` (not just an exact `refs/heads/<branch>`), and bare `rev-parse <branch>`
28
+ * resolves `refs/tags/<branch>` before `refs/heads/<branch>` when a same-named tag exists, either
29
+ * of which could compare the wrong commit. `ls-remote --heads` exits `0` with empty stdout when
30
+ * there is no match, so a missing branch is checked via stdout rather than the exit code. A
31
+ * remote ref that exists but points at a different commit than the local branch means local HEAD
32
+ * has commits the remote does not — that fails with {@link HeadOutOfDateError} rather than
33
+ * silently creating the pull request from the stale remote tip.
20
34
  */
21
35
  export declare function assertHeadPushed(runGit: RunTextCommand, { branch, remote }: AssertHeadPushedOptions): Promise<void>;
22
36
  export type BuildGhPrCreateArgsOptions = {
@@ -12,6 +12,16 @@ export class HeadNotPushedError extends Error {
12
12
  this.name = 'HeadNotPushedError';
13
13
  }
14
14
  }
15
+ /**
16
+ * Thrown by {@link assertHeadPushed} when `branch` exists on `remote` but at a different commit
17
+ * than the local branch — creating the pull request now would silently use the stale remote tip.
18
+ */
19
+ export class HeadOutOfDateError extends Error {
20
+ constructor(branch, remote) {
21
+ super(`branch "${branch}" on remote "${remote}" does not match the local branch — push the latest commits before creating the pull request`);
22
+ this.name = 'HeadOutOfDateError';
23
+ }
24
+ }
15
25
  /** Resolves the current branch via `git branch --show-current`, trimmed. */
16
26
  export async function resolveHeadBranch(runGit) {
17
27
  const branch = (await runGit(['branch', '--show-current'])).trim();
@@ -20,14 +30,26 @@ export async function resolveHeadBranch(runGit) {
20
30
  return branch;
21
31
  }
22
32
  /**
23
- * Confirms `branch` has a matching ref on `remote` (default `origin`) via
24
- * `git ls-remote --heads`. `ls-remote --heads` exits `0` with empty stdout when there is no
25
- * match, so this checks stdout rather than the exit code.
33
+ * Confirms `branch` has a matching, up-to-date ref on `remote` (default `origin`) via
34
+ * `git ls-remote --heads` and `git rev-parse`. Both use the full `refs/heads/<branch>` path
35
+ * rather than the bare branch name: `ls-remote`'s pattern otherwise matches any ref whose name
36
+ * ends with `branch` (not just an exact `refs/heads/<branch>`), and bare `rev-parse <branch>`
37
+ * resolves `refs/tags/<branch>` before `refs/heads/<branch>` when a same-named tag exists, either
38
+ * of which could compare the wrong commit. `ls-remote --heads` exits `0` with empty stdout when
39
+ * there is no match, so a missing branch is checked via stdout rather than the exit code. A
40
+ * remote ref that exists but points at a different commit than the local branch means local HEAD
41
+ * has commits the remote does not — that fails with {@link HeadOutOfDateError} rather than
42
+ * silently creating the pull request from the stale remote tip.
26
43
  */
27
44
  export async function assertHeadPushed(runGit, { branch, remote = 'origin' }) {
28
- const stdout = await runGit(['ls-remote', '--heads', remote, branch]);
29
- if (stdout.trim() === '')
45
+ const ref = `refs/heads/${branch}`;
46
+ const remoteLine = (await runGit(['ls-remote', '--heads', remote, ref])).trim();
47
+ if (remoteLine === '')
30
48
  throw new HeadNotPushedError(branch, remote);
49
+ const [remoteSha] = remoteLine.split(/\s+/);
50
+ const localSha = (await runGit(['rev-parse', ref])).trim();
51
+ if (localSha !== remoteSha)
52
+ throw new HeadOutOfDateError(branch, remote);
31
53
  }
32
54
  /** Pure argv builder for `gh pr create`. `head` is always passed explicitly (never omitted). */
33
55
  export function buildGhPrCreateArgs(options) {
package/dist/index.d.mts CHANGED
@@ -87,5 +87,5 @@ export type { DiagnosticReportLimitOptions, DiagnosticReportSummary, } from './v
87
87
  export { createCommandRunner, runGh, runGit } from './gh-cli/index.mts';
88
88
  export type { ExecFileText, RunTextCommand } from './gh-cli/index.mts';
89
89
  export { getDiffAgainstBase } from './gh-cli/index.mts';
90
- export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, resolveHeadBranch, } from './gh-cli/index.mts';
90
+ export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './gh-cli/index.mts';
91
91
  export type { AssertHeadPushedOptions, BuildGhPrCreateArgsOptions, CreatePullRequestExecutors, CreatePullRequestOptions, } from './gh-cli/index.mts';
package/dist/index.mjs CHANGED
@@ -49,4 +49,4 @@ export { validateResolvedPinDelta } from './swift-resolved-pin-delta/index.mjs';
49
49
  export { DEFAULT_MAX_DIAGNOSTIC_REPORTS, DEFAULT_MAX_FORMATTED_DIAGNOSTIC_REPORTS, formatDiagnosticReportSummaries, HARD_MAX_DIAGNOSTIC_REPORTS, readDiagnosticReportSummaries, summarizeDiagnosticReport, } from './vitest-diagnostics/index.mjs';
50
50
  export { createCommandRunner, runGh, runGit } from './gh-cli/index.mjs';
51
51
  export { getDiffAgainstBase } from './gh-cli/index.mjs';
52
- export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, resolveHeadBranch, } from './gh-cli/index.mjs';
52
+ export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './gh-cli/index.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vouchington-tooling",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Vouchington CLI and extractable tooling libraries.",
5
5
  "homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
6
6
  "bugs": {