From 83d2ad02d0322ef1673371a7cfad8be47399563e Mon Sep 17 00:00:00 2001 From: Robert van Diest Date: Tue, 24 Mar 2026 21:08:14 +0100 Subject: [PATCH] Combine actions into 1 with separate jobs --- .github/workflows/ci.yml | 137 +++++++++++++++++++++++++++++++++++ .github/workflows/deploy.yml | 74 ------------------- .github/workflows/e2e.yml | 75 ------------------- 3 files changed, 137 insertions(+), 149 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/deploy.yml delete mode 100644 .github/workflows/e2e.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..88e1352 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,137 @@ +name: CI / CD +run-name: "${{ github.event_name == 'pull_request' && format('PR #{0} — {1}', github.event.pull_request.number, github.event.pull_request.title) || format('merge to {0} by @{1}', github.ref_name, github.actor) }}" + +on: + push: + branches: [main] + pull_request: + +jobs: + # ── E2E Tests ────────────────────────────────────────────────────────────── + e2e: + name: E2E Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build images + run: docker compose -f cicd/docker/docker-compose.yml build + + - name: Start services + run: docker compose -f cicd/docker/docker-compose.yml up -d + env: + JWT_KEY: ${{ secrets.JWT_KEY }} + + - name: Wait for frontend to be ready + run: | + echo "Waiting for services to become healthy..." + timeout 120 bash -c \ + 'until curl -sf http://localhost > /dev/null; do echo " still waiting..."; sleep 3; done' + echo "Services are ready." + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: tests/e2e/package-lock.json + + - name: Install dependencies + working-directory: tests/e2e + run: npm ci + + - name: Install Playwright browsers + working-directory: tests/e2e + run: npx playwright install --with-deps chromium + + - name: Run E2E tests + working-directory: tests/e2e + env: + BASE_URL: http://localhost + CI: 'true' + run: npx playwright test + + - name: Upload Playwright report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: tests/e2e/playwright-report/ + retention-days: 30 + + - name: Dump Docker logs on failure + if: failure() + run: docker compose -f cicd/docker/docker-compose.yml logs + + - name: Stop services + if: always() + run: docker compose -f cicd/docker/docker-compose.yml down -v + + # ── Deploy ───────────────────────────────────────────────────────────────── + deploy: + name: Deploy + runs-on: ubuntu-latest + needs: e2e + # Only deploy on merges to main, not on pull requests + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure SSH + run: | + mkdir -p ~/.ssh + echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts + + - name: Sync source to VM + run: | + rsync -az --delete \ + -e "ssh -i ~/.ssh/deploy_key" \ + --exclude='.git/' \ + --exclude='.github/' \ + --exclude='tests/' \ + --exclude='src/frontend/node_modules/' \ + --exclude='src/frontend/dist/' \ + --exclude='src/backend/**/bin/' \ + --exclude='src/backend/**/obj/' \ + --exclude='src/backend/**/*.db' \ + --exclude='src/backend/**/*.db-shm' \ + --exclude='src/backend/**/*.db-wal' \ + ./ ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/ + + - name: Deploy with Docker Compose + uses: appleboy/ssh-action@v1.0.3 + env: + JWT_KEY: ${{ secrets.JWT_KEY }} + PORT: ${{ secrets.DEPLOY_PORT || '80' }} + with: + host: ${{ secrets.DEPLOY_HOST }} + username: ${{ secrets.DEPLOY_USER }} + key: ${{ secrets.DEPLOY_SSH_KEY }} + envs: JWT_KEY,PORT + script: | + set -e + cd "${{ secrets.DEPLOY_PATH }}" + + echo "Building backend..." + DOCKER_BUILDKIT=0 docker compose -f cicd/docker/docker-compose.yml build backend + + echo "Building frontend..." + DOCKER_BUILDKIT=0 docker compose -f cicd/docker/docker-compose.yml build frontend + + echo "Starting services..." + docker compose -f cicd/docker/docker-compose.yml up -d + + echo "Removing unused images..." + docker image prune -f + + echo "Running containers:" + docker compose -f cicd/docker/docker-compose.yml ps diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 412a154..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Deploy - -on: - workflow_run: - workflows: ["E2E Tests"] - types: [completed] - branches: [main] - -jobs: - deploy: - runs-on: ubuntu-latest - # Only deploy when E2E tests passed - if: ${{ github.event.workflow_run.conclusion == 'success' }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - # ── SSH setup ───────────────────────────────────────────────────────── - - name: Configure SSH - run: | - mkdir -p ~/.ssh - echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key - chmod 600 ~/.ssh/deploy_key - ssh-keyscan -H "${{ secrets.DEPLOY_HOST }}" >> ~/.ssh/known_hosts - - # ── File sync ───────────────────────────────────────────────────────── - # rsync over SSH — equivalent to SFTP but handles deletions and only - # transfers changed files, making re-deploys fast. - - name: Sync source to VM - run: | - rsync -az --delete \ - -e "ssh -i ~/.ssh/deploy_key" \ - --exclude='.git/' \ - --exclude='.github/' \ - --exclude='tests/' \ - --exclude='src/frontend/node_modules/' \ - --exclude='src/frontend/dist/' \ - --exclude='src/backend/**/bin/' \ - --exclude='src/backend/**/obj/' \ - --exclude='src/backend/**/*.db' \ - --exclude='src/backend/**/*.db-shm' \ - --exclude='src/backend/**/*.db-wal' \ - ./ ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/ - - # ── Docker Compose ──────────────────────────────────────────────────── - - name: Deploy with Docker Compose - uses: appleboy/ssh-action@v1.0.3 - env: - JWT_KEY: ${{ secrets.JWT_KEY }} - PORT: ${{ secrets.DEPLOY_PORT || '80' }} - with: - host: ${{ secrets.DEPLOY_HOST }} - username: ${{ secrets.DEPLOY_USER }} - key: ${{ secrets.DEPLOY_SSH_KEY }} - envs: JWT_KEY,PORT - script: | - set -e - cd "${{ secrets.DEPLOY_PATH }}" - - echo "Building backend..." - DOCKER_BUILDKIT=0 docker compose -f cicd/docker/docker-compose.yml build backend - - echo "Building frontend..." - DOCKER_BUILDKIT=0 docker compose -f cicd/docker/docker-compose.yml build frontend - - echo "Starting services..." - docker compose -f cicd/docker/docker-compose.yml up -d - - echo "Removing unused images..." - docker image prune -f - - echo "Running containers:" - docker compose -f cicd/docker/docker-compose.yml ps diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml deleted file mode 100644 index 5c92524..0000000 --- a/.github/workflows/e2e.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: E2E Tests - -on: - push: - branches: [main] - pull_request: - -jobs: - e2e: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - # ── Docker ──────────────────────────────────────────────────────────── - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build images - run: docker compose -f cicd/docker/docker-compose.yml build - - - name: Start services - run: docker compose -f cicd/docker/docker-compose.yml up -d - env: - # Falls back to the default in docker-compose.yml if the secret is absent - JWT_KEY: ${{ secrets.JWT_KEY }} - - - name: Wait for frontend to be ready - run: | - echo "Waiting for services to become healthy..." - timeout 120 bash -c \ - 'until curl -sf http://localhost > /dev/null; do echo " still waiting..."; sleep 3; done' - echo "Services are ready." - - # ── Playwright ──────────────────────────────────────────────────────── - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' - cache-dependency-path: tests/e2e/package-lock.json - - - name: Install dependencies - working-directory: tests/e2e - run: npm ci - - - name: Install Playwright browsers - working-directory: tests/e2e - run: npx playwright install --with-deps chromium - - - name: Run E2E tests - working-directory: tests/e2e - env: - BASE_URL: http://localhost - CI: 'true' - run: npx playwright test - - # ── Artifacts ───────────────────────────────────────────────────────── - - name: Upload Playwright report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: tests/e2e/playwright-report/ - retention-days: 30 - - - name: Dump Docker logs on failure - if: failure() - run: docker compose -f cicd/docker/docker-compose.yml logs - - # ── Cleanup ─────────────────────────────────────────────────────────── - - name: Stop services - if: always() - run: docker compose -f cicd/docker/docker-compose.yml down -v