feat: implement ci/cd pipeline with docker #3
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI/CD Pipeline | |
| on: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: 'Environment to deploy to' | |
| required: true | |
| default: 'staging' | |
| type: choice | |
| options: [staging, production] | |
| image_tag: | |
| description: 'Specific image tag to deploy (e.g. for rollback)' | |
| required: false | |
| type: string | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| REGISTRY: ghcr.io | |
| BACKEND_IMAGE: ${{ github.repository }}/backend | |
| FRONTEND_IMAGE: ${{ github.repository }}/frontend | |
| jobs: | |
| # 1. Quality Gate (Lint & Test) | |
| quality-gate: | |
| name: Lint & Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| cache: 'pip' | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Backend Lint | |
| working-directory: backend | |
| run: | | |
| pip install ruff | |
| ruff check . | |
| - name: Backend Tests | |
| working-directory: backend | |
| run: | | |
| pip install -r requirements.txt | |
| pip install pytest pytest-asyncio | |
| pytest tests/ | |
| env: | |
| DATABASE_URL: sqlite+aiosqlite:///./test.db | |
| - name: Frontend Lint & Typecheck | |
| working-directory: frontend | |
| run: | | |
| npm ci | |
| npm run lint --if-present | |
| npx tsc --noEmit | |
| # 2. Build & Push Images | |
| build-and-push: | |
| name: Build & Push Docker Images | |
| needs: quality-gate | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| 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.BACKEND_IMAGE }} | |
| ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }} | |
| tags: | | |
| type=sha,prefix= | |
| type=ref,event=branch | |
| type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} | |
| - name: Build and push Backend | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./backend | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:${{ github.sha }},${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE }}:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Build and push Frontend | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./frontend | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:${{ github.sha }},${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE }}:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # 3. Deploy to Staging | |
| deploy-staging: | |
| name: Deploy to Staging | |
| needs: build-and-push | |
| if: github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'staging') | |
| runs-on: ubuntu-latest | |
| environment: staging | |
| steps: | |
| - name: Deploy to Railway (or similar) | |
| run: echo "Deploying to staging environment..." | |
| # Example for Railway/Fly/DigitalOcean would go here | |
| # 4. Deploy to Production (Manual Approval) | |
| deploy-production: | |
| name: Deploy to Production | |
| needs: deploy-staging | |
| if: github.event_name == 'workflow_dispatch' && inputs.environment == 'production' | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: production | |
| url: https://solfoundry.org | |
| steps: | |
| - name: Deploy to Production | |
| run: | | |
| TAG=${{ inputs.image_tag || github.sha }} | |
| echo "Deploying image tag $TAG to production environment..." | |
| # 5. Rollback / Direct Tag Deploy | |
| deploy-tag: | |
| name: Deploy Specific Tag | |
| if: github.event_name == 'workflow_dispatch' && inputs.image_tag != '' | |
| runs-on: ubuntu-latest | |
| environment: ${{ inputs.environment }} | |
| steps: | |
| - name: Deploy Tag | |
| run: | | |
| echo "Executing direct deployment/rollback of tag ${{ inputs.image_tag }} to ${{ inputs.environment }}..." |