Files
mesh-delivery/modules/mesh-delivery-forge-bridge/internal/bridge/forgemade.go
T
jschoubben 767928eb94 Forge bridge reads gitea_api's {"result": body} wrapper in one place (issue 1966)
Both ForgeMade and Tags took the tool's wrapper for the pull request / the tag list, so every forge-made merge read
"#0 is not merged" and bridge_tags failed, leaving builds unnumbered.

Quick fix: unwrap "result" in forgeAPI (the one Go reader of gitea_api).
Thorough fix (chosen, the kind was met twice and hid behind a drifted fake): bridge.API answers what the tool answers,
forgeBody is the one reader of that shape and refuses an answer without a result, naming it; ForgeMade refuses number
0 as "no pull request"; the unit fakes answer the tool's real wrapped shape, with rows for the bare body.

Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
2026-11-09 11:29:31 +01:00

67 lines
2.9 KiB
Go

package bridge
import (
"encoding/json"
"fmt"
"net/url"
"strings"
)
// ForgePull is a pull request as the forge itself says it: what the bridge needs of a merge made on the forge, not
// through the gate (a recovery merge or the operator's own, which the gate holds no merge of; issue 1966).
type ForgePull struct {
Number int
Branch string // the pull request's head branch
Head string // the head commit
Merge string // the merge commit into the trunk
}
// ForgeMade reads, through the forge module's gitea_api (the path the bridge reads the forge by), the merged pull
// request whose merge commit is merge: by number when number is given (the pull request's merge commit must then equal
// merge), else by the merge commit (the forge's commit-to-pull-request lookup). A pull request not merged, merged into
// another branch than the trunk, or merged at another commit is refused.
func ForgeMade(api API, owner, repo string, number int, merge string) (ForgePull, error) {
path := fmt.Sprintf("/repos/%s/%s/commits/%s/pull", url.PathEscape(owner), url.PathEscape(repo), url.PathEscape(merge))
if number > 0 {
path = fmt.Sprintf("/repos/%s/%s/pulls/%d", url.PathEscape(owner), url.PathEscape(repo), number)
}
raw, err := api(path)
if err != nil {
return ForgePull{}, fmt.Errorf("the forge did not answer %s: %w", path, err)
}
body, err := forgeBody(raw)
if err != nil {
return ForgePull{}, fmt.Errorf("the forge's answer for %s: %w", path, err)
}
var p struct {
Number int `json:"number"`
Merged bool `json:"merged"`
MergeCommitSHA string `json:"merge_commit_sha"`
Head struct {
Label string `json:"label"`
SHA string `json:"sha"`
} `json:"head"`
Base struct {
Ref string `json:"ref"`
} `json:"base"`
}
if err := json.Unmarshal(body, &p); err != nil {
return ForgePull{}, fmt.Errorf("the forge's answer for %s is no pull request: %w", path, err)
}
switch {
case p.Number == 0:
return ForgePull{}, fmt.Errorf("the forge's answer for %s is no pull request (no number)", path)
case !p.Merged:
return ForgePull{}, fmt.Errorf("the forge says the pull request %s/%s#%d is not merged", owner, repo, p.Number)
case p.MergeCommitSHA != merge:
return ForgePull{}, fmt.Errorf("the forge says %s/%s#%d was merged at %s, not at %s", owner, repo, p.Number, p.MergeCommitSHA, merge)
case p.Base.Ref != Trunk:
return ForgePull{}, fmt.Errorf("the forge says %s/%s#%d was merged into %s, not the trunk %s", owner, repo, p.Number, p.Base.Ref, Trunk)
}
// For a merged pull request the forge's head.ref is "refs/pull/N/head"; the branch is in head.label (same-repository pull requests).
if p.Head.Label == "" || strings.HasPrefix(p.Head.Label, "refs/") {
return ForgePull{}, fmt.Errorf("the forge gives no head branch for %s/%s#%d (label %q)", owner, repo, p.Number, p.Head.Label)
}
return ForgePull{Number: p.Number, Branch: p.Head.Label, Head: p.Head.SHA, Merge: p.MergeCommitSHA}, nil
}