package main import ( "os" "os/exec" "path/filepath" "runtime" "strings" "testing" "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit/internal/cli" ) func TestReleaseKitSourceDeliveryWorkflowFromTaggedCheckout(t *testing.T) { _, sourceFile, _, ok := runtime.Caller(0) if !ok { t.Fatal("locate integration test source") } root := filepath.Clean(filepath.Join(filepath.Dir(sourceFile), "../..")) profile, err := cli.LoadSettings(root, "") if err != nil { t.Fatalf("load ReleaseKit source-only profile: %v", err) } if profile.Build != nil && len(profile.Build.Targets) != 0 { t.Fatalf("ReleaseKit profile declares binary targets: %#v", profile.Build.Targets) } if profile.Delivery == nil || profile.Delivery.BaseURL != "https://gitea.maximumdirect.net" || profile.Delivery.Owner != "eric" || profile.Delivery.Repository != "ReleaseKit" || profile.Delivery.TokenEnv != "RELEASEKIT_GITEA_TOKEN" { t.Fatalf("ReleaseKit delivery profile = %#v", profile.Delivery) } repository := filepath.Join(t.TempDir(), "tagged ReleaseKit") clone := exec.Command("git", "clone", "--local", "--no-hardlinks", root, repository) if output, err := clone.CombinedOutput(); err != nil { t.Fatalf("clone ReleaseKit source: %v\n%s", err, output) } cliGitRun(t, repository, "config", "user.name", "ReleaseKit Test") cliGitRun(t, repository, "config", "user.email", "releasekit@example.test") const version = "v1.2.3" note := "# ReleaseKit v1.2.3\n\nSource-only delivery fixture.\n\n" + "[COPYRIGHT](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v1.2.3/COPYRIGHT) |\n" + "[LICENSE](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v1.2.3/LICENSE) |\n" + "[ATTRIBUTION](https://gitea.maximumdirect.net/eric/ReleaseKit/src/tag/v1.2.3/docs/oss/attribution.md)\n" notePath := filepath.Join(repository, "docs", "releases", version+".md") if err := os.MkdirAll(filepath.Dir(notePath), 0o700); err != nil { t.Fatal(err) } if err := os.WriteFile(notePath, []byte(note), 0o600); err != nil { t.Fatal(err) } ignore, err := os.OpenFile(filepath.Join(repository, ".gitignore"), os.O_APPEND|os.O_WRONLY, 0) if err != nil { t.Fatal(err) } if _, err := ignore.WriteString("\n/.releasekit/\n"); err != nil { t.Fatal(err) } if err := ignore.Close(); err != nil { t.Fatal(err) } cliGitRun(t, repository, "add", "docs/releases/"+version+".md", ".gitignore") cliGitRun(t, repository, "commit", "-m", "prepare source-only delivery") state := &deliveryServerState{product: "ReleaseKit"} server := newDeliveryServer(t, state) defer server.Close() writeDeliveryProfile(t, repository, cli.Settings{ Product: "ReleaseKit", Delivery: &cli.DeliverySettings{BaseURL: server.URL, Owner: "owner", Repository: "example"}, }) state.commit = cliGitOutput(t, repository, "rev-parse", "HEAD") cliGitRun(t, repository, "tag", "-a", version, "-m", "ReleaseKit v1.2.3") remote := filepath.Join(t.TempDir(), "origin.git") initRemote := exec.Command("git", "init", "--bare", remote) if output, err := initRemote.CombinedOutput(); err != nil { t.Fatalf("initialize local delivery remote: %v\n%s", err, output) } cliGitRun(t, repository, "remote", "set-url", "origin", remote) cliGitRun(t, repository, "push", "origin", "HEAD:refs/heads/main", "refs/tags/"+version) tool := filepath.Join(repository, ".releasekit", "bin", "releasekit") template, err := os.ReadFile(filepath.Join(root, "examples", "woodpecker", "releasekit-source-delivery.yml")) if err != nil { t.Fatal(err) } buildBlock, err := extractWoodpeckerCommand(string(template), "build-tool") if err != nil { t.Fatal(err) } deliveryBlock, err := extractWoodpeckerCommand(string(template), "deliver-page") if err != nil { t.Fatal(err) } env := map[string]string{ "CI_WORKSPACE": repository, "CI_COMMIT_SHA": state.commit, "CI_COMMIT_TAG": version, "RELEASEKIT_GITEA_TOKEN": "fixture-token", "GOPROXY": "off", "GOSUMDB": "off", } runWoodpeckerBlock(t, strings.ReplaceAll(buildBlock, "$${", "${"), repository, env) if got := cliGitOutput(t, repository, "rev-parse", "HEAD"); got != state.commit { t.Fatalf("template checkout = %s, want %s", got, state.commit) } state.remoteCommit = strings.Repeat("f", 40) failure := exec.Command(tool, "deliver", "--repo", repository, version) failure.Dir = repository failure.Env = append(os.Environ(), "RELEASEKIT_GITEA_TOKEN=fixture-token") if output, err := failure.CombinedOutput(); err == nil { t.Fatalf("delivery with a mismatched hosted tag succeeded: %s", output) } if state.releaseCreates != 0 || cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version+"^{commit}") != state.commit { t.Fatal("delivery failure changed hosted release state or source tag") } state.remoteCommit = "" for attempt := 0; attempt < 2; attempt++ { output := runWoodpeckerBlock(t, strings.ReplaceAll(deliveryBlock, "$${", "${"), repository, env) if !strings.Contains(output, "state complete") { t.Fatalf("delivery attempt %d output = %q", attempt+1, output) } } if state.releaseCreates != 1 || state.assetUploads != 0 || state.assetListReads != 0 { t.Fatalf("page-only workflow calls = creates:%d uploads:%d asset reads:%d", state.releaseCreates, state.assetUploads, state.assetListReads) } if state.noteBody != note { t.Fatalf("hosted note body = %q, want complete note %q", state.noteBody, note) } if got := cliGitOutput(t, repository, "rev-parse", "refs/tags/"+version+"^{commit}"); got != state.commit { t.Fatalf("source tag moved after delivery: %s, want %s", got, state.commit) } if status := cliGitOutput(t, repository, "status", "--porcelain"); status != "" { t.Fatalf("tagged checkout became dirty after delivery: %s", status) } }