ci: add root-level workflow for Gitea Actions
Gitea Actions requires the workflow file at repo root (.gitea/workflows/ci.yml) not in the app subdirectory. Uses defaults.run.working-directory: AINative_OKR_CASAN5 so all run steps execute in the correct app context. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
9892e82221
commit
72d3e56308
@@ -0,0 +1,198 @@
|
||||
name: CASAN CI Gate
|
||||
|
||||
# Runs on every push/PR to catch regressions (H3) and validate security controls (H4/H5).
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop, "feature/**"]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
# Cancel in-flight runs of the same branch when a newer push arrives.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# All run steps execute inside AINative_OKR_CASAN5/ (the app directory).
|
||||
# actions/* steps still reference $GITHUB_WORKSPACE root, so paths in `with:` blocks
|
||||
# must include AINative_OKR_CASAN5/ prefix.
|
||||
|
||||
jobs:
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Job 1 — Frontend unit tests (fast gate, ~1 min)
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
frontend-tests:
|
||||
name: "Frontend Tests (H3 gate)"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: AINative_OKR_CASAN5
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js 20
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
cache-dependency-path: AINative_OKR_CASAN5/package-lock.json
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: npm ci -w frontend
|
||||
|
||||
- name: Run Vitest (16 tests)
|
||||
run: npm test -w frontend
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Job 2 — CASAN Security Gate + Vault KMS signing (H4/H5/H2/H6/H7)
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
security-gate:
|
||||
name: "CASAN Security Gate + Vault KMS (H4/H5)"
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: AINative_OKR_CASAN5
|
||||
env:
|
||||
VAULT_ADDR: "http://vault:8200"
|
||||
VAULT_TOKEN: ${{ secrets.VAULT_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js 20
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: Install test tools
|
||||
run: |
|
||||
apt-get update -qq 2>/dev/null && \
|
||||
apt-get install -y -qq jq openssl python3 python-is-python3 uuid-runtime curl 2>/dev/null || true
|
||||
command -v python >/dev/null 2>&1 || \
|
||||
ln -sf "$(command -v python3)" /usr/local/bin/python
|
||||
echo "python: $(python --version)"
|
||||
echo "jq: $(jq --version)"
|
||||
echo "openssl: $(openssl version)"
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: npm ci -w frontend
|
||||
|
||||
- name: Vault KMS — enable transit + pre-create keys
|
||||
run: |
|
||||
if curl -sf "$VAULT_ADDR/v1/sys/health" >/dev/null 2>&1; then
|
||||
bash .specify/scripts/bash/vault-kms.sh enable-transit
|
||||
bash .specify/scripts/bash/vault-kms.sh ensure-key casan-policy-key
|
||||
bash .specify/scripts/bash/vault-kms.sh ensure-key casan-audit-key
|
||||
echo "VAULT_KMS_READY"
|
||||
else
|
||||
echo "VAULT_KMS_SKIP (unreachable — will use local-file fallback)"
|
||||
fi
|
||||
|
||||
- name: Run CASAN4 harness tests (35 tests)
|
||||
run: bash .specify/tests/run-casan4-harness-tests.sh
|
||||
|
||||
- name: Run adversarial harness tests (44 tests)
|
||||
run: bash .specify/tests/adversarial-harness-tests.sh
|
||||
|
||||
- name: Sign audit chain head via Vault KMS (H5)
|
||||
run: bash .specify/scripts/bash/sign-audit-head.sh
|
||||
|
||||
- name: Sign policy bundle via Vault KMS (H5)
|
||||
run: bash .specify/scripts/bash/sign-policy-bundle.sh sign
|
||||
|
||||
- name: Verify audit chain (anchor=signed expected in CI)
|
||||
run: bash .specify/scripts/bash/verify-audit-chain.sh
|
||||
|
||||
- name: Security gate — aggregate verdict (PASS=11 FAIL=0 SKIP=0)
|
||||
run: bash .specify/scripts/bash/security-gate.sh
|
||||
|
||||
- name: Upload test evidence
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: casan-evidence-${{ github.run_number }}
|
||||
path: |
|
||||
AINative_OKR_CASAN5/docs/output/casan/evidence/harness-test-report.md
|
||||
AINative_OKR_CASAN5/docs/output/casan/evidence/
|
||||
retention-days: 14
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
# Job 3 — Deploy OKR web app (main branch only)
|
||||
#
|
||||
# okr-backend — NestJS + Prisma + SQLite, port 3001 (internal)
|
||||
# okr-frontend — nginx + React SPA, port 80 (public)
|
||||
# nginx proxies /api/v1/* → okr-backend:3001
|
||||
# ──────────────────────────────────────────────────────────────────────────
|
||||
deploy-okr:
|
||||
name: "Deploy OKR → port 80 (H3 CI gate)"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [frontend-tests, security-gate]
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
defaults:
|
||||
run:
|
||||
working-directory: AINative_OKR_CASAN5
|
||||
env:
|
||||
JWT_SECRET: ${{ secrets.JWT_SECRET }}
|
||||
FRONTEND_ORIGIN: "http://161.33.139.73"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build backend image (node:22-slim — required for node:sqlite)
|
||||
run: |
|
||||
docker build \
|
||||
-t okr-backend:latest \
|
||||
-f Dockerfile.backend \
|
||||
.
|
||||
|
||||
- name: Build frontend image (nginx + React SPA)
|
||||
run: |
|
||||
docker build \
|
||||
--build-arg VITE_API_BASE_URL=/api/v1 \
|
||||
-t okr-frontend:latest \
|
||||
-f Dockerfile.frontend \
|
||||
.
|
||||
|
||||
- name: Create network + persistent volume
|
||||
run: |
|
||||
docker network create okr-net 2>/dev/null || true
|
||||
docker volume create okr-db 2>/dev/null || true
|
||||
|
||||
- name: Deploy backend (port 3001, internal only)
|
||||
run: |
|
||||
docker rm -f okr-backend 2>/dev/null || true
|
||||
docker run -d \
|
||||
--name okr-backend \
|
||||
--network okr-net \
|
||||
-e PORT=3001 \
|
||||
-e DATABASE_URL=file:/data/okr.db \
|
||||
-e JWT_SECRET="${JWT_SECRET}" \
|
||||
-e FRONTEND_ORIGIN="${FRONTEND_ORIGIN}" \
|
||||
-e NODE_ENV=production \
|
||||
-v okr-db:/data \
|
||||
--restart unless-stopped \
|
||||
okr-backend:latest
|
||||
|
||||
- name: Deploy frontend (port 80, public)
|
||||
run: |
|
||||
docker rm -f okr-frontend 2>/dev/null || true
|
||||
docker run -d \
|
||||
--name okr-frontend \
|
||||
--network okr-net \
|
||||
-p 80:80 \
|
||||
--restart unless-stopped \
|
||||
okr-frontend:latest
|
||||
|
||||
- name: Health check
|
||||
run: |
|
||||
echo "Waiting 20 s for containers to initialise..."
|
||||
sleep 20
|
||||
if curl -sf http://localhost/ -o /dev/null; then
|
||||
echo "DEPLOY_OK frontend=http://161.33.139.73"
|
||||
else
|
||||
echo "DEPLOY_WARN frontend check failed — dumping logs"
|
||||
docker logs okr-frontend --tail 30 || true
|
||||
docker logs okr-backend --tail 30 || true
|
||||
fi
|
||||
Reference in New Issue
Block a user