Release

name: Release

on: push

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install dependencies and set path
        run: |
            sudo apt-get update
            sudo apt-get install -y ninja-build libcunit1-dev python3-pip
            # Install meson as root so we can install to the system below.
            sudo pip install meson
      - name: Build tarball and changelogs
        run: |
          git rm -rf c/tests/meson-subproject
          git config --global user.email "CI@CI.com"
          git config --global user.name "Mr Robot"
          git add -A
          git commit -m "dummy commit to make meson not add in the symlinked directory"
          meson c build-gcc
          meson dist -C build-gcc
          python docs/convert_changelog.py c/CHANGELOG.rst > C-CHANGELOG.txt
          python docs/convert_changelog.py python/CHANGELOG.rst > PYTHON-CHANGELOG.txt
      - name: Get the version
        id: get_version
        run:
          echo ::set-output name=VERSION::$(echo $GITHUB_REF | cut -d / -f 3)
      - name: C Release
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/') && contains(github.event.ref, 'C_')
        with:
          name: C API ${{ steps.get_version.outputs.VERSION }}
          body_path: C-CHANGELOG.txt
          draft: True
          fail_on_unmatched_files: True
          files: build-gcc/meson-dist/*
      - name: Python Release
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/') && !contains(github.event.ref, 'C_')
        with:
          name: Python ${{ steps.get_version.outputs.VERSION }}
          body_path: PYTHON-CHANGELOG.txt
          draft: True
          fail_on_unmatched_files: True


Tests

name: Tests

on:
  pull_request:
  push:
    branches: [main, test]

jobs:
  pre-commit:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.1
        with:
          access_token: ${{ github.token }}
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.10'
      - name: install clang-format
        if: steps.clang_format.outputs.cache-hit != 'true'
        run: |
          sudo pip install clang-format==6.0.1
          sudo ln -s /usr/local/bin/clang-format /usr/local/bin/clang-format-6.0
      - uses: pre-commit/action@v3.0.0

  benchmark:
    name: Benchmark
    runs-on: ubuntu-latest
    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.1
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'
          cache-dependency-path: python/requirements/benchmark.txt
      - name: Install deps
        run: pip install -r python/requirements/benchmark.txt
      - name: Build module
        run: |
          cd python
          python setup.py build_ext --inplace
      - name: Run benchmarks
        run: |
          cd python/benchmark
          pip uninstall -y tskit
          python run.py
      - name: Upload Results
        uses: actions/upload-artifact@v2
        with:
          name: benchmark-results
          path: python/benchmark

  test:
    name: Python
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python: [ 3.8, 3.9, "3.11" ]
        os:  [ macos-latest, ubuntu-latest, windows-latest ]
    defaults:
      run:
        shell: bash
    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.1
        with:
          access_token: ${{ github.token }}

      - name: Checkout
        uses: actions/checkout@v3

      - name: Install OSX libs
        if: matrix.os == 'macos-latest'
        run: |
          brew install libxml2 libxslt

      - name: Cache conda and dependencies
        id: cache
        uses: actions/cache@v4
        with:
          path: ${{ env.CONDA }}/envs
          key: ${{ runner.os }}-${{ runner.arch }}-${{ matrix.python}}-conda-v1-${{ hashFiles('python/requirements/CI-tests-conda/requirements.txt') }}-${{ hashFiles('python/requirements/CI-tests-pip/requirements.txt') }}

      - name: Install Conda
        uses: conda-incubator/setup-miniconda@v3
        if: steps.cache.outputs.cache-hit != 'true'
        with:
          activate-environment: anaconda-client-env
          python-version: ${{ matrix.python }}
          channels: conda-forge
          channel-priority: strict
          auto-update-conda: true

      - name: Fix windows symlinks
        working-directory: python
        if: matrix.os == 'windows-latest'
        run: |
          rm -r lib
          mkdir lib
          cp -r --dereference ../c/subprojects lib/.
          cp -r --dereference ../c/tskit lib/.
          cp ../c/tskit.h lib/.

      - name: Fix windows .profile
        if: steps.cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
        run: |
          cp ~/.bash_profile ~/.profile

      - name: Install conda deps
        if: steps.cache.outputs.cache-hit != 'true'
        shell: bash -l {0} #We need a login shell to get conda
        run: |
          conda install --yes --file=python/requirements/CI-tests-conda/requirements.txt

      - name: Install pip deps
        if: steps.cache.outputs.cache-hit != 'true'
        shell: bash -l {0}
        run: |
          pip install setuptools==57.5.0 #v58 broke some deps https://setuptools.pypa.io/en/latest/history.html#v58-0-0
          pip install -r python/requirements/CI-tests-pip/requirements.txt
          # Remove tskit installed by conda
          pip uninstall -y tskit

      - name: Build module
        working-directory: python
        run: |
          source ~/.profile
          conda activate anaconda-client-env
          python setup.py build_ext --inplace

      - name: Remove py311 incompatible tests (lack of numba support for 3.11, needed for lshmm)
        if: matrix.python == '3.11'
        run: |
          rm python/tests/test_*matching*

      - name: Run tests
        working-directory: python
        run: |
          source ~/.profile
          conda activate anaconda-client-env
          # pytest -xv -n2
          python -m pytest -x --cov=tskit --cov-report=xml --cov-branch -n2 tests

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          working-directory: python
          fail_ci_if_error: false
          flags: python-tests
          name: codecov-umbrella
          verbose: true

  msys2:
    runs-on: windows-latest
    strategy:
      matrix:
        include:
        - { sys: mingw32, env: i686 }
        - { sys: mingw64, env: x86_64 }
    name: Windows (${{ matrix.sys }}, ${{ matrix.env }})
    defaults:
      run:
        shell: msys2 {0}
    steps:
    - name: Cancel Previous Runs
      uses: styfle/cancel-workflow-action@0.12.1
      with:
        access_token: ${{ github.token }}

    - name: 'Checkout'
      uses: actions/checkout@v4

    - name: Setup MSYS2 ${{matrix.sys}}
      uses: msys2/setup-msys2@v2
      with:
        msystem: ${{matrix.sys}}
        update: true
        install: >-
          git
          mingw-w64-${{matrix.env}}-toolchain
          mingw-w64-${{matrix.env}}-ninja
          mingw-w64-${{matrix.env}}-meson
          mingw-w64-${{matrix.env}}-cunit

    - name: Build
      working-directory: c
      run: |
        meson build -Dbuild_examples=false
        ninja -C build

    - name: Run tests
      working-directory: c
      run: |
        ninja -C build test


Build and test wheels

name: Build and test wheels

on:
  push:
    branches:
      - main
      - test
    tags:
      - '*'
  release:
    types: [published]

jobs:
  OSX:
    runs-on: macos-latest
    strategy:
      matrix:
        python: [3.8, 3.9, "3.10", 3.11]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - name: Install deps
        run: |
          # Upgrade pip to get bdist_wheel
          pip install --upgrade pip
          pip install setuptools wheel
          # Instead of letting setup.py install a newer numpy we install it here
          # using the oldest supported version for ABI compatibility
          pip install oldest-supported-numpy
      - name: Build C extension
        run: |
          cd python
          python -VV
          python setup.py build_ext --inplace
      - name: Build Wheel
        run: |
          cd python
          python setup.py bdist_wheel
      - name: Delocate to bundle dynamic libs
        run: |
          cd python
          pip install delocate
          delocate-wheel -v dist/*.whl
      - name: Upload Wheels
        uses: actions/upload-artifact@v4
        with:
          name: osx-wheel-${{ matrix.python }}
          path: python/dist

  windows:
    runs-on: windows-latest
    strategy:
      matrix:
        python: [3.8, 3.9, "3.10", 3.11]
        wordsize: [64]
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install deps
        env:
          PYTHON: "py -${{ matrix.python }}-${{ matrix.wordsize }}"
        shell: bash
        run: |
          set -ex
          ${PYTHON} -m pip install --upgrade pip
          ${PYTHON} -m pip install setuptools wheel
          # Instead of letting setup.py install a newer numpy we install it here
          # using the oldest supported version for ABI compatibility
          ${PYTHON} -m pip install oldest-supported-numpy
      - name: Build C Extension
        env:
          PYTHON: "py -${{ matrix.python }}-${{ matrix.wordsize }}"
        shell: bash
        run: |
          set -ex
          git reset --hard
          cd python
          ${PYTHON} -VV
          # For some reason I can't work out the C compiler is not following symlinks
          rm -r lib
          mkdir lib
          cp -r --dereference ../c/subprojects lib/.
          cp -r --dereference ../c/tskit lib/.
          cp ../c/tskit.h lib/.
          ${PYTHON} setup.py build_ext --inplace
      - name: Build Wheel
        env:
          PYTHON: "py -${{ matrix.python }}-${{ matrix.wordsize }}"
        shell: bash
        run: |
          set -ex
          cd python
          ${PYTHON} setup.py bdist_wheel
      - name: Upload Wheels
        uses: actions/upload-artifact@v4
        with:
          name: win-wheel-${{ matrix.python }}-${{ matrix.wordsize }}
          path: python/dist

  manylinux:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Python 3.8
        uses: actions/setup-python@v5
        with:
          python-version: 3.8

      - name: Build sdist
        shell: bash
        run: |
          cd python
          python setup.py sdist

      - name: Upload sdist
        uses: actions/upload-artifact@v4
        with:
          name: sdist
          path: python/dist

      - name: Build wheels in docker
        shell: bash
        run: |
          docker run --rm -v `pwd`:/project -w /project quay.io/pypa/manylinux2014_x86_64 bash .github/workflows/docker/buildwheel.sh

      - name: Upload Wheels
        uses: actions/upload-artifact@v4
        with:
          name: linux-wheels
          path: python/dist/wheelhouse

  OSX-test:
    needs: ['OSX']
    runs-on: macos-latest
    strategy:
      matrix:
        python: [3.8, 3.9, "3.10", 3.11]
    steps:
      - name: Download wheels
        uses: actions/download-artifact@v4
        with:
          name: osx-wheel-${{ matrix.python }}
      - name: Set up Python ${{ matrix.python }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - name: Install wheel and test
        run: |
          python -VV
          # Install the local wheel
          pip install ./tskit-*.whl
          python -c "import tskit"

  windows-test:
    needs: ['windows']
    runs-on: windows-latest
    strategy:
      matrix:
        python: [3.8, 3.9, "3.10", 3.11]
        wordsize: [64]
    steps:
      - name: Download wheels
        uses: actions/download-artifact@v4
        with:
          name: win-wheel-${{ matrix.python }}-${{ matrix.wordsize }}
      - name: Set up Python ${{ matrix.python }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - name: Install wheel and test
        shell: bash
        run: |
          python -VV
          # Install the local wheel
          python -m pip install *.whl
          python -c "import tskit"

  manylinux-test:
    runs-on: ubuntu-latest
    needs: ['manylinux']
    strategy:
      matrix:
        python: [3.8, 3.9, "3.10", 3.11]
        include:

          - python: 3.8
            wheel: cp38
          - python: 3.9
            wheel: cp39
          - python: "3.10"
            wheel: cp310
          - python: 3.11
            wheel: cp311
    steps:
      - name: Download wheels
        uses: actions/download-artifact@v4
        with:
          name: linux-wheels
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python }}
      - name: Install wheel and test
        run: |
          python -VV
          # Install the local wheel
          pip install ./tskit-*-${{ matrix.wheel }}-*.whl
          python -c "import tskit"

  PyPI_Upload:
    runs-on: ubuntu-latest
    environment: release
    needs: ['windows-test', 'OSX-test', 'manylinux-test']
    permissions:
      id-token: write
    steps:
      - name: Download all
        uses: actions/download-artifact@v4
      - name: Move to dist
        run: |
          mkdir dist
          cp */*.{whl,gz} dist/.
      - name: Publish distribution to Test PyPI
        if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && !contains(github.event.ref, 'C_')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository_url: https://test.pypi.org/legacy/
      - name: Publish distribution to PRODUCTION PyPI
        if: github.event_name == 'release' && !startsWith(github.event.release.tag_name, 'C_')
        uses: pypa/gh-action-pypi-publish@release/v1


