Les actions composites permettent de regrouper plusieurs steps en une action réutilisable. Contrairement aux workflows réutilisables qui opèrent au niveau des jobs, les actions composites s'intègrent comme un step dans n'importe quel workflow.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Distinguer action composite et workflow réutilisable, et choisir le bon
- Créer un fichier
action.ymlavecruns.using: composite - Définir des inputs et des outputs typés et documentés
- Appeler une action composite locale ou hébergée dans un autre dépôt
- Sécuriser les steps : épinglage SHA, secrets passés par
env: - Publier et versionner une action sur le GitHub Marketplace
Ce guide s'adresse à ceux qui répètent les mêmes séquences de steps dans plusieurs workflows. Si vous débutez, voyez d'abord Workflows GitHub Actions.
Actions composites vs workflows réutilisables
Section intitulée « Actions composites vs workflows réutilisables »Les deux mécanismes factorisent du code CI/CD, mais à des granularités
différentes. Une action composite est un step : elle s'insère dans un job
existant, aux côtés d'autres steps. Un workflow réutilisable est un job
complet : il s'appelle à la place des steps:. Le tableau ci-dessous résume
quand chacun s'impose.
| Critère | Actions composites | Workflows réutilisables |
|---|---|---|
| Niveau | Step | Job |
| Fichier | action.yml | .github/workflows/*.yml |
| Appel | uses: dans un step | uses: au niveau job |
| Outputs | Outputs de step | Outputs de job |
| Secrets | Via ${{ secrets.* }} dans le workflow | Passés explicitement |
| Matrix | Non | Oui |
| Parallélisme | Non (séquentiel) | Oui (jobs parallèles) |
Utilisez les actions composites pour : des séquences de steps réutilisables. Utilisez les workflows réutilisables pour : des pipelines complets avec jobs.
Créer une action composite
Section intitulée « Créer une action composite »Une action composite vit dans son propre fichier action.yml, à la racine d'un
dossier dédié. Ce fichier décrit ce que l'action attend (inputs), ce
qu'elle renvoie (outputs) et la séquence de steps qu'elle exécute (runs).
Structure de base
Section intitulée « Structure de base »Une action composite tient dans un seul fichier action.yml. Il déclare les
métadonnées, les inputs, les outputs et la séquence de steps sous la clé
runs. Voici une action complète qui installe Node.js et lance les tests :
name: 'Setup and Test'description: "Configure l'environnement et lance les tests"author: 'Stéphane Robert'
inputs: node-version: description: 'Version de Node.js' required: false default: '20'
outputs: test-result: description: 'Résultat des tests' value: ${{ steps.test.outputs.result }}
runs: using: 'composite' # Indique que c'est une action composite steps: - name: Checkout du dépôt uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false
- name: Installer Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: 'npm'
- name: Installer les dépendances run: npm ci shell: bash
- name: Lancer les tests id: test run: | npm test echo "result=success" >> "$GITHUB_OUTPUT" shell: bashPropriétés obligatoires
Section intitulée « Propriétés obligatoires »Quatre clés structurent tout fichier action.yml, plus une contrainte propre
aux actions composites : chaque step run doit déclarer son shell.
| Propriété | Description |
|---|---|
name | Nom de l'action |
description | Description courte |
runs.using | Doit être composite |
runs.steps | Liste des steps |
shell | Obligatoire pour chaque step run |
Définir les inputs
Section intitulée « Définir les inputs »Les inputs sont les paramètres que le workflow appelant transmet à
l'action. Chacun se déclare avec une description, un caractère obligatoire ou
non (required) et, le cas échéant, une valeur par défaut.
inputs: # Input obligatoire environment: description: 'Environnement cible' required: true
# Input optionnel avec défaut node-version: description: 'Version de Node.js' required: false default: '20'
# Input booléen (passé comme string) skip-cache: description: 'Désactiver le cache' required: false default: 'false'Accès aux inputs : ${{ inputs.input-name }}
Définir les outputs
Section intitulée « Définir les outputs »Les outputs exposent au workflow appelant des valeurs calculées pendant
l'exécution. Chaque output référence la sortie d'un step interne via son
id — le step doit donc obligatoirement porter un id.
outputs: version: description: 'Version détectée' value: ${{ steps.detect.outputs.version }}
artifact-path: description: "Chemin de l'artifact" value: ${{ steps.build.outputs.path }}
runs: using: 'composite' steps: - name: Détecter la version id: detect run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT" shell: bash
- name: Construire le projet id: build run: | npm run build echo "path=./dist" >> "$GITHUB_OUTPUT" shell: bashAccès aux outputs (dans le workflow appelant) — la valeur transite par un
bloc env: avant d'être utilisée dans le run: :
- uses: ./my-action id: setup
- name: Afficher la version détectée env: DETECTED_VERSION: ${{ steps.setup.outputs.version }} run: echo "Version : $DETECTED_VERSION"Utiliser une action composite
Section intitulée « Utiliser une action composite »Une action composite s'invoque comme n'importe quelle action : le mot-clé
uses: dans un step. Ce qui change, c'est d'où vient l'action — un dossier
du dépôt courant ou un dépôt externe.
Action locale (même repository)
Section intitulée « Action locale (même repository) »Une action stockée dans le dépôt courant s'appelle par un chemin relatif
commençant par ./. Son code est versionné avec le workflow, au même commit :
inutile de l'épingler par SHA, c'est votre propre code.
name: CI
on: push: branches: [main]
permissions: {}
jobs: build: runs-on: ubuntu-24.04 permissions: contents: read steps: - name: Checkout du dépôt uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false
# Action dans le même repo - name: Lancer l'action composite locale uses: ./.github/actions/my-action with: node-version: '20'Structure du repo :
Répertoiremy-repo/
Répertoire.github/
Répertoireactions/
Répertoiremy-action/
- action.yml
Répertoireworkflows/
- ci.yml
Répertoiresrc/
- …
Action externe (autre repository)
Section intitulée « Action externe (autre repository) »Une action hébergée dans un autre dépôt s'appelle avec la notation
propriétaire/dépôt. C'est du code tiers : il s'épingle par SHA de
commit, exactement comme une action du Marketplace.
- name: Installer Node.js via une action externe uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: '20'Épingler les actions appelées
Section intitulée « Épingler les actions appelées »Une action externe se référence par un tag, une branche ou un SHA de commit.
Une seule de ces formes est sûre : le SHA. Un tag (@v4) comme une branche
(@main) sont des références mobiles — le mainteneur peut les déplacer vers
n'importe quel code, y compris malveillant, sans que votre workflow change
d'une ligne.
# ❌ Référence mobile : le mainteneur peut la déplacer- uses: actions/setup-node@v4
# ✅ SHA de commit épinglé, version en commentaire- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0L'épinglage et son outillage (Dependabot, pinact) sont détaillés dans
Épingler les actions par SHA.
Exemples concrets
Section intitulée « Exemples concrets »Trois actions composites tirées de cas réels : préparer un environnement Node.js, lancer une passe de sécurité, déployer sur Kubernetes. Chacune illustre un usage différent des inputs et des outputs.
Action de setup Node.js avec cache
Section intitulée « Action de setup Node.js avec cache »Cette action factorise l'installation de Node.js et la mise en cache des dépendances npm — la séquence que l'on recopie dans presque tous les pipelines JavaScript.
name: 'Setup Node.js with Cache'description: 'Configure Node.js avec cache npm optimisé'
inputs: node-version: description: 'Version de Node.js' default: '20' working-directory: description: 'Répertoire de travail' default: '.'
outputs: cache-hit: description: 'Cache hit' value: ${{ steps.cache.outputs.cache-hit }}
runs: using: 'composite' steps: - name: Installer Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }}
- name: Récupérer le répertoire de cache npm id: npm-cache-dir run: echo "dir=$(npm config get cache)" >> "$GITHUB_OUTPUT" shell: bash
- name: Mettre npm en cache id: cache uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ${{ steps.npm-cache-dir.outputs.dir }} key: npm-${{ runner.os }}-${{ hashFiles(format('{0}/package-lock.json', inputs.working-directory)) }} restore-keys: npm-${{ runner.os }}-
- name: Installer les dépendances working-directory: ${{ inputs.working-directory }} run: npm ci shell: bashAction de scan de sécurité
Section intitulée « Action de scan de sécurité »Cette action regroupe deux scanners — Trivy pour les vulnérabilités,
Gitleaks pour les secrets — derrière un seul uses:. L'input scan-type
permet d'activer la passe lente uniquement quand c'est utile.
name: 'Security Scan'description: 'Lance les scans de sécurité (Trivy + Gitleaks)'
inputs: scan-type: description: 'Type de scan (full, quick)' default: 'quick' fail-on-high: description: 'Échec si vulnérabilités high+' default: 'true'
outputs: vulnerabilities-found: description: 'Vulnérabilités trouvées' value: ${{ steps.result.outputs.found }}
runs: using: 'composite' steps: - name: Scanner les vulnérabilités avec Trivy id: trivy uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # v0.28.0 with: scan-type: 'fs' scan-ref: '.' severity: ${{ inputs.fail-on-high == 'true' && 'HIGH,CRITICAL' || 'CRITICAL' }} exit-code: ${{ inputs.fail-on-high == 'true' && '1' || '0' }} continue-on-error: true
- name: Évaluer le résultat de Trivy id: result env: TRIVY_OUTCOME: ${{ steps.trivy.outcome }} run: | if [ "$TRIVY_OUTCOME" = "failure" ]; then echo "found=true" >> "$GITHUB_OUTPUT" else echo "found=false" >> "$GITHUB_OUTPUT" fi shell: bash
- name: Détecter les secrets avec Gitleaks uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.9 if: inputs.scan-type == 'full' env: GITHUB_TOKEN: ${{ github.token }}Action de déploiement
Section intitulée « Action de déploiement »Cette action déploie une image sur Kubernetes. Elle manipule un secret (le kubeconfig) et des paramètres variables — l'occasion de montrer comment passer ces valeurs sans les exposer.
name: 'Deploy to Kubernetes'description: "Déploie l'application sur Kubernetes"
inputs: environment: description: 'Environnement (staging, production)' required: true image: description: 'Image Docker à déployer' required: true kubeconfig: description: 'Kubeconfig encodé en base64' required: true
outputs: deployment-url: description: 'URL du déploiement' value: ${{ steps.deploy.outputs.url }}
runs: using: 'composite' steps: - name: Installer kubectl uses: azure/setup-kubectl@3e0aec4d80787158d308d7b364cb1b702e7feb7f # v4.0.0
- name: Configurer le kubeconfig env: KUBECONFIG_B64: ${{ inputs.kubeconfig }} run: | mkdir -p ~/.kube printf '%s' "$KUBECONFIG_B64" | base64 -d > ~/.kube/config chmod 600 ~/.kube/config shell: bash
- name: Déployer sur Kubernetes id: deploy env: DEPLOY_IMAGE: ${{ inputs.image }} DEPLOY_ENV: ${{ inputs.environment }} run: | kubectl set image deployment/app "app=$DEPLOY_IMAGE" -n "$DEPLOY_ENV" kubectl rollout status deployment/app -n "$DEPLOY_ENV" URL=$(kubectl get ingress -n "$DEPLOY_ENV" \ -o jsonpath='{.items[0].spec.rules[0].host}') echo "url=https://$URL" >> "$GITHUB_OUTPUT" shell: bashOrganisation des actions
Section intitulée « Organisation des actions »Au-delà de deux ou trois actions, leur rangement compte. Deux approches dominent : un dépôt dédié qui regroupe toutes les actions de l'organisation, ou des actions locales au dépôt qu'elles servent.
Monorepo d'actions
Section intitulée « Monorepo d'actions »Un dépôt unique — souvent nommé actions — héberge toutes les actions
partagées de l'organisation. Chaque action occupe un sous-dossier avec son
action.yml.
Répertoireactions/
Répertoiresetup-node/
- action.yml
Répertoiresecurity-scan/
- action.yml
Répertoiredeploy/
- action.yml
Répertoirenotify/
- action.yml
Une action située dans un sous-dossier s'adresse par org/dépôt/sous-dossier.
Même au sein de votre organisation, épinglez chaque appel par SHA : un dépôt
interne peut être compromis comme un autre.
jobs: ci: runs-on: ubuntu-24.04 steps: - name: Préparer Node.js uses: org/actions/setup-node@2b9f4c7e1a8d3f6b0c5e9a2d7f4b1c8e3a6d0f9b # v1.4.0
- name: Scanner la sécurité uses: org/actions/security-scan@6e3a1f8c4b7d2a9e0c5f8b3d6a1e4c7f9b2d5a0e # v1.4.0Actions dans le même repo
Section intitulée « Actions dans le même repo »Quand une action ne sert qu'à un seul projet, inutile de la sortir :
placez-la dans .github/actions/ du dépôt. Elle est versionnée avec le code et
s'appelle par chemin relatif.
Répertoiremy-app/
Répertoire.github/
Répertoireactions/
Répertoiresetup/
- action.yml
Répertoiredeploy/
- action.yml
Répertoireworkflows/
- ci.yml
Répertoiresrc/
- …
Publier une action
Section intitulée « Publier une action »Une action composite utile à d'autres équipes peut être publiée sur le GitHub Marketplace, l'annuaire public des actions. La publication impose quelques métadonnées et une discipline de versionnement.
Sur GitHub Marketplace
Section intitulée « Sur GitHub Marketplace »La mise en ligne se fait depuis l'interface GitHub, une fois l'action prête.
-
Créez un repository public pour l'action.
-
Ajoutez un bloc
brandingdansaction.yml:name: 'My Action'description: "Description de l'action"branding:icon: 'check-circle'color: 'green'# inputs, outputs et runs : voir les sections précédentes -
Créez une release avec un tag semver (
v1.0.0). -
Publiez sur le Marketplace depuis la page Releases.
Versioning
Section intitulée « Versioning »Maintenez deux niveaux de tags : un tag exact et immuable par release
(v1.2.3), et un tag majeur mobile (v1) repointé à chaque release
compatible. Les consommateurs choisissent ainsi entre stabilité absolue et
mises à jour automatiques.
# Tag de version spécifiquegit tag v1.2.3git push origin v1.2.3
# Tag majeur (pointe vers la dernière v1.x.x)git tag -f v1git push -f origin v1Côté consommateur, on n'utilise ni l'un ni l'autre directement : on épingle le SHA du commit derrière le tag visé, version en commentaire (voir Épingler les actions appelées).
Bonnes pratiques
Section intitulée « Bonnes pratiques »Quelques réflexes rendent vos actions composites fiables et faciles à consommer pour les autres équipes.
Donner un id aux steps qui produisent un output
Section intitulée « Donner un id aux steps qui produisent un output »Un output d'action référence la sortie d'un step interne : ce step doit porter
un id, sinon la valeur est introuvable.
- name: Construire le projet id: build # Nécessaire pour référencer les outputs run: echo "version=1.0" >> "$GITHUB_OUTPUT" shell: bash
outputs: version: value: ${{ steps.build.outputs.version }}Documenter chaque input et output
Section intitulée « Documenter chaque input et output »Une description précise évite aux équipes consommatrices d'aller lire le
code de l'action pour comprendre ce qu'elles peuvent passer.
inputs: environment: description: | L'environnement cible pour le déploiement. Valeurs supportées : staging, production. Les permissions requises varient selon l'environnement. required: truePrévoir les chemins d'échec
Section intitulée « Prévoir les chemins d'échec »continue-on-error sur un step critique permet d'enchaîner une étape de
repli plutôt que d'interrompre brutalement le job.
- name: Étape critique id: critical run: ./critical-script.sh shell: bash continue-on-error: true
- name: Gérer l'échec if: steps.critical.outcome == 'failure' run: ./fallback.sh shell: bashÀ retenir
Section intitulée « À retenir »- Une action composite est un step réutilisable décrit dans un
action.ymlavecruns.using: composite. - Chaque step
run:d'une action composite doit déclarer unshell:; les stepsuses:n'en prennent pas. - Les inputs sont toujours des strings ; les outputs référencent la sortie d'un step interne identifié par un
id. - Une action locale s'appelle par chemin relatif (
./...) ; une action externe est du code tiers épinglé par SHA. - Secrets et paramètres passent par
env:, jamais interpolés en${{ }}dans un blocrun:— sinon injection de commande et fuite dans les logs. - Côté publication : un
action.ymlavecbranding, un tag exact immuable doublé d'un tag majeur mobile ; côté consommation, on épingle le SHA.
Prochaines étapes
Section intitulée « Prochaines étapes »Pour la référence complète, consultez la documentation officielle sur les actions composites.