From 102b822cf52d23a5e734b7276400230171e08af3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Gro=C3=9Fmann?= Date: Wed, 27 May 2026 15:07:09 +0200 Subject: [PATCH 1/2] refactor(pass): move root command examples to embedded markdown file --- plugins/pass/command.go | 65 ++-------------------------------------- plugins/pass/examples.md | 61 +++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 62 deletions(-) create mode 100644 plugins/pass/examples.md diff --git a/plugins/pass/command.go b/plugins/pass/command.go index 61cb341f..0b881476 100644 --- a/plugins/pass/command.go +++ b/plugins/pass/command.go @@ -16,6 +16,7 @@ package pass import ( "context" + _ "embed" "errors" "os" "strings" @@ -49,68 +50,8 @@ Examples: {{.Example}}{{end}} ` -const rootExample = ` -### Using keychain secrets in containers - -Create a secret: - -` + "```" + `console -$ docker pass set GH_TOKEN=123456789 -` + "```" + ` - -Create a secret from STDIN: - -` + "```" + `console -echo "my_val" | docker pass set GH_TOKEN -` + "```" + ` - -Run a container that uses the secret: - -` + "```" + `console -$ docker run -e GH_TOKEN= -dt --name demo busybox -` + "```" + ` - -Inspect the secret from inside the container: - -` + "```" + `console -$ docker exec demo sh -c 'echo $GH_TOKEN' -123456789 -` + "```" + ` - -Explicitly assign a secret to a different environment variable: - -` + "```" + `console -$ docker run -e GITHUB_TOKEN=se://GH_TOKEN -dt --name demo busybox -` + "```" + ` - -### Using keychain secrets in Compose - -Store the secrets: - -` + "```" + `console -$ docker pass set myapp/anthropic/api-key=sk-ant-... -$ docker pass set myapp/postgres/password=s3cr3t -` + "```" + ` - -` + "```" + `yaml -services: - api: - image: service1 - environment: - - ANTHROPIC_API_KEY=se://myapp/anthropic/api-key - - POSTGRES_PASSWORD=se://myapp/postgres/password - - worker: - image: service2 - command: worker - environment: - - ANTHROPIC_API_KEY=se://myapp/anthropic/api-key - - db: - image: postgres:17 - environment: - - POSTGRES_PASSWORD=se://myapp/postgres/password -` + "```" +//go:embed examples.md +var rootExample string // Root returns the root command for the docker-pass CLI plugin func Root(ctx context.Context, s store.Store, info commands.VersionInfo) *cobra.Command { diff --git a/plugins/pass/examples.md b/plugins/pass/examples.md new file mode 100644 index 00000000..97d49d6e --- /dev/null +++ b/plugins/pass/examples.md @@ -0,0 +1,61 @@ +### Using keychain secrets in containers + +Create a secret: + +```console +$ docker pass set GH_TOKEN=123456789 +``` + +Create a secret from STDIN: + +```console +echo "my_val" | docker pass set GH_TOKEN +``` + +Run a container that uses the secret: + +```console +$ docker run -e GH_TOKEN= -dt --name demo busybox +``` + +Inspect the secret from inside the container: + +```console +$ docker exec demo sh -c 'echo $GH_TOKEN' +123456789 +``` + +Explicitly assign a secret to a different environment variable: + +```console +$ docker run -e GITHUB_TOKEN=se://GH_TOKEN -dt --name demo busybox +``` + +### Using keychain secrets in Compose + +Store the secrets: + +```console +$ docker pass set myapp/anthropic/api-key=sk-ant-... +$ docker pass set myapp/postgres/password=s3cr3t +``` + +```yaml +services: + api: + image: service1 + environment: + - ANTHROPIC_API_KEY=se://myapp/anthropic/api-key + - POSTGRES_PASSWORD=se://myapp/postgres/password + + worker: + image: service2 + command: worker + environment: + - ANTHROPIC_API_KEY=se://myapp/anthropic/api-key + + db: + image: postgres:17 + environment: + - POSTGRES_PASSWORD=se://myapp/postgres/password +``` From f0417af1e543b8cb37942d5a8bf2145668cf2dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Gro=C3=9Fmann?= Date: Wed, 27 May 2026 15:15:37 +0200 Subject: [PATCH 2/2] refactor(pass): extract subcommand examples to embedded markdown Move the set, run, and rm command examples into sibling .md files and embed them via //go:embed, mirroring the root command. Wrap example shell snippets in fenced console blocks and use inline backticks for flag and value references in Long descriptions. --- plugins/pass/command.go | 16 +++++------ plugins/pass/commands/rm.go | 15 +++++------ plugins/pass/commands/rm_example.md | 17 ++++++++++++ plugins/pass/commands/run.go | 36 +++++++++---------------- plugins/pass/commands/run_example.md | 23 ++++++++++++++++ plugins/pass/commands/set.go | 40 +++++++++------------------- plugins/pass/commands/set_example.md | 30 +++++++++++++++++++++ 7 files changed, 110 insertions(+), 67 deletions(-) create mode 100644 plugins/pass/commands/rm_example.md create mode 100644 plugins/pass/commands/run_example.md create mode 100644 plugins/pass/commands/set_example.md diff --git a/plugins/pass/command.go b/plugins/pass/command.go index 0b881476..c70241b5 100644 --- a/plugins/pass/command.go +++ b/plugins/pass/command.go @@ -58,14 +58,14 @@ func Root(ctx context.Context, s store.Store, info commands.VersionInfo) *cobra. cmd := &cobra.Command{ Use: "pass set|get|ls|rm|run", Short: "Manage your local OS keychain secrets.", - Long: `Docker Pass is a helper for securely storing secrets in your local OS keychain and injecting them into containers when needed. -It uses platform-specific credential storage: - - - Windows: Windows Credential Manager API - - macOS: Keychain services API - - Linux: org.freedesktop.secrets API (requires DBus + gnome-keyring or kdewallet) - -Secrets can be injected into running containers at runtime using the se:// URI scheme.`, + Long: "Docker Pass is a helper for securely storing secrets in your local OS keychain and injecting them into containers when needed.\n" + + "It uses platform-specific credential storage:\n" + + "\n" + + " - Windows: Windows Credential Manager API\n" + + " - macOS: Keychain services API\n" + + " - Linux: `org.freedesktop.secrets` API (requires DBus + `gnome-keyring` or `kdewallet`)\n" + + "\n" + + "Secrets can be injected into running containers at runtime using the `se://` URI scheme.", Example: strings.TrimSpace(rootExample), SilenceUsage: true, TraverseChildren: true, diff --git a/plugins/pass/commands/rm.go b/plugins/pass/commands/rm.go index d6872713..f4ecdcc1 100644 --- a/plugins/pass/commands/rm.go +++ b/plugins/pass/commands/rm.go @@ -16,6 +16,7 @@ package commands import ( "context" + _ "embed" "errors" "fmt" "io" @@ -27,6 +28,9 @@ import ( "github.com/docker/secrets-engine/store" ) +//go:embed rm_example.md +var rmExample string + type rmOpts struct { All bool } @@ -37,15 +41,8 @@ func RmCommand(kc store.Store) *cobra.Command { Use: "rm name1 name2 ...", Aliases: []string{"delete", "erase", "remove"}, Short: "Remove secrets from local keychain.", - Long: "Removes one or more named secrets from the local OS keychain.\nUse --all to remove every stored secret at once.", - Example: `### Remove a specific secret: -docker pass rm GH_TOKEN - -### Remove multiple secrets: -docker pass rm GH_TOKEN NPM_TOKEN - -### Remove all secrets: -docker pass rm --all`, + Long: "Removes one or more named secrets from the local OS keychain. Use `--all` to remove every stored secret at once.", + Example: strings.Trim(rmExample, "\n"), RunE: func(cmd *cobra.Command, args []string) error { idList, err := validateArgs(args, opts) if err != nil { diff --git a/plugins/pass/commands/rm_example.md b/plugins/pass/commands/rm_example.md new file mode 100644 index 00000000..80790032 --- /dev/null +++ b/plugins/pass/commands/rm_example.md @@ -0,0 +1,17 @@ +### Remove a specific secret: + +```console +$ docker pass rm GH_TOKEN +``` + +### Remove multiple secrets: + +```console +$ docker pass rm GH_TOKEN NPM_TOKEN +``` + +### Remove all secrets: + +```console +$ docker pass rm --all +``` diff --git a/plugins/pass/commands/run.go b/plugins/pass/commands/run.go index 2022a6c6..ec615a93 100644 --- a/plugins/pass/commands/run.go +++ b/plugins/pass/commands/run.go @@ -16,6 +16,7 @@ package commands import ( "context" + _ "embed" "errors" "fmt" "maps" @@ -48,19 +49,8 @@ func (e *ExitCodeError) Error() string { return fmt.Sprintf("child exited with code %d", e.Code) } -const runExample = ` -### Run a command with one secret in its environment: -SE_TOKEN=se://gh-token docker pass run -- gh repo list - -### Multiple references: -DB_PASSWORD=se://myapp/postgres/password API_KEY=se://myapp/anthropic/api-key docker pass run -- ./my-binary - -### Resolve references from a dotenv file: -docker pass run --env-file .env -- ./my-binary - -### Multiple files (later overrides earlier; files override the process environment): -docker pass run --env-file .env --env-file .env.local -- ./my-binary -` +//go:embed run_example.md +var runExample string type runOpts struct { envFiles []string @@ -70,16 +60,16 @@ func RunCommand() *cobra.Command { opts := runOpts{} cmd := &cobra.Command{ Use: "run -- CMD [ARGS...]", - Short: "Run a command with se:// environment references resolved.", - Long: `Scans the current environment (plus any --env-file inputs) for variables -whose value is exactly se://NAME. Each reference is resolved through the -secrets-engine daemon and the resolved value is passed to the child process. -The child inherits stdin, stdout, and stderr. - -Requires the secrets-engine daemon (Docker Desktop) to be running. - -If any reference cannot be resolved, the command fails before the child is -started and exits non-zero.`, + Short: "Run a command with `se://` environment references resolved.", + Long: "Scans the current environment (plus any `--env-file` inputs) for variables\n" + + "whose value is exactly `se://`. Each reference is resolved through the\n" + + "secrets-engine daemon and the resolved value is passed to the child process.\n" + + "The child inherits stdin, stdout, and stderr.\n" + + "\n" + + "Requires the secrets-engine daemon (Docker Desktop) to be running.\n" + + "\n" + + "If any reference cannot be resolved, the command fails before the child is\n" + + "started and exits non-zero.", Example: strings.Trim(runExample, "\n"), Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/plugins/pass/commands/run_example.md b/plugins/pass/commands/run_example.md new file mode 100644 index 00000000..dc969034 --- /dev/null +++ b/plugins/pass/commands/run_example.md @@ -0,0 +1,23 @@ +### Run a command with one secret in its environment: + +```console +$ SE_TOKEN=se://gh-token docker pass run -- gh repo list +``` + +### Multiple references: + +```console +$ DB_PASSWORD=se://myapp/postgres/password API_KEY=se://myapp/anthropic/api-key docker pass run -- ./my-binary +``` + +### Resolve references from a dotenv file: + +```console +$ docker pass run --env-file .env -- ./my-binary +``` + +### Multiple files (later overrides earlier; files override the process environment): + +```console +$ docker pass run --env-file .env --env-file .env.local -- ./my-binary +``` diff --git a/plugins/pass/commands/set.go b/plugins/pass/commands/set.go index f6549ba1..959491bb 100644 --- a/plugins/pass/commands/set.go +++ b/plugins/pass/commands/set.go @@ -16,6 +16,7 @@ package commands import ( "context" + _ "embed" "encoding/json" "fmt" "io" @@ -29,23 +30,8 @@ import ( "github.com/docker/secrets-engine/x/secrets" ) -const setExample = ` -### Set a secret: -docker pass set POSTGRES_PASSWORD=my-secret-password - -### Or pass the secret via STDIN: -echo my-secret-password > pwd.txt -cat pwd.txt | docker pass set POSTGRES_PASSWORD - -### Set a secret with metadata: -docker pass set POSTGRES_PASSWORD=my-secret-password --metadata owner=alice --metadata expiry=2027-03-01 - -### Or pass a JSON payload with secret and metadata via STDIN: -echo '{"secret":"my-secret-password","metadata":{"owner":"alice"}}' | docker pass set POSTGRES_PASSWORD - -### Overwrite an existing secret: -docker pass set POSTGRES_PASSWORD=new-secret-password --force -` +//go:embed set_example.md +var setExample string type setOpts struct { metadata []string // raw "key=value" strings from --metadata flag @@ -63,16 +49,16 @@ func SetCommand(kc store.Store) *cobra.Command { Use: "set id[=value]", Aliases: []string{"store", "save"}, Short: "Set a secret", - Long: `Stores a secret in the local OS keychain. The secret value can be provided inline (NAME=VALUE) or piped via STDIN. - -Behavior when a secret with the same id already exists is platform-dependent: - - macOS (Keychain): the command fails with a duplicate-item error. - - Linux (Secret Service) and Windows (Credential Manager): the existing - value is silently overwritten. - -Pass --force to overwrite an existing secret. On Linux and Windows the -replacement is performed atomically. On macOS the Keychain API requires -a delete-then-add sequence.`, + Long: "Stores a secret in the local OS keychain. The secret value can be provided inline (`NAME=VALUE`) or piped via STDIN.\n" + + "\n" + + "Behavior when a secret with the same id already exists is platform-dependent:\n" + + " - macOS (Keychain): the command fails with a duplicate-item error.\n" + + " - Linux (Secret Service) and Windows (Credential Manager): the existing\n" + + " value is silently overwritten.\n" + + "\n" + + "Pass `--force` to overwrite an existing secret. On Linux and Windows the\n" + + "replacement is performed atomically. On macOS the Keychain API requires\n" + + "a delete-then-add sequence.", Example: strings.Trim(setExample, "\n"), Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/plugins/pass/commands/set_example.md b/plugins/pass/commands/set_example.md new file mode 100644 index 00000000..ec5cbd9d --- /dev/null +++ b/plugins/pass/commands/set_example.md @@ -0,0 +1,30 @@ +### Set a secret: + +```console +$ docker pass set POSTGRES_PASSWORD=my-secret-password +``` + +### Or pass the secret via STDIN: + +```console +$ echo my-secret-password > pwd.txt +$ cat pwd.txt | docker pass set POSTGRES_PASSWORD +``` + +### Set a secret with metadata: + +```console +$ docker pass set POSTGRES_PASSWORD=my-secret-password --metadata owner=alice --metadata expiry=2027-03-01 +``` + +### Or pass a JSON payload with secret and metadata via STDIN: + +```console +$ echo '{"secret":"my-secret-password","metadata":{"owner":"alice"}}' | docker pass set POSTGRES_PASSWORD +``` + +### Overwrite an existing secret: + +```console +$ docker pass set POSTGRES_PASSWORD=new-secret-password --force +```