diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index cd83df2..6437a1d 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -78,6 +78,129 @@ jobs: - name: Run tests run: pnpm test:run + # ── Smoke tests (full stack: postgres + mcpd + mcplocal) ── + + smoke: + runs-on: ubuntu-latest + needs: [lint, typecheck, test] + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: mcpctl + POSTGRES_PASSWORD: mcpctl + POSTGRES_DB: mcpctl + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + DATABASE_URL: postgresql://mcpctl:mcpctl@postgres:5432/mcpctl + MCPD_PORT: "3100" + MCPD_HOST: "0.0.0.0" + MCPLOCAL_HTTP_PORT: "3200" + MCPLOCAL_MCPD_URL: http://localhost:3100 + DOCKER_API_VERSION: "1.43" + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Generate Prisma client + run: pnpm --filter @mcpctl/db exec prisma generate + + - name: Build all packages + run: pnpm build + + - name: Push database schema + run: pnpm --filter @mcpctl/db exec prisma db push --accept-data-loss + + - name: Seed templates + run: node src/mcpd/dist/seed-runner.js + + - name: Start mcpd + run: node src/mcpd/dist/main.js & + + - name: Wait for mcpd + run: | + for i in $(seq 1 30); do + if curl -sf http://localhost:3100/health > /dev/null 2>&1; then + echo "mcpd is ready" + exit 0 + fi + echo "Waiting for mcpd... ($i/30)" + sleep 1 + done + echo "::error::mcpd failed to start within 30s" + exit 1 + + - name: Bootstrap auth and write credentials + run: | + RESULT=$(curl -sf -X POST http://localhost:3100/api/v1/auth/bootstrap \ + -H 'Content-Type: application/json' \ + -d '{"email":"ci@test.local","password":"ci-smoke-test"}') + echo "Auth bootstrapped" + mkdir -p ~/.mcpctl + echo "$RESULT" | node -e " + const res = JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')); + const creds = {token: res.token, mcpdUrl: 'http://localhost:3100', user: 'ci@test.local'}; + require('fs').writeFileSync(require('os').homedir()+'/.mcpctl/credentials', JSON.stringify(creds)); + console.log('Credentials written'); + " + + - name: Create mcpctl CLI wrapper + run: | + printf '#!/bin/sh\nexec node "%s/src/cli/dist/index.js" "$@"\n' "$GITHUB_WORKSPACE" > /usr/local/bin/mcpctl + chmod +x /usr/local/bin/mcpctl + + - name: Start mcplocal + run: node src/mcplocal/dist/main.js & + + - name: Wait for mcplocal + run: | + for i in $(seq 1 30); do + if curl -sf http://localhost:3200/health > /dev/null 2>&1; then + echo "mcplocal is ready" + exit 0 + fi + echo "Waiting for mcplocal... ($i/30)" + sleep 1 + done + echo "::error::mcplocal failed to start within 30s" + exit 1 + + - name: Apply smoke test fixtures + run: mcpctl apply -f src/mcplocal/tests/smoke/fixtures/smoke-data.yaml + + - name: Wait for server instance + run: | + echo "Waiting for smoke-aws-docs instance..." + for i in $(seq 1 60); do + STATUS=$(mcpctl get instances -o json 2>/dev/null | \ + node -e "try{const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8'));const i=Array.isArray(d)?d.find(x=>x.serverName&&x.serverName.includes('aws')):null;console.log(i?.status??'WAITING')}catch{console.log('WAITING')}" 2>/dev/null || echo "WAITING") + echo " Instance status: $STATUS ($i/60)" + if [ "$STATUS" = "RUNNING" ]; then + echo "Instance is running!" + break + fi + if [ "$i" = "60" ]; then + echo "::warning::Instance did not reach RUNNING — container management may not be available in CI" + echo "API-layer smoke tests will still run" + fi + sleep 5 + done + + - name: Run smoke tests + run: pnpm --filter mcplocal run test:smoke + # ── Build & package RPM ─────────────────────────────────── build: