ci / vet-and-test (pull_request) Successful in 1m3s
Why: the sandbox hides /home and only remounts the resolved Python prefix, so executing python/current/bin/python3 failed before the base layer could be imported. Evidence: go test ./internal/tools/ -run TestResolveInterpreterFollowsManagedSymlink passed, and env doctor against the built base layer printed doctor ok (SELF). Impact: run_python and env doctor follow the symlink into the mounted prefix or /usr/bin/python3. The layer and the prefix mount stay the same. Refs: Agent-System-Backend PR 207 Co-authored-by: Cursor <cursoragent@cursor.com>
151 lines
3.5 KiB
Go
151 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"agent-system-backend/internal/execenv"
|
|
)
|
|
|
|
func runEnv(args []string) int {
|
|
if len(args) == 0 {
|
|
fmt.Fprintln(os.Stderr, "用法:agent-system env <ensure|lock|gc|doctor|rebase|python> --root <dir>")
|
|
return 2
|
|
}
|
|
sub := args[0]
|
|
fs := flag.NewFlagSet("env "+sub, flag.ExitOnError)
|
|
rootPath := fs.String("root", "", "环境根目录")
|
|
ttl := fs.Duration("ttl", 7*24*time.Hour, "gc 保留窗口")
|
|
bin := fs.String("bin", "", "python link 要指向的解释器")
|
|
_ = fs.Parse(args[1:])
|
|
if *rootPath == "" {
|
|
fmt.Fprintln(os.Stderr, "env: --root 必填")
|
|
return 2
|
|
}
|
|
root, err := execenv.Open(*rootPath)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env:", err)
|
|
return 1
|
|
}
|
|
ctx := context.Background()
|
|
switch sub {
|
|
case "ensure":
|
|
py, err := root.EnsurePython(ctx)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env ensure:", err)
|
|
return 1
|
|
}
|
|
id, err := execenv.NewBroker(root, nil, execenv.Pip{Python: py}).EnsureBase(ctx)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env ensure:", err)
|
|
return 1
|
|
}
|
|
fmt.Println(id)
|
|
return runDoctor(ctx, root, py, true)
|
|
case "lock":
|
|
id, err := root.CurrentBase()
|
|
if err != nil || id == "" {
|
|
fmt.Fprintln(os.Stderr, "env lock: 没有 current 基础层")
|
|
return 1
|
|
}
|
|
lock, err := root.ReadLock(id)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env lock:", err)
|
|
return 1
|
|
}
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
if err := enc.Encode(lock); err != nil {
|
|
fmt.Fprintln(os.Stderr, "env lock:", err)
|
|
return 1
|
|
}
|
|
return 0
|
|
case "gc":
|
|
removed, err := execenv.NewBroker(root, nil, execenv.Pip{}).GC(*ttl)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env gc:", err)
|
|
return 1
|
|
}
|
|
for _, id := range removed {
|
|
fmt.Println(id)
|
|
}
|
|
return 0
|
|
case "doctor":
|
|
py, ok := root.PythonBin()
|
|
if !ok {
|
|
py = "/usr/bin/python3"
|
|
}
|
|
return runDoctor(ctx, root, py, true)
|
|
case "rebase":
|
|
py, err := root.EnsurePython(ctx)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env rebase:", err)
|
|
return 1
|
|
}
|
|
id, err := execenv.NewBroker(root, nil, execenv.Pip{Python: py}).Rebase(ctx)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env rebase:", err)
|
|
return 1
|
|
}
|
|
fmt.Println(id)
|
|
return 0
|
|
case "python":
|
|
action := "install"
|
|
if fs.NArg() > 0 {
|
|
action = fs.Arg(0)
|
|
}
|
|
switch action {
|
|
case "install":
|
|
py, err := root.EnsurePython(ctx)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, "env python:", err)
|
|
return 1
|
|
}
|
|
fmt.Println(py)
|
|
return 0
|
|
case "link":
|
|
if *bin == "" {
|
|
fmt.Fprintln(os.Stderr, "env python link: --bin 必填")
|
|
return 2
|
|
}
|
|
if err := root.LinkPython(*bin); err != nil {
|
|
fmt.Fprintln(os.Stderr, "env python link:", err)
|
|
return 1
|
|
}
|
|
fmt.Println(*bin)
|
|
return 0
|
|
default:
|
|
fmt.Fprintln(os.Stderr, "env python: install | link")
|
|
return 2
|
|
}
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "env: 未知动作 %s\n", sub)
|
|
return 2
|
|
}
|
|
}
|
|
|
|
func runDoctor(ctx context.Context, root *execenv.Root, py string, sandbox bool) int {
|
|
var pyPath []string
|
|
if id, err := root.CurrentBase(); err == nil && id != "" {
|
|
pyPath = append(pyPath, root.LayerDir(id))
|
|
}
|
|
mounts := append([]string{}, pyPath...)
|
|
if prefix := root.PythonPrefix(); prefix != "" {
|
|
mounts = append(mounts, prefix)
|
|
}
|
|
if resolved, err := filepath.EvalSymlinks(py); err == nil {
|
|
py = resolved
|
|
}
|
|
if err := execenv.Doctor(ctx, py, pyPath, nil, sandbox, mounts); err != nil {
|
|
fmt.Fprintln(os.Stderr, "env doctor:", err)
|
|
return 1
|
|
}
|
|
fmt.Println("doctor ok")
|
|
return 0
|
|
}
|