refactor(onboard,api): harden copydir repo-root detection and use platform-neutral proc attrs naming
Address latest review comments from sky5454 in PR #2654. scripts/copydir.go: - Improve repository root detection in a safer, more deterministic way. - Prefer locating repo root from the script source path via runtime.Caller(), then fallback to upward search from current working directory. - Replace .git-only root detection with repository anchor validation: go.sum, LICENSE, and .github must exist. - Keep \ placeholder expansion and existing in-repo path guards. - Preserve destination safety check to prevent deleting/copying to repo root. web/backend/api: - Rename applyLauncherWindowsProcAttrs() to applyLauncherProcAttrs() to expose a platform-independent interface name. - Keep platform-specific behavior split by build tags: windows keeps HideWindow SysProcAttr setup, non-windows remains no-op. - Update gateway startup path to call the renamed helper. Why: - Follow reviewer feedback to avoid relying on .git detection alone and prefer runtime/file-anchor based repository location. - Improve naming clarity by making cross-platform interfaces generic while preserving OS-specific implementation details internally. Validation: - go test ./cmd/picoclaw/internal/onboard - go test ./web/backend/api
This commit is contained in:
parent
494cc381b5
commit
b4a5965602
4 changed files with 32 additions and 6 deletions
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -57,6 +58,17 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func findRepoRoot() (string, error) {
|
func findRepoRoot() (string, error) {
|
||||||
|
_, file, _, ok := runtime.Caller(0)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("unable to locate copydir.go source path")
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptDir := filepath.Dir(file)
|
||||||
|
candidate := filepath.Clean(filepath.Join(scriptDir, ".."))
|
||||||
|
if err := validateRepoRoot(candidate); err == nil {
|
||||||
|
return candidate, nil
|
||||||
|
}
|
||||||
|
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -68,17 +80,31 @@ func findRepoRoot() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if _, err := os.Stat(filepath.Join(cur, ".git")); err == nil {
|
if err := validateRepoRoot(cur); err == nil {
|
||||||
return filepath.Clean(cur), nil
|
return filepath.Clean(cur), nil
|
||||||
}
|
}
|
||||||
parent := filepath.Dir(cur)
|
parent := filepath.Dir(cur)
|
||||||
if parent == cur {
|
if parent == cur {
|
||||||
return "", fmt.Errorf("could not find .git from %s", wd)
|
return "", fmt.Errorf("could not find repository root from %s", wd)
|
||||||
}
|
}
|
||||||
cur = parent
|
cur = parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateRepoRoot(root string) error {
|
||||||
|
anchors := []string{
|
||||||
|
filepath.Join(root, "go.sum"),
|
||||||
|
filepath.Join(root, "LICENSE"),
|
||||||
|
filepath.Join(root, ".github"),
|
||||||
|
}
|
||||||
|
for _, anchor := range anchors {
|
||||||
|
if _, err := os.Stat(anchor); err != nil {
|
||||||
|
return fmt.Errorf("missing repo anchor %s: %w", anchor, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func normalizePathArg(arg, repoRoot string) (string, error) {
|
func normalizePathArg(arg, repoRoot string) (string, error) {
|
||||||
resolved := strings.ReplaceAll(arg, "${codespace}", repoRoot)
|
resolved := strings.ReplaceAll(arg, "${codespace}", repoRoot)
|
||||||
abs, err := filepath.Abs(resolved)
|
abs, err := filepath.Abs(resolved)
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@ func launcherExecCommand(name string, args ...string) *exec.Cmd {
|
||||||
return exec.Command(name, args...)
|
return exec.Command(name, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyLauncherWindowsProcAttrs(_ *exec.Cmd) {}
|
func applyLauncherProcAttrs(_ *exec.Cmd) {}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import (
|
||||||
|
|
||||||
func launcherExecCommand(name string, args ...string) *exec.Cmd {
|
func launcherExecCommand(name string, args ...string) *exec.Cmd {
|
||||||
cmd := exec.Command(name, args...)
|
cmd := exec.Command(name, args...)
|
||||||
applyLauncherWindowsProcAttrs(cmd)
|
applyLauncherProcAttrs(cmd)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyLauncherWindowsProcAttrs(cmd *exec.Cmd) {
|
func applyLauncherProcAttrs(cmd *exec.Cmd) {
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -706,7 +706,7 @@ func (h *Handler) startGatewayLocked(initialStatus string, existingPid int) (int
|
||||||
logger.InfoC("gateway", fmt.Sprintf("Starting gateway process (%s)", execPath))
|
logger.InfoC("gateway", fmt.Sprintf("Starting gateway process (%s)", execPath))
|
||||||
|
|
||||||
cmd = gatewayExecCommand(execPath, h.gatewayCommandArgs()...)
|
cmd = gatewayExecCommand(execPath, h.gatewayCommandArgs()...)
|
||||||
applyLauncherWindowsProcAttrs(cmd)
|
applyLauncherProcAttrs(cmd)
|
||||||
cmd.Env = os.Environ()
|
cmd.Env = os.Environ()
|
||||||
// Forward the launcher's config path via the environment variable that
|
// Forward the launcher's config path via the environment variable that
|
||||||
// GetConfigPath() already reads, so the gateway sub-process uses the same
|
// GetConfigPath() already reads, so the gateway sub-process uses the same
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue