diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7e8566a --- /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 headersvalidator/ + + - 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 diff --git a/CLAUDE.md b/CLAUDE.md index 3f31e5e..20b4613 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -72,7 +72,8 @@ Score = `round(earned / (graded_count * 2) * 100)`. - Conventional commits: `fix:`, `feat:`, `fix(scope):`, `refactor:`, `test:`, `docs:` - Input validation lives in `cli.py` (URL normalisation delegated to `normalise_url()` in `http_utils.py`) - `fetch_headers()` from `http_utils` is the single I/O abstraction; patch it in tests via `monkeypatch` -- 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 diff --git a/pyproject.toml b/pyproject.toml index bf31e85..d83b72c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,17 @@ where = ["."] include = ["headersvalidator*"] [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 = ["."]