docs: Move IMPLEMENTATION-SUMMARY.md to plan/02-completed/ #96
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: Build and Push Docker Images to GHCR | |
| on: | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - 'v*.*.*' | |
| workflow_dispatch: | |
| # Prevent concurrent builds - API image takes 20+ minutes | |
| # Cancel in-progress builds when new commits are pushed | |
| concurrency: | |
| group: docker-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME_API: ${{ github.repository }}/api | |
| IMAGE_NAME_FRONTEND: ${{ github.repository }}/frontend | |
| jobs: | |
| # Security: Lint Dockerfiles | |
| lint-dockerfiles: | |
| name: Lint Dockerfiles (Hadolint) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Lint API Dockerfile | |
| uses: hadolint/hadolint-action@v3.3.0 | |
| with: | |
| dockerfile: api/Dockerfile | |
| failure-threshold: error | |
| - name: Lint Frontend Dockerfile | |
| uses: hadolint/hadolint-action@v3.3.0 | |
| with: | |
| dockerfile: frontend/Dockerfile | |
| failure-threshold: error | |
| build-and-push-api: | |
| name: Build and Push API Image | |
| runs-on: ubuntu-latest | |
| needs: lint-dockerfiles | |
| timeout-minutes: 60 # ML dependencies (PyTorch, sentence-transformers) need 20-30min | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| security-events: write # For Trivy SARIF upload | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | |
| docker system prune -af | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for API | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_API }} | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push API image | |
| id: build-api | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./api/Dockerfile | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # Registry cache is more reliable than gha for large ML images (PyTorch ~2GB) | |
| # See: https://docs.docker.com/build/cache/backends/registry/ | |
| cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME_API }}:buildcache | |
| # Only write cache on push (PRs skip registry login, can only read cache) | |
| cache-to: ${{ github.event_name != 'pull_request' && format('type=registry,ref={0}/{1}:buildcache,mode=max', env.REGISTRY, env.IMAGE_NAME_API) || '' }} | |
| platforms: linux/amd64 | |
| load: ${{ github.event_name == 'pull_request' }} # Load for scanning on PRs | |
| - name: Scan API image with Trivy | |
| uses: aquasecurity/trivy-action@0.33.1 | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_API }}:${{ steps.meta.outputs.version }} | |
| format: 'sarif' | |
| output: 'trivy-api-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| exit-code: '0' # Don't fail build, just report | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-api-results.sarif' | |
| category: 'trivy-api' | |
| build-and-push-frontend: | |
| name: Build and Push Frontend Image | |
| runs-on: ubuntu-latest | |
| needs: lint-dockerfiles | |
| timeout-minutes: 20 # Prevent indefinite hangs (VNtyper best practice) | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| security-events: write # For Trivy SARIF upload | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Free disk space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet | |
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | |
| docker system prune -af | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Frontend | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_FRONTEND }} | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=semver,pattern={{major}} | |
| type=sha | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push Frontend image | |
| id: build-frontend | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: ./frontend | |
| file: ./frontend/Dockerfile | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # Registry cache for consistency with API image caching strategy | |
| cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME_FRONTEND }}:buildcache | |
| # Only write cache on push (PRs skip registry login, can only read cache) | |
| cache-to: ${{ github.event_name != 'pull_request' && format('type=registry,ref={0}/{1}:buildcache,mode=max', env.REGISTRY, env.IMAGE_NAME_FRONTEND) || '' }} | |
| platforms: linux/amd64 | |
| load: ${{ github.event_name == 'pull_request' }} # Load for scanning on PRs | |
| build-args: | | |
| VITE_API_URL=/api/v1 | |
| - name: Scan Frontend image with Trivy | |
| uses: aquasecurity/trivy-action@0.33.1 | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_FRONTEND }}:${{ steps.meta.outputs.version }} | |
| format: 'sarif' | |
| output: 'trivy-frontend-results.sarif' | |
| severity: 'CRITICAL,HIGH' | |
| exit-code: '0' # Don't fail build, just report | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v4 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-frontend-results.sarif' | |
| category: 'trivy-frontend' |