No build reaches a machine without a gate; a release plan walks what waits (hq ADR 0236)
A send carries the machine's whole declaration, so at the switch to roll the next send of anything would have carried the old default's backlog, unjudged, to every machine. A gated send now carries and judges everything waiting on its machine; every other send is refused or leaves the machine; a release plan walks what waits one machine at a time, the control node last, and one that fails holds the next until a person releases it.
This commit is contained in:
@@ -46,8 +46,8 @@ type GateVerdict struct {
|
||||
Epoch uint64
|
||||
}
|
||||
|
||||
// RecordGate writes a build's verdict. **A failed build's row is written once**: a second failure for
|
||||
// the same build is refused with ErrGateKept, which is what keeps a rollback to one per build — the row
|
||||
// RecordGate writes a build's verdict. A build that passed on one machine may still fail on the next one
|
||||
// judged; **a failed build's row is written once**: any later verdict for it is refused with ErrGateKept, which is what keeps a rollback to one per build — the row
|
||||
// is written before the rollback's send, and a controller replaced in between finds it.
|
||||
func (i *Inventory) RecordGate(ctx context.Context, v GateVerdict) error {
|
||||
epoch, err := i.actingEpoch(ctx)
|
||||
@@ -64,7 +64,7 @@ func (i *Inventory) RecordGate(ctx context.Context, v GateVerdict) error {
|
||||
on conflict (build) do update set verdict = excluded.verdict, rollback = excluded.rollback,
|
||||
why = excluded.why, previous = excluded.previous, machines = excluded.machines,
|
||||
judged_at = now(), epoch = excluded.epoch
|
||||
where build_gate.verdict = 'passed' and excluded.verdict = 'passed'`,
|
||||
where build_gate.verdict = 'passed'`,
|
||||
v.Build, v.Module, v.Commit, v.Previous, v.Plan, v.Machines, v.Verdict, v.Rollback, v.Why, v.Component,
|
||||
v.JudgingFrom, epoch)
|
||||
if err != nil {
|
||||
@@ -245,3 +245,65 @@ func (i *Inventory) BuildFingerprints(ctx context.Context, module string) (map[s
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// Fingerprints is BuildFingerprints for every module at once: module → commit → fingerprint.
|
||||
func (i *Inventory) Fingerprints(ctx context.Context) (map[string]map[string]string, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select module, commit_hash, made, coalesce(manifest::text, '') from build
|
||||
where module is not null and failed = '' and commit_hash <> '' order by at desc`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]map[string]string{}
|
||||
for rows.Next() {
|
||||
var module, commit, manifest string
|
||||
var made []byte
|
||||
if err := rows.Scan(&module, &commit, &made, &manifest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if out[module] == nil {
|
||||
out[module] = map[string]string{}
|
||||
}
|
||||
if _, seen := out[module][commit]; seen {
|
||||
continue
|
||||
}
|
||||
sum := sha256.Sum256(append(append(made, 0), []byte(manifest)...))
|
||||
out[module][commit] = hex.EncodeToString(sum[:])
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// PassedCommits is, per module, the commits a build of which passed its gate on some machine.
|
||||
func (i *Inventory) PassedCommits(ctx context.Context) (map[string]map[string]bool, error) {
|
||||
rows, err := i.store.Pool().Query(ctx, `select module, commit_hash from build_gate where verdict = 'passed'`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]map[string]bool{}
|
||||
for rows.Next() {
|
||||
var module, commit string
|
||||
if err := rows.Scan(&module, &commit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if out[module] == nil {
|
||||
out[module] = map[string]bool{}
|
||||
}
|
||||
out[module][commit] = true
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// BuildOf is the id of the newest successful build of a module from a commit; empty when none is
|
||||
// recorded (a manifest handed over by hand).
|
||||
func (i *Inventory) BuildOf(ctx context.Context, module, commit string) (string, error) {
|
||||
var id string
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select id from build where module = $1 and commit_hash = $2 and failed = '' order by at desc limit 1`,
|
||||
module, commit).Scan(&id)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return "", nil
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user