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 }