feat(infra): auto-install and start shared database/auth at boot when missing

ensureInfraRunning now resolves the missing infra service from the registry
(via the new lib/registry package, extracted from the GetRegistry handler so
the UI and the bootstrap share one source of truth) and installs it before
starting it. A fresh host boots with its database and auth service without
manual UI steps; failures stay non-fatal warnings like before.
This commit is contained in:
2026-08-16 03:05:30 +01:00
parent 92f4787146
commit 483ed3b531
4 changed files with 429 additions and 312 deletions
+52
View File
@@ -0,0 +1,52 @@
// Copyright (C) 2026 Dcntrlized
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package registry
import (
"context"
"os"
"strings"
"testing"
"time"
)
// TestItemByNameLive resolves the infrastructure services against a live
// registry. It is opt-in (MAESTRO_REGISTRY_TEST=1) so the default test run
// stays offline; the dev VM and prod CI both set the Gitea env the function
// reads. Infrastructure services must resolve so Maestro can auto-install them
// on a fresh host (ensureInfraRunning).
func TestItemByNameLive(t *testing.T) {
if os.Getenv("MAESTRO_REGISTRY_TEST") != "1" {
t.Skip("set MAESTRO_REGISTRY_TEST=1 with GITEA_* env to run against a live registry")
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
for _, name := range []string{"dcntrlized-db", "dcntrlized-auth"} {
item, err := ItemByName(ctx, name)
if err != nil {
t.Fatalf("ItemByName(%q): %v", name, err)
}
if item.Image == "" {
t.Errorf("ItemByName(%q): empty image", name)
}
if !strings.HasPrefix(item.URL, "http") {
t.Errorf("ItemByName(%q): url %q not an http(s) compose source", name, item.URL)
}
t.Logf("resolved %s -> image=%s url=%s version=%s", name, item.Image, item.URL, item.Version)
}
}