Tests

name: Tests

on:
  push:
    branches:
      - v1.**
  pull_request:
    branches:
      - main
      - v1.**

concurrency:
  # avoid duplicate runs on both pushes and PRs
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

env:
  # Coloured output for GitHub Actions
  FORCE_COLOR: 3
  # Some common environment variables for both GNU/Linux and macOS jobs
  MPLBACKEND: Agg
  CYTHON_TRACE: 1
  CYTHONSPEC: cython
  NUMPY_MIN: numpy==1.23.0
  CYTHON_MIN: cython==3.0.4
  SCIPY_MIN: scipy==1.9.0

jobs:
  test_pywavelets_linux:
    name: linux-cp${{ matrix.python-version }}-${{ matrix.OPTIONS_NAME }}
    runs-on: ubuntu-latest
    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12"]
        MINIMUM_REQUIREMENTS: [0]
        USE_SCIPY: [0]
        USE_SDIST: [0]
        REFGUIDE_CHECK: [0]
        PIP_FLAGS: [""]
        OPTIONS_NAME: ["default"]
        include:
          - platform_id: manylinux_x86_64
            python-version: "3.10"
            MINIMUM_REQUIREMENTS: 1
            OPTIONS_NAME: "minimum-req"
          - platform_id: manylinux_x86_64
            python-version: "3.10"
            USE_SCIPY: 1
            OPTIONS_NAME: "with-scipy"
          - platform_id: manylinux_x86_64
            python-version: "3.10"
            USE_SDIST: 1
            OPTIONS_NAME: "install-from-sdist"
          - platform_id: manylinux_x86_64
            python-version: "3.12"
            PIP_FLAGS: "--pre"
            OPTIONS_NAME: "pre-releases"
          - platform_id: manylinux_x86_64
            python-version: "3.12"
            OPTIONS_NAME: "editable-install"
    steps:
      - name: Checkout PyWavelets
        uses: actions/checkout@v4

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

      - name: Build package
        env:
          VERSION: ${{ matrix.python-version }}
          MINIMUM_REQUIREMENTS: ${{ matrix.MINIMUM_REQUIREMENTS }}
          PIP_FLAGS: ${{ matrix.PIP_FLAGS }}
          USE_SDIST: ${{ matrix.USE_SDIST }}
          USE_SCIPY: ${{ matrix.USE_SCIPY }}
          REFGUIDE_CHECK: ${{ matrix.REFGUIDE_CHECK }}
          OPTIONS_NAME: ${{ matrix.OPTIONS_NAME }}
        run: |
          uname -a
          df -h
          ulimit -a
          # ccache -s
          which python
          python --version
          # sudo apt-get install libatlas-base-dev
          pip install --upgrade pip build
          # Set numpy version first, other packages link against it
          if [ "${MINIMUM_REQUIREMENTS}" == "1" ]; then
              pip install ${CYTHON_MIN}
              pip install ${NUMPY_MIN}
              if [ "${USE_SCIPY}" == "1" ]; then pip install ${SCIPY_MIN}; fi
          else
              pip install ${PIP_FLAGS} cython
              pip install ${PIP_FLAGS} numpy
              if [ "${USE_SCIPY}" == "1" ]; then pip install ${PIP_FLAGS} scipy; fi
          fi
          pip install ${PIP_FLAGS} matplotlib pytest

            set -o pipefail
            if [ "${USE_SDIST}" == "1" ]; then
                python -m build --sdist
                pip install dist/pyw*.tar.gz -v
            elif [ "${REFGUIDE_CHECK}" == "1" ]; then
                pip install sphinx numpydoc
                pip install . -v
            else
                pip install . -v
            fi

          if [ "${OPTIONS_NAME}" == "editable-install" ]; then
              pip install meson-python>=0.16.0 matplotlib pytest ninja
              pip install -e . --no-build-isolation
          fi

      - name: Run tests
        env:
          USE_SDIST: ${{ matrix.USE_SDIST }}
          REFGUIDE_CHECK: ${{ matrix.REFGUIDE_CHECK }}
          OPTIONS_NAME: ${{ matrix.OPTIONS_NAME }}
        run: |
          set -o pipefail
          # Move out of source directory to avoid finding local pywt
          pushd demo
          if [ "${USE_SDIST}" == "1" ]; then
              pytest --pyargs pywt
              python ../pywt/tests/test_doc.py
          elif [ "${REFGUIDE_CHECK}" == "1" ]; then
              # Run doctests and check if the refguide contains entries from __all__
              python util/refguide_check.py --doctests
              # Run Sphinx HTML docs builder, converting warnings to errors
              pip install -r util/readthedocs/requirements.txt
              sphinx-build -b html -W --keep-going -d _build/doctrees . doc/source doc/build
          else
              pytest --pyargs pywt
          fi
          popd

  test_pywavelets_macos:
    name: macos-cp${{ matrix.python-version }}-${{ matrix.OPTIONS_NAME }}
    runs-on: macos-latest
    strategy:
      # Ensure that a wheel builder finishes even if another fails
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.12"]
        MINIMUM_REQUIREMENTS: [0]
        USE_SCIPY: [0]
        USE_SDIST: [0]
        REFGUIDE_CHECK: [0]
        PIP_FLAGS: [""]
        OPTIONS_NAME: ["default"]
        include:
          - python-version: "3.10"
            MINIMUM_REQUIREMENTS: 1
            OPTIONS_NAME: "osx-minimum-req"
          - python-version: "3.12"
            PIP_FLAGS: "--pre"
            OPTIONS_NAME: "pre-releases"

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

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

      - name: Build package
        env:
          VERSION: ${{ matrix.python-version }}
          MINIMUM_REQUIREMENTS: ${{ matrix.MINIMUM_REQUIREMENTS }}
          PIP_FLAGS: ${{ matrix.PIP_FLAGS }}
          USE_SDIST: ${{ matrix.USE_SDIST }}
          USE_SCIPY: ${{ matrix.USE_SCIPY }}
          REFGUIDE_CHECK: ${{ matrix.REFGUIDE_CHECK }}
          CC: /usr/bin/clang
          CXX: /usr/bin/clang++
        run: |
          uname -a
          df -h
          ulimit -a
          which python
          python --version
          pip install --upgrade pip build
          # Set numpy version first, other packages link against it
          if [ "${MINIMUM_REQUIREMENTS}" == "1" ]; then
              pip install ${CYTHON_MIN} ${NUMPY_MIN}
              if [ "${USE_SCIPY}" == "1" ]; then pip install ${SCIPY_MIN}; fi
          else
              pip install ${PIP_FLAGS} cython numpy
              if [ "${USE_SCIPY}" == "1" ]; then pip install ${PIP_FLAGS} scipy; fi
          fi
          pip install ${PIP_FLAGS} matplotlib pytest

            set -o pipefail
            if [ "${USE_SDIST}" == "1" ]; then
                python -m build --sdist
                pip install pywavelets* -v
            elif [ "${REFGUIDE_CHECK}" == "1" ]; then
                pip install sphinx numpydoc
                pip install . -v
            else
                pip install . -v
            fi

      - name: Run tests
        env:
          PIP_FLAGS: ${{ matrix.PIP_FLAGS }}
          USE_SDIST: ${{ matrix.USE_SDIST }}
          REFGUIDE_CHECK: ${{ matrix.REFGUIDE_CHECK }}
        run: |
          # Move out of source directory to avoid finding local pywt
          pushd demo
          if [ "${USE_SDIST}" == "1" ]; then
              pytest --pyargs pywt
              python ../pywt/tests/test_doc.py
          elif [ "${REFGUIDE_CHECK}" == "1" ]; then
              python util/refguide_check.py --doctests
          else
              pytest --pyargs pywt
          fi
          popd


Build Wheels and Release

name: Build Wheels and Release
on:
  push:
    tags:
      - "v*"
      - "buildwheels*"
    branches:
      # Runs on every merge to main to upload .dev0 wheels to anaconda.org
      - main
      - v1.**
  # Make it possible to upload wheels manually if needed (for anaconda.org only, not PyPI)
  workflow_dispatch:
    inputs:
      push_wheels:
        description: >
          Push wheels to Anaconda if "true". Default is "false". Warning: this will overwrite existing wheels.
        required: false
        default: "false"
  # Upload wheels to anaconda.org on a schedule
  schedule:
    # Run at 0300 hours on days 3 and 17 of the month
    - cron: "0 3 3,17 * *"
env:
  CIBW_BUILD_VERBOSITY: 2
  CIBW_TEST_REQUIRES: pytest
  CIBW_TEST_COMMAND: pytest --pyargs pywt -m "not slow"
  CIBW_ENVIRONMENT: PIP_PREFER_BINARY=1

