Skip to content

.github/workflows/main.yml #4

.github/workflows/main.yml

.github/workflows/main.yml #4

Workflow file for this run

name: Main
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
workflow_dispatch:
jobs:
quality:
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Set up the environment
uses: ./.github/actions/setup-python-env
- name: Run checks
run: make check
tests:
runs-on: ${{ matrix.platform }}
strategy:
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
# Must track requires-python in pyproject.toml. An entry below the
# floor does not fail: uv ignores it and resolves a compatible
# interpreter instead, so the job reports a false pass.
python-version: ["3.11", "3.12", "3.13", "3.14"]
fail-fast: false
defaults:
run:
shell: bash
steps:
- name: Check out
uses: actions/checkout@v4
- name: Set up the environment
uses: ./.github/actions/setup-python-env
with:
python-version: ${{ matrix.python-version }}
- name: Run tests
run: uv run python -m pytest tests --cov --cov-config=pyproject.toml --cov-report=xml
- name: Check typing
run: uv run ty check
- name: Upload coverage reports to Codecov on Ubuntu / Python 3.12
uses: codecov/codecov-action@v4
if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }}
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
check-docs:
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4
- name: Set up the environment
uses: ./.github/actions/setup-python-env
- name: Check if documentation can be built
run: make docs-test
version-preview:
# Show contributors what a merge would release, based on the conventional
# commits on the branch. Pull requests only; informational.
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Check out
uses: actions/checkout@v4
with:
fetch-depth: 0 # PSR needs full history + tags to compute the bump
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up the environment
uses: ./.github/actions/setup-python-env
- name: Compute release preview
id: preview
run: |
# PSR only computes a release on the main branch; name the checked-out
# PR head 'main' so the preview reflects what a merge would release.
git checkout -B main >/dev/null 2>&1 || true
current=$(uv version --short 2>/dev/null || echo "unknown")
next=$(uv run semantic-release version --print 2>/dev/null || echo "")
if [ -n "$next" ] && [ "$next" != "$current" ]; then
echo "released=true" >> "$GITHUB_OUTPUT"
else
echo "released=false" >> "$GITHUB_OUTPUT"
fi
echo "current=$current" >> "$GITHUB_OUTPUT"
echo "next=$next" >> "$GITHUB_OUTPUT"
# Stamp the upcoming changelog section locally (no commit/tag/push/build)
# so the preview shows the notes this merge would add.
if [ -n "$next" ] && [ "$next" != "$current" ]; then
uv run semantic-release version --no-commit --no-tag --no-push --no-vcs-release --skip-build >/dev/null 2>&1 || true
fi
{
echo "changelog<<PSR_EOF"
git --no-pager diff HEAD -- CHANGELOG.md | awk '/^\+/ && !/^\+\+\+/ {sub(/^\+/,""); print}' | head -80
echo "PSR_EOF"
} >> "$GITHUB_OUTPUT"
- name: Post release preview comment
uses: marocchino/sticky-pull-request-comment@v2
with:
header: version-preview
message: |
## Release preview
${{ steps.preview.outputs.released == 'true' && format('Merging this PR would release **v{0}** (current: `v{1}`).', steps.preview.outputs.next, steps.preview.outputs.current) || format('No version bump from the current commits (stays at `v{0}`). Use conventional commit types (`feat`, `fix`, ...) to trigger a release.', steps.preview.outputs.current) }}
<details><summary>Changelog preview (truncated)</summary>
```markdown
${{ steps.preview.outputs.changelog }}
```
</details>
<sub>Preview via python-semantic-release and conventional commits.</sub>
# Continuous benchmarking with Bencher: pytest-benchmark produces the JSON,
# `bencher run` uploads it and comments the comparison against the base
# branch, using statistical boundary models instead of a fixed ratio.
#
# Setup: create a project at https://bencher.dev (free for public repos),
# then add the BENCHER_API_TOKEN secret and set BENCHER_PROJECT below. Until
# the secret exists the benchmarks still run and upload their results as an
# artifact; only the tracking step is skipped.
benchmark:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
permissions:
contents: read
pull-requests: write
env:
BENCHER_PROJECT: awl-python
# The secrets context is not available in a step-level `if`, so the token
# is promoted to an env var here and the steps gate on that instead.
BENCHER_API_TOKEN: ${{ secrets.BENCHER_API_TOKEN }}
steps:
- name: Check out
uses: actions/checkout@v4
- name: Set up the environment
uses: ./.github/actions/setup-python-env
- name: Run benchmarks
run: make bench
- name: Track benchmarks with Bencher
if: env.BENCHER_API_TOKEN != ''
uses: bencherdev/bencher@main
- name: Upload results to Bencher
if: env.BENCHER_API_TOKEN != ''
run: |
bencher run \
--project "$BENCHER_PROJECT" \
--token "$BENCHER_API_TOKEN" \
--branch "$GITHUB_HEAD_REF" \
--start-point "$GITHUB_BASE_REF" \
--start-point-hash "${{ github.event.pull_request.base.sha }}" \
--start-point-clone-thresholds \
--start-point-reset \
--testbed ubuntu-latest \
--adapter python_pytest \
--err \
--github-actions "${{ secrets.GITHUB_TOKEN }}" \
--file benchmark_results.json
- name: Upload benchmark results
uses: actions/upload-artifact@v4
if: always()
with:
name: benchmark-results
path: benchmark_results.json
retention-days: 30