Skip to content

style: remove trailing whitespace in conftest.py #388

style: remove trailing whitespace in conftest.py

style: remove trailing whitespace in conftest.py #388

Workflow file for this run

name: CI
on:
push:
branches: ["**"]
pull_request:
branches: [main]
# OpenSSF Scorecard: Token-Permissions — restrict default token to read-only
permissions: read-all
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.11"
IMAGE_NAME: bessai-edge-gateway
jobs:
# ─────────────────────────────────────────────────────────────────
# Job 1: Lint
# ─────────────────────────────────────────────────────────────────
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Install ruff
run: pip install ruff
- name: Run ruff check
run: ruff check src/ tests/
- name: Run ruff format check
run: ruff format --check src/ tests/
# ─────────────────────────────────────────────────────────────────
# Job 2: Type check
# ─────────────────────────────────────────────────────────────────
typecheck:
name: Type check (mypy)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Run mypy
run: mypy src/ --ignore-missing-imports --no-error-summary
# ─────────────────────────────────────────────────────────────────
# Job 3: Unit tests
# ─────────────────────────────────────────────────────────────────
test:
name: Tests (pytest)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Run tests with coverage
run: |
pytest tests/ -v --tb=short \
--cov=src \
--cov-report=xml \
--cov-report=term-missing \
--cov-fail-under=80
- name: Upload coverage to Codecov
if: always()
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
with:
file: coverage.xml
flags: unittests
fail_ci_if_error: false
# ─────────────────────────────────────────────────────────────────
# Job 3.5: Load tests (Locust)
# ─────────────────────────────────────────────────────────────────
load-test:
name: Load Tests (Locust)
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Start BESSAI Edge Server in background
env:
PYTHONPATH: ${{ github.workspace }}
run: |
python demo_server.py &
sleep 5
- name: Run Locust load test
run: |
locust -f tests/load/locustfile.py --headless -u 100 -r 20 --run-time 30s --host http://localhost:8000 --csv=load_results
- name: Validate SLAs
run: |
# Basic validation: Check if failure rate is greater than 0%
if grep -q failure load_results_stats.csv; then
echo "Warning/Errors found in run, verifying metrics"
awk -F',' 'NR>1 {if($3=="failure") exit 1}' load_results_stats.csv || { echo "SLA verification failed!"; exit 1; }
fi
- name: Upload load test results
if: always()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: load-test-results
path: load_results*.csv
# ─────────────────────────────────────────────────────────────────
# Job 4: Interoperability Contract Tests (BESSAI-SPEC-001 Category A)
# No hardware needed — uses SimulatorDriver
# ─────────────────────────────────────────────────────────────────
interop:
name: Interop Contract Tests (BESSAI-SPEC-001)
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Run BESSAI-SPEC-001 Category A contract tests
env:
SITE_ID: CI-INTEROP-TEST
INVERTER_IP: "127.0.0.1"
run: |
pytest tests/interop/test_driver_contract.py \
-v --tb=short \
-m "not slow" \
--junit-xml=interop-results.xml
- name: Upload interop test results
if: always()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: interop-test-results
path: interop-results.xml
# ─────────────────────────────────────────────────────────────────
# Job 5: Security — SAST + Dependency audit
# ─────────────────────────────────────────────────────────────────
security:
name: Security (bandit + pip-audit)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: pip
- name: Install security tools
run: pip install bandit pip-audit
- name: Run bandit SAST
run: |
bandit -r src/ \
--severity-level medium \
--confidence-level medium \
--format json \
--output bandit-report.json || true
bandit -r src/ \
--severity-level medium \
--confidence-level medium \
--exit-zero
- name: Upload bandit report
if: always()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: bandit-report
path: bandit-report.json
- name: Run pip-audit (dependency vulnerability scan)
run: |
pip-audit \
--requirement requirements.txt \
--format json \
--output pip-audit-report.json || true
pip-audit \
--requirement requirements.txt \
--ignore-vuln PYSEC-2022-42969
- name: Upload pip-audit report
if: always()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: pip-audit-report
path: pip-audit-report.json
# ─────────────────────────────────────────────────────────────────
# Job 6: Terraform validate (no GCP credentials needed)
# ─────────────────────────────────────────────────────────────────
terraform-validate:
name: Terraform validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: "~> 1.7"
- name: Terraform init (local backend only)
run: terraform -chdir=infrastructure/terraform init -backend=false
- name: Terraform validate
run: terraform -chdir=infrastructure/terraform validate
- name: Terraform fmt check
run: terraform -chdir=infrastructure/terraform fmt -check -recursive
# ─────────────────────────────────────────────────────────────────
# Job 7: Helm lint & template validation
# ─────────────────────────────────────────────────────────────────
helm-lint:
name: Helm lint & template
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Helm
uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
with:
version: "3.14.0"
- name: Helm lint
run: helm lint infrastructure/helm/bessai-edge/
- name: Helm template dry-run
run: |
helm template bessai-test infrastructure/helm/bessai-edge/ \
--set config.inverterIp=10.0.1.50 \
--set config.siteId=CI-TEST \
| head -60
# ─────────────────────────────────────────────────────────────────
# Job 8: Docker build (validates the image builds correctly)
# ─────────────────────────────────────────────────────────────────
docker-build:
name: Docker build
runs-on: ubuntu-latest
needs: [lint, test]
outputs:
image-digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
- name: Build image (no push)
id: build
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
context: .
file: infrastructure/docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: false
tags: ${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ─────────────────────────────────────────────────────────────────
# Job 9: Trivy container security scan
# ─────────────────────────────────────────────────────────────────
trivy:
name: Container scan (Trivy)
runs-on: ubuntu-latest
needs: [docker-build]
permissions:
security-events: write # Required to upload SARIF to GitHub Security tab
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
- name: Build image for scanning
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
context: .
file: infrastructure/docker/Dockerfile
platforms: linux/amd64
push: false
load: true
tags: ${{ env.IMAGE_NAME }}:scan
cache-from: type=gha
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # 0.28.0
with:
image-ref: "${{ env.IMAGE_NAME }}:scan"
format: "sarif"
output: "trivy-results.sarif"
severity: "CRITICAL,HIGH"
exit-code: "0" # Don't fail CI — upload results for visibility
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@1bb15d06a6fbb5d9d9ffd228746bf8ee208caec8 # codeql-bundle-v2.20.5
if: always()
with:
sarif_file: "trivy-results.sarif"
category: "trivy-container"
# ─────────────────────────────────────────────────────────────────
# Job 10: Push to GCP Artifact Registry (only on main)
# ─────────────────────────────────────────────────────────────────
docker-push:
name: Push to Artifact Registry
runs-on: ubuntu-latest
needs: [docker-build, security, trivy]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
permissions:
contents: read
id-token: write # Required for Workload Identity Federation
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # v2.1.7
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- name: Configure Docker for Artifact Registry
run: gcloud auth configure-docker ${{ secrets.GCP_REGION }}-docker.pkg.dev --quiet
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
- name: Build and push
uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0
with:
context: .
file: infrastructure/docker/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/bessai/${{ env.IMAGE_NAME }}:latest
${{ secrets.GCP_REGION }}-docker.pkg.dev/${{ secrets.GCP_PROJECT_ID }}/bessai/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# ─────────────────────────────────────────────────────────────────
# Job 11: BEP-0200 DRL + Global Market Adapters tests
# Installs drl-standalone extras (torch CPU, gymnasium, onnxruntime)
# ─────────────────────────────────────────────────────────────────
drl-market-tests:
name: "BEP-0200 DRL + Market Adapters (Python 3.12)"
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: "3.12"
cache: pip
- name: Install core dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Install DRL extras (torch CPU, gymnasium, onnxruntime, onnxscript)
run: |
pip install \
torch --index-url https://download.pytorch.org/whl/cpu \
"gymnasium>=1.0" \
"onnx>=1.15" \
"onnxscript>=0.1" \
"onnxruntime>=1.17"
- name: Restore CMg data cache
id: cmg-cache
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c6158d # v4.2.0
with:
path: ../bessai-web/data/cmg_data.json
key: cmg-data-v1
- name: Check out bessai-web for CMg data (if not cached)
if: steps.cmg-cache.outputs.cache-hit != 'true'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: bess-solutions/bessai-web
path: ../bessai-web
sparse-checkout: data/cmg_data.json
- name: Run DRL environment + dataset tests
env:
PYTHONPATH: ${{ github.workspace }}
run: |
pytest tests/test_drl_agent.py \
-v --tb=short \
-m "not slow" \
--junit-xml=drl-test-results.xml
- name: Run global market adapter tests (6 markets, mock HTTP)
env:
PYTHONPATH: ${{ github.workspace }}
run: |
pytest tests/test_market_adapter_global.py \
-v --tb=short \
--junit-xml=market-adapter-results.xml
- name: Upload DRL test results
if: always()
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: drl-market-test-results
path: |
drl-test-results.xml
market-adapter-results.xml