diff --git a/.claude-flow/metrics/system-metrics.json b/.claude-flow/metrics/system-metrics.json index 4edc398..205f883 100644 --- a/.claude-flow/metrics/system-metrics.json +++ b/.claude-flow/metrics/system-metrics.json @@ -334,5 +334,161 @@ "cpuLoad": 0.13654436383928573, "platform": "darwin", "uptime": 5183710 + }, + { + "timestamp": 1756497586629, + "memoryTotal": 25769803776, + "memoryUsed": 25638223872, + "memoryFree": 131579904, + "memoryUsagePercent": 99.4894027709961, + "memoryEfficiency": 0.5105972290039062, + "cpuCount": 14, + "cpuLoad": 0.13361467633928573, + "platform": "darwin", + "uptime": 5183740 + }, + { + "timestamp": 1756497616629, + "memoryTotal": 25769803776, + "memoryUsed": 25115246592, + "memoryFree": 654557184, + "memoryUsagePercent": 97.4599838256836, + "memoryEfficiency": 2.5400161743164062, + "cpuCount": 14, + "cpuLoad": 0.12283761160714286, + "platform": "darwin", + "uptime": 5183770 + }, + { + "timestamp": 1756497646631, + "memoryTotal": 25769803776, + "memoryUsed": 25625640960, + "memoryFree": 144162816, + "memoryUsagePercent": 99.4405746459961, + "memoryEfficiency": 0.5594253540039062, + "cpuCount": 14, + "cpuLoad": 0.11844308035714286, + "platform": "darwin", + "uptime": 5183800 + }, + { + "timestamp": 1756497676631, + "memoryTotal": 25769803776, + "memoryUsed": 25629868032, + "memoryFree": 139935744, + "memoryUsagePercent": 99.45697784423828, + "memoryEfficiency": 0.5430221557617188, + "cpuCount": 14, + "cpuLoad": 0.10808454241071429, + "platform": "darwin", + "uptime": 5183830 + }, + { + "timestamp": 1756497706632, + "memoryTotal": 25769803776, + "memoryUsed": 25579716608, + "memoryFree": 190087168, + "memoryUsagePercent": 99.26236470540366, + "memoryEfficiency": 0.7376352945963447, + "cpuCount": 14, + "cpuLoad": 0.11753627232142858, + "platform": "darwin", + "uptime": 5183860 + }, + { + "timestamp": 1756497736633, + "memoryTotal": 25769803776, + "memoryUsed": 25617006592, + "memoryFree": 152797184, + "memoryUsagePercent": 99.40706888834634, + "memoryEfficiency": 0.5929311116536553, + "cpuCount": 14, + "cpuLoad": 0.10857282366071429, + "platform": "darwin", + "uptime": 5183890 + }, + { + "timestamp": 1756497766634, + "memoryTotal": 25769803776, + "memoryUsed": 25662226432, + "memoryFree": 107577344, + "memoryUsagePercent": 99.5825449625651, + "memoryEfficiency": 0.4174550374349053, + "cpuCount": 14, + "cpuLoad": 0.108642578125, + "platform": "darwin", + "uptime": 5183920 + }, + { + "timestamp": 1756497796634, + "memoryTotal": 25769803776, + "memoryUsed": 25651675136, + "memoryFree": 118128640, + "memoryUsagePercent": 99.5416005452474, + "memoryEfficiency": 0.4583994547525947, + "cpuCount": 14, + "cpuLoad": 0.15876116071428573, + "platform": "darwin", + "uptime": 5183950 + }, + { + "timestamp": 1756497826636, + "memoryTotal": 25769803776, + "memoryUsed": 25676709888, + "memoryFree": 93093888, + "memoryUsagePercent": 99.63874816894531, + "memoryEfficiency": 0.3612518310546875, + "cpuCount": 14, + "cpuLoad": 0.16842215401785715, + "platform": "darwin", + "uptime": 5183980 + }, + { + "timestamp": 1756497856636, + "memoryTotal": 25769803776, + "memoryUsed": 25665978368, + "memoryFree": 103825408, + "memoryUsagePercent": 99.59710439046225, + "memoryEfficiency": 0.40289560953775094, + "cpuCount": 14, + "cpuLoad": 0.19695172991071427, + "platform": "darwin", + "uptime": 5184010 + }, + { + "timestamp": 1756497886638, + "memoryTotal": 25769803776, + "memoryUsed": 25674203136, + "memoryFree": 95600640, + "memoryUsagePercent": 99.62902069091797, + "memoryEfficiency": 0.37097930908203125, + "cpuCount": 14, + "cpuLoad": 0.21759905133928573, + "platform": "darwin", + "uptime": 5184040 + }, + { + "timestamp": 1756497916639, + "memoryTotal": 25769803776, + "memoryUsed": 25173065728, + "memoryFree": 596738048, + "memoryUsagePercent": 97.6843516031901, + "memoryEfficiency": 2.3156483968099053, + "cpuCount": 14, + "cpuLoad": 0.20626395089285715, + "platform": "darwin", + "uptime": 5184070 + }, + { + "timestamp": 1756497946640, + "memoryTotal": 25769803776, + "memoryUsed": 25338855424, + "memoryFree": 430948352, + "memoryUsagePercent": 98.32770029703775, + "memoryEfficiency": 1.672299702962249, + "cpuCount": 14, + "cpuLoad": 0.24553571428571427, + "platform": "darwin", + "uptime": 5184100 } ] \ No newline at end of file diff --git a/.github/workflows/README-DOCKER.md b/.github/workflows/README-DOCKER.md new file mode 100644 index 0000000..d06791c --- /dev/null +++ b/.github/workflows/README-DOCKER.md @@ -0,0 +1,213 @@ +# Docker Build and Push Workflow + +This GitHub Actions workflow automatically builds and pushes Docker images for both the backend and frontend to GitHub Container Registry (ghcr.io). + +## Features + +- 🔧 **Automatic builds** on push to main/develop branches, PRs, and releases +- 🏗️ **Multi-architecture support** (linux/amd64 and linux/arm64) +- 🏷️ **Smart tagging** based on branches, versions, and commits +- 🔒 **Security scanning** with Trivy vulnerability scanner +- 💾 **Build caching** for faster subsequent builds +- 📊 **Build summaries** in GitHub Actions UI + +## Authentication + +The workflow uses the built-in `GITHUB_TOKEN` for authentication with GitHub Container Registry. No additional setup or personal access tokens are required. + +### Required Permissions + +The workflow automatically sets the necessary permissions: +- `contents: read` - To checkout the repository +- `packages: write` - To push images to ghcr.io +- `security-events: write` - To upload security scan results + +## Image Tags + +Images are automatically tagged based on the trigger event: + +| Event | Tags Generated | +|-------|---------------| +| Push to main | `latest`, `main`, `main-` | +| Push to develop | `develop`, `develop-` | +| Pull Request | `pr-` | +| Release (v1.2.3) | `1.2.3`, `1.2`, `1`, `latest` | +| Manual trigger | Based on branch + optional push | + +## Usage + +### Pull Images + +Once published, images can be pulled using: + +```bash +# Pull backend image +docker pull ghcr.io/the-luap/picpeak/backend:latest + +# Pull frontend image +docker pull ghcr.io/the-luap/picpeak/frontend:latest + +# Pull specific version +docker pull ghcr.io/the-luap/picpeak/backend:v1.0.0 + +# Pull for specific architecture +docker pull --platform linux/arm64 ghcr.io/the-luap/picpeak/backend:latest +``` + +### Using in Docker Compose + +```yaml +version: '3.8' + +services: + backend: + image: ghcr.io/the-luap/picpeak/backend:latest + environment: + - NODE_ENV=production + ports: + - "3001:3000" + + frontend: + image: ghcr.io/the-luap/picpeak/frontend:latest + ports: + - "80:80" +``` + +### Using in Kubernetes + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: picpeak-backend +spec: + replicas: 3 + template: + spec: + containers: + - name: backend + image: ghcr.io/the-luap/picpeak/backend:latest + imagePullPolicy: Always +``` + +## Manual Workflow Trigger + +You can manually trigger the workflow from the Actions tab: + +1. Go to Actions → "Build and Push Docker Images" +2. Click "Run workflow" +3. Select branch and whether to push images +4. Click "Run workflow" + +## Security Scanning + +The workflow includes Trivy vulnerability scanning that: +- Scans for CRITICAL and HIGH severity vulnerabilities +- Uploads results to GitHub Security tab +- Available under Security → Code scanning alerts + +## Build Optimization + +The workflow uses several optimization techniques: + +1. **GitHub Actions Cache**: Speeds up builds by caching layers +2. **Multi-stage builds**: Reduces final image size +3. **Parallel builds**: Backend and frontend build simultaneously +4. **Smart rebuilds**: Only rebuilds changed components + +## Troubleshooting + +### Permission Denied Errors + +If you encounter permission errors when pushing images: + +1. **First-time setup**: The first push creates a private package. You may need to: + - Go to your package settings at `https://github.com/users/YOUR_USERNAME/packages` + - Link the package to your repository + - Set package visibility (public/private) + +2. **Organization repositories**: Ensure the organization allows GitHub Actions to create packages + +### Build Failures + +Check the workflow logs in the Actions tab for detailed error messages. Common issues: +- Missing dependencies in package.json +- Dockerfile syntax errors +- Network issues during package installation + +### Image Not Found + +If images aren't visible after successful push: +- Check package visibility settings +- Ensure you're authenticated to pull private images: + ```bash + echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin + ``` + +## Package Management + +### View Packages + +Your Docker images are available at: +- Backend: `https://github.com/users/the-luap/packages/container/package/picpeak%2Fbackend` +- Frontend: `https://github.com/users/the-luap/packages/container/package/picpeak%2Ffrontend` + +### Delete Old Versions + +To save storage, you can delete old versions: +1. Go to package settings +2. Click on "Manage versions" +3. Select versions to delete +4. Click "Delete selected versions" + +### Set Retention Policy + +Configure automatic cleanup in package settings: +1. Go to package settings +2. Click on "Manage Actions access" +3. Set retention days for untagged versions + +## Best Practices + +1. **Use semantic versioning** for releases (e.g., v1.2.3) +2. **Test images locally** before pushing to production +3. **Monitor security alerts** from Trivy scans +4. **Clean up old images** regularly to save storage +5. **Use specific tags** in production (avoid `latest`) + +## Advanced Configuration + +### Custom Registry + +To use a different registry, update the workflow: + +```yaml +env: + REGISTRY: docker.io # or your custom registry + BACKEND_IMAGE_NAME: yourusername/picpeak-backend +``` + +### Additional Platforms + +To build for more platforms: + +```yaml +platforms: linux/amd64,linux/arm64,linux/arm/v7 +``` + +### Custom Build Arguments + +Add build arguments in the workflow: + +```yaml +build-args: | + NODE_VERSION=20 + API_URL=${{ secrets.API_URL }} +``` + +## Related Documentation + +- [GitHub Container Registry Docs](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) +- [Docker Build Action](https://github.com/docker/build-push-action) +- [Trivy Security Scanner](https://github.com/aquasecurity/trivy) +- [Multi-platform Builds](https://docs.docker.com/build/building/multi-platform/) \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..01b9e6d --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,251 @@ +name: Build and Push Docker Images + +on: + push: + branches: [ main, develop ] + tags: [ 'v*.*.*' ] + pull_request: + branches: [ main ] + release: + types: [ published ] + workflow_dispatch: + inputs: + push: + description: 'Push images to registry' + required: false + default: 'false' + type: choice + options: + - 'true' + - 'false' + +env: + REGISTRY: ghcr.io + BACKEND_IMAGE_NAME: ${{ github.repository }}/backend + FRONTEND_IMAGE_NAME: ${{ github.repository }}/frontend + +jobs: + build-backend: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: Log in to Container Registry + if: github.event_name != 'pull_request' || github.event.inputs.push == 'true' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Backend + id: meta-backend + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }} + labels: | + org.opencontainers.image.title=PicPeak Backend + org.opencontainers.image.description=PicPeak photo sharing platform backend service + org.opencontainers.image.vendor=PicPeak + maintainer=${{ github.repository_owner }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,prefix={{branch}}-,format=short + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Backend Docker image + uses: docker/build-push-action@v5 + with: + context: ./backend + file: ./backend/Dockerfile + push: ${{ github.event_name != 'pull_request' || github.event.inputs.push == 'true' }} + tags: ${{ steps.meta-backend.outputs.tags }} + labels: ${{ steps.meta-backend.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=backend + cache-to: type=gha,mode=max,scope=backend + build-args: | + CACHEBUST=${{ github.run_number }} + BUILD_DATE=${{ github.event.head_commit.timestamp }} + VCS_REF=${{ github.sha }} + VERSION=${{ steps.meta-backend.outputs.version }} + + - name: Run Trivy vulnerability scanner + if: github.event_name != 'pull_request' + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:${{ steps.meta-backend.outputs.version }} + format: 'sarif' + output: 'trivy-backend.sarif' + severity: 'CRITICAL,HIGH' + timeout: '10m' + + - name: Upload Trivy scan results to GitHub Security tab + if: github.event_name != 'pull_request' + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'trivy-backend.sarif' + category: 'backend-vulnerabilities' + + build-frontend: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: Log in to Container Registry + if: github.event_name != 'pull_request' || github.event.inputs.push == 'true' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Frontend + id: meta-frontend + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }} + labels: | + org.opencontainers.image.title=PicPeak Frontend + org.opencontainers.image.description=PicPeak photo sharing platform frontend application + org.opencontainers.image.vendor=PicPeak + maintainer=${{ github.repository_owner }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,prefix={{branch}}-,format=short + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Frontend Docker image + uses: docker/build-push-action@v5 + with: + context: ./frontend + file: ./frontend/Dockerfile + push: ${{ github.event_name != 'pull_request' || github.event.inputs.push == 'true' }} + tags: ${{ steps.meta-frontend.outputs.tags }} + labels: ${{ steps.meta-frontend.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=frontend + cache-to: type=gha,mode=max,scope=frontend + build-args: | + CACHEBUST=${{ github.run_number }} + BUILD_DATE=${{ github.event.head_commit.timestamp }} + VCS_REF=${{ github.sha }} + VERSION=${{ steps.meta-frontend.outputs.version }} + + - name: Run Trivy vulnerability scanner + if: github.event_name != 'pull_request' + uses: aquasecurity/trivy-action@master + with: + image-ref: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:${{ steps.meta-frontend.outputs.version }} + format: 'sarif' + output: 'trivy-frontend.sarif' + severity: 'CRITICAL,HIGH' + timeout: '10m' + + - name: Upload Trivy scan results to GitHub Security tab + if: github.event_name != 'pull_request' + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'trivy-frontend.sarif' + category: 'frontend-vulnerabilities' + + publish-manifest: + needs: [build-backend, build-frontend] + if: github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push multi-arch manifest for Backend + run: | + docker manifest create \ + ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:latest \ + --amend ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:latest-amd64 \ + --amend ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:latest-arm64 + docker manifest push ${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}:latest + + - name: Create and push multi-arch manifest for Frontend + run: | + docker manifest create \ + ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:latest \ + --amend ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:latest-amd64 \ + --amend ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:latest-arm64 + docker manifest push ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:latest + + summary: + needs: [build-backend, build-frontend] + if: always() + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Build Summary + run: | + echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [[ "${{ needs.build-backend.result }}" == "success" ]]; then + echo "✅ **Backend**: Successfully built" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Backend**: Build failed" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.build-frontend.result }}" == "success" ]]; then + echo "✅ **Frontend**: Successfully built" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Frontend**: Build failed" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 📦 Images" >> $GITHUB_STEP_SUMMARY + echo "- Backend: \`${{ env.REGISTRY }}/${{ env.BACKEND_IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY + echo "- Frontend: \`${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 🏷️ Tags" >> $GITHUB_STEP_SUMMARY + echo "Images are tagged based on:" >> $GITHUB_STEP_SUMMARY + echo "- Branch name (for branch pushes)" >> $GITHUB_STEP_SUMMARY + echo "- PR number (for pull requests)" >> $GITHUB_STEP_SUMMARY + echo "- Version tags (for releases)" >> $GITHUB_STEP_SUMMARY + echo "- Short SHA with branch prefix" >> $GITHUB_STEP_SUMMARY + echo "- \`latest\` (for main branch)" >> $GITHUB_STEP_SUMMARY \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index 8a27083..9f81c4a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,7 +1,15 @@ FROM node:18-alpine AS builder -# Add build argument for cache busting +# Add build arguments ARG CACHEBUST=1 +ARG BUILD_DATE +ARG VCS_REF +ARG VERSION + +# Add labels for GitHub Container Registry +LABEL org.opencontainers.image.source="https://github.com/the-luap/picpeak" +LABEL org.opencontainers.image.description="PicPeak Backend Service" +LABEL org.opencontainers.image.licenses="MIT" WORKDIR /app diff --git a/frontend/Dockerfile b/frontend/Dockerfile index ca73e13..aef876d 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,6 +1,17 @@ # Build stage FROM node:20-alpine AS builder +# Add build arguments +ARG CACHEBUST=1 +ARG BUILD_DATE +ARG VCS_REF +ARG VERSION + +# Add labels for GitHub Container Registry +LABEL org.opencontainers.image.source="https://github.com/the-luap/picpeak" +LABEL org.opencontainers.image.description="PicPeak Frontend Application" +LABEL org.opencontainers.image.licenses="MIT" + # Set working directory WORKDIR /app