Skip to content

Follow-up to #265: report unusable .env files and stop the dotenv hang - #269

Merged
schpetbot merged 1 commit into
mainfrom
oss-pr-lander/265
Aug 31, 2026
Merged

schpetbot merged 1 commit into
mainfrom
oss-pr-lander/265

Conversation

@schpetbot

Copy link
Copy Markdown
Collaborator

Follow-up to #265 (which fixes #264). Draft until #265 lands — this branch currently contains @jackarch-2's commit too, and will be rebased down to just the delta once #265 is merged.

#265's ?.isFile check is right, and this builds on it rather than replacing it. Two things it leaves open, both of which hit the setup described in #264:

The reporter's .env files still hang the CLI

#264 says their .env files contain zsh syntax because engineers source them. A single ordinary line in such a file hangs linear forever at startup — no error, no exit:

echo 'export PATH=$PATH:/opt/bin' > .env
linear --version   # never returns

@std/dotenv expands $VAR references in unquoted values with a while loop that never terminates when a value refers to itself. This is worse than the crash #265 fixes (a crash at least prints something), and it's still present in the latest @std/dotenv (0.225.8), so a version bump doesn't help.

Since the only keys we ever apply are LINEAR_/GH_/GITHUB_, the fix is to drop every other line before handing the text to the parser. That removes the whole class of failure, and also silences the parser's per-line console.warn about invalid identifiers that were never ours to complain about. An unquoted $ reference in one of our keys is refused with a warning rather than expanded — an unset reference otherwise resolves to the literal string "undefined", which is silent corruption of a config value. Quoted values are untouched, since @std/dotenv takes those literally and they can't hang (verified against 0.225.6).

Skipping the file silently hides it

#264 explicitly asks not to be silent — "being entirely silent about the issue may hide deeper issues" — and CLAUDE.md's rule is to never fail silently. So an unusable candidate now prints one yellow warning and continues:

Warning: Ignoring /repo/.env: it is a directory, not a file. No variables were loaded from it.
  Set LINEAR_IGNORE_ENV_FILE=1 to skip .env loading entirely.

Always stderr, never stdout, so --json output and the completion scripts stay machine-readable (QA'd: completions zsh emits 93KB of clean stdout with the warning on stderr). LINEAR_IGNORE_ENV_FILE=1 opts out entirely, which is what keeps the warning from being a permanent per-invocation tax on a repo that will never have a dotenv-shaped .env.

Also folded in

  • An unreadable .env (mode 000) passed feat: silently ignore .env directories when loading config #265's isFile check and then crashed in the read. Read and parse failures are caught too.
  • A FIFO .env would have blocked the read forever; isFile covers that, and it's now exercised by QA.
  • The repository-root candidate goes through the same code path instead of a near-copy of it.

Behavior change worth a look

Unquoted values are no longer shell-expanded. Expansion was already half-broken — unset references became "undefined", self-referential ones hung — and every skipped key is warned about (unless the process environment already overrides it, in which case the .env value would have lost anyway), so nothing is dropped quietly.

Verification

571 tests pass; deno check, deno lint, deno fmt --check clean. 17 provenance tests including all four pre-existing .env tests unchanged. Manually QA'd 15 cases against the real CLI: missing / valid / directory / shell-sourced / unreadable / FIFO / symlink / dangling symlink / not-a-git-repo / nested-falls-back-to-root / opt-out, plus stdout cleanliness.

#265 stops the `IsADirectory` crash by requiring `.env` to be a regular file.
That check is right, and this builds on it rather than replacing it.

Two things it leaves open, both of which bite the setup in #264:

A `.env` written to be `source`d by a shell hangs the CLI outright. @std/dotenv
expands `$VAR` references in unquoted values using a `while` loop that never
terminates when a value refers to itself, so a single ordinary line like
`export PATH=$PATH:/opt/bin` spins forever at startup -- no error, no exit.
That is a worse failure than the crash #265 fixes, it is still present in the
latest @std/dotenv (0.225.8), and the reporter's files are exactly the
shell-sourced kind that contain it. Since the only keys we ever apply are
LINEAR_/GH_/GITHUB_, the fix is to drop every other line before parsing, which
removes the whole class of failure and also silences the parser's warnings
about keys that were never ours to complain about. An unquoted `$` reference
in one of our own keys is refused with a warning instead of expanded --
unexpanded references otherwise resolve to the literal string "undefined",
which is silent corruption of a config value. Quoted values are left alone,
since dotenv takes those literally and they cannot hang.

And skipping the file silently hides it. Both the issue and CLAUDE.md ask for
the opposite: the reporter explicitly said being entirely silent "may hide
deeper issues", and the project's rule is to never fail silently. So an
unusable candidate now prints one yellow warning on stderr -- never stdout, so
--json output and the completion scripts stay clean -- and the CLI continues.
`LINEAR_IGNORE_ENV_FILE=1` opts out entirely, so the warning is self-terminating
for a repo that will never have a dotenv-shaped .env.

Also folded in: an unreadable .env (mode 000) passed #265's isFile check and
then crashed in the read, so read and parse failures are caught too, and the
repository-root candidate goes through the same path instead of a near-copy of
it.
@schpetbot
schpetbot marked this pull request as ready for review August 31, 2026 19:01
@schpetbot
schpetbot merged commit 189d1ee into main Aug 31, 2026
15 checks passed
@schpetbot
schpetbot deleted the oss-pr-lander/265 branch August 31, 2026 19:02
@schpetbot

Copy link
Copy Markdown
Collaborator Author

Shipped in v2.6.0.

Follow-up to #265 (which fixed #264).

  • An unusable .env is reported as a warning on stderr and skipped, instead of crashing or hanging.
  • The dotenv expander no longer loops forever on a self-referential value such as export PATH=$PATH:/opt/bin.
  • An unquoted $VAR in a LINEAR_/GH_/GITHUB_ value is skipped with a warning rather than expanded.
  • LINEAR_IGNORE_ENV_FILE=1 skips .env loading entirely.

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.

Crash on startup due to odd .env

2 participants