From e0275189ce0d5f2c187ac07f56eb367f6e6bdf38 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Wed, 12 Aug 2026 14:48:06 +0200 Subject: [PATCH 1/4] ci: add GitHub Actions workflow Runs lint and the test suite on push and pull request to main, across Python 3.11-3.13 (the range pyproject declares via requires-python). Hardening applied per GitHub's own recommendations: - actions pinned to full commit SHAs, not floating tags - permissions: contents: read (the default token grant is far wider) - persist-credentials: false, so no token is left in .git/config - concurrency group cancels superseded runs - fail-fast: false, so one interpreter failing does not mask the rest - timeout-minutes, so a hung job cannot occupy a runner indefinitely --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..dc8fd9a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +# A new push supersedes the previous run on the same ref. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +# Least privilege: this workflow only ever reads the repository. +permissions: + contents: read + +jobs: + test: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + # One failing interpreter must not mask the others. + fail-fast: false + matrix: + # pyproject declares requires-python >= 3.11, so every supported + # interpreter is exercised. + python-version: ["3.11", "3.12", "3.13"] + + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + # No credential is needed after checkout; leaving one in .git/config + # would expose it to anything later in the job. + persist-credentials: false + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.python-version }} + cache: pip + cache-dependency-path: pyproject.toml + + - name: Install (editable, with dev extras) + run: python -m pip install -e ".[dev]" + + - name: Lint + run: ruff check quantumvalidator/ + + - name: Test + # Network I/O is isolated in the *_utils modules and mocked there, so + # the suite needs neither a live network nor any external binary. + run: pytest --tb=short -q From 8842f7d5de564389644be9c4d2bdb39d3da5e448 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Wed, 12 Aug 2026 15:00:50 +0200 Subject: [PATCH 2/4] ci: declare the ruff rule set and provision ruff in dev extras MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first CI run failed on every module, in two ways tracing to one root cause: lint was never actually pinned down. 1. 'ruff: command not found' — ruff is required by the before-commit checklist but was absent from the dev extras, so pip install -e '.[dev]' never provided it. It passed locally only because developer venvs had it installed by hand. 2. Lint failures on a clean checkout — with no [tool.ruff] section the linted rule set is whatever the installed ruff defaults to, and that widens each release, so a green local run and a red CI run could disagree purely by version. Copies the pattern portscanner already used (the only module that passed): pin target-version, state select = [E4, E7, E9, F] explicitly, add ruff>=0.6 to dev. No source change needed — every module passes this rule set as-is. --- pyproject.toml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 10e5b60..1dc221c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,17 @@ where = ["."] include = ["quantumvalidator*"] [project.optional-dependencies] -dev = ["pytest>=8", "pytest-cov>=5", "pytest-mock>=3.12"] +dev = ["pytest>=8", "pytest-cov>=5", "ruff>=0.6", "pytest-mock>=3.12"] + +[tool.ruff] +target-version = "py311" + +[tool.ruff.lint] +# Ruff's default rule set as of 0.6, made explicit. Without this the linted +# rule set is whatever the installed ruff defaults to, which widens on every +# release — so a green local run and a red CI run can disagree purely by +# version. Pinning it here makes the bar reviewable and stable. +select = ["E4", "E7", "E9", "F"] [tool.pytest.ini_options] pythonpath = ["."] From e2e8a5dda80075caff753632877edb136e1caf32 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Wed, 12 Aug 2026 15:06:28 +0200 Subject: [PATCH 3/4] docs: CLAUDE.md no longer claims CI is absent Review caught that adding the workflow left the repository guide stating 'No CI config currently present', giving contributors contradictory guidance about whether pushes are checked. --- CLAUDE.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8f45b6c..60f1962 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,7 +87,8 @@ Two mock targets in tests: - `assess()` in `assessor.py` is the **sole public entry point** for library users - `openssl` binary is a hard external dependency; missing → `RuntimeError` raised inside `probe_tls()` - OSErrors from subprocess are caught in `probe_tls()` and returned as `TLSProbeResult(error=...)` — they never propagate to the CLI -- No CI config currently present +- CI: `.github/workflows/ci.yml` runs `ruff check` and the full suite on + push and PR to `main`, across Python 3.11-3.13 ## Before Every Commit From cc67214c391df5fc6896a2b1b25d791e09cfcb10 Mon Sep 17 00:00:00 2001 From: t0kubetsu Date: Wed, 12 Aug 2026 15:53:29 +0200 Subject: [PATCH 4/4] ci: pin ruff to a tested major Review flagged that ruff>=0.6 with no upper bound lets a future release change lint behaviour without any repository change, since CI installs '.[dev]' with no lock file. This is not hypothetical here: the first CI run of this workflow failed precisely because current ruff widened its defaults beyond what the developer venvs had. The explicit [tool.ruff] select pins which rules run, which is the larger half of the fix; this bounds the remaining drift (rule behaviour, new fixes, interpreter support) to a tested major. Dependabot's github-actions and pip ecosystems will raise it. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1dc221c..49b36c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ where = ["."] include = ["quantumvalidator*"] [project.optional-dependencies] -dev = ["pytest>=8", "pytest-cov>=5", "ruff>=0.6", "pytest-mock>=3.12"] +dev = ["pytest>=8", "pytest-cov>=5", "ruff>=0.6,<0.17", "pytest-mock>=3.12"] [tool.ruff] target-version = "py311"