Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Do not build a job the combined result already passed for the branch

5f6163bb4051955a403b998d48f6dfabd3d6939a · Keenan Nemetz · 4h ago · parent c4ad0d5a

Do not build a job the combined result already passed for the branch

A landing lands the commit the combined result published: same tree, same
workflow, same job, already run for the base branch before it landed. The push
that moves the branch then queued all of it again, so one commit showed up twice
in the run list and the runner did the same work twice.

Queueing now asks whether a combined result for this branch already ran and passed
this job at this commit. If it did, the push owes nothing and the run that tested
the landing stays the one run for it. The question is one indexed query
(runs_repo_sha), asked only for a push to the branch itself: a tag is a release
build of its own, an event run is a new occurrence of one, and the candidate's own
run is the one being reused.

A job that deploys the site is the exception. A combined result never deploys — it
is not on the branch yet — so publishing the site is work it did not do, and that
run still happens. Repositories without a pages job, which is most of them, see the
duplicate disappear entirely.

Not in this change: a fast-forward landing still builds the head on the base branch
after the head's own branch built it. Those runs select workflows by branch, so they
are not the same work by construction; only the combined result is queued as "a
normal run for the base branch", which is what makes this dedupe exact.

Closes 01M6Z0MXZNRCEDD6HBKCJE4WCR

ci/runs/combined_test.go +83

@@ -0,0 +1,83 @@
1 package runs
2
3 // A combined result is the commit a landing lands, published under a candidate ref
4 // and queued as a run for the base branch. When it passes and the branch then points
5 // at it, the push that follows owes nothing: the same tree, workflow and job already
6 // ran for that branch. These tests pin what "already ran" is allowed to mean.
7
8 import (
9 "strings"
10 "testing"
11 "time"
12 )
13
14 func TestCombinedResultPassedOnlyCountsAPassingRunForThatBranch(t *testing.T) {
15 t.Parallel()
16 s := newStore(t)
17 now := time.Now().UTC()
18
19 candidate := run("01M6Z05K5PM58EE3H6VE2P2EYD", "demo", now)
20 candidate.Ref = CandidateRefRoot + "01M6Z05K5PM58EE3H6VE2P2EYD/aaaaaaaa-bbbbbbbb"
21 candidate.Branch = "main"
22 candidate.Status = StatusSuccess
23 if _, err := s.Enqueue(candidate, nil); err != nil {
24 t.Fatal(err)
25 }
26
27 passed, err := s.CombinedResultPassed("demo", "main", candidate.SHA, candidate.Workflow, candidate.Job)
28 if err != nil {
29 t.Fatal(err)
30 }
31 if !passed {
32 t.Fatal("a passed combined result for this branch was not recognised")
33 }
34
35 for _, c := range []struct {
36 name string
37 repo, branch, sha string
38 workflow, job string
39 }{
40 {"another repository", "other", "main", candidate.SHA, candidate.Workflow, candidate.Job},
41 {"another branch", "demo", "dev", candidate.SHA, candidate.Workflow, candidate.Job},
42 {"another commit", "demo", "main", strings.Repeat("c", 40), candidate.Workflow, candidate.Job},
43 {"another workflow", "demo", "main", candidate.SHA, ".vole/workflows/other.yaml", candidate.Job},
44 {"another job", "demo", "main", candidate.SHA, candidate.Workflow, "other"},
45 } {
46 passed, err := s.CombinedResultPassed(c.repo, c.branch, c.sha, c.workflow, c.job)
47 if err != nil {
48 t.Fatalf("%s: %v", c.name, err)
49 }
50 if passed {
51 t.Fatalf("%s was taken as the same work", c.name)
52 }
53 }
54 }
55
56 // Only a passed run is evidence. A combined result that failed, or is still queued,
57 // does not make the branch's own run redundant: the landing still owes a result for
58 // the commit.
59 func TestCombinedResultPassedNeedsAGreenRun(t *testing.T) {
60 t.Parallel()
61 now := time.Now().UTC()
62 for _, status := range []string{StatusQueued, StatusRunning, StatusFailed, StatusCanceled, StatusError} {
63 status := status
64 t.Run(status, func(t *testing.T) {
65 t.Parallel()
66 s := newStore(t)
67 candidate := run("01M6YWA3JX0WWHJ55JCVDZ2QTP", "demo", now)
68 candidate.Ref = CandidateRefRoot + "01M6YWA3JX0WWHJ55JCVDZ2QTP/aaaaaaaa-bbbbbbbb"
69 candidate.Status = status
70 if _, err := s.Enqueue(candidate, nil); err != nil {
71 t.Fatal(err)
72 }
73 passed, err := s.CombinedResultPassed("demo", "main", candidate.SHA,
74 candidate.Workflow, candidate.Job)
75 if err != nil {
76 t.Fatal(err)
77 }
78 if passed {
79 t.Fatalf("a %s combined result was taken as evidence", status)
80 }
81 })
82 }
83 }