diff --git a/.github/scripts/check-ci-per-commit-label.sh b/.github/scripts/check-ci-per-commit-label.sh new file mode 100755 index 0000000..8521d34 --- /dev/null +++ b/.github/scripts/check-ci-per-commit-label.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Check if the ci:per-commit label is present. +# +# For pull_request events, checks labels from the event payload. +# For push events, looks up the originating PR via the GitHub API. +# +# Usage: +# check-ci-per-commit-label.sh [labels_json] +# +# Arguments: +# event_name - "pull_request" or "push" +# repository - e.g. "owner/repo" +# sha - commit SHA +# labels_json - JSON array of label names (required for pull_request) +# +# Output: +# Prints has_label=true or has_label=false (for GITHUB_OUTPUT) +set -euo pipefail + +event_name="${1:?Usage: check-ci-per-commit-label.sh [labels_json]}" +repository="${2:?Missing repository}" +sha="${3:?Missing sha}" +labels_json="${4:-}" + +HAS_LABEL="false" + +if [ "$event_name" = "pull_request" ]; then + if echo "$labels_json" | grep -q "ci:per-commit"; then + HAS_LABEL="true" + fi +else + PRS=$(gh api \ + "repos/${repository}/commits/${sha}/pulls" \ + --jq '.[].number') + for pr in $PRS; do + LABELS=$(gh api \ + "repos/${repository}/pulls/${pr}" \ + --jq '.labels[].name') + if echo "$LABELS" | grep -q "^ci:per-commit$"; then + HAS_LABEL="true" + break + fi + done +fi + +echo "has_label=${HAS_LABEL}" diff --git a/.github/scripts/run-ci-per-commit.sh b/.github/scripts/run-ci-per-commit.sh new file mode 100755 index 0000000..8a5a10c --- /dev/null +++ b/.github/scripts/run-ci-per-commit.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Run the full CI checks on each commit in the given range, sequentially. +# Stops at the first commit that fails. +# +# Usage: +# run-ci-per-commit.sh +# +# Arguments: +# base_sha - the base commit (exclusive) +# head_sha - the head commit (inclusive) +set -euo pipefail + +base="${1:?Usage: run-ci-per-commit.sh }" +head="${2:?Missing head_sha}" + +commits=$(git rev-list --reverse "${base}..${head}") +total=$(echo "$commits" | wc -l | tr -d ' ') +current=0 + +for commit in $commits; do + current=$((current + 1)) + short=$(git rev-parse --short "$commit") + subject=$(git log -1 --format=%s "$commit") + echo "" + echo "=== [$current/$total] Testing ${short}: ${subject} ===" + echo "" + + git checkout --quiet "$commit" + make install + make lint + make check-format + make test + + echo "" + echo "=== [$current/$total] PASSED: ${short} ===" +done + +echo "" +echo "All ${total} commit(s) passed CI checks." diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 009984b..4972f79 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -46,12 +46,12 @@ jobs: - name: Install poetry run: pip install poetry==${{ matrix.poetry-version}} - name: Install deps - run: poetry install + run: make install - name: Run tests - run: poetry run pytest tests + run: make test - name: Check formatting - run: poetry run ruff format --check leakix/ tests/ example/ executable/ + run: make check-format - name: Run linter - run: poetry run ruff check leakix/ tests/ example/ executable/ + run: make lint - name: Security audit - run: poetry run pip-audit + run: make audit diff --git a/.github/workflows/ci-per-commit.yaml b/.github/workflows/ci-per-commit.yaml new file mode 100644 index 0000000..353c2f5 --- /dev/null +++ b/.github/workflows/ci-per-commit.yaml @@ -0,0 +1,66 @@ +name: CI per commit +on: + push: + branches: + - main + pull_request: + types: [labeled] + branches: + - main + +jobs: + check-label: + name: Check for ci:per-commit label + runs-on: ubuntu-latest + outputs: + has_label: ${{ steps.check.outputs.has_label }} + steps: + - uses: actions/checkout@v6 + - name: Check for label + id: check + env: + GH_TOKEN: ${{ github.token }} + run: | + .github/scripts/check-ci-per-commit-label.sh \ + "${{ github.event_name }}" \ + "${{ github.repository }}" \ + "${{ github.sha }}" \ + '${{ toJSON(github.event.pull_request.labels.*.name) }}' \ + >> "$GITHUB_OUTPUT" + + ci-per-commit: + name: CI per commit + needs: check-label + if: needs.check-label.outputs.has_label == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: actions/setup-python@v6 + with: + python-version: "3.13" + - name: Install GNU sed (macOS) + if: runner.os == 'macOS' + run: brew install gnu-sed + - name: Install poetry + run: pip install poetry==2.3.1 + - name: Determine commit range + id: range + env: + GH_TOKEN: ${{ github.token }} + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE="${{ github.event.pull_request.base.sha }}" + HEAD="${{ github.event.pull_request.head.sha }}" + else + BASE="${{ github.event.before }}" + HEAD="${{ github.sha }}" + fi + echo "base=${BASE}" >> "$GITHUB_OUTPUT" + echo "head=${HEAD}" >> "$GITHUB_OUTPUT" + - name: Run CI on each commit + run: | + .github/scripts/run-ci-per-commit.sh \ + "${{ steps.range.outputs.base }}" \ + "${{ steps.range.outputs.head }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ecfed6..9fddbc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to - Updated l9format requirement from =1.3.2 to =1.4.0 ([ae676d9]) +### Infrastructure + +- CI: use Makefile targets in Windows job and add sequential per-commit + testing triggered by the ci:per-commit label ([3967e42], [#66]) + ## [0.1.10] - 2024-12-XX ### Changed @@ -47,6 +52,7 @@ and this project adheres to [0.1.9]: https://github.com/LeakIX/LeakIXClient-Python/releases/tag/v0.1.9 +[3967e42]: https://github.com/LeakIX/LeakIXClient-Python/commit/3967e42 [ae676d9]: https://github.com/LeakIX/LeakIXClient-Python/commit/ae676d9 [65c5121]: https://github.com/LeakIX/LeakIXClient-Python/commit/65c5121 [0975c1c]: https://github.com/LeakIX/LeakIXClient-Python/commit/0975c1c @@ -55,3 +61,6 @@ and this project adheres to [6777ad9]: https://github.com/LeakIX/LeakIXClient-Python/commit/6777ad9 [62550bc]: https://github.com/LeakIX/LeakIXClient-Python/commit/62550bc [4dd4948]: https://github.com/LeakIX/LeakIXClient-Python/commit/4dd4948 + + +[#66]: https://github.com/LeakIX/LeakIXClient-Python/pull/66