jobs:
  build_linux_x86_64_wheels:
    name: Build ${{ matrix.cibw_python }} ${{ matrix.cibw_arch }} ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        cibw_python: ["cp310", "cp311", "cp312"]
        cibw_arch: ["x86_64"]
    steps:
      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v5
        name: Install Python
        with:
          python-version: "3.10"
      - name: Build the wheel
        uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # 2.18.1
        with:
          output-dir: dist
        env:
          CIBW_BUILD: ${{ matrix.cibw_python }}-*
          CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }}
          CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
          CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_1
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: wheels_linux_${{ matrix.cibw_arch }}_${{ matrix.cibw_python }}
          path: ./dist/*.whl
          if-no-files-found: error

  build_linux_aarch64_wheels:
    name: Build ${{ matrix.cibw_python }} ${{ matrix.cibw_arch }} ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        cibw_python: ["cp310", "cp311", "cp312"]
        cibw_arch: ["aarch64"]
    steps:
      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v5
        name: Install Python
        with:
          python-version: "3.10"
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: arm64
      - name: Build the wheel
        uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # 2.18.1
        with:
          output-dir: dist
        env:
          CIBW_BUILD: ${{ matrix.cibw_python }}-*
          CIBW_ARCHS_LINUX: ${{ matrix.cibw_arch }}
          CIBW_MANYLINUX_AARCH64_IMAGE: manylinux2014
          CIBW_MUSLLINUX_AARCH64_IMAGE: musllinux_1_1
      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: wheels_linux_${{ matrix.cibw_arch }}_${{ matrix.cibw_python }}
          path: ./dist/*.whl

  build_macos_wheels:
    name: Build ${{ matrix.cibw_python }} ${{ matrix.cibw_arch }} ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        # macos-13 is the last runner that supports Intel (x86_64) architecture
        os: [macos-13, macos-14]
        cibw_python: ["cp310", "cp311", "cp312"]
        cibw_arch: ["x86_64", "arm64"]
        exclude:
          - os: macos-14
            cibw_arch: "x86_64"
          - os: macos-13
            cibw_arch: "arm64"
    steps:
      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        name: Install Python
        with:
          python-version: "3.12"

      - name: Build wheels for CPython (macOS) (x86_64)
        if: matrix.cibw_arch == 'x86_64'
        uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # 2.18.1
        with:
          output-dir: dist
        env:
          CIBW_BUILD: ${{ matrix.cibw_python }}-*
          CIBW_ARCHS_MACOS: ${{ matrix.cibw_arch }}

      - name: Build wheels for CPython (macOS) (arm64)
        if: matrix.cibw_arch == 'arm64'
        uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # 2.18.1
        with:
          output-dir: dist
        env:
          CIBW_BUILD: ${{ matrix.cibw_python }}-*
          CIBW_ARCHS_MACOS: ${{ matrix.cibw_arch }}

      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: wheels_macos_${{ matrix.cibw_arch }}_${{ matrix.cibw_python }}
          path: ./dist/*.whl
          if-no-files-found: error

  build_windows_wheels:
    name: Build ${{ matrix.cibw_python }} ${{ matrix.cibw_arch }} ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [windows-latest]
        cibw_arch: ["AMD64", "x86"]
        cibw_python: ["cp310", "cp311", "cp312"]
    steps:
      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        name: Install Python
        with:
          python-version: "3.10"

      - name: Setup MSVC (32-bit)
        if: matrix.cibw_arch == 'x86'
        uses: bus1/cabuild/action/msdevshell@e22aba57d6e74891d059d66501b6b5aed8123c4d # v1
        with:
          architecture: x86

      - name: Setup MSVC (64-bit)
        if: matrix.cibw_arch == 'AMD64'
        uses: bus1/cabuild/action/msdevshell@e22aba57d6e74891d059d66501b6b5aed8123c4d # v1
        with:
          architecture: x64

      - name: Build Windows wheels for CPython
        uses: pypa/cibuildwheel@ba8be0d98853f5744f24e7f902c8adef7ae2e7f3 # 2.18.1
        with:
          output-dir: dist
        env:
          CIBW_BUILD: ${{ matrix.cibw_python }}-*
          CIBW_ARCHS_WINDOWS: ${{ matrix.cibw_arch }}

      - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
        with:
          name: wheels_windows_${{ matrix.cibw_arch }}_${{ matrix.cibw_python }}
          path: ./dist/*.whl
          if-no-files-found: error

  deploy_pypi:
    name: Release (PyPI)
    needs:
      [
        build_linux_x86_64_wheels,
        build_linux_aarch64_wheels,
        build_macos_wheels,
        build_windows_wheels,
      ]
    # Run only on tags pushed to the repository
    if: github.repository == 'PyWavelets/pywt' && startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v5
        name: Install Python
        with:
          python-version: "3.10"

      - name: Install Twine
        run: |
          python -m pip install --upgrade pip
          pip install twine
          pip install cython numpy build

      - uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
        id: download
        with:
          pattern: "wheels_*"
          path: ./dist
          merge-multiple: true

      - name: Publish the source distribution on PyPI
        run: |
          PYWT_VERSION=$(git describe --tags)
          python -m build --sdist
          ls -la ${{ github.workspace }}/dist
          # We prefer to release wheels before source because otherwise there is a
          # small window during which users who pip install pywt will require compilation.
          twine upload ${{ github.workspace }}/dist/*.whl
          twine upload ${{ github.workspace }}/dist/pywavelets-${PYWT_VERSION:1}.tar.gz
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.TWINE_TOKEN }}

      - name: Github release
        uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}

  deploy_anaconda:
    name: Release (Anaconda)
    needs:
      [
        build_linux_x86_64_wheels,
        build_linux_aarch64_wheels,
        build_macos_wheels,
        build_windows_wheels,
      ]
    # Run only on pushes to the main branch, on schedule, or when triggered manually
    if: >-
      github.repository == 'PyWavelets/pywt' &&
      (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
      (github.event_name == 'workflow_dispatch' && github.event.inputs.push_wheels == 'true') ||
      (github.event_name == 'schedule')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
        with:
          fetch-depth: 0

      - uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
        id: download
        with:
          pattern: "wheels_*"
          path: dist/
          merge-multiple: true

      - name: Push to Anaconda PyPI index
        uses: scientific-python/upload-nightly-action@b67d7fcc0396e1128a474d1ab2b48aa94680f9fc # v0.5.0
        with:
          artifacts_path: dist/
          anaconda_nightly_upload_token: ${{ secrets.ANACONDA_ORG_UPLOAD_TOKEN }}


Python Package

name: Python Package
on:
  push:
  pull_request:

jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-20.04]
        python:
          - "3.6"
          - "3.7"
          - "3.8"
          - "3.9"
          - "3.10"

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python }}

      - name: Install build dependencies
        run: |
          sudo apt-get install -qy \
            gettext \
            libkeyutils-dev \
            libgirepository1.0-dev

      - run: pip install -U pip
      - run: pip install setuptools wheel

      - run: python setup.py sdist bdist_wheel
      - run: pip install dist/*.whl keyutils
      - run: pip install twine flake8

      - run: twine check dist/*
      - run: flake8
      - run: python test/test_cache.py
      - run: python test/test_match.py

      - uses: actions/upload-artifact@v3
        with: {name: dist, path: dist/}
        if: matrix.os == 'ubuntu-20.04' &&
            matrix.python == '3.9'

  deploy:
    name: Upload release
    runs-on: ubuntu-latest
    needs: build
    if: startsWith(github.ref, 'refs/tags/v') && success()
    environment:
      name: pypi
    permissions:
      id-token: write

    steps:
      - uses: actions/download-artifact@v3
        with: {name: dist, path: dist/}
      - uses: pypa/gh-action-pypi-publish@release/v1
      # with:
      #   repository-url: https://test.pypi.org/legacy/


Translations

name: Translations
on:
  push:
    branches:
      - master

jobs:
  update_po:
    name: Update language files
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash -eo pipefail {0}

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v4
        with:
          python-version: 3.x

      - run: sudo apt-get install -qy gettext

      - run: ./lang/report.sh | tee before.tsv | column -t
      - run: make -BC lang
      - run: ./lang/report.sh | tee after.tsv | column -t
      - run: .github/pr_text.py | tee summary.md

      - run: |
          echo >>$GITHUB_OUTPUT "summary<<EOS_SUMMARY"
          cat  >>$GITHUB_OUTPUT <summary.md
          echo >>$GITHUB_OUTPUT "EOS_SUMMARY"
        id: report
      - run: rm before.tsv after.tsv summary.md

      - uses: peter-evans/create-pull-request@v5
        with:
          branch: ${{ github.ref_name }}_langfiles
          commit-message: Update language files
          title: Update language files on ${{ github.ref_name }}
          body: ${{ steps.report.outputs.summary }}


Check Release

name: Check Release
on:
  push:
    branches: ["main"]
  pull_request:

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

jobs:
  check_release:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Base Setup
        uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - name: Install Dependencies
        run: |
          pip install -e .
      - name: Check Release
        uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}


Enforce PR label

name: Enforce PR label

on:
  pull_request:
    types: [labeled, unlabeled, opened, edited, synchronize]
jobs:
  enforce-label:
    runs-on: ubuntu-latest
    steps:
      - name: enforce-triage-label
        uses: jupyterlab/maintainer-tools/.github/actions/enforce-label@v1


Step 1: Prep Release

name: "Step 1: Prep Release"
on:
  workflow_dispatch:
    inputs:
      version_spec:
        description: "New Version Specifier"
        default: "next"
        required: false
      branch:
        description: "The branch to target"
        required: false
      post_version_spec:
        description: "Post Version Specifier"
        required: false
      silent:
        description: "Set a placeholder in the changelog and don't publish the release."
        required: false
        type: boolean
      since:
        description: "Use PRs with activity since this date or git reference"
        required: false
      since_last_stable:
        description: "Use PRs with activity since the last stable git tag"
        required: false
        type: boolean
jobs:
  prep_release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - name: Prep Release
        id: prep-release
        uses: jupyter-server/jupyter_releaser/.github/actions/prep-release@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          version_spec: ${{ github.event.inputs.version_spec }}
          silent: ${{ github.event.inputs.silent }}
          post_version_spec: ${{ github.event.inputs.post_version_spec }}
          target: ${{ github.event.inputs.target }}
          branch: ${{ github.event.inputs.branch }}
          since: ${{ github.event.inputs.since }}
          since_last_stable: ${{ github.event.inputs.since_last_stable }}

      - name: "** Next Step **"
        run: |
          echo "Optional): Review Draft Release: ${{ steps.prep-release.outputs.release_url }}"


Publish Changelog

name: "Publish Changelog"
on:
  release:
    types: [published]

  workflow_dispatch:
    inputs:
      branch:
        description: "The branch to target"
        required: false

jobs:
  publish_changelog:
    runs-on: ubuntu-latest
    environment: release
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - name: Publish changelog
        id: publish-changelog
        uses: jupyter-server/jupyter_releaser/.github/actions/publish-changelog@v2
        with:
          token: ${{ steps.app-token.outputs.token }}
          branch: ${{ github.event.inputs.branch }}

      - name: "** Next Step **"
        run: |
          echo "Merge the changelog update PR: ${{ steps.publish-changelog.outputs.pr_url }}"


Step 2: Publish Release

name: "Step 2: Publish Release"
on:
  workflow_dispatch:
    inputs:
      branch:
        description: "The target branch"
        required: false
      release_url:
        description: "The URL of the draft GitHub release"
        required: false
      steps_to_skip:
        description: "Comma separated list of steps to skip"
        required: false

jobs:
  publish_release:
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - uses: actions/create-github-app-token@v1
        id: app-token
        with:
          app-id: ${{ vars.APP_ID }}
          private-key: ${{ secrets.APP_PRIVATE_KEY }}

      - name: Populate Release
        id: populate-release
        uses: jupyter-server/jupyter_releaser/.github/actions/populate-release@v2
        with:
          token: ${{ steps.app-token.outputs.token }}
          branch: ${{ github.event.inputs.branch }}
          release_url: ${{ github.event.inputs.release_url }}
          steps_to_skip: ${{ github.event.inputs.steps_to_skip }}

      - name: Finalize Release
        id: finalize-release
        uses: jupyter-server/jupyter_releaser/.github/actions/finalize-release@v2
        with:
          token: ${{ steps.app-token.outputs.token }}
          release_url: ${{ steps.populate-release.outputs.release_url }}

      - name: "** Next Step **"
        if: ${{ success() }}
        run: |
          echo "Verify the final release"
          echo ${{ steps.finalize-release.outputs.release_url }}

      - name: "** Failure Message **"
        if: ${{ failure() }}
        run: |
          echo "Failed to Publish the Draft Release Url:"
          echo ${{ steps.populate-release.outputs.release_url }}


Tests

name: Tests

on:
  push:
    branches: ["main"]
  pull_request:
  schedule:
    - cron: "0 0 * * *"

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

jobs:
  test:
    name: Test

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

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]
        python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]

    env:
      OS: ${{ matrix.os }}

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

      - name: Base Setup
        uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1

      - name: Install graphviz on Linux
        if: ${{ startsWith(matrix.os, 'ubuntu') }}
        run: sudo apt install -y graphviz

      - name: Install dependencies
        run: |
          pip install coveralls
          make install

      - name: Run the tests
        run: |
          make docs
          make help
          make cover

  test_miniumum_versions:
    name: Test Minimum Versions
    timeout-minutes: 20
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Base Setup
        uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
        with:
          dependency_type: minimum

      - name: Install
        run: make install
      - name: Run the unit tests
        run: make test

  make_sdist:
    name: Make SDist
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - uses: jupyterlab/maintainer-tools/.github/actions/make-sdist@v1

  test_sdist:
    runs-on: ubuntu-latest
    needs: [make_sdist]
    name: Install from SDist and Test
    timeout-minutes: 20
    steps:
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - uses: jupyterlab/maintainer-tools/.github/actions/test-sdist@v1

  link_check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
      - uses: jupyterlab/maintainer-tools/.github/actions/check-links@v1
        with:
          ignore_links: "http://some/file/from/* http://path/to/some/*"


Linux Tests

name: Linux Tests

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

jobs:
  codecov:
    name: Codecov Workflow
    runs-on: ubuntu-latest

    strategy:
      matrix:
        python-version: [3.9]

    steps:
      - uses: actions/checkout@v2
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@main
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"
      - name: Lint with Ruff
        run: pre-commit run --all-files --show-diff-on-failure --color always
      - name: Generate coverage report
        run: |
          coverage run -m pytest tests/
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v1
        with:
          env_vars: OS,PYTHON
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: unittests


CodeQL

name: "CodeQL"

on:
  push:
    branches: ["master"]
  pull_request:
    branches: ["master"]
  schedule:
    - cron: "36 9 * * 0"

permissions:
  contents: read

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

    strategy:
      fail-fast: false
      matrix:
        language: ["python"]

    steps:
    - name: Checkout repository
      uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

    - name: Initialize CodeQL
      uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
      with:
        languages: ${{ matrix.language }}
        queries: +security-and-quality

    - name: Autobuild
      uses: github/codeql-action/autobuild@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10
      with:
        category: "/language:${{matrix.language}}"


Build

name: Build

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

permissions:
  contents: read

jobs:
  build-and-test:
    name: Build project and run tests
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.10", "3.x"]

    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

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

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e ".[devel]"

    - name: Lint with flake8
      run: |
        flake8 --count --show-source --statistics src/ tests/

    - name: Static code analysis with pylint
      run: |
        pylint src/ tests/

    - name: Test with pytest
      run: |
        pytest --cov=powerapi --cov-report=term --cov-report=xml tests/unit

    - name: Upload coverage reports to Codecov
      if: ${{ matrix.python-version }} == "3.10"
      uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
        file: coverage.xml

  build-container-image:
    name: Build container image
    runs-on: ubuntu-latest

    steps:
      - name: Setup Docker buildx
        uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0

      - name: Build image
        uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 # v6.1.0
        with:
          push: false
          provenance: false
          load: true
          tags: localbuild/powerapi:sha-${{ github.sha }}


Release

name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: read

jobs:
  pre-checks:
    name: Release Pre-Checks
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

    - name: Check if package version corresponds to git tag
      shell: python
      env:
        PYTHONPATH: ${{ github.workspace }}/src
      run: |
        import os
        import sys
        from powerapi import __version__

        git_tag = os.environ['GITHUB_REF_NAME'].removeprefix('v')
        pkg_version = __version__

        if git_tag != pkg_version:
          title = 'Invalid version'
          file = 'src/powerapi/__init__.py'
          msg = f'Version mismatch between python package ({pkg_version}) and git tag ({git_tag})'
          print(f'::error title={title},file={file}::{msg}')
          sys.exit(1)

  pypi-package:
    name: Publish Pypi package
    runs-on: ubuntu-latest
    needs: pre-checks
    permissions:
      contents: read
      id-token: write

    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

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

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

    - name: Build package
      run: python -m build

    - name: Publish package
      uses: pypa/gh-action-pypi-publish@ec4db0b4ddc65acdf4bff5fa45ac92d78b56bdf0 # v1.9.0
      with:
        print-hash: true

  docker-image:
    name: Publish Docker image
    runs-on: ubuntu-latest
    needs: pre-checks
    permissions:
      contents: read
      packages: write

    steps:
      - name: Set up QEMU
        uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0

      - name: Setup Docker buildx
        uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0

      - name: Log in to Docker Hub
        uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
        with:
          username: ${{ vars.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_TOKEN }}

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

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1
        with:
          images: |
            docker.io/powerapi/powerapi
            ghcr.io/powerapi-ng/powerapi
          tags: |
            type=pep440,pattern={{version}}

      - name: Build and push Docker image
        uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 # v6.1.0
        id: build-and-push
        with:
          push: true
          provenance: false
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

  github-release:
    name: Publish GitHub release
    runs-on: ubuntu-latest
    needs: [pre-checks, pypi-package, docker-image]
    permissions:
      contents: write
    env:
      CHGLOG_VERSION: "0.15.4"

    steps:
    - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
      with:
        fetch-depth: 0

    - name: Generate version changelog
      run: |
        set -euo pipefail
        export BASE_URL="https://github.com/git-chglog/git-chglog/releases/download"
        export FILENAME="git-chglog_${CHGLOG_VERSION}_linux_amd64.tar.gz"
        curl -fsSL "${BASE_URL}/v${CHGLOG_VERSION}/${FILENAME}" |sudo tar xz --no-same-owner -C /usr/local/bin git-chglog
        git-chglog --config .github/chglog/config.yml --output CHANGELOG.md "${GITHUB_REF_NAME}"

    - name: Create GitHub release
      uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6
      with:
        body_path: CHANGELOG.md


SMACT CI

name: SMACT CI

on:
  pull_request:
  push:
    branches:
      - master

jobs:

  qa:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pre-commit/action@v3.0.1

  test:
    needs: qa
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9","3.10","3.11","3.12"]
        os: [ubuntu-latest,macos-latest,windows-latest]

    runs-on: ${{matrix.os}}
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip wheel setuptools
        pip install -e .
        pip install pytest-cov
    - name: Run tests and collect coverage
      run: python -m pytest --cov=smact --cov-report=xml -v
    - name: Upload coverage reports to CodeCov
      uses: codecov/codecov-action@v4
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
        #files: ./coverage.xml
        fail_ci_if_error: False
        env_vars: OS,PYTHON
        verbose: true


Combine PRs

name: 'Combine PRs'

# Controls when the action will run - in this case triggered manually
on:
  workflow_dispatch:
    inputs:
      branchPrefix:
        description: 'Branch prefix to find combinable PRs based on'
        required: true
        default: 'dependabot'
      mustBeGreen:
        description: 'Only combine PRs that are green (status is success)'
        required: true
        default: true
      combineBranchName:
        description: 'Name of the branch to combine PRs into'
        required: true
        default: 'combine-prs-branch'
      ignoreLabel:
        description: 'Exclude PRs with this label'
        required: true
        default: 'nocombine'

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "combine-prs"
  combine-prs:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      - uses: actions/github-script@v7
        id: create-combined-pr
        name: Create Combined PR
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', {
              owner: context.repo.owner,
              repo: context.repo.repo
            });
            let branchesAndPRStrings = [];
            let baseBranch = null;
            let baseBranchSHA = null;
            for (const pull of pulls) {
              const branch = pull['head']['ref'];
              console.log('Pull for branch: ' + branch);
              if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) {
                console.log('Branch matched prefix: ' + branch);
                let statusOK = true;
                if(${{ github.event.inputs.mustBeGreen }}) {
                  console.log('Checking green status: ' + branch);
                  const stateQuery = `query($owner: String!, $repo: String!, $pull_number: Int!) {
                    repository(owner: $owner, name: $repo) {
                      pullRequest(number:$pull_number) {
                        commits(last: 1) {
                          nodes {
                            commit {
                              statusCheckRollup {
                                state
                              }
                            }
                          }
                        }
                      }
                    }
                  }`
                  const vars = {
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    pull_number: pull['number']
                  };
                  const result = await github.graphql(stateQuery, vars);
                  const [{ commit }] = result.repository.pullRequest.commits.nodes;
                  const state = commit.statusCheckRollup.state
                  console.log('Validating status: ' + state);
                  if(state != 'SUCCESS') {
                    console.log('Discarding ' + branch + ' with status ' + state);
                    statusOK = false;
                  }
                }
                console.log('Checking labels: ' + branch);
                const labels = pull['labels'];
                for(const label of labels) {
                  const labelName = label['name'];
                  console.log('Checking label: ' + labelName);
                  if(labelName == '${{ github.event.inputs.ignoreLabel }}') {
                    console.log('Discarding ' + branch + ' with label ' + labelName);
                    statusOK = false;
                  }
                }
                if (statusOK) {
                  console.log('Adding branch to array: ' + branch);
                  const prString = '#' + pull['number'] + ' ' + pull['title'];
                  branchesAndPRStrings.push({ branch, prString });
                  baseBranch = pull['base']['ref'];
                  baseBranchSHA = pull['base']['sha'];
                }
              }
            }
            if (branchesAndPRStrings.length == 0) {
              core.setFailed('No PRs/branches matched criteria');
              return;
            }
            try {
              await github.rest.git.createRef({
                owner: context.repo.owner,
                repo: context.repo.repo,
                ref: 'refs/heads/' + '${{ github.event.inputs.combineBranchName }}',
                sha: baseBranchSHA
              });
            } catch (error) {
              console.log(error);
              core.setFailed('Failed to create combined branch - maybe a branch by that name already exists?');
              return;
            }

            let combinedPRs = [];
            let mergeFailedPRs = [];
            for(const { branch, prString } of branchesAndPRStrings) {
              try {
                await github.rest.repos.merge({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  base: '${{ github.event.inputs.combineBranchName }}',
                  head: branch,
                });
                console.log('Merged branch ' + branch);
                combinedPRs.push(prString);
              } catch (error) {
                console.log('Failed to merge branch ' + branch);
                mergeFailedPRs.push(prString);
              }
            }

            console.log('Creating combined PR');
            const combinedPRsString = combinedPRs.join('\n');
            let body = '✅ This PR was created by the Combine PRs action by combining the following PRs:\n' + combinedPRsString;
            if(mergeFailedPRs.length > 0) {
              const mergeFailedPRsString = mergeFailedPRs.join('\n');
              body += '\n\n⚠ The following PRs were left out due to merge conflicts:\n' + mergeFailedPRsString
            }
            await github.rest.pulls.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: 'Combined PR',
              head: '${{ github.event.inputs.combineBranchName }}',
              base: baseBranch,
              body: body
            });


Publish SMACT distributions to PyPI

name: Publish SMACT distributions to PyPI

on:
  push:
    tags:
      - 'v*'

jobs:
  build-n-publish:
    name: Build and publish SMACT distributions to PyPI
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: Create SMACT release
        uses: actions/setup-python@v5
        with:
          python-version: "3.x"
      - name: Install pypa/build
        run: >-
          python -m
          pip install
          build
          --user
      - name: Build a binary wheel and a source tarball
        run: >-
          python -m
          build
          --sdist
          --wheel
          --outdir dist/
          .
      - name: Publish distribution to PyPI
        if: startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{secrets.PYPI_API_TOKEN}}


name: Secret_Scan

on:
  pull_request:

jobs:
  TruffleHog:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      with:
        fetch-depth: 0
    - name: Secret Scanning
      uses: trufflesecurity/trufflehog@main
      with:
        extra_args: --only-verified


Labelbox Python SDK Shared

name: Labelbox Python SDK Shared

on:
  workflow_call:
    inputs:
      python-version:
        required: true
        type: string
      api-key:
        required: true
        type: string
      da-test-key:
        required: true
        type: string
      test-env:
        required: true
        type: string
      fixture-profile:
        required: true
        type: boolean

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: ${{ inputs.python-version }}
      - name: Linting
        working-directory: libs/labelbox
        run: rye run lint
  integration:
    runs-on: ubuntu-latest
    concurrency:
      group: labelbox-python-${{ inputs.test-env }}-${{ inputs.python-version }}
      cancel-in-progress: false
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: ${{ inputs.python-version }}
      - name: Integration Testing
        env:
          PYTEST_XDIST_AUTO_NUM_WORKERS: 20
          LABELBOX_TEST_API_KEY: ${{ secrets[inputs.api-key] }}
          DA_GCP_LABELBOX_API_KEY: ${{ secrets[inputs.da-test-key] }}
          LABELBOX_TEST_ENVIRON: ${{ inputs.test-env }}
        working-directory: libs/labelbox
        run: rye run integration
  unit-data:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: ${{ inputs.python-version }}
      - name: Unit && Data Testing
        env:
          PYTEST_XDIST_AUTO_NUM_WORKERS: 20
          LABELBOX_TEST_API_KEY: ${{ secrets[inputs.api-key] }}
          DA_GCP_LABELBOX_API_KEY: ${{ secrets[inputs.da-test-key] }}
          LABELBOX_TEST_ENVIRON: ${{ inputs.test-env }}
        working-directory: libs/labelbox
        run: |
          rye sync -f --features labelbox/data
          rye run unit
          rye run data


Labelbox Python SDK Staging (Develop)

name: Labelbox Python SDK Staging (Develop)

on:
  push:
    branches: [develop]
  pull_request:
    branches: [develop]

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

permissions:
  id-token: write

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - python-version: 3.8
            api-key: STAGING_LABELBOX_API_KEY_2
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.9
            api-key: STAGING_LABELBOX_API_KEY_3
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: "3.10"
            api-key: STAGING_LABELBOX_API_KEY_4
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.11
            api-key: STAGING_LABELBOX_API_KEY
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.12
            api-key: STAGING_LABELBOX_API_KEY_5
            da-test-key: DA_GCP_LABELBOX_API_KEY
    uses: ./.github/workflows/python-package-shared.yml
    with:
      python-version: ${{ matrix.python-version }}
      api-key: ${{ matrix.api-key }}
      da-test-key: ${{ matrix.da-test-key }}
      fixture-profile: true
      test-env: 'staging'
    secrets: inherit
  test-pypi:
    runs-on: ubuntu-latest
    needs: ['build']
    environment:
      name: Test-PyPI
      url: 'https://test.pypi.org/p/labelbox-test'
    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: '3.8'
      - name: Create build
        id: create-build
        working-directory: libs/labelbox
        run: |
          VERSION=$(date +"%Y.%m.%d.%H.%M")
          echo "pip install --index-url https://test.pypi.org/simple/ --extra-index-url=https://pypi.org/simple/ labelbox-test@$VERSION" >> "$GITHUB_STEP_SUMMARY"
          rye version "$VERSION"
          rye run toml set --toml-path pyproject.toml project.name labelbox-test
          rye build
      - name: Publish package distributions to Test PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: dist/
          repository-url: https://test.pypi.org/legacy/
  test-container:
    runs-on: ubuntu-latest
    needs: ['build']
    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      packages: write
    env:
      CONTAINER_IMAGE: "ghcr.io/${{ github.repository }}"
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}

      - name: downcase CONTAINER_IMAGE
        run: |
          echo "CONTAINER_IMAGE=${CONTAINER_IMAGE,,}" >> ${GITHUB_ENV}

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

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

      - name: Build and push (Develop)
        if: github.event_name == 'push'
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./libs/labelbox/Dockerfile
          github-token: ${{ secrets.GITHUB_TOKEN }}
          push: true

          platforms: |
            linux/amd64
            linux/arm64

          tags: |
            ${{ env.CONTAINER_IMAGE }}:develop
            ${{ env.CONTAINER_IMAGE }}:${{ github.sha }}

      - name: Build and push (Pull Request)
        if: github.event_name == 'pull_request'
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./libs/labelbox/Dockerfile
          github-token: ${{ secrets.GITHUB_TOKEN }}
          push: true

          platforms: |
            linux/amd64
            linux/arm64

          tags: |
            ${{ env.CONTAINER_IMAGE }}:${{ github.sha }}


Labelbox Python SDK Publish

name: Labelbox Python SDK Publish

on:
  workflow_dispatch:
    inputs:
      tag:
        description: 'Release Tag'
        required: true
      skip-tests:
        description: 'Skip PROD Test (Do not do this unless there is an emergency)'
        default: false
        type: boolean

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

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

jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      hashes: ${{ steps.hash.outputs.hashes }}
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.tag }}
      - name: Install the latest version of rye
        uses: eifinger/setup-rye@v2
        with:
          version: ${{ vars.RYE_VERSION }}
          enable-cache: true
      - name: Rye Setup
        run: |
           rye config --set-bool behavior.use-uv=true
      - name: Create build
        working-directory: libs/labelbox
        run: |
          rye sync
          rye build
      - name: "Generate hashes"
        id: hash
        run: |
          cd dist && echo "hashes=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
      - uses: actions/upload-artifact@v4
        with:
          name: build
          path: ./dist
  provenance_python:
    needs: [build]
    permissions:
      actions: read
      contents: write
      id-token: write # Needed to access the workflow's OIDC identity.
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
    with:
      base64-subjects: "${{ needs.build.outputs.hashes }}"
      upload-assets: true
      upload-tag-name: ${{ inputs.tag }} # Tag from the initiation of the workflow
  test-build:
    if: ${{ !inputs.skip-tests }}
    needs: ['build']
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - python-version: 3.8
            prod-key: PROD_LABELBOX_API_KEY_2
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.9
            prod-key: PROD_LABELBOX_API_KEY_3
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: "3.10"
            prod-key: PROD_LABELBOX_API_KEY_4
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.11
            prod-key: LABELBOX_API_KEY
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.12
            prod-key: PROD_LABELBOX_API_KEY_5
            da-test-key: DA_GCP_LABELBOX_API_KEY
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.tag }}
      - name: Install the latest version of rye
        uses: eifinger/setup-rye@v2
        with:
          version: ${{ vars.RYE_VERSION }}
          enable-cache: true
      - name: Rye Setup
        run: |
           rye config --set-bool behavior.use-uv=true
      - name: Python setup
        run: rye pin ${{ matrix.python-version }}
      - uses: actions/download-artifact@v4
        with:
          name: build
          path: ./dist
      - name: Prepare package and environment
        run: |
          rye sync -f --update-all
          rye run toml unset --toml-path pyproject.toml tool.rye.workspace
          rye sync -f --update-all
      - name: Integration Testing
        env:
          PYTEST_XDIST_AUTO_NUM_WORKERS: 32
          LABELBOX_TEST_API_KEY: ${{ secrets[matrix.prod-key] }}
          DA_GCP_LABELBOX_API_KEY: ${{ secrets[matrix.da-test-key] }}
          LABELBOX_TEST_ENVIRON: prod
        run: |
          rye add labelbox --path ./$(find ./dist/ -name *.tar.gz) --sync --absolute
          cd libs/labelbox
          rm pyproject.toml
          rye run pytest tests/integration
      - name: Data Testing
        env:
          PYTEST_XDIST_AUTO_NUM_WORKERS: 32
          LABELBOX_TEST_API_KEY: ${{ secrets[matrix.prod-key] }}
          DA_GCP_LABELBOX_API_KEY: ${{ secrets[matrix.da-test-key] }}
          LABELBOX_TEST_ENVIRON: prod
        run: |
          rye add labelbox --path ./$(find ./dist/ -name *.tar.gz) --sync --absolute --features data
          cd libs/labelbox
          rye run pytest tests/data
  publish-python-package-to-release:
    runs-on: ubuntu-latest
    needs: ['build']
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.tag }}
      - uses: actions/download-artifact@v4
        with:
          name: build
          path: ./artifact
      - name: Upload dist to release
        run: |
          gh release upload ${{ inputs.tag }} ./artifact/*
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  pypi-publish:
    runs-on: ubuntu-latest
    needs: ['build', 'test-build']
    if: |
      always() &&
      (needs.test-build.result == 'success' || needs.test-build.result == 'skipped') && github.event.inputs.tag
    environment:
      name: publish
      url: 'https://pypi.org/project/labelbox/'
    permissions:
      # IMPORTANT: this permission is mandatory for trusted publishing
      id-token: write
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: build
          path: ./artifact
      - name: Publish package distributions to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: artifact/
  container-publish:
    runs-on: ubuntu-latest
    needs: ['build', 'test-build']
    permissions:
      packages: write
    outputs:
      image: ${{ steps.image.outputs.image }}
      digest: ${{ steps.build_container.outputs.digest }}
    if: |
      always() &&
      (needs.test-build.result == 'success' || needs.test-build.result == 'skipped') && github.event.inputs.tag
    env:
      CONTAINER_IMAGE: "ghcr.io/${{ github.repository }}"
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.tag }}

      - name: downcase CONTAINER_IMAGE
        run: |
          echo "CONTAINER_IMAGE=${CONTAINER_IMAGE,,}" >> ${GITHUB_ENV}

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

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

      - name: Build and push
        uses: docker/build-push-action@v5
        id: build_container
        with:
          context: .
          file: ./libs/labelbox/Dockerfile
          github-token: ${{ secrets.GITHUB_TOKEN }}
          push: true

          platforms: |
            linux/amd64
            linux/arm64

          tags: |
            ${{ env.CONTAINER_IMAGE }}:latest
            ${{ env.CONTAINER_IMAGE }}:${{ inputs.tag }}
      - name: Output image
        id: image
        run: |
          # NOTE: Set the image as an output because the `env` context is not
          # available to the inputs of a reusable workflow call.
          image_name="${CONTAINER_IMAGE}"
          echo "image=$image_name" >> "$GITHUB_OUTPUT"

  provenance_container:
    needs: [container-publish]
    permissions:
      actions: read # for detecting the Github Actions environment.
      id-token: write # for creating OIDC tokens for signing.
      packages: write # for uploading attestations.
    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
    with:
      image: ${{ needs. container-publish.outputs.image }}
      digest: ${{ needs. container-publish.outputs.digest }}
      registry-username: ${{ github.actor }}
    secrets:
      registry-password: ${{ secrets.GITHUB_TOKEN }}


Labelbox Python SDK Prod (Develop)

name: Labelbox Python SDK Prod (Develop)

on:
    workflow_dispatch:

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

jobs:
  build:
    strategy:
      fail-fast: false
      matrix:
        include:
          - python-version: 3.8
            api-key: PROD_LABELBOX_API_KEY_2
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.9
            api-key: PROD_LABELBOX_API_KEY_3
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: "3.10"
            api-key: PROD_LABELBOX_API_KEY_4
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.11
            api-key: LABELBOX_API_KEY
            da-test-key: DA_GCP_LABELBOX_API_KEY
          - python-version: 3.12
            api-key: PROD_LABELBOX_API_KEY_5
            da-test-key: DA_GCP_LABELBOX_API_KEY
    uses: ./.github/workflows/python-package-shared.yml
    with:
      python-version: ${{ matrix.python-version }}
      api-key: ${{ matrix.api-key }}
      da-test-key: ${{ matrix.da-test-key }}
      fixture-profile: false
      test-env: 'prod'
    secrets: inherit


Labelbox Example Notebook Workflow

name: Labelbox Example Notebook Workflow

on:
  push:
    branches: [develop]
    paths:
      - examples/**
  pull_request:
    branches: [develop]
    paths:
      - examples/**

permissions:
  contents: write
  pull-requests: write

jobs:
  # Get installs from rye and run rye run clean to format
  format:
    if: github.event.pull_request.merged == false
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: 3.12
      - name: Format
        working-directory: examples
        run: rye run clean
      - name: Commit changes
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add examples/.
          git commit -m ":art: Cleaned" || exit 0
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.head_ref }}
  # See if notebooks were added or deleted (name change counts as both)
  changes:
    needs: format
    if: github.event.pull_request.merged == false
    runs-on: ubuntu-latest
    outputs:
      addedOrModified: ${{ steps.filter.outputs.addedOrModified }}
    steps:
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          filters: |
            addedOrModified:
              - added|deleted: 'examples/**/*.ipynb'
  # Create readme if the above job shows true using rye run create-readme
  create:
    needs: changes
    if: ${{ needs.changes.outputs.addedOrModified == 'true' }} && github.event.pull_request.merged == false
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: 3.12
      - name: Create readme
        working-directory: examples
        run: rye run create-readme
      - name: Commit changes
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add examples/.
          git commit -m ":memo: README updated" || exit 0
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.head_ref }}


