perf(ci): parallelize docker builds + add npm cache mount

Split single build-and-deploy job into 4 jobs: build-backend,
build-worker, build-frontend run in parallel (all 3 independent),
deploy job has needs:[...] on all three. Removes ~2 min of sequential
build time on warm cache runs.

Also: scope GHA cache keys per image (backend-lean / worker-chromium /
frontend) so builds don't invalidate each other's layer cache. Replace
sleep 8 health-check with a 30-iteration polling loop (fails fast on
bad deploy, doesn't over-wait on fast ones). Add --mount=type=cache to
frontend npm ci for node_modules cache across builds.
This commit is contained in:
lekss361 2026-05-11 15:01:10 +03:00
parent 1a0bf10017
commit a7d4223dfa
2 changed files with 60 additions and 16 deletions

View file

@ -25,7 +25,7 @@ env:
IMAGE_FRONTEND: ghcr.io/lekss361/gendesign-frontend
jobs:
build-and-deploy:
build-backend:
runs-on: ubuntu-latest
permissions:
contents: read
@ -34,10 +34,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set image tag
id: meta
run: echo "tag=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
- name: Login to GHCR
uses: docker/login-action@v3
with:
@ -54,11 +50,30 @@ jobs:
context: ./backend
target: runner
push: true
cache-from: type=gha,scope=backend
cache-to: type=gha,mode=max,scope=backend
cache-from: type=gha,scope=backend-lean
cache-to: type=gha,mode=max,scope=backend-lean
tags: |
${{ env.IMAGE_BACKEND }}:latest
${{ env.IMAGE_BACKEND }}:${{ steps.meta.outputs.tag }}
${{ env.IMAGE_BACKEND }}:${{ github.sha }}
build-worker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build & push worker (с Chromium для Playwright)
uses: docker/build-push-action@v6
@ -66,11 +81,30 @@ jobs:
context: ./backend
target: runner-with-chromium
push: true
cache-from: type=gha,scope=worker
cache-to: type=gha,mode=max,scope=worker
cache-from: type=gha,scope=worker-chromium
cache-to: type=gha,mode=max,scope=worker-chromium
tags: |
${{ env.IMAGE_WORKER }}:latest
${{ env.IMAGE_WORKER }}:${{ steps.meta.outputs.tag }}
${{ env.IMAGE_WORKER }}:${{ github.sha }}
build-frontend:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build & push frontend
uses: docker/build-push-action@v6
@ -81,12 +115,18 @@ jobs:
cache-to: type=gha,mode=max,scope=frontend
tags: |
${{ env.IMAGE_FRONTEND }}:latest
${{ env.IMAGE_FRONTEND }}:${{ steps.meta.outputs.tag }}
${{ env.IMAGE_FRONTEND }}:${{ github.sha }}
deploy:
runs-on: ubuntu-latest
needs: [build-backend, build-worker, build-frontend]
permissions:
contents: read
steps:
- name: Deploy to VM via SSH
uses: appleboy/ssh-action@v1.0.3
env:
IMAGE_TAG: ${{ steps.meta.outputs.tag }}
IMAGE_TAG: ${{ github.sha }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
@ -137,5 +177,8 @@ jobs:
docker image prune -af --filter "until=72h" || true
docker builder prune -af || true
sleep 8
curl -fsS http://localhost:8000/health | head -c 200 || true
# Wait for backend to be healthy (up to 30 s).
for i in $(seq 1 30); do
curl -fsS http://localhost:8000/health && break
sleep 1
done

View file

@ -5,7 +5,8 @@ FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
# --legacy-peer-deps: Tailwind 4 alpha + React 19 peer-dep mismatches.
RUN npm ci --legacy-peer-deps --no-audit --no-fund
RUN --mount=type=cache,target=/root/.npm \
npm ci --legacy-peer-deps --no-audit --no-fund
# ---- builder ----