package runner
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
)
// ForgeBranch asks the forge which branch a check-in is on
// (GET /api/v1/r/<repo>/checkin/<hash>), for a runner that runs one
// branch only. The task's own input is not asked: it came from a
// pipeline the check-in under test may have written.
type ForgeBranch struct {
API string // https://forge/api/v1/r/<repo>
// User and Password, for a private repository, as ArchiveCheckout.
User, Password string
Client *http.Client
}
func (f ForgeBranch) BranchOf(ctx context.Context, checkin string) (string, error) {
req, err := http.NewRequestWithContext(ctx, "GET", strings.TrimSuffix(f.API, "/")+"/checkin/"+url.PathEscape(checkin), nil)
if err != nil {
return "", err
}
if f.User != "" {
req.SetBasicAuth(f.User, f.Password)
}
c := f.Client
if c == nil {
c = http.DefaultClient
}
resp, err := c.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("the forge answered %d for check-in %.12s", resp.StatusCode, checkin)
}
var body struct {
Hash string `json:"hash"`
Branch string `json:"branch"`
}
if err := json.NewDecoder(io.LimitReader(resp.Body, 4<<20)).Decode(&body); err != nil {
return "", fmt.Errorf("check-in %.12s: %v", checkin, err)
}
if body.Hash != checkin {
// A prefix the forge resolved, or another check-in: either way
// not an answer about the one this task names.
return "", fmt.Errorf("the forge answered for %.12s when asked about %.12s", body.Hash, checkin)
}
return body.Branch, nil
}