From d36f2a28293b011a100f640cdf7a46aa93588229 Mon Sep 17 00:00:00 2001 From: overtrue Date: Sat, 5 Sep 2026 19:24:06 +0800 Subject: [PATCH] fix(ci): run existing script contracts in quick checks --- .github/actions/quick-checks/action.yml | 10 +++++----- scripts/check_test_wiring.py | 14 +++++++++----- scripts/test_python_bin.sh | 22 ++++++++-------------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/.github/actions/quick-checks/action.yml b/.github/actions/quick-checks/action.yml index e6cc59775..660df0b1d 100644 --- a/.github/actions/quick-checks/action.yml +++ b/.github/actions/quick-checks/action.yml @@ -94,13 +94,13 @@ runs: shell: bash run: ./scripts/check_embedded_secrets.sh + - name: Run script contract tests + shell: bash + run: make script-tests + - name: Check test wiring shell: bash - run: | - python3 ./scripts/check_test_wiring.py --self-test - python3 ./scripts/check_scheduled_validation_freshness.py --self-test - python3 ./scripts/test_security_workflow.py - python3 ./scripts/check_test_wiring.py + run: python3 ./scripts/check_test_wiring.py - name: Check no planning docs committed shell: bash diff --git a/scripts/check_test_wiring.py b/scripts/check_test_wiring.py index 63de463d3..ef3f15107 100755 --- a/scripts/check_test_wiring.py +++ b/scripts/check_test_wiring.py @@ -567,7 +567,7 @@ def check_quick_checks(root: Path) -> list[str]: errors.append(f"{relative}: missing composite action") return errors steps = yaml_block(runs, "steps", 2) or [] - for command in ("shellcheck --version && actionlint", "./scripts/check_error_other_format_ratchet.sh"): + for command in ("shellcheck --version && actionlint", "./scripts/check_error_other_format_ratchet.sh", "make script-tests"): step = workflow_step_block(steps, command, key="run", indent=4) if step is None: errors.append(f"{relative}: missing direct execution of {command}") @@ -906,6 +906,7 @@ class SelfTests(unittest.TestCase): " - name: Lint workflows\n shell: bash\n run: shellcheck --version && actionlint\n" " - name: Error format ratchet\n shell: bash\n" " run: ./scripts/check_error_other_format_ratchet.sh\n" + " - name: Script tests\n shell: bash\n run: make script-tests\n" ) sources = { ".github/workflows/ci.yml": caller.replace( @@ -964,6 +965,8 @@ class SelfTests(unittest.TestCase): "only installed actionlint": action.replace("run: shellcheck --version && actionlint", "run: echo actionlint"), "missing shellcheck preflight": action.replace("shellcheck --version && ", ""), "missing ratchet": action.replace("run: ./scripts/check_error_other_format_ratchet.sh", "run: echo skipped"), + "missing script tests": action.replace("run: make script-tests", "run: echo skipped"), + "swallowed script failure": action.replace("run: make script-tests", "run: make script-tests || true"), "swallowed lint failure": action.replace("&& actionlint", "&& actionlint || true"), "swallowed ratchet failure": action.replace("ratchet.sh", "ratchet.sh || true"), "conditional lint": action.replace("run: shellcheck", "if: false\n run: shellcheck"), @@ -973,7 +976,7 @@ class SelfTests(unittest.TestCase): "name: Lint workflows", "name: |\n run: shellcheck --version && actionlint" ).replace("\n run: shellcheck --version && actionlint\n", "\n run: shellcheck --version && actionlint\n || true\n"), } - for command in ("shellcheck --version && actionlint", "./scripts/check_error_other_format_ratchet.sh"): + for command in ("shellcheck --version && actionlint", "./scripts/check_error_other_format_ratchet.sh", "make script-tests"): for key in ("'if' : false", '"if": false', "'continue-on-error': true", '"continue-on-error" : true'): mutations[f"quoted {command} {key}"] = action.replace(f"run: {command}", f"{key}\n run: {command}") for separator in ("", "\n", " # continued command\n"): @@ -994,9 +997,10 @@ class SelfTests(unittest.TestCase): root = Path(tmp) (root / "scripts").mkdir() commands = ("shellcheck", "actionlint", "./scripts/check_error_other_format_ratchet.sh") - for failing in commands: + (root / "Makefile").write_text(".PHONY: script-tests\nscript-tests:\n\texit 17\n") + for failing in (*commands, "make script-tests"): with self.subTest(command=failing): - run = "shellcheck --version && actionlint" if failing != commands[-1] else failing + run = "shellcheck --version && actionlint" if failing in ("shellcheck", "actionlint") else failing step = workflow_step_block(steps, run, key="run", indent=4) self.assertIsNotNone(step) run_index = next(index for index, line in enumerate(step[1]) if line.startswith(" run:")) @@ -1011,7 +1015,7 @@ class SelfTests(unittest.TestCase): cwd=root, env=dict(os.environ, PATH=f"{root}{os.pathsep}{os.environ['PATH']}"), capture_output=True, text=True, ) - self.assertEqual(result.returncode, 17, result.stderr) + self.assertEqual(result.returncode, 2 if failing == "make script-tests" else 17, result.stderr) def test_validate_includes_quick_checks(self) -> None: error = "Quick Checks wiring regression" diff --git a/scripts/test_python_bin.sh b/scripts/test_python_bin.sh index 4c5e149fb..927412fff 100755 --- a/scripts/test_python_bin.sh +++ b/scripts/test_python_bin.sh @@ -62,20 +62,14 @@ exit 1 STUB chmod +x "$TMP_ROOT/bin/python3" -SANDBOX_PATH="$TMP_ROOT/bin:/usr/bin:/bin" -if PATH="$SANDBOX_PATH" command -v uv >/dev/null 2>&1; then - # uv is reachable even from the sandbox PATH, so the resolver would - # legitimately fall back to it instead of failing. Skip this case. - echo "ℹ️ uv is on the sandbox PATH; skipping the no-interpreter case" -else - if PATH="$SANDBOX_PATH" "$RESOLVER" -c 'pass' \ - >"$TMP_ROOT/none.out" 2>"$TMP_ROOT/none.err"; then - fail "resolver succeeded with no usable interpreter on PATH" - fi - grep -q 'No Python 3.11+ interpreter found' "$TMP_ROOT/none.err" \ - || fail "missing-interpreter failure did not name the requirement" - grep -q 'RUSTFS_PYTHON=' "$TMP_ROOT/none.err" \ - || fail "missing-interpreter failure did not point at the override" +ln -s "$(command -v bash)" "$TMP_ROOT/bin/bash" +if PATH="$TMP_ROOT/bin" RUSTFS_PYTHON="" "$RESOLVER" -c 'pass' \ + >"$TMP_ROOT/none.out" 2>"$TMP_ROOT/none.err"; then + fail "resolver succeeded with no usable interpreter on PATH" fi +grep -q 'No Python 3.11+ interpreter found' "$TMP_ROOT/none.err" \ + || fail "missing-interpreter failure did not name the requirement" +grep -q 'RUSTFS_PYTHON=' "$TMP_ROOT/none.err" \ + || fail "missing-interpreter failure did not point at the override" echo "✅ scripts/python_bin.sh resolver checks passed"