Expand description
Issue ↔ PR ↔ branch link storage + GitHub API fetch (via gh CLI).
Storage lives in git branch config: branch.<name>.gwm-issue and
branch.<name>.gwm-pr. Issue numbers are auto-detected from the
<type>/#<N>-<slug> branch convention when no explicit override is set.
Fetch shells out to gh and parses its JSON output. The parsing functions
(parse_issue_json, parse_pr_json) are exposed publicly so tests can
cover the JSON contract without depending on a real gh binary.
Structs§
- Branch
Link - Resolved link for one branch: which issue (if any), which PR (if any), and where each number came from.
- Created
Issue - Created
Pr - Issue
Create Request - Issue
Status - PrCreate
Request - PrHead
- The slice of PR metadata
gwm reviewneeds to materialise a worktree: the head ref name (slug source), the author login (path component), and the base ref (diff base). Distinct fromPrStatusso the TUI’s status/CI path stays untouched. - PrStatus
Enums§
- CiState
- Overall CI outcome derived from a PR’s
statusCheckRollup(issue #299). A single ordered signal so the sidebar can render pass/fail/running at a glance instead of a bareN/Mcount. Priority is failing > running > passing: the most actionable state always wins, so a red check is never hidden behind an in-flight one. - Issue
State - Link
Source - Where the issue or PR number came from.
- PrState
Functions§
- apply_
detected_ pr - Stamp an auto-detected PR number onto
linkwhen no PR is already linked. Pure helper (issue #181): the caller supplies the detection result — typicallyfind_pr_for_branch(slug, branch).ok().flatten()— and this decides whether to apply it. - clear_
persisted_ detected_ pr - Drop a persisted auto-detection (issue #283). A no-op when no detected PR was stored. Used when a detection no longer holds (the branch’s PR went away) so a stale number doesn’t linger in the config.
- create_
issue - create_
milestone - Create one milestone upstream via
gh api -X POST. ReturnsOk(())— the caller already has the spec; we don’t bother parsing the response back into aRemoteMilestone. - create_
pr - Shell out to
gh pr createwith a body file already rendered bycrate::pr_templates::render_pr_body. Parses the URL printed by gh on success to extract the PR number. - delete_
label - Delete one label on the remote via
gh label delete --yes. Used bygwm labels push --prunefor labels declared on the remote but not in.gwm.toml. - delete_
milestone - Delete one milestone on the remote via
gh api -X DELETE. Used bygwm milestones push --prunefor milestones declared on the remote but not in.gwm.toml. - fetch_
issue - Run
gh issue view <n> --repo <slug> --json …and parse the result. - fetch_
issue_ with fetch_issuewith an explicitly resolvedghprogram path. Used by the TUI’s off-thread fetch (issue #217): the program is resolved on the main thread viagh_programand handed to the worker thread, so the thread never touchesGWM_GH/ the process environment concurrently with env-mutating callers.- fetch_
pr - Run
gh pr view <n> --repo <slug> --json …and parse the result. - fetch_
pr_ head - Run
gh pr view <n> --repo <slug> --json …and parse the head metadatagwm reviewneeds (author / head ref / base ref). Works for PRs in any state — open, draft, closed, or merged. - fetch_
pr_ with fetch_prwith an explicitly resolvedghprogram path — PR-side counterpart tofetch_issue_with, used by the TUI off-thread fetch (issue #217).- fetch_
remote_ labels - Run
gh label list --repo <slug> --json …and parse the result. Returns an empty vec when the remote has no labels (which is distinct from “gh not installed” — that surfaces asCommandFailed). - fetch_
remote_ milestones - Run
gh api repos/<slug>/milestones?state=alland parse the result. Returns an empty vec when the remote has no milestones. - find_
pr_ argv - Argv for
gh pr list --repo <slug> --head <branch> --state all --json number --limit 1. Extracted so the test suite can pin theghcontract without shelling out;find_pr_for_branchis the caller that actually invokes it.--state allis the load-bearing bit: a closed or merged PR for the branch is still detected (itsPrStateis resolved later viafetch_pr). - find_
pr_ for_ branch - Find the most recent PR opened from
branch(head ref) on the given repo, regardless of state. ReturnsOk(Some(N))if at least one PR exists (open, draft, closed, or merged —gh pr list --state all),Ok(None)otherwise. Callers that need state-aware filtering should pair this withfetch_prto inspectPrStateafterwards. - gh_
command_ line - Build the human-readable command line stored on the Command Logs
transcript (issue #226) for a
ghinvocation: the program’s file name (so aGWM_GH=/usr/bin/ghoverride still reads asgh issue view …rather than leaking the full path) followed by the resolved args. Kept pure andpubso its argv format is unit-testable without spawninggh(which CI runners do not have). - gh_
program - Resolve the
ghprogram to invoke:$GWM_GHwhen set (test / override hook), elseghonPATH. Read once on the calling thread so off-thread fetches can capture it without re-reading the environment. - issue_
url - Build the canonical GitHub URL for an issue, given the repo slug.
- label_
create_ argv - Argv for
gh label create <name> --color <hex> [--description <desc>] --force --repo <slug>. The--forceflag is the key contract bit: GitHub’s CLI uses it to mean “create OR update”, which is exactly whatgwm labels pushneeds (no separate “edit” call). WhendescriptionisNonewe omit the flag entirely rather than pass""— gh would otherwise wipe an existing description that the user didn’t intend to touch. - label_
delete_ argv - Argv for
gh label delete <name> --repo <slug> --yes. The--yesflag bypasses the interactive confirm prompt; without it gh blocks on a TTY read andgwm labels push --prunehangs. - label_
list_ argv - Argv for
gh label list --repo <slug> --json name,color,description --limit 1000. Extracted so the test suite can pin the contract; callers should preferfetch_remote_labelswhich actually shells out. - link_
issue - link_pr
- milestone_
create_ argv - Argv for
gh api -X POST repos/<slug>/milestones -f title=… [-f description=…] [-f due_on=…] -f state=…. Each optional field is omitted entirely when absent —ghwould otherwise wipe the existing remote value. - milestone_
delete_ argv - Argv for
gh api -X DELETE repos/<slug>/milestones/<number>.gh api -X DELETEis non-interactive by construction (no TTY confirm), so there’s no--yesequivalent to add. - milestone_
list_ argv - Argv for
gh api --paginate repos/<slug>/milestones?state=all&per_page=100. - milestone_
update_ argv - Argv for
gh api -X PATCH repos/<slug>/milestones/<number> -f …. Same omission rules asmilestone_create_argv: absent optionals are skipped so the remote value isn’t wiped. - parse_
issue_ json - parse_
labels_ json - Parse the JSON returned by
gh label list --json name,color,description. Exposed publicly so unit tests can cover the contract without shelling out. Two normalisations happen here so callers get a uniformly-shapedRemoteLabel: - parse_
milestones_ json - Parse the JSON returned by
gh api repos/:owner/:repo/milestones?state=all. Exposed publicly so unit tests can cover the contract without shelling out. Thestatefield is mapped to the strictMilestoneStateenum — an unknown value is a hard error rather than a silent third state on the diff side. - parse_
pr_ head_ json - Parse the JSON from
gh pr view <n> --json number,author,headRefName,baseRefName. Kept pure +pubso its shape is unit-testable without spawninggh. - parse_
pr_ json - parse_
pr_ list_ number - Parse the JSON array printed by
gh pr list --json number --limit 1, returning the first PR number if any. Exposed for unit tests so the parse contract is covered without aghshell-out. - persist_
detected_ pr - Persist an auto-detected PR number to its own branch-config key
(
gwm-pr-detected, issue #283), distinct from the explicitgwm-pr. This lets the no-fetch table read path surface the detected PR on every row without a per-rowghshell-out, while keeping the detected/explicit distinction the pane badge needs. An explicitgwm link --prstill wins inread_link. Re-detection overwrites the stored value and clears a cached title only when the detected number actually changed. - persist_
detected_ pr_ state - persist_
detected_ pr_ title - persist_
issue_ state - persist_
issue_ title - persist_
pr_ state - persist_
pr_ title - pr_url
- Build the canonical GitHub URL for a PR, given the repo slug.
- push_
label - Push one label upstream via
gh label create --force. ReturnsOk(())on success; the caller is responsible for tracking which label was created vs. updated (the diff already knows). - read_
link - Read the link for
branch. Explicit overrides win over branch-name auto-detect. - read_
link_ with_ pr_ detection - Resolve the link for
branchand, unless a PR is explicitly linked, auto-detect the branch’s PR from GitHub viagh(issue #181). The detected PR is markedLinkSource::Detected. - repo_
slug - Extract the
owner/reposlug from theoriginremote URL. Supports the two GitHub URL flavours:git@github.com:owner/repo(.git)?andhttps://github.com/owner/repo(.git)?. - unlink_
issue - unlink_
pr - update_
milestone - Update one milestone upstream via
gh api -X PATCH.numberis the GitHub-issued identifier carried throughMilestoneUpdate.