Skip to content

refactor(cli): replace blanket env binding with selective DEVSY_* opt-in#419

Merged
skevetter merged 2 commits into
mainfrom
feat/selective-devsy-env-flags
May 24, 2026
Merged

refactor(cli): replace blanket env binding with selective DEVSY_* opt-in#419
skevetter merged 2 commits into
mainfrom
feat/selective-devsy-env-flags

Conversation

@skevetter

@skevetter skevetter commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replace inheritFlagsFromEnvironment (which auto-bound every CLI flag to DEVSY_<FLAG>) with an explicit allowlist via flags.BindEnv, wired inline next to each opt-in flag declaration. The env contract is now local to each call site and reviewable.
  • Uniform rule: env = "DEVSY_" + uppercase(flagName) with -_. Implemented in flags.EnvName and pinned against pkg/config constants by TestEnvName_MatchesConfigConstants.
  • BindEnv is loud on failure: panic on unknown flag (programmer bug at init), log.Fatalf on env values rejected by the flag's type (operator bug, e.g. DEVSY_DEBUG=garbage). Matches the prior log.Fatalf contract.
  • Renames --devsy-home--home for a clean DEVSY_HOME mapping. Hard cutover; SSH ProxyCommand emission (pkg/ssh/config.go) and the user docs are updated to match.
  • BuildRoot now returns (*cobra.Command, *flags.GlobalFlags) and the package-level var globalFlags singleton is gone — enables parallel tests, removes shared mutable state.

Opt-in flags (env support)

Flag Env Where
--home DEVSY_HOME global persistent
--context DEVSY_CONTEXT global persistent
--provider DEVSY_PROVIDER global persistent
--debug DEVSY_DEBUG global persistent
--host DEVSY_HOST every devsy pro … subcommand
--project DEVSY_PROJECT every devsy pro … subcommand that has it
--access-key DEVSY_ACCESS_KEY devsy agent container setup
--platform-host DEVSY_PLATFORM_HOST devsy agent container setup

Notable behavior changes

  • DEVSY_DEBUG semantics widened from "must equal true" to "any pflag-parseable bool" (1, t, TRUE, …) since it now flows through pflag.Set.
  • Flags not in the opt-in list no longer read DEVSY_*. Previously every flag did, silently.

Test coverage

  • cmd/env_flags_test.go covers: env→flag for all 8 opt-in flags; empty-env no-op; CLI overrides env via Execute(); env satisfies MarkFlagRequired via Execute(); sweep across every pro subcommand asserting --host/--project bindings stuck (this caught a missing binding on pro start during development).
  • cmd/flags/env_test.go pins EnvName(flagName) against pkg/config constants so the naming rule and the canonical env-var registry can't drift.

Summary by CodeRabbit

Release Notes

  • New Features

    • CLI commands now support setting flags via environment variables (e.g., DEVSY_HOST, DEVSY_PROJECT, DEVSY_HOME). CLI arguments take precedence over environment variables.
  • Documentation

    • Updated CLI documentation: --home flag replaces --devsy-home for configuring the Devsy home directory.
  • Tests

    • Added comprehensive test coverage for environment variable flag binding behavior.

Review Change Stack

Replaces the prior inheritFlagsFromEnvironment loop, which auto-bound every
flag in the tree to a DEVSY_<FLAG> env var, with an explicit allowlist driven
by flags.BindEnv. Each opt-in flag is wired inline next to its declaration,
making the env contract local and reviewable.

The uniform rule is DEVSY_ + uppercase(flagName) with "-" replaced by "_",
applied via flags.EnvName / flags.BindEnv. BindEnv panics on an unknown flag
(programmer bug) and log.Fatalf on rejected env values (operator bug), so
misconfigured env vars never silently no-op.

Renames --devsy-home to --home for a clean DEVSY_HOME mapping; the SSH
ProxyCommand and user docs are updated to match. BuildRoot now returns the
GlobalFlags struct alongside the root command, removing the package-level
mutable var globalFlags singleton.
@netlify

netlify Bot commented May 24, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 6af62c8
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a13044bf1f592000832f88c

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR implements environment variable binding for CLI flags across Devsy commands. It introduces standardized utilities for env var naming and wiring (EnvName, BindEnv), refactors root command initialization to return parsed global flags alongside the command, and applies env bindings to global flags and 20+ pro subcommands to enable configuration through environment variables.

Changes

Environment Variable Binding for CLI Flags

Layer / File(s) Summary
Env binding utilities and contracts
cmd/flags/env.go, cmd/flags/env_test.go
EnvName() maps flag names to canonical DEVSY_* environment variable names. BindEnv() wires env values into pflag flags: validates registration, updates usage strings, reads env values via os.LookupEnv, applies non-empty values, and updates default values.
Root command refactoring and BuildRoot signature
cmd/root.go, cmd/flag_aliases_test.go, cmd/runusercommands_test.go
BuildRoot() now returns (*cobra.Command, *flags.GlobalFlags) instead of just the command. Old env-flag inheritance logic removed. Execute() uses returned global flags for logging and error handling. registerSubcommands() centralizes subcommand wiring. Tests updated to unpack both return values.
Global flags environment binding
cmd/flags/flags.go
--home flag name fixed to literal "home" (removed binary-name prefix). Config import removed. SetGlobalFlags() adds BindEnv() calls for home, context, provider, and debug flags.
Pro flags BindEnv wrapper
cmd/pro/flags/flags.go
Introduces exported BindEnv() wrapper that forwards to root flags. GlobalFlags now embeds *rootflags.GlobalFlags to share root binding infrastructure.
Container setup command env binding
cmd/agent/container/setup.go
Parameter renamed from flags to globalFlags for clarity. Env bindings added for access-key and platform-host flags.
Pro commands environment variable binding
cmd/pro/add/cluster.go, cmd/pro/check_*.go, cmd/pro/create_workspace.go, cmd/pro/daemon/*.go, cmd/pro/list_*.go, cmd/pro/provider/rebuild.go, cmd/pro/rebuild.go, cmd/pro/self.go, cmd/pro/sleep.go, cmd/pro/start.go, cmd/pro/update_*.go, cmd/pro/version.go, cmd/pro/wakeup.go, cmd/pro/watch_workspaces.go
BindEnv() applied across 20+ pro subcommands to bind --host and/or --project flags to corresponding environment variables (DEVSY_HOST, DEVSY_PROJECT), enabling configuration through env vars alongside CLI arguments.
Environment flags integration test suite
cmd/env_flags_test.go
Comprehensive test coverage: table-driven test validates env values apply to flags and satisfy required-flag validation; focused tests verify empty env is a no-op, CLI args override env values, and env values are marked as changed; sweep validation ensures all pro subcommands exposing --host or --project receive expected changed status when corresponding env vars are set. Helper resolves nested commands by space-separated path.
Documentation and flag reference updates
docs/pages/developing-in-workspaces/create-a-workspace.mdx, pkg/ssh/config.go, pkg/ssh/config_test.go
CLI documentation updated to show --home={home_path} instead of --devsy-home={home_path}. SSH ProxyCommand generation updated to use --home flag. Test fixtures updated to reflect new flag name and adjusted Windows path examples.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • devsy-org/devsy#174: Modifies CLI flag aliases and tests; this PR updates flag alias tests to work with the refactored BuildRoot() API.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactoring: replacing automatic environment variable binding for all CLI flags with an explicit opt-in allowlist using DEVSY_* prefixed variables.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@skevetter
skevetter marked this pull request as ready for review May 24, 2026 14:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/root.go`:
- Around line 86-99: Capture the prior value of DEVSY_HOME before setting it:
when you check globalFlags.DevsyHome and call os.Setenv(config.EnvHome,
globalFlags.DevsyHome), store the previous value (e.g., prevDevsyHome, and a
flag prevDevsyHomeExists) in a variable accessible to the PersistentPostRunE
closure; then in rootCmd.PersistentPostRunE restore the environment to that
previous state — if prevDevsyHomeExists set it back with
os.Setenv(config.EnvHome, prevDevsyHome), otherwise unset it with
os.Unsetenv(config.EnvHome) — so the code paths around globalFlags.DevsyHome,
os.Setenv, and rootCmd.PersistentPostRunE preserve the caller's original
environment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 81fcdbcf-5f1b-44f6-b3a4-59e53433f5bf

📥 Commits

Reviewing files that changed from the base of the PR and between 2e94481 and 6af62c8.

📒 Files selected for processing (33)
  • cmd/agent/container/setup.go
  • cmd/env_flags_test.go
  • cmd/flag_aliases_test.go
  • cmd/flags/env.go
  • cmd/flags/env_test.go
  • cmd/flags/flags.go
  • cmd/pro/add/cluster.go
  • cmd/pro/check_health.go
  • cmd/pro/check_update.go
  • cmd/pro/create_workspace.go
  • cmd/pro/daemon/netcheck.go
  • cmd/pro/daemon/start.go
  • cmd/pro/daemon/status.go
  • cmd/pro/flags/flags.go
  • cmd/pro/list_clusters.go
  • cmd/pro/list_projects.go
  • cmd/pro/list_templates.go
  • cmd/pro/list_workspaces.go
  • cmd/pro/provider/rebuild.go
  • cmd/pro/rebuild.go
  • cmd/pro/self.go
  • cmd/pro/sleep.go
  • cmd/pro/start.go
  • cmd/pro/update_provider.go
  • cmd/pro/update_workspace.go
  • cmd/pro/version.go
  • cmd/pro/wakeup.go
  • cmd/pro/watch_workspaces.go
  • cmd/root.go
  • cmd/runusercommands_test.go
  • docs/pages/developing-in-workspaces/create-a-workspace.mdx
  • pkg/ssh/config.go
  • pkg/ssh/config_test.go

Comment thread cmd/root.go
Comment on lines +86 to +99
if globalFlags.DevsyHome != "" {
_ = os.Setenv(config.EnvHome, globalFlags.DevsyHome)
}

devsyConfig, err := config.LoadConfig(globalFlags.Context, globalFlags.Provider)
if err == nil {
telemetry.StartCLI(devsyConfig, cobraCmd)
}
return nil
}
rootCmd.PersistentPostRunE = func(_ *cobra.Command, _ []string) error {
if globalFlags.DevsyHome != "" {
_ = os.Unsetenv(config.EnvHome)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore prior DEVSY_HOME instead of always unsetting it

Line 98 always clears DEVSY_HOME when --home was used. If the process already had DEVSY_HOME set, this loses caller state and can leak across same-process command executions/tests. Capture the previous value before Line 87 and restore it in post-run.

Proposed fix
 func BuildRoot() (*cobra.Command, *flags.GlobalFlags) {
 	rootCmd := &cobra.Command{
 		Use:           config.BinaryName,
 		Short:         "Devsy",
 		SilenceUsage:  true,
 		SilenceErrors: true,
 	}
+	var prevHome string
+	var hadPrevHome bool
 	persistentFlags := rootCmd.PersistentFlags()
 	globalFlags := flags.SetGlobalFlags(persistentFlags)
 	_ = completion.RegisterFlagCompletionFuns(rootCmd, globalFlags)

 	rootCmd.PersistentPreRunE = func(cobraCmd *cobra.Command, _ []string) error {
 		log.Init(log.Config{
 			Verbosity: globalFlags.Verbosity,
 			Quiet:     globalFlags.Quiet,
 			Debug:     globalFlags.Debug,
 			Format:    globalFlags.LogOutput,
 		})
 		klog.SetLogger(logr.New(log.LogrSink()))

 		if globalFlags.DevsyHome != "" {
+			prevHome, hadPrevHome = os.LookupEnv(config.EnvHome)
 			_ = os.Setenv(config.EnvHome, globalFlags.DevsyHome)
 		}

 		devsyConfig, err := config.LoadConfig(globalFlags.Context, globalFlags.Provider)
 		if err == nil {
 			telemetry.StartCLI(devsyConfig, cobraCmd)
 		}
 		return nil
 	}
 	rootCmd.PersistentPostRunE = func(_ *cobra.Command, _ []string) error {
 		if globalFlags.DevsyHome != "" {
-			_ = os.Unsetenv(config.EnvHome)
+			if hadPrevHome {
+				_ = os.Setenv(config.EnvHome, prevHome)
+			} else {
+				_ = os.Unsetenv(config.EnvHome)
+			}
 		}
 		return nil
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if globalFlags.DevsyHome != "" {
_ = os.Setenv(config.EnvHome, globalFlags.DevsyHome)
}
devsyConfig, err := config.LoadConfig(globalFlags.Context, globalFlags.Provider)
if err == nil {
telemetry.StartCLI(devsyConfig, cobraCmd)
}
return nil
}
rootCmd.PersistentPostRunE = func(_ *cobra.Command, _ []string) error {
if globalFlags.DevsyHome != "" {
_ = os.Unsetenv(config.EnvHome)
}
if globalFlags.DevsyHome != "" {
prevHome, hadPrevHome = os.LookupEnv(config.EnvHome)
_ = os.Setenv(config.EnvHome, globalFlags.DevsyHome)
}
devsyConfig, err := config.LoadConfig(globalFlags.Context, globalFlags.Provider)
if err == nil {
telemetry.StartCLI(devsyConfig, cobraCmd)
}
return nil
}
rootCmd.PersistentPostRunE = func(_ *cobra.Command, _ []string) error {
if globalFlags.DevsyHome != "" {
if hadPrevHome {
_ = os.Setenv(config.EnvHome, prevHome)
} else {
_ = os.Unsetenv(config.EnvHome)
}
}
return nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/root.go` around lines 86 - 99, Capture the prior value of DEVSY_HOME
before setting it: when you check globalFlags.DevsyHome and call
os.Setenv(config.EnvHome, globalFlags.DevsyHome), store the previous value
(e.g., prevDevsyHome, and a flag prevDevsyHomeExists) in a variable accessible
to the PersistentPostRunE closure; then in rootCmd.PersistentPostRunE restore
the environment to that previous state — if prevDevsyHomeExists set it back with
os.Setenv(config.EnvHome, prevDevsyHome), otherwise unset it with
os.Unsetenv(config.EnvHome) — so the code paths around globalFlags.DevsyHome,
os.Setenv, and rootCmd.PersistentPostRunE preserve the caller's original
environment.

@skevetter
skevetter merged commit c3ec86d into main May 24, 2026
52 checks passed
@skevetter
skevetter deleted the feat/selective-devsy-env-flags branch May 24, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant