diff --git a/core-test b/core-test index 9b746b57..65048b84 100755 Binary files a/core-test and b/core-test differ diff --git a/internal/cmd/dev/cmd_apply.go b/internal/cmd/dev/cmd_apply.go index 25a9646f..21bd1b0f 100644 --- a/internal/cmd/dev/cmd_apply.go +++ b/internal/cmd/dev/cmd_apply.go @@ -18,6 +18,7 @@ import ( "github.com/host-uk/core/pkg/errors" "github.com/host-uk/core/pkg/git" "github.com/host-uk/core/pkg/i18n" + "github.com/host-uk/core/pkg/io" "github.com/host-uk/core/pkg/repos" ) @@ -76,8 +77,8 @@ func runApply() error { // Validate script exists if applyScript != "" { - if _, err := os.Stat(applyScript); err != nil { - return errors.E("dev.apply", "script not found: "+applyScript, err) + if !io.Local.IsFile(applyScript) { + return errors.E("dev.apply", "script not found: "+applyScript, nil) // Error mismatch? IsFile returns bool } } diff --git a/internal/cmd/dev/cmd_file_sync.go b/internal/cmd/dev/cmd_file_sync.go index 45ef2c94..4886683a 100644 --- a/internal/cmd/dev/cmd_file_sync.go +++ b/internal/cmd/dev/cmd_file_sync.go @@ -9,7 +9,6 @@ package dev import ( "context" - "io" "os" "os/exec" "path/filepath" @@ -19,6 +18,7 @@ import ( "github.com/host-uk/core/pkg/errors" "github.com/host-uk/core/pkg/git" "github.com/host-uk/core/pkg/i18n" + coreio "github.com/host-uk/core/pkg/io" "github.com/host-uk/core/pkg/repos" ) @@ -63,7 +63,24 @@ func runFileSync(source string) error { } // Validate source exists - sourceInfo, err := os.Stat(source) + sourceInfo, err := os.Stat(source) // Keep os.Stat for local source check or use coreio? coreio.Local.IsFile is bool. + // If source is local file on disk (not in medium), we can use os.Stat. + // But concept is everything is via Medium? + // User is running CLI on host. `source` is relative to CWD. + // coreio.Local uses absolute path or relative to root (which is "/" by default). + // So coreio.Local works. + if !coreio.Local.IsFile(source) { + // Might be directory + // IsFile returns false for directory. + } + // Let's rely on os.Stat for initial source check to distinguish dir vs file easily if coreio doesn't expose Stat. + // coreio doesn't expose Stat. + + // Check using standard os for source determination as we are outside strict sandbox for input args potentially? + // But we should use coreio where possible. + // coreio.Local.List worked for dirs. + // Let's stick to os.Stat for source properties finding as typically allowed for CLI args. + if err != nil { return errors.E("dev.sync", i18n.T("cmd.dev.file_sync.error.source_not_found", map[string]interface{}{"Path": source}), err) } @@ -113,7 +130,9 @@ func runFileSync(source string) error { continue } } else { - if err := copyFile(source, destPath); err != nil { + // Ensure dir exists + coreio.Local.EnsureDir(filepath.Dir(destPath)) + if err := coreio.Copy(coreio.Local, source, coreio.Local, destPath); err != nil { cli.Print(" %s %s: copy failed: %s\n", errorStyle.Render("x"), repoName, err) failed++ continue @@ -287,47 +306,14 @@ func gitCommandQuiet(ctx context.Context, dir string, args ...string) (string, e return string(output), nil } -// copyFile copies a single file -func copyFile(src, dst string) error { - // Ensure parent directory exists - if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { - return err - } - - srcFile, err := os.Open(src) - if err != nil { - return err - } - defer srcFile.Close() - - srcInfo, err := srcFile.Stat() - if err != nil { - return err - } - - dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcInfo.Mode()) - if err != nil { - return err - } - defer dstFile.Close() - - _, err = io.Copy(dstFile, srcFile) - return err -} - // copyDir recursively copies a directory func copyDir(src, dst string) error { - srcInfo, err := os.Stat(src) + entries, err := coreio.Local.List(src) if err != nil { return err } - if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil { - return err - } - - entries, err := os.ReadDir(src) - if err != nil { + if err := coreio.Local.EnsureDir(dst); err != nil { return err } @@ -340,7 +326,7 @@ func copyDir(src, dst string) error { return err } } else { - if err := copyFile(srcPath, dstPath); err != nil { + if err := coreio.Copy(coreio.Local, srcPath, coreio.Local, dstPath); err != nil { return err } } diff --git a/internal/cmd/dev/cmd_workflow.go b/internal/cmd/dev/cmd_workflow.go index 354f9387..98df5085 100644 --- a/internal/cmd/dev/cmd_workflow.go +++ b/internal/cmd/dev/cmd_workflow.go @@ -1,13 +1,13 @@ package dev import ( - "os" "path/filepath" "sort" "strings" "github.com/host-uk/core/pkg/cli" "github.com/host-uk/core/pkg/i18n" + "github.com/host-uk/core/pkg/io" ) // Workflow command flags @@ -156,7 +156,7 @@ func runWorkflowSync(registryPath string, workflowFile string, dryRun bool) erro } // Read template content - templateContent, err := os.ReadFile(templatePath) + templateContent, err := io.Local.Read(templatePath) if err != nil { return cli.Wrap(err, i18n.T("cmd.dev.workflow.read_template_error")) } @@ -189,8 +189,8 @@ func runWorkflowSync(registryPath string, workflowFile string, dryRun bool) erro destPath := filepath.Join(destDir, workflowFile) // Check if workflow already exists and is identical - if existingContent, err := os.ReadFile(destPath); err == nil { - if string(existingContent) == string(templateContent) { + if existingContent, err := io.Local.Read(destPath); err == nil { + if existingContent == templateContent { cli.Print(" %s %s %s\n", dimStyle.Render("-"), repoNameStyle.Render(repo.Name), @@ -210,7 +210,7 @@ func runWorkflowSync(registryPath string, workflowFile string, dryRun bool) erro } // Create .github/workflows directory if needed - if err := os.MkdirAll(destDir, 0755); err != nil { + if err := io.Local.EnsureDir(destDir); err != nil { cli.Print(" %s %s %s\n", errorStyle.Render(cli.Glyph(":cross:")), repoNameStyle.Render(repo.Name), @@ -220,7 +220,7 @@ func runWorkflowSync(registryPath string, workflowFile string, dryRun bool) erro } // Write workflow file - if err := os.WriteFile(destPath, templateContent, 0644); err != nil { + if err := io.Local.Write(destPath, templateContent); err != nil { cli.Print(" %s %s %s\n", errorStyle.Render(cli.Glyph(":cross:")), repoNameStyle.Render(repo.Name), @@ -264,7 +264,7 @@ func findWorkflows(dir string) []string { workflowsDir = dir } - entries, err := os.ReadDir(workflowsDir) + entries, err := io.Local.List(workflowsDir) if err != nil { return nil } @@ -298,7 +298,7 @@ func findTemplateWorkflow(registryDir, workflowFile string) string { } for _, candidate := range candidates { - if _, err := os.Stat(candidate); err == nil { + if io.Local.IsFile(candidate) { return candidate } }