feat(ka): consolidate canonical runtime #861
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: 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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Ruff | |
| run: pip install ruff==0.15.0 | |
| - name: Run Ruff | |
| run: ruff check . --select E9,F63,F7 | |
| # Backend Tests | |
| backend-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --require-hashes -r requirements.lock | |
| - 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. | |
| # CVE-2026-12243 (nltk path traversal, no patch available for 3.9.x yet) | |
| # PYSEC-2026-311 / GHSA-f4j7-r4q5-qw2c has no patched ChromaDB release; | |
| # constrained deployment is documented and production remains blocked. | |
| run: | | |
| pip-audit -r requirements.lock --desc \ | |
| --ignore-vuln CVE-2025-3000 \ | |
| --ignore-vuln CVE-2026-12243 \ | |
| --ignore-vuln PYSEC-2026-311 | |
| - name: Run smoke bootstrap check | |
| run: | | |
| python scripts/test_smoke.py | |
| - name: Set up Node.js for deterministic startup precheck | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| 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/ | |
| 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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install Dependencies | |
| run: | | |
| cd frontend | |
| npm ci | |
| env: | |
| 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: | | |
| 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 | |
| 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 | |
| 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) | |
| 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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Build backend executable | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $PSNativeCommandUseErrorActionPreference = $true | |
| python -m pip install --require-hashes -r requirements.lock | |
| python scripts/build_backend.py | |
| - name: Cache electron-builder binaries | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # 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: | |
| 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| 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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Set up Node.js | |
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install governance dependencies | |
| run: | | |
| pip install ruff==0.15.0 | |
| 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: Verify immutable workflow action pins | |
| run: | | |
| python scripts/verify_workflow_pins.py --json-report reports/workflow_pin_report_ci.json | |
| - name: Verify retired release paths stay unreachable | |
| run: | | |
| python scripts/verify_legacy_retirement.py --report reports/legacy_retirement_report_ci.json | |
| - name: Upload governance reports | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| if: always() | |
| with: | |
| name: governance-reports | |
| path: | | |
| reports/environment_parity_report_ci.json | |
| reports/lockfile_governance_report_ci.json | |
| reports/workflow_pin_report_ci.json | |
| reports/legacy_retirement_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@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 | |
| - name: Build Backend Image | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 | |
| with: | |
| context: . | |
| file: backend/Dockerfile | |
| push: false | |
| tags: ukg-backend:latest | |
| - name: Build Frontend Image | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 | |
| with: | |
| context: . | |
| file: frontend/Dockerfile | |
| push: false | |
| tags: ukg-frontend:latest |