From 5b5be2d8b4932174683d4f5a610dbc04617f39fa Mon Sep 17 00:00:00 2001 From: BitmapAsset Date: Thu, 3 Sep 2026 03:43:01 -0700 Subject: [PATCH] chore: add Dependabot config and drop the inert legacy CI workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What - Add .github/dependabot.yml (version 2) covering the two ecosystems this repo actually has: npm at "/" (the pnpm workspace root — its single pnpm-lock.yaml covers packages/* too) and github-actions at "/". Nothing for legacy/. - Delete legacy/ci/ci-cd.yml and update legacy/README.md accordingly. Why - Dependabot had no configuration at all, so version updates never ran and security updates arrived one PR per package. The groups below collapse that into one PR per ecosystem for minor+patch and one PR per ecosystem for security fixes; majors stay separate because they need a human. - Right after the legacy/taproot-assets-era removal merged, five "Dependabot Updates" runs failed trying to resolve deleted /legacy/... lockfiles. Scoping the config to the live tree stops that recurring. - legacy/ci/ci-cd.yml was inert (not under .github/workflows, so never executed) and built only deleted code, but it made legacy/ look like a live build surface. It is preserved byte-identical at tag legacy/taproot-assets-era-final (blob 4dc9c28). How verified - Enumerated every manifest on main: git ls-files matched exactly package.json, packages/sdk/package.json, pnpm-lock.yaml and .github/workflows/ci.yml. No Dockerfile, go.mod, Cargo.toml or Python manifests exist. Dependabot alerts API (state=all) returns none. - dependabot.yml validated against the SchemaStore Dependabot 2.0 JSON schema with ajv: PASS. - Confirmed the deleted workflow survives in the tag: blob sha of HEAD:legacy/ci/ci-cd.yml and legacy/taproot-assets-era-final:legacy/ci/ci-cd.yml are both 4dc9c28d30b0ff832bc687609954fabb98af8c02. - Ran the CI job locally on Node 22.23.1 / pnpm 10.28.2: pnpm install --frozen-lockfile, lint:boundaries (OK), typecheck (clean), test (170 passed, 3 skipped). --- .github/dependabot.yml | 60 +++++ legacy/README.md | 10 +- legacy/ci/ci-cd.yml | 490 ----------------------------------------- 3 files changed, 68 insertions(+), 492 deletions(-) create mode 100644 .github/dependabot.yml delete mode 100644 legacy/ci/ci-cd.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..337d978 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,60 @@ +# Dependabot configuration for RuneBolt. +# +# Scope: only the live tree. `legacy/` holds an archived, non-building snapshot of +# the Taproot-Assets-era code (full history preserved at tag +# legacy/taproot-assets-era-final) and is deliberately NOT tracked here — chasing +# lockfiles for deleted code produced failing "Dependabot Updates" runs. +# +# Grouping: minor+patch version updates arrive as ONE PR per ecosystem, majors +# stay separate (they need a human), and security fixes arrive as ONE PR per +# ecosystem instead of one PR per package. +version: 2 + +updates: + # pnpm workspace: the root pnpm-lock.yaml covers packages/* as well, so a + # single "/" entry is the whole JS dependency surface. + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/Los_Angeles" + open-pull-requests-limit: 5 + labels: + - "dependencies" + groups: + npm-minor-and-patch: + applies-to: version-updates + patterns: + - "*" + update-types: + - "minor" + - "patch" + npm-security: + applies-to: security-updates + patterns: + - "*" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "06:00" + timezone: "America/Los_Angeles" + open-pull-requests-limit: 5 + labels: + - "dependencies" + groups: + actions-minor-and-patch: + applies-to: version-updates + patterns: + - "*" + update-types: + - "minor" + - "patch" + actions-security: + applies-to: security-updates + patterns: + - "*" diff --git a/legacy/README.md b/legacy/README.md index 3350efa..7b2ddd4 100644 --- a/legacy/README.md +++ b/legacy/README.md @@ -34,8 +34,14 @@ RuneBolt hub, locked $DOG (`DOG•GO•TO•THE•MOON`, rune id `1:0`) on L1, t off-chain against a hub-operated ledger. Backend (Express + SQLite + WebSocket), Next.js frontend, `@runebolt/sdk`, Docker/Vault/Grafana infrastructure, and a large body of UX and security research. -`legacy/ci/ci-cd.yml` is the old GitHub Actions workflow, parked here so it no longer runs. -It is retained; the code it built now lives only under the tag above. +The old GitHub Actions workflow that built it (`legacy/ci/ci-cd.yml`) has now been removed +from `main` too. Parking it here already stopped it running, but it kept this directory +looking like a live build surface and it only ever built deleted code. It is preserved +unchanged under the tag above (blob `4dc9c28`): + +```sh +git show legacy/taproot-assets-era-final:legacy/ci/ci-cd.yml +``` ## Why it was superseded diff --git a/legacy/ci/ci-cd.yml b/legacy/ci/ci-cd.yml deleted file mode 100644 index 4dc9c28..0000000 --- a/legacy/ci/ci-cd.yml +++ /dev/null @@ -1,490 +0,0 @@ -# RuneBolt CI/CD Pipeline -# Automated testing, security scanning, and deployment -# Version: 1.0.0 - -name: CI/CD Pipeline - -on: - push: - branches: [main, develop, 'release/*'] - tags: ['v*'] - pull_request: - branches: [main, develop] - -env: - REGISTRY: ghcr.io - BACKEND_IMAGE_NAME: ${{ github.repository }}/backend - FRONTEND_IMAGE_NAME: ${{ github.repository }}/frontend - -jobs: - # ============================================ - # Job 1: Lint and Test - # ============================================ - lint-and-test: - name: Lint & Test - runs-on: ubuntu-latest - timeout-minutes: 15 - - strategy: - matrix: - component: [backend, frontend] - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - cache-dependency-path: ${{ matrix.component }}/package-lock.json - - - name: Install dependencies - working-directory: ./${{ matrix.component }} - run: npm ci - - - name: Run ESLint - working-directory: ./${{ matrix.component }} - run: npm run lint || echo "No lint script found" - continue-on-error: true - - - name: Run TypeScript check - working-directory: ./${{ matrix.component }} - run: npm run type-check || npx tsc --noEmit - continue-on-error: true - - - name: Run tests - working-directory: ./${{ matrix.component }} - run: npm test || echo "No tests found" - continue-on-error: true - - # ============================================ - # Job 2: Security Scan - # ============================================ - security-scan: - name: Security Scan - runs-on: ubuntu-latest - timeout-minutes: 20 - needs: lint-and-test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Scan Backend Dependencies - - name: Run Trivy vulnerability scanner (Backend) - uses: aquasecurity/trivy-action@master - with: - scan-type: 'fs' - scan-ref: './backend' - format: 'sarif' - output: 'trivy-backend-results.sarif' - severity: 'CRITICAL,HIGH' - exit-code: '0' - - # Scan Frontend Dependencies - - name: Run Trivy vulnerability scanner (Frontend) - uses: aquasecurity/trivy-action@master - with: - scan-type: 'fs' - scan-ref: './frontend' - format: 'sarif' - output: 'trivy-frontend-results.sarif' - severity: 'CRITICAL,HIGH' - exit-code: '0' - - # Scan with Snyk - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/node@master - continue-on-error: true - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --severity-threshold=high --all-projects - - # Upload Trivy results to GitHub Security tab - - name: Upload Trivy Backend results - uses: github/codeql-action/upload-sarif@v2 - if: always() - with: - sarif_file: 'trivy-backend-results.sarif' - category: 'trivy-backend' - - - name: Upload Trivy Frontend results - uses: github/codeql-action/upload-sarif@v2 - if: always() - with: - sarif_file: 'trivy-frontend-results.sarif' - category: 'trivy-frontend' - - # ============================================ - # Job 3: Build and Push Docker Images - # ============================================ - build-and-push: - name: Build & Push Images - runs-on: ubuntu-latest - timeout-minutes: 30 - needs: [lint-and-test, security-scan] - if: github.event_name != 'pull_request' - - permissions: - contents: read - packages: write - security-events: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - # Extract metadata for Backend - - name: Extract Backend metadata - id: meta-backend - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,prefix={{branch}}- - type=raw,value=latest,enable={{is_default_branch}} - - # Build and push Backend - - name: Build and push Backend image - uses: docker/build-push-action@v5 - with: - context: ./backend - file: ./Dockerfile - target: production - push: true - tags: ${{ steps.meta-backend.outputs.tags }} - labels: ${{ steps.meta-backend.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 - - # Scan Backend image - - name: Scan Backend Docker image - uses: aquasecurity/trivy-action@master - with: - image-ref: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:${{ github.sha }} - format: 'sarif' - output: 'trivy-image-backend.sarif' - severity: 'CRITICAL,HIGH' - exit-code: '0' - - # Extract metadata for Frontend - - name: Extract Frontend metadata - id: meta-frontend - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }} - tags: | - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha,prefix={{branch}}- - type=raw,value=latest,enable={{is_default_branch}} - - # Build and push Frontend (if Dockerfile exists) - - name: Build and push Frontend image - uses: docker/build-push-action@v5 - continue-on-error: true - with: - context: ./frontend - push: true - tags: ${{ steps.meta-frontend.outputs.tags }} - labels: ${{ steps.meta-frontend.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - platforms: linux/amd64,linux/arm64 - - # ============================================ - # Job 4: Deploy to Staging - # ============================================ - deploy-staging: - name: Deploy to Staging - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: build-and-push - if: github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') - environment: - name: staging - url: https://staging.runebolt.io - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup SSH - uses: webfactory/ssh-agent@v0.8.0 - with: - ssh-private-key: ${{ secrets.STAGING_SSH_KEY }} - - - name: Deploy to Staging - env: - STAGING_HOST: ${{ secrets.STAGING_HOST }} - STAGING_USER: ${{ secrets.STAGING_USER }} - VERSION: ${{ github.sha }} - run: | - ssh -o StrictHostKeyChecking=no $STAGING_USER@$STAGING_HOST << 'EOF' - set -e - cd /opt/runebolt - - # Pull latest changes - git fetch origin - git checkout ${{ github.sha }} - - # Pull latest images - docker pull ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:$VERSION - docker pull ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:$VERSION - - # Deploy with new version - VERSION=$VERSION docker compose -f docker-compose.prod.yml up -d --remove-orphans - - # Wait for health checks - sleep 30 - - # Verify deployment - curl -f http://localhost:3001/health || exit 1 - curl -f http://localhost:3000/health || exit 1 - - # Cleanup old images - docker image prune -af --filter "until=168h" - - echo "Staging deployment successful!" - EOF - - - name: Run Integration Tests - env: - STAGING_HOST: ${{ secrets.STAGING_HOST }} - run: | - echo "Running integration tests against staging..." - curl -f https://staging.runebolt.io/api/health || exit 1 - echo "Integration tests passed!" - - # ============================================ - # Job 5: Deploy to Production (Canary) - # ============================================ - deploy-production-canary: - name: Deploy to Production (Canary) - runs-on: ubuntu-latest - timeout-minutes: 20 - needs: build-and-push - if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') - environment: - name: production-canary - url: https://canary.runebolt.io - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup SSH - uses: webfactory/ssh-agent@v0.8.0 - with: - ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }} - - - name: Deploy Canary (5% Traffic) - env: - PRODUCTION_HOST: ${{ secrets.PRODUCTION_HOST }} - PRODUCTION_USER: ${{ secrets.PRODUCTION_USER }} - VERSION: ${{ github.sha }} - run: | - ssh -o StrictHostKeyChecking=no $PRODUCTION_USER@$PRODUCTION_HOST << 'EOF' - set -e - cd /opt/runebolt - - # Deploy canary version - VERSION=$VERSION docker compose -f docker-compose.prod.yml up -d backend-api-canary --remove-orphans - - # Configure HAProxy for 5% canary traffic - # This would update HAProxy config to route 5% traffic to canary - - sleep 30 - - # Verify canary health - curl -f http://localhost:3001/health || exit 1 - - echo "Canary deployment successful at 5% traffic" - EOF - - - name: Monitor Canary (10 minutes) - env: - PRODUCTION_HOST: ${{ secrets.PRODUCTION_HOST }} - DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }} - run: | - echo "Monitoring canary for 10 minutes..." - - # Wait 10 minutes and check metrics - sleep 600 - - # Check error rate via Prometheus/Grafana API - # If error rate > 0.1%, rollback - - echo "Canary monitoring complete" - continue-on-error: true - - # ============================================ - # Job 6: Deploy to Production (Full Rollout) - # ============================================ - deploy-production: - name: Deploy to Production - runs-on: ubuntu-latest - timeout-minutes: 30 - needs: deploy-production-canary - environment: - name: production - url: https://runebolt.io - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup SSH - uses: webfactory/ssh-agent@v0.8.0 - with: - ssh-private-key: ${{ secrets.PRODUCTION_SSH_KEY }} - - - name: Deploy to Production (25%) - env: - PRODUCTION_HOST: ${{ secrets.PRODUCTION_HOST }} - PRODUCTION_USER: ${{ secrets.PRODUCTION_USER }} - VERSION: ${{ github.sha }} - run: | - ssh -o StrictHostKeyChecking=no $PRODUCTION_USER@$PRODUCTION_HOST << 'EOF' - set -e - cd /opt/runebolt - - # Update to 25% traffic - # Update HAProxy config - - sleep 120 - curl -f http://localhost:3001/health || exit 1 - - echo "Deployment at 25%" - EOF - - - name: Deploy to Production (50%) - if: success() - env: - PRODUCTION_HOST: ${{ secrets.PRODUCTION_HOST }} - PRODUCTION_USER: ${{ secrets.PRODUCTION_USER }} - VERSION: ${{ github.sha }} - run: | - ssh -o StrictHostKeyChecking=no $PRODUCTION_USER@$PRODUCTION_HOST << 'EOF' - set -e - cd /opt/runebolt - - # Update to 50% traffic - sleep 120 - curl -f http://localhost:3001/health || exit 1 - - echo "Deployment at 50%" - EOF - - - name: Deploy to Production (100%) - if: success() - env: - PRODUCTION_HOST: ${{ secrets.PRODUCTION_HOST }} - PRODUCTION_USER: ${{ secrets.PRODUCTION_USER }} - VERSION: ${{ github.sha }} - run: | - ssh -o StrictHostKeyChecking=no $PRODUCTION_USER@$PRODUCTION_HOST << 'EOF' - set -e - cd /opt/runebolt - - # Full deployment - VERSION=$VERSION docker compose -f docker-compose.prod.yml up -d --remove-orphans - - sleep 60 - - # Full health check - curl -f http://localhost:3001/health || exit 1 - curl -f http://localhost:3000/health || exit 1 - - # Cleanup - docker image prune -af --filter "until=168h" - - echo "Production deployment successful!" - EOF - - - name: Notify Success - if: success() - uses: slackapi/slack-github-action@v1.24.0 - with: - payload: | - { - "text": "✅ RuneBolt production deployment successful!", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "✅ *RuneBolt Production Deployment Successful*\n\n*Version:* ${{ github.sha }}\n*Commit:* <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|View Commit>\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>" - } - } - ] - } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - - - name: Notify Failure - if: failure() - uses: slackapi/slack-github-action@v1.24.0 - with: - payload: | - { - "text": "❌ RuneBolt production deployment FAILED!", - "blocks": [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "❌ *RuneBolt Production Deployment FAILED*\n\n*Version:* ${{ github.sha }}\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>\n\n@oncall please investigate immediately!" - } - } - ] - } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - - # ============================================ - # Job 7: Smoke Tests - # ============================================ - smoke-tests: - name: Production Smoke Tests - runs-on: ubuntu-latest - timeout-minutes: 10 - needs: deploy-production - if: always() && needs.deploy-production.result == 'success' - - steps: - - name: Run smoke tests - run: | - echo "Running production smoke tests..." - - # Health checks - curl -f https://runebolt.io/health || exit 1 - curl -f https://api.runebolt.io/health || exit 1 - - # API checks - curl -f https://api.runebolt.io/api/status || exit 1 - - # WebSocket check - # wscat -c wss://ws.runebolt.io || exit 1 - - echo "All smoke tests passed!"