Merge pull request #385 from dotunv/feat/319-monte-carlo-simulation #376
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: Node CI | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - develop | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| jobs: | |
| ci: | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:14.4 | |
| env: | |
| POSTGRES_USER: user | |
| POSTGRES_PASSWORD: pass | |
| POSTGRES_DB: db | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd="pg_isready -U user" | |
| --health-interval=10s | |
| --health-timeout=5s | |
| --health-retries=5 | |
| env: | |
| NODE_ENV: test | |
| DATABASE_URL: postgresql://user:pass@localhost:5432/db | |
| WALLET_ENCRYPTION_KEY: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 | |
| # Non-secret stubs required by env.ts at module-load time | |
| STELLAR_NETWORK: testnet | |
| STELLAR_RPC_URL: https://rpc.example.com | |
| STELLAR_AGENT_SECRET_KEY: SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| VAULT_CONTRACT_ID: CDUMMYVAULTCONTRACTID | |
| USDC_TOKEN_ADDRESS: CDUMMYUSDC | |
| ANTHROPIC_API_KEY: sk-ant-smoke-key | |
| JWT_SEED: smoke-jwt-seed-0123456789abcdef0123456789 | |
| JWT_SESSION_TTL_HOURS: '24' | |
| JWT_NONCE_TTL_MS: '300000' | |
| JWT_CLEANUP_INTERVAL_MS: '86400000' | |
| TWILIO_AUTH_TOKEN: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| INTERNAL_SERVICE_TOKEN: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| TRUSTED_IPS: 127.0.0.1 | |
| CORS_ORIGINS: '*' | |
| HTTP_CLIENT_TIMEOUT_MS: '1000' | |
| steps: | |
| # Checkout repository | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| # Setup Node.js | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| # Install dependencies | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Prisma generate | |
| run: npx prisma generate | |
| - name: Prisma migrate deploy | |
| run: npx prisma migrate deploy | |
| # Run TypeScript lint check | |
| - name: Run lint | |
| run: npm run lint | |
| - name: Run format check | |
| run: npm run format:check | |
| # Build the project | |
| - name: Build project | |
| run: npm run build | |
| # Run tests | |
| - name: Run tests | |
| run: npm test | |
| # ── Security scanning ───────────────────────────────────────────────────── | |
| # Issue #114: npm audit for known vulnerabilities | |
| # Fails on high or critical CVEs to prevent merging vulnerable dependencies | |
| # | |
| # Policy: Builds fail on HIGH or CRITICAL vulnerabilities | |
| # - HIGH/CRITICAL: Must be fixed before merge (blocking) | |
| # - MODERATE: Review required, fix in follow-up PR (non-blocking via Dependabot) | |
| # - LOW: Tracked via Dependabot, fix during regular maintenance | |
| # | |
| # Dependabot automatically creates PRs for vulnerable dependencies | |
| security-scan: | |
| name: Security audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run npm audit (HIGH and CRITICAL only) | |
| run: npm audit --audit-level=high | |
| continue-on-error: false | |
| - name: License check (block GPL/AGPL/LGPL) | |
| # Prevents copyleft licences from entering the dependency tree. | |
| # Allowed: MIT, ISC, Apache-2.0, BSD-2-Clause, BSD-3-Clause, 0BSD, BlueOak-1.0.0, CC0-1.0, | |
| # Python-2.0 (PSF — permissive, transitive argparse@2), | |
| # CC-BY-4.0 (attribution-only, transitive caniuse-lite data), | |
| # Unlicense (public-domain dedication, transitive fast-sha256) | |
| run: | | |
| npx license-checker --onlyAllow \ | |
| 'MIT;ISC;Apache-2.0;BSD-2-Clause;BSD-3-Clause;0BSD;BlueOak-1.0.0;CC0-1.0;Python-2.0;CC-BY-4.0;Unlicense' \ | |
| --excludePrivatePackages | |
| # ── Issue #100: migration smoke gate ────────────────────────────────────── | |
| # Isolated job that re-applies migrations against a clean DB and runs the | |
| # smoke test. Fails the pipeline if either step errors, preventing a broken | |
| # migration from reaching production. | |
| migration-smoke: | |
| name: Migration smoke test | |
| runs-on: ubuntu-latest | |
| needs: ci | |
| services: | |
| postgres: | |
| image: postgres:14.4 | |
| env: | |
| POSTGRES_USER: smoke_user | |
| POSTGRES_PASSWORD: smoke_pass | |
| POSTGRES_DB: smoke_db | |
| ports: | |
| - 5433:5432 | |
| options: >- | |
| --health-cmd="pg_isready -U smoke_user" | |
| --health-interval=10s | |
| --health-timeout=5s | |
| --health-retries=5 | |
| env: | |
| NODE_ENV: test | |
| # scripts/smoke-health.sh probes port 3001 — the server must listen there. | |
| PORT: '3001' | |
| DATABASE_URL: postgresql://smoke_user:smoke_pass@localhost:5433/smoke_db | |
| # Non-secret stubs required by env.ts at module-load time | |
| STELLAR_NETWORK: testnet | |
| # This job boots the real server; startEventListener() calls | |
| # getLatestLedger() with no client timeout before app.listen(), so the | |
| # endpoint must resolve or startup stalls. | |
| STELLAR_RPC_URL: https://soroban-testnet.stellar.org | |
| STELLAR_AGENT_SECRET_KEY: SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| VAULT_CONTRACT_ID: CDUMMYVAULTCONTRACTID | |
| USDC_TOKEN_ADDRESS: CDUMMYUSDC | |
| ANTHROPIC_API_KEY: sk-ant-smoke-key | |
| JWT_SEED: smoke-jwt-seed-0123456789abcdef0123456789 | |
| JWT_SESSION_TTL_HOURS: '24' | |
| JWT_NONCE_TTL_MS: '300000' | |
| JWT_CLEANUP_INTERVAL_MS: '86400000' | |
| WALLET_ENCRYPTION_KEY: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 | |
| # Required by env.ts and asserted again by initServices(). | |
| TWILIO_AUTH_TOKEN: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Prisma generate | |
| run: npx prisma generate | |
| # Informational only: on a fresh smoke database every migration is | |
| # pending, and `migrate status` exits non-zero whenever that is true. | |
| # The real gate is "Confirm no pending migrations after deploy" below. | |
| - name: Check migration status (pre-deploy, informational) | |
| run: npx prisma migrate status || true | |
| - name: Apply migrations to smoke DB | |
| run: npx prisma migrate deploy | |
| - name: Confirm no pending migrations after deploy | |
| run: | | |
| npx prisma migrate status | tee /tmp/migrate-status.txt | |
| if grep -q "following migration have not yet been applied" /tmp/migrate-status.txt; then | |
| echo "::error::Pending migrations remain after migrate deploy" | |
| exit 1 | |
| fi | |
| # smoke-health.sh runs the compiled server, so the artifact must exist. | |
| - name: Build project | |
| run: npm run build | |
| - name: Run smoke test | |
| run: npm run smoke | |
| # ── Issue #195: Docs and deployment manifests validation ─────────────────── | |
| # Validates Markdown links, code fences, and Kubernetes manifests to catch | |
| # broken examples, stale commands, and malformed manifests before they reach operators. | |
| docs-validation: | |
| name: Docs and manifests validation | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| - name: Install markdown-link-check | |
| run: npm install -g markdown-link-check | |
| - name: Check Markdown links in docs | |
| run: | | |
| # Check internal links only (ignore external links that may be flaky) | |
| for file in docs/*.md; do | |
| if [ -f "$file" ]; then | |
| echo "Checking links in $file" | |
| markdown-link-check --config .markdown-link-check.json "$file" || { | |
| echo "::error file=$file::Markdown link check failed" | |
| exit 1 | |
| } | |
| fi | |
| done | |
| - name: Validate code fences in docs | |
| run: | | |
| # Check for unclosed code fences in markdown files | |
| for file in docs/*.md; do | |
| if [ -f "$file" ]; then | |
| # Count opening and closing code fences | |
| open=$(grep -c '^```' "$file" || true) | |
| if [ $((open % 2)) -ne 0 ]; then | |
| echo "::error file=$file::Unclosed code fence detected" | |
| exit 1 | |
| fi | |
| fi | |
| done | |
| # Kubernetes manifest validation lives in k8s-validate.yml (kubeconform, | |
| # fully offline) and triggers whenever deploy/k8s changes. kubectl's | |
| # client dry-run needs a live cluster for API discovery, so it can never | |
| # pass on a bare runner and was removed from this job. | |
| - name: Check referenced file paths in docs | |
| run: | | |
| # Extract file paths from markdown code blocks and check if they exist | |
| for file in docs/*.md; do | |
| if [ -f "$file" ]; then | |
| # Extract paths like `src/config/env.ts` or `docs/OBSERVABILITY.md` | |
| grep -oE '\`[a-zA-Z0-9_./-]+\.[a-z]+\`' "$file" | sed 's/`//g' | while read path; do | |
| if [ -n "$path" ] && [ ! -f "$path" ] && [ ! -d "$path" ]; then | |
| # Only warn for likely file references (contains .) | |
| if [[ "$path" == *.* ]]; then | |
| echo "::warning file=$file::Referenced file may not exist: $path" | |
| fi | |
| fi | |
| done | |
| fi | |
| done | |
| - name: Check command examples in docs | |
| run: | | |
| # Check for common command patterns that might be outdated | |
| for file in docs/*.md; do | |
| if [ -f "$file" ]; then | |
| # Check for npm scripts that don't exist in package.json | |
| grep -oE 'npm (run|test) [a-z0-9_-]+' "$file" | while read cmd; do | |
| script=$(echo "$cmd" | awk '{print $NF}') | |
| if ! grep -q "\"$script\"" package.json; then | |
| echo "::warning file=$file::npm script may not exist: $script" | |
| fi | |
| done | |
| fi | |
| done |