name: CI
on:
  pull_request:
  push:
    branches:
      - main
  workflow_call:
    inputs:
      attest-package:
        description: "Create GitHub provenance attestation for the package."
        default: "false"
        type: string
    outputs:
      artifact-name:
        description: "Name of the uploaded artifact; use for artifact retrieval."
        value: ${{ jobs.package.outputs.artifact-name }}

# Cancel active CI runs for a PR before starting another run
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

defaults:
  run:
    shell: bash  # https://github.com/beeware/briefcase/pull/912

env:
  FORCE_COLOR: "1"

jobs:
  pre-commit:
    name: Pre-commit checks
    uses: beeware/.github/.github/workflows/pre-commit-run.yml@main

  towncrier:
    name: Check towncrier
    uses: beeware/.github/.github/workflows/towncrier-run.yml@main

  package:
    name: Package Briefcase
    permissions:
      id-token: write
      contents: read
      attestations: write
    uses: beeware/.github/.github/workflows/python-package-create.yml@main
    with:
      attest: ${{ inputs.attest-package }}

  package-automation:
    name: Package Automation
    permissions:
      id-token: write
      contents: read
      attestations: write
    uses: beeware/.github/.github/workflows/python-package-create.yml@main
    with:
      build-subdirectory: "automation"

  unit-tests:
    name: Unit tests
    needs: [ pre-commit, towncrier, package ]
    runs-on: ${{ matrix.platform }}
    continue-on-error: ${{ matrix.experimental || false }}
    strategy:
      fail-fast: false
      matrix:
        platform: [ "macos-13", "macos-latest", "windows-latest", "ubuntu-24.04" ]
        python-version: [ "3.10", "3.14" ]
        include:
          # Ensure the Python versions between min and max are tested
          - platform: "ubuntu-24.04"
            python-version: "3.11"
          - platform: "ubuntu-24.04"
            python-version: "3.12"
          - platform: "ubuntu-24.04"
            python-version: "3.13"
          # # Allow dev Python to fail without failing entire job
          # - python-version: "3.15"
          #   experimental: true
    steps:
    - name: Checkout
      uses: actions/checkout@v5.0.0
      with:
        fetch-depth: 0

    - name: Set up Python
      uses: actions/setup-python@v5.6.0
      with:
        python-version: ${{ matrix.python-version }}
        allow-prereleases: true

    - name: Get Packages
      uses: actions/download-artifact@v5.0.0
      with:
        name: ${{ needs.package.outputs.artifact-name }}
        path: dist

    - name: Install Tox
      uses: beeware/.github/.github/actions/install-requirement@main
      with:
        requirements: tox
        extra: dev

    - name: Test
      id: test
      run: |
        RUNNER_OS=$(cut -d- -f1 <<< ${{ matrix.platform }})
        RUNNER_VERSION=$(cut -d- -f2 <<< ${{ matrix.platform }})
        COVERAGE_FILE=".coverage.${RUNNER_OS}.${RUNNER_VERSION}.${{ matrix.python-version }}" \
          tox -e py-cov --installpkg dist/briefcase-*.whl

    - name: Store Coverage Data
      if: always() && contains('success,failure', steps.test.outcome)
      uses: actions/upload-artifact@v4.6.2
      with:
        name: coverage-data-${{ matrix.platform }}-${{ matrix.python-version }}
        path: ".coverage.*"
        if-no-files-found: error
        include-hidden-files: true

    - name: Report Platform Coverage
      id: coverage
      if: always() && contains('success,failure', steps.test.outcome)
      # coverage reporting must use the same Python version used to produce coverage
      run: tox -qe coverage$(tr -dc "0-9" <<< "${{ matrix.python-version }}")

  coverage:
    name: Project coverage
    runs-on: ubuntu-24.04
    needs: unit-tests
    if: always() && contains('success,failure', needs.unit-tests.result)
    steps:
    - name: Checkout
      uses: actions/checkout@v5.0.0
      with:
        fetch-depth: 0

    - name: Setup Python
      uses: actions/setup-python@v5.6.0
      with:
        # Use minimum version of python for coverage to avoid phantom branches
        # https://github.com/nedbat/coveragepy/issues/1572#issuecomment-1522546425
        python-version: "3.10"

    - name: Install Tox
      uses: beeware/.github/.github/actions/install-requirement@main
      with:
        requirements: tox
        extra: dev

    - name: Retrieve Coverage Data
      uses: actions/download-artifact@v5.0.0
      with:
        pattern: coverage-data-*
        merge-multiple: true

    - name: Platform Coverage Reports
      id: platform-coverage
      run: |
        tox --parallel-no-spinner -qe \
          coverage-ci-platform-linux,coverage-ci-platform-macos,coverage-ci-platform-windows

    - name: Project Coverage Report
      id: project-coverage
      if: always() || contains('success,failure', needs.platform-coverage.result)
      run: tox -qe coverage-ci-project-html

    - name: Upload Project Coverage HTML Report
      if: always() && steps.project-coverage.outcome == 'failure'
      uses: actions/upload-artifact@v4.6.2
      with:
        name: html-coverage-report-project
        path: htmlcov

  verify-projects:
    name: Verify project
    needs: [ package, package-automation, unit-tests ]
    uses: beeware/.github/.github/workflows/app-create-verify.yml@main
    with:
      runner-os: ${{ matrix.runner-os }}
      framework: ${{ matrix.framework }}
    strategy:
      fail-fast: false
      matrix:
        framework: [ "toga", "pyside6", "pygame", "console" ]
        runner-os: [ "macos-13", "macos-latest", "ubuntu-24.04", "ubuntu-24.04-arm", "windows-latest" ]

  verify-apps:
    name: Build app
    needs: [ package, package-automation, unit-tests ]
    uses: beeware/.github/.github/workflows/app-build-verify.yml@main
    with:
      # Builds on Linux must use System Python; otherwise, fall back to version all GUI toolkits support
      python-version: ${{ startsWith(matrix.runner-os, 'ubuntu') && 'system' || '3.12' }}
      runner-os: ${{ matrix.runner-os }}
      framework: ${{ matrix.framework }}
      xcode-version: ${{ matrix.xcode-version }}
      ios-simulator: ${{ matrix.ios-simulator }}
    strategy:
      fail-fast: false
      matrix:
        framework: [ "toga", "pyside6", "pygame", "console" ]
        runner-os: [ "macos-13", "macos-15", "ubuntu-24.04", "ubuntu-24.04-arm", "windows-latest" ]
        include:
          - framework: "toga"
            runner-os: "macos-15"
            xcode-version: "16.4"
            ios-simulator: "iPhone 16e::iOS 18.5"

          # Default xcode handling works for macOS-13
          - framework: "toga"
            runner-os: "macos-13"
            ios-simulator: "iPhone SE (3rd generation)"
