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
2 changes: 1 addition & 1 deletion cmd/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func NewContextCmd(flags *flags.GlobalFlags) *cobra.Command {
contextCmd := &cobra.Command{
Use: "context",
Short: "Devsy Context commands",
Short: "Manage contexts",
}

contextCmd.AddCommand(NewCreateCmd(flags))
Expand Down
2 changes: 1 addition & 1 deletion cmd/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func NewEnvCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
envCmd := &cobra.Command{
Use: "env",
Short: "Devsy managed environment variables",
Short: "Manage environment variables",
Long: `Manage named environment variables stored per context and injected
into workspaces. Values are stored in plaintext in the Devsy config directory;
use "devsy secret" for sensitive values.`,
Expand Down
8 changes: 7 additions & 1 deletion cmd/env_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"strings"
"testing"

pkgflags "github.com/devsy-org/devsy/pkg/flags"
"github.com/devsy-org/devsy/pkg/flags/names"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -68,7 +69,12 @@ func TestOptInEnvFlags_AppliesEnvValueToFlag(t *testing.T) {
require.NotNil(t, f, "flag --%s not found on %q", tc.flagName, tc.cmdPath)
assert.Equal(t, tc.want, f.Value.String())
assert.True(t, f.Changed, "Changed must be true so MarkFlagRequired passes")
assert.Contains(t, f.Usage, tc.envName, "usage should advertise env var")
assert.Equal(
t,
[]string{tc.envName},
f.Annotations[pkgflags.EnvAnnotation],
"env var should be annotated for the help renderer",
)
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/ide/ide.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func NewIDECmd(flags *flags.GlobalFlags) *cobra.Command {
ideCmd := &cobra.Command{
Use: "ide",
Short: "Devsy IDE commands",
Short: "Manage IDE preferences",
}

ideCmd.AddCommand(NewUseCmd(flags))
Expand Down
2 changes: 1 addition & 1 deletion cmd/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func NewMachineCmd(flags *flags.GlobalFlags) *cobra.Command {
machineCmd := &cobra.Command{
Use: "machine",
Short: "Devsy Machine commands",
Short: "Manage provider-hosted machines",
}

machineCmd.AddCommand(NewListCmd(flags))
Expand Down
2 changes: 1 addition & 1 deletion cmd/mcp/mcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func NewMCPCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
cmd := &cobra.Command{
Use: "mcp",
Short: "Run Devsy as a Model Context Protocol server",
Short: "Run as a Model Context Protocol server",
}
cmd.AddCommand(NewServeCmd(globalFlags))
return cmd
Expand Down
2 changes: 1 addition & 1 deletion cmd/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func NewProviderCmd(flags *flags.GlobalFlags) *cobra.Command {
providerCmd := &cobra.Command{
Use: "provider",
Short: "Devsy Provider commands",
Short: "Manage providers",
}

providerCmd.AddCommand(NewAddCmd(flags))
Expand Down
106 changes: 41 additions & 65 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import (
"github.com/devsy-org/devsy/cmd/pro"
"github.com/devsy-org/devsy/cmd/provider"
secretscmd "github.com/devsy-org/devsy/cmd/secrets"
"github.com/devsy-org/devsy/cmd/self"
"github.com/devsy-org/devsy/cmd/template"
"github.com/devsy-org/devsy/cmd/update"
wsCmdPkg "github.com/devsy-org/devsy/cmd/workspace"
"github.com/devsy-org/devsy/pkg/clierr"
"github.com/devsy-org/devsy/pkg/clihelp"
"github.com/devsy-org/devsy/pkg/config"
"github.com/devsy-org/devsy/pkg/exitcode"
"github.com/devsy-org/devsy/pkg/flags/names"
Expand All @@ -50,18 +51,27 @@ const (
flagLogOutput = "--" + names.LogOutput
flagLogFormat = "--" + names.LogFormat

groupCore = "core"
groupConfig = "config"
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"

internalCommand = "internal"

rootLong = "Devsy — standardized development workspaces built on devcontainers, " +
"running on Docker, Kubernetes, cloud providers, and SSH remote hosts."

rootExample = `- Start a workspace from the current directory:

$ devsy workspace up .

- Open an SSH session to a workspace:

$ devsy workspace ssh my-workspace

- Configure a provider:

$ devsy provider add docker`
)

func proEnabled() bool {
Expand Down Expand Up @@ -250,13 +260,17 @@ func renderCLIError(cliErr *clierr.CLIError, machineMode bool) {
// without reaching for package-level mutable state.
func BuildRoot() (*cobra.Command, *flags.GlobalFlags) {
rootCmd := &cobra.Command{
Use: config.BinaryName,
Short: "Devsy",
Version: version.GetVersion(),
Use: config.BinaryName,
Short: "Devsy",
Long: rootLong,
Example: rootExample,
Version: version.GetVersion(),

SilenceUsage: true,
SilenceErrors: true,
}
rootCmd.SetVersionTemplate("{{.Version}}\n")
clihelp.Install(rootCmd)
persistentFlags := rootCmd.PersistentFlags()
globalFlags := flags.SetGlobalFlags(persistentFlags)
_ = completion.RegisterFlagCompletionFuns(rootCmd, globalFlags)
Expand Down Expand Up @@ -293,67 +307,29 @@ func BuildRoot() (*cobra.Command, *flags.GlobalFlags) {
return nil
}

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)

return rootCmd, globalFlags
}

func registerSubcommands(rootCmd *cobra.Command, globalFlags *flags.GlobalFlags) {
providerCmd := provider.NewProviderCmd(globalFlags)
providerCmd.GroupID = groupConfig
rootCmd.AddCommand(providerCmd)
ideCmd := ide.NewIDECmd(globalFlags)
ideCmd.GroupID = groupConfig
rootCmd.AddCommand(ideCmd)
machineCmd := machine.NewMachineCmd(globalFlags)
machineCmd.GroupID = groupCore
rootCmd.AddCommand(machineCmd)
contextCmd := context.NewContextCmd(globalFlags)
contextCmd.GroupID = groupConfig
rootCmd.AddCommand(contextCmd)
secretsCmd := secretscmd.NewSecretsCmd(globalFlags)
secretsCmd.GroupID = groupConfig
rootCmd.AddCommand(secretsCmd)
envCmd := envcmd.NewEnvCmd(globalFlags)
envCmd.GroupID = groupConfig
rootCmd.AddCommand(envCmd)
rootCmd.AddCommand(
ci.NewCICmd(globalFlags),
cliconfig.NewConfigCmd(globalFlags),
context.NewContextCmd(globalFlags),
envcmd.NewEnvCmd(globalFlags),
feature.NewFeatureCmd(globalFlags),
ide.NewIDECmd(globalFlags),
machine.NewMachineCmd(globalFlags),
mcp.NewMCPCmd(globalFlags),
provider.NewProviderCmd(globalFlags),
secretscmd.NewSecretsCmd(globalFlags),
template.NewTemplateCmd(globalFlags),
update.NewUpdateCmd(),
wsCmdPkg.NewWorkspaceCmd(globalFlags),
cmdinternal.NewInternalCmd(globalFlags),
)
if proEnabled() {
proCmd := pro.NewProCmd(globalFlags)
proCmd.GroupID = groupPlatform
rootCmd.AddCommand(proCmd)
rootCmd.AddCommand(pro.NewProCmd(globalFlags))
}
wsCmd := wsCmdPkg.NewWorkspaceCmd(globalFlags)
wsCmd.GroupID = groupCore
rootCmd.AddCommand(wsCmd)

selfCmd := self.NewSelfCmd(globalFlags)
selfCmd.GroupID = groupMeta
rootCmd.AddCommand(selfCmd)
mcpCmd := mcp.NewMCPCmd(globalFlags)
mcpCmd.GroupID = groupMeta
rootCmd.AddCommand(mcpCmd)
configCmd := cliconfig.NewConfigCmd(globalFlags)
configCmd.GroupID = groupDevcontainer
rootCmd.AddCommand(configCmd)
featureCmd := feature.NewFeatureCmd(globalFlags)
featureCmd.GroupID = groupDevcontainer
rootCmd.AddCommand(featureCmd)
templateCmd := template.NewTemplateCmd(globalFlags)
templateCmd.GroupID = groupDevcontainer
rootCmd.AddCommand(templateCmd)
ciCmd := ci.NewCICmd(globalFlags)
ciCmd.GroupID = groupDevcontainer
rootCmd.AddCommand(ciCmd)
rootCmd.AddCommand(cmdinternal.NewInternalCmd(globalFlags))
}
2 changes: 1 addition & 1 deletion cmd/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func NewSecretsCmd(flags *flags.GlobalFlags) *cobra.Command {
secretsCmd := &cobra.Command{
Use: "secret",
Short: "Devsy Secrets commands",
Short: "Manage secrets",
Long: `Manage named secrets stored locally and injected into workspaces.

Secret values are kept in the OS keyring (macOS Keychain, Windows Credential
Expand Down
16 changes: 0 additions & 16 deletions cmd/self/self.go

This file was deleted.

12 changes: 6 additions & 6 deletions cmd/self/update.go → cmd/update/update.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package self
package update

import (
"fmt"
Expand All @@ -14,7 +14,7 @@ const (
channelBeta = "beta"
)

// UpdateCmd is a struct that defines a command call for "self update".
// UpdateCmd is a struct that defines a command call for "update".
type UpdateCmd struct {
Version string
Channel string
Expand All @@ -24,9 +24,9 @@ type UpdateCmd struct {
// NewUpdateCmd creates a new update command.
func NewUpdateCmd() *cobra.Command {
cmd := &UpdateCmd{}
selfUpdateCmd := &cobra.Command{
updateCmd := &cobra.Command{
Use: "update",
Short: "Update the Devsy CLI to the newest version",
Short: "Update the CLI to the newest version",
Args: cobra.NoArgs,
PreRunE: func(_ *cobra.Command, _ []string) error {
switch cmd.Channel {
Expand Down Expand Up @@ -56,7 +56,7 @@ func NewUpdateCmd() *cobra.Command {
}

cliflags.Add(
selfUpdateCmd,
updateCmd,
cliflags.String(
&cmd.Version,
names.Version,
Expand All @@ -76,5 +76,5 @@ func NewUpdateCmd() *cobra.Command {
"Show which version would be downloaded without actually updating",
),
)
return selfUpdateCmd
return updateCmd
}
2 changes: 1 addition & 1 deletion cmd/self/update_test.go → cmd/update/update_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package self
package update

import (
"testing"
Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/self/self.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ var _ = ginkgo.Describe(
framework.ExpectNoError(err, "getting current working directory should not error")

f := framework.NewDefaultFramework(initialDir + "/bin")
output, err := f.ExecCommandOutput(ctx, []string{"self", "update", "--dry-run"})
framework.ExpectNoError(err, "self update --dry-run should not error")
output, err := f.ExecCommandOutput(ctx, []string{"update", "--dry-run"})
framework.ExpectNoError(err, "update --dry-run should not error")

ginkgo.By("Parsing dry-run key=value output")
lines := strings.Split(strings.TrimSpace(output), "\n")
Expand Down
Loading
Loading