Upload Python Package

# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

name: Upload Python Package

on:
  release:
    types: [created]

jobs:
  deploy:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v1
      with:
        python-version: '3.6'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel twine
    - name: Build and publish
      env:
        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
      run: |
        python setup.py sdist bdist_wheel
        twine upload dist/*


Mark stale issues and pull requests

# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Mark stale issues and pull requests

on:
  schedule:
  - cron: '37 11 * * *'

jobs:
  stale:

    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write

    steps:
    - uses: actions/stale@v3
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        stale-issue-message: 'Stale issue message'
        stale-pr-message: 'Stale pull request message'
        stale-issue-label: 'no-issue-activity'
        stale-pr-label: 'no-pr-activity'


Deploy images AlmaLinux9

name: Deploy images AlmaLinux9

on:
  workflow_dispatch:
    inputs:
      push_image:
        description: Push to DockerHub
        type: boolean
        default: false

      specific_job:
        description: Specific job to run
        type: choice
        default: none
        options:
          - none
          - py37
          - py38
          - py39
          - py310
          - py311
          - example

      platforms:
        description: Platforms to build for
        type: choice
        default: linux/amd64,linux/arm64
        options:
          - linux/amd64,linux/arm64
          - linux/amd64
          - linux/arm64

jobs:
  a9-py37:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py37') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_alma9_py37
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:a9-py37,riga/law:py37
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  a9-py38:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py38') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_alma9_py38
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:a9-py38,riga/law:py38
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  a9-py39:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py39') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_alma9_py39
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:a9-py39,riga/law:py39
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  a9-py310:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py310') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_alma9_py310
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:a9-py310,riga/law:py310
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  a9-py311:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py311') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_alma9_py311
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:a9-py311,riga/law:a9-py3,riga/law:a9,riga/law:py311,riga/law:py3,riga/law:latest
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  example:
    if: ${{ github.event.inputs.push_image == 'true' && (github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'example')) }}
    needs: a9-py311
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_example
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:example
          push_image: true


Deploy images CentOS7

name: Deploy images CentOS7

on:
  workflow_dispatch:
    inputs:
      push_image:
        description: "Push to DockerHub"
        type: boolean
        default: false

      specific_job:
        description: "Specific job to run"
        type: choice
        default: none
        options:
          - none
          - py27
          - py36
          - py37
          - py38
          - py39
          - py310

      platforms:
        description: Platforms to build for
        type: choice
        default: linux/amd64,linux/arm64
        options:
          - linux/amd64,linux/arm64
          - linux/amd64
          - linux/arm64

jobs:
  # still existing for legacy support, to be removed soon
  c7-py27:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py27') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: docker/build-push-action@v1
        with:
          path: docker
          dockerfile: docker/Dockerfile_centos7_py27
          repository: riga/law
          tags: c7-py27,py27,py2
          push: ${{ github.event.inputs.build_only != 'true' }}
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

  # still existing for legacy support, to be removed soon
  c7-py36:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py36') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: docker/build-push-action@v1
        with:
          path: docker
          dockerfile: docker/Dockerfile_centos7_py36
          repository: riga/law
          tags: c7-py36,py36
          push: ${{ github.event.inputs.build_only != 'true' }}
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

  c7-py37:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py37') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos7_py37
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c7-py37
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c7-py38:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py38') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos7_py38
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c7-py38
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c7-py39:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py39') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos7_py39
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c7-py39
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c7-py310:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py310') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos7_py310
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c7-py310,riga/law:c7-py3,riga/law:c7
          push_image: ${{ github.event.inputs.push_image == 'true' }}


Deploy images CentOS8

name: Deploy images CentOS8

on:
  workflow_dispatch:
    inputs:
      push_image:
        description: Push to DockerHub
        type: boolean
        default: false

      specific_job:
        description: Specific job to run
        type: choice
        default: none
        options:
          - none
          - py37
          - py38
          - py39
          - py310
          - py311

      platforms:
        description: Platforms to build for
        type: choice
        default: linux/amd64,linux/arm64
        options:
          - linux/amd64,linux/arm64
          - linux/amd64
          - linux/arm64

jobs:
  c8-py37:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py37') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos8_py37
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c8-py37
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c8-py38:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py38') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos8_py38
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c8-py38
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c8-py39:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py39') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos8_py39
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c8-py39
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c8-py310:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py310') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos8_py310
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c8-py310
          push_image: ${{ github.event.inputs.push_image == 'true' }}

  c8-py311:
    if: ${{ github.event.inputs.specific_job == 'none' || contains(github.event.inputs.specific_job, 'py311') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Build and deploy image 🐳
        uses: ./.github/actions/build-image
        with:
          dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
          dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
          docker_file: docker/Dockerfile_centos8_py311
          platforms: ${{ github.event.inputs.platforms }}
          tags: riga/law:c8-py311,riga/law:c8-py3,riga/law:c8
          push_image: ${{ github.event.inputs.push_image == 'true' }}


Lint and test

name: Lint and test

on:
  workflow_dispatch:
  push:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Pull docker image 🐳
        run: docker pull riga/law

      - name: Lint 🔍
        run: ./tests/docker.sh riga/law ./tests/lint.sh

  pypi:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Setup python 🐍
        uses: actions/setup-python@v5
        with:
          python-version: 3.9

      - name: Install dependencies ☕
        run: |
          python -m pip install --upgrade pip
          pip install twine

      - name: Check bundling 📦
        run: python setup.py sdist

      - name: Check setup 🚦
        run: twine check "dist/$( python setup.py --fullname ).tar.gz"

  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python: ["37", "38", "39", "310", "311"]
    name: test (python ${{ matrix.python }})
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Pull docker image 🐳
        run: docker pull riga/law:py${{ matrix.python }}

      - name: Test 🎢
        run: ./tests/docker.sh riga/law:py${{ matrix.python }}

  coverage:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout ⬇
        uses: actions/checkout@v4
        with:
          persist-credentials: false
          submodules: recursive

      - name: Pull docker image 🐳
        run: docker pull riga/law

      - name: Run coverage test 🎢
        run: ./tests/docker.sh riga/law ./tests/coverage.sh

      - name: Upload report 🔝
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./coverage.xml
          flags: unittests
          fail_ci_if_error: false


Publish on PyPI

name: Publish on PyPI

on:
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout 🛎
        uses: actions/checkout@v4
        with:
          persist-credentials: false

      - name: Setup python 🐍
        uses: actions/setup-python@v5
        with:
          python-version: 3.9

      - name: Bundle 📦
        run: python setup.py sdist

      - name: Publish package ⬆
        uses: pypa/gh-action-pypi-publish@master
        with:
          user: __token__
          password: ${{ secrets.PYPI_TOKEN }}
          skip_existing: true


docs

name: docs

on:
  push:
    tags:
      - v*
    branches:
      - master

jobs:
  build_publish_docs:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v3
        with:
          python-version: 3.9

      - name: Build docs
        run: |
          make develop-docs
          cd docs/
          make html

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: docs/build/html


name: publish_docker

on:
  push:
    tags:
      - v*
    branches:
      - master

jobs:
  publish_docker:
    runs-on: ubuntu-latest

    permissions:
      id-token: write
      contents: read
    env:
      DOCKER_TAG: ${{github.ref_type == 'tag' && github.ref_name || 'latest'}}
      REPO_NAME: 'augerai/a2ml'
      DOCKER_USER: ${{ secrets.DOCKER_USER }}
      DOCKER_PASS: ${{ secrets.DOCKER_PASS }}

      AWS_KS_ROLE: ${{ secrets[format('AWS_KS_{0}_ROLE', github.ref_type == 'tag' && 'STABLE' || 'EXPERIMENTAL')] }}
      CLUSTER_NAME: ${{ secrets[format('{0}_CLUSTER_NAME', github.ref_type == 'tag' && 'STABLE' || 'EXPERIMENTAL')] }}
      KUBECONFIG_FILE: '/home/runner/.kube/config'
      RELEASE_NAME: 'a2ml'

    steps:
      - uses: actions/checkout@v3

      - name: Build docker and run tests
        run: make config docker-test

      - name: Push docker image
        run: |
          docker login -u $DOCKER_USER -p $DOCKER_PASS
          docker push $REPO_NAME:$DOCKER_TAG

      - name: configure aws credentials with role1
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE }}
          aws-region: us-west-2

      - name: Assume execution role
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}
          aws-region: us-west-2
          aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
          aws-session-token: ${{ env.AWS_SESSION_TOKEN }}
          role-duration-seconds: 3000
          role-skip-session-tagging: true
          role-to-assume: ${{ env.AWS_KS_ROLE }}

      - uses: azure/setup-kubectl@v3
      - uses: Azure/setup-helm@v3
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: setup the kubectl config
        run : |
          kubectl version --client=true
          rm -f $KUBECONFIG_FILE
          aws eks update-kubeconfig --name $CLUSTER_NAME --region us-west-2
          cat $KUBECONFIG_FILE

      - name: Helm upgrade augerai repo
        run: |
          helm repo add augerai https://augerai.github.io/charts
          helm repo update
          helm repo list
          helm upgrade $RELEASE_NAME $REPO_NAME \
            --install \
            --namespace=$RELEASE_NAME \
            --set=image.tag=$DOCKER_TAG \
            --reuse-values \
            --wait \
            --atomic \
            --install \
            --kubeconfig $KUBECONFIG_FILE


name: publish_pip

on:
  push:
    tags:
      - v*

jobs:
  publish_pip:
    runs-on: ubuntu-latest
    env:
      PYPI_USERNAME: ${{ secrets.PYPI_USERNAME }}
      PYPI_PASSWORD: ${{ secrets.PYPI_PASSWORD }}

    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v3
        with:
          python-version: 3.9

      - name: Make pypirc file
        run: |
          echo -e "[pypi]" >> ~/.pypirc
          echo -e "username = $PYPI_USERNAME" >> ~/.pypirc
          echo -e "password = $PYPI_PASSWORD" >> ~/.pypirc

      - name: Install dependencies
        run: pip install wheel
      - name: Build wheel
        run: make build
      - name: Deploy package
        run: make release


test

name: test

on:
  push:
    branches-ignore:
      - master

    tags-ignore:
      - v*

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Run tests
        run: make config docker-test


Linux pip tests

name: Linux pip tests

on:
  push:
    branches:
    - master
    - 2.*
  pull_request:
    branches:
    - master
    - 2.*

concurrency:
  group: linux-pip-tests-${{ github.ref }}
  cancel-in-progress: true

jobs:
  linux:
    name: Py${{ matrix.PYTHON_VERSION }}
    runs-on: ubuntu-latest
    env:
      CI: True
      PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
      RUNNER_OS: 'ubuntu'
      USE_CONDA: 'false'
    strategy:
      fail-fast: false
      matrix:
        PYTHON_VERSION: ['3.8', '3.9', '3.10']
    timeout-minutes: 20
    steps:
      - name: Checkout branch
        uses: actions/checkout@v1
      - name: Install System Packages
        run: |
          sudo apt-get update
          sudo apt-get install libegl1-mesa
      - name: Install Conda
        uses: conda-incubator/setup-miniconda@v2
        with:
           activate-environment: test
           auto-update-conda: false
           auto-activate-base: false
           python-version: ${{ matrix.PYTHON_VERSION }}
      - name: Install package and dependencies
        shell: bash -l {0}
        run: |
          pip install -e .[test]
      - name: Show environment information
        shell: bash -l {0}
        run: |
          conda info
          conda list
      - name: Run tests
        shell: bash -l {0}
        run: |
          xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          fail_ci_if_error: false
          verbose: true


Linux tests

name: Linux tests

on:
  push:
    branches:
    - master
    - 2.*
  pull_request:
    branches:
    - master
    - 2.*

concurrency:
  group: linux-tests-${{ github.ref }}
  cancel-in-progress: true

jobs:
  linux:
    name: Py${{ matrix.PYTHON_VERSION }}
    runs-on: ubuntu-latest
    env:
      CI: True
      PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
      RUNNER_OS: 'ubuntu'
      USE_CONDA: 'true'
    strategy:
      fail-fast: false
      matrix:
        PYTHON_VERSION: ['3.8', '3.9', '3.10']
    timeout-minutes: 20
    steps:
      - name: Checkout branch
        uses: actions/checkout@v1
      - name: Install System Packages
        run: |
          sudo apt-get update
          sudo apt-get install libegl1-mesa
      - name: Install Conda
        uses: conda-incubator/setup-miniconda@v2
        with:
           activate-environment: test
           auto-update-conda: true
           auto-activate-base: false
           channels: conda-forge
           miniforge-variant: Mambaforge
           python-version: ${{ matrix.PYTHON_VERSION }}
      - name: Install package dependencies
        shell: bash -l {0}
        run: |
          mamba install --file requirements/posix.txt -y -q
      - name: Install test dependencies
        shell: bash -l {0}
        run: mamba install --file requirements/tests.txt -y -q
      - name: Install Package
        shell: bash -l {0}
        run: pip install -e .
      - name: Show environment information
        shell: bash -l {0}
        run: |
          conda info
          conda list
      - name: Run tests
        shell: bash -l {0}
        run: |
          xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          xvfb-run --auto-servernum pytest spyder_kernels --color=yes --cov=spyder_kernels -vv
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          fail_ci_if_error: false
          verbose: true


Macos tests

name: Macos tests

on:
  push:
    branches:
    - master
    - 2.*
  pull_request:
    branches:
    - master
    - 2.*

concurrency:
  group: macos-tests-${{ github.ref }}
  cancel-in-progress: true

jobs:
  macos:
    name: Py${{ matrix.PYTHON_VERSION }}
    runs-on: macos-latest
    env:
      CI: True
      PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
      RUNNER_OS: 'macos'
      USE_CONDA: 'true'
    strategy:
      fail-fast: false
      matrix:
        PYTHON_VERSION: ['3.8', '3.9', '3.10']
    timeout-minutes: 25
    steps:
      - name: Checkout branch
        uses: actions/checkout@v1
      - name: Install Conda
        uses: conda-incubator/setup-miniconda@v2
        with:
           activate-environment: test
           auto-update-conda: true
           auto-activate-base: false
           channels: conda-forge
           miniforge-variant: Mambaforge
           python-version: ${{ matrix.PYTHON_VERSION }}
      - name: Install package dependencies
        shell: bash -l {0}
        run: |
          mamba install --file requirements/posix.txt -y -q
      - name: Install test dependencies
        shell: bash -l {0}
        run: mamba install --file requirements/tests.txt -y -q
      - name: Install Package
        shell: bash -l {0}
        run: pip install -e .
      - name: Show environment information
        shell: bash -l {0}
        run: |
          conda info
          conda list
      - name: Run tests
        shell: bash -l {0}
        run: |
          pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          pytest spyder_kernels --color=yes --cov=spyder_kernels -vv
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          fail_ci_if_error: false
          verbose: true


Windows tests

name: Windows tests

on:
  push:
    branches:
    - master
    - 2.*
  pull_request:
    branches:
    - master
    - 2.*

concurrency:
  group: windows-tests-${{ github.ref }}
  cancel-in-progress: true

jobs:
  windows:
    name: Py${{ matrix.PYTHON_VERSION }}
    runs-on: windows-latest
    env:
      CI: True
      PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
      RUNNER_OS: 'windows'
      USE_CONDA: 'true'
    strategy:
      fail-fast: false
      matrix:
        PYTHON_VERSION: ['3.8', '3.9', '3.10']
    timeout-minutes: 25
    steps:
      - name: Checkout branch
        uses: actions/checkout@v1
      - name: Install Conda
        uses: conda-incubator/setup-miniconda@v2
        with:
           activate-environment: test
           auto-update-conda: true
           auto-activate-base: false
           channels: conda-forge
           miniforge-variant: Mambaforge
           python-version: ${{ matrix.PYTHON_VERSION }}
      - name: Install package dependencies
        shell: bash -l {0}
        run: mamba install --file requirements/windows.txt -y -q
      - name: Install test dependencies
        shell: bash -l {0}
        run: |
          mamba install --file requirements/tests.txt -y -q
      - name: Install Package
        shell: bash -l {0}
        run: pip install -e .
      - name: Show environment information
        shell: bash -l {0}
        run: |
          conda info
          conda list
      - name: Run tests
        shell: bash -l {0}
        run: |
          pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          pytest spyder_kernels --color=yes --cov=spyder_kernels -vv || \
          pytest spyder_kernels --cov=spyder_kernels -vv
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          fail_ci_if_error: false
          verbose: true


SolveBio Python Package

name: SolveBio Python Package

# on: [push, pull_request]
on: [push]

jobs:
  build:
    runs-on: ubuntu-20.04
    strategy:
      matrix:
        python-version: [3.6, 3.7, 3.8, '3.10', '3.11', '3.12', pypy2, pypy3]
    env:
      SOLVEBIO_API_HOST: ${{ secrets.QUARTZBIO_API_HOST }}
      SOLVEBIO_API_KEY: ${{ secrets.QUARTZBIO_API_KEY }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Setup Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}
      - name: Display Python version
        run: python -c "import sys; print(sys.version)"
      - name: Export pythonpath
        run: |
          export PYTHONPATH=$PYTHONPATH:$(pwd)
      - name: Install Tox and any other packages
        run: |
          pip install -U wheel --user
          pip install setuptools
          pip install flake8 pytest
      - name: Install dependencies
        run: |
          pip install -r requirements-dev.txt
          pip install XlsxWriter===0.9.3
      - name: Scripts
        run: |
          python -m pytest recipes/tests/test_recipes_sync.py
          python -m pytest solvebio/test/test_object.py
          python -m flake8 solvebio
  build_py27:
    runs-on: ubuntu-20.04
    env:
      SOLVEBIO_API_HOST: ${{ secrets.QUARTZBIO_API_HOST }}
      SOLVEBIO_API_KEY: ${{ secrets.QUARTZBIO_API_KEY }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v2
      - name: Setup Python 2.7
        run: |
          sudo apt install python2
          sudo apt-get install -y curl python2
          sudo curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
          sudo python2.7 get-pip.py
      - name: Display Python version
        run: python2.7 -c "import sys; print(sys.version)"
      - name: Export pythonpath
        run: |
          export PYTHONPATH=$PYTHONPATH:$(pwd)
      - name: Install Tox and any other packages
        run: |
          python2.7 -m pip install -U wheel --user
          python2.7 -m pip install setuptools
          python2.7 -m pip install flake8 pytest
      - name: Install dependencies
        run: |
          python2.7 -m pip install -r requirements-dev.txt
          python2.7 -m pip install XlsxWriter===0.9.3
      - name: Scripts
        run: |
          python2.7 -m pytest recipes/tests/test_recipes_sync.py
          python2.7 -m flake8 solvebio


Test Python Package

# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Test Python Package
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: [3.7, 3.8, 3.9]

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v1
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -e .[cloudvolume,meshing]
#     - name: Lint with flake8
#       run: |
#         pip install flake8
#         # stop the build if there are Python syntax errors or undefined names
#         flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
#         # exit-zero treats all errors as warnings.
#         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test unittests
      run: |
        python -m unittest discover .
    - name: Notify slack success
      if: success()
      env:
        SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
      uses: voxmedia/github-action-slack-notify-build@v1
      with:
        channel: ci
        status: SUCCESS
        color: good

    - name: Notify slack fail
      if: failure()
      env:
        SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
      uses: voxmedia/github-action-slack-notify-build@v1
      with:
        channel: ci
        status: FAILED
        color: danger


Docker images verification

name: Docker images verification
# This workflow is triggered on pushes and pull requests to the repository.
on: [push, pull_request]

jobs:
  build:
    # This job runs on Linux
    runs-on: ubuntu-latest

    steps:
       - uses: actions/checkout@v1
       - uses: ./.github/actions/docker-images-verification
         with:
          username: ${{ github.actor }}
          password: ${{ secrets.GITGUB_PAT }}
          registry: ghcr.io


Build and publish Docker images

name: Build and publish Docker images

on:
  workflow_dispatch:
  schedule:
    - cron: "0 0 * * 1"
  push:
    branches:
      - main
    tags:
      - "*.*.*"
    paths-ignore:
      - "test/*"
      - ".gitignore"
      - "docker-compose-separate-containers.yml"
      - "docker-compose.yml"
      - "LICENSE"
      - "Makefile"
      - "*.md"

jobs:
  multiarch-build:
    name: Build and publish image
    strategy:
      matrix:
        base: [alpine, debian]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Retrieve nginx-proxy version
        id: nginx-proxy_version
        run: echo "VERSION=$(git describe --tags)" >> "$GITHUB_OUTPUT"

      - name: Retrieve docker-gen version
        id: docker-gen_version
        run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT"

      - name: Get Docker tags
        id: docker_meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ghcr.io/nginx-proxy/nginx-proxy
            nginxproxy/nginx-proxy
            jwilder/nginx-proxy
          tags: |
            type=semver,pattern={{version}},enable=${{ matrix.base == 'debian' }}
            type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.base == 'debian' }}
            type=semver,suffix=-alpine,pattern={{version}},enable=${{ matrix.base == 'alpine' }}
            type=semver,suffix=-alpine,pattern={{major}}.{{minor}},enable=${{ matrix.base == 'alpine' }}
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && matrix.base == 'debian' }}
            type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' && matrix.base == 'alpine' }}
          labels: |
            org.opencontainers.image.authors=Nicolas Duchon <nicolas.duchon@gmail.com> (@buchdag), Jason Wilder
            org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }}
          flavor: |
            latest=false

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push the image
        id: docker_build
        uses: docker/build-push-action@v5
        with:
          context: .
          file: Dockerfile.${{ matrix.base }}
          build-args: |
            NGINX_PROXY_VERSION=${{ steps.nginx-proxy_version.outputs.VERSION }}
            DOCKER_GEN_VERSION=${{ steps.docker-gen_version.outputs.VERSION }}
          platforms: linux/amd64,linux/arm64,linux/s390x,linux/arm/v7
          sbom: true
          push: true
          provenance: mode=max
          tags: ${{ steps.docker_meta.outputs.tags }}
          labels: ${{ steps.docker_meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Images digests
        run: echo ${{ steps.docker_build.outputs.digest }}


Update Docker Hub Description

name: Update Docker Hub Description

on:
  push:
    branches:
      - main
    paths:
      - README.md
      - .github/workflows/dockerhub-description.yml

jobs:
  dockerHubDescription:
    name: Update Docker Hub Description
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Docker Hub Description
        uses: peter-evans/dockerhub-description@v4
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN_RWD }}
          repository: nginxproxy/nginx-proxy
          short-description: ${{ github.event.repository.description }}
          enable-url-completion: true


Tests

name: Tests

on:
  workflow_dispatch:
  push:
    branches:
      - main
    paths-ignore:
      - "LICENSE"
      - "**.md"
  pull_request:
    paths-ignore:
      - "LICENSE"
      - "**.md"

jobs:
  unit:
    name: Unit Tests
    runs-on: ubuntu-latest

    strategy:
      matrix:
        base_docker_image: [alpine, debian]

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python 3.9
        uses: actions/setup-python@v5
        with:
          python-version: 3.9

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r python-requirements.txt
        working-directory: test/requirements

      - name: Build Docker web server image
        run: make build-webserver

      - name: Build Docker nginx proxy test image
        run: make build-nginx-proxy-test-${{ matrix.base_docker_image }}

      - name: Run tests
        run: pytest
        working-directory: test


Publish

name: Publish

on:
  push:
    tags:
      - '*'

jobs:
  tests:
    name: Python ${{ matrix.python-version }}
    runs-on: ubuntu-latest

    strategy:
      matrix:
        python-version:
        - '3.8'
        - '3.9'
        - '3.10'
        - '3.11'
        - '3.12'

    steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        cache: pip
        cache-dependency-path: requirements/*.txt

    - name: Upgrade packaging tools
      run: python -m pip install --upgrade pip setuptools virtualenv wheel

    - name: Install dependencies
      run: python -m pip install --upgrade codecov tox

    - name: Run tox targets for ${{ matrix.python-version }}
      run: |
        ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}")
        TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') tox
    - name: Run lint
      if: ${{ matrix.python-version == '3.9' }}
      run: |
        tox -e lint

  deploy:
    runs-on: ubuntu-latest
    needs: [tests]
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.10'
    - name: Install dependencies
      run: |
        pip install build twine
    - name: Publish
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
      run: |
        python -m build
        twine upload dist/*


CI

name: CI

on:
  push:
    branches:
    - main
  pull_request:

jobs:
  tests:
    name: Python ${{ matrix.python-version }}
    runs-on: ubuntu-latest

    strategy:
      matrix:
        python-version:
        - '3.8'
        - '3.9'
        - '3.10'
        - '3.11'
        - '3.12'

    steps:
    - uses: actions/checkout@v4

    - uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}
        cache: pip
        cache-dependency-path: requirements/*.txt

    - name: Upgrade packaging tools
      run: python -m pip install --upgrade pip setuptools virtualenv wheel

    - name: Install dependencies
      run: python -m pip install --upgrade codecov tox

    - name: Run tox targets for ${{ matrix.python-version }}
      run: |
        ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}")
        TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') tox
    - name: Run lint
      if: ${{ matrix.python-version == '3.9' }}
      run: |
        tox -e lint
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v1
      with:
        flags: unittests
        name: codecov-umbrella
        fail_ci_if_error: false


Tests master

name: Tests master

on:
  # This avoids having duplicate builds for a pull request
  push:
    branches:
      - master
      - "*-maintenance"
    tags:
      - "v*"
  pull_request:
    branches:
      - master
      - "*-maintenance"

jobs:
  ############################################################################
  # Lint jobs
  ############################################################################
  lint:
    name: lint
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: "lts/*"
          cache: "npm"
          cache-dependency-path: "**/package-lock.json"
      - name: Install node dependencies
        run: make frontend/node_modules
      - uses: actions/setup-python@v4
        with:
          python-version: "3.9"
          cache: "pip"
          cache-dependency-path: "**/pyproject.toml"
      - uses: actions/cache@v3
        with:
          path: ~/.cache/pre-commit
          key: pre-commit|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
      - name: Install python dependencies
        id: pip
        run: |
          python -m pip install --upgrade pip
          python -m pip install tox pre-commit
          python -m pip freeze --local
      - name: Run pylint
        run: tox -e lint
      - name: Run pre-commit
        # run pre-commit check even if pylint check fails
        if: steps.pip.outcome == 'success'
        run: pre-commit run --show-diff-on-failure --color=always --all-files

  ############################################################################
  # Node tests
  ############################################################################
  node-tests:
    name: ${{ matrix.os}} node-${{ matrix.node }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        node: ["lts/*"]
        os: ["ubuntu-latest", "macos-latest", "windows-latest"]
        include:
          - node: "current"
            os: "ubuntu-latest"
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node }}
          cache: "npm"
          cache-dependency-path: "**/package-lock.json"
      - name: Build frontend
        run: make
      - name: Typecheck and run frontend tests
        run: make test-js

  ############################################################################
  # Python tests
  ############################################################################
  python-tests:
    name: ${{ matrix.os }} py${{ matrix.python }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: ["ubuntu-latest", "macos-latest", "windows-latest"]
        python: ["3.8", "3.9", "3.10", "3.11", "3.12"]
        exclude:
          - os: "macos-latest"
            python: "3.9"
          - os: "macos-latest"
            python: "3.10"
          - os: "macos-latest"
            python: "3.11"
          - os: "windows-latest"
            python: "3.9"
          - os: "windows-latest"
            python: "3.10"
          - os: "windows-latest"
            python: "3.11"
        include:
          - python: "3.12"
            install-ffmpeg: true
          - os: "macos-latest"
            # Omit test with older pillow versions on macOS.
            # (Binary wheels not available?)
            tox_skip_env: ".*-pillow"
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python }}
          cache: "pip"
          cache-dependency-path: "**/pyproject.toml"

      - name: Install Linux system dependencies
        if: runner.os == 'Linux' && matrix.install-ffmpeg
        run: |
          sudo apt-get update
          sudo apt-get -y install ffmpeg

      - name: Install macOS system dependencies
        if: runner.os == 'macOS' && matrix.install-ffmpeg
        run: brew install ffmpeg

      - name: Install Windows system dependencies
        if: runner.os == 'Windows' && matrix.install-ffmpeg
        run: choco install --no-progress --timeout 600 ffmpeg
        continue-on-error: true
      - name: Workaround for UnicodeDecodeError from tox on Windows
        # Refs:
        #   https://github.com/lektor/lektor/pull/933#issuecomment-923107580
        #   https://github.com/tox-dev/tox/issues/1550
        if: runner.os == 'Windows'
        run: Out-File $env:GITHUB_ENV utf8 -Append -InputObject 'PYTHONIOENCODING=utf-8'

      - name: Install python dependencies
        run: |
          python -m pip install --upgrade pip
          python -m pip install tox tox-gh-actions coverage[toml]
      - name: Run python tests
        run: tox
        env:
          TOX_SKIP_ENV: ${{ matrix.tox_skip_env }}
      - name: Publish coverage data to codecov
        uses: codecov/codecov-action@v3

  ############################################################################
  # Package and (Possibly) Publish to PyPI
  ############################################################################
  deploy:
    needs: ["lint", "node-tests", "python-tests"]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v4
      - uses: actions/setup-node@v3
        with:
          node-version: "lts/*"

      - name: Install dependencies
        run: python -m pip install build

      - name: Build release artifacts
        env:
          HATCH_BUILD_CLEAN: "true"
        run: python -m build

      - name: Save release artifacts (for inspection)
        uses: actions/upload-artifact@v3
        with:
          name: dist
          path: dist/*

      - name: Publish release
        if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          password: ${{ secrets.PYPI_PASSWORD }}


Lint

name: Lint

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: psf/black@stable


CodeQL

# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]
  schedule:
    - cron: '27 10 * * 1'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        language: [ 'python' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
        # Learn more:
        # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v1
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.
        # queries: ./path/to/local/query, your-org/your-repo/queries@main

    # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v1

    # ℹ Command-line programs to run using the OS shell.
    # 📚 https://git.io/JvXDl

    # ✏ If the Autobuild fails above, remove it and uncomment the following three lines
    #    and modify them (or add more) to build your code if your project
    #    uses a compiled language

    #- run: |
    #   make bootstrap
    #   make release

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v1


Django CI

name: Django CI

on:
  push:
    branches:
      - 'master'
    tags:
      - 'v*'
  pull_request:
    branches:
      - 'master'

jobs:
  django_ci:
    name: Build and Test a Django Project
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: ["3.8", "3.9", "3.10", "3.11"]

    # https://github.com/actions/example-services/tree/master/.github/workflows
    services:
      mysql:
        image: mysql:5.7
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes
        ports:
        - 3306:3306
        options: >-
          --name=mysql
          --health-cmd="mysqladmin ping"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5
      redis:
        image: redis
        ports:
        - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - name: Check out repository code
      uses: actions/checkout@v2

    - name: SET MySQL Cnf
      run: |
        cat << EOF > my.cnf
        [mysqld]
        server-id=100
        log_bin=ON
        character-set-server = utf8mb4
        collation-server = utf8mb4_general_ci
        lower_case_table_names=1
        default-time_zone = '+8:00'
        [client]
        default-character-set=utf8mb4
        EOF
        docker cp my.cnf mysql:/etc/mysql/conf.d/
        docker restart mysql

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install Dependencies
      run: |
        sudo apt-get update && sudo apt-get install libsasl2-dev libkrb5-dev libldap2-dev libssl-dev unixodbc unixodbc-dev
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install -r dev-requirements.txt

    - name: Init Table
      run: |
        mysql -h127.0.0.1 -uroot -e "CREATE DATABASE archery CHARSET UTF8MB4;"
        mysql -h127.0.0.1 -uroot -e "DROP DATABASE IF EXISTS test_archery;CREATE DATABASE test_archery CHARSET UTF8MB4;"

    - name: Run Tests
      run: |
        python manage.py makemigrations
        python manage.py makemigrations sql
        pytest --cov --cov-report xml

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v4
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
        files: ./coverage.xml
        fail_ci_if_error: true


Publish Docker Base image

name: Publish Docker Base image

on:
  push:
    branches:
      - 'master'
    paths:
      - 'src/docker/Dockerfile-base'
  pull_request:
    branches:
      - 'master'
    paths:
      - 'src/docker/Dockerfile-base'

jobs:
  push_to_registries:
    name: Build and push Docker Base images
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Check out the repo
        uses: actions/checkout@v2

      - name: Log in to Docker Hub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v3
        with:
          tags: |
            type=sha
          images: |
            hhyo/archery-base

      - name: Build and push Docker Base images
        uses: docker/build-push-action@v2
        with:
          context: .
          file: src/docker/Dockerfile-base
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


Publish Docker image

name: Publish Docker image

on:
  push:
    branches:
      - 'master'
    tags:
      - 'v*'
  pull_request:
    branches:
      - 'master'

jobs:
  push_to_registries:
    name: Build and push Docker images
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - name: Check out the repo
        uses: actions/checkout@v2

      - name: Log in to Docker Hub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Log in to the Container registry
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v3
        with:
          images: |
            hhyo/archery
            ghcr.io/${{ github.repository }}

      - name: Build and push Docker images
        uses: docker/build-push-action@v2
        with:
          context: .
          file: src/docker/Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


Python package

name: Python package

on: [push, pull_request]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.8"]

    steps:
    - uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        allow-prereleases: true
    - name: Install dependencies
      run: make requirements
    - name: Run tests
      run: make coverage
    - name: Static checks
      if: "matrix.python-version == '3.8'"
      run: make check
    - name: Build docs with sphinx
      if: "matrix.python-version == '3.8'"
      run: make docs
    - name: Build packages
      if: "matrix.python-version == '3.8'"
      run: make package
    - name: Upload packages
      if: "matrix.python-version == '3.8'"
      uses: actions/upload-artifact@v4
      with:
        name: more-itertools-packages
        path: dist/*


pgmpy

name: pgmpy
on: [push, pull_request]
jobs:
  build:
    name: Running tests - OS - ${{ matrix.os }}; Python - ${{ matrix.python-version}}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
        os: [ubuntu-latest, windows-latest, macOS-latest]
        group: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: ${{matrix.python-version}}
          cache: 'pip'
      - name: Print python info
        run: |
          which python
          python --version
      - name: Install dependencies
        run: |
          pip install pip -U
          pip install black -U
          pip install pytest-split
          pip install -r requirements.txt -U
      - name: Check formatting
        run: |
          black --diff .
          black --check .
      - name: Print package versions
        run: |
          pip freeze
      - name: Run tests
        run: pytest --cov-config .coveragerc --cov-report html --cov-report term --cov=pgmpy --splits 4 --group ${{ matrix.group }} --verbose


Changelog Update

name: Changelog Update

on:
  push:
    branches:
    - develop

permissions:
  contents: read

jobs:
  update-changelog:
    permissions:
      contents: write  # for Git to git push
    name: Update Changelog
    runs-on: ubuntu-latest
    env:
      PYTHON_VERSION: 3.11
    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
      with:
        fetch-depth: 0
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
      with:
        repository: Flexget/wiki
        path: wiki
        token: ${{ secrets.flexgetbot_pat }}
        ref: main
    - name: Setup Git User
      run: |
        git config --global user.email ${{ secrets.git_email }}
        git config --global user.name ${{ secrets.git_user }}
    - name: Set up Python ${{ env.PYTHON_VERSION }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
    - name: Install dev dependencies
      run: |
        pip --cache-dir pip_cache install -r dev-requirements.txt
    - name: Update the Changelog
      run: |
        set -x
        python update-changelog.py wiki/ChangeLog.md
        cd wiki
        git add ChangeLog.md
        if ! git diff --cached --exit-code; then
            git commit -m "Automated ChangeLog update"
            git push origin main
        fi


Docker Image CI

name: Docker Image CI

on:
  push:
    branches:
      - develop
    tags:
      - v*.*.*
  pull_request:
    branches:
      - "*"

permissions: {}

jobs:
  build:
    permissions:
      contents: read # to fetch code (actions/checkout)
      packages: write # to push to ghcr.io

    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4

      - name: Setup QEMU
        uses: docker/setup-qemu-action@v3

      - name: Setup Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v3
        with:
          install: true

      - name: Log in to Docker Hub
        if: github.event_name != 'pull_request'
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        if: github.event_name == 'push'
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ github.token }}

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            flexget/flexget
            ghcr.io/flexget/flexget
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=semver,pattern={{major}}
            type=ref,event=branch

      - name: Build Base Docker Image
        id: docker_build
        uses: docker/build-push-action@v6
        with:
          context: ./
          file: ./Dockerfile
          builder: ${{ steps.buildx.output.name }}
          push: ${{ github.event_name == 'push' && github.ref != 'refs/heads/master' }}
          platforms: linux/amd64,linux/arm64
          provenance: false
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: |
            type=gha, scope=${{ github.ref_name }}
            type=gha, scope=${{ github.base_ref }}
          cache-to: type=gha, mode=max, scope=${{ github.ref_name }}


Main Workflow

name: Main Workflow

on:
  push:
    branches:
    - develop
  pull_request:
    branches:
    - "*"
permissions:
  contents: read

jobs:
  tests:
    name: Run Tests
    runs-on: ${{ matrix.operating-system }}
    strategy:
      matrix:
        python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
        operating-system: [ubuntu-latest] # TODO: Fix a couple tests and enable windows. windows-latest
      fail-fast: false
    env:
      POETRY_VERSION: "1.2.2"
    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
    # Can we cache the apt-get stuff somehow?
    - name: Install unrar  # used by some flexget tests
      if: matrix.operating-system == 'ubuntu-latest'
      run: |
        sudo apt-get update
        sudo apt-get install -qy unrar
    # There is some poetry caching built in to setup-python, but there seems to be some confusion
    # about whether it works well when using a matrix of different python versions. Doing caching
    # manually for now, until there is more info.
    - name: Set up Python ${{ matrix.python-version }}
      id: setup-python
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    - name: cache poetry install
      uses: actions/cache@v4
      with:
        path: ~/.local
        key: poetry-${{ matrix.python-version }}-${{ env.POETRY_VERSION }}-0  # increment last number to reset cache
    - uses: snok/install-poetry@v1
      with:
        version: ${{ env.POETRY_VERSION }}
        virtualenvs-create: true
        virtualenvs-in-project: true
    - name: Load cached venv
      id: cached-poetry-dependencies
      uses: actions/cache@v4
      with:
        path: .venv
        key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
    - name: Install dependencies
      if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
      run: poetry install --no-interaction --no-root
    - name: Install project
      run: poetry install --no-interaction
    - name: Test with pytest
      run: |
        poetry run pytest -n auto


Trigger Deploy

name: Trigger Deploy

on:
  schedule:
    - cron: 0 15 * * *
  workflow_dispatch: {}

permissions: {}

jobs:
  deploy:
    permissions:
      contents: read # to fetch code (actions/checkout)
      deployments: write # to create deployment

    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
      - uses: actions/github-script@v7
        name: Create Deployment
        with:
          github-token: ${{ secrets.flexgetbot_pat }}
          script: |
            await github.rest.repos.createDeployment({
              ...context.repo,
              ref: context.ref.slice(11),
            });


No PRs to Master

name: No PRs to Master

on:
  pull_request:
    branches:
      - 'master'

permissions:
  contents: read

jobs:
  fail:
    permissions:
      contents: none
    runs-on: ubuntu-latest
    name: Please make them to develop
    steps:
      - name: No PRs to master, please make them to develop
        run: |
          echo "::error::No PRs should be made against master"
          exit 1


Build and release to PyPI

name: Build and release to PyPI
on: ['deployment']
permissions: {}
jobs:
  release:
    permissions:
      contents: write # for git push
      deployments: write # to create a deployment status

    runs-on: ubuntu-latest
    env:
      PYTHON_VERSION: "3.11"
    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
      with:
        token: ${{ secrets.flexgetbot_pat }}
        fetch-depth: 0
    - name: Setup Git User
      run: |
        git config user.email ${{ secrets.git_email }}
        git config user.name ${{ secrets.git_user }}
    - name: Set up Python ${{ env.PYTHON_VERSION }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ env.PYTHON_VERSION }}
        cache: 'pip'
        cache-dependency-path: 'dev-requirements.txt'
    - name: Install dev dependencies
      run: |
        pip install -r requirements-release.txt
    - name: Set Deployment Status Pending
      uses: justacodename/deployment-status@v2
      with:
        state: pending
    - name: Run release script
      env:
        TWINE_USERNAME: "__token__"
        TWINE_PASSWORD: ${{ secrets.pypi_token }}
      run: |
        ./release.sh
    - name: Build Changelog
      id: changelog
      if: env.release_tag
      run: |
        echo 'CHANGELOG_BODY<<EOF' >> $GITHUB_ENV
        python dev_tools.py get-changelog ${{ env.release_tag }} >> $GITHUB_ENV
        echo 'EOF' >> $GITHUB_ENV
    - name: Create GitHub Release
      uses: ncipollo/release-action@v1
      if: env.release_tag
      with:
        tag: ${{ env.release_tag }}
        artifacts: "dist/*"
        token: ${{ secrets.GITHUB_TOKEN }}
        body: ${{ env.CHANGELOG_BODY }}
    - name: Set Deployment Status Success
      uses: justacodename/deployment-status@v2
      with:
        state: success
    - name: Set Deployment Status Failure
      if: failure() || cancelled()
      uses: justacodename/deployment-status@v2
      with:
        state: failure


Close stale issues and PRs

name: 'Close stale issues and PRs'
on:
  schedule:
    - cron: '30 */12 * * *'
