Repository navigation
feat(cli): print a profile's scope as dotenv lines with context --format env - #129
Merged
Conversation
…mat env Other Voltage tools, such as the voltage-api skill's helper, take scope only from VOLTAGE_* variables or a dotenv file, so handing them a profile's scope meant copying IDs by hand or parsing `context --json`. `voltage context --format env` prints the effective organization, environment, and wallet under those names. It leaves out unset values, never prints a credential, refuses more than one environment with exit code 2, and refuses --json, --output, and --output-file before or after the command.
The hint after `profiles create` repeats --config-dir, which the staging and local wrappers refuse. Passing the directory as a variable keeps the hint runnable through the wrappers.
# Conflicts: # CHANGELOG.md
…ariables-under-a' into brandon/dev-2957-voltage-cli-no-way-to-hand-a-profiles-scope-to-another-tool # Conflicts: # CHANGELOG.md # src/context.rs
thebrandonlucas
changed the base branch from
master
to
brandon/dev-2956-voltage-cli-commands-ignore-voltage_-variables-under-a
November 4, 2026 18:33
This was referenced Nov 4, 2026
tee8z
approved these changes
Nov 4, 2026
tee8z
left a comment
Contributor
There was a problem hiding this comment.
Approved at a64f074. ContextFlags::check on the typed flags catches --json, -o, and --output-file before or after context, which a clap conflicts_with would miss. Context::dotenv builds from the resolved context, leaves out unset values, refuses two environments, and prints no credential. None of the output flags read an environment variable, so the check cannot refuse a run that had no flag.
CRAP on this head: 860 of 1073 functions measured, 0 violations. Highest: 18.10 wait_for_payment (unchanged). CI is green.
thebrandonlucas
deleted the
brandon/dev-2957-voltage-cli-no-way-to-hand-a-profiles-scope-to-another-tool
branch
November 4, 2026 22:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes DEV-2957
Closes #122
Problem
Profiles hold the CLI's scope, but other Voltage tools cannot read them. The
voltage-apiskill's helper takes scope only fromVOLTAGE_*variables or a dotenv file. After switching profiles, a person or agent had to copy the IDs by hand or turnvoltage context --jsoninto variables withjqand knowledge of its shape. A forgotten copy left the helper on the old environment.Behavior
voltage context --format envprints the effective organization, environment, and wallet as dotenv lines, under the names the helper reads (CONFIG_KEYSin itsscripts/request.py):--env)error: --format env takes one environment, but 2 are selected, hintSelect one with --env ENV_ID., exit 2, nothing on stdout.--json,--output, or--output-file, before or aftercontexterror: --format cannot be combined with --json(or the flag given), with a hint to redirect stdout, exit 2.contextreads no saved secret, and neither does this format. The docs say the other tool needs a key from elsewhere, for the printed organization and environment.Values resolve exactly as
voltage contextresolves them: flags, then the profile, then theVOLTAGE_*variables.Design notes
LocalCommand::ContextbecomesContext(ContextFlags)with--format <FORMAT>, aContextFormatvalue enum with one value,env. An enum leaves room for another format without a new flag.Context::dotenvbuilds the lines from the resolvedContext, so the format cannot drift fromvoltage context.startup::executewrites them withwrite_stdoutinstead of a result envelope.--json,--output, and--output-fileis checked on the typed flags (ContextFlags::check, called fromInvocation::from_matches). A clapconflicts_withon the subcommand flag does not see a global flag given beforecontext, sovoltage --json context --format envwould have passed.scripts/voltage-stagingandscripts/voltage-localpass their state directory asVOLTAGE_CONFIG_DIR, not--config-dir(e43d367). Hints the CLI prints then do not name a flag the wrapper refuses. fix(cli): warn when a profile or flag overrides a set VOLTAGE_* variable #128 makes the same change, so whichever PR merges second merges cleanly.voltage contextgains a--format env > scope.envexample; the help-examples test parses it.Try it on staging
Setup: Try a pull request on staging
Find an organization with
voltage organizations list --account meand an environment withvoltage environments list --account me --org ORG_ID:Expect: exactly two lines,
VOLTAGE_ORGANIZATION_ID=ORG_IDandVOLTAGE_ENVIRONMENT_ID=ENV_ID, and nothing that looks like a token or key. If.envsetsVOLTAGE_ORGANIZATION_IDorVOLTAGE_ENVIRONMENT_ID, the values still come from the profile.Find a wallet with
voltage wallets list --profile try-it:Expect:
VOLTAGE_WALLET_ID=WALLET_ID.--format env takes one environment, but 2 are selected, thenexit 2.--format cannot be combined with --json, thenexit 2.--format env > scope.envexample.Validation
Run on the head after merging
master(445e6b5):cargo fmt --check,cargo clippy --locked --all-targets -- -D warnings,cargo machete --with-metadata,cargo test --locked(200 unit, 150 integration), andpython3 scripts/check-coverage.pyall pass.context_env_format_prints_only_set_scope_and_never_a_credential: exact stdout for a profile plus--wallet, no saved or ambient key in it, only the organization when only--orgis set, empty output when nothing is set, and exit 2 with empty stdout for two environments.context_format_refuses_a_result_format_or_file_before_or_after_the_command:--jsonbefore and aftercontext,-o table, and--output-fileare each refused with the flag named;context --jsonalone still parses;--format shellis rejected by clap. The parse test also checks--format envparses toContextFormat::Env.scripts/crap-report.sh, cargo-llvm-cov 0.8.5, knots 1.16.0): 848 of 1060 functions measured, 0 violations. The highest score is 18.10,wait_for_paymentinsrc/api.rs, which this PR does not change. Changed functions:startup::execute16.0 (was 14.0, at 100% coverage),context::inspect13.01,Invocation::from_matches7.01,ContextFlags::check5.0,Context::dotenv2.0.--format envprinted the organization and environment lines, and a third wallet line with--wallet. Two--envvalues and--jsoneach exited 2 with the documented message. A login with no scope printed nothing.Limits
> scope.env), an error is reported as JSON on stderr, like every other command whose stdout is not a terminal. It is still exit 2 with nothing written to stdout.--format envunder a profile also gets the stderr warning for each ignoredVOLTAGE_*variable, which is the case where handing the scope over matters most.Checklist
docs/andCHANGELOG.md(Unreleased) are updated. (docs/guides/login-and-profiles.md,docs/guides/scripting.md)docs/development.mdpasses.voltage-pr-reviewskill, and every new error and early-return path has a test.wait_for_payment, unchanged); no function at or above 20.profiles create try-itsteps were not run, to avoid changing the shared staging config. The same checks ran with an existing profile.Stack
Stack #135, layer 4 of 9 (bottom targets
master):--allonorganizations,environments, andprofiles listVOLTAGE_*variablevoltage context --format envvoltage feedbackand panicsvoltage environments createflake.nixwith a KaifileThe layers are chained with merge commits, because history is not rewritten. Use squash and merge, bottom first.