L'objectif
À chaque push, la CI doit répondre à une seule question : ce code peut-il être livré ? Le plus simple est de réutiliser la commande que vous lancez en local.
Un workflow minimal
name: CI
on:
push:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- run: npm ci
- run: npm run checknpm run check enchaîne lint, vérification des types et tests. Une seule commande, identique en local et en CI : pas de dérive entre les deux.
Dockerfile multi-étapes
Une image de production ne doit contenir ni les outils de build ni les dépendances de développement.
FROM node:24-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:24-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public
COPY --from=build /app/content ./content
COPY --from=build /app/package*.json ./
RUN npm ci --omit=dev
USER node
EXPOSE 3000
CMD ["npm", "run", "start"]Trois étapes : dépendances, build, exécution. Seule la dernière est livrée.
Optimiser le cache
L'ordre des instructions compte. Copiez d'abord package*.json et installez : tant que les dépendances ne changent pas, Docker réutilise la couche en cache, même si le code change.
Bonnes pratiques
USER node: ne pas exécuter en root.- Un fichier
.dockerignorepour exclurenode_modules,.gitet.env. - Des images de base épinglées sur une version précise.
- Ne jamais copier de secrets dans l'image.
Pour aller plus loin
Une fois la vérification en place, on peut ajouter un job de construction d'image, puis de déploiement, déclenchés uniquement sur la branche principale.
À retenir
- Une commande de vérification unique, partagée entre local et CI.
- Multi-stage + ordre des couches = images petites et builds rapides.
- La CI est un contrat : ce qui passe est livrable.