v0.66.1: inject USERDATA_PATH on first deploy too (shared withUserdataPath)

DeployStack's initial compose-up builds env from deploy values (not stackEnv), so
v0.66.0 missed USERDATA_PATH on first deploy → ${USERDATA_PATH} resolved to '' and
Docker bound a root-owned dir at the container root (found live: radarr /media/movies
was 0:0 755). Shared withUserdataPath injector now used by stackEnv AND
composeExecWithEnv. Regression test included.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 22:23:36 +02:00
parent c48f95fe06
commit 3b7d08979c
4 changed files with 51 additions and 4 deletions
+17 -1
View File
@@ -13,6 +13,7 @@ import (
"strings"
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
"gitea.dooplex.hu/admin/felhom-controller/internal/crypto"
"gitea.dooplex.hu/admin/felhom-controller/internal/system"
"gopkg.in/yaml.v3"
@@ -513,16 +514,31 @@ func (m *Manager) RedeployFromEnv(name string, env map[string]string) error {
return m.RefreshStatus()
}
// composeExecWithEnv runs a compose command with custom env vars injected.
// composeExecWithEnv runs a compose command with custom env vars injected. Used by the initial deploy
// path (DeployStack), which builds env from the deploy values rather than from app.yaml via stackEnv —
// so USERDATA_PATH must be injected here too (mirrors stackEnv), else the FIRST deploy resolves
// ${USERDATA_PATH} to "" and binds a bogus root-owned dir at the container root.
func (m *Manager) composeExecWithEnv(dir string, env map[string]string, args ...string) (string, error) {
cmdEnv := os.Environ()
for k, v := range env {
cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v))
}
cmdEnv = append(cmdEnv, fmt.Sprintf("DOMAIN=%s", m.cfg.Customer.Domain))
cmdEnv = withUserdataPath(cmdEnv, env["HDD_PATH"])
return m.composeExecCustomEnv(dir, cmdEnv, args...)
}
// withUserdataPath appends USERDATA_PATH=<hdd>/userdata to a "K=V" env slice when hdd is non-empty.
// Shared by BOTH compose-env builders (stackEnv for start/redeploy, composeExecWithEnv for the initial
// deploy) so ${USERDATA_PATH} always resolves — the initial-deploy path missing it bound a bogus
// root-owned dir at the container root.
func withUserdataPath(cmdEnv []string, hdd string) []string {
if hdd != "" {
cmdEnv = append(cmdEnv, "USERDATA_PATH="+appbackup.UserdataDir(hdd))
}
return cmdEnv
}
// GetDeployFields returns the deployment fields for a stack (for the deploy form).
func (m *Manager) GetDeployFields(name string) (*Metadata, *AppConfig, error) {
stack, ok := m.GetStack(name)