ReadMe GitHub Action 🦉

name: ReadMe GitHub Action 🦉

on:
  pull_request:
    branches: [develop]
    types:
      - closed
    paths:
      - examples/README.md

jobs:
  custompages:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/python-package-shared-setup
        with:
          rye-version: ${{ vars.RYE_VERSION }}
          python-version: 3.12
      - name: Create readme
        working-directory: examples
        run: rye run create-doc-readme
      - name: Run `custompages` command
        uses: readmeio/rdme@v8
        with:
          rdme: custompages examples/tutorials.html --key=${{ secrets.README_API_KEY }}


CI

name: CI

on: [push, pull_request]

jobs:
  build:
    name: Build and test
    runs-on: ubuntu-22.04
    strategy:
      matrix:
        python-version: ["3.10"]
    env:
      Z3_VERSION: "4.11.2"
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
      - name: Install Z3
        run: |
          wget -nv https://github.com/Z3Prover/z3/releases/download/z3-${Z3_VERSION}/z3-${Z3_VERSION}-x64-glibc-2.31.zip
          unzip z3-${Z3_VERSION}-x64-glibc-2.31.zip
          echo `pwd`/z3-${Z3_VERSION}-x64-glibc-2.31/bin >> $GITHUB_PATH
          echo PYTHONPATH=`pwd`/z3-${Z3_VERSION}-x64-glibc-2.31/bin/python >> $GITHUB_ENV
      - run: z3 --version  # make sure z3 is installed
      - run: python3 -c 'import z3'  # make sure z3 can be imported from python
      - run: make gh-test


