name: CI/CD Pipeline on: push: branches: [ main, develop ] pull_request: branches: [ main ] env: REGISTRY: ghcr.io IMAGE_NAME: Dvorinka/trackeep jobs: test: name: Test runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_PASSWORD: postgres POSTGRES_DB: trackeep_test options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.24' - name: Install backend dependencies run: | cd backend go mod download - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' cache-dependency-path: | frontend/package.json landing/package.json - name: Run backend tests run: | cd backend go test -v -race -coverprofile=coverage.out ./... env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/trackeep_test?sslmode=disable - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: file: ./backend/coverage.out flags: backend - name: Install frontend dependencies run: | cd frontend npm ci - name: Run frontend tests run: | cd frontend npm run test - name: Build frontend run: | cd frontend npm run build security-scan: name: Security Scan runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v4 with: go-version: '1.24' - name: Run go vet run: | cd backend go vet ./... - name: Run npm audit run: | cd frontend npm audit --audit-level high build-and-push: name: Build and Push Images runs-on: ubuntu-latest needs: [test, security-scan] if: github.ref == 'refs/heads/main' permissions: contents: read packages: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Log in to Container Registry uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr type=sha,prefix={{branch}}- type=raw,value=latest,enable={{is_default_branch}} - name: Build and push backend image uses: docker/build-push-action@v5 with: context: ./backend push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/backend:${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - name: Build and push frontend image uses: docker/build-push-action@v5 with: context: ./frontend push: true tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/frontend:${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} deploy: name: Deploy to Production runs-on: ubuntu-latest needs: build-and-push if: github.ref == 'refs/heads/main' environment: production steps: - name: Checkout code uses: actions/checkout@v4 - name: Deploy to server uses: appleboy/ssh-action@v1.0.0 with: host: ${{ secrets.PROD_HOST }} username: ${{ secrets.PROD_USER }} key: ${{ secrets.PROD_SSH_KEY }} script: | cd /opt/trackeep docker-compose -f docker-compose.prod.yml pull docker-compose -f docker-compose.prod.yml up -d docker system prune -f - name: Run health check run: | sleep 30 curl -f ${{ secrets.PROD_URL }}/health || exit 1