Skip to content
Draft
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
22 changes: 22 additions & 0 deletions cmd/context/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (

"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/config"
"github.com/devsy-org/devsy/pkg/log"
"github.com/devsy-org/devsy/pkg/secrets"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -59,6 +61,8 @@ func (cmd *DeleteCmd) Run(ctx context.Context, context string) error {
return fmt.Errorf("cannot delete 'default' context")
}

deleteContextSecrets(devsyConfig, context)

delete(devsyConfig.Contexts, context)
if devsyConfig.DefaultContext == context {
devsyConfig.DefaultContext = "default"
Expand All @@ -74,3 +78,21 @@ func (cmd *DeleteCmd) Run(ctx context.Context, context string) error {

return nil
}

func deleteContextSecrets(devsyConfig *config.Config, contextName string) {
ctxConfig := devsyConfig.Contexts[contextName]
if ctxConfig == nil || len(ctxConfig.Secrets) == 0 {
return
}

store, err := secrets.NewStoreForConfig(devsyConfig)
if err != nil {
log.Warnf("skip deleting secrets for context %q: %v", contextName, err)
return
}
for _, name := range ctxConfig.Secrets {
if err := store.Delete(contextName, name); err != nil {
log.Warnf("delete secret %q for context %q: %v", name, contextName, err)
}
}
}
47 changes: 47 additions & 0 deletions cmd/env/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package env

import (
"context"
"fmt"

"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/log"
"github.com/spf13/cobra"
)

type DeleteCmd struct {
*flags.GlobalFlags
}

func NewDeleteCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd := &DeleteCmd{GlobalFlags: flags}
deleteCmd := &cobra.Command{
Use: "delete NAME",
Aliases: []string{"rm"},
Short: "Delete an environment variable from the active context",
Args: cobra.ExactArgs(1),
RunE: func(cobraCmd *cobra.Command, args []string) error {
return cmd.Run(cobraCmd.Context(), args[0])
},
}
return deleteCmd
}

func (cmd *DeleteCmd) Run(_ context.Context, name string) error {
contextName, store, err := resolveContext(cmd.GlobalFlags)
if err != nil {
return err
}
meta, err := store.Meta(contextName, name)
if err != nil {
return err
}
if meta.Sensitive() {
return fmt.Errorf("%q is a secret; use \"devsy secret delete\"", name)
}
if err := store.Delete(contextName, name); err != nil {
return err
}
log.Infof("env var %q deleted from context %q", name, contextName)
return nil
}
38 changes: 38 additions & 0 deletions cmd/env/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package env

import (
"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/config"
"github.com/devsy-org/devsy/pkg/secrets"
"github.com/spf13/cobra"
)

// NewEnvCmd returns the `devsy env` command group: managed, non-sensitive
// environment variables stored per context and injected into workspaces.
func NewEnvCmd(globalFlags *flags.GlobalFlags) *cobra.Command {
envCmd := &cobra.Command{
Use: "env",
Short: "Devsy managed 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.`,
}
envCmd.AddCommand(NewSetCmd(globalFlags))
envCmd.AddCommand(NewListCmd(globalFlags))
envCmd.AddCommand(NewGetCmd(globalFlags))
envCmd.AddCommand(NewDeleteCmd(globalFlags))
return envCmd
}

// resolveContext returns the active context name and a Store for it.
func resolveContext(globalFlags *flags.GlobalFlags) (string, secrets.Store, error) {
devsyConfig, err := config.LoadConfig(globalFlags.Context, globalFlags.Provider)
if err != nil {
return "", nil, err
}
store, err := secrets.NewStoreForConfig(devsyConfig)
if err != nil {
return "", nil, err
}
return devsyConfig.DefaultContext, store, nil
}
47 changes: 47 additions & 0 deletions cmd/env/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package env

import (
"context"
"fmt"

"github.com/devsy-org/devsy/cmd/flags"
"github.com/spf13/cobra"
)

type GetCmd struct {
*flags.GlobalFlags
}

func NewGetCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd := &GetCmd{GlobalFlags: flags}
getCmd := &cobra.Command{
Use: "get NAME",
Short: "Print an environment variable's value",
Args: cobra.ExactArgs(1),
RunE: func(cobraCmd *cobra.Command, args []string) error {
return cmd.Run(cobraCmd.Context(), args[0])
},
}
return getCmd
}

func (cmd *GetCmd) Run(_ context.Context, name string) error {
contextName, store, err := resolveContext(cmd.GlobalFlags)
if err != nil {
return err
}
meta, err := store.Meta(contextName, name)
if err != nil {
return err
}
if meta.Sensitive() {
return fmt.Errorf("%q is a secret; use \"devsy secret get\"", name)
}
value, err := store.Get(contextName, name)
if err != nil {
return err
}
//nolint:forbidigo // get prints the raw value to stdout for scripting.
fmt.Println(value)
return nil
}
82 changes: 82 additions & 0 deletions cmd/env/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package env

import (
"context"
"encoding/json"
"fmt"

"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/output"
"github.com/devsy-org/devsy/pkg/table"
"github.com/spf13/cobra"
)

type ListCmd struct {
*flags.GlobalFlags
}

func NewListCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd := &ListCmd{GlobalFlags: flags}
listCmd := &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List environment variables in the active context",
Args: cobra.NoArgs,
RunE: func(cobraCmd *cobra.Command, args []string) error {
return cmd.Run(cobraCmd.Context())
},
}
return listCmd
}

type envEntry struct {
Name string `json:"name"`
Value string `json:"value"`
}

func (cmd *ListCmd) Run(_ context.Context) error {
contextName, store, err := resolveContext(cmd.GlobalFlags)
if err != nil {
return err
}
metas, err := store.List(contextName)
if err != nil {
return err
}

entries := make([]envEntry, 0, len(metas))
for _, m := range metas {
if m.Sensitive() {
continue
}
entries = append(entries, envEntry{Name: m.Name, Value: m.Value})
}

mode, err := output.ResolveMode(cmd.ResultFormat)
if err != nil {
return err
}
if mode == output.ModeJSON {
return renderJSON(entries)
}
renderPlain(entries)
return nil
}

func renderJSON(entries []envEntry) error {
out, err := json.MarshalIndent(entries, "", " ")
if err != nil {
return err
}
//nolint:forbidigo // list --result-format json prints structured data to stdout.
fmt.Print(string(out))
return nil
}

func renderPlain(entries []envEntry) {
rows := make([][]string, 0, len(entries))
for _, e := range entries {
rows = append(rows, []string{e.Name, e.Value})
}
table.Print([]string{"Name", "Value"}, rows)
}
57 changes: 57 additions & 0 deletions cmd/env/set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package env

import (
"context"
"strings"

"github.com/devsy-org/devsy/cmd/flags"
cliflags "github.com/devsy-org/devsy/pkg/flags"
"github.com/devsy-org/devsy/pkg/flags/names"
"github.com/devsy-org/devsy/pkg/log"
"github.com/devsy-org/devsy/pkg/secrets"
"github.com/spf13/cobra"
)

type SetCmd struct {
*flags.GlobalFlags

Value string
}

func NewSetCmd(flags *flags.GlobalFlags) *cobra.Command {
cmd := &SetCmd{GlobalFlags: flags}
setCmd := &cobra.Command{
Use: "set NAME=VALUE | set NAME --value VALUE",
Short: "Create or update an environment variable",
Args: cobra.ExactArgs(1),
RunE: func(cobraCmd *cobra.Command, args []string) error {
return cmd.Run(cobraCmd.Context(), args[0])
},
}
cliflags.Add(
setCmd,
cliflags.String(&cmd.Value, names.Value, "", "The value (alternative to NAME=VALUE)"),
)
return setCmd
}

func (cmd *SetCmd) Run(_ context.Context, arg string) error {
name, value := arg, cmd.Value
if k, v, ok := strings.Cut(arg, "="); ok {
name, value = k, v
}
if err := secrets.ValidateName(name); err != nil {
return err
}

contextName, store, err := resolveContext(cmd.GlobalFlags)
if err != nil {
return err
}
if err := store.Set(contextName, name, value, secrets.KindEnv); err != nil {
return err
}

log.Infof("env var %q set in context %q", name, contextName)
return nil
}
23 changes: 15 additions & 8 deletions cmd/internal/agentcontainer/deferred_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"strings"

"github.com/devsy-org/devsy/cmd/flags"
"github.com/devsy-org/devsy/pkg/compress"
config2 "github.com/devsy-org/devsy/pkg/config"
"github.com/devsy-org/devsy/pkg/devcontainer/config"
"github.com/devsy-org/devsy/pkg/devcontainer/setup"
cliflags "github.com/devsy-org/devsy/pkg/flags"
Expand All @@ -17,14 +20,22 @@ import (
"github.com/spf13/cobra"
)

func secretsEnvFromEnvironment() []string {
raw := os.Getenv(config2.EnvSecretsEnv)
if raw == "" {
return nil
}

return strings.Split(raw, "\n")
}

// DeferredHooksCmd runs deferred lifecycle hooks as a detached background process.
type DeferredHooksCmd struct {
*flags.GlobalFlags
SetupInfo string
Prebuild bool
DotfilesRepo string
DotfilesScript string
SecretsEnv []string
}

// NewDeferredHooksCmd creates a new command.
Expand All @@ -51,12 +62,6 @@ func NewDeferredHooksCmd(flags *flags.GlobalFlags) *cobra.Command {
"",
"Dotfiles install script path",
),
cliflags.StringSlice(
&cmd.SecretsEnv,
names.SecretsEnv,
[]string{},
"Secrets to inject into lifecycle commands (KEY=VALUE)",
),
)
_ = deferredCmd.MarkFlagRequired(names.SetupInfo)
return deferredCmd
Expand All @@ -79,7 +84,9 @@ func (cmd *DeferredHooksCmd) Run(ctx context.Context) error {
Repository: cmd.DotfilesRepo,
InstallScript: cmd.DotfilesScript,
RemoteUser: config.GetRemoteUser(setupInfo),
}, cmd.SecretsEnv, setup.SkipPhases{})
// Mount-secret values are not piped to this re-exec'd process (only env
// secrets are), so there is nothing extra to add to the log redactor here.
}, secretsEnvFromEnvironment(), nil, setup.SkipPhases{})
if err != nil {
return fmt.Errorf("deferred hooks setup: %w", err)
}
Expand Down
Loading
Loading