Skip to content

CI Pipeline

CI Pipeline #596

Workflow file for this run

name: CI Pipeline
on:
push:
branches: [master]
pull_request:
branches: [master]
types: [opened, reopened, synchronize, ready_for_review]
schedule:
- cron: '14 3 * * *' # Every day at 03:14 UTC
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}-${{ github.event_name }}
cancel-in-progress: true
jobs:
test:
name: Test Workflow - Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
python-version: [3.11, 3.12, 3.13, 3.14]
permissions:
contents: read
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=test CI=true VERBOSE=true
- name: πŸ“‚ Create coverage directory
run: mkdir coverage
- name: πŸƒ Run Tests
run: make coverage CI=true VERBOSE=true
env:
COVERAGE_FILE: coverage/.coverage_${{ matrix.os }}_python_${{ matrix.python-version }}
COVERAGE_CONTEXT: ${{ matrix.os }}-python-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}
- name: πŸ«™ Store tests coverage
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
with:
name: coverage_${{ matrix.os }}_python_${{ matrix.python-version }}
path: coverage
include-hidden-files: true
retention-days: 10
combine-coverage-reports:
name: Combine Coverage Reports Workflow
needs: [test]
if: always()
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=coverage CI=true VERBOSE=true
- name: πŸ“₯ Download coverage files
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
merge-multiple: true
pattern: coverage_*
path: coverage
- name: πŸ“Š Generate coverage report
run: make coverage-report CI=true VERBOSE=true COVERAGE_HTML_TITLE="Coverage for ${{ github.sha }}"
- name: πŸ«™ Store coverage HTML report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
name: coverage-html
path: htmlcov
retention-days: 10
- name: πŸ«™ Store coverage data report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
name: coverage-data
path: .coverage
include-hidden-files: true
retention-days: 10
publish-coverage-report-commit:
name: Publish Coverage Report Commit Workflow
needs: [combine-coverage-reports]
if: needs.combine-coverage-reports.result == 'success' && github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
statuses: write
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=coverage CI=true VERBOSE=true
- name: πŸ“₯ Download coverage files
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: coverage-html
path: htmlcov
- name: πŸ“Š Upload coverage report
run: smokeshow upload htmlcov
env:
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 100
SMOKESHOW_GITHUB_CONTEXT: coverage
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.head_sha }}
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}
publish-coverage-comment-pr:
name: Publish Coverage Report Pull Request Workflow
needs: [combine-coverage-reports]
if: needs.combine-coverage-reports.result == 'success' && github.event_name == 'pull_request' && github.base_ref == 'master'
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: write
actions: read
pull-requests: write
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1000
persist-credentials: false
- name: πŸ“₯ Download coverage files
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: coverage-data
- name: πŸ“Š Generate coverage comment
id: coverage-comment
uses: py-cov-action/python-coverage-comment-action@a05be3d2e8a6272d3ef5fb2840ab20368bb2eb71 # v4.3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MINIMUM_GREEN: 100
COMMENT_ARTIFACT_NAME: coverage_comment
COMMENT_FILENAME: coverage_comment.txt
- name: πŸ«™ Store coverage comment
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: steps.coverage-comment.outputs.COMMENT_FILE_WRITTEN == 'true'
with:
name: coverage_comment
path: coverage_comment.txt
format:
name: Format Check Workflow - Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=format CI=true VERBOSE=true
- name: πŸƒ Run Formatter
run: make format CI=true VERBOSE=true
- name: 🧐 Verify no changes
run: |
CHANGES=$(git status --porcelain)
if [[ -n "${CHANGES}" ]]; then
echo "::error file=.github/workflows/ci.yaml,title=Format Check Workflow Failed::Code is not formatted correctly. Please run 'make format' locally and commit the changes."
exit 1
fi
lint:
name: Lint Workflow - Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=lint CI=true VERBOSE=true
- name: πŸƒ Run Linter
run: make lint CI=true VERBOSE=true
analyze:
name: Code Quality Workflow - ${{ matrix.language }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
language: [python]
build-mode: [none]
permissions:
contents: read
security-events: write
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: ▢️ CodeQL Initialization
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v3.29.5
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
queries: +security-extended,security-and-quality
- name: πŸ”¬ CodeQL Analysis
uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v3.29.5
with:
category: '/language:${{ matrix.language }}'
secrets:
name: Secrets Scan Workflow - Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
security-events: write
pull-requests: write
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=develop CI=true VERBOSE=true
- name: πŸƒ Run secrets scanner
run: make secrets CI=true VERBOSE=true
audit:
name: Audit Workflow - Python ${{ matrix.python-version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸƒ Run audit
run: make audit CI=true VERBOSE=true
check-all:
name: Check All Workflows Passed Workflow
needs: [test, format, lint, analyze, secrets, audit]
if: always()
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: βœ… All workflows passed
uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35 # v1.3.0
with:
jobs: ${{ toJSON(needs) }}
release:
name: Create Tag and GitHub Release Workflow
needs: [check-all]
if: needs.check-all.result == 'success' && github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
permissions:
contents: write
pull-requests: write
outputs:
released: ${{ steps.released.outputs.released }}
tag: ${{ steps.released.outputs.tag }}
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: πŸ”– Semantic Release
id: released
uses: python-semantic-release/python-semantic-release@9a026e9303981c866c3425723009becb2437c757 # v10.0.2
with:
build: false
push: true
changelog: true
commit: true
tag: true
vcs_release: true
config_file: pyproject.toml
git_committer_email: ${{ vars.GIT_COMMITTER_EMAIL }}
git_committer_name: ${{ vars.GIT_COMMITTER_NAME }}
github_token: ${{ secrets.GITHUB_TOKEN }}
ssh_public_signing_key: ${{ vars.SSH_PUBLIC_SIGNING_KEY }}
ssh_private_signing_key: ${{ secrets.SSH_PRIVATE_SIGNING_KEY }}
publish:
name: Publish Release to PyPI Workflow
needs: [release]
if: needs.release.result == 'success' && needs.release.outputs.released == 'true' && github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ${{ matrix.os }}
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: [3.14]
permissions:
contents: read
id-token: write
environment:
name: pypi
url: https://pypi.org/p/clock-pattern/
steps:
- name: πŸ›‘οΈ Harden runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
with:
egress-policy: audit
- name: πŸ“₯ Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: refs/tags/${{ needs.release.outputs.tag }}
- name: 🐍 Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: πŸ› οΈ Setup UV
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: 0.11.8
python-version: ${{ matrix.python-version }}
enable-cache: false
- name: πŸ“¦ Install dependencies
run: make install GROUP=release CI=true VERBOSE=true
- name: πŸƒπŸ»β€βž‘οΈ Build distribution
run: make build-code CI=true VERBOSE=true
- name: πŸ«™ Store distribution package
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
with:
name: python-package-distributions
path: dist/
retention-days: 10
- name: πŸš€ Publish
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2