name: CI

on:
  push:
    branches:
      - 'master'
      - 'pymbar4'
      - 'gha'
  pull_request:
    branches:
      - 'master'
      - 'pymbar4'
      - 'gha'
  schedule:
    # Nightly Tests
    - cron: '0 0 * * *'

jobs:

  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [macOS-latest, ubuntu-latest, windows-latest]
        python-version: ["3.8", "3.11", "3.12"]  # Check against oldest and newest versions
        jax: ["", "_jax"]
        exclude:  # Skip win + jax
          - jax: "_jax"
            os: windows-latest
    env:
      PYVER: ${{ matrix.python-version }}
      CI_OS: ${{ matrix.os }}
      PACKAGE: "pymbar"

    steps:
    - name: Checkout the code
      uses: actions/checkout@v3

    - name: Additional info about the build
      shell: bash
      run: |
        uname -a
        df -h
        ulimit -a

    - name: Setup Conda via Mamba
      uses: mamba-org/setup-micromamba@v1
      with:
        environment-file: devtools/conda-envs/test_env${{ matrix.jax }}.yaml
        environment-name: test
        condarc: |
          channels:
            - conda-forge
            - defaults
        create-args: >-
          python=${{ matrix.python-version }}

    - name: Install package
      shell: bash -l {0}
      run: |
        python -m pip install . --no-deps
        micromamba list

    - name: Run tests (pytest)
      shell: bash -l {0}
      run: |
        pytest -v --cov=$PACKAGE --cov-report=xml --color=yes --doctest-modules $PACKAGE/

    - name: Run examples
      shell: bash -l {0}
      run: |
        ### constant-force-optical-trap
        cd examples/constant-force-optical-trap
        python extract-data.py
        python force-bias-optical-trap.py
        ### harmonic-oscillators
        cd ../harmonic-oscillators
        python harmonic-oscillators.py
        python harmonic-oscillators-distributions.py
        cd ../heat-capacity
        python heat-capacity.py -b 2
        ### parallel-tempering-2dfes
        cd ../parallel-tempering-2dfes
        python parallel-tempering-2dfes.py
        ### umbrella-sampling-fes
        cd ../umbrella-sampling-fes
        python umbrella-sampling.py
        python umbrella-sampling-advanced-fes.py

    - name: CodeCov
      uses: codecov/codecov-action@v1
      with:
        token: ${{ secrets.CODECOV_TOKEN }}
        file: ./coverage.xml
        flags: unittests
        yml: ./.codecov.yml

  lint-format:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: [3.8]
    env:
      PACKAGE: "pymbar"

    steps:
    - name: Checkout the code
      uses: actions/checkout@v1

    - name: Setup Conda via Mamba
      uses: mamba-org/provision-with-micromamba@main
      with:
        environment-file: devtools/conda-envs/test_env.yaml
        environment-name: test
        channels: conda-forge,defaults
        extra-specs: |
          python=${{ matrix.python-version }}

    - name: Install linter and formatter
      shell: bash -l {0}
      run: |
        micromamba install pylint black

    - name: Run pylint
      shell: bash -l {0}
      run: |
        pylint $PACKAGE/

    # Black has a default --exclude which reads .gitignore.
    # Black's --exclude and --extend-exclude are single specified RegEx patterns and overwrite if multi-marked.
    - name: Run black check
      shell: bash -l {0}
      if: always()
      run: |
        black --check -l 99 $PACKAGE/ examples/ --extend-exclude "$PACKAGE/_version.py"