env:
  DAYS_BEFORE_ISSUE_STALE: 150
  DAYS_BEFORE_ISSUE_CLOSE: 30
  DAYS_BEFORE_PR_STALE: 150
  DAYS_BEFORE_PR_CLOSE: 60
permissions:
  contents: read

jobs:
  stale:
    permissions:
      issues: write  # for actions/stale to close stale issues
      pull-requests: write  # for actions/stale to close stale PRs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          stale-issue-message: 'This issue is stale because it has been open ${{ env.DAYS_BEFORE_ISSUE_STALE }} days with no activity. Remove stale label or comment or this will be closed in ${{ env.DAYS_BEFORE_ISSUE_CLOSE }} days.'
          close-issue-message: 'This issue has been stale for ${{ env.DAYS_BEFORE_ISSUE_CLOSE }} days and is being closed.'
          days-before-issue-stale: ${{ env.DAYS_BEFORE_ISSUE_STALE }}
          days-before-issue-close: ${{ env.DAYS_BEFORE_ISSUE_CLOSE }}
          stale-pr-message: 'This PR is stale because it has been open ${{ env.DAYS_BEFORE_PR_STALE }} days with no activity. Remove stale label or comment or this will be closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days.'
          close-pr-message: 'This PR has been stale for ${{ env.DAYS_BEFORE_PR_CLOSE }} days and is being closed. Apologies if this is still relevant, it can be hard to find the time to review and merge everything. Feel free to make sure it is up to date and open it again.'
          days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
          days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
          operations-per-run: 100
          ascending: true


