Skip to content

Allow passing several files to exec-env - #2324

Open
abd-ulbasit wants to merge 1 commit into
getsops:mainfrom
abd-ulbasit:sops-945
Open

abd-ulbasit wants to merge 1 commit into
getsops:mainfrom
abd-ulbasit:sops-945

Conversation

@abd-ulbasit

@abd-ulbasit abd-ulbasit commented Oct 17, 2026 •

Copy link
Copy Markdown

Refs #945

This lets sops exec-env take more than one encrypted file:

sops exec-env [flags] FILE [FILE...] COMMAND

All arguments except the last are files; the last is the command, run through sh -c as today. It's the smallest piece of #945, along the lines of the suggestion in #945 (comment).

Behaviour:

  • Values from all files go into the environment. If a key is in several files, the last file wins. That's what nesting gives today: sops exec-env a.yaml "sops exec-env b.yaml 'cmd'". Files still override the inherited environment, as before.
  • If a key appears in more than one file, sops keeps only the last value (exec.MergeEnv), so which file wins doesn't depend on how the child process, or execve with --same-process, treats duplicate keys.
  • Every file is decrypted before the command starts. If one fails (missing, not a sops file, MAC mismatch, complex value), the command doesn't run.
  • With more than one file, an error about a file is prefixed with its name, because several existing messages (e.g. sops metadata not found) don't say which file failed. Exit codes are unchanged. Errors loading .sops.yaml keep their message, since the config is the same for all files.
  • Each file's format comes from its own extension. The config (--config, or the .sops.yaml found from the working directory), --decryption-order, --ignore-mac and the key service flags are the same for all files.

Compatibility:

  • Today exec-env needs exactly two positional arguments, and anything else fails with error: missing file to decrypt. So nothing that works today changes meaning. Single-file calls give the same output, error messages and exit codes as before, with one exception: the config is now loaded after the flags are checked. So if both .sops.yaml and the flags are invalid, the flag error is reported instead of the config error.
  • One visible difference: the argv form from exec-env/exec-file: support "--" to separate command to run #1469 (sops exec-env f.yaml -- npm start) still fails, but now the error is about a file named npm instead of missing file to decrypt.
  • As everywhere in sops, flags go before the first file. With urfave/cli v1, a boolean flag between two files takes the next file with it (exec-env a.yaml --pristine b.yaml cmd decrypts b.yaml first). That's existing parser behaviour and I didn't try to work around it here; the exec-env help text now says so.
  • Values from earlier files stay in memory while later files are decrypted. That's the same exposure as one file with more keys, just mentioning it.

Not in this PR:

Open question: this uses the positional tail that the argv mode in #1469 might want. If you'd rather keep that free, I can switch to a repeatable flag for the extra files.

Heads-up: #2321 adds a field to the exec-env decryptOpts, which this PR moves into decryptFileToEnv. Whichever lands second needs a small rebase; I'm happy to do it here.

Tests:

  • cmd/sops/subcommand/exec/exec_test.go: MergeEnv.
  • cmd/sops/exec_env_test.go: envFromTree and the file-name prefix. The existing error messages are pinned byte-for-byte.
  • Functional test exec_env_multiple_files: two files with a shared key, in both orders. Then three failures where the command doesn't run: an unencrypted second file (the error starts with its name), that file alone (same message as today, no prefix), and a broken --config with two files (no prefix).
  • Locally on macOS arm64 with Go 1.26: go vet, gofmt -s, go test -race for ./cmd/sops/..., cargo fmt --check, and the functional tests (all pass except the two Vault publish tests, which need a Vault server). I also checked by hand with age-encrypted YAML, dotenv and JSON files, including --pristine and --same-process.

Once the shape is settled I'll send a PR to getsops/docs for the exec-env section.

All arguments except the last one are now files to decrypt; the last
one is still the command to run. The values of all files are inserted
into the environment. If a key appears in several files, the value from
the last of these files is used, which is the same result as nesting one
exec-env call per file. Only that last value is passed on, so which file
wins does not depend on how the started process (or execve with
--same-process) handles duplicate keys.

All files are decrypted before the command starts, so if one of them
fails, the command does not run. With more than one file, an error about
one of the files starts with its name. Errors loading the config file
are not prefixed, since the same config applies to all files.

Calls with a single file give the same output, error messages and exit
codes as before. The only difference is the order of the checks: the
flags are now checked before the config file is loaded, so if both are
invalid, the error about the flags is reported.

Signed-off-by: abd-ulbasit <basit@basit.engineer>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant