feat(devops): migrate filesystem operations to io.Local abstraction
Migrate config.go: - os.ReadFile → io.Local.Read Migrate devops.go: - os.Stat → io.Local.IsFile Migrate images.go: - os.MkdirAll → io.Local.EnsureDir - os.Stat → io.Local.IsFile - os.ReadFile → io.Local.Read - os.WriteFile → io.Local.Write Migrate test.go: - os.ReadFile → io.Local.Read - os.Stat → io.Local.IsFile Migrate claude.go: - os.Stat → io.Local.IsDir Updated tests to reflect improved behavior: - Manifest.Save() now creates parent directories - hasFile() correctly returns false for directories Part of #101 (io.Medium migration tracking issue). Closes #107 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
68f2f658f4
commit
e081869ba2
7 changed files with 52 additions and 25 deletions
|
|
@ -7,6 +7,8 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
// ClaudeOptions configures the Claude sandbox session.
|
||||
|
|
@ -124,7 +126,7 @@ func (d *DevOps) CopyGHAuth(ctx context.Context) error {
|
|||
}
|
||||
|
||||
ghConfigDir := filepath.Join(home, ".config", "gh")
|
||||
if _, err := os.Stat(ghConfigDir); os.IsNotExist(err) {
|
||||
if !io.Local.IsDir(ghConfigDir) {
|
||||
return nil // No gh config to copy
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ func LoadConfig() (*Config, error) {
|
|||
return DefaultConfig(), nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
content, err := io.Local.Read(configPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return DefaultConfig(), nil
|
||||
|
|
@ -78,7 +79,7 @@ func LoadConfig() (*Config, error) {
|
|||
}
|
||||
|
||||
cfg := DefaultConfig()
|
||||
if err := yaml.Unmarshal(data, cfg); err != nil {
|
||||
if err := yaml.Unmarshal([]byte(content), cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/container"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
// DevOps manages the portable development environment.
|
||||
|
|
@ -75,8 +76,7 @@ func (d *DevOps) IsInstalled() bool {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = os.Stat(path)
|
||||
return err == nil
|
||||
return io.Local.IsFile(path)
|
||||
}
|
||||
|
||||
// Install downloads and installs the dev image.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/host-uk/core/pkg/devops/sources"
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
)
|
||||
|
||||
// ImageManager handles image downloads and updates.
|
||||
|
|
@ -40,7 +41,7 @@ func NewImageManager(cfg *Config) (*ImageManager, error) {
|
|||
}
|
||||
|
||||
// Ensure images directory exists
|
||||
if err := os.MkdirAll(imagesDir, 0755); err != nil {
|
||||
if err := io.Local.EnsureDir(imagesDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -86,8 +87,7 @@ func (m *ImageManager) IsInstalled() bool {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = os.Stat(path)
|
||||
return err == nil
|
||||
return io.Local.IsFile(path)
|
||||
}
|
||||
|
||||
// Install downloads and installs the dev image.
|
||||
|
|
@ -167,7 +167,7 @@ func loadManifest(path string) (*Manifest, error) {
|
|||
path: path,
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
content, err := io.Local.Read(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return m, nil
|
||||
|
|
@ -175,7 +175,7 @@ func loadManifest(path string) (*Manifest, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, m); err != nil {
|
||||
if err := json.Unmarshal([]byte(content), m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.path = path
|
||||
|
|
@ -189,5 +189,5 @@ func (m *Manifest) Save() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(m.path, data, 0644)
|
||||
return io.Local.Write(m.path, string(data))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,10 +192,13 @@ func TestManifest_Save_Good_CreatesDirs(t *testing.T) {
|
|||
}
|
||||
m.Images["test.img"] = ImageInfo{Version: "1.0.0"}
|
||||
|
||||
// Should fail because nested directories don't exist
|
||||
// (Save doesn't create parent directories, it just writes to path)
|
||||
// Save creates parent directories automatically via io.Local.Write
|
||||
err := m.Save()
|
||||
assert.Error(t, err)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify file was created
|
||||
_, err = os.Stat(nestedPath)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestManifest_Save_Good_Overwrite(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -131,6 +131,6 @@ func TestHasFile_Bad_Directory(t *testing.T) {
|
|||
err := os.Mkdir(subDir, 0755)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// hasFile returns true for directories too (it's just checking existence)
|
||||
assert.True(t, hasFile(tmpDir, "subdir"))
|
||||
// hasFile correctly returns false for directories (only true for regular files)
|
||||
assert.False(t, hasFile(tmpDir, "subdir"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/host-uk/core/pkg/io"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
|
@ -114,13 +114,18 @@ func DetectTestCommand(projectDir string) string {
|
|||
// LoadTestConfig loads .core/test.yaml.
|
||||
func LoadTestConfig(projectDir string) (*TestConfig, error) {
|
||||
path := filepath.Join(projectDir, ".core", "test.yaml")
|
||||
data, err := os.ReadFile(path)
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := io.Local.Read(absPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cfg TestConfig
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
if err := yaml.Unmarshal([]byte(content), &cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -128,12 +133,22 @@ func LoadTestConfig(projectDir string) (*TestConfig, error) {
|
|||
}
|
||||
|
||||
func hasFile(dir, name string) bool {
|
||||
_, err := os.Stat(filepath.Join(dir, name))
|
||||
return err == nil
|
||||
path := filepath.Join(dir, name)
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return io.Local.IsFile(absPath)
|
||||
}
|
||||
|
||||
func hasPackageScript(projectDir, script string) bool {
|
||||
data, err := os.ReadFile(filepath.Join(projectDir, "package.json"))
|
||||
path := filepath.Join(projectDir, "package.json")
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
content, err := io.Local.Read(absPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
@ -141,7 +156,7 @@ func hasPackageScript(projectDir, script string) bool {
|
|||
var pkg struct {
|
||||
Scripts map[string]string `json:"scripts"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &pkg); err != nil {
|
||||
if err := json.Unmarshal([]byte(content), &pkg); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +165,13 @@ func hasPackageScript(projectDir, script string) bool {
|
|||
}
|
||||
|
||||
func hasComposerScript(projectDir, script string) bool {
|
||||
data, err := os.ReadFile(filepath.Join(projectDir, "composer.json"))
|
||||
path := filepath.Join(projectDir, "composer.json")
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
content, err := io.Local.Read(absPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
@ -158,7 +179,7 @@ func hasComposerScript(projectDir, script string) bool {
|
|||
var pkg struct {
|
||||
Scripts map[string]interface{} `json:"scripts"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &pkg); err != nil {
|
||||
if err := json.Unmarshal([]byte(content), &pkg); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue