feat: add secrets with variants#507
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: New CI commands use inline JSON instead of writeJSON
- Replaced inline JSON encoding with shared output helpers and added shared output validation for the affected commands.
- ✅ Fixed: Unused variant repository match helper functions
- Removed the unused unexported repository match helper functions from variants.go.
Or push these changes by commenting:
@cursor push 28af60c909
Preview (28af60c909)
diff --git a/pkg/cmd/ci/secrets.go b/pkg/cmd/ci/secrets.go
--- a/pkg/cmd/ci/secrets.go
+++ b/pkg/cmd/ci/secrets.go
@@ -1,7 +1,6 @@
package ci
import (
- "encoding/json"
"fmt"
"io"
"os"
@@ -483,6 +482,9 @@
depot ci secrets get MY_API_KEY production --output json`,
Args: cobra.MaximumNArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
+ if err := validateTextOrJSONOutput(output); err != nil {
+ return err
+ }
ctx := cmd.Context()
if orgID == "" {
@@ -534,10 +536,8 @@
resolved = matches[0]
}
- if output == "json" {
- enc := json.NewEncoder(os.Stdout)
- enc.SetIndent("", " ")
- return enc.Encode(resolved)
+ if outputIsJSON(output) {
+ return writeJSON(resolved)
}
printSecretVariantDetail(secretName, resolved)
@@ -621,6 +621,9 @@
Aliases: []string{"ls"},
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
+ if err := validateTextOrJSONOutput(output); err != nil {
+ return err
+ }
ctx := cmd.Context()
if orgID == "" {
@@ -657,10 +660,8 @@
}
}
- if output == "json" {
- enc := json.NewEncoder(os.Stdout)
- enc.SetIndent("", " ")
- return enc.Encode(result)
+ if outputIsJSON(output) {
+ return writeJSON(result)
}
if len(result.Secrets) == 0 {
diff --git a/pkg/cmd/ci/variants.go b/pkg/cmd/ci/variants.go
--- a/pkg/cmd/ci/variants.go
+++ b/pkg/cmd/ci/variants.go
@@ -45,14 +45,6 @@
return strings.Join(repos, ",")
}
-func secretVariantRepositoryMatches(variant api.CISecretVariant, repos []string) bool {
- return variantAttributesMatch(variant.Attributes, repos, nil, nil, nil)
-}
-
-func variableVariantRepositoryMatches(variant api.CIVariableVariant, repos []string) bool {
- return variantAttributesMatch(variant.Attributes, repos, nil, nil, nil)
-}
-
func filterSecretVariants(secret api.CISecretGroup, repo, environment, branch, workflow []string) api.CISecretGroup {
if len(repo) == 0 && len(environment) == 0 && len(branch) == 0 && len(workflow) == 0 {
return secret
diff --git a/pkg/cmd/ci/vars.go b/pkg/cmd/ci/vars.go
--- a/pkg/cmd/ci/vars.go
+++ b/pkg/cmd/ci/vars.go
@@ -1,9 +1,7 @@
package ci
import (
- "encoding/json"
"fmt"
- "os"
"strings"
"github.com/depot/cli/pkg/api"
@@ -324,6 +322,9 @@
Aliases: []string{"ls"},
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
+ if err := validateTextOrJSONOutput(output); err != nil {
+ return err
+ }
ctx := cmd.Context()
if orgID == "" {
@@ -360,10 +361,8 @@
}
}
- if output == "json" {
- enc := json.NewEncoder(os.Stdout)
- enc.SetIndent("", " ")
- return enc.Encode(result)
+ if outputIsJSON(output) {
+ return writeJSON(result)
}
if len(result.Variables) == 0 {You can send follow-ups to the cloud agent here.
Fetch all secret and variable pages internally, keep secrets list output compact, and reject unsupported output formats consistently.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Stdin consumed by SecretValueFromInput conflicts with SecretsBulk
- Added a hidden --value fallback to secrets set so CI automation can provide the secret without blocking on stdin.
Or push these changes by commenting:
@cursor push 44d042712b
Preview (44d042712b)
diff --git a/pkg/cmd/ci/ci_test.go b/pkg/cmd/ci/ci_test.go
--- a/pkg/cmd/ci/ci_test.go
+++ b/pkg/cmd/ci/ci_test.go
@@ -3,6 +3,7 @@
import (
"testing"
+ "github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -44,15 +45,20 @@
}
func TestSecretsAddKeepsHiddenValueFlagForCompatibility(t *testing.T) {
- cmd := NewCmdSecretsAdd()
-
- valueFlag := cmd.Flags().Lookup("value")
- if valueFlag == nil {
- t.Fatal("expected hidden --value compatibility flag")
+ for name, cmd := range map[string]*cobra.Command{
+ "secrets add": NewCmdSecretsAdd(),
+ "secrets set": NewCmdSecretsSet(),
+ } {
+ t.Run(name, func(t *testing.T) {
+ valueFlag := cmd.Flags().Lookup("value")
+ if valueFlag == nil {
+ t.Fatal("expected hidden --value compatibility flag")
+ }
+ if !valueFlag.Hidden {
+ t.Fatal("expected --value to stay hidden from help")
+ }
+ })
}
- if !valueFlag.Hidden {
- t.Fatal("expected --value to stay hidden from help")
- }
}
func TestVariantSelectorFlagsAreRepeatable(t *testing.T) {
diff --git a/pkg/cmd/ci/secrets.go b/pkg/cmd/ci/secrets.go
--- a/pkg/cmd/ci/secrets.go
+++ b/pkg/cmd/ci/secrets.go
@@ -62,6 +62,7 @@
var (
orgID string
token string
+ value string
description string
repo []string
environment []string
@@ -112,9 +113,12 @@
return fmt.Errorf("secret name cannot be empty")
}
- secretValue, err := helpers.SecretValueFromInput(fmt.Sprintf("Enter value for secret '%s': ", secretName))
- if err != nil {
- return fmt.Errorf("failed to read secret value: %w", err)
+ secretValue := value
+ if secretValue == "" {
+ secretValue, err = helpers.SecretValueFromInput(fmt.Sprintf("Enter value for secret '%s': ", secretName))
+ if err != nil {
+ return fmt.Errorf("failed to read secret value: %w", err)
+ }
}
result, err := api.CISetSecretVariant(ctx, tokenVal, orgID, api.CISetSecretVariantOptions{
@@ -138,11 +142,13 @@
cmd.Flags().StringVar(&orgID, "org", "", "Organization ID (required when user is a member of multiple organizations)")
cmd.Flags().StringVar(&token, "token", "", "Depot API token")
+ cmd.Flags().StringVar(&value, "value", "", "Secret value (deprecated; prefer stdin)")
cmd.Flags().StringVar(&description, "description", "", "Description of the secret variant")
cmd.Flags().StringArrayVar(&repo, "repo", nil, "Apply variant to a repository (repeatable, e.g. owner/repo)")
cmd.Flags().StringArrayVar(&environment, "env", nil, "Apply variant to an environment (repeatable)")
cmd.Flags().StringArrayVar(&branch, "branch", nil, "Apply variant to a branch (repeatable)")
cmd.Flags().StringArrayVar(&workflow, "workflow", nil, "Apply variant to a workflow file (repeatable)")
+ _ = cmd.Flags().MarkHidden("value")
return cmd
}You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Vars set prompts interactively in non-interactive environments
- Added a non-interactive stdin guard for vars set so it fails clearly unless --value is provided, with a regression test covering the behavior.
Or push these changes by commenting:
@cursor push 38046dc8d1
Preview (38046dc8d1)
diff --git a/pkg/cmd/ci/ci_test.go b/pkg/cmd/ci/ci_test.go
--- a/pkg/cmd/ci/ci_test.go
+++ b/pkg/cmd/ci/ci_test.go
@@ -72,6 +72,21 @@
}
}
+func TestVarsSetRequiresValueInNonInteractiveMode(t *testing.T) {
+ cmd := NewCmdVarsSet()
+ cmd.SilenceUsage = true
+ cmd.SilenceErrors = true
+ cmd.SetArgs([]string{"MY_VAR"})
+
+ err := cmd.Execute()
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ if !strings.Contains(err.Error(), "pass --value") {
+ t.Fatalf("error = %q", err)
+ }
+}
+
func TestVariantSelectorFlagsAreRepeatable(t *testing.T) {
for name, cmd := range map[string]commandWithFlags{
"secrets set": {flags: NewCmdSecretsSet().Flags()},
diff --git a/pkg/cmd/ci/vars.go b/pkg/cmd/ci/vars.go
--- a/pkg/cmd/ci/vars.go
+++ b/pkg/cmd/ci/vars.go
@@ -86,6 +86,10 @@
orgID = config.GetCurrentOrganization()
}
+ if value == "" && !helpers.IsStdinTerminal() {
+ return fmt.Errorf("cannot prompt for a variable value in non-interactive mode; pass --value to provide the value")
+ }
+
tokenVal, err := helpers.ResolveProjectAuth(ctx, token)
if err != nil {
return errYou can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: List commands ignore user filter flags for API
- List commands now pass repo, environment, branch, and workflow filters into the secret and variable variant API calls before applying local filtering.
Or push these changes by commenting:
@cursor push 61597b7dc0
Preview (61597b7dc0)
diff --git a/pkg/cmd/ci/secrets.go b/pkg/cmd/ci/secrets.go
--- a/pkg/cmd/ci/secrets.go
+++ b/pkg/cmd/ci/secrets.go
@@ -678,10 +678,10 @@
} else {
var err error
result, err = api.CIListSecretVariants(ctx, tokenVal, orgID, api.CIListSecretVariantsOptions{
- Repo: nil,
- Environment: nil,
- Branch: nil,
- Workflow: nil,
+ Repo: repo,
+ Environment: environment,
+ Branch: branch,
+ Workflow: workflow,
})
if err != nil {
return fmt.Errorf("failed to list secrets: %w", err)
diff --git a/pkg/cmd/ci/vars.go b/pkg/cmd/ci/vars.go
--- a/pkg/cmd/ci/vars.go
+++ b/pkg/cmd/ci/vars.go
@@ -362,10 +362,10 @@
} else {
var err error
result, err = api.CIListVariableVariants(ctx, tokenVal, orgID, api.CIListVariableVariantsOptions{
- Repo: nil,
- Environment: nil,
- Branch: nil,
- Workflow: nil,
+ Repo: repo,
+ Environment: environment,
+ Branch: branch,
+ Workflow: workflow,
})
if err != nil {
return fmt.Errorf("failed to list CI variables: %w", err)You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 8f63bc1. Configure here.


Note
Medium Risk
Introduces new CI secrets/variables variant APIs and rewires multiple CLI verbs (add/list/get/remove) to use them, which can affect existing workflows and deletion behavior. Risk is mitigated by compatibility shims (hidden legacy flags/JSON paths) and added unit tests, but it’s still a broad behavior change.
Overview
Adds first-class CI secret/variable variants (with match attributes like
--repo/--env/--branch/--workflow) by introducing v3beta2 API clients/types inpkg/api/ci.go, including pagination-aware listing, group/variant retrieval, set/update, and variant/group deletion.Updates
depot ci secretsanddepot ci varscommands to manage variants: newsetverbs, newsecrets bulkdotenv import, newsecrets get, repeatable selector flags, and updated list/table output to show variants (while keeping legacy JSON/list behavior when only the old single---reposelector is used).Improves non-interactive handling by reading secret values from stdin (
--from-stdin) and hiding the old--valueflag for secrets, plus adds helper functions (IsStdinTerminal, stdin secret parsing) and extensive tests for variant resolution/filtering and flag/UX compatibility.Also bumps numerous Go module dependencies in
examples/go.mod/go.sum.Reviewed by Cursor Bugbot for commit 8f63bc1. Bugbot is set up for automated code reviews on this repo. Configure here.