P2 redesign Phase 5 — prune/check/unlock + maintenance ticker + repo stats + pending-runs queue #3
@@ -20,14 +20,26 @@ func (s *fakeSender) Send(e api.Envelope) error {
|
|||||||
|
|
||||||
// setupScript writes a shell script (without shebang) to a temp dir,
|
// setupScript writes a shell script (without shebang) to a temp dir,
|
||||||
// names it "restic", makes it executable, and returns the path.
|
// names it "restic", makes it executable, and returns the path.
|
||||||
|
//
|
||||||
|
// Writes to "<path>.tmp" then renames into place. The rename is what
|
||||||
|
// makes this race-free: under -race + many t.Parallel tests, a
|
||||||
|
// fork-from-another-goroutine can inherit the writable fd from
|
||||||
|
// os.WriteFile before close completes, and exec'ing the file then
|
||||||
|
// returns ETXTBSY ("text file busy"). Once the rename lands, the
|
||||||
|
// final path is a fresh dirent pointing at an inode that has no
|
||||||
|
// writable fd open anywhere — exec is safe.
|
||||||
func setupScript(t *testing.T, body string) string {
|
func setupScript(t *testing.T, body string) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
p := filepath.Join(dir, "restic")
|
final := filepath.Join(dir, "restic")
|
||||||
if err := os.WriteFile(p, []byte("#!/bin/sh\n"+body+"\n"), 0o755); err != nil {
|
tmp := final + ".tmp"
|
||||||
t.Fatalf("setupScript: %v", err)
|
if err := os.WriteFile(tmp, []byte("#!/bin/sh\n"+body+"\n"), 0o755); err != nil {
|
||||||
|
t.Fatalf("setupScript: write tmp: %v", err)
|
||||||
}
|
}
|
||||||
return p
|
if err := os.Rename(tmp, final); err != nil {
|
||||||
|
t.Fatalf("setupScript: rename: %v", err)
|
||||||
|
}
|
||||||
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
// firstEnvOfType returns the first envelope with the given type, or
|
// firstEnvOfType returns the first envelope with the given type, or
|
||||||
|
|||||||
@@ -13,15 +13,23 @@ import (
|
|||||||
// makes it executable, and returns its path. scriptBody is the
|
// makes it executable, and returns its path. scriptBody is the
|
||||||
// complete script content (without the shebang line — that's added
|
// complete script content (without the shebang line — that's added
|
||||||
// automatically).
|
// automatically).
|
||||||
|
// Writes to "<path>.tmp" then renames into place — see the matching
|
||||||
|
// helper in internal/agent/runner/runner_test.go for the ETXTBSY
|
||||||
|
// race rationale. Same fix applied here so this helper doesn't lose
|
||||||
|
// the race the next time CI gets unlucky.
|
||||||
func setupScriptBin(t *testing.T, scriptBody string) string {
|
func setupScriptBin(t *testing.T, scriptBody string) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
p := filepath.Join(dir, "restic")
|
final := filepath.Join(dir, "restic")
|
||||||
|
tmp := final + ".tmp"
|
||||||
content := "#!/bin/sh\n" + scriptBody + "\n"
|
content := "#!/bin/sh\n" + scriptBody + "\n"
|
||||||
if err := os.WriteFile(p, []byte(content), 0o755); err != nil {
|
if err := os.WriteFile(tmp, []byte(content), 0o755); err != nil {
|
||||||
t.Fatalf("setupScriptBin: %v", err)
|
t.Fatalf("setupScriptBin: write tmp: %v", err)
|
||||||
}
|
}
|
||||||
return p
|
if err := os.Rename(tmp, final); err != nil {
|
||||||
|
t.Fatalf("setupScriptBin: rename: %v", err)
|
||||||
|
}
|
||||||
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
// captureLines returns a LineHandler that appends "stream:line" into
|
// captureLines returns a LineHandler that appends "stream:line" into
|
||||||
|
|||||||
Reference in New Issue
Block a user