Skip to content

Commit 33103db

Browse files
Apply Linux analysis process exit fix
1 parent 2250b00 commit 33103db

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Apply Linux analysis process exit fix
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
types:
8+
- opened
9+
- synchronize
10+
- reopened
11+
12+
permissions:
13+
contents: write
14+
15+
jobs:
16+
apply-fix:
17+
if: github.actor != 'github-actions[bot]' && github.event.pull_request.head.repo.full_name == github.repository
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: actions/checkout@v4
21+
with:
22+
ref: agent/release-readiness-audit
23+
fetch-depth: 0
24+
- uses: actions/setup-python@v5
25+
with:
26+
python-version: '3.11'
27+
- name: Install package and test tooling
28+
run: |
29+
python -m pip install --upgrade pip
30+
python -m pip install --no-cache-dir -e . pytest
31+
- name: Patch completed postprocessing command exits
32+
shell: bash
33+
run: |
34+
set -euo pipefail
35+
python - <<'PY'
36+
from pathlib import Path
37+
38+
path = Path('src/wwgpt/cli.py')
39+
text = path.read_text()
40+
marker = "def _seeds(s: str | None) -> list[int] | None:\n return None if not s else [int(x) for x in s.split(',') if x]\n\n\n"
41+
helper = "def _seeds(s: str | None) -> list[int] | None:\n return None if not s else [int(x) for x in s.split(',') if x]\n\n\ndef _exit_after_flush(status: int = 0) -> None:\n \"\"\"Exit a completed CLI command without waiting on library worker threads.\"\"\"\n sys.stderr.flush()\n sys.stdout.flush()\n os._exit(status)\n\n\n"
42+
if 'def _exit_after_flush(status: int = 0)' not in text:
43+
if marker not in text:
44+
raise SystemExit('seed helper marker not found')
45+
text = text.replace(marker, helper, 1)
46+
47+
old = ' elif args.cmd=="analyze-results": print(analyze_results(args.results_root, args.analysis_plan))\n'
48+
new = ' elif args.cmd=="analyze-results":\n print(analyze_results(args.results_root, args.analysis_plan), flush=True)\n _exit_after_flush(0)\n'
49+
if old in text:
50+
text = text.replace(old, new, 1)
51+
elif new.strip() not in text:
52+
raise SystemExit('analyze-results branch not found')
53+
54+
old = " sys.stderr.flush()\n sys.stdout.flush()\n # Some streaming dataset backends can leave non-daemon workers alive after all artifacts\n # have been written. Exit the CLI process deterministically so shell wrappers can finish.\n os._exit(0)\n"
55+
new = " # Some data and analysis backends can leave non-daemon workers alive after all\n # artifacts have been written. Exit deterministically so shell wrappers finish.\n _exit_after_flush(0)\n"
56+
if old in text:
57+
text = text.replace(old, new, 1)
58+
59+
old = ' elif args.cmd=="generate-reproducibility-report":\n print(\n write_reproducibility_report(\n args.experiment_root,\n strict=args.strict,\n analysis_plan=args.analysis_plan,\n )\n )\n'
60+
new = ' elif args.cmd=="generate-reproducibility-report":\n print(\n write_reproducibility_report(\n args.experiment_root,\n strict=args.strict,\n analysis_plan=args.analysis_plan,\n ),\n flush=True,\n )\n _exit_after_flush(0)\n'
61+
if old in text:
62+
text = text.replace(old, new, 1)
63+
elif new.strip() not in text:
64+
raise SystemExit('reproducibility-report branch not found')
65+
path.write_text(text)
66+
67+
tests = Path('tests/test_release_readiness_audit.py')
68+
test_text = tests.read_text()
69+
addition = '''
70+
71+
def test_postprocessing_cli_exits_after_flushing_completed_artifacts() -> None:
72+
source = Path("src/wwgpt/cli.py").read_text()
73+
assert "def _exit_after_flush(status: int = 0)" in source
74+
assert 'elif args.cmd=="analyze-results":\\n print(' in source
75+
assert 'elif args.cmd=="generate-reproducibility-report":' in source
76+
assert source.count("_exit_after_flush(0)") >= 3
77+
'''
78+
if 'test_postprocessing_cli_exits_after_flushing_completed_artifacts' not in test_text:
79+
tests.write_text(test_text + addition)
80+
PY
81+
rm -f .github/workflows/apply-linux-analysis-exit.yml
82+
rm -f .github/workflows/apply-linux-cli-exit-fix.yml
83+
- name: Validate exact Linux clean-process path
84+
env:
85+
MPLBACKEND: Agg
86+
shell: bash
87+
run: |
88+
set -euo pipefail
89+
python -m compileall -q src tests scripts
90+
bash -n scripts/*.sh
91+
pytest -q \
92+
tests/test_release_readiness_audit.py \
93+
tests/test_acceleration_analysis.py \
94+
tests/test_level012_full_pipeline.py
95+
timeout 300 ./scripts/run_bounded_level012_acceptance.sh /tmp/linux-analysis-exit-acceptance
96+
- name: Commit validated fix
97+
shell: bash
98+
run: |
99+
set -euo pipefail
100+
git config user.name github-actions[bot]
101+
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
102+
git add src/wwgpt/cli.py tests/test_release_readiness_audit.py \
103+
.github/workflows/apply-linux-analysis-exit.yml \
104+
.github/workflows/apply-linux-cli-exit-fix.yml
105+
git diff --cached --check
106+
git commit -m 'Exit completed postprocessing commands deterministically'
107+
git push origin HEAD:agent/release-readiness-audit

0 commit comments

Comments
 (0)