Skip to content

Commit 893fdf3

Browse files
jfrocheyvan-sraka
authored andcommitted
fix(github-matrix): handle evaluation errors without deadlock
Fix github-matrix that would hang when nix-eval-jobs encountered errors due to subprocess pipe deadlock - stderr buffer would fill while reading stdout. This change ensure that evaluation errors are visible and the workflow fails properly while still showing which packages succeeded.
1 parent c4f1c62 commit 893fdf3

File tree

2 files changed

+37
-11
lines changed

2 files changed

+37
-11
lines changed

.github/workflows/nix-eval.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@ jobs:
3232
run: |
3333
set -Eeu -o pipefail
3434
result=$(nix run --accept-flake-config .\#github-matrix -- checks legacyPackages)
35+
if [ -z "$result" ]; then
36+
echo "Error: github-matrix returned empty output" >&2
37+
exit 1
38+
fi
3539
echo matrix="$result" >> "$GITHUB_OUTPUT"

nix/packages/github-matrix/github_matrix.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,46 +102,54 @@ def build_nix_eval_command(max_workers: int, flake_outputs: List[str]) -> List[s
102102
return nix_eval_cmd
103103

104104

105-
def parse_nix_eval_line(line: str, drv_paths: Set[str]) -> Optional[NixEvalJobsOutput]:
105+
def parse_nix_eval_line(
106+
line: str, drv_paths: Set[str], errors: List[str]
107+
) -> Optional[NixEvalJobsOutput]:
106108
"""Parse a single line of nix-eval-jobs output"""
107109
if not line.strip():
108110
return None
109111

110112
try:
111113
data: NixEvalJobsOutput = json.loads(line)
112114
if "error" in data:
113-
raise ValueError(
115+
error_msg = (
114116
f"Error in nix-eval-jobs output for {data['attr']}: {data['error']}"
115117
)
118+
errors.append(error_msg)
119+
return None
116120
if data["drvPath"] in drv_paths:
117121
return None
118122
drv_paths.add(data["drvPath"])
119123
return data
120124
except json.JSONDecodeError:
121-
print(f"Skipping invalid JSON line: {line}", file=sys.stderr)
125+
error_msg = f"Skipping invalid JSON line: {line}"
126+
print(error_msg, file=sys.stderr)
127+
errors.append(error_msg)
122128
return None
123129

124130

125-
def run_nix_eval_jobs(cmd: List[str]) -> Generator[NixEvalJobsOutput, None, None]:
131+
def run_nix_eval_jobs(
132+
cmd: List[str], errors: List[str]
133+
) -> Generator[NixEvalJobsOutput, None, None]:
126134
"""Run nix-eval-jobs and yield parsed package data."""
127135
print(f"Running command: {' '.join(cmd)}", file=sys.stderr)
128136

129137
with subprocess.Popen(
130-
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
138+
cmd, stdout=subprocess.PIPE, stderr=None, text=True
131139
) as process:
132140
drv_paths: Set[str] = set()
133141
assert process.stdout is not None # for mypy
134-
assert process.stderr is not None # for mypy
135142
for line in process.stdout:
136-
package = parse_nix_eval_line(line, drv_paths)
143+
package = parse_nix_eval_line(line, drv_paths, errors)
137144
if package:
138145
yield package
139146

140147
process.wait()
141148
if process.returncode != 0:
142-
print("Error: Evaluation failed", file=sys.stderr)
143-
sys.stderr.write(process.stderr.read())
144-
sys.exit(process.returncode)
149+
error_msg = "Error: nix-eval-jobs process failed with non-zero exit code"
150+
print(error_msg, file=sys.stderr)
151+
errors.append(error_msg)
152+
# Don't exit here - let main() handle it after reporting all errors
145153

146154

147155
def is_extension_pkg(pkg: NixEvalJobsOutput) -> bool:
@@ -227,7 +235,9 @@ def main() -> None:
227235

228236
cmd = build_nix_eval_command(max_workers, args.flake_outputs)
229237

230-
gh_action_packages = sort_pkgs_by_closures(list(run_nix_eval_jobs(cmd)))
238+
# Collect all evaluation errors
239+
errors: List[str] = []
240+
gh_action_packages = sort_pkgs_by_closures(list(run_nix_eval_jobs(cmd, errors)))
231241

232242
def clean_package_for_output(pkg: NixEvalJobsOutput) -> GitHubActionPackage:
233243
"""Convert nix-eval-jobs output to GitHub Actions matrix package"""
@@ -277,6 +287,18 @@ def clean_package_for_output(pkg: NixEvalJobsOutput) -> GitHubActionPackage:
277287
)
278288
print(json.dumps(gh_output))
279289

290+
# Check if any errors occurred during evaluation
291+
if errors:
292+
print("\n=== Evaluation Errors ===", file=sys.stderr)
293+
for i, error in enumerate(errors, 1):
294+
print(f"\nError {i}:", file=sys.stderr)
295+
print(error, file=sys.stderr)
296+
print(
297+
f"\n=== Total: {len(errors)} error(s) occurred during evaluation ===",
298+
file=sys.stderr,
299+
)
300+
sys.exit(1)
301+
280302

281303
if __name__ == "__main__":
282304
main()

0 commit comments

Comments
 (0)