mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 03:38:32 +02:00
668cab60223e2c9b2de5ac5dc6289c925f25fdb0
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
668cab6022 |
feat(github): mirror PR CI checks and merge conflict status (MUL-2228) (#2632)
* feat(github): mirror PR CI checks and merge conflict status (MUL-2228)
Surface "checks passed/failed" and "conflicts/no conflicts" badges under
each linked PR on the issue page so users can judge readiness without
flipping over to GitHub. CI state is fed by check_suite webhooks
(GitHub Actions + apps using the Checks API; legacy status events are
out of scope for MVP); conflicts are read from pull_request.mergeable_state.
Data model:
* github_pull_request: add head_sha + mergeable_state
* github_pull_request_check_suite: per-suite rows keyed by (pr_id, suite_id)
* Aggregation done at query time, filtering by current head_sha so
late-arriving suites for a stale head can't contaminate the new head's
pending view; per-app latest suite chosen first so a single app firing
multiple suites isn't counted N times.
Webhook hardening:
* synchronize/opened/reopened/edited(base) explicitly clear mergeable_state
* single-row ordering protection on the check_suite upsert prevents a
late-delivered older event from overwriting a newer one
* check_suite.pull_requests is iterated; unknown PRs are logged and dropped
UI:
* PR row shows Checks + Conflicts badges; opaque mergeable values
(blocked/behind/unstable/...) render as no badge, not as conflicts.
* Terminal PR states (merged/closed) suppress the status row entirely.
Tests: * Pure unit coverage for derivePRMergeableState + aggregateChecksConclusion
* Webhook integration tests: multi-app aggregation, old-head ignore,
late-older-event ignore, synchronize clears mergeable_state
* Vitest coverage for pull-request-list badge rendering across CI/conflict
combinations and the legacy (null) fallback.
Co-authored-by: multica-agent <github@multica.ai>
* fix(github): scope check_suite PR lookup; preserve mergeable on metadata
Addresses code review on PR #2632.
1. check_suite handler now resolves the PR through the workspace-scoped
GetGitHubPullRequest query instead of GetGitHubPullRequestByRepoNumber.
The (workspace_id, repo_owner, repo_name, pr_number) tuple is the real
uniqueness key, so a bare (owner, repo, number) lookup could return a
stale row from another workspace and either land the suite on the wrong
PR or skip the right one when the installation ids drifted. The old
unscoped query is removed.
2. derivePRMergeableState now returns (value, clear) and the upsert SQL
distinguishes three cases: state-changing actions clear the column to
NULL, non-empty payloads write the value, and metadata events with an
empty payload preserve the existing column. Previously every empty
payload became NULL, so a labeled/assigned event silently wiped a
known clean/dirty verdict in violation of the RFC's "metadata empty
payload preserves" rule.
3. ListPullRequestsByIssue narrows to the issue's PR ids before running
the per-app check_suite aggregation, avoiding a full-table scan over
github_pull_request_check_suite when only a handful of rows belong to
the requested issue.
New helper test covers labeled+empty preserves; new integration test
verifies a metadata event after a known mergeable_state keeps the value.
Co-authored-by: multica-agent <github@multica.ai>
* feat(github): PR card layout v3 increment — stats + segmented progress bar
Replaces the row + badge layout under "Pull requests" on the issue
detail sidebar with a card that mirrors the GitHub PR summary look:
title, author/avatar, +N −M · K files diff stats, segmented progress
bar (failed → pending → passed, failure leftmost), and a one-line
status caption following an explicit priority pass-through.
Backend
- Migration 092: github_pull_request adds additions / deletions /
changed_files (INT NOT NULL DEFAULT 0). Zero defaults are what the
new frontend treats as "legacy backend — hide the stats row" so old
PR rows that pre-date this migration don't render "+0 −0 · 0 files".
- pull_request webhook handler reads stats off the top-level payload.
- ListPullRequestsByIssue now surfaces per-suite counts
(checks_passed / failed / pending) alongside the existing aggregate
conclusion, so the segmented bar reuses the already-computed counts
with no new aggregation.
Frontend (packages)
- core/github/pull-request-status.{ts,test.ts}: pure-function module
for the status-kind priority table and the segment derivation; 15
cases covered, includes the "all-zero → hide stats" guard.
- views/issues/components/pull-request-list.tsx: PullRequestCard plus
a compact-row fallback used when count > 4 (first 3 as cards, the
remainder collapsed behind a Show more toggle).
- i18n: new `pull_request_card_*` keys in en + zh-Hans.
Tests
- 12 component tests covering each rule of the priority table, the
legacy-zero stats fallback, and the collapse threshold.
- Reuse of the v3 webhook handler tests confirmed.
Verification
- pnpm typecheck + pnpm test green (60 test files, 536 tests).
- go build ./... + go vet ./... clean.
- 6 demo issues (DEV-2..DEV-7) screenshotted via Playwright; see the
PR comments for the visual check matrix.
Co-authored-by: multica-agent <github@multica.ai>
* fix(views): collapse PR cards at N>=4, not N>4
The card-vs-collapse threshold used `>` so 4 PRs slipped past it and
all rendered as full cards, contrary to RFC v3 (N >= 4 collapses to
3 cards + compact tail). Switch to `>=` and update the threshold-
boundary test to expect "Show 1 more".
Co-authored-by: multica-agent <github@multica.ai>
* fix(views): align PR sidebar rows with existing list style
Co-authored-by: multica-agent <github@multica.ai>
* fix(views): hide terminal PR status badges
Co-authored-by: multica-agent <github@multica.ai>
---------
Co-authored-by: multica-agent <github@multica.ai>
|
||
|
|
a02e58b488 |
fix(github): only auto-close issue after all linked PRs resolve (#2470)
* fix(github): only auto-close issue when all linked PRs have resolved Previously, the webhook handler unconditionally moved an issue to `done` as soon as a single linked PR was merged. If a second PR was also linked to the same issue and still open / draft, the issue would close before the work was actually finished. Add `CountOpenSiblingPullRequestsForIssue` and gate the auto-status transition on it: a merged PR advances its linked issues only when no sibling PR linked to the same issue is still in flight. Issues stay put while siblings are open or draft, and the merge that resolves the last in-flight PR is the one that closes the issue. Adds an integration test that opens two PRs against the same issue, merges the first, asserts the issue stays in_progress, then merges the second and asserts the issue advances to done. Co-authored-by: multica-agent <github@multica.ai> * fix(github): re-evaluate auto-close on closed-without-merge events too GPT-Boy review on #2470: gating only the `state == "merged"` branch left one ordering hole. PR-A merges first → issue stays in_progress because PR-B is open; PR-B later closes WITHOUT merging → no event ever re-runs the auto-close check, so the issue is stuck in_progress. Generalise the trigger to every terminal PR event (`merged` or `closed`) and advance the issue only when: - the issue is not already terminal (done / cancelled); - no sibling PR is still in flight (open / draft); - at least one linked PR — current or sibling — actually merged. Rule (3) preserves "user closed every PR without merging → leave the issue alone": if no work was delivered, the user decides what to do. Replace `CountOpenSiblingPullRequestsForIssue` with `GetSiblingPullRequestStateCountsForIssue`, which returns both the in-flight count and the merged count in a single roundtrip. Adds `TestWebhook_ClosedSiblingAfterMerge` (the regression GPT-Boy flagged) and `TestWebhook_AllClosedWithoutMerge` (the negative case guarding rule 3). Refactors the multi-PR webhook helper out of the existing two-merge test so all three multi-PR scenarios share it. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |
||
|
|
caeb146bac |
feat(github): GitHub App integration for PR ↔ issue linking (#1817)
* feat(github): GitHub App backend for PR ↔ issue linking - New tables: github_installation (workspace ↔ App install), github_pull_request (mirrored PR state), issue_pull_request (M:N link). - Webhook handler verifies HMAC-SHA256, upserts PR rows, parses issue identifiers from PR title/body/branch and auto-links them. Merging a linked PR moves the issue to done. - Connect/setup endpoints power the zero-config "Connect GitHub" install flow; state token is HMAC-signed so the setup callback can recover the workspace. - Workspace-scoped admin routes for listing/disconnecting installations, plus a per-issue `pull-requests` list endpoint. Co-authored-by: multica-agent <github@multica.ai> * feat(github): UI for connecting GitHub and viewing linked PRs - Settings → Integrations: new tab with Connect GitHub / installations list / disconnect, gated on the deployment having the App configured. - Issue detail sidebar: Pull requests section showing linked PR title, repo, state (open/draft/merged/closed), and author, with deep link to GitHub. - Real-time refresh: github_installation:* and pull_request:* events invalidate the matching TanStack Query caches. Co-authored-by: multica-agent <github@multica.ai> * fix(github): address review — null actor, role gating, configured guard, scoped uninstall broadcast - listeners: use optionalUUID(e.ActorID) so the system actor on the github-driven issue:updated event no longer panics activity / notification listeners; merged-PR → issue done now produces a status_changed activity and inbox entry. - IntegrationsTab: gate the admin-only installations query on canManage so members no longer hit /github/installations 403; the configured/not-configured copy is also scoped to admins. - backend: introduce isGitHubConfigured() requiring both GITHUB_APP_SLUG and GITHUB_WEBHOOK_SECRET, and surface that single flag from list-installations + connect endpoints so the frontend Connect button stays disabled until both are set. - DeleteGitHubInstallationByInstallationID now RETURNs workspace_id; webhook handler publishes github_installation:deleted scoped to the right workspace so already-open Settings tabs invalidate in real time. ErrNoRows on a re-fired delete short-circuits cleanly. - tests: focused webhook integration coverage (auto-link + merge → done, cancelled preservation, uninstall returns workspace). Co-authored-by: multica-agent <github@multica.ai> * fix(github): i18n the new GitHub UI strings to satisfy lint CI flagged every literal string in the Integrations tab, the Pull requests sidebar section, and the per-PR row label. Move them through useT() and add the matching `integrations.*` block to settings.json (en / zh-Hans) plus `detail.section_pull_requests` / `detail.pull_request_state_*` / loading + empty copy under `issues.json`. Co-authored-by: multica-agent <github@multica.ai> --------- Co-authored-by: multica-agent <github@multica.ai> |