Skip to content

Commit 52560b3

Browse files
authored
Merge pull request #62 from LeakIX/fix/ci-per-commit-pr-trigger
CI: trigger per-commit workflow on labeled PRs
2 parents 4c870ed + 265fec6 commit 52560b3

5 files changed

Lines changed: 138 additions & 45 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
# Check if the ci:per-commit label is present.
3+
#
4+
# For pull_request events, checks labels from the event payload.
5+
# For push events, looks up the originating PR via the GitHub API.
6+
#
7+
# Usage:
8+
# check-ci-per-commit-label.sh <event_name> <repository> <sha> [labels_json]
9+
#
10+
# Arguments:
11+
# event_name - "pull_request" or "push"
12+
# repository - e.g. "owner/repo"
13+
# sha - commit SHA
14+
# labels_json - JSON array of label names (required for pull_request)
15+
#
16+
# Output:
17+
# Prints has_label=true or has_label=false (for GITHUB_OUTPUT)
18+
set -euo pipefail
19+
20+
event_name="${1:?Usage: check-ci-per-commit-label.sh <event_name> <repository> <sha> [labels_json]}"
21+
repository="${2:?Missing repository}"
22+
sha="${3:?Missing sha}"
23+
labels_json="${4:-}"
24+
25+
HAS_LABEL="false"
26+
27+
if [ "$event_name" = "pull_request" ]; then
28+
if echo "$labels_json" | grep -q "ci:per-commit"; then
29+
HAS_LABEL="true"
30+
fi
31+
else
32+
PRS=$(gh api \
33+
"repos/${repository}/commits/${sha}/pulls" \
34+
--jq '.[].number')
35+
for pr in $PRS; do
36+
LABELS=$(gh api \
37+
"repos/${repository}/pulls/${pr}" \
38+
--jq '.labels[].name')
39+
if echo "$LABELS" | grep -q "^ci:per-commit$"; then
40+
HAS_LABEL="true"
41+
break
42+
fi
43+
done
44+
fi
45+
46+
echo "has_label=${HAS_LABEL}"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
# Run the full CI checks on each commit in the given range, sequentially.
3+
# Stops at the first commit that fails.
4+
#
5+
# Usage:
6+
# run-ci-per-commit.sh <base_sha> <head_sha>
7+
#
8+
# Arguments:
9+
# base_sha - the base commit (exclusive)
10+
# head_sha - the head commit (inclusive)
11+
set -euo pipefail
12+
13+
base="${1:?Usage: run-ci-per-commit.sh <base_sha> <head_sha>}"
14+
head="${2:?Missing head_sha}"
15+
16+
commits=$(git rev-list --reverse "${base}..${head}")
17+
total=$(echo "$commits" | wc -l | tr -d ' ')
18+
current=0
19+
20+
for commit in $commits; do
21+
current=$((current + 1))
22+
short=$(git rev-parse --short "$commit")
23+
subject=$(git log -1 --format=%s "$commit")
24+
echo ""
25+
echo "=== [$current/$total] Testing ${short}: ${subject} ==="
26+
echo ""
27+
28+
git checkout --quiet "$commit"
29+
make install
30+
make lint
31+
make check-format
32+
make check-sort
33+
make typecheck
34+
make test
35+
36+
echo ""
37+
echo "=== [$current/$total] PASSED: ${short} ==="
38+
done
39+
40+
echo ""
41+
echo "All ${total} commit(s) passed CI checks."

.github/workflows/action.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,21 @@ jobs:
1515
uses: astral-sh/setup-uv@v6
1616
with:
1717
python-version: ${{ matrix.python-version }}
18+
- name: Install gsed (macOS)
19+
if: runner.os == 'macOS'
20+
run: brew install gnu-sed
1821
- name: Install dependencies
19-
run: uv sync
22+
run: make install
2023
- name: lint
21-
run: uv run ruff check .
24+
run: make lint
2225
- name: format check
23-
run: uv run black --check .
26+
run: make check-format
2427
- name: sort-check
25-
run: uv run isort --check-only .
28+
run: make check-sort
2629
- name: typecheck
27-
run: uv run mypy l9format
30+
run: make typecheck
2831
- name: test
29-
run: uv run pytest
32+
run: make test
3033
- name: security-audit
31-
run: uv run pip-audit
34+
run: make security-check
3235

.github/workflows/ci-per-commit.yaml

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
push:
55
branches:
66
- main
7+
pull_request:
8+
types: [labeled]
9+
branches:
10+
- main
711