CI

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 }}
    strategy:
      fail-fast: false
      matrix:
        platform: [ "macos-12", "macos-14", "windows-latest", "ubuntu-latest" ]
        python-version: [ "3.8", "3.12", "3.13-dev" ]
        include:
          - experimental: false
          - platform: "ubuntu-latest"
            python-version: "3.9"
            experimental: false
          - platform: "ubuntu-latest"
            python-version: "3.10"
            experimental: false
          - platform: "ubuntu-latest"
            python-version: "3.11"
            experimental: false
          # Allow dev Python to fail without failing entire job
          - python-version: "3.13-dev"
            experimental: true
        exclude:
          # macos-14 (i.e. arm64) does not support Python 3.8
          - platform: "macos-14"
            python-version: "3.8"
    steps:
    - name: Checkout
      uses: actions/checkout@v4.1.7
      with:
        fetch-depth: 0

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

    - name: Get Packages
      uses: actions/download-artifact@v4.1.7
      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.3.3
      with:
        name: coverage-data-${{ matrix.platform }}-${{ matrix.python-version }}
        path: ".coverage.*"
        if-no-files-found: ignore

    - 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-latest
    needs: unit-tests
    if: always() && contains('success,failure', needs.unit-tests.result)
    steps:
    - name: Checkout
      uses: actions/checkout@v4.1.7
      with:
        fetch-depth: 0

    - name: Setup Python
      uses: actions/setup-python@v5.1.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.8"

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

    - name: Retrieve Coverage Data
      uses: actions/download-artifact@v4.1.7
      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.3.3
      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-12", "macos-14", "ubuntu-22.04", "windows-latest" ]

  verify-apps:
    name: Build app
    needs: [ package, package-automation, unit-tests ]
    uses: beeware/.github/.github/workflows/app-build-verify.yml@main
    with:
      # This *must* be the version of Python that is the system Python on the
      # Ubuntu version used to run Linux tests. We use a fixed ubuntu-22.04
      # rather than `-latest` because at some point, `-latest` will become
      # `-24.04`, but it will be a soft changeover, which will cause havoc with
      # the hard Python version requirement for local system packages.
      python-version: "3.10"
      runner-os: ${{ matrix.runner-os }}
      framework: ${{ matrix.framework }}
    strategy:
      fail-fast: false
      matrix:
        framework: [ "toga", "pyside6", "pygame", "console" ]
        runner-os: [ "macos-12", "macos-14", "ubuntu-22.04", "windows-latest" ]


