Prepare recovery tools for the host target
This commit is contained in:
@@ -57,8 +57,8 @@ func TestVendoredAPIOnlyPublicationRecovery(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
launcher := exec.Command(goBinary, append(launcherArgs, "publish", "--repo", repo, "v1.2.3")...)
|
launcher := exec.Command(goBinary, append(launcherArgs.Args, "publish", "--repo", repo, "v1.2.3")...)
|
||||||
launcher.Dir = t.TempDir()
|
launcher.Dir = launcherArgs.Directory
|
||||||
publicationOutput, err := launcher.CombinedOutput()
|
publicationOutput, err := launcher.CombinedOutput()
|
||||||
var exitError *exec.ExitError
|
var exitError *exec.ExitError
|
||||||
if !errors.As(err, &exitError) || exitError.ExitCode() != 1 {
|
if !errors.As(err, &exitError) || exitError.ExitCode() != 1 {
|
||||||
@@ -98,12 +98,29 @@ func TestRecoveryToolPreservesVersionedReplacement(t *testing.T) {
|
|||||||
if err := os.WriteFile(filepath.Join(module, "go.mod"), []byte(data), 0600); err != nil {
|
if err := os.WriteFile(filepath.Join(module, "go.mod"), []byte(data), 0600); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
shimDir := t.TempDir()
|
||||||
|
logPath := filepath.Join(t.TempDir(), "target")
|
||||||
|
realGo := filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||||
|
shim := "#!/bin/sh\ncase \" $* \" in *\" -deps \"*) printf '%s/%s\\n' \"$GOOS\" \"$GOARCH\" > \"$RECOVERY_TARGET_LOG\" ;; esac\nexec \"$RECOVERY_REAL_GO\" \"$@\"\n"
|
||||||
|
if err := os.WriteFile(filepath.Join(shimDir, "go"), []byte(shim), 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Setenv("RECOVERY_TARGET_LOG", logPath)
|
||||||
|
t.Setenv("RECOVERY_REAL_GO", realGo)
|
||||||
|
t.Setenv("PATH", shimDir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||||
|
foreignOS, foreignArch := foreignTarget()
|
||||||
|
t.Setenv("GOOS", foreignOS)
|
||||||
|
t.Setenv("GOARCH", foreignArch)
|
||||||
args, err := gotool.RecoveryTool(t.Context(), module, t.TempDir())
|
args, err := gotool.RecoveryTool(t.Context(), module, t.TempDir())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
command := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
|
if target, err := os.ReadFile(logPath); err != nil || strings.TrimSpace(string(target)) != runtime.GOOS+"/"+runtime.GOARCH {
|
||||||
command.Dir = t.TempDir()
|
t.Fatalf("recovery preparation target = %q, %v", target, err)
|
||||||
|
}
|
||||||
|
command := exec.Command(realGo, args.Args...)
|
||||||
|
command.Dir = args.Directory
|
||||||
|
command.Env = setEnvironment(setEnvironment(os.Environ(), "GOOS", runtime.GOOS), "GOARCH", runtime.GOARCH)
|
||||||
output, err := command.CombinedOutput()
|
output, err := command.CombinedOutput()
|
||||||
if err != nil || !strings.Contains(string(output), "tool-version=v0.2.198 helper=tool-only dependency") {
|
if err != nil || !strings.Contains(string(output), "tool-version=v0.2.198 helper=tool-only dependency") {
|
||||||
t.Fatalf("versioned recovery selection: %v: %s", err, output)
|
t.Fatalf("versioned recovery selection: %v: %s", err, output)
|
||||||
@@ -119,8 +136,8 @@ func TestRecoveryToolSupportsReleaseKitSourceModule(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
command := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), append(args, "version")...)
|
command := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), append(args.Args, "version")...)
|
||||||
command.Dir = t.TempDir()
|
command.Dir = args.Directory
|
||||||
output, err := command.CombinedOutput()
|
output, err := command.CombinedOutput()
|
||||||
if err != nil || !strings.HasPrefix(string(output), "releasekit ") {
|
if err != nil || !strings.HasPrefix(string(output), "releasekit ") {
|
||||||
t.Fatalf("source module recovery entry point: %v: %s", err, output)
|
t.Fatalf("source module recovery entry point: %v: %s", err, output)
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ checks in Stage 6.
|
|||||||
|
|
||||||
## Stage 4: Normalize module-based recovery preparation
|
## Stage 4: Normalize module-based recovery preparation
|
||||||
|
|
||||||
|
**Completed.** The CLI adapter and assembled handoff tests follow in the next stage.
|
||||||
|
|
||||||
1. In [the Go recovery implementation](../../internal/gotool/recovery.go),
|
1. In [the Go recovery implementation](../../internal/gotool/recovery.go),
|
||||||
keep the existing registered-tool, API-only, and no-dependency selections.
|
keep the existing registered-tool, API-only, and no-dependency selections.
|
||||||
Have `RecoveryTool` return `(*RecoveryInvocation, error)` within the
|
Have `RecoveryTool` return `(*RecoveryInvocation, error)` within the
|
||||||
|
|||||||
@@ -43,10 +43,16 @@ func TemporaryExecutable(path string) bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecoveryTool returns module-selected Go arguments for a later invocation.
|
// RecoveryInvocation describes a module-selected Go tool invocation.
|
||||||
// An empty result means this module does not select ReleaseKit. API-only modules
|
type RecoveryInvocation struct {
|
||||||
|
Directory string
|
||||||
|
Args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecoveryTool returns a module-selected Go invocation for later use.
|
||||||
|
// A nil result means this module does not select ReleaseKit. API-only modules
|
||||||
// receive a private module root that outlives launcher-owned bootstraps.
|
// receive a private module root that outlives launcher-owned bootstraps.
|
||||||
func RecoveryTool(ctx context.Context, moduleDir, recordDir string) ([]string, error) {
|
func RecoveryTool(ctx context.Context, moduleDir, recordDir string) (*RecoveryInvocation, error) {
|
||||||
path := filepath.Join(moduleDir, "go.mod")
|
path := filepath.Join(moduleDir, "go.mod")
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,7 +64,7 @@ func RecoveryTool(ctx context.Context, moduleDir, recordDir string) ([]string, e
|
|||||||
}
|
}
|
||||||
for _, tool := range module.Tool {
|
for _, tool := range module.Tool {
|
||||||
if tool.Path == releaseKitCommand {
|
if tool.Path == releaseKitCommand {
|
||||||
return []string{"-C", moduleDir, "tool", "-modfile=", releaseKitCommand}, nil
|
return &RecoveryInvocation{Directory: moduleDir, Args: []string{"tool", "-modfile=", releaseKitCommand}}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ask Go about the selected module graph, including transitive dependencies.
|
// Ask Go about the selected module graph, including transitive dependencies.
|
||||||
@@ -140,15 +146,31 @@ func RecoveryTool(ctx context.Context, moduleDir, recordDir string) ([]string, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Prepare the private graph and sums before the later tool invocation.
|
// Prepare the private graph and sums before the later tool invocation.
|
||||||
|
environment := map[string]string{"GOWORK": "off", "GOTOOLCHAIN": "local"}
|
||||||
|
hostJSON, err := discover(ctx, directory, environment, "go", "env", "-json", "GOHOSTOS", "GOHOSTARCH")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("prepare recovery tool: %w", err)
|
||||||
|
}
|
||||||
|
var host struct {
|
||||||
|
GOHOSTOS string
|
||||||
|
GOHOSTARCH string
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(hostJSON, &host); err != nil {
|
||||||
|
return nil, fmt.Errorf("prepare recovery tool: decode host target: %w", err)
|
||||||
|
}
|
||||||
|
if host.GOHOSTOS == "" || host.GOHOSTARCH == "" {
|
||||||
|
return nil, fmt.Errorf("prepare recovery tool: missing host target")
|
||||||
|
}
|
||||||
|
environment["GOOS"], environment["GOARCH"] = host.GOHOSTOS, host.GOHOSTARCH
|
||||||
captured := &combinedWriter{mode: captureStatus}
|
captured := &combinedWriter{mode: captureStatus}
|
||||||
err = process.Run(ctx, process.Invocation{
|
err = process.Run(ctx, process.Invocation{
|
||||||
Args: []string{"go", "list", "-mod=mod", "-modfile=", "-deps", releaseKitCommand},
|
Args: []string{"go", "list", "-mod=mod", "-modfile=", "-deps", releaseKitCommand},
|
||||||
Dir: directory, Env: map[string]string{"GOWORK": "off", "GOTOOLCHAIN": "local"},
|
Dir: directory, Env: environment,
|
||||||
Stdout: captured, Stderr: captured,
|
Stdout: captured, Stderr: captured,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("prepare recovery tool: %w: %s", err, diagnosticText(captured.take(), captured.omitted()))
|
return nil, fmt.Errorf("prepare recovery tool: %w: %s", err, diagnosticText(captured.take(), captured.omitted()))
|
||||||
}
|
}
|
||||||
complete = true
|
complete = true
|
||||||
return []string{"-C", directory, "tool", "-modfile=", releaseKitCommand}, nil
|
return &RecoveryInvocation{Directory: directory, Args: []string{"tool", "-modfile=", releaseKitCommand}}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user