Github PR support#1555
Draft
gilescope wants to merge 24 commits into
Draft
Conversation
Adds common.Type (Issue | PR) with zero-value IssueType so existing bugs deserialize without migration. Extends common.Status with MergedStatus and DraftStatus for upcoming PR ops (merged / ready-to- review transitions). Signed-off-by: Giles Cope <gilescope@gmail.com>
Snapshot gains a Kind discriminator (Issue | PR), immutable BaseRef /
HeadRef set on Create, a mutable HeadCommit, a MergeCommit populated by
the merge transition, and a Reviews slice. Kind's zero value is
IssueType, so existing serialised bugs decode as issues without
migration; the wire key is "kind" to avoid colliding with OpBase.Type.
New ops gated on Kind == PRType:
* UpdateHeadOperation — records that the PR's head branch advanced,
capturing previous/new commit hashes for the timeline.
* AddReviewOperation — a PR review verdict (approved /
changes-requested / commented) against a specific head commit.
* AddReviewCommentOperation — line-anchored review comment pinned to
(commit, path, start..end line), with optional ReplyTo for threads.
Anchors are immutable so comments don't rot when the branch moves.
SetStatusOperation now accepts MergedStatus (requires merge_commit) and
DraftStatus; CreateOperation accepts Draft=true to start a PR in draft.
Merge / MarkReady convenience functions wrap the new transitions.
Signed-off-by: Giles Cope <gilescope@gmail.com>
BugExcerpt carries Kind plus the PR-only fields (BaseRef, HeadRef, HeadCommit, MergeCommit, LenReviews) so the cache can filter and sort PRs without loading full entities. Gob-serialized old caches decode correctly: missing fields become zero values, meaning Kind=IssueType and empty refs — exactly what a pre-PR bug would look like. Filter.KindFilter and Matcher.Kind wire the new query.Filters.Kind into the existing OR-match pipeline. BugCache gains Merge / MarkReady / MergeRaw, and RepoCacheBug gains NewPR / NewPRRaw so callers can author pull-requests as first-class entities. Signed-off-by: Giles Cope <gilescope@gmail.com>
Filters gains a Kind []common.Type. The parser accepts either the "kind" or "type" qualifier (kind:pr, kind:issue, type:pr), and the existing status qualifier now recognises "merged" and "draft" via the 4-state common.Status. Parser tests cover both qualifiers plus an unknown-kind rejection. Signed-off-by: Giles Cope <gilescope@gmail.com>
gqlgen's autobind greedily mapped GraphQL's built-in __Type introspection node to common.Type, breaking the generated resolvers. Renaming the Go type to common.Kind (with IssueKind / PRKind values) removes the collision. The GraphQL enum stays named BugKind and now binds cleanly to common.Kind. Also renames snapshot/excerpt field Type->Kind for consistency. The wire JSON tag remains "kind", so on-disk data is unchanged. Signed-off-by: Giles Cope <gilescope@gmail.com>
Schema extensions:
* Bug.kind discriminator (BugKind enum = ISSUE | PR)
* Bug.baseRef / headRef / headCommit / mergeCommit (null for issues)
* Bug.reviews + new Review and ReviewComment types
* Status enum gains MERGED and DRAFT
* BugCreateOperation exposes kind + base/head refs + draft
* BugSetStatusOperation exposes mergeCommit
* New operation types: BugUpdateHeadOperation, BugAddReviewOperation,
BugAddReviewCommentOperation
* Corresponding timeline items
BugWrapper interface grows Kind, BaseRef, HeadRef, HeadCommit,
MergeCommit, Reviews (lazy + loaded implementations). gqlgen.yml
binds BugKind -> common.Kind and ReviewState -> bug.ReviewState.
New resolvers stub Author/CreatedAt for Review + ReviewComment and
Author for each new operation / timeline item type.
Regenerated graph/*.generated.go via go generate ./...
Signed-off-by: Giles Cope <gilescope@gmail.com>
git bug new --pr --base REF --head REF [--head-commit HASH] [--draft]
-t title -m body
Creates a pull-request. When --head-commit is omitted, git bug resolves
the tip of --head in the working repo.
git bug status merge BUG_ID --commit HASH
Marks a PR as merged and records the merge commit. Rejected for issues.
git bug status ready BUG_ID
Transitions a draft PR to open. Rejected for non-draft / non-PR.
Verified end-to-end: creating a PR, filtering kind:pr vs kind:issue,
merging a PR, and rejecting merge of an issue all behave as expected.
Signed-off-by: Giles Cope <gilescope@gmail.com>
The import now makes a second pass over the repo's pullRequests connection after exhausting the issues one. Each PR becomes a bug created via NewPRRaw, carrying baseRef / headRef / headCommit / draft from the GitHub payload plus the usual origin metadata. Terminal PR states are materialised at create time: if Merged, a MergeOperation records the merge commit; if Closed-without-merge, a SetStatus close op fires. Live timeline items are replayed too: MergedEvent -> Merge, ReadyForReviewEvent -> Open (draft->open), ConvertToDraftEvent -> ignored (no SetDraft op yet). Issue-shared timeline items (IssueComment, LabeledEvent, RenamedTitleEvent, ...) reuse the existing ensureTimelineItem handler via a shim. Review threads are deliberately out of scope for v1; the data model already supports them via AddReviewOperation / AddReviewCommentOperation, so that's a later commit. Adds a new mocked fixture TestGithubPRImport covering merged / draft / closed-without-merge; the existing issue-only integration test gets an empty-PR-query expectation so the second pass is a no-op there. Signed-off-by: Giles Cope <gilescope@gmail.com>
Flips the Pull-request row from X to ~ for Core / CLI / WebUI (WebUI works because PRs ride the existing Bug list/detail; no PR-specific UI chrome yet). Adds a dedicated "PR support (import only)" table with the subset of features covered by the v1 GitHub import: create / comments / merge state / draft state. Reviews and review comments remain X. Also documents the v1 CLI surface (--pr on new, status merge, status ready, kind:pr filter) and the GraphQL additions. Fixes pre-existing heading-level skip (H2 -> H4 -> H3) caught by markdownlint while at it. Signed-off-by: Giles Cope <gilescope@gmail.com>
Extends the PR import to replay PullRequestReview timeline items as AddReviewOperation, plus the first N (NumReviewComments=50) inline PullRequestReviewComments as AddReviewCommentOperation anchored to (commit hash, path, line). GitHub review-state mapping: APPROVED -> bug.ReviewApproved CHANGES_REQUESTED -> bug.ReviewChangesRequested COMMENTED/PENDING/ -> bug.ReviewCommented DISMISSED Outdated review comments (GitHub reports line=0 when the anchor no longer exists in the current diff) are skipped silently. Reply-to parent lookups fall back to a top-level comment when the parent isn't present in the import. BugCache gains AddReview / AddReviewRaw / AddReviewRawWithId and AddReviewComment / AddReviewCommentRaw. core.ImportResult gains ImportEventReview and ImportEventReviewComment with their constructors. A new integration test TestGithubPRReviewImport covers the full chain: approved review with a single-line review comment, both imported as PR-only operations on a bug with Kind=PR. Signed-off-by: Giles Cope <gilescope@gmail.com>
Flips reviews to done and review comments to partial (capped at 50 per review in v1). Signed-off-by: Giles Cope <gilescope@gmail.com>
When a PR review carries more than NumReviewComments inline comments, the inline batch reports PageInfo.HasNextPage=true. The importer now follows the cursor via a new prReviewCommentsQuery (node(id:review) on PullRequestReview, selecting comments(first,after)) and keeps importing until HasNextPage is false. New mediator method importMediator.QueryReviewComments(ctx, reviewId, cursor) returns a (nodes, nextCursor, hasNext) triple; ensureReview loops until exhausted. Integration test TestGithubPRReviewCommentsPagination covers the two- page case: one inline comment (HasNextPage=true) + one paged comment, both end up on the review with body matching. Signed-off-by: Giles Cope <gilescope@gmail.com>
Signed-off-by: Giles Cope <gilescope@gmail.com>
BugRow shows kind-aware status icons: issues keep the open/closed
circle pair; PRs get a merge-type glyph for open, a purple call-merge
for merged, and an edit pencil for draft.
Bug detail gains:
* PrInfo panel (shown only for kind=PR) summarising base <- head
refs, current head commit, and the merge commit when merged.
* Reviews section rendering each PR review with state badge
(APPROVED=green, CHANGES_REQUESTED=red, COMMENTED=grey), commit
anchor, body, and any line-anchored review comments with file /
line metadata.
SetStatus timeline item recognises the MERGED and DRAFT verbs so
merge / draft transitions render with a sensible past-tense phrase.
Fragments extended: BugRow adds kind; Bug adds kind + baseRef +
headRef + headCommit + mergeCommit + reviews (with nested comments).
TypeScript typecheck green via npx tsc --noEmit.
Signed-off-by: Giles Cope <gilescope@gmail.com>
v1 PR export is update-only: creating a pull-request from git-bug is
skipped with a NewExportNothing because it requires pushing the head
branch to GitHub first (out of scope for the bridge). An attempt to
export-create a PR surfaces a clear message pointing the user to open
it on GitHub and re-import.
For PRs that are already imported (metadata[github-id] present),
existing update paths now route to PR-specific GraphQL mutations:
* EditCreateComment (body edit) -> updatePullRequest.body
* SetTitle -> updatePullRequest.title
* SetStatus OPEN/CLOSED -> updatePullRequest.state
* SetStatus MERGED -> skipped (merging must happen on
GitHub; status then re-imports)
* SetStatus DRAFT -> skipped in v1
* UpdateHead / AddReview /
AddReviewComment -> skipped in v1 (no direct mapping
for the head-commit tracking op,
and write paths for review threads
are a later commit)
* LabelChange / AddComment -> unchanged; GitHub's labelable and
addComment mutations accept PR
node ids, so the existing issue
path covers them.
Adds updatePullRequestMutation + updateGithubPullRequestStatus / Body /
Title helpers using githubv4.UpdatePullRequestInput.
Signed-off-by: Giles Cope <gilescope@gmail.com>
Removes the v1 "cannot create a new pull-request via export" bail-out. When exportBug finds a git-bug entity with Kind=PR that has no github-id metadata yet, it now issues a createPullRequest mutation with RepositoryID + BaseRefName + HeadRefName + Title + Body + Draft from the CreateOperation. GitHub requires the head branch to already exist on its side — git-bug does not push branches. If the branch is missing on the remote, the createPullRequest call returns a clear GitHub error that surfaces via NewExportError; the user pushes the branch and re-runs `git bug push`. The base branch (usually main/master) is assumed to exist. Adds createPullRequestMutation and createGithubPullRequest helper. The helper strips the `refs/heads/` prefix from base/head refs since GitHub's createPullRequest expects short branch names. CLI --pr flag help now calls out the branch-must-be-pushed requirement. Signed-off-by: Giles Cope <gilescope@gmail.com>
CLI additions under `git bug bug review`:
* review - list reviews + anchored comments on a PR
* review add BUG_ID - record a verdict (--approve / --request-changes /
--comment) with optional body and commit anchor
* review comment REV_ID - attach a line-anchored comment to a review, with
--path / --line / --end-line / --reply-to;
replying to a parent inherits the anchor.
Cache helpers (BugCache.AddReview / AddReviewComment) already existed for
the import path; this commit wires the CLI through them.
Export on the GitHub bridge:
* AddReviewOperation -> addPullRequestReview mutation; maps
bug.ReviewState to PullRequestReviewEvent
(APPROVED/CHANGES_REQUESTED/COMMENTED).
* AddReviewCommentOperation -> for replies (ReplyTo set) only, via
addPullRequestReviewComment(inReplyTo:);
standalone new threads are skipped with a
clear message and should be created as
inline comments on the review itself — a
look-ahead bundle is a follow-up.
* UpdateHeadOperation -> not exported (the user's `git push` is the
source of truth for branch tips).
New core ExportEvent types ExportEventReview / ExportEventReviewComment and
NewExportReview / NewExportReviewComment constructors.
Signed-off-by: Giles Cope <gilescope@gmail.com>
Signed-off-by: Giles Cope <gilescope@gmail.com>
… config man pages are refreshed by go generate; they are not markdown-lint targets so they commit cleanly. The .markdownlint-cli2.jsonc file is a local config that exempts doc/md/ from MD010 / MD040 / MD012 for tools that auto-discover it (the global pre-commit hook uses an explicit --config path and so does not pick it up). Signed-off-by: Giles Cope <gilescope@gmail.com>
Bug GraphQL type gains originUrl: String — the external-tracker URL stored on the CreateOp's metadata by the bridge (github-url / gitlab-url / jira-url / launchpad-url). The BugWrapper lazy + loaded implementations probe those keys in order. WebUI list: each row now shows the bridge's numeric id (git-bug#1459) parsed from the URL path, rendered as a link that opens the external tracker in a new tab, next to the git-bug humanId. Click doesn't navigate to the local bug page (stopPropagation on the external link). WebUI detail: the bug title area shows the same #NNN link next to the title, letting reviewers jump to the upstream page with one click. Regenerated gqlgen code + webui .generated.tsx for the new field. Signed-off-by: Giles Cope <gilescope@gmail.com>
…gration
git-bug webui gains --repo PATH (repeatable) and --root DIR (scan for
<org>/<repo> siblings). Each extra repo is registered in the existing
MultiRepoCache with name "<org>/<repo>". The cwd repo still serves as
the default, and is de-duplicated from the scan so lockfile contention
is avoided.
Request routing: a new X-Repo-Name HTTP header is parsed by a mux
middleware into the request context; the Repository resolver falls
back to that when no ref is supplied, letting every existing GraphQL
query (which writes `repository { ... }`) scope automatically. This
avoids touching ~12 .graphql files and their generated siblings.
WebUI:
* Apollo setContext sets X-Repo-Name from a mutable module-level var
that RepoContextBinder updates from the URL (/r/:repoName/*).
* New /r/:repoName/* route mounts the usual list/detail/new pages
with the header bound. Slashes in repo names are URL-encoded.
* Single-repo routes stay intact. Root "/" adapts: multi-repo mode
shows a ReposLanding page listing registered repos with counts;
single-repo mode shows the bug list as before.
* Header grows a "Repositories" link and a RepoPicker dropdown
(hidden in single-repo mode).
* Multi-repo forces --read-only in v1 — auth middleware uses a
single identity and authoring across N repos needs per-repo
identity selection, deferred.
Bleve resilience: openBleveIndex now detects stale-but-incompatible
indexes (bleve.Open fails and index_meta.json is present) and wipes the
path before bleve.New. This covers the migration case where the
existing on-disk store is from an older bleve version (e.g. moltdb or
upsidedown) and the new version treats the "store" path as a directory
only. The rebuild is automatic on first access; git refs (source of
truth) are untouched.
Verified against 149 midnightntwrk+shieldedtech repos: all register
cleanly after the new tolerant open path; X-Repo-Name routing returns
the expected bugs for a probed repo (example-bboard → 417 PRs).
Signed-off-by: Giles Cope <gilescope@gmail.com>
Adds a /search route that aggregates matches across every registered
repository using the existing repositories { nodes { allBugs(query) } }
shape. gqlgen resolves allBugs concurrently per repo (each owns its own
bleve index), so 150 repos return in roughly one search's wall time.
Results are grouped by repo with hit counts, empty repos hidden, and
each row links straight to /r/<repo>/bug/<id>. A "view all" link per
repo opens the full repo list filtered by the same query.
Query DSL gains two repo-level selectors:
* repo:<org>/<name> — restrict to that repo
* org:<org> — restrict to all repos under that org
These are parsed server-side into query.Filters.Repo / .Org (no-op
inside a single-repo query since the scope is already pinned). The
search page extracts them client-side to filter which repositories
render — matching happens before the fan-out view is built so it also
trims render cost for big result sets.
UI additions:
* Header GlobalSearch — a TextField in the app bar, always visible.
Enter navigates to /search?q=<value>. When mounted under /r/:name/
it pre-fills `repo:<org>/<name> status:open` so users see the
current scope and can remove it for a broader search.
* New SearchPage + SearchQuery fragment generating SearchQuery.gen.
Scopes + tokens are merged with the DSL: repo:/org: pull out of the
query before it's passed server-side, so the remaining portion (e.g.
"status:open label:bug authN") runs unchanged as the existing query.
Signed-off-by: Giles Cope <gilescope@gmail.com>
Backend adds a new /sync endpoint wired into the webui router:
GET /sync — current status (running, done/total, current repo,
cumulative imported bugs + identities, per-repo errors)
POST /sync — start a background run; 409 if one is already in flight
The handler walks MultiRepoCache.AllRepos() in sorted order, loads each
repo's DefaultBridge (repos without a configured bridge are skipped
silently, not errored), and drains its ImportAll channel. Bugs and
identities are counted; per-repo errors are collected into a map that
the UI surfaces in the button's tooltip.
UI adds a Sync button in the header that posts to /sync, then polls
GET /sync every 2s while running. The label switches between "Sync"
and "<done>/<total>", the spinner confirms activity, and on completion
the Apollo cache is reset so newly-imported rows show up without a
hard page refresh. Errors surface in the tooltip as "<N> errors".
Single-repo mode works too (MultiRepoCache has one registered repo).
Signed-off-by: Giles Cope <gilescope@gmail.com>
Signed-off-by: Giles Cope <gilescope@gmail.com>
Contributor
|
While that could be interesting for if/when we implement PR support, imho it really should be a separate entity (that is, not retrofitted into Before any token are spent on implementation, we really need to come to an agreement on:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Claude did this. Feel free to take it / parts / or pass.
Closes #373 (feat: support for pull requests).
PR support itself doesn't have any philosophical questions, but CI for a PR does. Linux doesn't have blocking CI gates — it has trusted approvers. I don't know what decentralised CI looks like though I suspect proof of reproducibility probably comes into it. Either way this is probably out of the scope of this PR — for now it has some links to centralised CI.
I thought this would be a good checkpoint as am going to pull in the project PR — will branch off here as I do that.
Running the webui across many repos
Two flags on
git-bug webuilet one process serve a whole directory of checked-out repos, so the UI becomes a single pane of glass across an org:--repo <path>(repeatable) — register an extra repo. Its display name comes from the parent dir plus basename (so/a/b/myorg/myreposhows up asmyorg/myrepo).--root <dir>— scan<root>/<org>/<repo>/.gitsiblings and register each one found. Only directories with git-bug refs are included; symlinks that escape<root>are rejected.Multi-repo mode forces
--read-only(per-repo identity selection isn't wired up yet).Example layout I use locally:
Serve the lot:
Or register individual repos:
The list view shows every registered repo; a repo-scoped search can be done via the
repo:/org:query filters, and the Sync button can either sync every registered repo or just the one you're looking at.