2026-10-08 16:44:08 +00:00
|
|
|
package release_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"runtime"
|
2026-10-11 01:54:04 +00:00
|
|
|
"sort"
|
2026-10-08 16:44:08 +00:00
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestReleaseIntegration(t *testing.T) {
|
|
|
|
|
if os.Getenv("COMMANDKIT_RELEASE_INTEGRATION") != "1" {
|
|
|
|
|
t.Skip("set COMMANDKIT_RELEASE_INTEGRATION=1 to run the local publication exercise; prepare Go module caches first")
|
|
|
|
|
}
|
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
|
if _, err := exec.LookPath("sh"); err != nil {
|
|
|
|
|
t.Skip("POSIX shell is unavailable")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
shell := requirePOSIXShell(t)
|
|
|
|
|
git, err := exec.LookPath("git")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Skip("Git is unavailable")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
|
|
|
|
defer cancel()
|
|
|
|
|
fixture := newPublicationFixture(t, ctx, git)
|
|
|
|
|
marker := filepath.Join(fixture.root, "suite marker")
|
|
|
|
|
env := fixture.releaseEnv(marker)
|
|
|
|
|
|
|
|
|
|
candidate := gitRun(t, ctx, git, fixture.worktree, env, "rev-parse", "HEAD")
|
2026-10-11 01:54:04 +00:00
|
|
|
stdout, stderr, status := runPublisher(t, ctx, shell, fixture, env, "core", "v0.0.1")
|
2026-10-08 16:44:08 +00:00
|
|
|
if status != 0 {
|
2026-10-11 01:54:04 +00:00
|
|
|
t.Fatalf("publish core v0.0.1 status = %d\nstdout:\n%s\nstderr:\n%s", status, stdout, stderr)
|
2026-10-08 16:44:08 +00:00
|
|
|
}
|
|
|
|
|
if !strings.Contains(stdout, "v0.0.1") || !strings.Contains(stdout, candidate) {
|
|
|
|
|
t.Fatalf("publication output does not identify version and candidate %s:\n%s", candidate, stdout)
|
|
|
|
|
}
|
|
|
|
|
assertTag(t, ctx, git, fixture, "v0.0.1", candidate)
|
2026-10-11 01:54:04 +00:00
|
|
|
assertTags(t, ctx, git, fixture, "v0.0.1")
|
2026-10-08 16:44:08 +00:00
|
|
|
if got := readLines(t, marker); !equalStrings(got, []string{"candidate suite ran"}) {
|
|
|
|
|
t.Fatalf("candidate suite marker = %#v, want one run", got)
|
|
|
|
|
}
|
|
|
|
|
if got := gitRun(t, ctx, git, fixture.worktree, env, "status", "--porcelain"); got != "" {
|
|
|
|
|
t.Fatalf("candidate worktree is dirty after publication: %s", got)
|
|
|
|
|
}
|
|
|
|
|
if _, err := os.Stat(filepath.Join(fixture.worktree, "dist")); !errors.Is(err, os.ErrNotExist) {
|
|
|
|
|
t.Fatalf("unexpected binary delivery output at dist: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := os.Stat(filepath.Join(fixture.worktree, ".releasekit", "bin", "releasekit")); err != nil {
|
|
|
|
|
t.Fatalf("expected ignored bootstrap executable: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-11 01:54:04 +00:00
|
|
|
nestedCandidate := gitRun(t, ctx, git, fixture.worktree, env, "rev-parse", "HEAD")
|
|
|
|
|
stdout, stderr, status = runPublisher(t, ctx, shell, fixture, env, "ckconfig", "v0.0.1")
|
|
|
|
|
if status != 0 {
|
|
|
|
|
t.Fatalf("publish ckconfig v0.0.1 status = %d\nstdout:\n%s\nstderr:\n%s", status, stdout, stderr)
|
|
|
|
|
}
|
|
|
|
|
nestedTag := "integrations/ckconfig/v0.0.1"
|
|
|
|
|
if !strings.Contains(stdout, nestedTag) || !strings.Contains(stdout, nestedCandidate) {
|
|
|
|
|
t.Fatalf("nested publication output does not identify tag %s and candidate %s:\n%s", nestedTag, nestedCandidate, stdout)
|
|
|
|
|
}
|
|
|
|
|
assertTag(t, ctx, git, fixture, nestedTag, nestedCandidate)
|
|
|
|
|
assertTags(t, ctx, git, fixture, "v0.0.1", nestedTag)
|
|
|
|
|
|
2026-10-08 16:44:08 +00:00
|
|
|
fixture.prepareSecondCandidate(t, ctx, git)
|
2026-10-11 01:54:04 +00:00
|
|
|
stdout, stderr, status = runPublisher(t, ctx, shell, fixture, env, "core", "v0.0.2")
|
2026-10-08 16:44:08 +00:00
|
|
|
if status != 1 {
|
|
|
|
|
t.Fatalf("publish with mismatched endpoint status = %d, want 1\nstdout:\n%s\nstderr:\n%s", status, stdout, stderr)
|
|
|
|
|
}
|
|
|
|
|
if !strings.Contains(strings.ToLower(stderr), "unexpected publication push url") {
|
|
|
|
|
t.Fatalf("endpoint mismatch diagnostic not found in stderr:\n%s", stderr)
|
|
|
|
|
}
|
2026-10-11 01:54:04 +00:00
|
|
|
assertTags(t, ctx, git, fixture, "v0.0.1", nestedTag)
|
2026-10-08 16:44:08 +00:00
|
|
|
if got := readLines(t, marker); !equalStrings(got, []string{"candidate suite ran"}) {
|
|
|
|
|
t.Fatalf("candidate suite ran after endpoint preflight failure: %#v", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type publicationFixture struct {
|
2026-10-11 01:54:04 +00:00
|
|
|
root string
|
|
|
|
|
worktree string
|
|
|
|
|
bare string
|
|
|
|
|
outside string
|
|
|
|
|
profile string
|
|
|
|
|
nestedProfile string
|
|
|
|
|
gitConfig string
|
|
|
|
|
gitEnv []string
|
2026-10-08 16:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newPublicationFixture(t *testing.T, ctx context.Context, git string) publicationFixture {
|
|
|
|
|
t.Helper()
|
|
|
|
|
_, sourceFile, _, ok := runtime.Caller(0)
|
|
|
|
|
if !ok {
|
|
|
|
|
t.Fatal("could not locate test source")
|
|
|
|
|
}
|
|
|
|
|
commandkitRoot := filepath.Dir(filepath.Dir(sourceFile))
|
|
|
|
|
root := t.TempDir()
|
|
|
|
|
worktree := filepath.Join(root, "candidate worktree")
|
|
|
|
|
bare := filepath.Join(root, "authoritative local.git")
|
|
|
|
|
outside := filepath.Join(root, "outside publisher directory")
|
|
|
|
|
configPath := filepath.Join(root, "isolated git config")
|
|
|
|
|
hooks := filepath.Join(root, "empty hooks")
|
|
|
|
|
for _, dir := range []string{worktree, outside, hooks} {
|
|
|
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, configPath, fmt.Sprintf(`[user]
|
|
|
|
|
name = CommandKit Release Test
|
|
|
|
|
email = release-test@example.invalid
|
|
|
|
|
[commit]
|
|
|
|
|
gpgsign = false
|
|
|
|
|
[tag]
|
|
|
|
|
gpgSign = false
|
|
|
|
|
[core]
|
|
|
|
|
hooksPath = %s
|
|
|
|
|
`, hooks), 0o600)
|
|
|
|
|
gitEnv := isolatedProcessEnv(map[string]string{
|
|
|
|
|
"GIT_CONFIG_GLOBAL": configPath,
|
|
|
|
|
"GIT_CONFIG_NOSYSTEM": "1",
|
|
|
|
|
"GIT_TERMINAL_PROMPT": "0",
|
|
|
|
|
}, gitOverrideKeys...)
|
|
|
|
|
bareEnv := append([]string(nil), gitEnv...)
|
|
|
|
|
gitRun(t, ctx, git, root, bareEnv, "init", "--bare", bare)
|
|
|
|
|
gitRun(t, ctx, git, root, bareEnv, "--git-dir", bare, "symbolic-ref", "HEAD", "refs/heads/main")
|
|
|
|
|
gitRun(t, ctx, git, worktree, gitEnv, "init")
|
|
|
|
|
gitRun(t, ctx, git, worktree, gitEnv, "branch", "-M", "main")
|
|
|
|
|
|
|
|
|
|
for _, name := range []string{"go.mod", "go.sum", ".gitignore"} {
|
|
|
|
|
copyFile(t, filepath.Join(commandkitRoot, name), filepath.Join(worktree, name), 0o644)
|
|
|
|
|
}
|
2026-10-11 01:54:04 +00:00
|
|
|
for _, name := range []string{"COPYRIGHT", "LICENSE", "docs/oss/attribution.md"} {
|
|
|
|
|
target := filepath.Join(worktree, name)
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
copyFile(t, filepath.Join(commandkitRoot, name), target, 0o644)
|
|
|
|
|
}
|
2026-10-08 16:44:08 +00:00
|
|
|
script := filepath.Join(worktree, "scripts", "release")
|
|
|
|
|
if err := os.MkdirAll(filepath.Dir(script), 0o755); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
copyFile(t, filepath.Join(commandkitRoot, "scripts", "release"), script, 0o755)
|
|
|
|
|
profile := filepath.Join(worktree, "releasekit.json")
|
|
|
|
|
profileData, err := os.ReadFile(filepath.Join(commandkitRoot, "releasekit.json"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var fields map[string]json.RawMessage
|
|
|
|
|
if err := json.Unmarshal(profileData, &fields); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
fields["expected_push_url"], err = json.Marshal(bare)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
profileData, err = json.MarshalIndent(fields, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, profile, string(profileData)+"\n", 0o644)
|
2026-10-11 01:54:04 +00:00
|
|
|
nestedModule := filepath.Join(worktree, "integrations", "ckconfig")
|
|
|
|
|
if err := os.MkdirAll(nestedModule, 0o755); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, filepath.Join(nestedModule, "go.mod"), "module gitea.maximumdirect.net/eric/CommandKit/integrations/ckconfig\n\ngo 1.26.0\n", 0o644)
|
|
|
|
|
writeFile(t, filepath.Join(nestedModule, "fixture.go"), "package fixture\n\nfunc Ready() bool { return true }\n", 0o644)
|
|
|
|
|
writeFile(t, filepath.Join(nestedModule, "fixture_test.go"), "package fixture\n\nimport \"testing\"\n\nfunc TestReady(t *testing.T) { if !Ready() { t.Fatal(\"not ready\") } }\n", 0o644)
|
|
|
|
|
if err := os.MkdirAll(filepath.Join(nestedModule, "docs", "releases"), 0o755); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, filepath.Join(nestedModule, "docs", "releases", "v0.0.1.md"), releaseNoteFor("CommandKit ckconfig", "v0.0.1"), 0o644)
|
|
|
|
|
nestedProfile := filepath.Join(nestedModule, "releasekit.json")
|
|
|
|
|
nestedProfileData, err := os.ReadFile(filepath.Join(commandkitRoot, "integrations", "ckconfig", "releasekit.json"))
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var nestedFields map[string]json.RawMessage
|
|
|
|
|
if err := json.Unmarshal(nestedProfileData, &nestedFields); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
nestedFields["expected_push_url"], err = json.Marshal(bare)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
nestedProfileData, err = json.MarshalIndent(nestedFields, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, nestedProfile, string(nestedProfileData)+"\n", 0o644)
|
2026-10-08 16:44:08 +00:00
|
|
|
writeFile(t, filepath.Join(worktree, "fixture_test.go"), fixtureSuiteTest, 0o644)
|
|
|
|
|
if err := os.MkdirAll(filepath.Join(worktree, "docs", "releases"), 0o755); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, filepath.Join(worktree, "docs", "releases", "v0.0.1.md"), releaseNote("v0.0.1"), 0o644)
|
|
|
|
|
gitRun(t, ctx, git, worktree, gitEnv, "remote", "add", "origin", bare)
|
|
|
|
|
gitRun(t, ctx, git, worktree, gitEnv, "add", "--all")
|
|
|
|
|
gitRun(t, ctx, git, worktree, gitEnv, "commit", "-m", "Prepare local release candidate")
|
|
|
|
|
gitRun(t, ctx, git, worktree, gitEnv, "push", "origin", "HEAD:refs/heads/main")
|
|
|
|
|
|
|
|
|
|
return publicationFixture{
|
2026-10-11 01:54:04 +00:00
|
|
|
root: root,
|
|
|
|
|
worktree: worktree,
|
|
|
|
|
bare: bare,
|
|
|
|
|
outside: outside,
|
|
|
|
|
profile: profile,
|
|
|
|
|
nestedProfile: nestedProfile,
|
|
|
|
|
gitConfig: configPath,
|
|
|
|
|
gitEnv: gitEnv,
|
2026-10-08 16:44:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fixture publicationFixture) releaseEnv(marker string) []string {
|
|
|
|
|
values := map[string]string{
|
|
|
|
|
"COMMANDKIT_FIXTURE_MARKER": marker,
|
|
|
|
|
"GOWORK": "off",
|
|
|
|
|
"GOTOOLCHAIN": "local",
|
|
|
|
|
"GOPROXY": "off",
|
|
|
|
|
"GONOPROXY": "none",
|
|
|
|
|
"GOSUMDB": "off",
|
|
|
|
|
"GIT_CONFIG_GLOBAL": fixture.gitConfig,
|
|
|
|
|
"GIT_CONFIG_NOSYSTEM": "1",
|
|
|
|
|
"GIT_TERMINAL_PROMPT": "0",
|
|
|
|
|
}
|
|
|
|
|
return isolatedProcessEnv(values, gitOverrideKeys...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (fixture publicationFixture) prepareSecondCandidate(t *testing.T, ctx context.Context, git string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
writeFile(t, filepath.Join(fixture.worktree, "docs", "releases", "v0.0.2.md"), releaseNote("v0.0.2"), 0o644)
|
|
|
|
|
profileData, err := os.ReadFile(fixture.profile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
var fields map[string]json.RawMessage
|
|
|
|
|
if err := json.Unmarshal(profileData, &fields); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
wrongEndpoint := filepath.Join(fixture.root, "different local destination.git")
|
|
|
|
|
fields["expected_push_url"], err = json.Marshal(wrongEndpoint)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
profileData, err = json.MarshalIndent(fields, "", " ")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
writeFile(t, fixture.profile, string(profileData)+"\n", 0o644)
|
|
|
|
|
gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "add", "--all")
|
|
|
|
|
gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "commit", "-m", "Prepare endpoint mismatch candidate")
|
|
|
|
|
gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "push", "origin", "HEAD:refs/heads/main")
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-11 01:54:04 +00:00
|
|
|
func runPublisher(t *testing.T, ctx context.Context, shell string, fixture publicationFixture, env []string, module, version string) (string, string, int) {
|
2026-10-08 16:44:08 +00:00
|
|
|
t.Helper()
|
2026-10-11 01:54:04 +00:00
|
|
|
cmd := exec.CommandContext(ctx, shell, filepath.Join(fixture.worktree, "scripts", "release"), module, version)
|
2026-10-08 16:44:08 +00:00
|
|
|
cmd.Dir = fixture.outside
|
|
|
|
|
cmd.Env = env
|
|
|
|
|
var stdout, stderr strings.Builder
|
|
|
|
|
cmd.Stdout = &stdout
|
|
|
|
|
cmd.Stderr = &stderr
|
|
|
|
|
err := cmd.Run()
|
|
|
|
|
if err == nil {
|
|
|
|
|
return stdout.String(), stderr.String(), 0
|
|
|
|
|
}
|
|
|
|
|
var exitErr *exec.ExitError
|
|
|
|
|
if errors.As(err, &exitErr) {
|
|
|
|
|
return stdout.String(), stderr.String(), exitErr.ExitCode()
|
|
|
|
|
}
|
|
|
|
|
t.Fatalf("run release launcher: %v", err)
|
|
|
|
|
return stdout.String(), stderr.String(), -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func assertTag(t *testing.T, ctx context.Context, git string, fixture publicationFixture, version, candidate string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
tagRef := "refs/tags/" + version
|
|
|
|
|
tagType := gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "cat-file", "-t", tagRef)
|
|
|
|
|
if tagType != "tag" {
|
|
|
|
|
t.Fatalf("tag type = %q, want annotated tag", tagType)
|
|
|
|
|
}
|
|
|
|
|
tagObject := gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "rev-parse", tagRef)
|
|
|
|
|
tagData := gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "cat-file", "-p", tagRef)
|
|
|
|
|
if !strings.HasPrefix(tagData, "object "+candidate+"\n") || !strings.Contains(tagData, "type commit\n") {
|
|
|
|
|
t.Fatalf("annotated tag does not identify candidate %s:\n%s", candidate, tagData)
|
|
|
|
|
}
|
|
|
|
|
remoteTagObject := gitRun(t, ctx, git, fixture.root, fixture.gitEnv, "--git-dir", fixture.bare, "rev-parse", tagRef)
|
|
|
|
|
if remoteTagObject != tagObject {
|
|
|
|
|
t.Fatalf("remote tag object = %s, local tag object = %s", remoteTagObject, tagObject)
|
|
|
|
|
}
|
|
|
|
|
remoteMain := gitRun(t, ctx, git, fixture.root, fixture.gitEnv, "--git-dir", fixture.bare, "rev-parse", "refs/heads/main")
|
|
|
|
|
if remoteMain != candidate {
|
|
|
|
|
t.Fatalf("remote main = %s, want candidate %s", remoteMain, candidate)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-11 01:54:04 +00:00
|
|
|
func assertTags(t *testing.T, ctx context.Context, git string, fixture publicationFixture, versions ...string) {
|
2026-10-08 16:44:08 +00:00
|
|
|
t.Helper()
|
|
|
|
|
local := gitRun(t, ctx, git, fixture.worktree, fixture.gitEnv, "for-each-ref", "--format=%(refname)", "refs/tags")
|
|
|
|
|
remote := gitRun(t, ctx, git, fixture.root, fixture.gitEnv, "--git-dir", fixture.bare, "for-each-ref", "--format=%(refname)", "refs/tags")
|
2026-10-11 01:54:04 +00:00
|
|
|
wantRefs := make([]string, len(versions))
|
|
|
|
|
for i, version := range versions {
|
|
|
|
|
wantRefs[i] = "refs/tags/" + version
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(wantRefs)
|
|
|
|
|
want := strings.Join(wantRefs, "\n")
|
2026-10-08 16:44:08 +00:00
|
|
|
if local != want || remote != want {
|
|
|
|
|
t.Fatalf("tag refs local=%q remote=%q, want only %q", local, remote, want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gitRun(t *testing.T, ctx context.Context, git, dir string, env []string, args ...string) string {
|
|
|
|
|
t.Helper()
|
|
|
|
|
cmd := exec.CommandContext(ctx, git, args...)
|
|
|
|
|
cmd.Dir = dir
|
|
|
|
|
cmd.Env = env
|
|
|
|
|
output, err := cmd.CombinedOutput()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("git %s in %s: %v\n%s", strings.Join(args, " "), dir, err, output)
|
|
|
|
|
}
|
|
|
|
|
return strings.TrimSuffix(string(output), "\n")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var gitOverrideKeys = []string{
|
|
|
|
|
"GIT_DIR",
|
|
|
|
|
"GIT_WORK_TREE",
|
|
|
|
|
"GIT_INDEX_FILE",
|
|
|
|
|
"GIT_COMMON_DIR",
|
|
|
|
|
"GIT_OBJECT_DIRECTORY",
|
|
|
|
|
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
|
|
|
"GIT_CEILING_DIRECTORIES",
|
|
|
|
|
"GIT_PREFIX",
|
|
|
|
|
"GIT_CONFIG",
|
|
|
|
|
"GIT_CONFIG_COUNT",
|
|
|
|
|
"GIT_CONFIG_PARAMETERS",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isolatedProcessEnv(overrides map[string]string, cleared ...string) []string {
|
|
|
|
|
blocked := make(map[string]struct{}, len(overrides)+len(cleared))
|
|
|
|
|
for key := range overrides {
|
|
|
|
|
blocked[key] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
for _, key := range cleared {
|
|
|
|
|
blocked[key] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
var env []string
|
|
|
|
|
for _, entry := range os.Environ() {
|
|
|
|
|
key, _, _ := strings.Cut(entry, "=")
|
|
|
|
|
if _, skip := blocked[key]; !skip {
|
|
|
|
|
env = append(env, entry)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for key, value := range overrides {
|
|
|
|
|
env = append(env, key+"="+value)
|
|
|
|
|
}
|
|
|
|
|
return env
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func releaseNote(version string) string {
|
2026-10-11 01:54:04 +00:00
|
|
|
return releaseNoteFor("CommandKit", version)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func releaseNoteFor(product, version string) string {
|
|
|
|
|
return "# " + product + " " + version + "\n\nLocal fixture release candidate.\n"
|
2026-10-08 16:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const fixtureSuiteTest = `package fixture
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestCandidateSuiteMarker(t *testing.T) {
|
|
|
|
|
marker := os.Getenv("COMMANDKIT_FIXTURE_MARKER")
|
|
|
|
|
if marker == "" {
|
|
|
|
|
t.Fatal("COMMANDKIT_FIXTURE_MARKER is empty")
|
|
|
|
|
}
|
|
|
|
|
file, err := os.OpenFile(marker, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o600)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := file.WriteString("candidate suite ran\n"); err != nil {
|
|
|
|
|
file.Close()
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if err := file.Close(); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
`
|