Skip to content

feat: stream large submission exports directly from db without memory… #384

feat: stream large submission exports directly from db without memory…

feat: stream large submission exports directly from db without memory… #384

Workflow file for this run

name: Backend CI
# Triggered on push/PR for main/master when BackEnd files or this workflow change.
# Jobs:
# 1. build-and-lint – TypeScript compilation, linting, formatting
# 2. openapi-check – headless OpenAPI spec generation + artifact upload
# 3. backend-ci-gate – aggregate gate: fails if either upstream job fails
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [main, master]
paths:
- "BackEnd/**"
- ".github/workflows/backend-ci.yml"
pull_request:
paths:
- "BackEnd/**"
- ".github/workflows/backend-ci.yml"
jobs:
# ---------------------------------------------------------------------------
# Job 0: Toolchain Preflight Check [BE-136]
# ---------------------------------------------------------------------------
toolchain-check:
name: Toolchain Preflight Check
runs-on: ubuntu-latest
defaults:
run:
working-directory: BackEnd
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
# The preflight script (check:toolchain) verifies Rust/Cargo versions because
# some backend workflows interact with the Soroban contract workspace.
# Match contract-ci.yml's toolchain choice so requirements stay in sync.
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-backend-npm-${{ hashFiles('BackEnd/package-lock.json') }}
restore-keys: |
${{ runner.os }}-backend-npm-
- name: Install dependencies
run: npm ci
- name: Validate toolchain versions
run: npm run check:toolchain
# ---------------------------------------------------------------------------
# Job 1: Build, Lint & Format
# ---------------------------------------------------------------------------
build-and-lint:
name: Build, Lint & Format
runs-on: ubuntu-latest
defaults:
run:
working-directory: BackEnd
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-backend-npm-${{ hashFiles('BackEnd/package-lock.json') }}
restore-keys: |
${{ runner.os }}-backend-npm-
- name: Install dependencies
run: npm ci
- name: TypeScript compilation
run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: backend-dist
path: BackEnd/dist/
retention-days: 7
- name: Lint
run: npm run lint
- name: Format check
run: npx prettier --check "src/**/*.ts" "test/**/*.ts"
# ---------------------------------------------------------------------------
# Job 2: OpenAPI Spec Generation & Artifact Upload [BE-116]
# ---------------------------------------------------------------------------
openapi-check:
name: OpenAPI Generation Check
runs-on: ubuntu-latest
defaults:
run:
working-directory: BackEnd
# Minimal stubs so the NestJS app module can bootstrap without real services.
# The generate-openapi.ts script also sets these itself, but declaring them
# here makes the CI environment explicit and auditable.
env:
NODE_ENV: openapi-gen
DB_HOST: localhost
DB_PORT: "5432"
DB_USERNAME: postgres
DB_PASSWORD: password
DB_DATABASE: stellar_earn
DATABASE_URL: postgresql://postgres:password@localhost:5432/stellar_earn
REDIS_HOST: localhost
REDIS_PORT: "6379"
REDIS_URL: redis://localhost:6379
JWT_SECRET: openapi-gen-secret
JWT_PRIVATE_KEY: dummy
JWT_PUBLIC_KEY: dummy
STELLAR_NETWORK: testnet
HORIZON_URL: https://horizon-testnet.stellar.org
SOROBAN_RPC_URL: https://soroban-testnet.stellar.org
SOROBAN_SECRET_KEY: SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
SENDGRID_API_KEY: SG.dummy
EMAIL_FROM_ADDRESS: noreply@stellarearn.com
EMAIL_FROM_NAME: StellarEarn
APP_URL: http://localhost:3000
STELLAR_MOCK_CURRENT_LEDGER: "60000000"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-backend-npm-${{ hashFiles('BackEnd/package-lock.json') }}
restore-keys: |
${{ runner.os }}-backend-npm-
- name: Install dependencies
run: npm ci
- name: Generate OpenAPI specification
run: npm run openapi:generate
- name: Validate generated spec
# Confirm the file exists, is valid JSON, has an "openapi" field,
# and exposes at least one path.
run: |
SPEC_FILE="dist/openapi/openapi.json"
if [ ! -f "$SPEC_FILE" ]; then
echo "❌ OpenAPI spec file not found at $SPEC_FILE"
exit 1
fi
# Validate JSON syntax
if ! python3 -c "import json,sys; json.load(open('$SPEC_FILE'))" 2>/dev/null; then
echo "❌ $SPEC_FILE is not valid JSON"
exit 1
fi
OPENAPI_VERSION=$(python3 -c "import json; d=json.load(open('$SPEC_FILE')); print(d.get('openapi',''))")
PATH_COUNT=$(python3 -c "import json; d=json.load(open('$SPEC_FILE')); print(len(d.get('paths',{})))")
echo "openapi field : $OPENAPI_VERSION"
echo "path count : $PATH_COUNT"
if [ -z "$OPENAPI_VERSION" ]; then
echo "❌ 'openapi' field is missing or empty in the spec"
exit 1
fi
if [ "$PATH_COUNT" -eq 0 ]; then
echo "❌ Generated spec contains no paths – check that controllers are wired up correctly"
exit 1
fi
echo "✅ OpenAPI spec is valid (openapi=$OPENAPI_VERSION, paths=$PATH_COUNT)"
- name: Upload OpenAPI spec artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: openapi-spec-${{ github.sha }}
path: BackEnd/dist/openapi/openapi.json
retention-days: 30
# ---------------------------------------------------------------------------
# Job 3: Aggregate CI Gate
# ---------------------------------------------------------------------------
backend-ci-gate:
name: Backend CI Gate
runs-on: ubuntu-latest
needs: [toolchain-check, build-and-lint, openapi-check]
if: always()
steps:
- name: Validate all checks passed
run: |
toolchain_status="${{ needs.toolchain-check.result }}"
build_status="${{ needs.build-and-lint.result }}"
openapi_status="${{ needs.openapi-check.result }}"
echo "Toolchain check : $toolchain_status"
echo "Build & lint : $build_status"
echo "OpenAPI check : $openapi_status"
if [ "$toolchain_status" != "success" ]; then
echo "❌ Toolchain preflight check failed"
exit 1
fi
if [ "$build_status" != "success" ]; then
echo "❌ Build or lint failed"
exit 1
fi
if [ "$openapi_status" != "success" ]; then
echo "❌ OpenAPI generation check failed"
exit 1
fi
echo "✅ All backend CI checks passed"
exit 0
- name: Workflow Summary
if: always()
run: |
echo "## Backend CI Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Toolchain Check | ${{ needs.toolchain-check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Build & Lint | ${{ needs.build-and-lint.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| OpenAPI Check | ${{ needs.openapi-check.result }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.toolchain-check.result }}" != "success" ] || \
[ "${{ needs.build-and-lint.result }}" != "success" ] || \
[ "${{ needs.openapi-check.result }}" != "success" ]; then
echo "### ❌ Failed Checks" >> $GITHUB_STEP_SUMMARY
echo "One or more backend CI checks failed. Please review the job logs above." >> $GITHUB_STEP_SUMMARY
else
echo "### ✅ All Checks Passed" >> $GITHUB_STEP_SUMMARY
echo "All backend CI checks completed successfully." >> $GITHUB_STEP_SUMMARY
fi