This repository was archived by the owner on Apr 19, 2026. It is now read-only.
Merge pull request #2 from recomendapp/feat/nx-migration #1
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 & Deploy (Production) | |
| on: | |
| push: | |
| branches: ['main'] | |
| jobs: | |
| # ================================================================================= | |
| # JOB 1: Build & Push Docker Images | |
| # Optimized: Builds and Pushes in parallel within a single runner using Nx & Background jobs | |
| # ================================================================================= | |
| build-and-push: | |
| name: π³ Build & Push Images | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write # Required to push to GHCR | |
| outputs: | |
| version: ${{ steps.prep.outputs.VERSION }} | |
| affected_projects: ${{ steps.affected.outputs.PROJECTS }} | |
| steps: | |
| # 1. Checkout with full history for Nx Affected analysis | |
| - name: Checkout Code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| # 2. Setup Environment (PNPM & Node) | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v2 | |
| with: | |
| version: 9 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: 'pnpm' | |
| # 3. Install Dependencies (Cached) | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| # 4. Nx Setup: Calculate SHAs for "affected" detection | |
| # This action automatically determines the base and head commits | |
| - name: Initialize Nx SHAs | |
| uses: nrwl/nx-set-shas@v4 | |
| # 5. Login to GitHub Container Registry | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # 6. Generate Version Tag (Hybrid Strategy) | |
| # Format: b<RunNumber>-<ShortSHA> (e.g., b842-a1b2c3d) | |
| # This provides both sequence order and commit traceability. | |
| - name: Generate Version Tag | |
| id: prep | |
| run: | | |
| SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7) | |
| VERSION="b${{ github.run_number }}-${SHORT_SHA}" | |
| echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "π Build Version: ${VERSION}" | |
| # 7. Build Docker Images (Parallel Build) | |
| # Nx handles parallelism based on CPU cores available in the runner | |
| - name: Build Docker Images (Nx Affected) | |
| run: npx nx affected -t docker-build --parallel=3 | |
| # 8. Tag and Push to GHCR (Parallel Push via Bash) | |
| # We use Bash background processes (&) to push all images simultaneously | |
| - name: Retag and Push to GHCR | |
| id: affected | |
| run: | | |
| # Get list of affected projects with a 'docker-build' target as JSON | |
| AFFECTED=$(npx nx show projects --affected --with-target=docker-build --json) | |
| echo "Affected projects (JSON): $AFFECTED" | |
| # Parse JSON to a space-separated string for the loop | |
| PROJECTS=$(echo $AFFECTED | jq -r '.[]') | |
| # Exit early if no projects are affected | |
| if [ -z "$PROJECTS" ]; then | |
| echo "No docker projects affected." | |
| echo "PROJECTS=[]" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| VERSION="${{ steps.prep.outputs.VERSION }}" | |
| # Ensure owner is lowercase for Docker tags | |
| OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') | |
| # Array to track background process IDs | |
| PIDS="" | |
| for PROJECT in $PROJECTS; do | |
| ( | |
| echo "π [${PROJECT}] Processing..." | |
| # Local image built by Nx | |
| LOCAL_IMAGE="api-${PROJECT}:latest" | |
| # Remote image for GHCR | |
| REMOTE_IMAGE="ghcr.io/${OWNER}/api-${PROJECT}" | |
| # Re-tag local image for registry | |
| docker tag "${LOCAL_IMAGE}" "${REMOTE_IMAGE}:${VERSION}" | |
| docker tag "${LOCAL_IMAGE}" "${REMOTE_IMAGE}:latest" | |
| # Push to registry (Specific version + Latest) | |
| echo "β¬οΈ [${PROJECT}] Pushing..." | |
| docker push "${REMOTE_IMAGE}:${VERSION}" | |
| docker push "${REMOTE_IMAGE}:latest" | |
| echo "β [${PROJECT}] Done." | |
| ) & | |
| # Capture the PID of the background process | |
| PIDS="$PIDS $!" | |
| done | |
| # Wait for all background pushes to finish | |
| FAIL=0 | |
| for PID in $PIDS; do | |
| wait $PID || FAIL=1 | |
| done | |
| if [ "$FAIL" -eq 1 ]; then | |
| echo "β One or more images failed to push." | |
| exit 1 | |
| fi | |
| # Export affected list for the next job | |
| echo "PROJECTS=$AFFECTED" >> $GITHUB_OUTPUT | |
| # ================================================================================= | |
| # JOB 2: Update Infrastructure Repo (GitOps) | |
| # Updates deployment.yaml files with the new version tag | |
| # ================================================================================= | |
| update-infra: | |
| name: ποΈ Update Infra Manifests | |
| needs: build-and-push | |
| # Only run if projects were actually built/pushed | |
| if: needs.build-and-push.outputs.affected_projects != '[]' && needs.build-and-push.outputs.affected_projects != '' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Clone Infra Repository | |
| run: | | |
| git clone https://ci-bot:${{ secrets.PAT_TOKEN }}@github.com/${{ github.repository_owner }}/infra.git | |
| - name: Update Deployment Manifests | |
| run: | | |
| cd infra | |
| SERVICES_JSON='${{ needs.build-and-push.outputs.affected_projects }}' | |
| VERSION='${{ needs.build-and-push.outputs.version }}' | |
| OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') | |
| echo "Updates to apply: $SERVICES_JSON -> $VERSION" | |
| # Loop through each affected service and update its yaml | |
| for service in $(echo $SERVICES_JSON | jq -r '.[]'); do | |
| IMAGE_NAME="api-$service" | |
| # WARNING: Adjust this path to match your actual infra repo structure! | |
| DEPLOYMENT_PATH="apps/services/api/$service/deployment.yaml" | |
| if [ -f "$DEPLOYMENT_PATH" ]; then | |
| echo "β Updating $service -> $VERSION" | |
| # Regex replacement for the image tag | |
| sed -i "s|image: ghcr.io.*/${IMAGE_NAME}:.*|image: ghcr.io/${OWNER}/${IMAGE_NAME}:${VERSION}|" "$DEPLOYMENT_PATH" | |
| else | |
| echo "β οΈ File not found: $DEPLOYMENT_PATH (Skipping update for $service)" | |
| fi | |
| done | |
| - name: Commit and Push Changes | |
| run: | | |
| cd infra | |
| git config user.name "ci-bot" | |
| git config user.email "ci-bot@users.noreply.github.com" | |
| git add . | |
| # Only commit if something actually changed | |
| if git diff --staged --quiet; then | |
| echo "No changes detected." | |
| else | |
| git commit -m "deploy(api): update images to version ${{ needs.build-and-push.outputs.version }}" | |
| git push | |
| fi |