Skip to content
This repository was archived by the owner on Dec 27, 2025. It is now read-only.

Commit 416b420

Browse files
committed
feat: add automatic .env file loading to run and list commands
Users can now use environment variables in scenario files without manual setup. By default, .env files are loaded before scenario execution, following common practices in testing and development workflows. - Add loadEnvironment() utility for .env file loading via @std/dotenv - Load .env automatically before config file reading in run/list commands - Add --env <file> option to specify custom .env file path - Add --no-env option to skip automatic .env loading - Update usage documentation with environment variable examples Environment variables loaded in main thread are inherited by Worker threads, making them available to all scenario executions.
1 parent b380f71 commit 416b420

7 files changed

Lines changed: 143 additions & 19 deletions

File tree

assets/usage-list.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Options:
1414
Supports ! prefix for negation
1515
--json Output in JSON format
1616
--config <path> Path to config file (deno.json/deno.jsonc)
17+
--env <file> Load environment variables from file (default: .env)
18+
--no-env Skip loading .env file
1719
-r, --reload Reload dependencies before running
1820

1921
Selector Format:
@@ -66,6 +68,12 @@ Examples:
6668
probitas list --json Output as JSON
6769
probitas list --reload Reload dependencies before listing
6870

71+
# Environment variables
72+
probitas list Load .env file automatically
73+
probitas list --env .env.test Load custom env file
74+
probitas list --no-env Skip loading .env file
75+
6976
Note: Uses patterns from config file or defaults to "**/*.probitas.ts"
77+
.env file is automatically loaded before scenario listing (use --no-env to skip)
7078

7179
Documentation: https://jsr-probitas.github.io/documents/

assets/usage-run.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Options:
2222
--max-failures <n> Stop after N failures
2323
-f, --fail-fast Stop on first failure (alias for --max-failures=1)
2424
--config <path> Path to config file (deno.json/deno.jsonc)
25+
--env <file> Load environment variables from file (default: .env)
26+
--no-env Skip loading .env file
2527
-r, --reload Reload dependencies before running
2628
-q, --quiet Quiet output (errors only)
2729
-v, --verbose Verbose output
@@ -96,7 +98,13 @@ Examples:
9698
probitas run -d Debug output
9799
probitas run --debug Debug output (same as -d)
98100

101+
# Environment variables
102+
probitas run Load .env file automatically
103+
probitas run --env .env.test Load custom env file
104+
probitas run --no-env Skip loading .env file
105+
99106
Environment Variables:
100107
NO_COLOR Disable colored output
108+
.env File: Automatically loaded before scenario execution (use --no-env to skip)
101109

102110
Documentation: https://jsr-probitas.github.io/documents/

deno.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@std/assert": "jsr:@std/assert@^1.0.16",
4444
"@std/cli": "jsr:@std/cli@^1.0.24",
4545
"@std/collections": "jsr:@std/collections@^1.1.3",
46+
"@std/dotenv": "jsr:@std/dotenv@^0.225.4",
4647
"@std/fs": "jsr:@std/fs@^1.0.20",
4748
"@std/jsonc": "jsr:@std/jsonc@^1.0.2",
4849
"@std/path": "jsr:@std/path@^1.1.3",

deno.lock

Lines changed: 29 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/list.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { applySelectors } from "@probitas/core/selector";
1414
import { EXIT_CODE } from "../constants.ts";
1515
import { findProbitasConfigFile, loadConfig } from "../config.ts";
1616
import { createDiscoveryProgress, writeStatus } from "../progress.ts";
17-
import { readAsset } from "../utils.ts";
17+
import { loadEnvironment, readAsset } from "../utils.ts";
1818

1919
const logger = getLogger("probitas", "cli", "list");
2020

@@ -34,8 +34,16 @@ export async function listCommand(
3434
try {
3535
// Parse command-line arguments
3636
const parsed = parseArgs(args, {
37-
string: ["config", "include", "exclude", "selector"],
38-
boolean: ["help", "json", "reload", "quiet", "verbose", "debug"],
37+
string: ["config", "include", "exclude", "selector", "env"],
38+
boolean: [
39+
"help",
40+
"json",
41+
"no-env",
42+
"reload",
43+
"quiet",
44+
"verbose",
45+
"debug",
46+
],
3947
collect: ["include", "exclude", "selector"],
4048
alias: {
4149
h: "help",
@@ -85,6 +93,13 @@ export async function listCommand(
8593
// Silently ignore logging configuration errors (e.g., in test environments)
8694
}
8795

96+
// Load environment variables before loading configuration
97+
// This allows config files to reference environment variables
98+
await loadEnvironment(cwd, {
99+
noEnv: parsed["no-env"],
100+
envFile: parsed.env,
101+
});
102+
88103
// Load configuration
89104
const configPath = parsed.config ??
90105
await findProbitasConfigFile(cwd, { parentLookup: true });

src/commands/run.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
toScenarioMetadata,
2222
} from "@probitas/runner";
2323
import {
24+
loadEnvironment,
2425
parsePositiveInteger,
2526
parseTimeout,
2627
readAsset,
@@ -56,11 +57,13 @@ export async function runCommand(
5657
"exclude",
5758
"selector",
5859
"timeout",
60+
"env",
5961
],
6062
boolean: [
6163
"help",
6264
"no-color",
6365
"no-timeout",
66+
"no-env",
6467
"reload",
6568
"quiet",
6669
"verbose",
@@ -119,6 +122,13 @@ export async function runCommand(
119122
// Silently ignore logging configuration errors (e.g., in test environments)
120123
}
121124

125+
// Load environment variables before loading configuration
126+
// This allows config files to reference environment variables
127+
await loadEnvironment(cwd, {
128+
noEnv: parsed["no-env"],
129+
envFile: parsed.env,
130+
});
131+
122132
// Load configuration
123133
const configPath = parsed.config ??
124134
await findProbitasConfigFile(cwd, { parentLookup: true });

src/utils.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import { getLogger } from "@probitas/logger";
99
import { JSONReporter, ListReporter } from "@probitas/reporter";
1010
import type { Reporter } from "@probitas/runner";
1111
import type { ReporterOptions } from "@probitas/reporter";
12+
import { load } from "@std/dotenv";
13+
import { exists } from "@std/fs";
14+
import { resolve } from "@std/path";
1215

1316
const logger = getLogger("probitas", "cli", "utils");
1417

@@ -255,3 +258,69 @@ export async function getVersionInfo(): Promise<VersionInfo | undefined> {
255258
return undefined;
256259
}
257260
}
261+
262+
/**
263+
* Load environment variables from a .env file
264+
*
265+
* @param cwd - Current working directory
266+
* @param options - Environment loading options
267+
* @returns void
268+
*
269+
* @example
270+
* ```ts
271+
* import { loadEnvironment } from "./utils.ts";
272+
*
273+
* const cwd = Deno.cwd();
274+
*
275+
* // Load default .env file
276+
* await loadEnvironment(cwd);
277+
*
278+
* // Skip loading .env
279+
* await loadEnvironment(cwd, { noEnv: true });
280+
*
281+
* // Load custom .env file
282+
* await loadEnvironment(cwd, { envFile: ".env.test" });
283+
* ```
284+
*/
285+
export async function loadEnvironment(
286+
cwd: string,
287+
options?: {
288+
/** Skip loading .env file */
289+
noEnv?: boolean;
290+
/** Custom .env file path (relative to cwd or absolute) */
291+
envFile?: string;
292+
},
293+
): Promise<void> {
294+
const { noEnv = false, envFile } = options ?? {};
295+
296+
// Skip if --no-env is specified
297+
if (noEnv) {
298+
logger.debug("Environment loading disabled via --no-env");
299+
return;
300+
}
301+
302+
// Determine which file to load
303+
const targetFile = envFile ?? ".env";
304+
const targetPath = resolve(cwd, targetFile);
305+
306+
// Check if file exists
307+
if (!(await exists(targetPath))) {
308+
logger.debug("Environment file not found", { path: targetPath });
309+
return;
310+
}
311+
312+
// Load environment variables
313+
try {
314+
const env = await load({ envPath: targetPath, export: true });
315+
logger.debug("Environment loaded", {
316+
path: targetPath,
317+
keys: Object.keys(env),
318+
});
319+
} catch (err: unknown) {
320+
// Log error but don't fail - missing .env is acceptable
321+
logger.debug("Failed to load environment file", {
322+
path: targetPath,
323+
error: err,
324+
});
325+
}
326+
}

0 commit comments

Comments
 (0)