feat(dev): support direct environment overrides

This commit is contained in:
Thomas Camlong committed 2026-08-14 21:09:41 +02:00
1 parent f62add8ae4
commit b8c06a8622
5 files changed
+68 -9

No files matched your search

@@ -34,8 +34,10 @@ Images built by this CLI record their source checkout, revision, and optional pu
## Run images
```bash
homarr --pr <number> --env HOMARR_WEBSITE_URL=https://app-v2.preview.homarr.dev/
homarr run <name>
homarr run --pr <number>
```
Add `--detach` to run in the background, `--demo` to enable demo data, or repeat `--env KEY=VALUE` to provide additional environment variables.
The direct `homarr --pr <number>` form is shorthand for `homarr run --pr <number>`. Add `--detach` to run in the
background, `--demo` to enable demo data, or repeat `--env KEY=VALUE` to provide additional environment variables.
+4
View File
@@ -31,6 +31,7 @@ Checkout builds are tagged as `homarr:<name>`. The source checkout and revision
## Launch
```sh
homarr --pr 6441 --env WORKSHOP_WEB_URL=https://app-v2.preview.homarr.dev/
pnpm dev:cli -- run dev
pnpm dev:cli -- run --detach dev
pnpm dev:cli -- run --pr 6441
@@ -39,6 +40,8 @@ pnpm dev:cli -- run --env FOO=bar --env FEATURE=true dev
```
PR launches always pull the latest GHCR tag before starting. Local tags remain local.
The installed binary accepts `--pr`, `--demo`, `--detach`, and repeatable `--env KEY=VALUE` flags directly, so
`homarr --pr <number> --env KEY=VALUE` is shorthand for `homarr run --pr <number> --env KEY=VALUE`.
The first launch generates a per-user encryption key in the OS config directory and reuses it for later launches. Set `HOMARR_DEV_SECRET_ENCRYPTION_KEY` to a 64-character hexadecimal key to override it.
The command reference below assumes the optional `homarr` installation. Without it, prefix each command with `pnpm dev:cli --` (for example, `pnpm dev:cli -- dev`).
@@ -47,6 +50,7 @@ The command reference below assumes the optional `homarr` installation. Without
```text
homarr Interactive local and remote image browser
homarr --pr <number> Pull and launch a PR image, with optional --env overrides
homarr dev Explicit alias for the main browser
homarr dash Start on the instance dashboard
homarr build <name> Build this checkout as homarr:<name>
+18
View File
@@ -33,6 +33,24 @@ func TestBuildPlanFailsWhenNoPortIsAvailable(t *testing.T) {
}
}
func TestBuildPlanPreservesEnvironmentOverrides(t *testing.T) {
want := []string{
"WORKSHOP_WEB_URL=https://app-v2.preview.homarr.dev/",
"FEATURE_OPTIONS=one=two",
}
plan, err := BuildPlan(Options{
PR: 1,
Env: want,
FindPort: func(int) int { return 7575 },
})
if err != nil {
t.Fatal(err)
}
if !slices.Equal(plan.Env, want) {
t.Fatalf("environment = %v, want %v", plan.Env, want)
}
}
func TestStartPRIntegration(t *testing.T) {
if os.Getenv("HOMARR_DOCKER_INTEGRATION_TEST") != "1" {
t.Skip("set HOMARR_DOCKER_INTEGRATION_TEST=1 and HOMARR_TEST_PR=<number>")
+22 -7
View File
@@ -21,6 +21,7 @@ var (
flagDetach bool
flagBuildPR int
runMain = tui.RunDev
runRootMain = runLaunch
)
func main() {
@@ -39,6 +40,8 @@ Start a local image, a PR test build from ghcr.io, browse open PRs,
or manage running instances from an interactive dashboard.
Examples:
homarr --pr 6390 # launch remote PR test image directly
homarr --pr 6390 -e FOO=bar # PR image with environment overrides
homarr run dev # launch local homarr:dev image
homarr run --pr 6390 # launch remote PR test image
homarr run --pr 6390 --demo # PR + demo mode
@@ -52,9 +55,7 @@ Examples:
homarr logs homarr_pr_6390 # follow logs
homarr doctor # check Docker and optional integrations`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runMain()
},
RunE: runRoot,
}
var dashCmd = &cobra.Command{
@@ -240,15 +241,29 @@ var doctorCmd = &cobra.Command{
}
func init() {
runCmd.Flags().BoolVarP(&flagDemo, "demo", "m", false, "Demo mode (mock integrations)")
runCmd.Flags().IntVarP(&flagPR, "pr", "p", 0, "Launch PR test image from ghcr.io")
runCmd.Flags().StringArrayVarP(&flagEnv, "env", "e", nil, "Extra env vars (repeatable, KEY=VALUE)")
runCmd.Flags().BoolVar(&flagDetach, "detach", false, "Start in the background")
addRunFlags(rootCmd)
addRunFlags(runCmd)
buildCmd.Flags().IntVarP(&flagBuildPR, "pr", "p", 0, "Build a pull request in a temporary checkout")
rootCmd.AddCommand(runCmd, dashCmd, devCmd, buildCmd, rebuildCmd, listCmd, logsCmd, stopCmd, restartCmd, removeCmd, openCmd, doctorCmd)
}
func addRunFlags(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&flagDemo, "demo", "m", false, "Demo mode (mock integrations)")
cmd.Flags().IntVarP(&flagPR, "pr", "p", 0, "Launch PR test image from ghcr.io")
cmd.Flags().StringArrayVarP(&flagEnv, "env", "e", nil, "Extra env vars (repeatable, KEY=VALUE)")
cmd.Flags().BoolVar(&flagDetach, "detach", false, "Start in the background")
}
func runRoot(cmd *cobra.Command, args []string) error {
for _, name := range []string{"pr", "env", "demo", "detach"} {
if cmd.Flags().Changed(name) {
return runRootMain(cmd, args)
}
}
return runMain()
}
func runLaunch(cmd *cobra.Command, args []string) error {
tag := ""
if len(args) > 0 {
+21 -1
View File
@@ -4,6 +4,8 @@ import (
"errors"
"strings"
"testing"
"github.com/spf13/cobra"
)
func TestRootCommandLaunchesDevelopmentDashboard(t *testing.T) {
@@ -12,7 +14,25 @@ func TestRootCommandLaunchesDevelopmentDashboard(t *testing.T) {
t.Cleanup(func() { runMain = previous })
runMain = func() error { return want }
if err := rootCmd.RunE(rootCmd, nil); !errors.Is(err, want) {
cmd := &cobra.Command{}
if err := runRoot(cmd, nil); !errors.Is(err, want) {
t.Fatalf("root command error = %v", err)
}
}
func TestRootCommandLaunchesPRWhenRunFlagIsProvided(t *testing.T) {
want := errors.New("PR launched")
previous := runRootMain
t.Cleanup(func() { runRootMain = previous })
runRootMain = func(cmd *cobra.Command, args []string) error { return want }
cmd := &cobra.Command{}
cmd.Flags().Bool("pr", false, "")
if err := cmd.Flags().Set("pr", "true"); err != nil {
t.Fatal(err)
}
if err := runRoot(cmd, nil); !errors.Is(err, want) {
t.Fatalf("root command error = %v", err)
}
}