Add guarded single-command release publication
This commit is contained in:
@@ -9,120 +9,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Exercise the copyable runbook against disposable Git repositories, not a
|
||||
// publication service. The checker itself is covered separately.
|
||||
func TestReleaseRunbookGuards(t *testing.T) {
|
||||
document, err := os.ReadFile("docs/release.md")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block := func(heading string) string {
|
||||
t.Helper()
|
||||
_, section, ok := strings.Cut(string(document), heading)
|
||||
if !ok {
|
||||
t.Fatalf("missing section %q", heading)
|
||||
}
|
||||
_, section, ok = strings.Cut(section, "```sh\n")
|
||||
if !ok {
|
||||
t.Fatalf("missing commands in %q", heading)
|
||||
}
|
||||
commands, _, ok := strings.Cut(section, "```")
|
||||
if !ok {
|
||||
t.Fatalf("unterminated commands in %q", heading)
|
||||
}
|
||||
return commands
|
||||
}
|
||||
prepare := block("## 4. Validate the source candidate")
|
||||
publish := block("## 6. Run publication guards") + block("## 7. Tag and publish the exact ref")
|
||||
for _, scenario := range []string{"success", "dirty", "changed-candidate", "remote-tag-exists", "failed-validation"} {
|
||||
t.Run(scenario, func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
remote := filepath.Join(t.TempDir(), "remote.git")
|
||||
run := func(program string, args ...string) string {
|
||||
t.Helper()
|
||||
cmd := exec.Command(program, args...)
|
||||
cmd.Dir = root
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%s %v: %v\n%s", program, args, err, out)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
run("git", "init", "-q", "-b", "main")
|
||||
run("git", "init", "-q", "--bare", remote)
|
||||
run("git", "config", "user.name", "Test")
|
||||
run("git", "config", "user.email", "test@example.invalid")
|
||||
run("git", "config", "commit.gpgsign", "false")
|
||||
run("git", "config", "tag.gpgsign", "false")
|
||||
const origin = "https://gitea.maximumdirect.net/eric/ConfigKit.git"
|
||||
run("git", "remote", "add", "origin", remote)
|
||||
// Substitute only the fixture's authoritative URL; execute the guards
|
||||
// themselves unchanged. Disallow network transports as a backstop.
|
||||
localPublish := strings.ReplaceAll(publish, origin, remote)
|
||||
for _, dir := range []string{"docs/releases", "scripts"} {
|
||||
if err := os.MkdirAll(filepath.Join(root, dir), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
write := func(path, content string) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(filepath.Join(root, path), []byte(content), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
checker := "#!/bin/sh\nexit 0\n"
|
||||
if scenario == "failed-validation" {
|
||||
checker = "#!/bin/sh\nexit 37\n"
|
||||
}
|
||||
write("scripts/release-check.sh", checker)
|
||||
write("docs/releases/v0.9.0.md", "# ConfigKit v0.9.0\n\n## Summary\n\n## Compatibility\n\n## Upgrade\n\n## Changes\n")
|
||||
run("git", "add", ".")
|
||||
bash := func(commands string) ([]byte, error) {
|
||||
cmd := exec.Command("bash", "-c", "set -euo pipefail\nrelease_version=v0.9.0\n"+commands)
|
||||
cmd.Dir = root
|
||||
cmd.Env = append(os.Environ(), "GIT_ALLOW_PROTOCOL=file")
|
||||
return cmd.CombinedOutput()
|
||||
}
|
||||
out, err := bash(prepare)
|
||||
if scenario == "failed-validation" {
|
||||
if err == nil {
|
||||
t.Fatal("failed validation accepted")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, ".git/configkit-release-candidate")); !os.IsNotExist(err) {
|
||||
t.Fatal("failed validation recorded a candidate")
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Fatalf("prepare: %v\n%s", err, out)
|
||||
}
|
||||
switch scenario {
|
||||
case "dirty":
|
||||
write("unexpected.txt", "uncommitted\n")
|
||||
case "changed-candidate":
|
||||
run("git", "commit", "--allow-empty", "-qm", "Changed candidate")
|
||||
case "remote-tag-exists":
|
||||
run("git", "tag", "v0.9.0")
|
||||
run("git", "push", remote, "refs/tags/v0.9.0")
|
||||
run("git", "tag", "-d", "v0.9.0")
|
||||
}
|
||||
// Seed the remote primary branch so fetch works before first publication.
|
||||
run("git", "push", remote, "HEAD:refs/heads/main")
|
||||
out, err = bash(localPublish)
|
||||
if scenario == "success" {
|
||||
if err != nil {
|
||||
t.Fatalf("publish: %v\n%s", err, out)
|
||||
}
|
||||
if got := run("git", "--git-dir="+remote, "rev-parse", "v0.9.0^{}"); got != run("git", "rev-parse", "HEAD") {
|
||||
t.Fatal("published wrong commit")
|
||||
}
|
||||
} else if err == nil {
|
||||
t.Fatalf("publication accepted %s", scenario)
|
||||
} else if scenario != "remote-tag-exists" && run("git", "--git-dir="+remote, "tag", "--list") != "" {
|
||||
t.Fatal("failed guard published a tag")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDevelopmentCheckHandlesFilenamesAndStopsOnFailure(t *testing.T) {
|
||||
for _, scriptName := range []string{"check.sh"} {
|
||||
t.Run(scriptName, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user