wip: guided standalone installation and workspace checks

This commit is contained in:
Codex
2026-09-26 16:41:15 +02:00
parent 0d2e573e0d
commit 67ee52624c
15 changed files with 902 additions and 560 deletions
+46 -2
View File
@@ -3,6 +3,7 @@ package setup
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
@@ -35,7 +36,8 @@ type Result struct {
}
// Run validates the host, creates or validates non-secret configuration, and by default builds,
// starts, and verifies the current checkout. ConfigureOnly stops after Compose rendering.
// starts, and verifies the current checkout. Complete additionally migrates the Catalog and
// imports the configured workspace repository. ConfigureOnly stops after Compose rendering.
func Run(ctx context.Context, runner compose.Runner, request Request, input io.Reader, output io.Writer) (Result, error) {
if runner == nil {
return Result{}, errors.New("setup requires a Docker command runner")
@@ -71,13 +73,33 @@ func Run(ctx context.Context, runner compose.Runner, request Request, input io.R
fmt.Fprintf(output, "Configuration is ready: %s\n", result.DescriptorPath)
return result, nil
}
if err := service.Start(ctx, installation, runner, true); err != nil {
if request.Complete {
if err := runCompose(ctx, runner, installation, "build"); err != nil {
return Result{}, fmt.Errorf("setup image build: %w", err)
}
if err := runCompose(ctx, runner, installation, "up", "--detach", "catalog-db"); err != nil {
return Result{}, fmt.Errorf("setup Catalog database start: %w", err)
}
if err := runCompose(ctx, runner, installation,
"--profile", "catalog-maintenance", "run", "--rm", "catalog-migrate"); err != nil {
return Result{}, fmt.Errorf("setup Catalog migration: %w", err)
}
}
if err := service.Start(ctx, installation, runner, !request.Complete); err != nil {
if strings.Contains(err.Error(), "image build") {
return Result{}, fmt.Errorf("setup %w", err)
}
return Result{}, withStartupRecovery(fmt.Errorf("setup %w", err), recoveryService(err))
}
result.Built, result.Started, result.Healthy = true, true, true
if request.Complete {
if err := runOperator(ctx, runner, installation, "workspace-pull"); err != nil {
return Result{}, withStartupRecovery(fmt.Errorf("setup workspace import: %w", err), "core")
}
if err := runOperator(ctx, runner, installation, "pi-test"); err != nil {
return Result{}, withStartupRecovery(fmt.Errorf("setup LLM credential test: %w", err), "core")
}
}
report, err := doctor.Run(ctx, installation, runner)
if err != nil {
return Result{}, withStartupRecovery(fmt.Errorf("setup doctor: %w", err), "core")
@@ -85,6 +107,9 @@ func Run(ctx context.Context, runner compose.Runner, request Request, input io.R
if !report.OK {
return Result{}, withStartupRecovery(errors.New("setup doctor reported failed checks"), "core")
}
if request.Complete {
fmt.Fprintln(output, "Workspace repository pulled and activated; run 'tht workspace test' after configuring each workspace database.")
}
fmt.Fprintf(output, "ThothII is ready at %s\nInstallation descriptor: %s\nNext: tht status\n", frontendURL(installation), result.DescriptorPath)
return result, nil
}
@@ -219,6 +244,25 @@ func runCompose(ctx context.Context, runner compose.Runner, installation config.
return nil
}
func runOperator(ctx context.Context, runner compose.Runner, installation config.Installation, action string) error {
result, err := runner.Run(ctx, installation.ComposeArgs(
"exec", "-T", "core", "node", "dist/operator-command.js", action,
), nil)
if err != nil {
if result.ExitCode != 0 {
return fmt.Errorf("Docker exited with status %d", result.ExitCode)
}
return err
}
var payload struct {
Ready *bool `json:"ready"`
}
if err := json.Unmarshal([]byte(result.Stdout), &payload); err != nil || payload.Ready == nil || !*payload.Ready {
return fmt.Errorf("operator action %s reported failure", action)
}
return nil
}
func composeFailure(result compose.Result, cause error) error {
if result.ExitCode != 0 {
return fmt.Errorf("Docker exited with status %d", result.ExitCode)