812
jobs:
913
check-label:
@@ -12,53 +16,48 @@ jobs:
1216
outputs:
1317
has_label: ${{ steps.check.outputs.has_label }}
1418
steps:
15-
- name: Find associated PR and check label
19+
- uses: actions/checkout@v6
20+
- name: Check for label
1621
id: check
1722
env:
1823
GH_TOKEN: ${{ github.token }}
1924
run: |
20-
PRS=$(gh api \
21-
"repos/${{ github.repository }}/commits/${{ github.sha }}/pulls" \
22-
--jq '.[].number')
23-
HAS_LABEL="false"
24-
for pr in $PRS; do
25-
LABELS=$(gh api \
26-
"repos/${{ github.repository }}/pulls/${pr}" \
27-
--jq '.labels[].name')
28-
if echo "$LABELS" | grep -q "^ci:per-commit$"; then
29-
HAS_LABEL="true"
30-
break
31-
fi
32-
done
33-
echo "has_label=${HAS_LABEL}" >> "$GITHUB_OUTPUT"
25+
.github/scripts/check-ci-per-commit-label.sh \
26+
"${{ github.event_name }}" \
27+
"${{ github.repository }}" \
28+
"${{ github.sha }}" \
29+
'${{ toJSON(github.event.pull_request.labels.*.name) }}' \
30+
>> "$GITHUB_OUTPUT"
3431
35-
ci:
36-
name: CI
32+
ci-per-commit:
33+
name: CI per commit
3734
needs: check-label
3835
if: needs.check-label.outputs.has_label == 'true'
39-
strategy:
40-
fail-fast: false
41-
matrix:
42-
python-version: ["3.11", "3.12", "3.13"]
43-
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
44-
runs-on: ${{ matrix.os }}
36+
runs-on: ubuntu-latest
4537
steps:
4638
- uses: actions/checkout@v6
39+
with:
40+
fetch-depth: 0
4741
- name: Install uv
4842
uses: astral-sh/setup-uv@v6
4943
with:
50-
python-version: ${{ matrix.python-version }}
51-
- name: Install dependencies
52-
run: uv sync
53-
- name: lint
54-
run: uv run ruff check .
55-
- name: format check
56-
run: uv run black --check .
57-
- name: sort-check
58-
run: uv run isort --check-only .
59-
- name: typecheck
60-
run: uv run mypy l9format
61-
- name: test
62-
run: uv run pytest
63-
- name: security-audit
64-
run: uv run pip-audit
44+
python-version: "3.13"
45+
- name: Determine commit range
46+
id: range
47+
env:
48+
GH_TOKEN: ${{ github.token }}
49+
run: |
50+
if [ "${{ github.event_name }}" = "pull_request" ]; then
51+
BASE="${{ github.event.pull_request.base.sha }}"
52+
HEAD="${{ github.event.pull_request.head.sha }}"
53+
else
54+
BASE="${{ github.event.before }}"
55+
HEAD="${{ github.sha }}"
56+
fi
57+
echo "base=${BASE}" >> "$GITHUB_OUTPUT"
58+
echo "head=${HEAD}" >> "$GITHUB_OUTPUT"
59+
- name: Run CI on each commit
60+
run: |
61+
.github/scripts/run-ci-per-commit.sh \
62+
"${{ steps.range.outputs.base }}" \
63+
"${{ steps.range.outputs.head }}"

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ and this project adheres to
1919
and switch CI to `astral-sh/setup-uv` ([5f4fc51], [#56])
2020
- CI: add per-commit workflow triggered by `ci:per-commit` label for easier
2121
bisect and revert ([bc4872d], [#59])
22+
- CI: use Makefile targets in workflows and add sequential per-commit testing
23+
([fa582dc], [#62])
2224

2325
## [1.4.0] - 2026-02-09
2426

@@ -165,6 +167,7 @@ and this project adheres to
165167

166168
<!-- Commit links -->
167169

170+
[fa582dc]: https://github.com/LeakIX/l9format-python/commit/fa582dc
168171
[bc4872d]: https://github.com/LeakIX/l9format-python/commit/bc4872d
169172
[6c9eecd]: https://github.com/LeakIX/l9format-python/commit/6c9eecd
170173
[5f4fc51]: https://github.com/LeakIX/l9format-python/commit/5f4fc51
@@ -258,4 +261,5 @@ and this project adheres to
258261
[#51]: https://github.com/LeakIX/l9format-python/pull/51
259262
[#56]: https://github.com/LeakIX/l9format-python/pull/56
260263
[#59]: https://github.com/LeakIX/l9format-python/issues/59
264+
[#62]: https://github.com/LeakIX/l9format-python/pull/62
261265
[#43]: https://github.com/LeakIX/l9format-python/issues/43

0 commit comments

Comments
 (0)