OAS build

name: OAS build

# This workflow runs all unit and regression tests, as well as coveralls.
# On the pull-request events, this workflow runs OAS tests, checks code format by flake8/black, and builds the docs.
# On the push-to-main events, it also deploys the docs.

on:
  pull_request:
  push:
    branches:
      - main
    tags:
      - v*.*.*
  schedule:
    # Run the tests at 7:23pm UTC on the 2nd and 17th of every month
    - cron: '23 19 2,17 * *'

jobs:
  # --- run OAS unit and regression tests ---
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false  # continue other jobs even if one of the jobs in matrix fails
      matrix:
        dep-versions: ["oldest", "latest"]
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set versions to test here ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        PYTHON_VERSION_OLDEST: ['3.8']
        PYTHON_VERSION_LATEST: ['3.11']
        WHEEL_VERSION_OLDEST: ['0.38.4']  # latest wheel cannot build the oldest OpenMDAO (3.17 or older)
        NUMPY_VERSION_OLDEST: ['1.20']  # latest is most recent on PyPI
        SCIPY_VERSION_OLDEST: ['1.6.0']  # latest is most recent on PyPI
        OPENMDAO_VERSION_OLDEST: ['3.15']  # latest is most recent on PyPI
        MPHYS_VERSION_OLDEST: ['1.0.0']  # latest is most recent on PyPI
        PYGEO_VERSION_OLDEST: ['1.6.0']  # latest is pulled from main branch, for some reason anything after 1.12.2 seg faults on the old build
        VSP_VERSION: ['3.27.1']  # used for both builds
        PETSC_VERSION_LATEST: ['3.19.1']
        CYTHON_VERSION: ['0.29.36']  # used for both builds

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.PYTHON_VERSION_OLDEST }}
      if: ${{ matrix.dep-versions == 'oldest' }}
      uses: actions/setup-python@v2
      with:
        python-version:  ${{ matrix.PYTHON_VERSION_OLDEST }}
    - name: Set up Python ${{ matrix.PYTHON_VERSION_LATEST }}
      if: ${{ matrix.dep-versions == 'latest' }}
      uses: actions/setup-python@v2
      with:
        python-version:  ${{ matrix.PYTHON_VERSION_LATEST }}

    # we need OpenVSP to run vsp tests.
    - name: Install OpenVSP
      run: |
        sudo apt-get update
        export PYTHON_INCLUDE_DIR=$(python -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())")
        export PYTHON_LIBRARY=$(python -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
        export INST_PREFIX=$(python -c "import distutils.sysconfig as sysconfig; print(sysconfig.get_config_var('prefix'))")
        cd ..
        sudo apt-get install cmake libx11-dev libfltk1.3-dev libcpptest-dev libglm-dev libeigen3-dev libcminpack-dev \
                             libglew-dev doxygen graphviz texlive-latex-base
        mkdir OpenVSP
        cd OpenVSP
        mkdir build buildlibs
        # Download source code
        wget -q https://github.com/OpenVSP/OpenVSP/archive/refs/tags/OpenVSP_${{ matrix.VSP_VERSION }}.tar.gz
        tar -xf OpenVSP_${{ matrix.VSP_VERSION }}.tar.gz
        mv OpenVSP-OpenVSP_${{ matrix.VSP_VERSION }} repo
        # Build dependency libs
        cd buildlibs
        cmake -DVSP_USE_SYSTEM_LIBXML2=true -DVSP_USE_SYSTEM_FLTK=true -DVSP_USE_SYSTEM_GLM=true \
              -DVSP_USE_SYSTEM_GLEW=true -DVSP_USE_SYSTEM_CMINPACK=true -DVSP_USE_SYSTEM_LIBIGES=false \
              -DVSP_USE_SYSTEM_EIGEN=false -DVSP_USE_SYSTEM_CODEELI=false -DVSP_USE_SYSTEM_CPPTEST=false \
              -DPYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR} -DPYTHON_LIBRARY=${PYTHON_LIBRARY} ../repo/Libraries -DCMAKE_BUILD_TYPE=Release
        make -j8
        # Build OpenVSP
        cd ..
        export BUILD_LIBS_PATH=`pwd`
        cd build
        cmake ../repo/src/ -DVSP_NO_GRAPHICS=true -DVSP_LIBRARY_PATH=${BUILD_LIBS_PATH}/buildlibs \
              -DPYTHON_INCLUDE_DIR=${PYTHON_INCLUDE_DIR} -DPYTHON_LIBRARY=${PYTHON_LIBRARY} \
              -DCMAKE_BUILD_TYPE=Release
        make -j8
        make package
        # Install python interface
        pushd _CPack_Packages/Linux/ZIP/OpenVSP-${{ matrix.VSP_VERSION }}-Linux/python
        pip install -r requirements.txt
        pushd ..
        cp vspaero vspscript vspslicer $INST_PREFIX/bin
        popd
        popd

    # OAS dependencies are specified in setup.py.
    - name: Install OAS and its dependencies (oldest versions)
      if: ${{ matrix.dep-versions == 'oldest' }}
      run: |
        python -m pip install wheel==${{ matrix.WHEEL_VERSION_OLDEST }}
        pip install numpy==${{ matrix.NUMPY_VERSION_OLDEST }} scipy==${{ matrix.SCIPY_VERSION_OLDEST }} openmdao==${{ matrix.OPENMDAO_VERSION_OLDEST }} mphys==${{ matrix.MPHYS_VERSION_OLDEST }}
        pip install -e .[test]
    - name: Install OAS and its dependencies (latest versions)
      if: ${{ matrix.dep-versions == 'latest' }}
      run: |
        python -m pip install --upgrade pip wheel
        pip install -e .[test,mphys]

    - name: Install MPI
      run: |
        sudo apt-get install openmpi-bin libopenmpi-dev
        pip install mpi4py

    # install PETSc on the latest for MPI tests
    - name: Install PETSc
      if: ${{ matrix.dep-versions == 'latest' }}
      run: |
        cd ..
        pip download petsc==${{ matrix.PETSC_VERSION_LATEST }}
        tar -xf petsc-${{ matrix.PETSC_VERSION_LATEST }}.tar.gz
        cd petsc-${{ matrix.PETSC_VERSION_LATEST }}
        export PETSC_ARCH=real-debug
        export PETSC_DIR=`pwd`
        ./configure --PETSC_ARCH=$PETSC_ARCH --download-fblaslapack
        make PETSC_DIR=$PETSC_DIR PETSC_ARCH=$PETSC_ARCH all
        # Cython 3.0.0 break petsc4py install
        pip install cython==${{ matrix.CYTHON_VERSION }}
        pip install petsc4py==${{ matrix.PETSC_VERSION_LATEST }}

    # We need pySpline/pyGeo to run FFD tests.
    - name: Install pySpline
      run: |
        cd ..
        git clone https://github.com/mdolab/pyspline.git
        cd pyspline
        cp config/defaults/config.LINUX_GFORTRAN.mk config/config.mk
        make
        pip install -e .

    - name: Install pyGeo ${{ matrix.PYGEO_VERSION_OLDEST }}
      if: ${{ matrix.dep-versions == 'oldest' }}
      run: |
        pip install "pygeo[testing] @ git+https://github.com/mdolab/pygeo.git@v${{ matrix.PYGEO_VERSION_OLDEST }}"
    - name: Install pyGeo latest
      if: ${{ matrix.dep-versions == 'latest' }}
      run: |
        pip install "pygeo[testing] @ git+https://github.com/mdolab/pygeo.git"

    - name: List installed Python packages
      run: |
        pip list -v

    - name: Run tests (latest)
      env:
        OMPI_MCA_btl: ^openib   # prevent OpenMPI warning messages
      if: ${{ matrix.dep-versions == 'latest' }}
      run: |
        testflo -n 2 -v openaerostruct --coverage --coverpkg openaerostruct
        coverage xml
    # skip MPI tests on the oldest
    - name: Run tests (oldest)
      env:
        OMPI_MCA_btl: ^openib   # prevent OpenMPI warning messages
      if: ${{ matrix.dep-versions == 'oldest' }}
      run: |
        testflo -n 2 -v --exclude \*MPI\* openaerostruct --coverage --coverpkg openaerostruct
        coverage xml

    - name: Upload Coverage to Codecov
      uses: codecov/codecov-action@v4
      with:
        fail_ci_if_error: true
        verbose: true
      env:
        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

  # --- linting and formatting ---
  black:
    uses: mdolab/.github/.github/workflows/black.yaml@main
  flake8:
    uses: mdolab/.github/.github/workflows/flake8.yaml@main

  # --- publish to PyPI
  pypi:
    needs: [test, flake8, black]
    uses: mdolab/.github/.github/workflows/pypi.yaml@main
    secrets: inherit


