fix: crew start rig inference + refactor overlay to shared utility
Two improvements: 1. gt crew start now infers rig from cwd when first arg is not a valid rig name (gt-czltv). Previously, running `gt crew start bob` from within a rig directory would fail because "bob" was treated as the rig name. Now it checks if the arg is a valid rig first. 2. Refactored copyOverlay to shared rig.CopyOverlay utility: - Eliminates code duplication between crew and polecat managers - Preserves source file permissions instead of hardcoding 0644 - Follows PR #278 improvements Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
86
internal/rig/overlay.go
Normal file
86
internal/rig/overlay.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package rig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// CopyOverlay copies files from <rigPath>/.runtime/overlay/ to the destination path.
|
||||
// This allows storing gitignored files (like .env) that services need at their root.
|
||||
// The overlay is copied non-recursively - only files, not subdirectories.
|
||||
// File permissions from the source are preserved.
|
||||
//
|
||||
// Structure:
|
||||
//
|
||||
// rig/
|
||||
// .runtime/
|
||||
// overlay/
|
||||
// .env <- Copied to destPath
|
||||
// config.json <- Copied to destPath
|
||||
//
|
||||
// Returns nil if the overlay directory doesn't exist (nothing to copy).
|
||||
// Individual file copy failures are logged as warnings but don't stop the process.
|
||||
func CopyOverlay(rigPath, destPath string) error {
|
||||
overlayDir := filepath.Join(rigPath, ".runtime", "overlay")
|
||||
|
||||
// Check if overlay directory exists
|
||||
entries, err := os.ReadDir(overlayDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// No overlay directory - not an error, just nothing to copy
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("reading overlay dir: %w", err)
|
||||
}
|
||||
|
||||
// Copy each file (not directories) from overlay to destination
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
// Skip subdirectories - only copy files at overlay root
|
||||
continue
|
||||
}
|
||||
|
||||
srcPath := filepath.Join(overlayDir, entry.Name())
|
||||
dstPath := filepath.Join(destPath, entry.Name())
|
||||
|
||||
if err := copyFilePreserveMode(srcPath, dstPath); err != nil {
|
||||
// Log warning but continue - don't fail spawn for overlay issues
|
||||
fmt.Printf("Warning: could not copy overlay file %s: %v\n", entry.Name(), err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFilePreserveMode copies a file from src to dst, preserving the source file's permissions.
|
||||
func copyFilePreserveMode(src, dst string) error {
|
||||
// Get source file info for permissions
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat source: %w", err)
|
||||
}
|
||||
|
||||
// Open source file
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open source: %w", err)
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Create destination file with same permissions
|
||||
dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcInfo.Mode().Perm())
|
||||
if err != nil {
|
||||
return fmt.Errorf("create destination: %w", err)
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
// Copy contents
|
||||
if _, err := io.Copy(dstFile, srcFile); err != nil {
|
||||
return fmt.Errorf("copy contents: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user