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!"