a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
86 lines
2.1 KiB
Go
86 lines
2.1 KiB
Go
package forge
|
|
|
|
import "time"
|
|
|
|
// staleAfterDays is the age past which an open PR is classified STALE
|
|
// (when nothing more pressing applies).
|
|
const staleAfterDays = 30
|
|
|
|
// Status is the pure-Go classification of a PR against a default base.
|
|
// It carries no network dependency and is table-testable.
|
|
type Status struct {
|
|
State string
|
|
BaseMismatch bool
|
|
Draft bool
|
|
AgeDays int
|
|
Blockers []string
|
|
}
|
|
|
|
// ClassifyStatus reduces a PR to a single review-state label, computed
|
|
// purely from its already-fetched fields against the repo's default base.
|
|
// State precedence: DRAFT → BASE_MISMATCH → CHANGES_REQUESTED → APPROVED
|
|
// → STALE → READY. Blockers accumulates every condition that would hold
|
|
// a merge.
|
|
func ClassifyStatus(pr PR, defaultBase string) Status {
|
|
s := Status{
|
|
Draft: pr.IsDraft,
|
|
AgeDays: ageDays(pr.UpdatedAt),
|
|
}
|
|
if defaultBase != "" && pr.BaseRef != "" && pr.BaseRef != defaultBase {
|
|
s.BaseMismatch = true
|
|
}
|
|
|
|
if pr.IsDraft {
|
|
s.Blockers = append(s.Blockers, "draft")
|
|
}
|
|
if s.BaseMismatch {
|
|
s.Blockers = append(s.Blockers, "base-mismatch")
|
|
}
|
|
if pr.ReviewDecision == "CHANGES_REQUESTED" {
|
|
s.Blockers = append(s.Blockers, "changes-requested")
|
|
}
|
|
if RollupCI(pr) == "FAILURE" {
|
|
s.Blockers = append(s.Blockers, "ci-failure")
|
|
}
|
|
|
|
switch {
|
|
case pr.IsDraft:
|
|
s.State = "DRAFT"
|
|
case s.BaseMismatch:
|
|
s.State = "BASE_MISMATCH"
|
|
case pr.ReviewDecision == "CHANGES_REQUESTED":
|
|
s.State = "CHANGES_REQUESTED"
|
|
case pr.ReviewDecision == "APPROVED":
|
|
s.State = "APPROVED"
|
|
case s.AgeDays >= staleAfterDays:
|
|
s.State = "STALE"
|
|
default:
|
|
s.State = "READY"
|
|
}
|
|
return s
|
|
}
|
|
|
|
// RollupCI echoes a PR's reconstructed CI rollup, normalized to one of
|
|
// NONE / FAILURE / PENDING / SUCCESS. An empty rollup reads as NONE.
|
|
func RollupCI(pr PR) string {
|
|
switch pr.CIRollup {
|
|
case "FAILURE", "PENDING", "SUCCESS":
|
|
return pr.CIRollup
|
|
default:
|
|
return "NONE"
|
|
}
|
|
}
|
|
|
|
// ageDays returns the whole-day age of t relative to now, clamped at 0
|
|
// for a zero or future timestamp.
|
|
func ageDays(t time.Time) int {
|
|
if t.IsZero() {
|
|
return 0
|
|
}
|
|
d := time.Since(t)
|
|
if d < 0 {
|
|
return 0
|
|
}
|
|
return int(d.Hours() / 24)
|
|
}
|