Structure d'un workflow
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm test
Triggers (on)
on:
push:
branches: [main, develop]
paths: ['src/**', 'package.json']
pull_request:
types: [opened, synchronize]
schedule:
- cron: '0 6 * * 1' # Chaque lundi à 6h UTC
workflow_dispatch: # Déclenchement manuel
inputs:
environment:
description: 'Target environment'
required: true
default: 'staging'
type: choice
options: [staging, production]
Jobs et dépendances
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
build:
needs: [lint, test] # Attend lint ET test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
deploy:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
steps:
- run: echo "Deploying..."
Matrix strategy
jobs:
test:
strategy:
matrix:
node-version: [20, 22]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci && npm test
Cache
- uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: pnpm-
Secrets et variables
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
DEPLOY_URL: ${{ vars.DEPLOY_URL }}
run: |
curl -X POST "$DEPLOY_URL" \
-H "Authorization: Bearer $API_KEY"
Artefacts
# Upload
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7
# Download (dans un autre job)
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
GitHub Pages deployment
name: Deploy to GitHub Pages
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: corepack enable
- run: pnpm install --frozen-lockfile
- run: pnpm build
- uses: actions/upload-pages-artifact@v3
with:
path: out
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
Docker build et push
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
Expressions utiles
# Conditions
if: github.event_name == 'push'
if: contains(github.event.head_commit.message, '[skip ci]') == false
if: github.ref == 'refs/heads/main'
if: always() # Exécuté même si un step précédent a échoué
if: failure() # Exécuté uniquement si un step précédent a échoué
# Fonctions
${{ hashFiles('**/pnpm-lock.yaml') }}
${{ github.sha }}
${{ github.ref_name }}
${{ runner.os }}
Bonnes pratiques
- Pinning des actions : utiliser
@v4 ou un SHA complet, pas @main
- Minimal permissions :
permissions: contents: read par défaut
- Secrets : jamais en dur, toujours via
${{ secrets.* }}
- Concurrency : éviter les déploiements parallèles
- Timeouts : définir
timeout-minutes pour éviter les jobs bloqués
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true