name: CI on: push: branches: - "**" pull_request: branches: - main env: CARGO_TERM_COLOR: always RUSTFLAGS: "-D warnings" # sccache caches compilation artifacts within a job so that compiling # both --features server and --features web shares common crate work. RUSTC_WRAPPER: /usr/local/bin/sccache SCCACHE_DIR: /tmp/sccache # Cancel in-progress runs for the same branch/PR concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: # --------------------------------------------------------------------------- # Stage 1: Code quality checks (run in parallel) # --------------------------------------------------------------------------- fmt: name: Format runs-on: docker container: image: rust:1.89-bookworm steps: - name: Checkout run: | git init git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" git fetch --depth=1 origin "${GITHUB_SHA}" git checkout FETCH_HEAD - run: rustup component add rustfmt # Format check does not compile, so sccache is not needed here. - run: cargo fmt --check env: RUSTC_WRAPPER: "" clippy: name: Clippy runs-on: docker container: image: rust:1.89-bookworm steps: - name: Checkout run: | git init git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" git fetch --depth=1 origin "${GITHUB_SHA}" git checkout FETCH_HEAD - name: Install sccache run: | curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \ | tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache chmod +x /usr/local/bin/sccache - run: rustup component add clippy # Lint both feature sets independently. # sccache deduplicates shared crates between the two compilations. - name: Clippy (server) run: cargo clippy --features server --no-default-features -- -D warnings - name: Clippy (web) run: cargo clippy --features web --no-default-features -- -D warnings - name: Show sccache stats run: sccache --show-stats if: always() audit: name: Security Audit runs-on: docker if: github.ref == 'refs/heads/main' container: image: rust:1.89-bookworm steps: - name: Checkout run: | git init git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" git fetch --depth=1 origin "${GITHUB_SHA}" git checkout FETCH_HEAD - run: cargo install cargo-audit env: RUSTC_WRAPPER: "" - run: cargo audit env: RUSTC_WRAPPER: "" # --------------------------------------------------------------------------- # Stage 2: Tests (only after all quality checks pass) # --------------------------------------------------------------------------- test: name: Tests runs-on: docker needs: [fmt, clippy, audit] container: image: rust:1.89-bookworm steps: - name: Checkout run: | git init git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" git fetch --depth=1 origin "${GITHUB_SHA}" git checkout FETCH_HEAD - name: Install sccache run: | curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \ | tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache chmod +x /usr/local/bin/sccache - name: Run tests (server) run: cargo test --features server --no-default-features - name: Run tests (web) run: cargo test --features web --no-default-features - name: Show sccache stats run: sccache --show-stats if: always() # --------------------------------------------------------------------------- # Stage 4: E2E tests (only on main, after deploy) # --------------------------------------------------------------------------- e2e: name: E2E Tests runs-on: docker needs: [deploy] if: github.ref == 'refs/heads/main' container: image: rust:1.89-bookworm # MongoDB and SearXNG can start immediately (no repo files needed). # Keycloak requires realm-export.json from the repo, so it is started # manually after checkout via docker CLI. services: mongo: image: mongo:latest env: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: example ports: - 27017:27017 searxng: image: searxng/searxng:latest env: SEARXNG_BASE_URL: http://localhost:8888 ports: - 8888:8080 env: KEYCLOAK_URL: http://localhost:8080 KEYCLOAK_REALM: certifai KEYCLOAK_CLIENT_ID: certifai-dashboard MONGODB_URI: mongodb://root:example@mongo:27017 MONGODB_DATABASE: certifai SEARXNG_URL: http://searxng:8080 LANGGRAPH_URL: "" LANGFLOW_URL: "" LANGFUSE_URL: "" steps: - name: Checkout run: | git init git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" git fetch --depth=1 origin "${GITHUB_SHA}" git checkout FETCH_HEAD - name: Install system dependencies run: | apt-get update -qq apt-get install -y -qq --no-install-recommends \ unzip curl docker.io \ libglib2.0-0 libnss3 libnspr4 libdbus-1-3 libatk1.0-0 \ libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 \ libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 \ libcairo2 libasound2 libatspi2.0-0 libxshmfence1 - name: Start Keycloak run: | docker run -d --name ci-keycloak --network host \ -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ -e KC_DB=dev-mem \ -e KC_HEALTH_ENABLED=true \ -v "$PWD/keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json:ro" \ -v "$PWD/keycloak/themes/certifai:/opt/keycloak/themes/certifai:ro" \ quay.io/keycloak/keycloak:26.0 start-dev --import-realm echo "Waiting for Keycloak..." for i in $(seq 1 60); do if curl -sf http://localhost:8080/realms/certifai > /dev/null 2>&1; then echo "Keycloak is ready" break fi if [ "$i" -eq 60 ]; then echo "Keycloak failed to start within 60s" docker logs ci-keycloak exit 1 fi sleep 2 done - name: Install sccache run: | curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \ | tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache chmod +x /usr/local/bin/sccache - name: Install dioxus-cli run: cargo install dioxus-cli --locked - name: Install bun run: | curl -fsSL https://bun.sh/install | bash echo "$HOME/.bun/bin" >> "$GITHUB_PATH" - name: Install Playwright run: | export PATH="$HOME/.bun/bin:$PATH" bun install bunx playwright install chromium - name: Build app run: dx build --release - name: Start app and run E2E tests run: | export PATH="$HOME/.bun/bin:$PATH" # Start the app in the background dx serve --release --port 8000 & APP_PID=$! # Wait for the app to be ready echo "Waiting for app to start..." for i in $(seq 1 60); do if curl -sf http://localhost:8000 > /dev/null 2>&1; then echo "App is ready" break fi if [ "$i" -eq 60 ]; then echo "App failed to start within 60s" exit 1 fi sleep 1 done BASE_URL=http://localhost:8000 bunx playwright test --reporter=list kill "$APP_PID" 2>/dev/null || true - name: Upload test report if: always() uses: actions/upload-artifact@v4 with: name: playwright-report path: playwright-report/ retention-days: 7 - name: Cleanup Keycloak if: always() run: docker rm -f ci-keycloak 2>/dev/null || true - name: Show sccache stats run: sccache --show-stats if: always() # --------------------------------------------------------------------------- # Stage 3: Deploy (only after tests pass, only on main) # --------------------------------------------------------------------------- deploy: name: Deploy runs-on: docker needs: [test] if: github.ref == 'refs/heads/main' container: image: docker:27-cli steps: - name: Checkout run: | apk add --no-cache git curl openssl git init git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" git fetch --depth=1 origin "${GITHUB_SHA}" git checkout FETCH_HEAD - name: Build and push image run: | IMAGE=registry.meghsakha.com/certifai-dashboard echo "${{ secrets.REGISTRY_PASSWORD }}" | \ docker login registry.meghsakha.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin docker build -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" . docker push "$IMAGE:latest" docker push "$IMAGE:${GITHUB_SHA}" - name: Trigger orca redeploy run: | PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/certifai"},"head_commit":{"id":"%s","message":"CI deploy"}}' "${GITHUB_SHA}") SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}') echo "Calling orca webhook for sharang/certifai@${GITHUB_SHA}" RESP=$(curl -fsS -w "\nHTTP %{http_code}" -X POST "http://46.225.100.82:6880/api/v1/webhooks/github" \ -H "Content-Type: application/json" \ -H "X-Hub-Signature-256: sha256=$SIG" \ -d "$PAYLOAD") echo "$RESP"