Bump Config File Dependencies

name: Bump Config File Dependencies

on:
  schedule:
    - cron: "0 20 * * SUN"  # Sunday @ 2000 UTC
  workflow_dispatch:

jobs:
  dep-bump-versions:
    name: Bump Config File Dependencies
    uses: beeware/.github/.github/workflows/dep-version-bump.yml@main
    secrets: inherit
    with:
      subdirectory: . automation


Dependabot Change Note

name: Dependabot Change Note

on:
  push:
    branches:
      - 'dependabot/**'

jobs:
  changenote:
    name: Dependabot Change Note
    uses: beeware/.github/.github/workflows/dependabot-changenote.yml@main
    secrets: inherit


Update pre-commit

name: Update pre-commit

on:
  schedule:
    - cron: "0 20 * * SUN"  # Sunday @ 2000 UTC
  workflow_dispatch:

jobs:
  pre-commit-update:
    name: Update pre-commit
    uses: beeware/.github/.github/workflows/pre-commit-update.yml@main
    secrets: inherit


Upload Python Package

name: Upload Python Package

on:
  release:
    types: published

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      # This permission is required for trusted publishing.
      id-token: write
    steps:
      - uses: dsaltares/fetch-gh-release-asset@1.1.2
        with:
          version: tags/${{ github.event.release.tag_name }}
          file: ${{ github.event.repository.name }}.*
          regex: true
          target: dist/

      - name: Publish release to production PyPI
        uses: pypa/gh-action-pypi-publish@release/v1


