// Package fileutil provides narrow filesystem helpers for durable artifacts. package fileutil import ( "encoding/json" "fmt" "os" "path/filepath" ) func WriteFileAtomic(path string, data []byte) error { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return fmt.Errorf("create directory %q: %w", filepath.Dir(path), err) } tmp, err := os.CreateTemp(filepath.Dir(path), "."+filepath.Base(path)+".*.tmp") if err != nil { return fmt.Errorf("create temporary file for %q: %w", path, err) } tmpName := tmp.Name() defer os.Remove(tmpName) if _, err := tmp.Write(data); err != nil { tmp.Close() return fmt.Errorf("write temporary file for %q: %w", path, err) } if err := tmp.Close(); err != nil { return fmt.Errorf("close temporary file for %q: %w", path, err) } if err := os.Rename(tmpName, path); err != nil { return fmt.Errorf("save %q: %w", path, err) } return nil } func WriteJSONAtomic(path string, value any) error { data, err := json.MarshalIndent(value, "", " ") if err != nil { return fmt.Errorf("marshal %q: %w", path, err) } return WriteFileAtomic(path, data) } func CopyFileAtomic(source string, target string) error { data, err := os.ReadFile(source) if err != nil { return fmt.Errorf("read %q: %w", source, err) } return WriteFileAtomic(target, data) }