CodeQL

name: "CodeQL"

on:
  push:
    branches: [ "master", "osbs_ocp3" ]
  pull_request:
    branches: [ "master" ]
  schedule:
    - cron: "4 21 * * 3"

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

    strategy:
      fail-fast: false
      matrix:
        language: [ python ]

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v2
        with:
          languages: ${{ matrix.language }}
          queries: +security-and-quality

      - name: Autobuild
        uses: github/codeql-action/autobuild@v2

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v2
        with:
          category: "/language:${{ matrix.language }}"


Linters

name: Linters

on:
  pull_request:

  push:
    branches:
      - master
      - release_*

jobs:
  bandit:
    name: Bandit analyzer for Python ${{ matrix.os.python }}
    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        os:
          - name: fedora
            version: 33
            python: 3
            engine: docker

    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - run: ./test.sh
        env:
          OS: ${{ matrix.os.name }}
          OS_VERSION: ${{ matrix.os.version }}
          PYTHON_VERSION: ${{ matrix.os.python }}
          ENGINE: ${{ matrix.os.engine }}
          ACTION: bandit

  flake8:
    name: Flake8 for Python 3
    runs-on: ubuntu-latest

    steps:
      - name: Check out repo
        uses: actions/checkout@v3

      - name: Run flake8 for Python 3
        uses: containerbuildsystem/actions/flake8@master

  markdownlint:
    name: Markdownlint
    runs-on: ubuntu-latest

    steps:
      - name: Check out repo
        uses: actions/checkout@v3

      - name: Run markdownlint
        uses: containerbuildsystem/actions/markdownlint@master

  pylint:
    name: Pylint analyzer for Python ${{ matrix.os.python }}
    runs-on: ubuntu-latest

    strategy:
      matrix:
        os:
          - name: fedora
            version: 33
            python: 3
            engine: docker

    steps:
      - uses: actions/checkout@v3

      - run: ./test.sh
        env:
          OS: ${{ matrix.os.name }}
          OS_VERSION: ${{ matrix.os.version }}
          PYTHON_VERSION: ${{ matrix.os.python }}
          ENGINE: ${{ matrix.os.engine }}
          ACTION: pylint

  shellcheck:
    name: Shellcheck
    runs-on: ubuntu-latest

    steps:
      - name: Check out repo
        uses: actions/checkout@v3

      - name: Run ShellCheck
        uses: containerbuildsystem/actions/shellcheck@master