Create Release

name: Create Release

on:
  push:
    tags:
      - 'v*'

jobs:
  ci:
    name: CI
    uses: ./.github/workflows/ci.yml
    with:
      attest-package: "true"

  docs:
    name: Verify Docs Build
    uses: beeware/.github/.github/workflows/docs-build-verify.yml@main
    secrets: inherit
    with:
      project-name: "briefcase"
      project-version: ${{ github.ref_name }}

  release:
    name: Create Release
    needs: [ ci, docs ]
    runs-on: ubuntu-latest
    permissions:
      contents: write
      # This permission is required for trusted publishing.
      id-token: write
    steps:
      - name: Set build variables
        run: |
          echo "VERSION=${GITHUB_REF_NAME#v}" | tee -a $GITHUB_ENV

      - name: Set up Python
        uses: actions/setup-python@v5.1.0
        with:
          python-version: "3.x"

      - name: Get packages
        uses: actions/download-artifact@v4.1.7
        with:
          name: ${{ needs.ci.outputs.artifact-name }}
          path: dist

      - name: Purge non-release packages
        run: rm -rf dist/x-*

      - name: Install packages
        run: pip install dist/*.whl

      - name: Check version number
        # Check that the setuptools_scm-generated version number is still the same when
        # installed from a wheel with setuptools_scm not present.
        run: |
          set -x
          test $(briefcase --version) = $VERSION

      - name: Create release
        uses: ncipollo/release-action@v1.14.0
        with:
          name: ${{ env.VERSION }}
          draft: true
          artifacts: dist/${{ github.event.repository.name }}*
          artifactErrorsFailBuild: true

      - name: Publish release to Test PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/


tests

name: tests

on:
  merge_group:
  push:
    branches-ignore:
    # temporary GH branches relating to merge queues (jaraco/skeleton#93)
    - gh-readonly-queue/**
    tags:
    # required if branches-ignore is supplied (jaraco/skeleton#103)
    - '**'
  pull_request:

permissions:
  contents: read

env:
  # Environment variable to support color support (jaraco/skeleton#66)
  FORCE_COLOR: 1

  # Suppress noisy pip warnings
  PIP_DISABLE_PIP_VERSION_CHECK: 'true'
  PIP_NO_PYTHON_VERSION_WARNING: 'true'
  PIP_NO_WARN_SCRIPT_LOCATION: 'true'

  # Ensure tests can sense settings about the environment
  TOX_OVERRIDE: >-
    testenv.pass_env+=GITHUB_*,FORCE_COLOR

jobs:
  test:
    strategy:
      # https://blog.jaraco.com/efficient-use-of-ci-resources/
      matrix:
        python:
        - "3.8"
        - "3.12"
        platform:
        - ubuntu-latest
        - macos-latest
        - windows-latest
        include:
        - python: "3.9"
          platform: ubuntu-latest
        - python: "3.10"
          platform: ubuntu-latest
        - python: "3.11"
          platform: ubuntu-latest
        - python: pypy3.10
          platform: ubuntu-latest
    runs-on: ${{ matrix.platform }}
    continue-on-error: ${{ matrix.python == '3.13' }}
    steps:
      - uses: actions/checkout@v4
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python }}
          allow-prereleases: true
      - name: Install tox
        run: python -m pip install tox
      - name: Run
        run: tox

  collateral:
    strategy:
      fail-fast: false
      matrix:
        job:
        - diffcov
        - docs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: 3.x
      - name: Install tox
        run: python -m pip install tox
      - name: Eval ${{ matrix.job }}
        run: tox -e ${{ matrix.job }}

  check:  # This job does nothing and is only used for the branch protection
    if: always()

    needs:
    - test
    - collateral

    runs-on: ubuntu-latest

    steps:
    - name: Decide whether the needed jobs succeeded or failed
      uses: re-actors/alls-green@release/v1
      with:
        jobs: ${{ toJSON(needs) }}

  release:
    permissions:
      contents: write
    needs:
    - check
    if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: 3.x
      - name: Install tox
        run: python -m pip install tox
      - name: Run
        run: tox -e release
        env:
          TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


Continuous Integration

name: Continuous Integration
on:
  schedule:
    - cron: "0 0 * * 2"
  push:
    branches:
      - main
      - staging
      - trying
  pull_request_target:
    branches:
      - main
    paths:
      - .github/workflows/ci.yml
      - "pyvisa/**"
      - pyproject.toml

jobs:
  formatting:
    name: Check code formatting
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Install tools
        run: |
          python -m pip install --upgrade pip
          pip install ruff mypy pytest numpy
      - name: Formatting
        run: |
          ruff format pyvisa --check;
      - name: Linting
        if: always()
        run: |
          ruff pyvisa;
      - name: typing
        if: always()
        run: |
          mypy pyvisa;
  tests:
    name: Unit tests
    runs-on: ${{ matrix.os }}
    needs:
      - formatting
    if: needs.formatting.result == 'success'
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ["3.9", "3.10", "3.11", "3.12"]
        exclude:
          - python-version: '3.9'
            os: macos-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install numpy
      - name: Install project
        run: |
          pip install -e .
      - name: Test with pytest
        run: |
          pip install pytest-cov
          python -X dev -m pytest --pyargs pyvisa --cov --cov-report xml -v
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: unittests
          name: codecov-umbrella
          fail_ci_if_error: true

  # Added to summarize the matrix (otherwise we would need to list every single
  # job in bors.toml)
  tests-result:
    name: Tests result
    if: always()
    needs:
      - tests
    runs-on: ubuntu-latest
    steps:
      - name: Mark the job as a success
        if: needs.tests.result == 'success'
        run: exit 0
      - name: Mark the job as a failure
        if: needs.tests.result != 'success'
        run: exit 1


Documentation building

name: Documentation building
on:
  schedule:
    - cron: "0 0 * * 2"
  push:
    branches:
      - main
      - staging
      - trying
  pull_request_target:
    branches:
      - main
    paths:
      - .github/workflows/docs.yml
      - "pyvisa/**"
      - "docs/**"

jobs:
  docs:
    name: Docs building
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python
        uses: actions/setup-python@v5
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r docs/requirements.txt
      - name: Install project
        run: |
          pip install .
      - name: Install graphviz
        uses: ts-graphviz/setup-graphviz@v2
      - name: Build documentation
        run: |
          mkdir docs_output;
          sphinx-build docs/source docs_output -W -b html;