Skip to content

feat: #5 add .env config file support#62

Merged
TheRealAgentK merged 13 commits into
mainfrom
issue-5-dotenv-config
May 8, 2026
Merged

feat: #5 add .env config file support#62
TheRealAgentK merged 13 commits into
mainfrom
issue-5-dotenv-config

Conversation

@TheRealAgentK
Copy link
Copy Markdown
Contributor

#5: add .env config file support

Closes #5.

Description 📝

  • Purpose: Implements the long-standing request from issue Support config file for arguments like the app-id #5: let users configure raygun-cli via a project-root config file instead of having to pass every credential as a CLI argument or environment variable on every invocation. This is a follow-up to perf: #5 refactor config props #15, which restructured ConfigProp so each property is loaded independently — a prerequisite for adding a third source.
  • Approach: Adds a third resolution tier to ConfigProp.load(). Lookup order (highest priority first): CLI argument → environment variable → .env file. The .env is discovered automatically (CWD, then walking up parent directories to $HOME) or explicitly via a new --config-file=<path> global flag. Parsing uses the dotenv package so we get correct handling of quoted values, # comments, export KEY=value syntax, and other .env conventions out of the box.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Updates

👉 New lib/src/config_file.dart: ConfigFile class with discovery (walks parent dirs, stops at $HOME), explicit-path support, dotenv parsing, and a singleton + setInstance/resetForTest test seam mirroring Environment.
👉 lib/src/config_props.dart: extended load() to fall through to the .env file when arg + env are both absent. Updated error message to mention all three sources.
👉 bin/raygun_cli.dart: added --config-file=<path> global flag and initializes ConfigFile before any subcommand runs.
👉 New dependencies: dotenv: ^4.2.0 and path: ^1.9.0 (the latter was already transitive — promoted to direct since we use it directly).
👉 README + AGENT.md updated, sample example/.env.example committed.
👉 Opportunistic test debt cleanup (separate commits): added previously missing tests for Environment (singleton, [] operator) and RaygunMultipartRequestBuilder / RaygunPostRequestBuilder (the shared HTTP request builders used by every command).

Test plan 🧪

The PR ships with +68 net new tests (58 → 126 total). All green locally and in CI:

dart format .   # clean
dart analyze    # no issues
dart test       # 126/126 pass

Test coverage breakdown for the new feature:

Test file Tests Layer
test/config_file_test.dart 21 Unit (discovery, parsing, singleton)
test/config_props_test.dart 13 (was 4) Integration (precedence chain across all 3 sources)
test/e2e_config_file_test.dart 9 E2E — spawns real CLI subprocess
test/environment_test.dart 11 Infrastructure (was 0)
test/core/raygun_api_test.dart 18 Infrastructure (was 0)

Manual smoke test (with verbose output to confirm values flow through):

mkdir -p /tmp/raygun-cli-smoke
cat > /tmp/raygun-cli-smoke/.env <<EOF
RAYGUN_TOKEN=smoke-token-xyz
RAYGUN_API_KEY=smoke-api-key-abc
EOF
cd /tmp/raygun-cli-smoke
dart run /path/to/bin/raygun_cli.dart -v deployments --version=1.2.3
# Expect to see:
#   Loaded config from /tmp/raygun-cli-smoke/.env
#   token: smoke-token-xyz
#   api-key: smoke-api-key-abc
#   ...
#   Error creating deployment: 401  (expected — fake creds)

Reviewers can also try:

  • --config-file=<path> to point at an explicit file
  • Mixing arg + env + file to verify precedence
  • Running from a nested directory to verify upward discovery
  • Removing all sources to verify exit code 2 with the new error message

Backward compatibility

This is an additive change:

  • Missing .env is a silent no-op — existing scripts using args or env vars are unaffected.
  • All pre-existing tests still pass without modification.
  • No existing CLI flags or env vars changed semantics.

Author to check 👓

  • Project and all contained modules builds successfully
  • Self-/dev-tested
  • Unit/UI/Automation/Integration tests provided where applicable
  • Code is written to standards
  • Appropriate documentation written (code comments, internal docs)