Unittests

name: Unittests

on:
  pull_request:

  push:
    branches:
      - master
      - release_*

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

    strategy:
      fail-fast: false
      matrix:
        os:
          - name: fedora
            version: 36
            python: 3
            engine: docker

          - name: fedora
            version: 37
            python: 3
            engine: docker

    steps:
      - name: Check out repo
        uses: actions/checkout@v3

      - name: pytests via test.sh
        env:
          OS: ${{ matrix.os.name }}
          OS_VERSION: ${{ matrix.os.version }}
          PYTHON_VERSION: ${{ matrix.os.python }}
          ENGINE: ${{ matrix.os.engine }}
        run: ./test.sh

      - name: Upload pytest html report
        uses: actions/upload-artifact@v3
        if: failure()
        with:
          path: __pytest_reports/osbs-unit-tests.html
          name: osbs-unit-tests_${{ matrix.os.name }}_${{ matrix.os.version }}.python${{ matrix.os.python }}.html

      - name: Run coveralls-python
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          COVERALLS_FLAG_NAME: ${{ matrix.os.name }}-${{ matrix.os.version }}-python${{ matrix.os.python }}
          COVERALLS_PARALLEL: true
        run: |
          pip3 install --upgrade pip==20.0.2
          pip3 install --upgrade setuptools
          pip3 install --upgrade coveralls
          /home/runner/.local/bin/coveralls --service=github

  coveralls-finish:
    name: Finish coveralls-python
    needs: test
    runs-on: ubuntu-latest
    steps:
    - name: Finished
      run: |
        pip3 install --upgrade pip==20.0.2
        pip3 install --upgrade setuptools
        pip3 install --upgrade coveralls
        /home/runner/.local/bin/coveralls --finish --service=github
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


Build

name: "Build"
on: [push, workflow_dispatch]
jobs:
  codeanalysis:
    name: "Code Quality"
    runs-on: ubuntu-latest

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

      - name: Black
        uses: psf/black@stable
        with:
          src: "./src ./tests"

  test:
    name: "Test"
    runs-on: ${{ matrix.os-version }}
    env:
      LANG: en_US.UTF-8
    strategy:
      matrix:
        python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
        os-version: ['ubuntu-20.04', 'windows-latest', 'macos-latest']
# Pinned Ubuntu version to 20.04 since no Python 3.6 builds available on ubuntu-latest (22.04) as of 2022-12-7.
#        os-version: [ubuntu-latest, windows-latest, macos-latest]

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

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

      - name: Install dependencies (Ubuntu)
        if: startsWith(matrix.os-version, 'ubuntu')
        run: |
          sudo apt-get update
          sudo apt-get install build-essential libkrb5-dev

      - name: Install dependencies (Common)
        run: |
          # Setup tox & code coverage
          pip install --upgrade pip
          pip install tox tox-gh-actions pytest pytest-cov scikit-learn

      - name: Run Tests
        run: |
          tox

      # Separate upload task for unit test coverage allows for flagged analysis of test coverage
      - name: Upload Unit Test Coverage
        if: startsWith(matrix.os-version, 'ubuntu')
        uses: codecov/codecov-action@v3
        with:
          directory: ./.reports/
          fail_ci_if_error: false
          file: unit.xml
          flags: unit
          verbose: true

      # Separate upload task for integration test coverage allows for flagged analysis of test coverage
      - name: Upload Integration Test Coverage
        if: startsWith(matrix.os-version, 'ubuntu')
        uses: codecov/codecov-action@v3
        with:
          directory: ./.reports/
          fail_ci_if_error: false
          file: integration.xml
          flags: integration
          verbose: true

# Uncomment when scenario test cases are working again
#      Separate upload task for scenario test coverage allows for flagged analysis of test coverage
#      - name: Upload Scenario Test Coverage
#        if: matrix.os-version == 'ubuntu-latest'
#        uses: codecov/codecov-action@v3
#        with:
#          directory: ./.reports/
#          fail_ci_if_error: true
#          file: scenarios.xml
#          flags: scenarios
#          verbose: true

  gh-pages:
    name: "Build Documentation"
    runs-on: ubuntu-latest
    needs: test
    if: startsWith(github.ref, 'refs/tags/') # run only on tagged commits

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

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

      - name: Setup environment
        run: |
          mv doc docs
          sudo apt-get install build-essential
          pip install sphinx six pyyaml pandas pydata-sphinx-theme numpydoc

      - name: Check documentation
        uses: ammaraskar/sphinx-problem-matcher@master

      - name: Build documentation
        run: sphinx-build -Ean -b html -j auto -D todo_include_todos=0 ./docs ./docs/_build/html

      - name: Archive artifacts
        uses: actions/upload-artifact@v2
        with:
          name: html-docs
          path: ./docs/_build/html

  # Build a package for distribution through PyPI.org (pip install sasctl)
  build_pypi:
    name: "Build PyPI Package"
    runs-on: ubuntu-latest
    needs: test
    if: startsWith(github.ref, 'refs/tags/')  # run only on tagged commits

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

      - name: Setup Python
        uses: actions/setup-python@v2

      - name: Install dependencies
        run: |
          pip install --upgrade pip
          pip install setuptools wheel twine

      - name: Build Package
        run: |
          python setup.py sdist bdist_wheel

      - name: Extract Changes
        shell: python
        run: |
          import os, re
          tag_name = os.environ['GITHUB_REF'].replace('refs/tags/', '')
          changes = ''
          with open('CHANGELOG.md') as f:
              lines = f.read()
          match = re.search('%s [()\d\-\s]*' % tag_name, lines)
          if match:
              lines = lines[match.end():]
              changes = re.split('-----+', lines)[0].split('\n')
              changes = '\n'.join(changes[:-2])
          with open('release_notes.md', 'w') as f:
              f.write(changes)

      - name: Archive distribution artifacts
        # Archive distribution files for use by auto (or manual) PyPI upload
        uses: actions/upload-artifact@v3
        with:
          name: pypi-dist
          path: ./dist

      - name: Archive changelog artifacts
        uses: actions/upload-artifact@v3
        with:
          name: release_notes
          path: release_notes.md

  # Build a package for distribution through Anaconda.org (conda install sasctl)
  build_conda:
    name: "Build Conda Package"
    runs-on: ubuntu-latest
    needs: test
    if: startsWith(github.ref, 'refs/tags/') # run only on tagged commits

    steps:
      # Setup Miniconda
      - uses: conda-incubator/setup-miniconda@v2
        with:
          auto-update-conda: true

      - name: Install conda-build
        shell: bash -l {0}
        run: |
          conda install conda-build

      - name: Checkout repository
        uses: actions/checkout@v3

      # Build package and store results in .build folder
      - name: Build package
        shell: bash -l {0}
        run: |
          conda build --output-folder .build .conda

      # Archive distribution files.  Will upload in a downstream job.
      - name: Archive distribution artifacts
        uses: actions/upload-artifact@v3
        with:
          name: conda-dist
          path: .build

  # Publishes the new package to PyPI, uploads the latest documentation to GitHub Pages
  # and creates a new release with change notes on GitHub.
  publish:
    name: "Publish"
    runs-on: ubuntu-latest
    needs: [gh-pages, build_pypi, build_conda]

    steps:
      - name: Download documentation
        uses: actions/download-artifact@v3
        with:
          name: html-docs
          path: ./html-docs

      - name: Download release
        uses: actions/download-artifact@v3
        with:
          name: pypi-dist
          path: ./dist

      - name: Download release notes
        uses: actions/download-artifact@v3
        with:
          name: release_notes

      - name: Zip Documentation
        run: zip -r documentation.zip ./html-docs

      - name: Display structure of downloaded files
        run: ls -R

      # Create a draft release on GitHub
      - name: Create Release
        id: create_release
        uses: softprops/action-gh-release@v1
        with:
          draft: true
          body_path: release_notes.md
          body: ""
          files: documentation.zip

      # Publish the documentation to GitHub Pages
      - name: Deploy documentation
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./html-docs

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          verbose: true

      # Publish the release on GitHub (remove draft status)
      - name: Publish release
        uses: StuYarrow/publish-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          id: ${{ steps.create_release.outputs.id }}

  # Uploads the package to Anaconda.
  # NOTE: could be merged with `publish` job above.  Left as a separate, final job since it
  #       involves multiple steps to setup the environment and if this job fails, the package
  #       has already been made available through pip, and the release info published.
  upload_conda:
    name: "Upload Conda Package"
    runs-on: ubuntu-latest
    needs: [publish]

    steps:
      # Setup Miniconda
      - uses: conda-incubator/setup-miniconda@v2
        with:
          auto-update-conda: true

      # Setup Anaconda client (required for upload)
      - name: Install anaconda client
        shell: bash -l {0}
        run: |
          conda install anaconda-client

      # Download release files
      - name: Download release
        uses: actions/download-artifact@v3
        with:
          name: conda-dist
          path: ./dist

      # Upload release to Anaconda.org
      - name: Upload release
        shell: bash -l {0}
        run: |
          anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload -u sas-institute ./dist/noarch/sasctl-*.tar.bz2


