-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreleaseflow_recut_test.go
More file actions
163 lines (150 loc) · 6.13 KB
/
Copy pathreleaseflow_recut_test.go
File metadata and controls
163 lines (150 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
const redraftStepName = "Re-draft a published prerelease before replacing its bytes"
// Re-cutting an rc replaces the assets of a release that is ALREADY PUBLIC
// (replace_existing_artifacts - the amend loop this repo actually uses). For the
// length of that run the rc's download URLs served bytes whose new digests
// nothing had attested yet: draft-until-complete covered a tag's first cut and
// not its second, and both workflow guards deliberately skip prereleases.
//
// Putting the release back to draft first restores the property: the tag goes
// dark while it is incomplete, and the same final step publishes it again.
func TestRecutRedraftsAPublishedPrerelease(t *testing.T) {
wf := mustReadRepoFile(t, releaseWorkflowPath)
script := extractRunBlock(t, wf, redraftStepName)
log := filepath.Join(t.TempDir(), "gh.log")
code, out := runWorkflowScript(t, script, "published", "v0.62.0-rc.5", log)
if code != 0 {
t.Fatalf("re-draft of a published rc exited %d, want 0:\n%s", code, out)
}
var edited bool
for _, c := range ghCalls(t, log) {
if strings.Contains(c, "release edit") && strings.Contains(c, "--draft=true") {
edited = true
}
}
if !edited {
t.Fatalf("a published rc was NOT put back to draft before its assets were replaced; the tag stays public while it is incomplete:\n%v", ghCalls(t, log))
}
}
// The first cut of a tag has no published release, and must not be disturbed -
// that is the ordinary path.
func TestRecutLeavesAFirstCutAlone(t *testing.T) {
wf := mustReadRepoFile(t, releaseWorkflowPath)
script := extractRunBlock(t, wf, redraftStepName)
log := filepath.Join(t.TempDir(), "gh.log")
code, out := runWorkflowScript(t, script, "absent", "v0.62.0-rc.5", log)
if code != 0 {
t.Fatalf("first cut exited %d, want 0 (nothing to re-draft):\n%s", code, out)
}
for _, c := range ghCalls(t, log) {
if strings.Contains(c, "--draft=true") {
t.Fatalf("a first cut tried to re-draft something: %q", c)
}
}
}
// FAIL CLOSED. If the API cannot be reached, continuing would replace the bytes
// of a live release with nothing attesting them - exactly what the step exists
// to prevent - so an unreachable API stops the run instead.
func TestRecutFailsClosedWhenTheAPIIsUnreachable(t *testing.T) {
wf := mustReadRepoFile(t, releaseWorkflowPath)
script := extractRunBlock(t, wf, redraftStepName)
log := filepath.Join(t.TempDir(), "gh.log")
code, out := runWorkflowScript(t, script, "flaky", "v0.62.0-rc.5", log)
if code == 0 {
t.Fatalf("an unreachable API let the re-cut proceed (exit 0): a published prerelease would be overwritten in place\n%s", out)
}
}
// Gating: the step is prerelease-only. A stable tag never needs it (its own
// guard refuses a re-cut outright), and running it there would put a published
// stable release back into draft, which is precisely what must not happen.
func TestRecutStepIsPrereleaseOnly(t *testing.T) {
wf := mustReadRepoFile(t, releaseWorkflowPath)
i := strings.Index(wf, "- name: "+redraftStepName)
if i < 0 {
t.Fatalf("step %q not found", redraftStepName)
}
// The condition sits directly under the step name.
window := wf[i:min(i+300, len(wf))]
if !strings.Contains(window, "if: ${{ contains(github.ref_name, '-') }}") {
t.Errorf("the re-draft step is not gated to prerelease tags; on a stable tag it would un-publish a live release:\n%s", window)
}
// And it must run BEFORE the bytes are replaced.
gore := strings.Index(wf, "- name: Run GoReleaser")
if gore < i {
t.Error("the re-draft step runs AFTER GoReleaser, so the replacement bytes are published before the tag goes dark")
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
// ghStubEditFails answers the "is it published?" probe successfully but refuses
// every `release edit`. Neither existing stub mode reaches the retry tail: the
// `published` mode succeeds on the first edit, and `flaky` never gets past the
// probe - so the fail-closed branch that matters most went untested, and two
// mutations of it survived the suite byte-identically.
const ghStubEditFails = `#!/bin/sh
echo "$*" >> "$GH_STUB_LOG"
case "$1 $2" in
"api repos/pingular/pingularity/releases/tags/v0.62.0-rc.5")
echo '{"id":123,"draft":false}'
exit 0 ;;
esac
case "$1" in
release) echo "gh: could not edit release" >&2; exit 1 ;;
esac
exit 0
`
// If the release cannot be put back to draft, the run must STOP. Continuing
// would replace the assets of a live release with bytes nothing has attested,
// which is the whole reason the step exists.
func TestRecutStopsWhenItCannotRedraft(t *testing.T) {
wf := mustReadRepoFile(t, releaseWorkflowPath)
script := extractRunBlock(t, wf, redraftStepName)
dir := t.TempDir()
scriptPath := filepath.Join(dir, "step.sh")
if err := os.WriteFile(scriptPath, []byte(script), 0o644); err != nil {
t.Fatal(err)
}
stubDir := filepath.Join(dir, "bin")
if err := os.Mkdir(stubDir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(stubDir, "gh"), []byte(ghStubEditFails), 0o755); err != nil {
t.Fatal(err)
}
// sleep is stubbed out so the retry loop does not cost the suite 15 seconds.
if err := os.WriteFile(filepath.Join(stubDir, "sleep"), []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
t.Fatal(err)
}
logPath := filepath.Join(dir, "gh.log")
cmd := exec.Command("bash", "--noprofile", "--norc", "-e", scriptPath)
cmd.Env = append(os.Environ(),
"PATH="+stubDir+string(os.PathListSeparator)+os.Getenv("PATH"),
"GITHUB_REF_NAME=v0.62.0-rc.5",
"GITHUB_REPOSITORY=pingular/pingularity",
"GH_TOKEN=stub-token",
"GH_STUB_LOG="+logPath,
)
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("the step reported success although it never managed to re-draft the release:\n%s", out)
}
if !strings.Contains(string(out), "refusing to overwrite a published prerelease") {
t.Errorf("the failure does not explain itself:\n%s", out)
}
// It must have actually tried more than once before giving up.
b, _ := os.ReadFile(logPath)
if n := strings.Count(string(b), "release edit"); n < 2 {
t.Errorf("gave up after %d edit attempt(s); the retry loop is meant to absorb a transient API failure", n)
}
}