Skip to content

docs: HANDOFF — Phase C correction + desktop-auth keep-path #709

docs: HANDOFF — Phase C correction + desktop-auth keep-path

docs: HANDOFF — Phase C correction + desktop-auth keep-path #709

Workflow file for this run

name: CI/CD Pipeline
on:
push:
branches: [ "main", "dev", "develop" ]
pull_request:
branches: [ "main", "dev", "develop" ]
jobs:
# Coding Standards
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install Ruff
run: pip install ruff
- name: Run Ruff
run: ruff check . --select E9,F63,F7
# Backend Tests
backend-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pip-audit
pip install neo4j bleach user-agents web3 fastapi reportlab asgiref python-json-logger simple-salesforce jira
- name: Security Audit
# CVE-2025-3000 (torch.jit.script memory corruption, local-host only, no
# patched release) is suppressed: torch is transitive and the app never
# calls torch.jit.script. Keep in sync with security.yml. See TODO.md.
run: |
pip-audit -r requirements.txt --desc --ignore-vuln CVE-2025-3000
- name: Run smoke bootstrap check
run: |
python scripts/test_smoke.py
- name: Set up Node.js for deterministic startup precheck
uses: actions/setup-node@v6
with:
node-version: '24'
- name: Install frontend dependencies for startup precheck
run: |
cd frontend
npm ci
env:
CHROMEDRIVER_SKIP_DOWNLOAD: true
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
- name: Run deterministic startup precheck gate
run: |
python scripts/runtime_precheck.py --strict --skip-ports --allow-env-from-process --json-report reports/runtime_precheck_report_ci.json
env:
FLASK_ENV: "testing"
SESSION_SECRET: "ci-session-secret"
DATABASE_URL: "sqlite:///:memory:"
OPENAI_API_KEY: "mock-key-for-ci"
- name: Validate docs references
run: |
python scripts/verify_docs_references.py
- name: Validate SQLite/Postgres schema parity
run: |
python scripts/validate_schema_parity.py --report reports/schema_parity_report_ci.json
- name: Run Tests
run: |
python -m pytest tests/ --no-cov
env:
PYTHONPATH: "."
DATABASE_URL: "sqlite:///:memory:"
OPENAI_API_KEY: "mock-key-for-ci"
FLASK_ENV: "testing"
SESSION_SECRET: "ci-session-secret"
SECRET_KEY: "ci-test-secret-key"
JWT_SECRET_KEY: "ci-test-jwt-secret"
- name: Run API contract tests
run: |
python -m pytest -q --no-cov tests/contract/test_api_contract.py
env:
PYTHONPATH: "."
DATABASE_URL: "sqlite:///:memory:"
OPENAI_API_KEY: "mock-key-for-ci"
FLASK_ENV: "testing"
SESSION_SECRET: "ci-session-secret"
- name: Run local mode parity tests
run: |
python -m pytest -q --no-cov tests/parity/test_local_mode_parity.py
env:
PYTHONPATH: "."
DATABASE_URL: "sqlite:///:memory:"
FLASK_ENV: "testing"
SESSION_SECRET: "ci-session-secret"
- name: Run security regression smoke suite
run: |
python -m pytest -q --no-cov tests/security/test_security_headers.py tests/security/test_request_limits.py
env:
PYTHONPATH: "."
DATABASE_URL: "sqlite:///:memory:"
OPENAI_API_KEY: "mock-key-for-ci"
FLASK_ENV: "testing"
SESSION_SECRET: "ci-session-secret"
# Frontend Build
frontend-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install Dependencies
run: |
cd frontend
npm ci
env:
# Prevent chromedriver/electron postinstall from downloading binaries during npm ci.
# These downloads time out or are slow in CI; the binaries are either not needed (electron)
# or set up separately below (chromedriver).
CHROMEDRIVER_SKIP_DOWNLOAD: true
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
- name: Generate Design Tokens
run: |
cd frontend
npm run tokens:build
- name: Lint Frontend
run: |
cd frontend
npm run lint
- name: Typecheck Frontend
run: |
cd frontend
npm run typecheck
- name: Run Frontend Unit Tests
run: |
cd frontend
npm run test
- name: Build Next.js
run: |
cd frontend
npm run build
env:
NEXT_TELEMETRY_DISABLED: 1
- name: Install Playwright system dependencies
timeout-minutes: 10
run: |
cd frontend
npx playwright install-deps chromium
- name: Set up ChromeDriver for a11y sweep
run: |
# GitHub Actions ubuntu-latest has ChromeDriver pre-installed (matches pre-installed Chrome).
# Link it into the location the chromedriver npm package expects so that axe-cli can find it.
SYSTEM_CD=$(which chromedriver 2>/dev/null || echo "")
if [ -z "$SYSTEM_CD" ]; then
echo "System ChromeDriver not found; installing via npm postinstall"
cd frontend && node node_modules/chromedriver/install.js
else
echo "Using system ChromeDriver: $SYSTEM_CD ($(chromedriver --version))"
mkdir -p frontend/node_modules/chromedriver/lib/chromedriver
ln -sf "$SYSTEM_CD" frontend/node_modules/chromedriver/lib/chromedriver/chromedriver
fi
# Detect Chrome binary and export for the a11y sweep. CHROME_TEST_PATH causes
# @axe-core/cli to set the binary path explicitly and add --no-sandbox, which is
# required on Ubuntu 24.04+ CI runners. The run-a11y-ci.mjs script also adds
# --disable-dev-shm-usage and --disable-setuid-sandbox when CI=true.
CHROME_BIN=$(which google-chrome google-chrome-stable chromium-browser chromium 2>/dev/null | head -1)
if [ -n "$CHROME_BIN" ]; then
echo "Found Chrome at $CHROME_BIN"
echo "CHROME_TEST_PATH=$CHROME_BIN" >> $GITHUB_ENV
else
echo "::warning::No Chrome binary found; a11y sweep may fail"
fi
- name: Run Frontend Accessibility Sweep
# Pre-existing WCAG violations in the app should not block the build for
# PRs that don't introduce new a11y issues. Soft-fail so the job continues
# to E2E and visual regression steps.
continue-on-error: true
run: |
cd frontend
npm run test:a11y:ci
- name: Run Frontend Route E2E Smoke
run: |
cd frontend
npm run test:e2e -- tests/e2e/route-sidebar-smoke.spec.ts
- name: Run Frontend Visual Regression Smoke (create missing baselines)
# Playwright 1.58 with --update-snapshots=missing fails the run even when it
# successfully writes new snapshot files. Allow this step to fail so the job
# continues to the validation pass below.
continue-on-error: true
run: |
cd frontend
npm run test:e2e:visual -- --update-snapshots=missing
- name: Run Frontend Visual Regression Smoke (validate)
run: |
cd frontend
npm run test:e2e:visual -- --update-snapshots=missing
windows-packaging-smoke:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Build backend executable
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller neo4j bleach user-agents web3 fastapi reportlab asgiref python-json-logger simple-salesforce jira eventlet tiktoken langchain-openai langchain-community
pyinstaller backend.spec
- name: Cache electron-builder binaries
uses: actions/cache@v4
with:
path: ${{ env.LOCALAPPDATA }}\electron-builder\Cache
key: electron-builder-${{ runner.os }}-${{ hashFiles('frontend/package-lock.json') }}
restore-keys: |
electron-builder-${{ runner.os }}-
- name: Install frontend dependencies
run: |
cd frontend
npm ci
env:
# Skip the electron npm package post-install binary download — this job
# only needs electron-builder for packaging, not a local Electron runtime.
# electron-builder manages its own binary download separately (cached above).
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
CHROMEDRIVER_SKIP_DOWNLOAD: true
- name: Verify NSIS governance policy
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/verify_nsis_governance.ps1 -RepoRoot (Get-Location).Path
- name: Build Windows installer
run: |
npm --prefix frontend run electron:dist
- name: Run packaging smoke (portable launch)
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/run_packaging_smoke.ps1 -RepoRoot (Get-Location).Path
- name: Upload packaging smoke report
uses: actions/upload-artifact@v5
if: always()
with:
name: packaging-smoke-report
path: |
reports/packaging_smoke_report.json
reports/nsis_governance_report.json
if-no-files-found: warn
governance:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install governance dependencies
run: |
python -m pip install --upgrade pip
pip install ruff
npm --prefix frontend ci
env:
CHROMEDRIVER_SKIP_DOWNLOAD: true
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
- name: Run pre-commit governance checks
run: |
python scripts/dev/run_precommit_checks.py
- name: Verify environment parity
run: |
python scripts/verify_environment_parity.py --strict --json-report reports/environment_parity_report_ci.json
- name: Verify lockfile governance
run: |
python scripts/verify_lockfiles.py --json-report reports/lockfile_governance_report_ci.json
- name: Upload governance reports
uses: actions/upload-artifact@v5
if: always()
with:
name: governance-reports
path: |
reports/environment_parity_report_ci.json
reports/lockfile_governance_report_ci.json
if-no-files-found: warn
# Docker Build (Verification)
docker-build:
needs: [backend-test, frontend-build, windows-packaging-smoke, governance]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Backend Image
uses: docker/build-push-action@v6
with:
context: .
file: backend/Dockerfile
push: false
tags: ukg-backend:latest
- name: Build Frontend Image
uses: docker/build-push-action@v6
with:
context: frontend
file: frontend/Dockerfile
push: false
tags: ukg-frontend:latest