Skip to content

[eas-cli] Fix fingerprint commands corrupting --json output with --environment#3870

Open
nossila wants to merge 1 commit into
expo:mainfrom
nossila:fingerprint-json-env-var-stdout
Open

[eas-cli] Fix fingerprint commands corrupting --json output with --environment#3870
nossila wants to merge 1 commit into
expo:mainfrom
nossila:fingerprint-json-env-var-stdout

Conversation

@nossila

@nossila nossila commented Jun 17, 2026

Copy link
Copy Markdown

Why

Running eas fingerprint:generate --platform ios --environment preview --json --non-interactive (or fingerprint:compare with --environment) emits an eas-cli log line to stdout before the JSON, so the output is not valid JSON and tools like jq fail to parse it:

Environment variables with visibility "Plain text" and "Sensitive" loaded from the "preview" environment on EAS: APP_ANDROID_PACKAGE, ...

{
  "sources": [ ... ]
}

What's going on

In --json mode, src/utils/json.ts redirects process.stdout.write → process.stderr.write so progress logs don't pollute the final JSON, and only printJsonOnlyOutput() writes to the real stdout.

But in both fingerprint:generate and fingerprint:compare, enableJsonOutput() was called after getContextAsync(). When --environment is set, the ProjectConfig context (PrivateProjectConfigContextField) eagerly loads the server-side environment variables during context resolution, and loadServerSideEnvironmentVariablesAsync() logs the Environment variables with visibility ... line via Log.logconsole.logprocess.stdout.write. Because that happens before the redirect is installed, the line lands on the real stdout and corrupts the JSON. (The value is cached, so the later getServerSideEnvironmentVariablesAsync() call doesn't re-log it — the context load is the only emitter.)

This is a separate, in-process cause from the subprocess-output corruption fixed in #3659; that PR set silent: true for @expo/fingerprint subprocesses and does not touch this ordering.

How

Move the if (json) { enableJsonOutput(); } block to before getContextAsync() in both commands, so the stdout→stderr redirect is active while the context loads (and logs) the server-side environment variables. The json flag is already resolved earlier, so this is safe. Added a CHANGELOG bug-fix entry.

Test Plan

  • yarn typecheck — passes.

  • yarn lint / yarn fmt:check — pass (0 errors in touched files).

  • Manual reproduction:

    # Before: leading "Environment variables with visibility ..." line breaks parsing
    eas fingerprint:generate -p ios --environment preview --json --non-interactive | jq -r '.hash'
    
    # After: stdout is clean JSON, jq extracts the hash directly

    With the fix, the env-var log is written to stderr and stdout contains only the JSON, so jq -r '.hash' succeeds. Redirecting stderr to /dev/null (2>/dev/null) leaves valid JSON.

…vironment

`fingerprint:generate` and `fingerprint:compare` called enableJsonOutput()
after getContextAsync(), but the ProjectConfig context eagerly loads
server-side environment variables — logging the "Environment variables with
visibility ... loaded from the ... environment on EAS: ..." line — before the
stdout->stderr redirect was installed. That line leaked into stdout and broke
JSON parsing. Enable JSON output before resolving context so the redirect is
active while env vars load.
@github-actions

Copy link
Copy Markdown

Subscribed to pull request

File Patterns Mentions
**/* @douglowder

Generated by CodeMention

@nossila

nossila commented Jun 17, 2026

Copy link
Copy Markdown
Author

Related prior art: #3659 fixed a different cause of corrupted --json output in the same fingerprint surface — stray stdout from @expo/fingerprint subprocesses (the ExpoConfigLoader IPC fallback console.log) — by setting silent: true in src/fingerprint/cli.ts. Since those are separate OS processes, the in-process stdout → stderr redirect can't reach them.

This PR addresses an independent, in-process cause: eas-cli's own Environment variables with visibility ... log (emitted while the ProjectConfig context eagerly loads server-side env vars for --environment) was written to stdout because enableJsonOutput() ran after getContextAsync(). The two fixes are complementary — #3659 silenced subprocess noise; this one ensures the redirect is installed before the context logs.

@nossila

nossila commented Jun 17, 2026

Copy link
Copy Markdown
Author

@quinlanj would you be able to review? You own most of the fingerprint command code, so you're the natural reviewer here. cc @wschurman / @Mookiies

@nossila

nossila commented Jun 17, 2026

Copy link
Copy Markdown
Author

Workaround for affected releases (until this is merged/released): use --build-profile instead of --environment.

# Broken: leading "Environment variables with visibility ..." line corrupts stdout
eas fingerprint:generate -p ios --environment preview --json --non-interactive | jq -r '.hash'

# Works: stdout is clean JSON
eas fingerprint:generate -p ios --build-profile preview --json --non-interactive | jq -r '.hash'

Why it works: the bug only triggers with --environment, which makes the ProjectConfig context eagerly load server-side env vars (and log) before enableJsonOutput() installs the stdout→stderr redirect. The --build-profile path instead loads env vars via evaluateConfigWithEnvVarsAsync() after enableJsonOutput(), so the log correctly goes to stderr and stdout stays valid JSON. (Only equivalent if the build profile maps to the same environment in eas.json.)

Note: 2>/dev/null does not help here — the offending line is written to stdout, not stderr.

For reference, this bug is present in every release for the --environment + --json combo: since v16.1.0 for fingerprint:generate and v16.2.0 for fingerprint:compare, through the current v20.2.0.

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.

1 participant