name: CI/CD

on:
  merge_group:
  pull_request:
    branches:
      - dev
      - main
      - v1
  push:
    branches:
      - dev
      - main
      - v1
    tags:
      - 'v*'
  workflow_dispatch:
  workflow_run:
    workflows: ['Update pre-commit hooks']
    branches:
      - update-pre-commit-hooks
    types:
      - completed

env:
  FORCE_COLOR: 1

concurrency:
  cancel-in-progress: ${{ !contains(fromJSON('["dev", "main", "v1"]'), github.ref_name) }}
  group: ${{ github.repository }}-${{ github.workflow }}-${{ github.head_ref || github.ref_name }}

jobs:
  pre-commit:
    name: Run pre-commit
    runs-on: ubuntu-24.04
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

      - name: Install uv
        uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b
        with:
          python-version: '3.14'
          version: '0.9.16'

      - name: Install Python
        run: uv python install "$UV_PYTHON"

      - name: Run pre-commit
        run: uv tool run --with pre-commit-uv pre-commit run --show-diff-on-failure --color=always --all-files

  code-ql:
    name: CodeQL
    needs:
      - pre-commit
    permissions:
      security-events: write
    runs-on: ubuntu-24.04
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

      - name: Initialize CodeQL
        uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30
        with:
          languages: python

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30
        with:
          category: '/language:python'

  test:
    name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    needs:
      - pre-commit
    strategy:
      matrix:
        os:
          - macos-latest
          - ubuntu-latest
          - windows-latest
        python-version:
          - '3.10'
          - '3.11'
          - '3.12'
          - '3.13'
          - '3.14'
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

      - name: Install uv
        uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b
        with:
          python-version: ${{ matrix.python-version }}
          version: '0.9.16'

      - name: Install Python ${{ matrix.python-version }}
        run: uv python install ${{ matrix.python-version }}

      - name: Install dependencies
        run: uv sync --frozen --no-dev --group tests --link-mode=copy

      - name: Run tests
        run: make test

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  build:
    name: Build distribution
    needs: test
    runs-on: ubuntu-24.04
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

      - name: Install uv
        uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b
        with:
          python-version: '3.14'
          version: '0.9.16'

      - name: Install Python
        run: uv python install "$UV_PYTHON"

      - name: Install dependencies
        run: uv sync --frozen --no-default-groups --no-install-project --group build --link-mode=copy

      - name: Build distribution
        run: make package

      - name: Upload package artifacts
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
        with:
          name: dist
          path: dist

      - name: Set version
        if: startsWith(github.event.ref, 'refs/tags/v')
        run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/^v//')" >> $GITHUB_ENV

      - name: Generate SBOM
        if: startsWith(github.event.ref, 'refs/tags/v')
        run: make sbom > holidays-${{ env.VERSION }}-sbom.json

      - name: Upload SBOM
        if: startsWith(github.event.ref, 'refs/tags/v')
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
        with:
          name: sbom
          path: holidays-${{ env.VERSION }}-sbom.json

  test-build:
    name: Test build on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    needs: build
    strategy:
      matrix:
        os:
          - macos-latest
          - ubuntu-latest
          - windows-latest
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

      - name: Install uv
        uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b
        with:
          python-version: '3.14'
          version: '0.9.16'

      - name: Install Python
        shell: bash
        run: uv python install "$UV_PYTHON"

      - name: Get package artifacts
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          name: dist
          path: dist

      - name: Run tests
        shell: bash
        run: |
          uv sync --frozen --no-default-groups --no-install-project --group tests --link-mode=copy
          rm -rf holidays
          uv pip install dist/*.whl --link-mode=copy
          uv run --no-sync pytest --dist loadscope --numprocesses auto tests/countries tests/financial
          uv pip uninstall holidays
          uv pip install dist/*.tar.gz --link-mode=copy
          uv run --no-sync pytest --dist loadscope --numprocesses auto tests/countries tests/financial

  test-docs:
    name: Test docs build
    runs-on: ubuntu-24.04
    needs: test
    steps:
      - name: Check out repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd

      - name: Install uv
        uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b
        with:
          python-version: '3.14'
          version: '0.9.16'

      - name: Install Python
        run: uv python install "$UV_PYTHON"

      - name: Install dependencies
        run: uv sync --frozen --no-default-groups --no-install-project --group docs --link-mode=copy

      - name: Build docs
        run: make doc

  publish-main:
    name: Publish generated artifacts
    if: |
      github.repository == 'vacanza/holidays' &&
      github.event_name == 'push' &&
      startsWith(github.event.ref, 'refs/tags/v')
    environment: main
    needs:
      - test-build
      - test-docs
    permissions:
      contents: write
      id-token: write
    runs-on: ubuntu-24.04
    steps:
      - name: Download package artifacts
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          name: dist
          path: dist

      - name: Publish package distributions to PyPI
        uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e

  sign-artifacts:
    name: Create SHA1 checksums and Sigstore signatures
    runs-on: ubuntu-24.04
    needs:
      - publish-main
    permissions:
      id-token: write
    steps:
      - name: Download package artifacts
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          name: dist
          path: dist

      - name: Compute SHA1 checksums
        run: |
          cd dist
          for file in *; do
            sha1sum "$file" > "$file.sha1"
          done

      - name: Sign the files using Sigstore
        uses: sigstore/gh-action-sigstore-python@a5caf349bc536fbef3668a10ed7f5cd309a4b53d
        with:
          inputs: |
            ./dist/*.tar.gz
            ./dist/*.whl

      - name: Upload package dist and signatures
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
        with:
          name: signed-artifacts
          path: dist

  update-github-release:
    name: Update GitHub release with SBOM and signed artifacts
    runs-on: ubuntu-24.04
    needs:
      - sign-artifacts
    permissions:
      contents: write
    steps:
      - name: Download SBOM
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          name: sbom

      - name: Download package dist and signatures
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          name: signed-artifacts
          path: dist

      - name: Update Github release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload --repo vacanza/holidays ${{ github.ref_name }} dist/*
          gh release upload --repo vacanza/holidays ${{ github.ref_name }} holidays-*-sbom.json
