Skip to content

Add Tox to manage dynamic options with pytest. #397

Add Tox to manage dynamic options with pytest.

Add Tox to manage dynamic options with pytest. #397

Workflow file for this run

name: CICD
on:
push:
branches:
- main
pull_request:
branches:
- "**"
workflow_dispatch:
inputs:
rewrite_dependencies:
description:
If "true" or empty CICD will rewrite BL_Python dependencies,
during the build only, to use the local filesystem checkout rather than PyPI.
If "false" CICD will not rewrite the BL_Python dependencies.
type: choice
options:
- true
- false
default: "true"
required: true
use_dependency_cache:
description:
If "true", the workflow will use the dependency cache if the associated
cache key has a hit. If "false," any existing dependency cache will be ignored.
The workflow will save a new dependency cache with the same cache key regardless
of `use_dependency_cache` if there was not a cache hit.
type: choice
options:
- true
- false
default: "true"
required: false
jobs:
Checkout:
name: Checkout and Setup
runs-on: ubuntu-latest
outputs:
cache-key-dependencies: ${{ steps.generate-cache-keys.outputs.cache_key }}
cache-key-run: ${{ steps.generate-cache-keys.outputs.cache_key_run }}
python-version: ${{ steps.install-python.outputs.python-version }}
env:
PYTHON_VERSION: "3.10"
steps:
- uses: actions/checkout@v4
- name: Generate cache keys
id: generate-cache-keys
# Use separate cache keys for dependencies and for run workspace.
# The dependencies can be restored between runs,
# while the workspace is restored between jobs in the same run.
run: |
rand=$(( (1000+$RANDOM)%$RANDOM*$RANDOM ))
run_attempt=$([ -n "${{ github.run_attempt }}" ] && printf ${{ github.run_attempt }} || printf $rand)
cache_key_dependencies='${{ runner.os }}-dependencies-${{ hashFiles('pyproject.toml', 'src/*/pyproject.toml') }}'
cache_key_run='${{ runner.os }}-run-${{ hashFiles('pyproject.toml', 'src/*/pyproject.toml') }}-run-id_${{ github.run_id }}-number_${{ github.run_number }}-attempt_'"$run_attempt"
echo "cache_key_dependencies=$cache_key_dependencies" >> $GITHUB_OUTPUT
echo "cache_key_run=$cache_key_run" >> $GITHUB_OUTPUT
- name: Set up Python ${{ steps.generate-cache-keys.outputs.python-version }}
#if: ${{ success() && (inputs.use_dependency_cache == 'false' || !steps.restore-dependency-cache.outputs.cache-hit) }}
uses: actions/setup-python@v5
id: install-python
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
#if: ${{ success() && (inputs.use_dependency_cache == 'false' || !steps.restore-dependency-cache.outputs.cache-hit) }}
run: |
echo Setting up dependencies
VENV=.github-venv \
REWRITE_DEPENDENCIES=${{ inputs.rewrite_dependencies }} \
make cicd
echo 'prefix=${{ github.workspace }}/node_modules' >> ~/.npmrc
. .github-venv/bin/activate && \
npm install -g "pyright@`pyright --version | awk '/^pyright [0-9]/{print $2}'`"
- name: Save run cache
uses: actions/cache/save@v4
id: save-run-cache
with:
path: ${{ github.workspace }}
key: ${{ steps.generate-cache-keys.outputs.cache_key_run }}
Pyright:
name: Static type checking
runs-on: ubuntu-latest
needs:
- Checkout
env:
PYTHON_VERSION: ${{ needs.Checkout.outputs.python-version }}
if: ${{( success() && !cancelled() ) }}
steps:
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
id: install-python
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/cache/restore@v4
name: Restore run cache
id: restore-run-cache
with:
key: ${{ needs.Checkout.outputs.cache-key-run }}
path: ${{ github.workspace }}
fail-on-cache-miss: true
- name: Test with pyright
run: |
echo Running pyright
VENV=.github-venv \
PYRIGHT_MODE=npm \
DEFAULT_TARGET=cicd \
make test-pyright
Pytest:
name: Automated testing
runs-on: ubuntu-latest
needs:
- Checkout
env:
PYTHON_VERSION: ${{ needs.Checkout.outputs.python-version }}
if: ${{( success() && !cancelled() ) }}
steps:
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
id: install-python
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/cache/restore@v4
name: Restore run cache
id: restore-run-cache
with:
key: ${{ needs.Checkout.outputs.cache-key-run }}
path: ${{ github.workspace }}
fail-on-cache-miss: true
- name: Test with pytest and generate reports
run: |
echo Running pytest
VENV=.github-venv \
PYTEST_FLAGS="-k 'not acceptance'" \
DEFAULT_TARGET=cicd \
make test-pytest
- name: Output pytest report
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: pytest-and-coverage-report
path: |
reports/pytest/
retention-days: 1
if-no-files-found: error
Bandit:
name: SAST scanning
runs-on: ubuntu-latest
needs:
- Checkout
env:
PYTHON_VERSION: ${{ needs.Checkout.outputs.python-version }}
if: ${{( success() && !cancelled() ) }}
# FIXME Ignore errors while testing Bandit
continue-on-error: true
steps:
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
id: install-python
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/cache/restore@v4
name: Restore run cache
id: restore-run-cache
with:
key: ${{ needs.Checkout.outputs.cache-key-run }}
path: ${{ github.workspace }}
fail-on-cache-miss: true
- name: Run bandit scan and generate reports
run: |
echo Running bandit
VENV=.github-venv \
DEFAULT_TARGET=cicd \
make test-bandit || BANDIT_EXIT_CODE=$?
echo "Bandit exit code: $BANDIT_EXIT_CODE"
if [ $BANDIT_EXIT_CODE -ne 0 ]; then
echo "::warning title=Bandit::Bandit exit code: $BANDIT_EXIT_CODE"
fi
- name: Output bandit report artifact
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: bandit-sast-report
path: |
reports/bandit.sarif
retention-days: 1
if-no-files-found: error
- name: Upload bandit report to CodeQL
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: reports/bandit.sarif
Style:
name: Style and formatting
runs-on: ubuntu-latest
needs:
- Checkout
env:
PYTHON_VERSION: ${{ needs.Checkout.outputs.python-version }}
if: ${{( success() && !cancelled() ) }}
steps:
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
id: install-python
with:
python-version: ${{ env.PYTHON_VERSION }}
- uses: actions/cache/restore@v4
name: Restore run cache
id: restore-run-cache
with:
key: ${{ needs.Checkout.outputs.cache-key-run }}
path: ${{ github.workspace }}
fail-on-cache-miss: true
- name: Check code style
run: |
VENV=.github-venv \
DEFAULT_TARGET=cicd \
make test-ruff
- name: Check import order
run: |
VENV=.github-venv \
DEFAULT_TARGET=cicd \
make test-isort
Final-status-check:
name: Check workflow success
runs-on: ubuntu-latest
needs:
- Checkout
- Pyright
- Pytest
- Bandit
- Style
# this job should run regardless of success, failure, or skips,
# but not if the workflow is cancelled. `always()` ignores cancelled,
# and so we check for the cancelled state.
if: ${{( always() && !cancelled() ) }}
steps:
- name: Failure
# once the "needs" jobs have completed, if any of them
# failed, then the entire workflow is considered failed.
if: |
contains(toJSON(needs), '"result": "failure"')
env:
RANDOM_EOF: |
EOF`echo $RANDOM`
run: |
echo One or more required jobs did not complete successfully;
jq -r 'keys[] as $job | "Job ID: \($job)'"\n"' \(.[$job] | .result)"' <<${{ env.RANDOM_EOF }}
${{ toJSON(needs) }}
${{ env.RANDOM_EOF }}
exit 1;
# TODO consider uploading the repository so errors can be more easily resolved.
#- name: Upload repository build
# uses: actions/upload-artifact@v3
# with:
# name: repository-build
# path: ${{ github.workspace }}
# retention-days: 1
# if-no-files-found: error
- name: Success
run: echo Workflow succeeded; exit 0;