Merge pull request #52 from DavidOsipov/renovate/node-22.x #70
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: 🛡️ Bastion Quality Gates | |
on: | |
push: | |
branches: [ "main" ] | |
pull_request: | |
branches: [ "main" ] | |
schedule: | |
- cron: '0 0 * * 0' # Weekly on Sunday | |
workflow_dispatch: | |
permissions: | |
pull-requests: read | |
security-events: write | |
contents: read | |
jobs: | |
Python_Tests: | |
uses: ./.github/workflows/python-tests.yml | |
# This workflow now depends on the successful completion of the python-tests workflow | |
Analyze: | |
needs: Python_Tests | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
tool: [bandit, ruff, mypy, flake8, pylint, codeql, snyk, pyright, cyclonedx] | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 | |
- name: Set up Python | |
if: matrix.tool != 'codeql' # Snyk needs Python setup | |
uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 | |
with: | |
python-version: '3.13' | |
- name: Cache pip dependencies | |
if: matrix.tool != 'codeql' | |
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 | |
with: | |
path: | | |
~/.cache/pip | |
~/.cache/pypoetry | |
/usr/local/lib/python3.13/site-packages | |
key: ${{ runner.os }}-poetry-${{ matrix.tool }}-${{ hashFiles('**/poetry.lock', 'pyproject.toml') }} | |
restore-keys: | | |
${{ runner.os }}-poetry-${{ matrix.tool }}- | |
${{ runner.os }}-poetry- | |
- name: Install Poetry | |
if: matrix.tool != 'codeql' | |
run: | | |
pip install poetry | |
poetry --version | |
- name: Install project dependencies | |
if: matrix.tool != 'codeql' | |
run: | | |
poetry install --with dev --no-interaction | |
- name: Install analysis tools | |
if: matrix.tool != 'codeql' && matrix.tool != 'snyk' && matrix.tool != 'pyright' && matrix.tool != 'cyclonedx' | |
run: | | |
poetry run pip install bandit ruff mypy flake8 pylint | |
poetry run bandit --version | |
poetry run ruff --version | |
poetry run mypy --version | |
poetry run flake8 --version | |
poetry run pylint --version | |
- name: Install Node.js for Pyright | |
if: matrix.tool == 'pyright' | |
uses: actions/setup-node@v4 | |
with: | |
node-version: '22' | |
- name: Install Pyright | |
if: matrix.tool == 'pyright' | |
run: | | |
npm install -g pyright | |
pyright --version | |
- name: Install CycloneDX | |
if: matrix.tool == 'cyclonedx' | |
run: | | |
poetry run pip install cyclonedx-bom | |
poetry run cyclonedx-py --version || echo "CycloneDX installed" | |
- name: Setup Snyk CLI | |
if: matrix.tool == 'snyk' | |
uses: snyk/actions/setup@cdb760004ba9ea4d525f2e043745dfe85bb9077e | |
with: | |
snyk-version: latest | |
- name: Run Snyk Security Scan | |
if: matrix.tool == 'snyk' | |
env: | |
SNYK_TOKEN: ${{ secrets.SNYK_SECRET_TOKEN }} | |
DEBUG: snyk* | |
run: | | |
pip install poetry | |
pip install poetry-plugin-export | |
poetry export --format requirements.txt --output requirements.txt --with dev | |
pip install black blake3 click colorama flake8 gmpy2 isort mccabe msgpack-types mypy-extensions pathspec psutil pycodestyle pyflakes setuptools types-requests types-setuptools typing-extensions | |
snyk test --file=requirements.txt --sarif-file-output=snyk_report.sarif --skip-unresolved || snyk test --file=requirements.txt --sarif-file-output=snyk_report.sarif --skip-unresolved --all-projects | |
- name: Run Bandit | |
if: matrix.tool == 'bandit' | |
run: poetry run bandit -r . -o bandit_report.json --format json --exclude tests,.git || true | |
- name: Run Ruff | |
if: matrix.tool == 'ruff' | |
run: poetry run ruff check . --output-format json --output-file ruff_report.json --exclude tests,.git || true | |
- name: Run Mypy | |
if: matrix.tool == 'mypy' | |
run: poetry run mypy . 2>&1 | tee mypy_report.txt || true | |
- name: Run Flake8 | |
if: matrix.tool == 'flake8' | |
run: poetry run flake8 . --output-file flake8_report.txt --format=pylint || true | |
- name: Run Pylint | |
if: matrix.tool == 'pylint' | |
run: poetry run pylint --recursive=y . --output-format=json > pylint_report.json || true | |
- name: Run Pyright | |
if: matrix.tool == 'pyright' | |
run: pyright --outputjson > pyright_report.json || true | |
- name: Generate Software Bill of Materials (SBOM) | |
if: matrix.tool == 'cyclonedx' | |
run: | | |
poetry run cyclonedx-py -o cyclonedx_report.json -j . || true | |
- name: Initialize CodeQL | |
if: matrix.tool == 'codeql' | |
uses: github/codeql-action/init@main | |
with: | |
languages: python | |
- name: Perform CodeQL Analysis | |
if: matrix.tool == 'codeql' | |
uses: github/codeql-action/analyze@main | |
with: | |
output: codeql_report.sarif | |
- name: Upload report artifact | |
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 | |
with: | |
name: ${{ matrix.tool }}-report | |
path: | | |
${{ matrix.tool }}_report.* | |
codeql_report.sarif | |
SonarQube: | |
needs: Analyze | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2 | |
with: | |
fetch-depth: 0 | |
- name: Download analysis reports | |
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e | |
with: | |
path: reports | |
- name: Process reports and prepare for SonarQube | |
run: | | |
mkdir -p processed_reports | |
# Function to safely move reports | |
safe_move_report() { | |
local source_dir="$1" | |
local report_file="$2" | |
local target_file="$3" | |
if [ -f "${source_dir}/${report_file}" ]; then | |
echo "✅ Found ${report_file}" | |
cp "${source_dir}/${report_file}" "${target_file}" | |
return 0 | |
else | |
echo "⚠️ Warning: ${report_file} not found in ${source_dir}" | |
# For JSON reports, create an empty valid JSON file | |
if [[ "${report_file}" == *".json" ]]; then | |
echo "Creating empty JSON file for ${target_file}" | |
echo "[]" > "${target_file}" | |
# For SARIF reports, create a minimal valid SARIF file | |
elif [[ "${report_file}" == *".sarif" ]]; then | |
echo "Creating minimal SARIF file for ${target_file}" | |
echo '{"version":"2.1.0","runs":[{"tool":{"driver":{"name":"Missing Report","rules":[]}},"results":[]}]}' > "${target_file}" | |
# For text reports, create an empty file | |
else | |
echo "Creating empty file for ${target_file}" | |
touch "${target_file}" | |
fi | |
return 1 | |
fi | |
} | |
# Initialize list of available report paths for SonarQube | |
sonar_args="" | |
# Process each report type | |
safe_move_report "reports/bandit-report" "bandit_report.json" "processed_reports/bandit_report.json" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.python.bandit.reportPaths=processed_reports/bandit_report.json" | |
fi | |
safe_move_report "reports/ruff-report" "ruff_report.json" "processed_reports/ruff_report.json" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.python.ruff.reportPaths=processed_reports/ruff_report.json" | |
fi | |
safe_move_report "reports/mypy-report" "mypy_report.txt" "processed_reports/mypy_report.txt" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.python.mypy.reportPaths=processed_reports/mypy_report.txt" | |
fi | |
safe_move_report "reports/flake8-report" "flake8_report.txt" "processed_reports/flake8_report.txt" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.python.flake8.reportPaths=processed_reports/flake8_report.txt" | |
fi | |
safe_move_report "reports/pylint-report" "pylint_report.json" "processed_reports/pylint_report.json" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.python.pylint.reportPaths=processed_reports/pylint_report.json" | |
fi | |
# Process SARIF reports and combine into a single list if both exist | |
sarif_reports="" | |
safe_move_report "reports/codeql-report" "codeql_report.sarif" "processed_reports/codeql_report.sarif" | |
if [ $? -eq 0 ]; then | |
sarif_reports="processed_reports/codeql_report.sarif" | |
fi | |
safe_move_report "reports/snyk-report" "snyk_report.sarif" "processed_reports/snyk_report.sarif" | |
if [ $? -eq 0 ]; then | |
if [ -n "$sarif_reports" ]; then | |
sarif_reports="${sarif_reports},processed_reports/snyk_report.sarif" | |
else | |
sarif_reports="processed_reports/snyk_report.sarif" | |
fi | |
fi | |
if [ -n "$sarif_reports" ]; then | |
sonar_args="${sonar_args} -Dsonar.sarifReportPaths=${sarif_reports}" | |
fi | |
safe_move_report "reports/pyright-report" "pyright_report.json" "processed_reports/pyright_report.json" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.externalIssuesReportPaths=processed_reports/pyright_report.json" | |
fi | |
safe_move_report "reports/cyclonedx-report" "cyclonedx_report.json" "processed_reports/cyclonedx_report.json" | |
if [ $? -eq 0 ]; then | |
sonar_args="${sonar_args} -Dsonar.dependencyCheck.jsonReportPath=processed_reports/cyclonedx_report.json" | |
fi | |
# Store SonarQube args in environment variable for next step | |
echo "SONAR_EXTRA_ARGS=${sonar_args}" >> $GITHUB_ENV | |
# Print summary | |
echo "✨ Report processing complete. SonarQube will use the following reports:" | |
echo "${sonar_args}" | |
- name: Analyze with SonarQube | |
uses: SonarSource/sonarqube-scan-action@aa494459d7c39c106cc77b166de8b4250a32bb97 | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} | |
with: | |
args: > | |
-Dsonar.projectKey=DavidOsipov_PostQuantum-Feldman-VSS | |
-Dsonar.organization=davidosipov | |
-Dsonar.python.version=3.10-3.13 | |
-Dsonar.languages=python | |
${{ env.SONAR_EXTRA_ARGS }} |