dw-sdlc 0.12.0 → 0.12.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.
@@ -119,12 +119,36 @@ function liveWorkers() {
119
119
  function workersText(workers) {
120
120
  return workers.map((worker) => `${worker.name} (${worker.status})`).join(", ");
121
121
  }
122
- /** The current branch's pull request and the Action the Ship script's decision gives it, or null. */
123
- function readShipped(root) {
122
+ /**
123
+ * The remote branch name `branch` tracks, when it differs from `branch`. Read from the config, so it holds
124
+ * after the remote deleted the branch.
125
+ */
126
+ function upstreamName(root, branch) {
127
+ const remote = asked(root, ["config", "--get", `branch.${branch}.remote`]);
128
+ const merge = asked(root, ["config", "--get", `branch.${branch}.merge`]);
129
+ if (remote === undefined || remote === "." || merge === undefined || !merge.startsWith("refs/heads/")) {
130
+ return undefined;
131
+ }
132
+ const name = merge.slice("refs/heads/".length);
133
+ return name === branch ? undefined : name;
134
+ }
135
+ /** Whether `pr`'s commits hold `head`, so the head is on GitHub whatever became of the upstream. */
136
+ function holds(pr, head) {
137
+ return head !== null && pr.commits.includes(head);
138
+ }
139
+ /**
140
+ * The current branch's pull request and the Action the Ship script's decision gives it, or null. The one
141
+ * found by the upstream's name counts only when it holds the head: a branch started from another tracks
142
+ * that branch until its first push.
143
+ */
144
+ function readShipped(root, branch, head) {
124
145
  // An invalid config layer resolves to ask and full, as in the Ship script.
125
146
  const policy = declaredPolicy(resolveConfig(root).config.pr);
126
- const pr = readBranch(policy.review.depth === "auto");
127
- return pr === null ? null : { pr, answer: decide(pr, now(), policy) };
147
+ const found = readBranch(policy.review.depth === "auto", head === null ? undefined : upstreamName(root, branch));
148
+ if (found === null || (found.byUpstream && !holds(found.pr, head))) {
149
+ return null;
150
+ }
151
+ return { pr: found.pr, answer: decide(found.pr, now(), policy) };
128
152
  }
129
153
  /**
130
154
  * The tracking refs, as `--remotes` names them, that the remote contradicts, which only a fetch would show:
@@ -302,8 +326,8 @@ function report(json) {
302
326
  const { branch, head } = read;
303
327
  // A detached HEAD names no branch for gh to look up, and a repository with no remote has no GitHub.
304
328
  const asksGh = branch !== null && remotesOf(root).length > 0;
305
- const shipped = asksGh ? readShipped(root) : null;
306
- const held = shipped !== null && head !== null && shipped.pr.commits.includes(head);
329
+ const shipped = asksGh ? readShipped(root, branch, head) : null;
330
+ const held = shipped !== null && holds(shipped.pr, head);
307
331
  const local = standing(root, read, shipped, held);
308
332
  // An open pull request can still take the head, and a commit-less repository has nothing to merge.
309
333
  const nothingToMerge = held || shipped?.pr.state === "OPEN" || head === null;
package/dist/pr/read.d.ts CHANGED
@@ -23,8 +23,14 @@ export declare function gh(args: string[]): {
23
23
  * `sized`, as `pr.review: auto` needs, the read sizes the changes a Review would read.
24
24
  */
25
25
  export declare function read(named: Named, command: string, sized: boolean): PrState;
26
- /** The state of the current branch's pull request, open or not, or null when gh finds none. */
27
- export declare function readBranch(sized: boolean): PrState | null;
26
+ /**
27
+ * The current branch's pull request, open or not, or null. gh tries `@{push}`, else the local name, which
28
+ * misses a branch pushed under another name; then `upstreamName` is asked, and `byUpstream` says so.
29
+ */
30
+ export declare function readBranch(sized: boolean, upstreamName: string | undefined): {
31
+ pr: PrState;
32
+ byUpstream: boolean;
33
+ } | null;
28
34
  /** The default branch of the repository gh resolves from the current directory. */
29
35
  export declare function defaultBranch(): string;
30
36
  /** The time the decision reads: DW_SDLC_NOW, an ISO 8601 time a test sets, else the system clock. */
package/dist/pr/read.js CHANGED
@@ -93,18 +93,33 @@ export function read(named, command, sized) {
93
93
  requireAuth();
94
94
  return query(targetOf(named, command), sized);
95
95
  }
96
- /** The state of the current branch's pull request, open or not, or null when gh finds none. */
97
- export function readBranch(sized) {
98
- requireAuth();
99
- const view = gh(["pr", "view", "--json", "url"]);
96
+ /** The pull request `gh pr view` resolves for the head branch `branch`, else the current one; null when none. */
97
+ function viewed(branch) {
98
+ const view = gh(["pr", "view", ...(branch === undefined ? [] : [branch]), "--json", "url"]);
100
99
  if (view.status === 0) {
101
- return query(parseOrCannotRun(() => parseView(view.stdout)), sized);
100
+ return parseOrCannotRun(() => parseView(view.stdout));
102
101
  }
103
102
  if (said(view.stderr).startsWith("no pull requests found")) {
104
103
  return null;
105
104
  }
106
105
  throw new CannotRun(`gh pr view failed: ${said(view.stderr)}`, REMEDIES.read);
107
106
  }
107
+ /**
108
+ * The current branch's pull request, open or not, or null. gh tries `@{push}`, else the local name, which
109
+ * misses a branch pushed under another name; then `upstreamName` is asked, and `byUpstream` says so.
110
+ */
111
+ export function readBranch(sized, upstreamName) {
112
+ requireAuth();
113
+ const own = viewed(undefined);
114
+ if (own !== null) {
115
+ return { pr: query(own, sized), byUpstream: false };
116
+ }
117
+ if (upstreamName === undefined) {
118
+ return null;
119
+ }
120
+ const found = viewed(upstreamName);
121
+ return found === null ? null : { pr: query(found, sized), byUpstream: true };
122
+ }
108
123
  /** The default branch of the repository gh resolves from the current directory. */
109
124
  export function defaultBranch() {
110
125
  const view = gh(["repo", "view", "--json", "defaultBranchRef"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dw-sdlc",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "The Gate and the agentic config of a repository: the dw-sdlc bin with the Gate, the setup, the doctor, the config check, the Sync and setup of References, and the Ship script that drives a pull request to merged; the resolver of the config's layers, its JSON Schema and the validator that reads it.",
5
5
  "license": "MIT",
6
6
  "repository": {