package preflight import ( "context" "encoding/json" "os" "path/filepath" "github.com/aritmolab/thothii/tools/tht/internal/config" ) // CheckCompose resolves the distributed release plus authored overrides without starting services. func CheckCompose(ctx context.Context, runner Runner, installation config.Installation, m Manifest, manifestPath, platform string) Report { r := NewReport() args := []string{"compose", "--project-directory", installation.ProjectDirectory, "--env-file", installation.EnvFile} for _, name := range m.Compose { args = append(args, "-f", filepath.Join(filepath.Dir(manifestPath), filepath.FromSlash(name))) } for _, path := range installation.Overrides { if _, err := os.Stat(path); os.IsNotExist(err) { // Only the two well-known distribution-owned transport overlays can be relocated. name := "deploy/" + filepath.Base(path) if path != filepath.Join(installation.ProjectDirectory, filepath.FromSlash(name)) || (name != "deploy/compose.git-https.yaml" && name != "deploy/compose.git-ssh.yaml") || m.Files[name] == "" { r.Add("compose-assets", "error", "overrides", "Include the selected Git transport overlay in the verified release.") return r } path = filepath.Join(filepath.Dir(manifestPath), filepath.FromSlash(name)) } args = append(args, "-f", path) } args = append(args, "config", "--format", "json") result, err := docker(ctx, runner, args...) var effective struct { Services map[string]struct { Image string `json:"image"` Build any `json:"build"` Platform string `json:"platform"` } `json:"services"` } if err != nil || json.Unmarshal([]byte(result.Stdout), &effective) != nil { r.Add("compose-configuration", "error", "operator.env", "Correct the effective Compose configuration using the release assets and prepared environment; no raw output is logged.") return r } roles := map[string]string{"core": "core", "catalog-migrate": "core", "workspace-maintenance": "core", "frontend": "frontend", "catalog-db": "catalog", "qdrant": "qdrant", "embedding": "embedding", "embedding-model-init": "embedding"} for service, role := range roles { entry, exists := effective.Services[service] if !exists || entry.Build != nil || entry.Image != m.Images[role][platform] || (entry.Platform != "" && entry.Platform != platform) { r.Add("compose-image-"+service, "error", "release.compose", "Each runtime and maintenance service must use its released immutable image without a source build.") } } for service := range effective.Services { if _, ok := roles[service]; !ok { r.Add("compose-service", "error", "release.compose", "Additional services need an explicit release contract before execution.") } } if r.OK { r.Add("compose-configuration", "passed", "release.compose", "Effective Compose configuration uses the complete released image set.") } return r }