Skip to content

test(ci): cover portable release toolchain wrappers in quality gate - #86

Open
navarrocorbi-prog wants to merge 1 commit into
mainfrom
cursor/release-assets-workflow-tests-6b9d
Open

test(ci): cover portable release toolchain wrappers in quality gate#86
navarrocorbi-prog wants to merge 1 commit into
mainfrom
cursor/release-assets-workflow-tests-6b9d

Conversation

@navarrocorbi-prog

@navarrocorbi-prog navarrocorbi-prog commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Addresses Greptile findings on #81 and lands the useful CodeRabbit-generated coverage properly:

  • Add scripts/test_release_portable_assets_workflow.py for the Linux aarch64-musl Zig cc/cxx wrappers + RUSTFLAGS
  • Wire the module into both just maintenance and the explicit CI unittest list in .github/workflows/ci.yml (so Quality gate actually runs it)
  • Scope heredoc/RUSTFLAGS extraction to the Install build tools on Linux step so same-named wrappers elsewhere cannot poison assertions
  • Add a regression test that injects a decoy wrapper outside that step

#81 is closed as superseded. CI: Quality gate + Maintenance green (16 tests covered via Maintenance).

Test plan

Open in Web Open in Cursor 

Greptile Summary

Adds regression coverage for the portable release workflow’s aarch64-musl compiler wrappers and Rust flags, and registers the checks in both maintenance CI and just maintenance.

The new tests execute wrapper scripts extracted from the real workflow. All 16 tests passed. In a disposable copy, changing the cc and cxx wrappers to forward the duplicate Rust target flag caused three wrapper assertions to fail, confirming that the coverage detects the intended target-filtering regression.

Confidence Score: 5/5

T-Rex T-Rex Logs

What T-Rex did

  • I ran the PR wrapper unittest against the repository workflow and confirmed that all 16 tests passed.
  • I performed a disposable-copy validation that preserved the workflow unchanged and mutated only the cc and cxx target-filter branches to forward --target=aarch64-unknown-linux-musl.
  • The unchanged copy passed all tests, while the altered copy detected a bad target argument in fake Zig argv and failed the cc duplicate-target, cc repeated-target, and cxx duplicate-target assertions, confirming the wrapper behavior is exercised.
  • The mutation validation showed the bad target flag was recorded in fake-Zig argv and caused failures in test_cc_wrapper_strips_duplicate_rust_target_triple, test_cc_wrapper_strips_every_occurrence_of_duplicate_target, and test_cxx_wrapper_strips_duplicate_rust_target_triple.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "test(ci): cover portable release toolcha..." | Re-trigger Greptile

Add maintenance coverage for the Linux aarch64-musl Zig wrappers and
wire the module into both just maintenance and the CI unittest list.
Scope extraction to the Install build tools on Linux step so decoy
wrappers elsewhere cannot poison assertions.

refs #81

Co-authored-by: navarrocorbi-prog <navarrocorbi-prog@users.noreply.github.com>
@SoulWayy
WaySoul (SoulWayy) force-pushed the cursor/release-assets-workflow-tests-6b9d branch from 86844b1 to 037dba0 Compare August 1, 2026 21:55
@OnlineChef
OnlineChef marked this pull request as ready for review August 1, 2026 21:55
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@github-actions github-actions Bot added the quality-remediation CI failure needs agent remediation label Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Quality remediation needed

CI run 30720154310 failed for PR #86 on cursor/release-assets-workflow-tests-6b9d at 037dba0955d5.

PR: #86

Failed jobs

  • Windows lint (failure, job id 91422486594, log remediation-logs/91422486594-Windows_lint.log)
  • Release smoke build (x86_64-unknown-linux-musl) (failure, job id 91422486747, log remediation-logs/91422486747-Release_smoke_build_x86_64-unknown-linux-musl.log)
  • Quality gate (failure, job id 91423044546, log remediation-logs/91423044546-Quality_gate.log)

