6fe28464d5
CopySeedDir is the v0.3 user seed overlay primitive: recursive copy from a seed directory to a workspace, overwriting destination files. task.yaml and the .ctask metadata directory at the seed root are intentionally skipped so a stale or hostile seed cannot overwrite ctask-owned state. Missing src is a no-op (the seed directory is optional).
94 lines
1.9 KiB
Go
94 lines
1.9 KiB
Go
package seed
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// CopySeedDir recursively copies the contents of srcDir into dstDir.
|
|
// Files at the destination are overwritten. Subdirectories are preserved.
|
|
//
|
|
// The following entries at the top level of srcDir are intentionally skipped:
|
|
// - task.yaml (ctask owns this file)
|
|
// - .ctask (directory; reserved for ctask metadata)
|
|
//
|
|
// If srcDir does not exist, this function returns nil (no-op). Other I/O errors
|
|
// are returned to the caller.
|
|
func CopySeedDir(srcDir, dstDir string) error {
|
|
info, err := os.Stat(srcDir)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
entries, err := os.ReadDir(srcDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, entry := range entries {
|
|
name := entry.Name()
|
|
if name == "task.yaml" || name == ".ctask" {
|
|
continue
|
|
}
|
|
srcPath := filepath.Join(srcDir, name)
|
|
dstPath := filepath.Join(dstDir, name)
|
|
if err := copyEntry(srcPath, dstPath); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func copyEntry(src, dst string) error {
|
|
info, err := os.Stat(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
if err := os.MkdirAll(dst, 0755); err != nil {
|
|
return err
|
|
}
|
|
entries, err := os.ReadDir(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, e := range entries {
|
|
if err := copyEntry(filepath.Join(src, e.Name()), filepath.Join(dst, e.Name())); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
return copyFile(src, dst, info.Mode())
|
|
}
|
|
|
|
func copyFile(src, dst string, mode os.FileMode) error {
|
|
in, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer in.Close()
|
|
|
|
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
|
|
return err
|
|
}
|
|
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode.Perm())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
|
|
if _, err := io.Copy(out, in); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|