name: Publish Python 🐍 distributions 📦 to PyPI

on:
  release:
    types:
      - published

permissions:
  contents: read

jobs:
  build-n-publish-pypi:
    name: Build and publish Python 🐍 distributions 📦 to PyPI
    environment: production
    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write
    runs-on: ubuntu-latest
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            files.pythonhosted.org:443
            github.com:443
            pypi.org:443
            upload.pypi.org:443
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
      - name: Set up Python3
        uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
        with:
          python-version: "3.x"
      - name: Install packaging libraries
        run: |
          python -m pip install flit==3.9.0
      - name: Build a binary wheel and a source tarball
        run: |
          python -m flit build
      - name: Publish distribution 📦 to PyPI
        uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0


Scorecard supply-chain security

# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.

name: Scorecard supply-chain security
on:
  # For Branch-Protection check. Only the default branch is supported. See
  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
  branch_protection_rule:
  # To guarantee Maintained check is occasionally updated. See
  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
  schedule:
    - cron: '41 8 * * 4'
  push:
    branches:
      - main

# Declare default permissions as read only.
permissions: read-all

jobs:
  analysis:
    name: Scorecard analysis
    runs-on: ubuntu-latest
    permissions:
      # Needed to upload the results to code-scanning dashboard.
      security-events: write
      # Needed to publish results and get a badge (see publish_results below).
      id-token: write

    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
        with:
          egress-policy: audit

      - name: "Checkout code"
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
        with:
          persist-credentials: false

      - name: "Run analysis"
        uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534
        with:
          results_file: results.sarif
          results_format: sarif
          # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
          # - you want to enable the Branch-Protection check on a *public* repository, or
          # - you are installing Scorecard on a *private* repository
          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
          repo_token: ${{ secrets.OPENSSF_SCORECARD_TOKEN }}

          # Public repositories:
          #   - Publish results to OpenSSF REST API for easy access by consumers
          #   - Allows the repository to include the Scorecard badge.
          #   - See https://github.com/ossf/scorecard-action#publishing-results.
          publish_results: true

      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
      # format to the repository Actions tab.
      - name: "Upload artifact"
        uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808
        with:
          name: SARIF file
          path: results.sarif
          retention-days: 5

      # Upload the results to GitHub's code scanning dashboard.
      - name: "Upload to code-scanning"
        uses: github/codeql-action/upload-sarif@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118  # 3.23.0
        with:
          sarif_file: results.sarif


name: Publish Python 🐍 distributions 📦 to TestPyPI

on:
  push:
    tags:
      - 'v*'

permissions:
  contents: read

jobs:
  build-n-publish-testpypi:
    name: Build and publish Python 🐍 distributions 📦 to TestPyPI
    environment: staging
    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write
    runs-on: ubuntu-latest
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            files.pythonhosted.org:443
            github.com:443
            pypi.org:443
            test.pypi.org:443
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
      - name: Set up Python3
        uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
        with:
          python-version: "3.x"
      - name: Install packaging libraries
        run: |
          python -m pip install flit==3.9.0
      - name: Build a binary wheel and a source tarball
        run: |
          python -m flit build
      - name: Publish distribution 📦 to Test PyPI
        uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0
        with:
          repository-url: https://test.pypi.org/legacy/
          skip-existing: true


Verify Testing Data

name: Verify Testing Data

on:
  pull_request:
    types:
      - opened
      - reopened
      - synchronize
    paths:
      - .github/workflows/main.yml

permissions:
  contents: read

jobs:
  use-latest-tag:
    name: Check Latest xclim-testdata Tag
    runs-on: ubuntu-latest
    if: |
     (github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name)
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            api.github.com:443
            github.com:443
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
      - name: Find xclim-testdata Tag and CI Testing Branch
        run: |
          XCLIM_TESTDATA_TAG="$( \
          git -c 'versionsort.suffix=-' \
          ls-remote --exit-code --refs --sort='version:refname' --tags https://github.com/Ouranosinc/xclim-testdata '*.*.*' \
          | tail --lines=1 \
          | cut --delimiter='/' --fields=3)"
          echo "XCLIM_TESTDATA_TAG=${XCLIM_TESTDATA_TAG}" >> $GITHUB_ENV
          XCLIM_TESTDATA_BRANCH="$(grep -E "XCLIM_TESTDATA_BRANCH" .github/workflows/main.yml | cut -d ' ' -f4)"
          echo "XCLIM_TESTDATA_BRANCH=${XCLIM_TESTDATA_BRANCH}" >> $GITHUB_ENV
      - name: Report Versions Found
        run: |
          echo "Latest xclim-testdata tag: ${{ env.XCLIM_TESTDATA_TAG }}"
          echo "Tag for xclim-testdata in CI: ${{ env.XCLIM_TESTDATA_BRANCH }}"
      - name: Find Comment
        uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
        id: fc
        with:
          issue-number: ${{ github.event.pull_request.number }}
          comment-author: 'github-actions[bot]'
          body-includes: It appears that this Pull Request modifies the `main.yml` workflow.
      - name: Compare Versions
        if: ${{( env.XCLIM_TESTDATA_TAG != env.XCLIM_TESTDATA_BRANCH )}}
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            core.setFailed('Configured `xclim-testdata` tag is not `latest`.')
      - name: Update Failure Comment
        if: ${{ failure() }}
        uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
        with:
          comment-id: ${{ steps.fc.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          body: |
            > **Warning**
            > It appears that this Pull Request modifies the `main.yml` workflow.

            On inspection, it seems that the `XCLIM_TESTDATA_BRANCH` environment variable is set to a tag that is not the latest in the `Ouranosinc/xclim-testdata` repository.

            This value must match the most recent tag (`${{ env.XCLIM_TESTDATA_TAG }}`) in order to merge this Pull Request.

            If this PR depends on changes in a new testing dataset branch, be sure to tag a new version of `Ouranosinc/xclim-testdata` once your changes have been merged to its `main` branch.
          edit-mode: replace
      - name: Update Success Comment
        if: ${{ success() }}
        uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
        with:
          comment-id: ${{ steps.fc.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          body: |
            > **Note**
            > It appears that this Pull Request modifies the `main.yml` workflow.

            On inspection, the `XCLIM_TESTDATA_BRANCH` environment variable is set to the most recent tag (`${{ env.XCLIM_TESTDATA_TAG }}`).

            No further action is required.
          edit-mode: replace


Test Upstream Dependencies

name: Test Upstream Dependencies
on:
  push:
    branches:
      - main
    paths-ignore:
      - CHANGES.rst
      - README.rst
      - pyproject.toml
      - xclim/__init__.py
  schedule:
    - cron: "0 0 * * *" # Daily “At 00:00” UTC
  workflow_dispatch: # allows you to trigger the workflow run manually

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

permissions:
  contents: read

jobs:
  upstream-dev:
    name: test-upstream-dev (Python${{ matrix.python-version }})
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: write
    if: |
      (github.event_name == 'schedule') ||
      (github.event_name == 'workflow_dispatch') ||
      (github.event_name == 'push')
    strategy:
      fail-fast: false
      matrix:
        python-version:
          - "3.10"
    defaults:
      run:
        shell: bash -l {0}
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            api.github.com:443
            conda.anaconda.org:443
            dap.service.does.not.exist:443
            files.pythonhosted.org:443
            github.com:443
            objects.githubusercontent.com:443
            pypi.org:443
            raw.githubusercontent.com:443
            repo.anaconda.com:443
      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
        with:
          fetch-depth: 0 # Fetch all history for all branches and tags.
      - name: Setup Conda (Micromamba) with Python${{ matrix.python-version }}
        uses: mamba-org/setup-micromamba@f8b8a1e23a26f60a44c853292711bacfd3eac822 # v1.9.0
        with:
          cache-downloads: true
          cache-environment: true
          environment-file: environment.yml
          create-args: >-
            conda
            eigen
            pybind11
            python=${{ matrix.python-version }}
            pytest-reportlog
      - name: Conda and Mamba versions
        run: |
          conda --version
          echo "micromamba: $(micromamba --version)"
      - name: Install upstream versions and SBCK
        run: |
          python -m pip install -r requirements_upstream.txt
          python -m pip install "sbck @ git+https://github.com/yrobink/SBCK-python.git@master"
      - name: Install xclim
        run: |
          python -m pip install --no-user --no-deps --editable .
      - name: Check versions
        run: |
          conda list
          xclim show_version_info
          python -m pip check || true
      - name: Run Tests
        if: success()
        id: status
        run: |
          python -m pytest --numprocesses=logical --durations=10 --cov=xclim --cov-report=term-missing --report-log output-${{ matrix.python-version }}-log.jsonl
      - name: Generate and publish the report
        if: |
          failure()
          && steps.status.outcome == 'failure'
          && github.event_name == 'schedule'
          && github.repository_owner == 'Ouranosinc'
        uses: xarray-contrib/issue-from-pytest-log@138db94bfe4b12ac11fc1aff307ee0835feab403 # v1.2.8
        with:
          log-path: output-${{ matrix.python-version }}-log.jsonl


Workflow Changes Warnings

name: Workflow Changes Warnings

on:
  # Note: potential security risk from this action using pull_request_target.
  # Do not add actions in here which need a checkout of the repo, and do not use any caching in here.
  # See: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
  pull_request_target:
    types:
      - opened
      - reopened
      - synchronize
    paths:
      - .github/workflows/*.yml

permissions:
  contents: read

jobs:
  comment-concerning-workflow-changes:
    name: Comment Concerning Workflow Changes
    runs-on: ubuntu-latest
    if: |
      (github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name)
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Harden Runner
        uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
        with:
          disable-sudo: true
          egress-policy: block
          allowed-endpoints: >
            api.github.com:443
      - name: Find comment
        uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
        id: fc
        with:
          issue-number: ${{ github.event.pull_request.number }}
          comment-author: 'github-actions[bot]'
          body-includes: |
            This Pull Request modifies GitHub workflows and is coming from a fork.
      - name: Create comment
        if: |
          (steps.fc.outputs.comment-id == '') &&
          (!contains(github.event.pull_request.labels.*.name, 'approved')) &&
          (github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name)
        uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
        with:
          comment-id: ${{ steps.fc.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          body: |
            > **Warning**
            > This Pull Request modifies GitHub Workflows and is coming from a fork.
            **It is very important for the reviewer to ensure that the workflow changes are appropriate.**
          edit-mode: replace
      - name: Update comment
        if: |
          contains(github.event.pull_request.labels.*.name, 'approved')
        uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
        with:
          comment-id: ${{ steps.fc.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          body: |
            > **Note**
            > Changes have been approved by a maintainer.
          reactions: |
            hooray
          edit-mode: append


Docker Image CI

name: Docker Image CI

on:
  push:
    branches: [ "master" ]
  workflow_dispatch: # run manually

env:
  GHCRIO_DOCKER_TAG: ghcr.io/ocr-d/core
  DOCKERIO_DOCKER_TAG: docker.io/ocrd/core

jobs:

  build:

    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read

    steps:
    - name: Checkout
      uses: actions/checkout@v4
      with:
        # we need tags for docker version tagging
        fetch-tags: true
        fetch-depth: 0
    - # Activate cache export feature to reduce build time of images
      name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    - name: Build the Docker image
      run: make docker
    - name: Build the Docker image with GPU support
      run: make docker-cuda
    - name: Alias Docker images
      # default tag uses docker.io, so tag post-hoc
      run: |
        docker tag ocrd/core ${{ env.GHCRIO_DOCKER_TAG }}
        docker tag ocrd/core-cuda ${{ env.GHCRIO_DOCKER_TAG }}-cuda
    - name: Smoke Test that ocrd --help works
      run: |
        docker run --rm ${{ env.GHCRIO_DOCKER_TAG }} ocrd --version
        docker run --rm ${{ env.GHCRIO_DOCKER_TAG }}-cuda ocrd --version
    - name: Login to GitHub Container Registry
      uses: docker/login-action@v3
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    - name: Log in to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKERIO_USERNAME }}
        password: ${{ secrets.DOCKERIO_PASSWORD }}
    - name: Push images to Github Container Registry
      run: |
        docker push ${{ env.GHCRIO_DOCKER_TAG }}:latest
        docker push ${{ env.GHCRIO_DOCKER_TAG }}-cuda:latest
    - name: Push images to Docker Hub
      run: |
        docker tag ${{ env.GHCRIO_DOCKER_TAG }} ${{ env.DOCKERIO_DOCKER_TAG }}
        docker tag ${{ env.GHCRIO_DOCKER_TAG }}-cuda ${{ env.DOCKERIO_DOCKER_TAG }}-cuda
        docker push ${{ env.DOCKERIO_DOCKER_TAG }}:latest
        docker push ${{ env.DOCKERIO_DOCKER_TAG }}-cuda:latest
        docker push ${{ env.DOCKERIO_DOCKER_TAG }}:latest
        docker push ${{ env.DOCKERIO_DOCKER_TAG }}-cuda:latest
        docker tag ${{ env.DOCKERIO_DOCKER_TAG }} ${{ env.DOCKERIO_DOCKER_TAG }}:$(git describe --tags --abbrev=0)
        docker tag ${{ env.DOCKERIO_DOCKER_TAG }}-cuda ${{ env.DOCKERIO_DOCKER_TAG }}-cuda:$(git describe --tags --abbrev=0)
        docker push ${{ env.DOCKERIO_DOCKER_TAG }}:$(git describe --tags --abbrev=0)
        docker push ${{ env.DOCKERIO_DOCKER_TAG }}-cuda:$(git describe --tags --abbrev=0)


name: ocrd network module and integration tests

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]
  workflow_dispatch: # run manually

jobs:
  build:

    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        python-version:
          - '3.8'
          - '3.9'
          - '3.10'
          - '3.11'
          - '3.12'
        os:
          - ubuntu-22.04
          # - macos-latest

    steps:
      - uses: actions/checkout@v4
      - name: Set up Homebrew
        id: set-up-homebrew
        uses: Homebrew/actions/setup-homebrew@master
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          if [[ "${{ matrix.os }}" == "ubuntu"* ]];then
            sudo apt-get -y update
            sudo make deps-ubuntu
          else
            HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 \
            HOMEBREW_NO_AUTO_UPDATE=1 \
            brew install imagemagick geos bash  # opencv
          fi
          make install deps-test
      - name: Install Docker on macOS
        if: runner.os == 'macos'
        run: |
          brew install docker docker-compose
          colima start
      - name: Run network modules tests with pytest
        run: make network-module-test
      - name: Run network integration tests with pytest
        run: |
          if [[ "${{ matrix.os }}" == "macos"* ]];then
            make network-integration-test-cicd DOCKER_COMPOSE=docker-compose
          else
            make network-integration-test-cicd
          fi


Test core installation and run tests

name: Test core installation and run tests

# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  build:

    runs-on: ${{ matrix.os }}

    strategy:
      fail-fast: false
      matrix:
        python-version:
          - '3.8'
          - '3.9'
          - '3.10'
          - '3.11'
          - '3.12'
        os:
          - ubuntu-22.04
          - ubuntu-20.04
          - macos-latest

    steps:
    - uses: actions/checkout@v4
    - name: Set up Homebrew
      id: set-up-homebrew
      uses: Homebrew/actions/setup-homebrew@master
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        if [[ "${{ matrix.os }}" == "ubuntu"* ]];then
          sudo apt-get -y update
          sudo make deps-ubuntu
        else
          HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 \
          HOMEBREW_NO_AUTO_UPDATE=1 \
          brew install imagemagick geos bash  # opencv
        fi
        make install deps-test
    - name: Test with pytest
      run: |
        make test benchmark
    - name: test to ensure that --editable install works
      run: |
        make install-dev; ocrd --version
    - name: Lint with flake8
      run: |
        python -m pip install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 src --count --exit-zero --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 src --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics


CI

name: CI

on:
  # Triggers the workflow on push only for the master branch or pull request events
  push:
    branches: [master]
  pull_request:

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

defaults:
  run:
    shell: bash

jobs:
  # This workflow contains a single job called "build"
  build:
    name: "Python ${{ matrix.python-version }} on ${{ matrix.os }} ${{ matrix.name-suffix }}"
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        include:
          - name-suffix: "PyQt5 sdist"
            os: ubuntu-20.04
            python-version: "3.8"
            BUILD_OPTION: --sdist
            QT_BINDING: PyQt5
            RUN_TESTS_OPTIONS: --qt-binding=PyQt5 --no-opencl --low-mem
          - name-suffix: "PyQt5 wheel"
            os: macos-latest
            python-version: "3.10"
            BUILD_OPTION: --wheel
            QT_BINDING: PyQt5
            RUN_TESTS_OPTIONS: --qt-binding=PyQt5 --no-opencl --low-mem

          - name-suffix: "PySide6 sdist"
            os: ubuntu-latest
            python-version: "3.8"
            BUILD_OPTION: --sdist
            QT_BINDING: PySide6
            RUN_TESTS_OPTIONS: --qt-binding=PySide6 --no-opencl --low-mem
          - name-suffix: "PySide6 wheel"
            os: macos-latest
            python-version: "3.9"
            BUILD_OPTION: --wheel
            QT_BINDING: "PySide6<6.7"
            RUN_TESTS_OPTIONS: --qt-binding=PySide6 --no-opencl --low-mem

          - name-suffix: "PyQt6 wheel"
            os: ubuntu-latest
            python-version: "3.11"
            BUILD_OPTION: --wheel
            QT_BINDING: PyQt6
            RUN_TESTS_OPTIONS: --qt-binding=PyQt6 --no-opengl --low-mem
          - name-suffix: "PyQt6 wheel"
            os: macos-latest
            python-version: "3.11"
            BUILD_OPTION: --wheel
            QT_BINDING: PyQt6
            RUN_TESTS_OPTIONS: --qt-binding=PyQt6 --no-opencl --low-mem

          - name-suffix: "No GUI wheel"
            os: windows-latest
            python-version: "3.9"
            BUILD_COMMAND: --wheel
            QT_BINDING: PyQt5
            RUN_TESTS_OPTIONS: --no-gui --low-mem
            # No GUI tests on Windows

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      - uses: actions/checkout@v4

        # Install X server packages
        # libegl1-mesa: Required by Qt xcb platform plugin
        # ocl-icd-opencl-dev: OpenCL headers, lib and icd loader
        # libgl1-mesa-glx: For OpenGL
        # xserver-xorg-video-dummy: For OpenGL
        # libxkbcommon-x11-0: needed for Qt plugins
      - name: Install X server
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install libegl1-mesa ocl-icd-opencl-dev intel-opencl-icd libgl1-mesa-glx xserver-xorg-video-dummy libxkbcommon-x11-0 libxkbcommon0 libxkbcommon-dev libxcb-icccm4 libxcb-image0 libxcb-shm0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-render0 libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxcb-cursor0 libxcb1

      # Runs a single command using the runners shell
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: "pip"

      - name: Upgrade distribution modules
        run: |
          python -m pip install --upgrade pip
          pip install --upgrade build setuptools wheel
          pip install --upgrade --pre cython

      - name: Print python info used for build
        run: |
          python ./ci/info_platform.py
          pip list

      - name: Generate source package or wheel
        run: |
          if [ ${{ runner.os }} == 'macOS' ]; then
              export MACOSX_DEPLOYMENT_TARGET=10.9;
          fi
          python -m build --no-isolation ${{ matrix.BUILD_OPTION }}
          ls dist

      - name: Pre-install dependencies
        run: |
          if [ -s "ci/requirements-pinned.txt" ];
          then
            pip install -r ci/requirements-pinned.txt;
          fi
          pip install --pre -r requirements.txt
          pip uninstall -y PyQt5 PyQt6 PySide6
          pip install --pre "${{ matrix.QT_BINDING }}"

      - name: Install pytest
        run: |
          pip install pytest
          pip install pytest-xvfb
          pip install pytest-mock

      - name: Install silx package
        run: pip install --pre --find-links dist/ --no-cache-dir --no-index --no-build-isolation silx

      - name: Print python info used for tests
        run: |
          python ./ci/info_platform.py
          pip list

      # For Linux: Start X server with dummy video dirver
      # Use this instead of Xvfb to have RANDR extension
      # Otherwise there is a bug with Qt5.10.0
      - name: Run the tests
        run: |
          if [ ${{ runner.os }} == 'Linux' ]; then
              export OCL_ICD_VENDORS=$(pwd)/intel_opencl_icd/vendors
              export DISPLAY=:99.0
              Xorg -noreset +extension GLX +extension RANDR +extension RENDER -logfile ./99.log -config ./ci/xorg.conf :99 &
              sleep 3
          fi
          echo "RUN_TESTS_OPTIONS="${{ matrix.RUN_TESTS_OPTIONS }}
          python run_tests.py --installed -v ${{ matrix.RUN_TESTS_OPTIONS }}


Build and deploy

name: Build and deploy

on:
  workflow_dispatch:
  release:
    types:
      - published

jobs:
  build_sdist:
    name: Build source distribution
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build twine
      - name: Build sdist
        run: python -m build --sdist
      - name: Check the package
        run: python -m twine check dist/*
      - uses: actions/upload-artifact@v4
        with:
          name: cibw-sdist
          path: dist/*.tar.gz

  test_sdist:
    needs: [build_sdist]
    name: Test source distribution
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"
      - uses: actions/download-artifact@v4
        with:
          name: cibw-sdist
          path: dist
      - name: Install from sdist
        run: pip install "$(ls dist/silx-*.tar.gz)[full,test]"
      - name: Run tests
        env:
          WITH_QT_TEST: "False"
          WITH_GL_TEST: "False"
          SILX_OPENCL: "False"
          SILX_TEST_LOW_MEM: "False"
        run: python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=3))"

  build_doc:
    name: Build documentation
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: "pip"
      - name: Install pandoc&graphviz
        run: sudo apt-get install pandoc graphviz
      - name: Install silx
        run: pip install .[full,test,doc]
      - name: Build doc
        env:
          QT_QPA_PLATFORM: "offscreen"
        run: |
          export SILX_VERSION="$(python -c 'import silx; print(silx.strictversion)')"
          sphinx-build doc/source/ "silx-${SILX_VERSION}_documentation/"
          zip -r "silx-${SILX_VERSION}_documentation.zip" "silx-${SILX_VERSION}_documentation/"
      - uses: actions/upload-artifact@v4
        with:
          name: documentation
          path: silx-*_documentation.zip

  build_windows_installer:
    name: Build Windows "fat binary"
    runs-on: windows-2022
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
          cache: "pip"
      - name: Install silx
        run: pip install .[full,test]
      - name: Install pyinstaller
        # Install pyinstaller from source and compile bootloader
        env:
          PYINSTALLER_COMPILE_BOOTLOADER: "1"
          PYINSTALLER_BOOTLOADER_WAF_ARGS: "--msvc_target=x64"
        run: pip install pyinstaller --no-binary pyinstaller
      - name: Build fat binary
        run: |
          cd package/windows
          pyinstaller pyinstaller.spec
      - uses: actions/upload-artifact@v4
        with:
          name: windows-installer
          path: |
            ./package/windows/artifacts/silx-*.exe
            ./package/windows/artifacts/silx-*.zip

  test_windows_installer:
    needs: [build_windows_installer]
    name: Test Windows "fat binary"
    runs-on: windows-2022
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: windows-installer
      - name: Unzip "fat binary"
        run: 7z x silx-*.zip
      - name: Test
        run: |
          cd silx
          ./silx-view.exe --help
          ./silx.exe --help

  build_wheels:
    name: Build wheels on ${{ matrix.os }}-${{ matrix.cibw_archs }}
    runs-on: ${{ matrix.os }}
    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-20.04
            cibw_archs: "auto64"
            with_sse2: true
          - os: ubuntu-20.04
            cibw_archs: "aarch64"
            with_sse2: false
          - os: ubuntu-20.04
            cibw_archs: "ppc64le"
            with_sse2: false
          - os: windows-2019
            cibw_archs: "auto64"
            with_sse2: true
          - os: macos-11
            cibw_archs: "universal2"
            with_sse2: true

    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-qemu-action@v3
        if: runner.os == 'Linux'
        with:
          platforms: all
      - uses: pypa/cibuildwheel@v2.16.5
        env:
          # Configure silx build
          SILX_FORCE_CYTHON: "True"
          SILX_WITH_OPENMP: "False"
          # Configure silx tests
          WITH_QT_TEST: "False"
          WITH_GL_TEST: "False"
          SILX_OPENCL: "False"
          SILX_TEST_LOW_MEM: "False"

          CIBW_ENVIRONMENT_PASS_LINUX: SILX_FORCE_CYTHON SILX_WITH_OPENMP WITH_QT_TEST WITH_GL_TEST SILX_OPENCL SILX_TEST_LOW_MEM

          CIBW_BUILD_VERBOSITY: 1
          CIBW_BUILD: cp38-* cp39-* cp310-* cp311-* cp312-*
          # Do not build for pypy and muslinux
          CIBW_SKIP: pp* *-musllinux_*
          CIBW_ARCHS: ${{ matrix.cibw_archs }}

          # Install test dependencies
          CIBW_TEST_EXTRAS: "full,test"
          CIBW_TEST_COMMAND: python -c "import silx.test, sys; sys.exit(silx.test.run_tests(verbosity=3))"
          # Skip tests for emulated architectures and arm64 macos
          # Skip cp38 test on macos: Issue with libOpenGL
          # Skip cp311 and cp312 on macos: bitshuffle does not build there
          CIBW_TEST_SKIP: "*-*linux_{aarch64,ppc64le,s390x} *-macosx_arm64 *-macosx_universal2:arm64 cp38-macosx* cp311-macosx* cp312-macosx*"

      - uses: actions/upload-artifact@v4
        with:
          name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
          path: ./wheelhouse/*.whl

  pypi-publish:
    needs: [build_doc, build_sdist, build_wheels, test_sdist, build_windows_installer, test_windows_installer]
    name: Upload release to PyPI
    runs-on: ubuntu-latest
    environment:
      name: pypi
    permissions:
      id-token: write
    if: github.event_name == 'release' && github.event.action == 'published'
    # or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
    # if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: cibw-*
          path: dist
          merge-multiple: true
      - uses: pypa/gh-action-pypi-publish@release/v1

  windows-installer-publish:
    needs: [build_windows_installer, test_windows_installer]
    name: Upload Windows installer
    runs-on: ubuntu-latest
    environment:
      name: assets
    permissions:
      contents: write
    if: github.event_name == 'release' && github.event.action == 'published'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: windows-installer
      - name: Upload release assets
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          export ASSETS=$(ls silx-*.exe silx-*.zip)
          echo "ASSETS=${ASSETS}"
          gh release upload ${{ github.event.release.tag_name }} ${ASSETS}


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: '33 0 * * 2'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    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


Semgrep

on:
  pull_request: {}
  push:
    branches:
    - main
    - master
name: Semgrep
jobs:
  semgrep:
    name: Scan
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: returntocorp/semgrep-action@v1
      with:
        auditOn: push
        publishToken: ${{ secrets.SEMGREP_APP_TOKEN }}
        publishDeployment: 1581


Meerk40t (CentOS 7)

# This workflow builds PyInstaller single-file executables
# of Meerk40t for Linux (CentOS 7 self-hosted runner)

name: Meerk40t (CentOS 7)

on:
  release:
    types: [published]

jobs:
  build-centos7:
    runs-on: centos7
    steps:
    - name: Checkout meerk40t
      uses: actions/checkout@v2

    - name: Ensure dependencies installed
      run: |
        pip3 install ezdxf pyserial
        pip3 install scipy
        pip3 install meerk40t-camera opencv-python-headless

    - name: Build Meerk40t
      run: |
        mv meerk40t.py mk40t.py
        pyinstaller --windowed --onefile --name meerk40t .github/workflows/linux/meerk40t.spec
        mv mk40t.py meerk40t.py
        mv dist/MeerK40t dist/MeerK40t-Linux

    - name: Upload Release Assets
      id: release
      uses: softprops/action-gh-release@v1
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        files: |
          dist/MeerK40t-Linux


CodeQL

name: "CodeQL"

on:
  push:
    branches: [main, legacy6, legacy7, legacy8]
    paths:
      - '**.py'
      - '.github/workflows/codeql*.yml'
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [main, legacy6, legacy7, legacy8]
    paths:
      - '**.py'
      - '.github/workflows/codeql*.yml'
  schedule:
    - cron: '0 23 * * 5'

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

defaults:
  run:
    shell: bash

jobs:
  analyze:
    name: CodeQL
    runs-on: ubuntu-latest
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-18.04]
        python-version: [3.9]

    steps:
    - name: Checkout ${{ github.ref }}
      uses: actions/checkout@v2

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

    - name: Identify precise Python version
      id: full-python-version
      shell: bash
      run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")

    - name: Cache Python env for ${{ matrix.os }}-py${{ steps.full-python-version.outputs.version }}
      uses: actions/cache@v2
      with:
        path: ${{ env.pythonLocation }}
        key: ${{ matrix.os }}-py${{ steps.full-python-version.outputs.version }}-${{ hashFiles('requirements.txt') }}

    - name: Install Python dependencies
      run: |
        python3 -m pip install --upgrade --upgrade-strategy eager pip setuptools wheel babel
        if [ -f requirements-nogui.txt ]; then pip install --upgrade --upgrade-strategy eager -r requirements-nogui.txt; fi

    - name: List environment
      env:
        GITHUB_CONTEXT: ${{ toJSON(github) }}
        JOB_CONTEXT: ${{ toJSON(job) }}
        STEPS_CONTEXT: ${{ toJSON(steps) }}
        RUNNER_CONTEXT: ${{ toJSON(runner) }}
        STRATEGY_CONTEXT: ${{ toJSON(strategy) }}
        MATRIX_CONTEXT: ${{ toJSON(matrix) }}
      run: |
        pip list
        env

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: python
        # 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

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


MeerK40t (Macintosh)

name: MeerK40t (Macintosh)
##Options for VM
##macos-10.15 or macos-latest
##macos-11
##
on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  build-macos11:
    runs-on: macos-11
    steps:
    - name: Checkout meerk40t
      uses: actions/checkout@v3
##    - name: Set up Python 3.9
##      uses: actions/setup-python@v2
##      with:
##        python-version: 3.9
    - name: Alternative Python Install if needed
      run: |
        wget https://www.python.org/ftp/python/3.8.10/python-3.8.10-macosx10.9.pkg
        sudo installer -pkg python-3.8.10-macosx10.9.pkg -target /
    - name: Install Mac dependencies
      run: |
        which curl
        python3 -V
        which wget
        which git
        which gcc
        which xcode-select
        which cmake
        which pkgbuild
        which productsign
        which plutil
        which codesign
        which xcrun
        which xcode-select
        ls
        git clone https://github.com/libusb/libusb.git
        cd libusb
        ./autogen.sh && ./configure && make && sudo make install
        cd ..
        curl --remote-name http://www.ijg.org/files/jpegsrc.v9c.tar.gz
        tar -zxvf jpegsrc.v9c.tar.gz
        cd jpeg-9c
        ./configure && make && sudo make install
        cd ..
        ## git clone https://github.com/numpy/numpy.git
        ## cd numpy
        ## python3.8 -m pip install .
        ## cd ..

    - name: Ensure latest pip and meerk40t-camera
      run: |
        python3.8 -m pip install --upgrade pip
        python3.8 -m pip install wheel setuptools
        python3.8 -m pip install meerk40t-camera opencv-python-headless==3.4.13.47
        python3.8 -m pip install --no-use-pep517 pyinstaller==4.3

        ## numpy toggle based on 32 bit fix
        python3.8 -m pip install six numpy ezdxf
        ## python3.8 -m pip install wheel six ezdxf

        python3.8 -m pip install potracer

        python3.8 -m pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 wxPython==4.1.1
        if [ -f requirements-nogui.txt ]; then python3.8 -m pip install -r requirements-nogui.txt; fi
        python3.8 -m pip uninstall -y meerk40t[gui]

    - name: Build meerk40t
      id: build
      run: |
        ## git clone https://github.com/meerk40t/meerk40t.git
        ## cd meerk40t
        ## git pull
        git clone https://github.com/ronaldoussoren/py2app.git
        cd py2app
        git checkout master
        git pull
        python3.8 -m pip install .
        cd ..
        python3.8 -m pip list
            ##Set bundle version
            ##Download the extra files here meerk40t.icns entitlements.plist setup_mac.py
            ##May need to compare extras_required vs. current version of setup.py
            ##Unpack inside the meerk40t directory
        cp .github/workflows/mac/setup_mac.py setup_mac.py
        cp .github/workflows/mac/entitlements.plist entitlements.plist
        cp .github/workflows/mac/meerk40t.icns meerk40t.icns
        cp .github/workflows/mac/libusb-1.0.dylib libusb-1.0.dylib
        VERSION=$(python3.8 meerk40t.py -V)
        MKVERSION=$(echo $VERSION | cut -d" " -f 2)
        sed -i "" "s|0.0.0|$MKVERSION|g" setup_mac.py

        ## Export env variable to github actions scope to use in tag_name of upload release
        ## echo "::set-output name=mkversion::$(echo $MKVERSION)"
        echo "mkversion=$(echo $MKVERSION)" >> $GITHUB_OUTPUT
        ## python3.8 setup_mac.py py2app --dylib-excludes /usr/local/lib/libssl.1.1.dylib,/usr/local/lib/libcrypto.1.1.dylib,/Users/runner/hostedtoolcache/Python/3.9.7/x64/lib/python3.9/site-packages/PIL/.dylibs/libfreetype.6.dylib,/Users/runner/hostedtoolcache/Python/3.9.7/x64/lib/python3.9/site-packages/PIL/.dylibs/liblzma.5.dylib --frameworks libusb-1.0.dylib
        mv meerk40t.py mk40t.py
        python3.8 -m PyInstaller --hidden-import wx._adv --hidden-import wx._xml --hidden-import meerk40t --windowed --noconfirm -i meerk40t.icns --add-data 'locale/:locale/' --name MeerK40t mk40t.py
        mv mk40t.py meerk40t.py
        rm -rf dist/MeerK40t
        sed -i "" "s|$MKVERSION|0.0.0|g" setup_mac.py
        ## Backwards compatibility
        rm dist/MeerK40t.app/Contents/MacOS/libusb-1.0.dylib
        cp libusb-1.0.dylib dist/MeerK40t.app/Contents/MacOS/libusb-1.0.dylib
        ## plutil -insert NSRequiresAquaSystemAppearance -bool NO dist/meerk40t.app/Contents/Info.plist
        plutil -insert NSRequiresAquaSystemAppearance -bool NO dist/meerk40t.app/Contents/Info.plist
        plutil -insert CFBundleDevelopmentRegion -string en dist/meerk40t.app/Contents/Info.plist
        plutil -insert CFAppleHelpAnchor -string YES dist/meerk40t.app/Contents/Info.plist
        plutil -replace CFBundleIdentifier -string org.tatarize.MeerK40t dist/meerk40t.app/Contents/Info.plist
        plutil -replace CFBundleShortVersionString -string $MKVERSION dist/meerk40t.app/Contents/Info.plist
        plutil -insert CFBundleSupportedPlatforms -string MacOSX dist/meerk40t.app/Contents/Info.plist
        ## plutil -insert CFBundleTypeExtensions -string "*" dist/meerk40t.app/Contents/Info.plist
        plutil -insert CFBundleTypeRole -string Editor dist/meerk40t.app/Contents/Info.plist
        plutil -insert CFBundleVersion -string $MKVERSION dist/meerk40t.app/Contents/Info.plist
        plutil -insert LSApplicationCategoryType -string public.app-category.utilities dist/meerk40t.app/Contents/Info.plist
        ## plutil -insert LSFileQuarantineEnabled -string YES dist/meerk40t.app/Contents/Info.plist
        ## plutil -insert NSAppleScriptEnabled -bool NO dist/meerk40t.app/Contents/Info.plist
        plutil -insert NSHumanReadableCopyright -string "Copyright © 2019-2021 MeerK40t Developers, MIT License" dist/meerk40t.app/Contents/Info.plist

        ## plutil -insert  -string  dist/meerk40t.app/Contents/Info.plist
        ## plutil -replace  -string  dist/meerk40t.app/Contents/Info.plist
        SIG="-"
        plutil -replace CFBundleSupportedPlatforms -xml "<array><string>MacOSX</string></array>" dist/MeerK40t.app/Contents/Info.plist

        ## Fix the numpy 32 bit issue inside the app bundle - or install numpy from source.
        ## lipo dist/MeerK40t.app/Contents/MacOS/libgcc_s.1.dylib -remove i386 -output dist/MeerK40t.app/Contents/MacOS/libgcc_s_new.1.dylib
        ## rm dist/MeerK40t.app/Contents/MacOS/libgcc_s.1.dylib
        ## mv dist/MeerK40t.app/Contents/MacOS/libgcc_s_new.1.dylib dist/MeerK40t.app/Contents/MacOS/libgcc_s.1.dylib

        echo "Codesign for GITHUB"
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/*.zip

        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/wx/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/Python
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/PIL/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/numpy/random/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/numpy/linalg/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/numpy/fft/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/numpy/core/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/lib-dynload/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/ezdxf/acc/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/cv2/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/*.dylib
        ## codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/*.so
        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app/Contents/MacOS/MeerK40t

        codesign -s $SIG --force --verbose=4 --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/MeerK40t.app
        ## codesign -s $SIG --deep --timestamp --entitlements entitlements.plist -i org.tatarize.MeerK40t -o runtime --arch x86_64 dist/meerk40t.app

        echo "DMG Build..."
        hdiutil create -volname "MeerK40t Mac" -srcfolder dist -ov MeerK40t_unsigned.dmg
            ## GUI function check - Fails on runner (security settings).
        ## open dist/MeerK40t.app
            ## Binary bundle CLI function check - Fails on runner (security settings).
        ##. dist/MeerK40t.app/Contents/MacOS/MeerK40t -V
            ##I don't do this part; mv dist/meerk40t dist/MeerK40t-Ubuntu-Latest

    - name: Directory contents
      run: |
        cd ..
        ls
        cd ..
        ls

# Switched to using softprops/action-gh-release@v1
# because it supports uploading to existing release based on current tag.
    - name: Upload Release Assets
      id: release
      uses: softprops/action-gh-release@v1
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        files: |
          MeerK40t_unsigned.dmg


Platform configurations

name: Platform configurations

on:
  workflow_dispatch:

concurrency:
  group: platform-configuration
  cancel-in-progress: true

jobs:
  check-mac:
    name: ${{ matrix.os }} system_profiler
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macos-10.15, macos-11]

    steps:
    - name: Create artifacts directory
      run: mkdir -p artifacts

    - name: system_profiler basic
      run: |
        system_profiler -detailLevel basic | tee artifacts/${{ matrix.os }}_system_profile_basic.txt

    - name: system_profiler full
      run: |
        system_profiler -detailLevel full | tee artifacts/${{ matrix.os }}_system_profile_full.txt

    - name: Upload analysis results
      uses: actions/upload-artifact@v2
      with:
        name: System-Information
        path: artifacts/

  check-ubuntu:
    name: ${{ matrix.os }} lshw
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-18.04, ubuntu-20.04]

    steps:
    - name: Create artifacts directory
      run: mkdir -p artifacts

    - name: lshw
      run: |
        sudo lshw | tee artifacts/${{ matrix.os }}-lshw.txt
        sudo lshw -html > artifacts/${{ matrix.os }}-lshw.html

    - name: Upload analysis results
      uses: actions/upload-artifact@v2
      with:
        name: System-Information
        path: artifacts/

  check-windows:
    name: ${{ matrix.os }} systeminfo
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-2016, windows-2019, windows-2022]

    steps:
    - name: Create artifacts directory
      run: mkdir -p artifacts

    - name: systeminfo
      run: |
        systeminfo
        systeminfo > artifacts/${{ matrix.os }}-systeminfo.txt

    - name: Upload analysis results
      uses: actions/upload-artifact@v2
      with:
        name: System-Information
        path: artifacts/


Pull Request Labeler

# Add labels to Pull-Requests based on the types of files modified
# https://github.com/actions/labeler

name: "Pull Request Labeler"
on:
- pull_request_target

jobs:
  labeler:
    runs-on: ubuntu-latest
    timeout-minutes: 1
    steps:
    - uses: actions/labeler@v3
      with:
        repo-token: "${{ secrets.GITHUB_TOKEN }}"
        sync-labels:	true


Python code quality

name: Python code quality

on:
  push:
    branches: [main, legacy6, legacy7, legacy8]
    paths:
      - '**.py'
      - '.github/workflows/python-code-quality.yml'
      - 'pyproject.toml'
  pull_request:
    branches: [main, legacy6, legacy7, legacy8]
    paths:
      - '**.py'
      - '.github/workflows/python-code-quality.yml'
      - 'pyproject.toml'

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

defaults:
  run:
    shell: bash

jobs:
  analyze:

    name: Lint ${{ matrix.os }} / ${{ matrix.python-version }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-18.04]
        python-version: [3.9]

    steps:
    - name: Checkout ${{ github.ref }}
      uses: actions/checkout@v2

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

    - name: Identify precise Python version
      id: full-python-version
      shell: bash
      run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")

    - name: Cache Python env for ${{ matrix.os }}-py${{ steps.full-python-version.outputs.version }}
      uses: actions/cache@v2
      with:
        path: ${{ env.pythonLocation }}
        key: ${{ matrix.os }}-py${{ steps.full-python-version.outputs.version }}-${{ hashFiles('requirements.txt') }}

    - name: Install Python dependencies
      run: |
        python3 -m pip install --upgrade --upgrade-strategy eager pip setuptools wheel babel
        pip install --upgrade --upgrade-strategy eager pylint pyproject-flake8 flake8-deprecated black mypy isort
        if [ "${{ runner.os }}" == "Windows" ]; then
            pip install --upgrade --upgrade-strategy eager wxPython
        else
            pip install --upgrade --upgrade-strategy eager -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/${{ matrix.os }} wxPython
        fi
        if [ -f requirements-gui.txt ]; then pip install --upgrade --upgrade-strategy eager -r requirements-gui.txt; fi

    - name: List environment
      env:
        GITHUB_CONTEXT: ${{ toJSON(github) }}
        JOB_CONTEXT: ${{ toJSON(job) }}
        STEPS_CONTEXT: ${{ toJSON(steps) }}
        RUNNER_CONTEXT: ${{ toJSON(runner) }}
        STRATEGY_CONTEXT: ${{ toJSON(strategy) }}
        MATRIX_CONTEXT: ${{ toJSON(matrix) }}
      run: |
        pip list
        env

    - name: Create output directory
      run: mkdir -p artifacts

    - name: Analyse with pylint
      id: pylint
      continue-on-error: true
      run: |
        pylint -j 0 --output-format=text:artifacts/pylint.txt,colorized meerk40t

    - name: Analyse with flake8
      id: flake8
      continue-on-error: true
      run: |
        pflake8 meerk40t --tee --output-file=artifacts/flake8.txt

    - name: Analyse with black
      id: black
      continue-on-error: true
      run: |
        black --diff --color meerk40t

    - name: Analyse with isort
      id: isort
      continue-on-error: true
      run: |
        isort meerk40t -c --diff

# MK Not strongly typed - mypy is not going to be run
#    - name: Analyse with mypy
#      id: mypy
#      continue-on-error: true
#      run: |
#        mypy meerk40t
#        exit 0

    - name: Upload analysis results
      uses: actions/upload-artifact@v2
      with:
        name: Artifacts
        path: artifacts/

    - name: "Set status"
      run: |
        if ${{ steps.pylint.outcome != 'success' || steps.flake8.outcome != 'success' || steps.black.outcome != 'success' || steps.isort.outcome != 'success' }}; then
         exit 0
         exit 1
        fi


Check Translations

name: Check Translations

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  check_translations:
    runs-on: ubuntu-latest

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

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

    - name: Run translation check script
      run: |
        cp .github/workflows/translate-error-check.py translate-error-check.py
        python translate-error-check.py

    - name: Check for script exit code
      run: |
        if [ $? -ne 0 ]; then
          echo "Error: Flawed translations found."
          exit 1
        fi


Meerk40t (Ubuntu)

# This workflow builds PyInstaller single-file executables
# of Meerk40t for Ubuntu (latest)

name: Meerk40t (Ubuntu)

on:
  release:
    types: [published]

jobs:
  build-ubuntu:
    if: ${{ false }}
    runs-on: ubuntu-18.04
    steps:
    - name: Checkout meerk40t
      uses: actions/checkout@v2
    - name: Set up Python 3.9
      uses: actions/setup-python@v2
      with:
        python-version: 3.9
    - name: Install dependencies
      run: |
        sudo apt-get install libgtk-3-dev
        python3 -m pip install --upgrade pip
        pip install pyinstaller wheel
        pip install -U -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-18.04 wxPython
        if [ -f requirements-nogui.txt ]; then pip install -r requirements-nogui.txt; fi

    - name: Build meerk40t
      run: |
        mv meerk40t.py mk40t.py
        pyinstaller --windowed --onefile --name meerk40t mk40t.py
        mv mk40t.py meerk40t.py
        mv dist/meerk40t dist/MeerK40t-Ubuntu-Latest

# Switched to using softprops/action-gh-release@v1
# because it supports uploading to existing release based on current tag.
    - name: Upload Release Assets
      id: release
      uses: softprops/action-gh-release@v1
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        files: |
          dist/MeerK40t-Ubuntu-Latest


Unittest

name: Unittest

on:
  push:
    branches: [main, legacy6, legacy7, legacy8]
    paths:
      - '**.py'
      - '.github/workflows/unittests.yml'
  pull_request:
    branches: [main, legacy6, legacy7, legacy8]
    paths:
      - '**.py'
      - '.github/workflows/unittests.yml'

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

defaults:
  run:
    shell: bash

jobs:
  unittests:

    name: ${{ matrix.os }}+py${{ matrix.python-version }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 10
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-2019, macos-11]
        python-version: [3.8, 3.12]

    steps:

    - name: Checkout ${{ github.ref }}
      uses: actions/checkout@v2

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

    - name: Identify precise Python version
      id: full-python-version
      shell: bash
      run: echo ::set-output name=version::$(python -c "import sys; print('-'.join(str(v) for v in sys.version_info))")

    - name: Cache Python env for ${{ matrix.os }}-py${{ steps.full-python-version.outputs.version }}
      uses: actions/cache@v2
      with:
        path: ${{ env.pythonLocation }}
        key: new-${{ matrix.os }}-py${{ steps.full-python-version.outputs.version }}-${{ hashFiles('requirements-nogui.txt') }}

    - name: Install Python dependencies
      run: |
        python -m pip install --upgrade --upgrade-strategy eager pip setuptools wheel babel
        if [ -f requirements-nogui.txt ]; then pip install --upgrade --upgrade-strategy eager -r requirements-nogui.txt; fi

    - name: List environment
      env:
        GITHUB_CONTEXT: ${{ toJSON(github) }}
        JOB_CONTEXT: ${{ toJSON(job) }}
        STEPS_CONTEXT: ${{ toJSON(steps) }}
        RUNNER_CONTEXT: ${{ toJSON(runner) }}
        STRATEGY_CONTEXT: ${{ toJSON(strategy) }}
        MATRIX_CONTEXT: ${{ toJSON(matrix) }}
      run: |
        pip list
        env

    - name: Run Unittests
      run: |
        python -m unittest discover test -v
        if ${{ matrix.experimental }} == true; then
            exit 0
        fi


Meerk40t (Windows)

# This workflow builds PyInstaller single-file executables
# of Meerk40t for Windows

name: Meerk40t (Windows)

on:
  release:
    types: [published]

jobs:
  build-windows:
    runs-on: windows-latest
    steps:
    - name: Checkout meerk40t
      uses: actions/checkout@v3

    - name: Set up Python 3.8
      uses: actions/setup-python@v4
      with:
        python-version: 3.8
        # Latest version of python support by Windows 7 is Python 3.8
        architecture: x86
        # 32-bit architecture required for libusb to connect properly

    - name: Install dependencies
      # Numpy-locked 1.23.5 on 2023-9-28, beyond this crashes in 32-bit python for Windows 7
      run: |
        ## pip install . establishes meerk40t as a package in pip, so that meerk40t-barcodes can find it as a dependency
        pip install numpy==1.23.5
        pip install pyusb
        pip install wheel
        pip install potracer
        pip install pillow==9.5.0
        pip install wxPython
        pip install ezdxf
        pip install pyserial
        pip install bitarray
        pip install scipy
        pip install pypotrace-windows
        pip install pyclipr
        pip install .
        pip install meerk40t-camera opencv-python-headless==4.5.3.56
        pip install meerk40t-barcodes

    - name: pip list
      run: |
          pip list

# Compile bootloaders in order to reduce virus totals
# PYTHONHASHSEED is an attempt to get deterministic builds for VirusTotal
    - name: Build pyinstaller, generate bootloaders
      env:
        PYTHONHASHSEED: 12506
      run: |
        git clone --depth=1 https://github.com/pyinstaller/pyinstaller
        cd pyinstaller/bootloader
        python3 ./waf distclean all --target-arch=32bit
        cd ..
        pip3 install .
        cd ..

    - name: Build MeerK40t
      run: |
        ## pyinstaller struggles recognizing and including imports that are dynamic (such as for the plugins)
        ## So for build purposes, we replace the dynamic imports with the static listing that we include in builds
        cd meerk40t
        move external_plugins.py external_plugins.unused
        move external_plugins_build.py external_plugins.py
        cd ..

        ## pyinstaller struggles with meerk40t.py having the same name as the package meerk40t. Rename for the build
        move meerk40t.py mk40t.py
        pyinstaller .github/workflows/win/meerk40t.spec
        move mk40t.py meerk40t.py

        ## Restore original configuration (not really necessary in build environment which is fresh each time)
        ##cd meerk40t
        ##move external_plugins.py external_plugins_build.py
        ##move external_plugins.unused external_plugins.py
        ##cd ..

    - name: Upload Release Assets
      id: release
      uses: softprops/action-gh-release@v1
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        files: |
          dist/MeerK40t.exe


Build and Deploy Docs

name: Build and Deploy Docs
on:
  push:
    branches:
      - main
      - master

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
  pages: write
  id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  # Build job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Setup Pages
        uses: actions/configure-pages@v3
      - name: Set up Python 3.10
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'
      - name: Compile package requirements.txt
        run: |
           pip install pip-tools
           pip-compile requirements.in
      - name: Install required packages
        run: |
            pip install -r requirements.txt
            pip install furo
      - name: Build
        run: ./docs/build.sh
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v2
        with:
          path: './docs/build/html/'

  # Deployment job
  deploy:
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2

      # - name: Deploy
      #   uses: JamesIves/github-pages-deploy-action@v4
      #   with:
      #     folder: ./docs/build/html
      #     repository-name: OasisLMF/OasisLMF.github.io
      #     token: ${{ secrets.BUILD_DEPLOY_TOKEN }}


Build Docs

name: Build Docs
on:
  pull_request:
    # build docs for all PRs targeting main
    branches:
      - main

permissions:
  contents: write

jobs:
  build:
    concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession.
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Set up Python 3.10
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'
      - name: Compile package requirements.txt
        run: |
             pip install pip-tools
             pip-compile requirements.in
      - name: Install required packages
        run: |
            pip install -r requirements.txt
            pip install furo
      - name: Build
        run: ./docs/build.sh
      - name: Upload artifact
        uses: actions/upload-pages-artifact@v2
        with:
          path: './docs/build/html/'


Oasislmf Build

# Workflow for building the oasislmf python package
#
#  Inputs:
#    ktools_branch: If given a git reference for the ktools repo, add in an optional
#                    'build ktools' Job. The output binaries from that job is used to
#                    build oasislmf instead of pulling them from a ktools release
#
#    build_osx: either `true`, `false`, when set to true run the steps to build an
#                OSX python package using the mac (intel) amd64 ktool binaries.
#                If 'ktools_branch' is not set, or the file 'Darwin_x86_64.tar.gz'
#                has not been attached to the pinned ktools release this will fail.
#
#  Outputs:
#    src_pkg_filename:  source package filename, used for downloading the stored
#                       artifact in other related Jobs
#
#    linux_pkg_filename: Linux wheel package filename
#
#    osx_pkg_filename:  OSX wheel package filename
#

name: Oasislmf Build

on:
  workflow_dispatch:
    inputs:
      ktools_branch:
        description: 'If set, build ktools from scratch and use output to build package: [Branch, commit]'
        required: false
        default: ""
      build_osx:
        description: 'If set, build ktools from scratch and use output to build package: [true, false]'
        required: false
        default: "false"

  workflow_call:
    inputs:
      ktools_branch:
        description: 'If set, build ktools from scratch and use output to build package: [Branch, commit]'
        required: false
        default: ""
        type: string
      build_osx:
        description: 'If set, build ktools from scratch and use output to build package: [true, false]'
        required: false
        default: "false"
        type: string

    outputs:
      src_pkg_filename:
        description: "Source package filename"
        value: ${{ jobs.oasislmf.outputs.src_pkg_filename }}
      linux_pkg_filename:
        description: "Linux package filename"
        value: ${{ jobs.oasislmf.outputs.linux_pkg_filename }}
      osx_pkg_filename:
        description: "OSX package filename"
        value: ${{ jobs.oasislmf.outputs.osx_pkg_filename }}

jobs:
  ktools:
    if: inputs.ktools_branch != ''
    uses:  OasisLMF/ktools/.github/workflows/build.yml@master
    secrets: inherit
    with:
      create_ktools_builder: 'false'
      skip_cmake: 'true'
      ktools_branch: ${{ inputs.ktools_branch }}

  oasislmf:
    if: ${{ ! failure() || ! cancelled() }}
    needs: [ktools]
    runs-on: ubuntu-latest
    outputs:
      src_pkg_filename: ${{ steps.package_src.outputs.filename }}
      linux_pkg_filename: ${{ steps.linux_whl.outputs.filename }}
      osx_pkg_filename: ${{ steps.darwin_whl.outputs.filename }}
    steps:
    - name: Dump github context
      run:   echo "$GITHUB_CONTEXT"
      shell: bash
      env:
       GITHUB_CONTEXT: ${{ toJson(github) }}

    - uses: actions/checkout@v3
    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: 3.9
    - run: pip install pip-tools

    # --- Ktools setup and download --- #

    # With this option set the package build will install local ktools binary tarballs
    # stored in `KTOOLS_TAR_FILE_DIR`, rather than trying to pull a release using
    # the pinned version tag in setup.py
    - name: Set local ktools tar dir
      if: inputs.ktools_branch != ''
      run: |
        echo "KTOOLS_TAR_FILE_DIR=${{ github.workspace }}" >> $GITHUB_ENV

    - name: Download ktools build (Linux)
      if: inputs.ktools_branch != ''
      uses: actions/download-artifact@v3
      with:
        name: Linux_x86_64
        path: ${{ github.workspace }}/

    - name: Download ktools build (Darwin)
      if: inputs.ktools_branch != ''
      uses: actions/download-artifact@v3
      with:
        name: Darwin_x86_64
        path: ${{ github.workspace }}/

    # --- Source Distribution --- #
    - name: Build - source distribution
      run: |
        python setup.py sdist
        TAR_PKG=$(find ./dist/ -name "oasislmf-*.tar.gz")
        echo "PKG_SRC_PATH=${TAR_PKG}" >> $GITHUB_ENV

    - name: Store - source distribution
      uses: actions/upload-artifact@v3
      with:
        name: oasislmf-source-pkg
        path: ${{ env.PKG_SRC_PATH }}
        retention-days: 5

    - name: Filename - source distribution
      id: package_src
      run: |
        FILENAME=$(find ./dist/ -name "oasislmf-*.tar.gz" -exec basename {} \;)
        echo "filename=$FILENAME" >> $GITHUB_OUTPUT

    # --- Linux Distribution --- #
    - name: Build - binary distribution (Linux)
      run: |
        python setup.py bdist_wheel --plat-name Linux_x86_64
        WHL_LINUX=$(find ./dist/ -name "oasislmf-*manylinux1_x86_64.whl")
        echo "PKG_LINUX_PATH=${WHL_LINUX}" >> $GITHUB_ENV

    - name: Store - binary distribution (Linux)
      uses: actions/upload-artifact@v3
      with:
        name: oasislmf-bin-pkg_linux
        path: ${{ env.PKG_LINUX_PATH }}
        retention-days: 5

    - name: Filename - binary distribution (Linux)
      id: linux_whl
      run: |
        FILENAME=$(find ./dist/ -name "oasislmf-*manylinux1_x86_64.whl" -exec basename {} \;)
        echo "filename=$FILENAME" >> $GITHUB_OUTPUT

    ## --- Darwin Distribution --- #
    - name:  Build - binary distribution (Darwin)
      if: inputs.build_osx == 'true' || inputs.ktools_branch != ''
      run: |
        python setup.py bdist_wheel --plat-name Darwin_x86_64
        WHL_OSX=$(find ./dist/ -name "oasislmf-*macosx_10_6_intel.whl")
        echo "PKG_DARWIN_PATH=${WHL_OSX}" >> $GITHUB_ENV

    - name: Store - binary distribution (Darwin)
      if: inputs.build_osx == 'true' || inputs.ktools_branch != ''
      uses: actions/upload-artifact@v3
      with:
        name: oasislmf-bin-pkg_darwin
        path: ${{ env.PKG_DARWIN_PATH }}
        retention-days: 5

    - name: Filename - binary distribution (Darwin)
      if: inputs.build_osx == 'true' || inputs.ktools_branch != ''
      id: darwin_whl
      run: |
        FILENAME=$(find ./dist/ -name "oasislmf-*macosx_10_6_intel.whl" -exec basename {} \;)
        echo "osx_pkg=$FILENAME" >> $GITHUB_OUTPUT


Code Quality

# This workflow performs code quality checks like:
#   - PEP8: the workflow fails if code is not PEP8 compliant
#   - flake8: the problems identified by flake 8 are listed but the workflow
#             presently doesn't fail if flake reports errors.

name: Code Quality

on:
  pull_request:
  push:
    branches:
      - main
      - stable**

env:
  max_line_length: 150

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10"]
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: "3.10"

      - name: install pip tools
        run: |
          python -m pip install --upgrade pip
          pip install --upgrade pip-tools

      - name: install code quality tools
        run: pip install --upgrade autopep8 flake8

      - name: Run (partial) flake8
        if: ${{ ! cancelled() }}
        run: flake8 --select F401,F522,F524,F541 --show-source oasislmf/

      - name: check PEP8 compliance
        if: ${{ ! cancelled() }}
        id: autopep8
        run: |
          autopep8 --diff --exit-code --recursive --max-line-length ${{ env.max_line_length }} --ignore E402 .


Slack Message

# Workflow to send slack notifications to a slack channel
#    The repo needs to have the follow slack hook urls added as secrets
#     https://api.slack.com/messaging/webhooks
#
#    SLACK_WEBHOOK_URL_OASIS: Post messages to the channel '#oasis-release' (workspace CoreOasis)
#    SLACK_WEBHOOK_URL_MEMBERS: Post messages to the channel '#oasis-release' (workspace OasisMembers)
#
#    If one of these hooks is not set, the job won't fail, it should instead skip that channel.
#    To add more channels, extend the job matrix

name: Slack Message

on:
  workflow_call:
    inputs:
      heading:
        description: 'Slack text block heading'
        required: true
        default: ""
        type: string
      title:
        description: 'Title of build message'
        required: true
        default: ""
        type: string
      build_branch:
        description: 'Branch which build ran on'
        required: true
        default: ""
        type: string
      run_url:
        description: 'URL for Github actions run'
        required: true
        default: ""
        type: string
      run_id:
        description: 'run ID of workflow run'
        required: true
        default: ""
        type: string
      run_status:
        description: 'Status of run'
        required: true
        default: ""
        type: string
      run_date:
        description: 'Date run was executed on'
        required: true
        default: ""
        type: string

  workflow_dispatch:
    inputs:
      heading:
        description: 'Slack text block heading'
        required: true
        default: ""
      title:
        description: 'Title of build message'
        required: true
        default: ""
      build_branch:
        description: 'Branch which build ran on'
        required: true
        default: ""
      run_url:
        description: 'URL for Github actions run'
        required: true
        default: ""
      run_id:
        description: 'run ID of workflow run'
        required: true
        default: ""
      run_status:
        description: 'Status of run'
        required: true
        default: ""
      run_date:
        description: 'Date run was executed on'
        required: true
        default: ""

jobs:
  notify:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        channel: [oasis, members] #Array of slack channels
        include:
        - channel: oasis
          webhook_secret: SLACK_WEBHOOK_URL_OASIS
        - channel: members
          webhook_secret: SLACK_WEBHOOK_URL_MEMBERS

    steps:
    - name: Send slack message
      id: slack
      uses: slackapi/slack-github-action@v1.23.0
      with:
        # For posting a rich message using Block Kit
        payload: |
          {
            "text": "${{ inputs.heading }}",
            "blocks": [
              {
                "type": "section",
                "text": {
                  "type": "mrkdwn",
                  "text": "${{ inputs.title }}"
                }
              },
              {
                "type": "section",
                "text": {
                  "type": "mrkdwn",
                  "text": " *build branch:* ${{ inputs.build_branch }} \n *workflow run:* <${{ inputs.run_url }}| Build ${{ inputs.run_id }}>  \n *build result:* ${{ inputs.run_status }} \n *date:* ${{ inputs.run_date }}"
                }
              }
            ]
          }
      env:
        SLACK_WEBHOOK_URL: ${{ secrets[matrix.webhook_secret] }}
        SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK


PiWind MDK

# Workflow to test the MDK command line works by invoking
#   'oasislmf model run' on a piwind branch
#
#    The output is not checked, and will fail on either exceptions
#    or empty output files

name: PiWind MDK

on:
  pull_request:
  push:
    branches:
      - main
      - stable**

  workflow_dispatch:
    inputs:
      piwind_branch:
        description: 'Branch to run PiWind from'
        required: true
        default: main
      mdk_run_type:
        description: 'Loss modes to test, options are one of "[gul, il, ri]"'
        required: true
        default: ri
      ods_branch:
        description: 'Build ods_tools package before test [git ref]'
        required: false
        type: string

jobs:
  PiWind:
    uses: OasisLMF/OasisPiWind/.github/workflows/run_mdk.yml@main
    with:
      mdk_branch: ${{ github.ref }}
      mdk_run_type: ${{ github.event_name != 'workflow_dispatch' && 'ri' || inputs.mdk_run_type }}
      piwind_branch: ${{ github.event_name != 'workflow_dispatch' && 'main' || inputs.piwind_branch }}
      ods_branch: ${{ github.event_name != 'workflow_dispatch' && 'main' || inputs.ods_branch }}


PiWind output check

# Workflow to remote call the PiWind output checks using
# a branch of oasislmf
#
# Inputs:
#   platform_version: The docker tag (OasisPlatfrom Release) to test the oasis stack with
#                     SemVar string of published docker images "1.26.4" ({n}.{n}.{n})
#                     default is "latest"
#
#   piwind_branch: The git reference (like branch name / Pull request) to run the PiWind tests from
#
#   ktools_branch: The git reference to a ktools branch, If a ktools reference is set, then
#                  also build ktools and use the binaries when building the oasislmf package.
name: PiWind output check

on:
  pull_request:
  push:
    branches:
      - main
      - stable**

  workflow_dispatch:
    inputs:
      platform_1_version:
        description: 'OasisPlatform 1 - Version to test [docker tag]'
        required: true
        default: latest
      platform_2_version:
        description: 'OasisPlatform 2 - Version to test [docker tag]'
        required: true
        default: latest
      piwind_branch:
        description: 'Branch to run PiWind from [git ref]'
        required: true
      ktools_branch:
        description: 'Build ktools [git ref]'
        required: false
      ods_branch:
        description: 'Build ods_tools package before test [git ref]'
        required: false
        type: string

jobs:
  build:
    uses: ./.github/workflows/build.yml
    secrets: inherit
    with:
      ktools_branch: ${{ github.event_name != 'workflow_dispatch' && '' ||  inputs.ktools_branch }}

  ods_tools:
    if: ${{ inputs.ods_branch != '' || github.event_name != 'workflow_dispatch' }}
    uses: OasisLMF/ODS_Tools/.github/workflows/build.yml@main
    secrets: inherit
    with:
      ods_branch: ${{ github.event_name != 'workflow_dispatch' && 'main' ||  inputs.ods_branch }}

  platform1:
    if: ${{ ! failure() || ! cancelled() }}
    uses: OasisLMF/OasisPiWind/.github/workflows/test.yml@main
    secrets: inherit
    needs: [build, ods_tools]
    with:
      piwind_branch: ${{ github.event_name != 'workflow_dispatch' && 'main' || inputs.piwind_branch }}
      oasislmf_package: ${{ needs.build.outputs.linux_pkg_filename }}
      oasislmf_branch: ''
      ods_package: ${{ needs.ods_tools.outputs.whl_filename }}
      platform_version: ${{ github.event_name != 'workflow_dispatch' && 'latest' ||  inputs.platform_1_version }}
      pytest_opts: "--docker-compose=./docker/plat2-v2.docker-compose.yml"
      worker_api_ver: 'v1'
      storage_suffix: '_platform1'

  platform2:
    if: ${{ ! failure() || ! cancelled() }}
    uses: OasisLMF/OasisPiWind/.github/workflows/test.yml@main
    secrets: inherit
    needs: [build, ods_tools]
    with:
      piwind_branch: ${{ github.event_name != 'workflow_dispatch' && 'main' || inputs.piwind_branch }}
      oasislmf_package: ${{ needs.build.outputs.linux_pkg_filename }}
      oasislmf_branch: ''
      ods_package: ${{ needs.ods_tools.outputs.whl_filename }}
      platform_version: ${{ github.event_name != 'workflow_dispatch' && 'latest' ||  inputs.platform_2_version }}
      pytest_opts: "--docker-compose=./docker/plat2-v2.docker-compose.yml"
      worker_api_ver: 'v2'
      storage_suffix: '_platform2'


Add PR to project

name: Add PR to project
on:
  pull_request:
    types:
      - opened
jobs:
  track_pr:
    runs-on: ubuntu-latest
    steps:
      - name: Get project data
        env:
          GITHUB_TOKEN: ${{ secrets.PROJECT_ACCESS_TOKEN }}
          ORGANIZATION: Oasislmf
          PROJECT_NUMBER: 44
        run: |
          gh api graphql -f query='
            query($org: String!, $number: Int!) {
              organization(login: $org){
                projectV2(number: $number) {
                  id
                  fields(first:20) {
                    nodes {
                      ... on ProjectV2Field {
                        id
                        name
                      }
                      ... on ProjectV2SingleSelectField {
                        id
                        name
                        options {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json

          echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV

      - name: Add PR to project
        env:
          GITHUB_TOKEN: ${{ secrets.PROJECT_ACCESS_TOKEN }}
          PR_ID: ${{ github.event.pull_request.node_id }}
        run: |
          item_id="$( gh api graphql -f query='
            mutation($project:ID!, $pr:ID!) {
              addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
                item {
                  id
                }
              }
            }' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectV2ItemById.projectV2Item.id')"


Add issue to project

name: Add issue to project
on:
  issues:
    types:
      - opened
jobs:
  track_issue:
    runs-on: ubuntu-latest
    steps:
      - name: Get project data
        env:
          GITHUB_TOKEN: ${{ secrets.PROJECT_ACCESS_TOKEN }}
          ORGANIZATION: Oasislmf
          PROJECT_NUMBER: 44
        run: |
          gh api graphql -f query='
            query($org: String!, $number: Int!) {
              organization(login: $org){
                projectV2(number: $number) {
                  id
                  fields(first:20) {
                    nodes {
                      ... on ProjectV2Field {
                        id
                        name
                      }
                      ... on ProjectV2SingleSelectField {
                        id
                        name
                        options {
                          id
                          name
                        }
                      }
                    }
                  }
                }
              }
            }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json

          echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV

      - name: Add issue to project
        env:
          GITHUB_TOKEN: ${{ secrets.PROJECT_ACCESS_TOKEN }}
          ISSUE_ID: ${{ github.event.issue.node_id }}
        run: |
          item_id="$( gh api graphql -f query='
            mutation($project:ID!, $issue:ID!) {
              addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
                item {
                  id
                }
              }
            }' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectV2ItemById.projectV2Item.id')"


Oasislmf Release

# Workflow to manage the GitHub release for OasisLMF (CD)
#   Tags, builds, creates a GitHub release and uploads to pypi
#
# inputs:
#   release_tag: The SemVar release tag to publish as format - [{n}.{n}.{n}] e.g. 3.0.0,
#                where 'n' is an int.  For Pre-Releases the format is  [{n}.{n}.{n}rc{n}]
#                If the tag format is not followed the workflow fails.
#
#
#   prev_release_tag: When building the release notes and changelogs, this is the tag to start listing
#                     changes from. so if (prev_release_tag=1.26.3, release_tag=1.26.5)
#
#                     the changelogs will document all PRs between the exisiting tag '1.26.3' and the
#                     newly created tag '1.26.5'
#
#   pre_release: Either 'true', 'false', if set to true then the release is marked as a pre-release and the version
#                tag must be formatted as "{n}.{n}.{n}rc{n}"
#
#   build_osx: Either 'true', 'false', optional setting to build and upload an OSX python package (intel support only)

name: Oasislmf Release

on:
  workflow_dispatch:
    inputs:
      release_tag:
        description: 'Release tag, for release [semvar]'
        required: true

      prev_release_tag:
        description: 'The previous oasislmf release version [semvar]'
        required: true

      pre_release:
        description: 'Mark GitHub release as pre-release [true, false]'
        required: true
        default: 'false'

      build_osx:
        description: 'Build OSX package package [true, false]'
        required: false
        default: 'false'

env:
  WORKSPACE: ${{ github.workspace }}/OasisLMF
  RELEASE_TAG: ${{ inputs.release_tag }}
  PREV_RELEASE_TAG: ${{ inputs.prev_release_tag }}
  PRE_RELEASE: ${{ inputs.pre_release }}

jobs:
  update:
    uses: ./.github/workflows/version.yml
    secrets: inherit
    with:
      oasislmf_version: ${{ inputs.release_tag }}

  build:
    uses: ./.github/workflows/build.yml
    secrets: inherit
    needs: update
    with:
      build_osx: ${{ inputs.build_osx }}

  release:
    runs-on: ubuntu-latest
    needs: build
    outputs:
      heading: ${{ steps.slack_vars.outputs.heading }}
      title: ${{ steps.slack_vars.outputs.title }}
      build_branch: ${{ steps.slack_vars.outputs.branch }}
      run_url: ${{ steps.slack_vars.outputs.run_url }}
      run_id: ${{ steps.slack_vars.outputs.run_id }}
      run_status: ${{ steps.slack_vars.outputs.run_status }}
      run_date: ${{ steps.slack_vars.outputs.run_date }}

    steps:
    # --- Pre Publish checks --- #
    - name: check branchname is valid for release
      if: ${{ !startsWith(github.ref_name , 'release/') && !startsWith(github.ref_name, 'stable/') }}
      run: |
        echo "Releases must be trigged on branchs 'release/x.x.x' or 'stable/x.x.x' "
        exit 1

    - name: Check tag is valid for release
      if: env.PRE_RELEASE == 'false'
      run: |
        VALID=$(echo ${{ env.RELEASE_TAG }} | grep -oPc "^(\d+)\.(\d+)\.(\d+)$")
        if [[ ! "$VALID" == 1 ]]; then
            echo "Release Tag ${{ env.RELEASE_TAG }} is not valid"
            exit 1
        fi

    - name: Check tag is valid for pre-release
      if: env.PRE_RELEASE == 'true'
      run: |
        VALID=$(echo ${{ env.RELEASE_TAG }} | grep -oPc "^(\d+)\.(\d+)\.(\d+)rc(\d+)$")
        if [[ ! "$VALID" == 1 ]]; then
            echo "Release Tag ${{ env.RELEASE_TAG }} is not valid"
            exit 1
        fi

    - name: Checkout
      uses: actions/checkout@v3
      with:
        fetch-depth: 0 # fetch the whole repo for complete history
        ref: ${{ github.ref_name }}
        path: ${{ env.WORKSPACE }}

    - name: Check tag matches version set
      working-directory: ${{ env.WORKSPACE }}
      run: |
       BUILD_VER=$(grep '__version__' oasislmf/__init__.py | awk -F"'" '{print $2}')
       RELEASE_VER=${{ env.RELEASE_TAG }}
       [[ "$RELEASE_VER" = "$BUILD_VER" ]] && ERROR_CODE=0 || ERROR_CODE=1
       if [[ "$ERROR_CODE" == 1 ]]; then
           echo "PACKAGE_VER: $BUILD_VER stored in 'oasislmf/__init__.py' dosn't match RELEASE_TAG: $RELEASE_VER" && exit $ERROR_CODE
       fi

    - name: Test package names
      run: |
        ERROR_CODE=0
        SRC_VER=$(echo ${{ needs.build.outputs.src_pkg_filename }} | grep -oP "(\d+)\.(\d+)\.(\d+)rc(\d+)|(\d+)\.(\d+)\.(\d+)")
        BIN_VER=$(echo ${{ needs.build.outputs.linux_pkg_filename }} | grep -oP "(\d+)\.(\d+)\.(\d+)rc(\d+)|(\d+)\.(\d+)\.(\d+)")
        [[ "${{ env.RELEASE_TAG }}" = "$SRC_VER" ]] || ERROR_CODE=1
        [[ "${{ env.RELEASE_TAG }}" = "$BIN_VER" ]] || ERROR_CODE=1
        if [[ "$ERROR_CODE" == 1 ]]; then
            echo "Package names don't match release tag."
            echo "  RELEASE_TAG: ${{ env.RELEASE_TAG }}"
            echo "  PACKAGES: ${{ needs.build.outputs.src_pkg_filename }}, ${{ needs.build.outputs.linux_pkg_filename }}"
            exit $ERROR_CODE
        fi

    # --- Setup Changelog builder script --- #
    - name: Setup github user
      run: |
        git config --global user.email ${{ env.GIT_EMAIL }}
        git config --global user.name ${{ env.GIT_USERNAME }}
        git config --global pull.ff only
      env:
        GIT_EMAIL: ${{ secrets.BUILD_GIT_EMAIL }}
        GIT_USERNAME: ${{ secrets.BUILD_GIT_USERNAME }}

    - name: Tag Release
      working-directory: ${{ env.WORKSPACE }}
      env:
        GITHUB_TOKEN: ${{ secrets.BUILD_GIT_TOKEN }}
      run: |
        git checkout ${{ github.ref_name }}
        git tag ${{ env.RELEASE_TAG }}

    - name: Setup python
      uses: actions/setup-python@v4
      with:
        python-version: '3.9'

    - name: Setup Changelog builder
      run: |
        BRANCH='main'
        BASE_URL="https://raw.githubusercontent.com/OasisLMF/OasisPlatform/$BRANCH/scripts"
        pip install -r $BASE_URL/requirments-changelog.txt
        wget $BASE_URL/update-changelog.py
        chmod +x update-changelog.py

    - name: Setup Twine
      working-directory: ${{ github.workspace }}
      run: pip install twine

    - name: Create changelog
      working-directory: ${{ env.WORKSPACE }}
      env:
        GITHUB_TOKEN: ${{ secrets.BUILD_GIT_TOKEN }}
      run: |
        ${{ github.workspace }}/update-changelog.py build-changelog \
          --repo ${{ github.event.repository.name }} \
          --from-tag ${{ env.PREV_RELEASE_TAG }} \
          --to-tag ${{ env.RELEASE_TAG }} \
          --github-token ${{ secrets.BUILD_GIT_TOKEN }} \
          --local-repo-path ./ \
          --output-path ./CHANGELOG.rst \
          --apply-milestone
        git add ./CHANGELOG.rst
        git commit -m 'Update changelog'

    - name: Create Release notes
      working-directory: ${{ env.WORKSPACE }}
      run: |
        ${{ github.workspace }}/update-changelog.py build-release \
          --repo ${{ github.event.repository.name }} \
          --from-tag ${{ env.PREV_RELEASE_TAG }} \
          --to-tag ${{ env.RELEASE_TAG }} \
          --github-token ${{ secrets.BUILD_GIT_TOKEN }} \
          --local-repo-path ./ \
          --output-path ./RELEASE.md

    - name: Download Source package
      uses: actions/download-artifact@v3
      with:
        name: oasislmf-source-pkg
        path: ${{ github.workspace }}/

    - name: Download Linux package
      uses: actions/download-artifact@v3
      with:
        name: oasislmf-bin-pkg_linux
        path: ${{ github.workspace }}/

    - name: Download OSX package
      if: needs.build.outputs.osx_pkg_filename != ''
      uses: actions/download-artifact@v3
      with:
        name: oasislmf-bin-pkg_darwin
        path: ${{ github.workspace }}/

    # --- Create Release --- #
    - name: Push changes
      working-directory: ${{ env.WORKSPACE }}
      run: |
        git push origin ${{ env.RELEASE_TAG  }}
        git push

    - name: Create Release
      id: create_release
      uses: actions/create-release@v1
      env:
        GITHUB_TOKEN: ${{ secrets.BUILD_GIT_TOKEN }}
      with:
        tag_name: ${{ env.RELEASE_TAG }}
        release_name: Release ${{ env.RELEASE_TAG }}
        body_path: ${{ env.WORKSPACE }}/RELEASE.md
        draft: false
        prerelease: ${{ env.PRE_RELEASE }}

    # --- Attach build assest --- #
    - name: Upload Source package
      id: upload-source-release-asset
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.BUILD_GIT_TOKEN }}
      with:
        upload_url: ${{ steps.create_release.outputs.upload_url }}
        asset_path: ${{ github.workspace }}/${{ needs.build.outputs.src_pkg_filename }}
        asset_name: ${{ needs.build.outputs.src_pkg_filename }}
        asset_content_type: application/octet-stream

    - name: Upload Linux package
      id: upload-linux-release-asset
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.BUILD_GIT_TOKEN }}
      with:
        upload_url: ${{ steps.create_release.outputs.upload_url }}
        asset_path: ${{ github.workspace }}/${{ needs.build.outputs.linux_pkg_filename }}
        asset_name: ${{ needs.build.outputs.linux_pkg_filename }}
        asset_content_type: application/octet-stream

    - name: Upload OSX package
      if: needs.build.outputs.osx_pkg_filename != ''
      id: upload-osx-release-asset
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.BUILD_GIT_TOKEN }}
      with:
        upload_url: ${{ steps.create_release.outputs.upload_url }}
        asset_path: ${{ github.workspace }}/${{ needs.build.outputs.osx_pkg_filename }}
        asset_name: ${{ needs.build.outputs.osx_pkg_filename }}
        asset_content_type: application/octet-stream

    # --- Publish to Pypi --- #
    - name: PYPI - Source package
      run: twine upload ${{ needs.build.outputs.src_pkg_filename }}
      env:
        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}

    - name: PYPI - Linux package
      run: twine upload ${{ needs.build.outputs.linux_pkg_filename }}
      env:
        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}

    - name: PYPI - OSX package
      if: needs.build.outputs.osx_pkg_filename != ''
      run: twine upload ${{ needs.build.outputs.osx_pkg_filename }}
      env:
        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}

    # --- Slack notify --- #
    - name: slack message vars
      id: slack_vars
      run: |
        HEAD=$(echo "*${{ github.event.repository.name}} Release* (${{ env.RELEASE_TAG }})")
        DATE=$(date)
        TITLE=$(echo "• <https://github.com/${{ github.repository }}/releases/tag/${{ env.RELEASE_TAG }}|${{ github.event.repository.name }} ${{ env.RELEASE_TAG }} - Release Notes>")
        JOB_URL=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
        echo "heading=$HEAD" >> $GITHUB_OUTPUT
        echo "run_date=$DATE" >> $GITHUB_OUTPUT
        echo "title=$TITLE" >> $GITHUB_OUTPUT
        echo "run_url=$JOB_URL" >> $GITHUB_OUTPUT
        echo "run_id=${{ github.run_id }}"  >> $GITHUB_OUTPUT
        echo "branch=${{ github.ref_name }}" >> $GITHUB_OUTPUT
        echo "run_status=${{ job.status }}" >> $GITHUB_OUTPUT

  # --- Notify Slack --- #
  slack:
    uses: ./.github/workflows/notify.yml
    secrets: inherit
    needs: release
    with:
      heading: ${{ needs.release.outputs.heading }}
      title: ${{ needs.release.outputs.title }}
      build_branch: ${{ needs.release.outputs.build_branch }}
      run_url: ${{ needs.release.outputs.run_url }}
      run_id: ${{ needs.release.outputs.run_id }}
      run_status: ${{ needs.release.outputs.run_status }}
      run_date: ${{ needs.release.outputs.run_date }}