Top extracted errors

  • 91422486594-Windows_lint.log:1103 2026-08-01T21:57:48.7546983Z �[1m�[92m Checking�[0m static_assertions v1.1.0
  • 91423044546-Quality_gate.log:37 2026-08-01T22:01:44.0089919Z �[36;1m echo "Quality gate failed; $name result was: $result"�[0m
  • 91423044546-Quality_gate.log:63 2026-08-01T22:01:44.1369906Z Quality gate failed; windows-lint result was: failure

Agent commands

  • Inspect failed logs: gh run view 30720154310 --repo OnlineChefGroep/herdr --log-failed
  • Watch required checks: gh pr checks 86 --repo OnlineChefGroep/herdr --watch
  • Confirm current PR files: gh pr view 86 --repo OnlineChefGroep/herdr --json files
  • Fix only the failing surfaces, push to cursor/release-assets-workflow-tests-6b9d, then let CI rerun.

Files touched

  • .github/workflows/ci.yml
  • justfile
  • scripts/test_release_portable_assets_workflow.py

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Portable release workflow tests

Layer / File(s) Summary
Workflow wrapper behavior tests
scripts/test_release_portable_assets_workflow.py
Added tests that parse the Linux build-tools step, execute extracted wrappers with fake zig binaries, and validate flags, arguments, failures, missing tools, Rustflags, and step isolation.
Maintenance test wiring
.github/workflows/ci.yml, justfile
Added the new unittest module to CI and local maintenance commands.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: ogulcancelik, onlinechef

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the added CI coverage for portable release toolchain wrappers.
Description check ✅ Passed The description accurately explains the new tests, CI integration, scoped workflow parsing, and reported test results.
Linked Issues check ✅ Passed The changes satisfy issue #81 by adding executable wrapper tests, wiring them into CI and Just, and scoping workflow assertions.
Out of Scope Changes check ✅ Passed All code changes support the linked issue objectives and no unrelated implementation changes are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/release-assets-workflow-tests-6b9d

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
scripts/test_release_portable_assets_workflow.py (1)

96-130: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Consider adding timeouts and reducing duplicated subprocess setup.

None of the subprocess.run calls in _run_wrapper (Line 119-124) or the two inline failure tests (Line 226-231, Line 245-250) set a timeout. If a future change to the workflow's wrapper script introduces a hang (for example, a script that waits on stdin), the test would block until the CI job's 15-minute timeout instead of failing quickly with a clear error.

The two failure tests (Line 213-232, Line 234-251) also duplicate most of _run_wrapper's setup (temp dir, wrapper write, fake zig, subprocess.run call) without reusing it, since _run_wrapper assumes a log file always exists after a successful run.

♻️ Proposed direction: add a bounded timeout
             result = subprocess.run(
                 [str(wrapper_path), *args],
                 env=env,
                 capture_output=True,
                 text=True,
+                timeout=10,
             )

Also applies to: 213-232, 234-251

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_release_portable_assets_workflow.py` around lines 96 - 130,
Update _run_wrapper and both inline failure tests to pass a bounded timeout to
subprocess.run, so hung wrapper scripts fail promptly. Reduce duplicated setup
in the failure tests by reusing _run_wrapper where its recorded-argv assumption
permits, or extract shared temporary-directory, fake-zig, and subprocess setup
while preserving each test’s failure assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test_release_portable_assets_workflow.py`:
- Around line 234-251: Update test_cc_wrapper_fails_closed_when_zig_is_missing
to set PATH to existing directories such as /usr/bin:/bin that do not contain
zig, keeping bash resolvable so the wrapper body reaches the missing-zig failure
path.

---

Nitpick comments:
In `@scripts/test_release_portable_assets_workflow.py`:
- Around line 96-130: Update _run_wrapper and both inline failure tests to pass
a bounded timeout to subprocess.run, so hung wrapper scripts fail promptly.
Reduce duplicated setup in the failure tests by reusing _run_wrapper where its
recorded-argv assumption permits, or extract shared temporary-directory,
fake-zig, and subprocess setup while preserving each test’s failure assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: af50c713-8d73-49cb-a5a1-d5560594a070

📥 Commits

Reviewing files that changed from the base of the PR and between a260421 and 037dba0.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • justfile
  • scripts/test_release_portable_assets_workflow.py

Comment on lines +234 to +251
def test_cc_wrapper_fails_closed_when_zig_is_missing(self) -> None:
# `set -euo pipefail` plus the unresolved `exec zig` must produce a
# non-zero exit rather than silently doing nothing.
with tempfile.TemporaryDirectory() as tmp:
tmp_path = Path(tmp)

wrapper_path = tmp_path / "cc"
self._write_executable(wrapper_path, self.wrappers["cc"])

env = dict(os.environ)
env["PATH"] = "/nonexistent"
result = subprocess.run(
[str(wrapper_path), "-c", "foo.c"],
env=env,
capture_output=True,
text=True,
)
self.assertNotEqual(result.returncode, 0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
git ls-files | rg '(^|/)scripts/test_release_portable_assets_workflow\.py$|(^|/)\.github/workflows/release-portable-assets\.yml$|(^|/)CONTRIBUTING\.md$' || true

echo
echo "== target test section =="
if [ -f scripts/test_release_portable_assets_workflow.py ]; then
  sed -n '1,120p' scripts/test_release_portable_assets_workflow.py
  echo "---"
  sed -n '200,260p' scripts/test_release_portable_assets_workflow.py
fi

echo
echo "== find wrapper contents =="
rg -n 'wrappers|cc\"|zig|exec zig|set -euo pipefail' scripts/test_release_portable_assets_workflow.py

echo
echo "== workflow mentions =="
if [ -f .github/workflows/release-portable-assets.yml ]; then
  rg -n 'Install build tools on Linux|zig|CC|cc' .github/workflows/release-portable-assets.yml
fi

echo
echo "== deterministic subprocess/env probe =="
python3 - <<'PY'
import os
import subprocess
import tempfile
from pathlib import Path

script = b'#!/usr/bin/env bash\nset -euo pipefail\nexec zig "$@"\n'
with tempfile.TemporaryDirectory() as tmp:
    path = Path(tmp) / "cc"
    path.write_bytes(script)
    os.chmod(path, 0o755)
    for val in ["/nonexistent", "/usr/bin", "/"]:
        env = dict(os.environ)
        env["PATH"] = val
        r = subprocess.run([str(path), "-c", "foo.c"], env=env, capture_output=True, text=True)
        print(f"PATH={val!r}")
        print(f"  returncode={r.returncode}")
        print(f"  stdout={r.stdout!r}")
        print(f"  stderr={r.stderr.strip()!r[:200]}")
PY

Repository: OnlineChefGroep/herdr

Length of output: 10719


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wrapper cc body =="
sed -n '125,200p' .github/workflows/release-portable-assets.yml

echo
echo "== deterministic subprocess/env probe =="
python3 - <<'PY'
import os
import subprocess
import stat
import tempfile
from pathlib import Path

script = "#!/usr/bin/env bash\nset -euo pipefail\nexec zig \"$@\"\n"
with tempfile.TemporaryDirectory() as tmp:
    path = Path(tmp) / "cc"
    path.write_text(script, encoding="utf-8")
    st = path.stat()
    path.chmod(st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
    for val in ["/nonexistent", "/usr/bin", "/"]:
        env = dict(os.environ)
        env["PATH"] = val
        r = subprocess.run([str(path), "-c", "foo.c"], env=env, capture_output=True, text=True)
        print(f"PATH={val!r}")
        print(f"  returncode={r.returncode}")
        print(f"  stdout={r.stdout!r}")
        stderr = next(iter(r.stderr.splitlines(), ""))
        print(f"  stderr_first_line={stderr!r}")
PY

Repository: OnlineChefGroep/herdr

Length of output: 3629


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== wrapper output probe =="
python3 - <<'PY'
import os
import subprocess
import stat
import tempfile
from pathlib import Path

script = "#!/usr/bin/env bash\nset -euo pipefail\nexec zig \"$@\"\n"
with tempfile.TemporaryDirectory() as tmp:
    path = Path(tmp) / "cc"
    path.write_text(script, encoding="utf-8")
    st = path.stat()
    path.chmod(st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
    for val in ["/nonexistent", "/usr/bin", "/"]:
        env = dict(os.environ)
        env["PATH"] = val
        r = subprocess.run([str(path), "-c", "foo.c"], env=env, capture_output=True, text=True)
        lines = r.stderr.splitlines()
        print(f"PATH={val!r}")
        print(f"  returncode={r.returncode}")
        print(f"  stdout={r.stdout!r}")
        print(f"  stderr_line_count={len(lines)}")
        print(f"  stderr_first={lines[0]!r if lines else ''}")
        print(f"  stderr_second={lines[1]!r if len(lines) > 1 else ''}")
PY

Repository: OnlineChefGroep/herdr

Length of output: 367


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import os
import subprocess
from pathlib import Path

script = "#!/usr/bin/env bash\nset -euo pipefail\nexec zig \"$@\"\n"
with tempfile.TemporaryDirectory() as tmp:
    toolchain = Path(tmp)
    wrapper = toolchain / "cc"
    wrapper.write_text(script)
    wrapper.chmod(0o755)
    for path in ["/nonexistent", "/usr/bin", "/"]:
        env = dict(os.environ)
        env["PATH"] = path
        r = subprocess.run([str(wrapper), "-c", "foo.c"], env=env, capture_output=True, text=True)
        lines = r.stderr.splitlines()
        print(f"PATH={path!r}")
        print(f"returncode={r.returncode}")
        print("stderr:")
        for line in lines[:3]:
            print(" ", line)
PY

Repository: OnlineChefGroep/herdr

Length of output: 274


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import os
import subprocess
import tempfile
from pathlib import Path

script = "#!/usr/bin/env bash\nset -euo pipefail\nexec zig \"$@\"\n"
with tempfile.TemporaryDirectory() as tmp:
    toolchain = Path(tmp)
    wrapper = toolchain / "cc"
    wrapper.write_text(script)
    wrapper.chmod(0o755)
    for path in ["/nonexistent", "/usr/bin", "/"]:
        env = dict(os.environ)
        env["PATH"] = path
        r = subprocess.run([str(wrapper), "-c", "foo.c"], env=env, capture_output=True, text=True)
        lines = r.stderr.splitlines()
        print("PATH=", repr(path))
        print("returncode=", r.returncode)
        print("stdout=", repr(r.stdout))
        print("stderr:")
        for line in lines[:3]:
            print(" ", line)
PY

Repository: OnlineChefGroep/herdr

Length of output: 464


Keep bash resolvable in the missing-zig test.

The wrapper uses #!/usr/bin/env bash, while this test sets PATH to /nonexistent. That makes /usr/bin/env fail with No such file or directory, so the wrapper body never reaches exec zig. Set PATH to existing directories that do not contain zig, such as /usr/bin:/bin, so the test exercises the wrapper’s failure path instead of interpreter resolution.

🧰 Tools
🪛 ast-grep (0.45.0)

[error] 244-249: Command coming from incoming request
Context: subprocess.run(
[str(wrapper_path), "-c", "foo.c"],
env=env,
capture_output=True,
text=True,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🪛 Ruff (0.16.0)

[error] 245-245: subprocess call: check for execution of untrusted input

(S603)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/test_release_portable_assets_workflow.py` around lines 234 - 251,
Update test_cc_wrapper_fails_closed_when_zig_is_missing to set PATH to existing
directories such as /usr/bin:/bin that do not contain zig, keeping bash
resolvable so the wrapper body reaches the missing-zig failure path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

quality-remediation CI failure needs agent remediation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants