Hi,
I have a weird issue in one of my builds, where one of the commands in buildPhase is using ora, which displays a spinner in the terminal and therefore is controlling stdout (or so I think).
Because of this spinner, the build hangs indefinitely.
The only option I found to fix this is to set the CI environment variable to true when running the command (see also “Additional details” section).
I’m ok setting this variable, but I’d like to understand why is this happening?
Reproducing the issue
Commands to run
mkdir -p test-ora-nix-build-issue && cd $_
touch index.mjs # Set content to relevant snippet below
touch default.nix # Set content to relevant snippet below
npm init -y
npm i ora@9.0.0
nix-build .
File: index.mjs
import ora from 'ora'
async function triggerError() {
return new Promise((_, reject) => {
setTimeout(() => reject("oh no"), 5000)
})
}
const spinner = ora({ discardStdin: false })
// This will never render
spinner.start("Processing...")
try {
await triggerError()
} catch (e) {
spinner.stopAndPersist()
// This actually throws, but is never rendered,
// as if ora never gave back stdout control to the nix build
throw e
}
spinner.stopAndPersist()
File: default.nix
{
pkgs ? import <nixpkgs> { },
}:
pkgs.buildNpmPackage {
name = "demo";
src = ./.;
# Update this if needed
npmDepsHash = "sha256-XxFvAn3dHqTmNDE6rGs1yjbexJsTFH3sUy+XGCwn8AU=";
buildPhase = ''
node index.mjs
'';
}
Output
This is the output of the nix-build command, which times out after 5+ hours.
this derivation will be built:
/nix/store/52g7c7xnd97zrs7izphb5rilzxzs69f0-demo.drv
building '/nix/store/52g7c7xnd97zrs7izphb5rilzxzs69f0-demo.drv'...
Running phase: unpackPhase
unpacking source archive /nix/store/hzrjiq452sag09igvkmv4zs9898mdd8m-test-ora
source root is test-ora
Running phase: patchPhase
Executing npmConfigHook
Configuring npm
Validating consistency between /build/test-ora/package-lock.json and /nix/store/dild7mwm63028kq515r6n8wm93d3n8w3-demo-npm-deps/package-lock.json
Installing dependencies
added 17 packages, and audited 18 packages in 299ms
17 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
patching script interpreter paths in node_modules
rebuilt dependencies successfully
patching script interpreter paths in node_modules
Finished npmConfigHook
Running phase: updateAutotoolsGnuConfigScriptsPhase
Running phase: configurePhase
no configure script, doing nothing
Running phase: buildPhase
(node:165) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Fixing the build
Simply prepend the “build” command as below in default.nix:
- node index.mjs
+ CI=true node index.mjs
Output
Works like a charm, I can see the build error.
this derivation will be built:
/nix/store/j3vfk5zws1x5pd2srxxvcgk028dnxswi-demo.drv
building '/nix/store/j3vfk5zws1x5pd2srxxvcgk028dnxswi-demo.drv'...
Running phase: unpackPhase
unpacking source archive /nix/store/qg3cpd0vlng9jz4i6cmdsclqaal2120f-test-ora
source root is test-ora
Running phase: patchPhase
Executing npmConfigHook
Configuring npm
Validating consistency between /build/test-ora/package-lock.json and /nix/store/dild7mwm63028kq515r6n8wm93d3n8w3-demo-npm-deps/package-lock.json
Installing dependencies
added 17 packages, and audited 18 packages in 298ms
17 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
patching script interpreter paths in node_modules
rebuilt dependencies successfully
patching script interpreter paths in node_modules
Finished npmConfigHook
Running phase: updateAutotoolsGnuConfigScriptsPhase
Running phase: configurePhase
no configure script, doing nothing
Running phase: buildPhase
(node:169) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
- Processing...
Processing...
node:internal/modules/run_main:129
triggerUncaughtException(
^
oh no
(Use `node --trace-uncaught ...` to show where the exception was thrown)
Node.js v20.17.0
error: builder for '/nix/store/j3vfk5zws1x5pd2srxxvcgk028dnxswi-demo.drv' failed with exit code 1;
last 25 log lines:
>
> 17 packages are looking for funding
> run `npm fund` for details
>
> found 0 vulnerabilities
> patching script interpreter paths in node_modules
> rebuilt dependencies successfully
> patching script interpreter paths in node_modules
> Finished npmConfigHook
> Running phase: updateAutotoolsGnuConfigScriptsPhase
> Running phase: configurePhase
> no configure script, doing nothing
> Running phase: buildPhase
> (node:169) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
> (Use `node --trace-warnings ...` to show where the warning was created)
> - Processing...
> Processing...
>
> node:internal/modules/run_main:129
> triggerUncaughtException(
> ^
> oh no
> (Use `node --trace-uncaught ...` to show where the exception was thrown)
>
> Node.js v20.17.0
For full logs, run 'nix log /nix/store/j3vfk5zws1x5pd2srxxvcgk028dnxswi-demo.drv'.
Additional details
Ora is disabled by default if stderr is non-TTY or it’s in CI. It effectively uses this condition as default value of isEnabled:
return Boolean(
process.stderr && process.stderr.isTTY &&
process.env.TERM !== 'dumb' &&
!('CI' in process.env)
)
However, in Nix builds this condition evaluates to true because:
process.stderr.isTTY: true
process.env.TERM: xterm-256color
process.env.CI: undefined
It feels kinda weird that Nix builds are considered as TTYs… But I may be missing something.
Any other idea than setting CI environment variable?