From a0986aa94a27b803ea4a452c35f90329f725e2b8 Mon Sep 17 00:00:00 2001 From: gen740 <54583542+gen740@users.noreply.github.com> Date: Sun, 10 May 2026 23:19:28 +0900 Subject: [PATCH 1/5] docs: add env fallback reference page Co-Authored-By: Claude Sonnet 4.6 --- docs/docs/env-fallback.md | 62 +++++++++++++++++++++++++++++++++++++++ docs/sidebars.ts | 1 + 2 files changed, 63 insertions(+) create mode 100644 docs/docs/env-fallback.md diff --git a/docs/docs/env-fallback.md b/docs/docs/env-fallback.md new file mode 100644 index 0000000..7577048 --- /dev/null +++ b/docs/docs/env-fallback.md @@ -0,0 +1,62 @@ +--- +sidebar_position: 3 +title: Environment Variable Fallback +--- + +# Environment Variable Fallback + +Any option can fall back to an environment variable when it is absent from the command line. Set the `.env` field in the parameter bag: + +```cpp +struct Args { + cli::StringOption<"output", 'o'> output{{.env = "MYAPP_OUTPUT"}}; + cli::IntOption<"jobs", 'j'> jobs{{.env = "MYAPP_JOBS"}}; + cli::Flag<"verbose", 'v'> verbose{{.env = "MYAPP_VERBOSE"}}; +}; +``` + +If `--output` is not passed, the parser calls `getenv("MYAPP_OUTPUT")` and uses the result as the value. If the variable is also absent, the option remains unset. + +## Precedence + +Command-line values always take precedence. The env variable is only read when the option was not provided on the command line. + +| Provided on CLI | Env var set | Result | +|---|---|---| +| yes | any | CLI value is used | +| no | yes | env value is used | +| no | no | default value (or missing-required error) | + +## Type conversion and validation + +The env value passes through the same action pipeline as a CLI value. Conversion and validation actions apply normally: + +```cpp +cli::IntOption<"port"> port{{.env = "MYAPP_PORT"}}; +``` + +If `MYAPP_PORT=not_a_number`, the parser returns an `invalid_value` error exactly as it would for `--port not_a_number`. + +## Required options + +An env variable satisfies a `required` option: + +```cpp +cli::StringOption<"token"> token{{ + .presence = cli::required, + .env = "MYAPP_TOKEN", +}}; +``` + +If neither `--token` nor `MYAPP_TOKEN` is present, the parser returns a `missing_required` error. + +## Flags + +For flags (`nargs::none`), any non-empty env value triggers the action, equivalent to passing the flag on the command line: + +```cpp +cli::Flag<"verbose", 'v'> verbose{{.env = "MYAPP_VERBOSE"}}; +// MYAPP_VERBOSE=1 → verbose = true +``` + +The content of the variable is ignored; only its presence matters. diff --git a/docs/sidebars.ts b/docs/sidebars.ts index fa453bb..9bd1c0b 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -4,6 +4,7 @@ const sidebars: SidebarsConfig = { tutorialSidebar: [ 'intro', 'tutorial', + 'env-fallback', { type: 'category', label: 'Examples', From d52ee7aeed1dc6f946fefd88783f6531a6d0dbe6 Mon Sep 17 00:00:00 2001 From: gen740 <54583542+gen740@users.noreply.github.com> Date: Mon, 11 May 2026 00:26:48 +0900 Subject: [PATCH 2/5] feat: add env-fallback example with interactive env-var editor playground - examples/env_fallback.cc: threads (NUM_THREADS, range<1,32>), config (CONFIG_FILE, matches<*.toml>), verbose (VERBOSE flag) - ExamplePlayground: add initialEnv prop with editable env-var table; inject via ccall('setenv') before callMain - CSS: pg-env-panel styles; fix cursor blink to animate background only - exampleSetups: setupEnvFallbackFs creates /job.toml and /configs/prod.toml - docs/examples/env-fallback.mdx: example page with interactive playground - docs/examples/index.mdx: add env-fallback to TOC - parser.hh: prefix missing_required subject with '--' - test_parse.cc, test_sugar.cc: update subject assertions to '--option' Co-Authored-By: Claude Sonnet 4.6 --- TODO.md | 1 + docs/docs/examples/env-fallback.mdx | 60 ++++++++++ docs/docs/examples/index.mdx | 2 + docs/src/components/ExamplePlayground.tsx | 87 ++++++++++++++ docs/src/components/exampleSetups.ts | 8 ++ docs/src/css/custom.css | 136 ++++++++++++++++++++-- examples/env_fallback.cc | 48 ++++++++ include/cli/parser.hh | 2 +- tests/test_parse.cc | 6 +- tests/test_sugar.cc | 2 +- 10 files changed, 340 insertions(+), 12 deletions(-) create mode 100644 docs/docs/examples/env-fallback.mdx create mode 100644 examples/env_fallback.cc diff --git a/TODO.md b/TODO.md index 275c168..427db81 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,7 @@ - [ ] `validate()` — post-parse validation hook on the parsed struct - [ ] `visit_subcommand()` — visitor dispatch over matched subcommands - [ ] `range` / `choice` — built-in validation constraints +- [ ] pipeline `action` types — validate action types and show clear error messages - [ ] env fallback — populate unprovided options from environment variables - [ ] completion — shell completion script generation - [x] short cluster / attached value — `-xvf`, `-ofile` diff --git a/docs/docs/examples/env-fallback.mdx b/docs/docs/examples/env-fallback.mdx new file mode 100644 index 0000000..b074dce --- /dev/null +++ b/docs/docs/examples/env-fallback.mdx @@ -0,0 +1,60 @@ +--- +sidebar_position: 10 +title: 9. Env Fallback +--- + +import {ExampleSource} from '@site/src/components/ExampleDoc'; +import {ExamplePlayground} from '@site/src/components/ExamplePlayground'; +import {setupEnvFallbackFs} from '@site/src/components/exampleSetups'; + +# Env Fallback + +Options can be populated from environment variables when they are not provided +on the command line. The `.env` field on any option names the environment +variable to check. The same type conversion and validation pipeline runs +regardless of whether the value came from the CLI or the environment. + +## Source + + + +## What This Example Shows + +- **`--threads` / `NUM_THREADS`** — an integer option with `range<1, 32>` + validation. If `NUM_THREADS` is set in the environment, it is parsed and + validated exactly like a CLI argument. A value outside 1–32 is an error. +- **`--config` / `CONFIG_FILE`** — a string option with `matches<".*\\.toml">` + validation. The env value must end in `.toml` or parsing fails. +- **`--verbose` / `VERBOSE`** — a flag. When the env var is present (any + value), the flag is set to `true`. +- **CLI takes precedence** — if the option is supplied on the command line, + the environment variable is ignored. + +## Try It + +Edit the environment variables in the panel below, then run the program. + + + +Things to try: + +- **Env only** — `build-job` with `NUM_THREADS=4` and `CONFIG_FILE=job.toml` set +- **CLI overrides env** — `--threads 8 build-job` while `NUM_THREADS=4` is set +- **Validation via env** — set `NUM_THREADS=99` to see a range error +- **Bad config name** — set `CONFIG_FILE=settings.json` to see the regex fail +- **Verbose from env** — add `VERBOSE=1` and run `build-job` +- **Help** — `--help` + +## Related Examples + +- [Validation](/docs/examples/validation) +- [Simple API](/docs/examples/simple-api) diff --git a/docs/docs/examples/index.mdx b/docs/docs/examples/index.mdx index e035049..23283ce 100644 --- a/docs/docs/examples/index.mdx +++ b/docs/docs/examples/index.mdx @@ -28,6 +28,8 @@ Each one is small enough to copy into a project, but specific enough to answer Short-cluster flags, negatable key-value options, and `std::map` storage with `insert_or_assign`. 8. [Distribution and Platform Examples](./distribution-and-platform) Single-header usage, C++20 modules, and the Windows Unicode sample used by CI. +9. [Env Fallback](./env-fallback) + Populate options from environment variables with the same conversion and validation pipeline. ## Building diff --git a/docs/src/components/ExamplePlayground.tsx b/docs/src/components/ExamplePlayground.tsx index 3ffc16f..44c27e1 100644 --- a/docs/src/components/ExamplePlayground.tsx +++ b/docs/src/components/ExamplePlayground.tsx @@ -58,16 +58,20 @@ function SpinnerIcon() { } type HistoryEntry = {args: string; output: string; exitCode: number}; +type EnvRow = {id: number; key: string; value: string}; export function ExamplePlayground({ name, defaultArgs = '--help', setupFs, + initialEnv, }: { name: string; defaultArgs?: string; /** Optional callback to populate the Emscripten virtual FS before each run. */ setupFs?: (fs: any) => void; + /** If provided, show an env-var editor panel above the terminal. */ + initialEnv?: Record; }) { const wasmUrl = useBaseUrl(`/wasm/${name}.js`); const factoryRef = useRef Promise> | null>(null); @@ -85,6 +89,37 @@ export function ExamplePlayground({ // Track cursor position so the block cursor renders at the right place. const [cursorPos, setCursorPos] = useState(0); + // Env editor — active only when initialEnv prop is provided. + const showEnvEditor = initialEnv !== undefined; + const [envRows, setEnvRows] = useState(() => + initialEnv + ? Object.entries(initialEnv).map(([key, value], i) => ({id: i, key, value})) + : [], + ); + const nextIdRef = useRef(initialEnv ? Object.keys(initialEnv).length : 0); + + const addEnvRow = () => { + setEnvRows((rows) => [ + ...rows, + {id: nextIdRef.current++, key: '', value: ''}, + ]); + }; + const removeEnvRow = (id: number) => { + setEnvRows((rows) => rows.filter((r) => r.id !== id)); + }; + const updateEnvRow = (id: number, field: 'key' | 'value', val: string) => { + setEnvRows((rows) => + rows.map((r) => (r.id === id ? {...r, [field]: val} : r)), + ); + }; + const buildEnv = (): Record => { + const env: Record = {}; + for (const row of envRows) { + if (row.key.trim()) env[row.key.trim()] = row.value; + } + return env; + }; + const run = async (overrideArgs?: string) => { if (runningRef.current) return; runningRef.current = true; @@ -92,6 +127,7 @@ export function ExamplePlayground({ setLoadError(null); const usedArgs = overrideArgs ?? args; + const usedEnv = buildEnv(); let out = ''; let exitCode = 0; @@ -114,6 +150,13 @@ export function ExamplePlayground({ noExitRuntime: false, }); + // Inject env vars via setenv (requires _setenv + ccall in EXPORTED_FUNCTIONS/METHODS). + if (mod.ccall) { + for (const [k, v] of Object.entries(usedEnv)) { + mod.ccall('setenv', 'number', ['string', 'string', 'number'], [k, v, 1]); + } + } + // Populate the virtual FS after the module is fully initialised. try { if (setupFs) setupFs(mod.FS); @@ -240,6 +283,50 @@ export function ExamplePlayground({ return (
+ {showEnvEditor && ( +
+
+ Environment + +
+ {envRows.length === 0 ? ( +
No environment variables — click + Add to set one
+ ) : ( +
+ {envRows.map((row) => ( +
+ updateEnvRow(row.id, 'key', e.target.value)} + spellCheck={false} + /> + = + + updateEnvRow(row.id, 'value', e.target.value) + } + spellCheck={false} + /> + +
+ ))} +
+ )} +
+ )}
inputRef.current?.focus()}> diff --git a/docs/src/components/exampleSetups.ts b/docs/src/components/exampleSetups.ts index bcad5e6..94e6ae6 100644 --- a/docs/src/components/exampleSetups.ts +++ b/docs/src/components/exampleSetups.ts @@ -16,3 +16,11 @@ export function setupValidationFs(fs: any) { // --output requires parent_exists. /tmp gives users a writable parent to try. fs.mkdir('/tmp'); } + +export function setupEnvFallbackFs(fs: any) { + // --config expects a *.toml path. Pre-create one so users can try it directly. + fs.writeFile('/job.toml', ''); + // /configs/ lets users try paths like configs/prod.toml (parent_exists variant). + fs.mkdir('/configs'); + fs.writeFile('/configs/prod.toml', ''); +} diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 2b341ab..28a2957 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -146,24 +146,23 @@ .pg-cursor { display: inline-block; min-width: 0.55em; - background: #e6edf3; - color: #0d1117; - opacity: 0; + background: transparent; + color: #e6edf3; line-height: 1; } .pg-cursor-blink { - opacity: 1; animation: pg-blink 1.1s step-end infinite; } .pg-cursor-idle { - opacity: 1; + background: #e6edf3; + color: #0d1117; } @keyframes pg-blink { - 0%, 100% { opacity: 1; } - 50% { opacity: 0; } + 0%, 100% { background: #e6edf3; color: #0d1117; } + 50% { background: transparent; color: #e6edf3; } } /* transparent input overlay — keyboard capture only */ @@ -199,3 +198,126 @@ color: rgba(255, 255, 255, 0.5); flex-shrink: 0; } + +/* ── Environment variable editor panel ──────────────────────────── */ + +.pg-wrap { + display: flex; + flex-direction: column; +} + +.pg-env-panel { + background: #161b22; + border: 1px solid #30363d; + border-bottom: none; + border-radius: 10px 10px 0 0; + padding: 0.55rem 0.75rem; + font-family: var(--ifm-font-family-monospace); + font-size: 0.8rem; + color: #8b949e; +} + +/* When env panel is present, flatten the terminal's top corners. */ +.pg-env-panel + .pg-terminal { + border-radius: 0 0 10px 10px; + margin-top: 0; +} + +.pg-env-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.45rem; +} + +.pg-env-title { + color: #8b949e; + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.07em; +} + +.pg-env-add { + background: transparent; + border: 1px solid #30363d; + border-radius: 4px; + color: #8b949e; + cursor: pointer; + font-family: var(--ifm-font-family-monospace); + font-size: 0.75rem; + line-height: 1; + padding: 0.15rem 0.45rem; + transition: border-color 0.15s, color 0.15s; +} + +.pg-env-add:hover { + border-color: #8b949e; + color: #e6edf3; +} + +.pg-env-empty { + color: #484f58; + font-size: 0.75rem; +} + +.pg-env-rows { + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +.pg-env-row { + display: flex; + align-items: center; + gap: 0.3rem; +} + +.pg-env-input { + background: #0d1117; + border: 1px solid #30363d; + border-radius: 4px; + color: #e6edf3; + font-family: var(--ifm-font-family-monospace); + font-size: 0.8rem; + padding: 0.15rem 0.4rem; + outline: none; + transition: border-color 0.15s; +} + +.pg-env-input:focus { + border-color: #6366f1; +} + +.pg-env-key { + width: 10rem; + color: #ffa657; +} + +.pg-env-key::placeholder { color: #484f58; } + +.pg-env-val { + flex: 1; +} + +.pg-env-val::placeholder { color: #484f58; } + +.pg-env-eq { + color: #484f58; + user-select: none; +} + +.pg-env-remove { + background: transparent; + border: none; + color: #484f58; + cursor: pointer; + font-size: 1.1rem; + line-height: 1; + padding: 0 0.2rem; + transition: color 0.15s; +} + +.pg-env-remove:hover { + color: #f85149; +} + diff --git a/examples/env_fallback.cc b/examples/env_fallback.cc new file mode 100644 index 0000000..7a45817 --- /dev/null +++ b/examples/env_fallback.cc @@ -0,0 +1,48 @@ +#include + +#include "cli/argument.hh" +#include "cli/parser.hh" + +struct Args { + cli::Description description{ + "Shows env-var fallback with validation: options can be set via CLI " + "flags or environment variables, and the same validation applies to " + "both."}; + cli::Help<> help; + + cli::Arg<"threads", 't', + cli::conversion::integer | cli::validation::range<1, 32> | + cli::pack::set_once> + threads{{.help = "Worker threads 1-32 (env: NUM_THREADS)", + .presence = cli::required, + .env = "NUM_THREADS"}}; + + cli::Arg<"config", 'c', + cli::conversion::string | cli::validation::matches<".*\\.toml"> | + cli::pack::set_once> + config{{.help = "Config file *.toml (env: CONFIG_FILE)", + .presence = cli::optional, + .env = "CONFIG_FILE"}}; + + cli::Flag<"verbose", 'v'> verbose{ + {.help = "Verbose output (env: VERBOSE)", + .presence = cli::optional, + .env = "VERBOSE"}}; + + cli::Positional job{ + {.help = "Job name to run", .presence = cli::required}}; +}; + +auto main(int argc, char* argv[]) -> int { + const auto args = cli::parse_or_exit(argc, argv); + + if (args.verbose.value()) { + std::cout << "verbose: true\n"; + } + std::cout << "threads: " << *args.threads.value() << '\n'; + if (const auto& cfg = args.config.value()) { + std::cout << "config: " << *cfg << '\n'; + } + std::cout << "job: " << *args.job.value() << '\n'; + return 0; +} diff --git a/include/cli/parser.hh b/include/cli/parser.hh index e0d5c61..27d6ddc 100644 --- a/include/cli/parser.hh +++ b/include/cli/parser.hh @@ -913,7 +913,7 @@ struct Parser { if (f.presence == Presence::required && !f.provided()) { std::string subject; if constexpr (std::derived_from) { - subject = std::string(F::name.view()); + subject = "--" + std::string(F::name.view()); } else { subject = ""; } diff --git a/tests/test_parse.cc b/tests/test_parse.cc index 68fcd8a..186b095 100644 --- a/tests/test_parse.cc +++ b/tests/test_parse.cc @@ -68,7 +68,7 @@ TEST(Parse, MissingRequiredOptionFails) { ASSERT_TRUE(result.has_error()); EXPECT_EQ(result.error.code, cli::ErrorCode::missing_required); - EXPECT_EQ(result.error.subject, "count"); + EXPECT_EQ(result.error.subject, "--count"); } TEST(Parse, DuplicateSetOnceFails) { @@ -102,7 +102,7 @@ TEST(Parse, MissingRequiredSubcommandOptionFailsInsideCommand) { ASSERT_TRUE(result.has_error()); EXPECT_EQ(result.error.code, cli::ErrorCode::missing_required); - EXPECT_EQ(result.error.subject, "threads"); + EXPECT_EQ(result.error.subject, "--threads"); } #ifdef _WIN32 @@ -207,7 +207,7 @@ TEST(ParseWide, MissingRequiredOptionFails) { ASSERT_TRUE(result.has_error()); EXPECT_EQ(result.error.code, cli::ErrorCode::missing_required); - EXPECT_EQ(result.error.subject, "name"); + EXPECT_EQ(result.error.subject, "--name"); } // Free-function parse() overload for wchar_t*. diff --git a/tests/test_sugar.cc b/tests/test_sugar.cc index 230b2de..4093e02 100644 --- a/tests/test_sugar.cc +++ b/tests/test_sugar.cc @@ -104,5 +104,5 @@ TEST(Sugar, RequiredAliasStillEnforcesPresence) { ASSERT_TRUE(result.has_error()); EXPECT_EQ(result.error.code, cli::ErrorCode::missing_required); - EXPECT_EQ(result.error.subject, "port"); + EXPECT_EQ(result.error.subject, "--port"); } From d53404337738f923f2d51da4b3b771398d4f2d16 Mon Sep 17 00:00:00 2001 From: gen740 <54583542+gen740@users.noreply.github.com> Date: Mon, 11 May 2026 00:29:26 +0900 Subject: [PATCH 3/5] chore: apply nix fmt Co-Authored-By: Claude Sonnet 4.6 --- examples/env_fallback.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/env_fallback.cc b/examples/env_fallback.cc index 7a45817..eca9fb2 100644 --- a/examples/env_fallback.cc +++ b/examples/env_fallback.cc @@ -24,10 +24,9 @@ struct Args { .presence = cli::optional, .env = "CONFIG_FILE"}}; - cli::Flag<"verbose", 'v'> verbose{ - {.help = "Verbose output (env: VERBOSE)", - .presence = cli::optional, - .env = "VERBOSE"}}; + cli::Flag<"verbose", 'v'> verbose{{.help = "Verbose output (env: VERBOSE)", + .presence = cli::optional, + .env = "VERBOSE"}}; cli::Positional job{ {.help = "Job name to run", .presence = cli::required}}; From fef3a527318bc895b86710dcc67102d10b8bf5ea Mon Sep 17 00:00:00 2001 From: gen740 <54583542+gen740@users.noreply.github.com> Date: Mon, 11 May 2026 00:35:16 +0900 Subject: [PATCH 4/5] docs: remove standalone env-fallback.md (superseded by examples page) Co-Authored-By: Claude Sonnet 4.6 --- docs/docs/env-fallback.md | 62 --------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 docs/docs/env-fallback.md diff --git a/docs/docs/env-fallback.md b/docs/docs/env-fallback.md deleted file mode 100644 index 7577048..0000000 --- a/docs/docs/env-fallback.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -sidebar_position: 3 -title: Environment Variable Fallback ---- - -# Environment Variable Fallback - -Any option can fall back to an environment variable when it is absent from the command line. Set the `.env` field in the parameter bag: - -```cpp -struct Args { - cli::StringOption<"output", 'o'> output{{.env = "MYAPP_OUTPUT"}}; - cli::IntOption<"jobs", 'j'> jobs{{.env = "MYAPP_JOBS"}}; - cli::Flag<"verbose", 'v'> verbose{{.env = "MYAPP_VERBOSE"}}; -}; -``` - -If `--output` is not passed, the parser calls `getenv("MYAPP_OUTPUT")` and uses the result as the value. If the variable is also absent, the option remains unset. - -## Precedence - -Command-line values always take precedence. The env variable is only read when the option was not provided on the command line. - -| Provided on CLI | Env var set | Result | -|---|---|---| -| yes | any | CLI value is used | -| no | yes | env value is used | -| no | no | default value (or missing-required error) | - -## Type conversion and validation - -The env value passes through the same action pipeline as a CLI value. Conversion and validation actions apply normally: - -```cpp -cli::IntOption<"port"> port{{.env = "MYAPP_PORT"}}; -``` - -If `MYAPP_PORT=not_a_number`, the parser returns an `invalid_value` error exactly as it would for `--port not_a_number`. - -## Required options - -An env variable satisfies a `required` option: - -```cpp -cli::StringOption<"token"> token{{ - .presence = cli::required, - .env = "MYAPP_TOKEN", -}}; -``` - -If neither `--token` nor `MYAPP_TOKEN` is present, the parser returns a `missing_required` error. - -## Flags - -For flags (`nargs::none`), any non-empty env value triggers the action, equivalent to passing the flag on the command line: - -```cpp -cli::Flag<"verbose", 'v'> verbose{{.env = "MYAPP_VERBOSE"}}; -// MYAPP_VERBOSE=1 → verbose = true -``` - -The content of the variable is ignored; only its presence matters. From 3e0e23edb1a6ff4a89dbc48310b5123a972e78a1 Mon Sep 17 00:00:00 2001 From: gen740 <54583542+gen740@users.noreply.github.com> Date: Mon, 11 May 2026 00:39:40 +0900 Subject: [PATCH 5/5] docs: fix sidebar + use dev/test/staging/prod.toml as playground config files Co-Authored-By: Claude Sonnet 4.6 --- docs/docs/examples/env-fallback.mdx | 16 +++++++++++++--- docs/sidebars.ts | 1 - docs/src/components/exampleSetups.ts | 10 +++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/docs/examples/env-fallback.mdx b/docs/docs/examples/env-fallback.mdx index b074dce..182f06b 100644 --- a/docs/docs/examples/env-fallback.mdx +++ b/docs/docs/examples/env-fallback.mdx @@ -38,17 +38,27 @@ regardless of whether the value came from the CLI or the environment. Edit the environment variables in the panel below, then run the program. +The virtual filesystem for this playground contains the following config files +you can reference as `CONFIG_FILE`: + +| Path | Notes | +|---|---| +| `dev.toml` | local development | +| `test.toml` | test environment | +| `staging.toml` | staging environment | +| `prod.toml` | production | + Things to try: -- **Env only** — `build-job` with `NUM_THREADS=4` and `CONFIG_FILE=job.toml` set -- **CLI overrides env** — `--threads 8 build-job` while `NUM_THREADS=4` is set +- **Switch environment** — change `CONFIG_FILE` to `staging.toml` or `prod.toml` +- **CLI overrides env** — `--config test.toml build-job` while `CONFIG_FILE` is set - **Validation via env** — set `NUM_THREADS=99` to see a range error - **Bad config name** — set `CONFIG_FILE=settings.json` to see the regex fail - **Verbose from env** — add `VERBOSE=1` and run `build-job` diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 9bd1c0b..fa453bb 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -4,7 +4,6 @@ const sidebars: SidebarsConfig = { tutorialSidebar: [ 'intro', 'tutorial', - 'env-fallback', { type: 'category', label: 'Examples', diff --git a/docs/src/components/exampleSetups.ts b/docs/src/components/exampleSetups.ts index 94e6ae6..82eef53 100644 --- a/docs/src/components/exampleSetups.ts +++ b/docs/src/components/exampleSetups.ts @@ -18,9 +18,9 @@ export function setupValidationFs(fs: any) { } export function setupEnvFallbackFs(fs: any) { - // --config expects a *.toml path. Pre-create one so users can try it directly. - fs.writeFile('/job.toml', ''); - // /configs/ lets users try paths like configs/prod.toml (parent_exists variant). - fs.mkdir('/configs'); - fs.writeFile('/configs/prod.toml', ''); + // Per-environment config files — try CONFIG_FILE=prod.toml etc. + fs.writeFile('/dev.toml', ''); + fs.writeFile('/test.toml', ''); + fs.writeFile('/staging.toml', ''); + fs.writeFile('/prod.toml', ''); }