Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions cmd/env_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
// setting the corresponding DEVSY_* env var pushes the value into the bound
// pflag at registration time, satisfying cobra's required-flag check.
func TestOptInEnvFlags_AppliesEnvValueToFlag(t *testing.T) {
t.Setenv(envProEnabled, "true")
cases := []struct {
cmdPath string
flagName string
Expand Down Expand Up @@ -84,6 +85,7 @@ func TestOptInEnvFlags_NoEnvLeavesDefault(t *testing.T) {
// BindEnv: setting DEVSY_HOST="" must NOT mark the flag as Changed (otherwise
// MarkFlagRequired would be satisfied by an empty value).
func TestOptInEnvFlags_EmptyEnvIsNoOp(t *testing.T) {
t.Setenv(envProEnabled, "true")
t.Setenv(envDevsyHost, "")
rootCmd, _ := BuildRoot()
cmd := resolveCommand(t, rootCmd, "pro workspace list")
Expand All @@ -96,6 +98,7 @@ func TestOptInEnvFlags_EmptyEnvIsNoOp(t *testing.T) {
// wins over a value supplied via DEVSY_*. This is the standard precedence
// users expect: flag > env > default.
func TestOptInEnvFlags_CLIOverridesEnv(t *testing.T) {
t.Setenv(envProEnabled, "true")
t.Setenv(envDevsyHost, "env-value")
rootCmd, _ := BuildRoot()
// Replace the leaf command's RunE so Execute() doesn't try to actually
Expand All @@ -116,6 +119,7 @@ func TestOptInEnvFlags_CLIOverridesEnv(t *testing.T) {
// ValidateRequiredFlags pipeline to prove DEVSY_HOST satisfies
// MarkFlagRequired("host") at execution time, not just in static inspection.
func TestOptInEnvFlags_EnvSatisfiesRequired(t *testing.T) {
t.Setenv(envProEnabled, "true")
t.Setenv(envDevsyHost, "from-env.example.com")
rootCmd, _ := BuildRoot()
leaf := resolveCommand(t, rootCmd, "pro workspace list")
Expand All @@ -134,6 +138,7 @@ func TestOptInEnvFlags_EnvSatisfiesRequired(t *testing.T) {
// This catches the regression where a future addition to cmd/pro/ forgets
// the inline BindEnv call.
func TestOptInEnvFlags_ProSubcommandSweep(t *testing.T) {
t.Setenv(envProEnabled, "true")
for _, env := range []string{envDevsyHost, envDevsyProject} {
t.Setenv(env, "sweep-"+strings.ToLower(env))
}
Expand All @@ -157,6 +162,24 @@ func TestOptInEnvFlags_ProSubcommandSweep(t *testing.T) {
walk(proCmd)
}

// TestProCommand_HiddenWhenDisabled verifies pro is gated off by default.
func TestProCommand_HiddenWhenDisabled(t *testing.T) {
t.Setenv(envProEnabled, "")
rootCmd, _ := BuildRoot()
for _, c := range rootCmd.Commands() {
use := c.Use
if i := strings.IndexByte(use, ' '); i >= 0 {
use = use[:i]
}
require.NotEqual(
t,
"pro",
use,
"pro command should not be registered when DEVSY_PRO_ENABLED is unset",
)
}
}

func resolveCommand(t *testing.T, rootCmd *cobra.Command, path string) *cobra.Command {
t.Helper()
if path == "" {
Expand Down
35 changes: 24 additions & 11 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,17 @@ const (
groupPlatform = "platform"
groupDevcontainer = "devcontainer"
groupMeta = "meta"

// envProEnabled gates registration of the `pro` command tree. The pro
// feature is not ready for general use; set DEVSY_PRO_ENABLED=true to
// expose it (e.g. for internal testing).
envProEnabled = "DEVSY_PRO_ENABLED"
)

func proEnabled() bool {
return os.Getenv(envProEnabled) == "true"
}

// isMachineLogFormat reports whether the configured --log-output mode produces
// a structured, machine-parseable stream (json or logfmt). Callers use this to
// suppress decorative human-readable affordances that would corrupt the stream.
Expand Down Expand Up @@ -163,13 +172,16 @@ func BuildRoot() (*cobra.Command, *flags.GlobalFlags) {
return nil
}

rootCmd.AddGroup(
&cobra.Group{ID: groupCore, Title: "Core commands:"},
&cobra.Group{ID: groupConfig, Title: "Configuration commands:"},
&cobra.Group{ID: groupPlatform, Title: "Platform commands:"},
&cobra.Group{ID: groupDevcontainer, Title: "Devcontainer commands:"},
&cobra.Group{ID: groupMeta, Title: "Meta:"},
)
groups := []*cobra.Group{
{ID: groupCore, Title: "Core commands:"},
{ID: groupConfig, Title: "Configuration commands:"},
{ID: groupDevcontainer, Title: "Devcontainer commands:"},
{ID: groupMeta, Title: "Meta:"},
}
if proEnabled() {
groups = append(groups, &cobra.Group{ID: groupPlatform, Title: "Platform commands:"})
}
rootCmd.AddGroup(groups...)

registerSubcommands(rootCmd, globalFlags)

Expand All @@ -189,10 +201,11 @@ func registerSubcommands(rootCmd *cobra.Command, globalFlags *flags.GlobalFlags)
contextCmd := context.NewContextCmd(globalFlags)
contextCmd.GroupID = groupConfig
rootCmd.AddCommand(contextCmd)
proCmd := pro.NewProCmd(globalFlags)
proCmd.GroupID = groupPlatform
rootCmd.AddCommand(proCmd)

if proEnabled() {
proCmd := pro.NewProCmd(globalFlags)
proCmd.GroupID = groupPlatform
rootCmd.AddCommand(proCmd)
}
wsCmd := wsCmdPkg.NewWorkspaceCmd(globalFlags)
wsCmd.GroupID = groupCore
rootCmd.AddCommand(wsCmd)
Expand Down
Loading