Security Scan

name: "Security Scan"

on:
  push:
    branches: [ master ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ master ]
  schedule:
    - cron: '43 20 * * 4'

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

    permissions:
      # Require to update security info on GH repo
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'python' ]

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

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: python

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


TFRS release-2.20.0

## For each release, the value of name, branches, RELEASE_NAME and PR_NUMBER need to be adjusted accordingly
## For each release, update lib/config.js: version and releaseBranch

name: TFRS release-2.20.0

on:
  workflow_dispatch:
  workflow_call:

env:
  ## The pull request number of the Tracking pull request to merge the release branch to main
  ## Also remember to update the version in .pipeline/lib/config.js
  PR_NUMBER: 2894
  RELEASE_NAME: release-2.20.0

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

jobs:

  unit-test:

    name: Run Backend Unit Tests
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Run coverage report for django tests
        uses: kuanfandevops/django-test-action@itvr-django-test
        continue-on-error: true
        with:
          settings-dir-path: "backend/api"
          requirements-file: "backend/requirements.txt"
          managepy-dir: backend

  lint:

    name: Linting
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Frontend Linting
        continue-on-error: true
        run: |
          cd frontend
          pwd
          npm install
          npm run lint

      - name: Backend linting
        uses: github/super-linter/slim@v4
        continue-on-error: true
        env:
          DEFAULT_BRANCH: ${{ env.RELEASE_NAME }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          FILTER_REGEX_INCLUDE: .*backend/.*.py
          VALIDATE_PYTHON_PYLINT: true
          LOG_LEVEL: WARN

  build:

    name: Build TFRS on Openshift
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:

      ## it will checkout to /home/runner/work/itvr/itvr
      - name: Check out repository
        uses: actions/checkout@v3

      ## Log in to Openshift with a token of service account
      - name: Log in to Openshift
        ##uses: redhat-actions/oc-login@v1
        uses: smlgbl/oc-login@main
        with:
          openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }}
          openshift_token: ${{ secrets.OPENSHIFT_TOKEN }}
          insecure_skip_tls_verify: true
          namespace: ${{ secrets.OPENSHIFT_NAMESPACE_PLATE }}-tools

      ## Run build on Openshift
      - name: Run build
        run: |
          cd .pipeline
          npm install
          npm run build -- --pr=${{ env.PR_NUMBER }} --env=build

  deploy-on-test:

    name: Deploy TFRS on Test
    runs-on: ubuntu-latest
    timeout-minutes: 240
    needs: build

    steps:

      ## it will checkout to /home/runner/work/itvr/itvr
      - name: Check out repository
        uses: actions/checkout@v3

      - name: Log in to Openshift
        ##uses: redhat-actions/oc-login@v1
        uses: smlgbl/oc-login@main
        with:
          openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }}
          openshift_token: ${{ secrets.OPENSHIFT_TOKEN }}
          insecure_skip_tls_verify: true
          namespace: ${{ secrets.OPENSHIFT_NAMESPACE_PLATE }}-tools

      - name: Ask for approval for TFRS Test deployment
        uses: trstringer/manual-approval@v1.6.0
        with:
          secret: ${{ github.TOKEN }}
          approvers: AlexZorkin,emi-hi,tim738745,kuanfandevops,jig-patel,prv-proton,JulianForeman
          minimum-approvals: 1
          issue-title: "TFRS ${{ env.RELEASE_NAME }} Test Deployment"

      - name: Run deploy
        run: |
          cd .pipeline
          npm install
          npm run deploy -- --pr=${{ env.PR_NUMBER }} --env=test

  deploy-on-prod:

    name: Deploy TFRS on Prod
    runs-on: ubuntu-latest
    timeout-minutes: 2880
    needs: deploy-on-test

    steps:

      ## it will checkout to /home/runner/work/itvr/itvr
      - name: Check out repository
        uses: actions/checkout@v3

      - name: Log in to Openshift
        ##uses: redhat-actions/oc-login@v1
        uses: smlgbl/oc-login@main
        with:
          openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }}
          openshift_token: ${{ secrets.OPENSHIFT_TOKEN }}
          insecure_skip_tls_verify: true
          namespace: ${{ secrets.OPENSHIFT_NAMESPACE_PLATE }}-tools

      - name: Ask for approval for TFRS Prod deployment
        uses: trstringer/manual-approval@v1.6.0
        with:
          secret: ${{ github.TOKEN }}
          approvers: AlexZorkin,kuanfandevops,tim738745,jig-patel,prv-proton,JulianForeman
          minimum-approvals: 2
          issue-title: "TFRS ${{ env.RELEASE_NAME }} Prod Deployment"

      - name: Run deploy
        run: |
          cd .pipeline
          npm install
          npm run deploy -- --pr=${{ env.PR_NUMBER }} --env=prod


TFRS Dev release-2.20.0

## For each release, the value of name, branches, RELEASE_NAME and PR_NUMBER need to be adjusted accordingly
## For each release, update lib/config.js: version and releaseBranch

name: TFRS Dev release-2.20.0

on:
  push:
    branches: [ release-2.20.0 ]
    paths:
      - frontend/**
      - backend/**
  workflow_dispatch:
  workflow_call:

env:
  ## The pull request number of the Tracking pull request to merge the release branch to main
  ## Also remember to update the version in .pipeline/lib/config.js
  PR_NUMBER: 2894
  RELEASE_NAME: release-2.20.0

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

jobs:

  unit-test:

    name: Run Backend Unit Tests
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Run coverage report for django tests
        uses: kuanfandevops/django-test-action@itvr-django-test
        continue-on-error: true
        with:
          settings-dir-path: "backend/api"
          requirements-file: "backend/requirements.txt"
          managepy-dir: backend

  lint:

    name: Linting
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Frontend Linting
        continue-on-error: true
        run: |
          cd frontend
          pwd
          npm install
          npm run lint

      - name: Backend linting
        uses: github/super-linter/slim@v4
        continue-on-error: true
        env:
          DEFAULT_BRANCH: ${{ env.RELEASE_NAME }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          FILTER_REGEX_INCLUDE: .*backend/.*.py
          VALIDATE_PYTHON_PYLINT: true
          LOG_LEVEL: WARN

  build:

    name: Build TFRS on Openshift
    runs-on: ubuntu-latest
    timeout-minutes: 60

    steps:

      ## it will checkout to /home/runner/work/itvr/itvr
      - name: Check out repository
        uses: actions/checkout@v3

      ## Log in to Openshift with a token of service account
      - name: Log in to Openshift
        uses: redhat-actions/oc-login@v1.3
        with:
          openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }}
          openshift_token: ${{ secrets.OPENSHIFT_TOKEN }}
          insecure_skip_tls_verify: true
          namespace: ${{ secrets.OPENSHIFT_NAMESPACE_PLATE }}-tools

      ## Run build on Openshift
      - name: Run build
        run: |
          cd .pipeline
          npm install
          npm run build -- --pr=${{ env.PR_NUMBER }} --env=build

  deploy-on-dev:

    name: Deploy TFRS on Dev
    runs-on: ubuntu-latest
    timeout-minutes: 240
    needs: build

    steps:

      ## it will checkout to /home/runner/work/itvr/itvr
      - name: Check out repository
        uses: actions/checkout@v3

      - name: Log in to Openshift
        uses: redhat-actions/oc-login@v1.3
        with:
          openshift_server_url: ${{ secrets.OPENSHIFT_SERVER }}
          openshift_token: ${{ secrets.OPENSHIFT_TOKEN }}
          insecure_skip_tls_verify: true
          namespace: ${{ secrets.OPENSHIFT_NAMESPACE_PLATE }}-tools

      - name: Run deploy
        run: |
          cd .pipeline
          npm install
          npm run deploy -- --pr=${{ env.PR_NUMBER }} --env=dev


Cleanup old workflow runs

name: Cleanup old workflow runs
on:
  workflow_dispatch:
    inputs:
      days:
        description: 'Number of days.'
        required: true
        default: 15
      minimum_runs:
        description: 'The minimum runs to keep for each workflow.'
        required: true
        default: 10
jobs:
  del_runs:
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - name: Delete workflow runs
        uses: Mattraks/delete-workflow-runs@v2.0.4
        with:
          token: ${{ github.token }}
          repository: ${{ github.repository }}
          retain_days: ${{ github.event.inputs.days }}
          keep_minimum_runs: ${{ github.event.inputs.minimum_runs }}


Scheduled cleanup old workflow runs

name: Scheduled cleanup old workflow runs
on:
  schedule:
    - cron: '0 0 * * 0'
# At 00:00 on Sunday.

jobs:
  del_runs:
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - name: Delete workflow runs
        uses: Mattraks/delete-workflow-runs@v2.0.4
        with:
          token: ${{ github.token }}
          repository: ${{ github.repository }}
          retain_days: 15
          keep_minimum_runs: 10