Skip to content

Merge pull request #65 from mariusiordan/dev #46

Merge pull request #65 from mariusiordan/dev

Merge pull request #65 from mariusiordan/dev #46

Workflow file for this run

# .github/workflows/staging.yml
# Trigger: push to staging branch
#
# Pipeline 2 - Staging Deployment
# Flow:
# lint
# ├── test-jwt ┐
# ├── test-auth ├── build-images → push-images → deploy-staging → integration-tests
# └── test-account ┘
name: Staging
on:
push:
branches:
- staging
jobs:
# ============================================================
# JOB 1 - Lint
# Runs ESLint static analysis on the codebase
# Catches syntax errors and code style issues before running tests
# ============================================================
lint:
name: ESLint
runs-on: ubuntu-24.04
defaults:
run:
working-directory: silver-bank
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: |
echo "Installing Node.js dependencies..."
npm ci
echo "✅ OK - Dependencies installed"
- name: Run ESLint
run: |
echo "Running ESLint static analysis..."
npm run lint
echo "✅ OK - Lint passed"
# ============================================================
# JOB 2 - JWT Tests
# Validates JWT token generation, signing, and verification
# Runs in parallel with Auth and Account tests after lint passes
# ============================================================
test-jwt:
name: JWT Tests
runs-on: ubuntu-24.04
needs: lint
defaults:
run:
working-directory: silver-bank
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run JWT tests
run: |
echo "Running JWT unit tests..."
npx vitest run __tests__/jwt.test.ts
echo "✅ OK - JWT tests passed"
# ============================================================
# JOB 3 - Auth Tests
# Tests user authentication flows: login and registration
# Runs in parallel with JWT and Account tests after lint passes
# ============================================================
test-auth:
name: Auth Tests
runs-on: ubuntu-24.04
needs: lint
defaults:
run:
working-directory: silver-bank
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run auth tests
run: |
echo "Running authentication unit tests..."
npx vitest run __tests__/login.test.ts
npx vitest run __tests__/register.test.ts
echo "✅ OK - Auth tests passed"
# ============================================================
# JOB 4 - Account Tests
# Tests account management operations
# Runs in parallel with JWT and Auth tests after lint passes
# ============================================================
test-account:
name: Account Tests
runs-on: ubuntu-24.04
needs: lint
defaults:
run:
working-directory: silver-bank
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run account tests
run: |
echo "Running account management unit tests..."
npx vitest run __tests__/account.test.ts
echo "✅ OK - Account tests passed"
# ============================================================
# JOB 5 - Build Docker Images
# Builds frontend (Next.js) and backend (Express.js) Docker images
# Only runs after all three test suites pass
# Uses multi-stage Dockerfiles to produce optimized production images
# ============================================================
build-images:
name: Build Docker Images
runs-on: ubuntu-24.04
needs: [test-jwt, test-auth, test-account]
permissions:
contents: read
packages: write
outputs:
image_tag: ${{ steps.tag.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Generate image tag
id: tag
run: |
# Application version — increment this manually for each release
# Versioning convention:
# MAJOR (v1 → v2) — breaking changes, major architecture changes
# MINOR (v1.0 → v1.1) — new features, improvements
# PATCH (v1.0.0 → v1.0.1) — bug fixes (optional, add if needed)
#
# Examples:
# v1.0 — initial release
# v1.1 — added transaction history feature
# v2.0 — migrated to 3-tier architecture
VERSION="v1.0"
# Git SHA — automatically generated, unique per commit
# Short SHA (7 chars) is sufficient for uniqueness in most projects
# This makes the tag immutable — same commit always produces same tag
SHA=$(git rev-parse --short HEAD)
# Final tag format: v{MAJOR}.{MINOR}-sha-{git-sha}
# Example: v1.0-sha-abc1234
# - No date — dates create confusion and add no traceability value
# - No environment — same image is promoted from staging to production
TAG="${VERSION}-sha-${SHA}"
echo "tag=${TAG}" >> $GITHUB_OUTPUT
echo "Image tag: ${TAG}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build frontend image
run: |
echo "Building frontend Docker image (Next.js)..."
echo "This step compiles the React application and creates a production-ready image"
- uses: docker/build-push-action@v5
with:
context: ./silver-bank
file: ./silver-bank/Dockerfile
platforms: linux/amd64
push: false
tags: |
ghcr.io/mariusiordan/silverbank-frontend:${{ steps.tag.outputs.tag }}
ghcr.io/mariusiordan/silverbank-frontend:staging
- name: Build backend image
run: |
echo "Building backend Docker image (Express.js + Prisma)..."
echo "This step compiles TypeScript and creates a production-ready API image"
- uses: docker/build-push-action@v5
with:
context: ./silver-bank/backend
file: ./silver-bank/backend/Dockerfile
platforms: linux/amd64
push: false
tags: |
ghcr.io/mariusiordan/silverbank-backend:${{ steps.tag.outputs.tag }}
ghcr.io/mariusiordan/silverbank-backend:staging
- name: Build summary
run: |
echo "✅ OK - Both Docker images built successfully"
echo "Frontend image: ghcr.io/mariusiordan/silverbank-frontend:${{ steps.tag.outputs.tag }}"
echo "Backend image: ghcr.io/mariusiordan/silverbank-backend:${{ steps.tag.outputs.tag }}"
# ============================================================
# JOB 6 - Push Docker Images to Registry
# Pushes the built images to GitHub Container Registry (ghcr.io)
# Images are tagged with both the unique build tag and 'staging'
# The 'staging' tag always points to the latest staging build
# ============================================================
push-images:
name: Push Images to Registry
runs-on: ubuntu-24.04
needs: build-images
permissions:
contents: read
packages: write
outputs:
image_tag: ${{ needs.build-images.outputs.image_tag }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
run: |
echo "Authenticating with GitHub Container Registry (ghcr.io)..."
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u mariusiordan --password-stdin
echo "✅ OK - Authentication successful"
- name: Push frontend image
run: |
echo "Pushing frontend image to ghcr.io..."
echo "Tag: ${{ needs.build-images.outputs.image_tag }}"
- uses: docker/build-push-action@v5
with:
context: ./silver-bank
file: ./silver-bank/Dockerfile
platforms: linux/amd64
push: true
tags: |
ghcr.io/mariusiordan/silverbank-frontend:${{ needs.build-images.outputs.image_tag }}
ghcr.io/mariusiordan/silverbank-frontend:staging
- name: Push backend image
run: |
echo "Pushing backend image to ghcr.io..."
echo "Tag: ${{ needs.build-images.outputs.image_tag }}"
- uses: docker/build-push-action@v5
with:
context: ./silver-bank/backend
file: ./silver-bank/backend/Dockerfile
platforms: linux/amd64
push: true
tags: |
ghcr.io/mariusiordan/silverbank-backend:${{ needs.build-images.outputs.image_tag }}
ghcr.io/mariusiordan/silverbank-backend:staging
- name: Push summary
run: |
echo "✅ OK - Both images pushed to GitHub Container Registry"
echo "Frontend: ghcr.io/mariusiordan/silverbank-frontend:${{ needs.build-images.outputs.image_tag }}"
echo "Backend: ghcr.io/mariusiordan/silverbank-backend:${{ needs.build-images.outputs.image_tag }}"
echo "Both images also tagged as ':staging' for easy reference"
# ============================================================
# JOB 7 - Deploy to Staging
# Pulls the newly pushed images and deploys them to the staging VM
# Uses Ansible to manage the deployment process
# Staging runs all 3 tiers: frontend, backend, and a local PostgreSQL instance
# ============================================================
deploy-staging:
name: Deploy to Staging
runs-on: self-hosted
needs: push-images
steps:
- name: Checkout infrastructure repo
uses: actions/checkout@v4
with:
repository: mariusiordan/DevOps-final-project
token: ${{ secrets.GHCR_TOKEN }}
- name: Install Ansible
run: |
echo "Installing Ansible..."
pip install ansible --break-system-packages
echo "$HOME/.local/bin" >> $GITHUB_PATH
echo "✅ OK - Ansible installed"
- name: Setup SSH key
run: |
echo "Configuring SSH access to staging VM..."
mkdir -p ~/.ssh
echo "${{ secrets.PROXMOX_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
echo "StrictHostKeyChecking no" >> ~/.ssh/config
echo "✅ OK - SSH configured"
- name: Setup Ansible vault password
run: |
echo "Configuring Ansible Vault for secrets decryption..."
echo "${{ secrets.VAULT_PASSWORD }}" > ~/.vault-password
chmod 600 ~/.vault-password
echo "✅ OK - Vault password configured"
- name: Deploy to staging VM
run: |
echo "Starting deployment to staging VM..."
echo "Image tag: ${{ needs.push-images.outputs.image_tag }}"
cd proxmox-silverbank/ansible
ansible-playbook playbooks/deploy-staging.yml \
-e "app_tag=${{ needs.push-images.outputs.image_tag }}" \
-i inventory.ini
echo "✅ OK - Deployment to staging complete"
# ============================================================
# JOB 8 - Integration Tests
# Runs end-to-end API tests against the live staging environment
# Tests the full user lifecycle: register, login, and cleanup
# These tests verify the application works correctly end-to-end
# before promoting to production
# ============================================================
integration-tests:
name: Integration Tests
runs-on: self-hosted
needs: deploy-staging
steps:
- name: Wait for application to be ready
run: |
echo "Waiting for staging application to fully start..."
sleep 20
echo "✅ OK - Wait complete"
- name: Run integration tests
run: |
TEST_EMAIL="ci-test-${GITHUB_RUN_ID}@test.com"
echo "Starting integration tests against staging environment"
echo "Test email: ${TEST_EMAIL}"
# Health check - verify the backend API is running and database is connected
echo "Running health check..."
curl -f http://192.168.7.70:4000/api/health || exit 1
echo "✅ OK - Health check passed"
# Register - create a new test user account
echo "Testing user registration..."
REGISTER=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST http://192.168.7.70/api/auth/register \
-H "Content-Type: application/json" \
-d "{\"name\":\"CI Test\",\"email\":\"${TEST_EMAIL}\",\"password\":\"test123\"}")
[ "$REGISTER" = "201" ] || exit 1
echo "✅ OK - Registration passed (HTTP 201)"
# Login - authenticate with the test user credentials
echo "Testing user login..."
LOGIN=$(curl -s -c /tmp/cookies.txt -o /dev/null -w "%{http_code}" \
-X POST http://192.168.7.70/api/auth/login \
-H "Content-Type: application/json" \
-d "{\"email\":\"${TEST_EMAIL}\",\"password\":\"test123\"}")
[ "$LOGIN" = "200" ] || exit 1
echo "✅ OK - Login passed (HTTP 200)"
# Cleanup - delete the test user to keep staging clean
echo "Cleaning up test data..."
curl -s -b /tmp/cookies.txt \
-X DELETE http://192.168.7.70/api/auth/delete \
-H "Content-Type: application/json" > /dev/null
echo "✅ OK - Test user deleted"
echo "---"
echo "✅ OK - All integration tests passed"
echo "Staging environment is healthy and ready for production promotion"