Reviewer to check ✔️

  • Project and all contained modules builds successfully
  • Change has been dev-/reviewer-tested, where possible
  • Unit/UI/Automation/Integration tests provided where applicable
  • Code is written to standards
  • Appropriate documentation written (code comments, internal docs)

Comment thread lib/src/config_file.dart
Comment thread lib/src/config_file.dart Outdated
Comment thread README.md
Comment thread lib/src/config_props.dart Outdated
…se source attribution

Addresses PR #62 review comment from @velocitysystems: a user who copies example/.env.example, forgets to fill in a value (or types only whitespace), would get an opaque HTTP 401/404 from Raygun instead of the friendly 'Missing ...' error. The empty-value passthrough was actually codified as the expected behavior in two tests, with a misleading comment.

Changes:

- ConfigProp.load now treats empty ('') and whitespace-only ('   ', '\t') values at any tier (CLI arg, env var, .env file) as missing and falls through to the next source. Implemented via a single _isPresent(value) => value != null && value.trim().isNotEmpty helper.

- ConfigProp.load gains a verbose: parameter. When -v/--verbose is passed, each property prints '[VERBOSE] Resolved <name> from <source>' for the winning tier and '[VERBOSE] Ignoring blank value for <name> from <source>; falling through' when a present-but-blank value is skipped.

- Threaded verbose: through all 10 ConfigProp.load() call sites across 6 command files.

- Updated the user-facing error message to mention that empty/whitespace-only values are treated as missing.

- README updated with the new behavior + verbose hint.

Test changes:

- Flipped the lock-step test in config_file_test.dart that documented the buggy behavior; rewrote its comment so the contract is unambiguous.

- Added 13 net-new tests in config_props_test.dart and e2e_config_file_test.dart covering: empty fall-through at every tier, whitespace fall-through (CLI / env / tab), exit-code-2 when all tiers are blank, verbose attribution lines for each tier, and 'Ignoring blank value' notices.

- Total test count: 126 -> 139, all passing.

Verification:

- dart format . / dart analyze / dart test: all clean

- Manual smoke test confirms friendly error replaces opaque HTTP failure
Addresses PR #62 review comments #2 and #3 from @velocitysystems.

The bug: when neither HOME nor USERPROFILE is set (sandboxed CI runners, minimal containers, systemd PrivateUsers=true), _defaultStopDir() fell back to the filesystem root prefix (e.g. '/'). The discovery loop in _discover() would then walk every parent of CWD all the way up to '/', meaning a stray /.env or /etc/.env could be silently picked up — a correctness and potential security concern.

The fix: _defaultStopDir() now returns String? and returns null when neither home env var is set. ConfigFile.load() handles the null case by checking only the start directory (CWD) — no upward walking. Behavior when HOME/USERPROFILE IS set is unchanged. Users in HOME-less environments who need a .env from elsewhere can use --config-file=<path> (the existing escape hatch).

Also closes review comment #3 (README ambiguity about 'up to $HOME' when not under $HOME) by rewriting the discovery section in both README.md and AGENT.md.

Test seam: load() gains an optional environmentOverride: Map<String, String>? parameter (test-only) so we can simulate empty/partial environments without spawning a child process. Production callers pass null (the default) and read from Platform.environment as before.

Test changes:

- New unit group 'discovery without HOME' in config_file_test.dart (5 tests): finds .env in CWD when HOME unset; does NOT walk to parent dirs (regression for the bug); empty HOME string treated as unset; USERPROFILE honored as Windows fallback; verbose hint is printed.

- New E2E test in e2e_config_file_test.dart: spawns the CLI with environment: const {} (no HOME), with .env in a parent dir; asserts the parent .env is NOT loaded and the friendly Missing error is emitted.

- Total test count: 139 -> 145, all passing.

Verification:

- dart format . / dart analyze / dart test: all clean

- Manual smoke: env -i dart run bin/raygun_cli.dart ... from a CWD with a parent .env confirms the parent .env is ignored and the verbose hint is shown

AGENT.md updated to reflect the new discovery contract.
@TheRealAgentK TheRealAgentK merged commit 8feaa78 into main May 8, 2026
5 checks passed
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.

Support config file for arguments like the app-id

2 participants