الأسبوع ٤ · فصل ٤

CI و Docker و Changesets

GitHub Actions --affected · turbo prune --docker · Changesets
🎯 الهدف: الـ CI يبني اللي اتغيّر بس، والـ Docker image بتاعة الـ api ماتشيلش الريبو كله، وتعرف تنشر packages بنظام.

المشكلة في الـ CI

ريبو فيه web و api و ui و types. غيّرت سطر في الـ README بتاع api — هل محتاج تبني الـ web وتشغّل الـ Storybook build؟ لأ طبعًا. الحل فكرتين مع بعض:

🎯 Affected

شغّل المهام بس على الـ packages اللي اتغيّرت + اللي بيعتمدوا عليها.

☁️ Remote Cache

حتى اللي “اتأثر” ممكن يكون اتبنى قبل كده في PR تاني أو على جهاز زميلك → hit.

GitHub Actions

.github/workflows/ci.yml
name: CIon:  pull_request:  push:    branches: [main]jobs:  ci:    runs-on: ubuntu-latest    env:      TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}   # remote cache      TURBO_TEAM: ${{ vars.TURBO_TEAM }}    steps:      - uses: actions/checkout@v4        with:          fetch-depth: 0          # محتاج الـ history عشان يقارن بـ main      - uses: pnpm/action-setup@v4   # بيقرا packageManager من package.json      - uses: actions/setup-node@v4        with:          node-version: 20          cache: pnpm      - run: pnpm install --frozen-lockfile      - run: pnpm turbo run lint typecheck test build --affected

Docker لـ app واحد: turbo prune

المشكلة: لو عملت COPY . . للـ api، الـ image هتشيل كود الـ web والـ Storybook وكل الـ dependencies بتاعتهم. وأسوأ: أي تعديل في web هيكسر الـ Docker layer cache بتاع الـ api.

turbo prune api --docker بيطلّعلك فولدر out/ فيه بس اللي الـ api محتاجه:

out/├── json/             # package.json بتاع api و types و tsconfig بس├── full/             # الكود الكامل للـ packages دي بس└── pnpm-lock.yaml    # lockfile متقلّم للـ subset ده
apps/api/Dockerfile
FROM node:20-alpine AS baseRUN corepack enableWORKDIR /app# 1) قلّم الريبو لـ api بسFROM base AS prunerCOPY . .RUN pnpm dlx turbo prune api --docker# 2) نزّل الـ deps (layer بيتكيّش طالما الـ package.json ماتغيرش)FROM base AS builderCOPY --from=pruner /app/out/json/ .COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yamlRUN pnpm install --frozen-lockfile# 3) انسخ الكود وابنيCOPY --from=pruner /app/out/full/ .RUN pnpm turbo run build --filter=api...# 4) image التشغيلFROM base AS runnerENV NODE_ENV=productionCOPY --from=builder /app .USER nodeCMD ["node", "apps/api/dist/main.js"]
# من الـ root بتاع الريبو (مش من جوه apps/api)docker build -f apps/api/Dockerfile -t jobs-api .

Changesets: لو هتنشر packages

لو الـ packages كلها private ومستخدمة جوه الريبو بس، مش محتاج Changesets. هتحتاجه لو هتنشر الـ design system على npm (حتى لو private registry) لفرق تانية بره الريبو.

pnpm add -Dw @changesets/clipnpm changeset initpnpm changeset            # اختار الـ packages ونوع التغيير (patch/minor/major)pnpm changeset version    # يرفع الأرقام ويكتب CHANGELOG ويحدّث workspace:^pnpm turbo build --filter=@repo/uipnpm changeset publish
.changeset/brave-lions-dance.md
---"@repo/ui": minor---Add `loading` prop to Button

Checklist الأسبوع الرابع

🧠 اختبر نفسك
١) ليه بنعمل COPY out/json و install قبل COPY out/full؟
عشان Docker يكيّش الـ install layer؛ تعديل الكود مايعيدش تنزيل الـ deps.
٢) --affected بيبني كل حاجة في كل PR. أول شك؟
الـ checkout بـ depth 1 فمفيش base يقارن بيه.
٣) كل الـ packages private. محتاج Changesets؟
غالبًا لأ؛ الـ workspace protocol كفاية.