From d1a508f9b48d432bf1ccb614872f03cef779f6bd Mon Sep 17 00:00:00 2001 From: Oz Date: Thu, 13 Aug 2026 23:42:54 +0000 Subject: [PATCH 1/3] Fix nx run completions: use nx graph --file=stdout The workspace_targets generator that powers `nx run ` completions has been broken since Dec 2024 (PR #166) and was not actually fixed by the Apr 2026 attempt (PR #251, APP-3498): - PR #166 turned `nx graph --file $temp > /dev/null && cat $temp` into `... 2>/dev/null && cat $temp`, so nx's stdout banner from writing the file stopped being discarded and got prepended to the JSON, breaking parsing. - PR #251 tried `nx graph --print` first, but Nx 20.x/21.0 declare that flag without consuming it: nx ignores it and starts the interactive project-graph web server on 127.0.0.1:4211, which never exits. Because it never exits non-zero, the `||` fallback (which still carried the original stdout bug) never ran. `nx graph --file=stdout` is understood by every Nx version tested (20.x-23.x, see APP-5384) and writes only the graph JSON to stdout, with no confirmation banner and no interactive server. No fallback is needed since a single form covers the supported version range, and a fallback that's never reached correctly is worse than none. Verified against real Nx workspaces on nx@20.3.0 (the original reporter's version) and nx@23.1.1: both produce valid JSON and the expected `project:target` suggestions, with no process left listening on port 4211. Fixes warpdotdev/warp#4691. Co-Authored-By: Warp Agent --- command-signatures/src/generators/nx.rs | 112 ++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 8 deletions(-) diff --git a/command-signatures/src/generators/nx.rs b/command-signatures/src/generators/nx.rs index 422bd0ea..4fa0b3a2 100644 --- a/command-signatures/src/generators/nx.rs +++ b/command-signatures/src/generators/nx.rs @@ -10,16 +10,21 @@ use warp_completion_metadata::{ lazy_static! { /// Command that retrieves the Nx project graph with target information. /// - /// Uses `nx graph --print` (Nx 19.20+) which outputs JSON to stdout. - /// Falls back to `nx graph --file` with a tmpfile for older Nx versions - /// (avoids a truncation bug in stdout output, see - /// https://github.com/nrwl/nx/issues/18689). - static ref NX_WORKSPACE_TARGETS_COMMAND: CommandBuilder = CommandBuilder::single_command( - "sh -c \"nx graph --print 2>/dev/null || { temp=\\$(mktemp -u).json && nx graph --file \\$temp 2>/dev/null && cat \\$temp && rm -f \\$temp; }\"" - ); + /// Uses `nx graph --file=stdout`, which writes only the graph JSON to stdout (no + /// human-readable banner, unlike `nx graph --file `, which prints a confirmation + /// banner alongside writing the file). Verified empirically to produce valid JSON across + /// Nx 20.x-23.x (see APP-5384). + /// + /// Earlier implementations tried `nx graph --print` first, but Nx 20.x/21.0 declare that + /// flag without consuming it: nx silently ignores it and starts the interactive + /// project-graph web server instead, which never exits, so no fallback ever gets a chance + /// to run. `--file=stdout` is understood by every Nx version this generator needs to + /// support, so no fallback is needed. + static ref NX_WORKSPACE_TARGETS_COMMAND: CommandBuilder = + CommandBuilder::single_command("nx graph --file=stdout"); } -/// Parsed output from `nx graph --print` / `nx graph --file`. +/// Parsed output from `nx graph --file=stdout`. #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "camelCase")] struct NXGraphFile { @@ -159,3 +164,94 @@ pub fn generator() -> CommandSignatureGenerators { }), ) } + +#[cfg(test)] +mod tests { + use super::*; + use warp_completion_metadata::Shell; + + /// Regression test for APP-5384: a future `CommandBuilder` refactor (like the one in PR #166 + /// that silently turned a stdout redirect into a stderr redirect) must not be able to change + /// the built command string without this test catching it. + #[test] + fn test_workspace_targets_command_string() { + assert_eq!( + NX_WORKSPACE_TARGETS_COMMAND.build(Shell::Posix), + "nx graph --file=stdout" + ); + } + + #[test] + fn test_process_workspace_targets_parses_projects_and_targets() { + let output = r#"{ + "graph": { + "nodes": { + "admin-integrations": { + "name": "admin-integrations", + "type": "app", + "data": { + "root": "apps/admin-integrations", + "targets": { + "build": {}, + "serve": {} + } + } + }, + "utils": { + "name": "utils", + "type": "lib", + "data": { + "root": "libs/utils", + "targets": { + "test": {} + } + } + } + }, + "dependencies": {} + } + }"#; + + let results = process_workspace_targets(output); + assert!(!results.is_ordered); + + let mut names: Vec<&str> = results + .suggestions + .iter() + .map(|s| s.exact_string.as_str()) + .collect(); + names.sort_unstable(); + assert_eq!( + names, + vec![ + "admin-integrations:build", + "admin-integrations:serve", + "utils:test" + ] + ); + assert!(results + .suggestions + .iter() + .all(|s| s.description.as_deref() == Some("nx target"))); + } + + /// Regression test for the original Dec 2024 bug (PR #166): if a human-readable banner from + /// `nx` ever precedes the JSON on stdout again, parsing must fail closed (no suggestions), + /// not panic or silently return garbage. + #[test] + fn test_process_workspace_targets_fails_closed_on_banner_text() { + let output = + " NX JSON output created in /tmp/tmp.DrVPzrwmkX.json\n{\"graph\":{\"nodes\":{}}}"; + assert_eq!( + process_workspace_targets(output), + GeneratorResults::default() + ); + } + + #[test] + fn test_process_workspace_targets_empty_graph() { + let output = r#"{"graph": {"nodes": {}}}"#; + let results = process_workspace_targets(output); + assert!(results.suggestions.is_empty()); + } +} From 13ffde998f8e20daa7b600ace48cf1b224959c94 Mon Sep 17 00:00:00 2001 From: Oz Date: Thu, 13 Aug 2026 23:55:29 +0000 Subject: [PATCH 2/3] Remove workspace_targets rationale doc comment Addresses review feedback on PR #335: delete the multi-paragraph rationale block above NX_WORKSPACE_TARGETS_COMMAND (the requested line range), keeping only the one-line summary. Co-Authored-By: Oz --- command-signatures/src/generators/nx.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/command-signatures/src/generators/nx.rs b/command-signatures/src/generators/nx.rs index 4fa0b3a2..3d93ef41 100644 --- a/command-signatures/src/generators/nx.rs +++ b/command-signatures/src/generators/nx.rs @@ -9,17 +9,6 @@ use warp_completion_metadata::{ lazy_static! { /// Command that retrieves the Nx project graph with target information. - /// - /// Uses `nx graph --file=stdout`, which writes only the graph JSON to stdout (no - /// human-readable banner, unlike `nx graph --file `, which prints a confirmation - /// banner alongside writing the file). Verified empirically to produce valid JSON across - /// Nx 20.x-23.x (see APP-5384). - /// - /// Earlier implementations tried `nx graph --print` first, but Nx 20.x/21.0 declare that - /// flag without consuming it: nx silently ignores it and starts the interactive - /// project-graph web server instead, which never exits, so no fallback ever gets a chance - /// to run. `--file=stdout` is understood by every Nx version this generator needs to - /// support, so no fallback is needed. static ref NX_WORKSPACE_TARGETS_COMMAND: CommandBuilder = CommandBuilder::single_command("nx graph --file=stdout"); } From ff077e2c3b97124eb4e112ec4e04bb235d2d73fb Mon Sep 17 00:00:00 2001 From: Oz Date: Thu, 13 Aug 2026 23:56:11 +0000 Subject: [PATCH 3/3] Remove nx workspace targets comment Co-Authored-By: Andy Co-Authored-By: Oz --- command-signatures/src/generators/nx.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/command-signatures/src/generators/nx.rs b/command-signatures/src/generators/nx.rs index 3d93ef41..22a976b1 100644 --- a/command-signatures/src/generators/nx.rs +++ b/command-signatures/src/generators/nx.rs @@ -8,7 +8,6 @@ use warp_completion_metadata::{ }; lazy_static! { - /// Command that retrieves the Nx project graph with target information. static ref NX_WORKSPACE_TARGETS_COMMAND: CommandBuilder = CommandBuilder::single_command("nx graph --file=stdout"); }