Honor cancellation throughout publication recovery Git operations
This commit is contained in:
@@ -73,6 +73,28 @@ func MetadataDirectories(ctx context.Context, directory string) ([]string, error
|
||||
return directories, nil
|
||||
}
|
||||
|
||||
// CommonDirectory resolves the shared Git metadata directory.
|
||||
func CommonDirectory(ctx context.Context, directory string) (string, error) {
|
||||
output, err := run(ctx, directory, nil, "rev-parse", "--path-format=absolute", "--git-common-dir")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("resolve shared Git metadata directory: %w", err)
|
||||
}
|
||||
return normalizeRepositoryRoot(output)
|
||||
}
|
||||
|
||||
// IsIgnored checks whether Git excludes the selected worktree path.
|
||||
func IsIgnored(ctx context.Context, directory, path string) (bool, error) {
|
||||
_, err := run(ctx, directory, nil, "check-ignore", "-q", "--", path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// DiscoverRepositoryRoot finds a containing checkout or confirms standalone use.
|
||||
func DiscoverRepositoryRoot(ctx context.Context, directory string) (string, bool, error) {
|
||||
if directory == "" {
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -26,11 +25,14 @@ func ListAttempts(ctx context.Context, repositoryDir, override string) (Attempts
|
||||
repositoryDir = "."
|
||||
}
|
||||
result := AttemptsResult{Attempts: []Attempt{}, Diagnostics: []*RecoveryError{}}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return result, recoveryFailure(ctx, "storage_enumeration", err)
|
||||
}
|
||||
var directory string
|
||||
{
|
||||
resolved, err := recordDirectoryFor(ctx, repositoryDir, override)
|
||||
if err != nil {
|
||||
return result, &RecoveryError{Kind: "storage_enumeration", cause: err}
|
||||
return result, recoveryFailure(ctx, "storage_enumeration", err)
|
||||
}
|
||||
directory = resolved
|
||||
}
|
||||
@@ -40,7 +42,7 @@ func ListAttempts(ctx context.Context, repositoryDir, override string) (Attempts
|
||||
}
|
||||
entries, err := os.ReadDir(directory)
|
||||
if err != nil {
|
||||
return result, &RecoveryError{Kind: "storage_enumeration", cause: fmt.Errorf("enumerate records in %q: %w", directory, err)}
|
||||
return result, recoveryFailure(ctx, "storage_enumeration", fmt.Errorf("enumerate records in %q: %w", directory, err))
|
||||
}
|
||||
paths := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
@@ -75,17 +77,6 @@ func recordDirectoryFor(ctx context.Context, repositoryDir, override string) (st
|
||||
if filepath.IsAbs(override) {
|
||||
return filepath.Clean(override), nil
|
||||
}
|
||||
root, err := exec.CommandContext(ctx, "git", "-C", repositoryDir, "rev-parse", "--show-toplevel").Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rootPath := strings.TrimSpace(string(root))
|
||||
if override != "" {
|
||||
return filepath.Join(rootPath, override), nil
|
||||
}
|
||||
common, err := exec.CommandContext(ctx, "git", "-C", repositoryDir, "rev-parse", "--path-format=absolute", "--git-common-dir").Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(strings.TrimSpace(string(common)), "releasekit", "publication-attempts"), nil
|
||||
directory, _, err := resolveRecordDir(ctx, repositoryDir, override)
|
||||
return directory, err
|
||||
}
|
||||
|
||||
147
internal/publish/cancellation_test.go
Normal file
147
internal/publish/cancellation_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package publish
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRecoveryAlreadyCanceledDiscovery(t *testing.T) {
|
||||
for _, override := range []string{"", "relative"} {
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
cancel()
|
||||
result, err := ListAttempts(ctx, t.TempDir(), override)
|
||||
requireCanceledRecovery(t, err)
|
||||
if len(result.Attempts) != 0 {
|
||||
t.Fatalf("unexpected attempts: %#v", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func requireCanceledRecovery(t *testing.T, err error) {
|
||||
t.Helper()
|
||||
var recovery *RecoveryError
|
||||
if !errors.As(err, &recovery) || recovery.Kind != "canceled" || !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("cancellation = %v; want canceled kind and cause", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecoveryCancelsActiveGitOperations(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture uses a POSIX Git launcher")
|
||||
}
|
||||
tests := []struct {
|
||||
name, command string
|
||||
run func(context.Context, string) error
|
||||
}{
|
||||
{"default discovery root", "--show-toplevel", func(ctx context.Context, repo string) error { _, err := ListAttempts(ctx, repo, ""); return err }},
|
||||
{"relative discovery root", "--show-toplevel", func(ctx context.Context, repo string) error { _, err := ListAttempts(ctx, repo, "records"); return err }},
|
||||
{"default discovery metadata", "--git-common-dir", func(ctx context.Context, repo string) error { _, err := ListAttempts(ctx, repo, ""); return err }},
|
||||
{"record metadata", "--git-common-dir", func(ctx context.Context, repo string) error {
|
||||
_, err := SaveEvidence(ctx, repo, "", Evidence{})
|
||||
return err
|
||||
}},
|
||||
{"record ignore guard", "check-ignore", func(ctx context.Context, repo string) error {
|
||||
_, err := SaveEvidence(ctx, repo, "records", Evidence{})
|
||||
return err
|
||||
}},
|
||||
{"publication metadata", "--git-common-dir", func(ctx context.Context, repo string) error {
|
||||
result, err := Publish(ctx, Request{RepositoryDir: repo, Product: "Sample", Version: "v1.2.3"})
|
||||
if !errors.Is(result.RecordError, context.Canceled) {
|
||||
return fmt.Errorf("record cancellation lost: %v", result.RecordError)
|
||||
}
|
||||
return err
|
||||
}},
|
||||
{"tag validation", "check-ref-format", func(ctx context.Context, repo string) error {
|
||||
_, err := Reconcile(ctx, Evidence{RepositoryDir: repo, Endpoint: repo, Tag: "v1.2.3"})
|
||||
return err
|
||||
}},
|
||||
{"tag query", "ls-remote", func(ctx context.Context, repo string) error {
|
||||
_, err := Reconcile(ctx, Evidence{RepositoryDir: repo, Endpoint: repo, Tag: "v1.2.3"})
|
||||
return err
|
||||
}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
repo := recordRepo(t)
|
||||
if test.name == "publication metadata" {
|
||||
if err := os.WriteFile(filepath.Join(repo, "go.mod"), []byte("module example.com/sample\n\ngo 1.26.0\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(repo, "docs", "releases"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(repo, "docs", "releases", "v1.2.3.md"), []byte("# Sample v1.2.3\n\nChanges.\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recordGit(t, repo, "checkout", "-B", "main")
|
||||
recordGit(t, repo, "add", ".")
|
||||
recordGit(t, repo, "commit", "-qm", "Release candidate")
|
||||
}
|
||||
realGit, err := exec.LookPath("git")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bin := t.TempDir()
|
||||
ready := filepath.Join(t.TempDir(), "ready")
|
||||
quote := func(s string) string { return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'" }
|
||||
script := "#!/bin/sh\nfor arg in \"$@\"; do\n if [ \"$arg\" = " + quote(test.command) + " ]; then\n echo $$ > " + quote(ready) + "\n exec sleep 600\n fi\ndone\nexec " + quote(realGit) + " \"$@\"\n"
|
||||
if err := os.WriteFile(filepath.Join(bin, "git"), []byte(script), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- test.run(ctx, repo) }()
|
||||
// Synchronize cancellation with the intercepted operation, rather than timing its start.
|
||||
ticker := time.NewTicker(5 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
timeout := time.NewTimer(10 * time.Second)
|
||||
defer timeout.Stop()
|
||||
var pid int
|
||||
waiting:
|
||||
for {
|
||||
select {
|
||||
case err := <-done:
|
||||
t.Fatalf("operation finished before interception: %v", err)
|
||||
case <-timeout.C:
|
||||
t.Fatal("Git operation was never intercepted")
|
||||
case <-ticker.C:
|
||||
data, err := os.ReadFile(ready)
|
||||
if err == nil {
|
||||
pid, err = strconv.Atoi(strings.TrimSpace(string(data)))
|
||||
if err == nil {
|
||||
break waiting
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
select {
|
||||
case err := <-done:
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("lost context cause: %v", err)
|
||||
}
|
||||
if strings.Contains(test.name, "discovery") || test.command == "check-ref-format" || test.command == "ls-remote" {
|
||||
requireCanceledRecovery(t, err)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
process, _ := os.FindProcess(pid)
|
||||
if process != nil {
|
||||
_ = process.Kill()
|
||||
}
|
||||
<-done
|
||||
t.Fatal("cancellation did not stop the Git operation")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func Publish(ctx context.Context, request Request) (result Result, resultErr err
|
||||
return
|
||||
}
|
||||
recorded = true
|
||||
path, err := saveEvidenceInDir(result.RepositoryDir, repositoryRoot, request.RecordDir, recordDir, result.Evidence)
|
||||
path, err := saveEvidenceInDir(ctx, result.RepositoryDir, repositoryRoot, request.RecordDir, recordDir, result.Evidence)
|
||||
if err != nil {
|
||||
result.RecordError = err
|
||||
return
|
||||
@@ -118,7 +118,7 @@ func Publish(ctx context.Context, request Request) (result Result, resultErr err
|
||||
initialized = true
|
||||
result.Evidence.RecordedAt = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
var storageErr error
|
||||
recordDir, repositoryRoot, storageErr = resolveRecordDir(captured.RepositoryDir, request.RecordDir)
|
||||
recordDir, repositoryRoot, storageErr = resolveRecordDir(ctx, captured.RepositoryDir, request.RecordDir)
|
||||
if storageErr != nil {
|
||||
result.RecordError = &RecoveryError{Kind: "record_write", cause: storageErr}
|
||||
recordDir = ""
|
||||
@@ -200,7 +200,7 @@ func Publish(ctx context.Context, request Request) (result Result, resultErr err
|
||||
result.State = Unknown
|
||||
if !recorded {
|
||||
recorded = true
|
||||
path, recordErr := saveEvidenceInDir(result.RepositoryDir, repositoryRoot, request.RecordDir, recordDir, result.Evidence)
|
||||
path, recordErr := saveEvidenceInDir(ctx, result.RepositoryDir, repositoryRoot, request.RecordDir, recordDir, result.Evidence)
|
||||
if recordErr != nil {
|
||||
result.RecordError = recordErr
|
||||
} else {
|
||||
|
||||
@@ -108,7 +108,7 @@ func observe(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
}
|
||||
valid, err := git.ValidateTagRef(ctx, evidence.RepositoryDir, evidence.Tag)
|
||||
if err != nil {
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: "remote_observation", cause: err}
|
||||
return Observation{Evidence: evidence, State: Unknown}, recoveryFailure(ctx, "remote_observation", err)
|
||||
}
|
||||
if !valid {
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: "invalid_evidence"}
|
||||
@@ -129,7 +129,7 @@ func observe(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
}
|
||||
ref, err := endpoint.QueryTag(ctx, evidence.RepositoryDir, evidence.Tag)
|
||||
if err != nil {
|
||||
return Observation{Evidence: evidence, State: Unknown}, &RecoveryError{Kind: "remote_observation", cause: err}
|
||||
return Observation{Evidence: evidence, State: Unknown}, recoveryFailure(ctx, "remote_observation", err)
|
||||
}
|
||||
result, err := classify(evidence, ref)
|
||||
if err != nil && ctx.Err() != nil {
|
||||
@@ -142,3 +142,11 @@ func observe(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
func Reconcile(ctx context.Context, evidence Evidence) (Observation, error) {
|
||||
return observe(ctx, evidence)
|
||||
}
|
||||
|
||||
// recoveryFailure classifies failed operations while preserving caller cancellation.
|
||||
func recoveryFailure(ctx context.Context, kind string, err error) *RecoveryError {
|
||||
if contextErr := ctx.Err(); contextErr != nil {
|
||||
return &RecoveryError{Kind: "canceled", cause: contextErr}
|
||||
}
|
||||
return &RecoveryError{Kind: kind, cause: err}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -53,19 +52,19 @@ func LoadEvidence(path string) (Evidence, error) {
|
||||
}
|
||||
|
||||
// SaveEvidence writes one private attempt record and returns its absolute path.
|
||||
func SaveEvidence(repositoryDir, override string, evidence Evidence) (string, error) {
|
||||
recordDir, root, err := resolveRecordDir(repositoryDir, override)
|
||||
func SaveEvidence(ctx context.Context, repositoryDir, override string, evidence Evidence) (string, error) {
|
||||
recordDir, root, err := resolveRecordDir(ctx, repositoryDir, override)
|
||||
if err != nil {
|
||||
return "", &RecoveryError{Kind: "record_write", cause: err}
|
||||
}
|
||||
return saveEvidenceInDir(repositoryDir, root, override, recordDir, evidence)
|
||||
return saveEvidenceInDir(ctx, repositoryDir, root, override, recordDir, evidence)
|
||||
}
|
||||
|
||||
func saveEvidenceInDir(repositoryDir, root, override, recordDir string, evidence Evidence) (string, error) {
|
||||
return saveEvidenceNamed(repositoryDir, root, override, recordDir, evidence, proposedRecordPath)
|
||||
func saveEvidenceInDir(ctx context.Context, repositoryDir, root, override, recordDir string, evidence Evidence) (string, error) {
|
||||
return saveEvidenceNamed(ctx, repositoryDir, root, override, recordDir, evidence, proposedRecordPath)
|
||||
}
|
||||
|
||||
func saveEvidenceNamed(repositoryDir, root, override, recordDir string, evidence Evidence, nextPath func(string) (string, error)) (string, error) {
|
||||
func saveEvidenceNamed(ctx context.Context, repositoryDir, root, override, recordDir string, evidence Evidence, nextPath func(string) (string, error)) (string, error) {
|
||||
if evidence.RecordedAt == "" {
|
||||
evidence.RecordedAt = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
}
|
||||
@@ -86,7 +85,7 @@ func saveEvidenceNamed(repositoryDir, root, override, recordDir string, evidence
|
||||
return "", &RecoveryError{Kind: "record_write", cause: fmt.Errorf("name publication record: %w", err)}
|
||||
}
|
||||
if override != "" && root != "" {
|
||||
if err := rejectUnignoredWorktreePath(repositoryDir, root, path); err != nil {
|
||||
if err := rejectUnignoredWorktreePath(ctx, repositoryDir, root, path); err != nil {
|
||||
return "", &RecoveryError{Kind: "record_write", cause: err}
|
||||
}
|
||||
}
|
||||
@@ -111,14 +110,10 @@ func saveEvidenceNamed(repositoryDir, root, override, recordDir string, evidence
|
||||
return "", &RecoveryError{Kind: "record_write", cause: fmt.Errorf("could not allocate a unique record name in %q", recordDir)}
|
||||
}
|
||||
|
||||
func resolveRecordDir(repositoryDir, override string) (string, string, error) {
|
||||
rootOutput, err := exec.Command("git", "-C", repositoryDir, "rev-parse", "--show-toplevel").Output()
|
||||
func resolveRecordDir(ctx context.Context, repositoryDir, override string) (string, string, error) {
|
||||
root, err := git.RepositoryRoot(ctx, repositoryDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("resolve source repository root: %w", err)
|
||||
}
|
||||
root, err := filepath.Abs(strings.TrimSpace(string(rootOutput)))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("resolve source repository root: %w", err)
|
||||
return "", "", err
|
||||
}
|
||||
if override != "" {
|
||||
if filepath.IsAbs(override) {
|
||||
@@ -126,11 +121,11 @@ func resolveRecordDir(repositoryDir, override string) (string, string, error) {
|
||||
}
|
||||
return filepath.Join(root, override), root, nil
|
||||
}
|
||||
common, err := exec.Command("git", "-C", repositoryDir, "rev-parse", "--path-format=absolute", "--git-common-dir").Output()
|
||||
common, err := git.CommonDirectory(ctx, repositoryDir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("resolve shared Git metadata directory: %w", err)
|
||||
return "", "", err
|
||||
}
|
||||
return filepath.Join(strings.TrimSpace(string(common)), "releasekit", "publication-attempts"), root, nil
|
||||
return filepath.Join(common, "releasekit", "publication-attempts"), root, nil
|
||||
}
|
||||
|
||||
func proposedRecordPath(directory string) (string, error) {
|
||||
@@ -141,7 +136,7 @@ func proposedRecordPath(directory string) (string, error) {
|
||||
return filepath.Join(directory, "attempt-"+hex.EncodeToString(nonce[:])+".json"), nil
|
||||
}
|
||||
|
||||
func rejectUnignoredWorktreePath(repositoryDir, root, proposed string) error {
|
||||
func rejectUnignoredWorktreePath(ctx context.Context, repositoryDir, root, proposed string) error {
|
||||
parent, err := filepath.EvalSymlinks(filepath.Dir(proposed))
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolve record directory %q: %w", filepath.Dir(proposed), err)
|
||||
@@ -151,7 +146,7 @@ func rejectUnignoredWorktreePath(repositoryDir, root, proposed string) error {
|
||||
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
return nil
|
||||
}
|
||||
metadata, err := git.MetadataDirectories(context.Background(), repositoryDir)
|
||||
metadata, err := git.MetadataDirectories(ctx, repositoryDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -161,14 +156,12 @@ func rejectUnignoredWorktreePath(repositoryDir, root, proposed string) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
cmd := exec.Command("git", "-C", repositoryDir, "check-ignore", "-q", "--", actual)
|
||||
err = cmd.Run()
|
||||
if err == nil {
|
||||
return nil
|
||||
ignored, err := git.IsIgnored(ctx, repositoryDir, actual)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check Git ignore rules for record path %q: %w", actual, err)
|
||||
}
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 {
|
||||
if !ignored {
|
||||
return fmt.Errorf("record path %q is inside the worktree and is not ignored", actual)
|
||||
}
|
||||
return fmt.Errorf("check Git ignore rules for record path %q: %w", actual, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -74,11 +74,11 @@ func TestSaveEvidenceUsesPrivateUniqueRecordsAndLinkedWorktreeMetadata(t *testin
|
||||
worktree := filepath.Join(t.TempDir(), "linked")
|
||||
recordGit(t, repo, "worktree", "add", "--detach", worktree)
|
||||
evidence := Evidence{Tag: "v1.2.3", Endpoint: "https://user:secret@example.invalid/repo.git"}
|
||||
first, err := SaveEvidence(worktree, "", evidence)
|
||||
first, err := SaveEvidence(t.Context(), worktree, "", evidence)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := SaveEvidence(worktree, "", evidence)
|
||||
second, err := SaveEvidence(t.Context(), worktree, "", evidence)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -129,14 +129,14 @@ func TestSaveEvidenceResolvesRelativeAndIgnoredOverrides(t *testing.T) {
|
||||
if err := os.WriteFile(filepath.Join(repo, ".gitignore"), []byte("saved-records/\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := SaveEvidence(repo, "saved-records", Evidence{Tag: "v1.2.3"}); err != nil {
|
||||
if _, err := SaveEvidence(t.Context(), repo, "saved-records", Evidence{Tag: "v1.2.3"}); err != nil {
|
||||
t.Fatalf("ignored override: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(repo, "saved-records")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
external := filepath.Join(t.TempDir(), "external-store")
|
||||
path, err := SaveEvidence(repo, external, Evidence{Tag: "v2.0.0"})
|
||||
path, err := SaveEvidence(t.Context(), repo, external, Evidence{Tag: "v2.0.0"})
|
||||
if err != nil || !strings.HasPrefix(path, external+string(filepath.Separator)) {
|
||||
t.Fatalf("absolute external override path=%q error=%v", path, err)
|
||||
}
|
||||
@@ -154,13 +154,13 @@ func TestSaveEvidenceAllowsExplicitGitMetadataStores(t *testing.T) {
|
||||
case "separate":
|
||||
recordGit(t, repo, "init", "--separate-git-dir", filepath.Join(t.TempDir(), "metadata"))
|
||||
}
|
||||
defaultPath, err := SaveEvidence(repo, "", Evidence{})
|
||||
defaultPath, err := SaveEvidence(t.Context(), repo, "", Evidence{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
privateDir := strings.TrimSuffix(recordGit(t, repo, "rev-parse", "--absolute-git-dir"), "\n")
|
||||
for _, directory := range []string{filepath.Dir(defaultPath), filepath.Join(privateDir, "other-private-records")} {
|
||||
path, err := SaveEvidence(repo, directory, Evidence{Tag: "v1.2.3"})
|
||||
path, err := SaveEvidence(t.Context(), repo, directory, Evidence{Tag: "v1.2.3"})
|
||||
if err != nil || filepath.Dir(path) != directory {
|
||||
t.Fatalf("explicit metadata store %q = %q, %v", directory, path, err)
|
||||
}
|
||||
@@ -174,7 +174,7 @@ func TestSaveEvidenceAllowsExplicitGitMetadataStores(t *testing.T) {
|
||||
|
||||
func TestSaveEvidenceRejectsUnignoredOverrideInsideWorktree(t *testing.T) {
|
||||
repo := recordRepo(t)
|
||||
_, err := SaveEvidence(repo, "records", Evidence{Tag: "v1.2.3"})
|
||||
_, err := SaveEvidence(t.Context(), repo, "records", Evidence{Tag: "v1.2.3"})
|
||||
var recovery *RecoveryError
|
||||
if !errors.As(err, &recovery) || recovery.Kind != "record_write" {
|
||||
t.Fatalf("SaveEvidence() error=%v", err)
|
||||
@@ -198,14 +198,14 @@ func TestSaveEvidenceChecksEachActualFilename(t *testing.T) {
|
||||
next := func(name string) func(string) (string, error) {
|
||||
return func(directory string) (string, error) { return filepath.Join(directory, name), nil }
|
||||
}
|
||||
path, err := saveEvidenceNamed(repo, repo, dir, dir, Evidence{Tag: "v1.0.0"}, next("attempt-0.json"))
|
||||
path, err := saveEvidenceNamed(t.Context(), repo, repo, dir, dir, Evidence{Tag: "v1.0.0"}, next("attempt-0.json"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recordGit(t, repo, "check-ignore", "--", path)
|
||||
// A collision must validate the replacement name as well.
|
||||
calls := 0
|
||||
_, err = saveEvidenceNamed(repo, repo, dir, dir, Evidence{Tag: "v2.0.0"}, func(directory string) (string, error) {
|
||||
_, err = saveEvidenceNamed(t.Context(), repo, repo, dir, dir, Evidence{Tag: "v2.0.0"}, func(directory string) (string, error) {
|
||||
calls++
|
||||
name := "attempt-0.json"
|
||||
if calls > 1 {
|
||||
@@ -234,8 +234,8 @@ func TestListAttemptsReturnsReadableRecordsAndDiagnosticsInPathOrder(t *testing.
|
||||
if err := os.Mkdir(directory, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, _ := SaveEvidence(repo, directory, Evidence{Tag: "v2.0.0"})
|
||||
first, _ := SaveEvidence(repo, directory, Evidence{Tag: "v1.0.0"})
|
||||
second, _ := SaveEvidence(t.Context(), repo, directory, Evidence{Tag: "v2.0.0"})
|
||||
first, _ := SaveEvidence(t.Context(), repo, directory, Evidence{Tag: "v1.0.0"})
|
||||
if err := os.WriteFile(filepath.Join(directory, "broken.json"), []byte("{"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user