Go · 1774 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package pulls
4
5 import (
6 "context"
7 "errors"
8
9 "github.com/tenseleyFlow/shithub/internal/auth/audit"
10 "github.com/tenseleyFlow/shithub/internal/issues"
11 pullsdb "github.com/tenseleyFlow/shithub/internal/pulls/sqlc"
12 repogit "github.com/tenseleyFlow/shithub/internal/repos/git"
13 )
14
15 // SetState delegates to issues.SetState with a couple of PR-specific
16 // guardrails:
17 //
18 // - reopening a merged PR is a no-op (a merged PR can't reopen).
19 // - reopening when base or head no longer resolves returns a typed
20 // error so the handler can render "branches moved, cannot reopen".
21 func SetState(ctx context.Context, deps Deps, gitDir string, actorUserID, prID int64, newState string) error {
22 q := pullsdb.New()
23 pr, err := q.GetPullRequestByIssueID(ctx, deps.Pool, prID)
24 if err != nil {
25 return ErrPRNotFound
26 }
27 if pr.MergedAt.Valid {
28 return ErrAlreadyMerged
29 }
30 if newState == "open" {
31 // Validate refs still resolve; otherwise reopen would leave a PR
32 // pointing at a missing branch which the diff renderer can't
33 // handle cleanly.
34 if _, err := repogit.ResolveRefOID(ctx, gitDir, pr.BaseRef); err != nil {
35 if errors.Is(err, repogit.ErrRefNotFound) {
36 return ErrBaseNotFound
37 }
38 return err
39 }
40 if _, err := repogit.ResolveRefOID(ctx, gitDir, pr.HeadRef); err != nil {
41 if errors.Is(err, repogit.ErrRefNotFound) {
42 return ErrHeadNotFound
43 }
44 return err
45 }
46 }
47 if err := issues.SetState(ctx, issues.Deps{Pool: deps.Pool, Logger: deps.Logger}, actorUserID, prID, newState, ""); err != nil {
48 return err
49 }
50 if deps.Audit != nil {
51 _ = deps.Audit.Record(ctx, deps.Pool, actorUserID,
52 audit.ActionPullStateChanged, audit.TargetPull, prID,
53 map[string]any{"state": newState})
54 }
55 return nil
56 }
57