fix: harden model catalog projections

This commit is contained in:
Codex
2026-09-02 19:25:01 +02:00
parent ce4c31a6fb
commit a6a5bf2036
38 changed files with 573 additions and 83 deletions
+7 -2
View File
@@ -528,8 +528,13 @@ func piCommand(ctx context.Context, installation config.Installation, runner com
defer func() { _ = lock.Release() }()
}
if args[0] == "restart" || args[0] == "update" || args[0] == "rollback" {
if err := modelprojection.Generate(installation); err != nil {
return commandUsageError(stderr, "installation model catalog could not be projected: "+err.Error())
drift, err := modelprojection.Check(installation)
if err != nil {
return commandUsageError(stderr, "installation model catalog could not be verified: "+err.Error())
}
if len(drift) > 0 {
return commandUsageError(stderr,
"installation model catalog changed; run tht start to apply the complete runtime projection")
}
}
controlled := compose.InstallationRunner{Installation: installation, Runner: runner}
+20
View File
@@ -1057,6 +1057,7 @@ func TestRunPiStatusUsesImageBundledPi(t *testing.T) {
func TestRunPiUpdateResolvesLatestStableVersionWhenVersionIsOmitted(t *testing.T) {
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
fixture.setEnvironment(t)
fixture.generateModelProjection(t)
if err := os.WriteFile(filepath.Join(fixture.projectDirectory, "docker", "core.Dockerfile"), []byte("ARG PI_VERSION=0.79.0\n"), 0o600); err != nil {
t.Fatal(err)
}
@@ -1079,6 +1080,7 @@ func TestRunPiUpdateResolvesLatestStableVersionWhenVersionIsOmitted(t *testing.T
func TestRunPiUpdateRequiresExplicitConfirmationWithoutInvokingDocker(t *testing.T) {
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
fixture.setEnvironment(t)
fixture.generateModelProjection(t)
var stdout, stderr bytes.Buffer
exitCode := run(context.Background(), []string{"--installation", fixture.installationPath, "pi", "update", "--version", "0.81.0", "--source", "build"}, &stdout, &stderr)
@@ -1095,6 +1097,7 @@ func TestRunPiUpdateRequiresExplicitConfirmationWithoutInvokingDocker(t *testing
func TestRunPiRestartRequiresExplicitConfirmationWithoutInvokingDocker(t *testing.T) {
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
fixture.setEnvironment(t)
fixture.generateModelProjection(t)
var stdout, stderr bytes.Buffer
exitCode := run(context.Background(), []string{
@@ -1117,6 +1120,7 @@ func TestRunPiRestartSanitizesSuccessOutput(t *testing.T) {
t.Fatal(err)
}
fixture.setEnvironment(t, secretPath)
fixture.generateModelProjection(t)
t.Setenv("THT_FAKE_PI_VERSION", "pi-restart-secret")
var stdout, stderr bytes.Buffer
@@ -1138,6 +1142,7 @@ func TestRunPiRestartSanitizesSuccessOutput(t *testing.T) {
func TestRunPiUpdateAcceptsExplicitVersionWithoutAdvancedFlags(t *testing.T) {
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
fixture.setEnvironment(t)
fixture.generateModelProjection(t)
var stdout, stderr bytes.Buffer
exitCode := run(context.Background(), []string{
@@ -1152,6 +1157,21 @@ func TestRunPiUpdateAcceptsExplicitVersionWithoutAdvancedFlags(t *testing.T) {
}
}
func TestRunPiRestartRejectsCatalogDriftBeforeInvokingDocker(t *testing.T) {
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
fixture.setEnvironment(t)
var stdout, stderr bytes.Buffer
exitCode := run(context.Background(), []string{
"--installation", fixture.installationPath, "pi", "restart", "--yes",
}, &stdout, &stderr)
if exitCode != 2 || !strings.Contains(stderr.String(), "run tht start") {
t.Fatalf("run() = %d, stderr=%q; want complete projection guidance", exitCode, stderr.String())
}
assertDockerNotInvoked(t, fixture)
}
func TestRunPiMaintenanceStatusAndRecoverConfirmationContract(t *testing.T) {
fixture := newCLIFixture(t, "THT_LLM_URL=https://llm.example.invalid\n")
fixture.setEnvironment(t)