Aller au contenu
CI/CD & Automatisation medium

actionlint : valider vos workflows GitHub Actions

35 min de lecture

Vous avez passé une heure à écrire un workflow GitHub Actions. Vous le poussez, et… échec immédiat. Une erreur de syntaxe. Un nom d'action mal orthographié. Une référence à un secret qui n'existe pas. Ces erreurs auraient pu être détectées avant le push.

actionlint est un linter statique qui analyse vos fichiers de workflow et détecte les erreurs avant qu'elles ne se produisent sur GitHub.

  • Installer actionlint sur Linux, macOS et Windows
  • Valider vos workflows et interpréter les messages d'erreur
  • Intégrer actionlint à VS Code et à un hook pre-commit
  • Ajouter un workflow de lint qui valide les pull requests
  • Configurer les règles et les variables propres à votre organisation

actionlint est un outil en ligne de commande qui analyse statiquement vos workflows GitHub Actions. Il vérifie la syntaxe YAML, les références aux actions, les expressions, les permissions, et bien plus, le tout sans exécuter le workflow.

Concrètement, actionlint détecte :

  • Les erreurs de syntaxe YAML (indentation, structure)
  • Les propriétés inconnues ou mal orthographiées, y compris les entrées with: invalides des actions populaires
  • Les références invalides à des contextes (${{ github.sha }})
  • Les actions inexistantes ou mal référencées
  • Les permissions déclarées avec un scope ou une valeur inconnus
  • Les problèmes de type dans les expressions
  • Les patterns glob invalides
  • Et beaucoup d'autres catégories, dont l'injection de script et les identifiants codés en dur

Le tableau ci-dessous oppose la boucle de correction habituelle à celle qu'apporte actionlint. La ligne qui pèse le plus est la deuxième : sans linter, chaque coquille dans un workflow coûte un aller-retour complet avec le runner GitHub, plusieurs dizaines de secondes au mieux, et ce cycle se répète tant que l'erreur n'est pas trouvée. actionlint ramène cette boucle à une validation locale en moins d'une seconde, avant même le commit.

Sans actionlintAvec actionlint
Erreur découverte après le pushErreur détectée avant le commit
Attente du runner (30s à plusieurs min)Validation instantanée (< 1s)
Historique pollué de runs échouésRuns plus propres
Debug par essai-erreurMessages d'erreur clairs et précis

Avec Homebrew :

Fenêtre de terminal
brew install actionlint

Vérification de l'installation :

Fenêtre de terminal
actionlint --version

La première ligne affiche la version installée (ex: 1.7.12), suivie de deux lignes sur la méthode d'installation et le compilateur Go utilisé.

À la racine de votre projet :

Fenêtre de terminal
actionlint

actionlint trouve automatiquement les fichiers dans .github/workflows/ et les analyse tous.

Exemple de sortie : chaque erreur affiche le fichier, la ligne, la colonne, puis la catégorie entre crochets ([action], [expression], etc.). C'est la catégorie qui vous dit à quelle règle vous avez affaire :

.github/workflows/ci.yml:11:11: input "node-verion" is not defined in action "actions/setup-node@v4". available inputs are "always-auth", "architecture", "cache", ..., "node-version", ... [action]
|
11 | node-verion: 20
| ^~~~~~~~~~~~

Ici, actionlint reconnaît actions/setup-node, connaît ses entrées valides et repère la faute de frappe node-verion au lieu de node-version. Sans ce linter, l'erreur ne se manifesterait qu'à l'exécution sur GitHub.

Passer un chemin en argument restreint l'analyse à ce seul fichier, ce qui est pratique quand vous travaillez sur un workflow précis dans un dépôt qui en compte plusieurs. actionlint attend toujours de trouver la racine du dépôt Git au-dessus du fichier, car il détecte le projet par le dossier .github/workflows/ : lancé hors d'un dépôt, il s'arrête avec no project was found.

Fenêtre de terminal
actionlint .github/workflows/ci.yml

Utile pour les scripts ou les pipelines :

Fenêtre de terminal
cat .github/workflows/ci.yml | actionlint -

actionlint fournit des messages précis avec le numéro de ligne, la colonne, et une explication. Voici les catégories principales :

.github/workflows/ci.yml:1:1: "on" section is missing in workflow [syntax-check]

Le workflow n'a pas de section on: qui définit quand il se déclenche.

.github/workflows/ci.yml:11:11: input "node-verion" is not defined in action "actions/setup-node@v4" [action]

Faute de frappe : node-verion au lieu de node-version. La catégorie [action] indique qu'actionlint a validé la clé with: contre la liste réelle des entrées de l'action.

.github/workflows/ci.yml:5:14: label "123" is unknown. available labels are "windows-latest", ..., "ubuntu-24.04", ... [runner-label]

La valeur de runs-on ne correspond à aucun label de runner connu. Un label personnalisé de runner self-hosted se déclare dans le fichier actionlint.yaml pour éviter ce faux positif.

.github/workflows/ci.yml:7:24: "github.event.issue.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable [expression]

actionlint détecte l'injection de script : une donnée contrôlable par un tiers (github.event.issue.title) interpolée directement dans un run:. La parade est de la passer par un bloc env:. La catégorie [credentials] couvre de son côté les mots de passe de conteneur écrits en clair. actionlint ne signale en revanche pas l'absence d'épinglage par SHA : voir Épinglage SHA et les scanners dédiés.

.github/workflows/ci.yml:9:18: property "secret" is not defined in object type {action: string; ...} [expression]

Le contexte github.secret n'existe pas (c'est secrets.X qu'il faut utiliser).

Le format par défaut est fait pour être lu par un humain dans le terminal. Les trois autres servent à brancher actionlint sur un autre outil : json pour un script qui parse les résultats, sarif pour remonter les alertes dans l'onglet Security de GitHub via Code Scanning, et le gabarit ::error pour que GitHub Actions surligne l'erreur directement dans l'interface du run. Choisissez le format selon le consommateur, pas selon votre préférence.

Fenêtre de terminal
# Format par défaut (lisible)
actionlint
# Format JSON (pour intégration CI)
actionlint -format json
# Format SARIF (pour GitHub Code Scanning)
actionlint -format sarif > results.sarif
# Format compatible avec les problèmes GitHub Actions
actionlint -format '{{range $err := .}}::error file={{$err.Filepath}},line={{$err.Line}},col={{$err.Column}}::{{$err.Message}}{{end}}'

Le drapeau -ignore prend une expression régulière comparée au message d'erreur, pas un identifiant de règle. C'est utile pour taire un avertissement shellcheck connu et assumé : les codes SC2086, SC2129 viennent de l'intégration shellcheck qui analyse vos blocs run:.

Fenêtre de terminal
# Taire un avertissement shellcheck précis (mots non quotés)
actionlint -ignore 'SC2086'
# En taire plusieurs
actionlint -ignore 'SC2086' -ignore 'SC2129'

Vous pouvez aussi neutraliser une catégorie sur un bloc précis, avec un commentaire à l'intérieur du fichier de workflow :

# actionlint: ignore=expression
- name: Step with ignored warning
run: echo "${{ github.secret }}"
Fenêtre de terminal
actionlint -verbose

Affiche les fichiers analysés et le temps d'exécution.

Vérification des actions populaires, sans réseau

Section intitulée « Vérification des actions populaires, sans réseau »

actionlint valide les entrées with: d'une centaine d'actions populaires (actions/checkout, actions/setup-node, actions/cache, etc.) grâce à une base de données embarquée dans le binaire au moment de sa compilation. Il n'a donc jamais besoin d'accéder au réseau pour ces vérifications, ce qui le rend utilisable hors ligne et dans un runner isolé. Cette base ne couvre que la forme en version majeure (actions/checkout@v4) ; une version figée au patch (@v4.0.1) ou le suivi de @main ne sont pas validés.

actionlint s'intègre avec les éditeurs pour afficher les erreurs en temps réel.

  1. Installez l'extension GitHub Actions de GitHub
  2. Elle utilise automatiquement actionlint s'il est installé

Ou utilisez l'extension dédiée actionlint :

  1. Ouvrez VS Code
  2. Extensions → Recherchez "actionlint"
  3. Installez l'extension

Les erreurs apparaissent directement dans l'éditeur avec des soulignements.

Dans .vscode/settings.json :

{
"actionlint.executable": "/usr/local/bin/actionlint",
"yaml.schemas": {
"https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml"
}
}

Créez un workflow qui valide vos autres workflows :

name: Lint Workflows
on:
push:
paths:
- '.github/workflows/**'
pull_request:
paths:
- '.github/workflows/**'
# Aucun droit par défaut : le job demande le minimum
permissions: {}
jobs:
actionlint:
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install actionlint
env:
ACTIONLINT_VERSION: 1.7.12
run: |
BASE="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}"
curl -sSLO "${BASE}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
curl -sSLO "${BASE}/actionlint_${ACTIONLINT_VERSION}_checksums.txt"
sha256sum --ignore-missing --check "actionlint_${ACTIONLINT_VERSION}_checksums.txt"
tar -xzf "actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" actionlint
sudo install -m 0755 actionlint /usr/local/bin/actionlint
- name: Validate workflows
run: actionlint -color
name: Lint Workflows
on:
pull_request:
paths:
- '.github/workflows/**'
# Aucun droit par défaut : le job demande le minimum
permissions: {}
jobs:
actionlint:
runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: write # Pour que reviewdog commente la PR
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: reviewdog/action-actionlint@50842263c20a7c46bd0065b9e624d3c569db061e # v1.73.0
with:
reporter: github-pr-review

Cette action utilise reviewdog pour commenter directement les PRs avec les erreurs détectées.

Validez avant chaque commit avec pre-commit :

.pre-commit-config.yaml
repos:
- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
hooks:
- id: actionlint

Puis :

Fenêtre de terminal
pip install pre-commit
pre-commit install

actionlint vérifie de nombreuses règles. Le mot entre crochets à la fin de chaque message d'erreur est la catégorie de la règle déclenchée, et c'est cette catégorie que le drapeau -ignore ou le commentaire # actionlint: ignore= ciblent. Les tableaux ci-dessous regroupent les catégories les plus fréquentes par thème.

Ce sont les erreurs qu'actionlint attrape en premier, avant même de comprendre la logique du workflow : un YAML mal indenté, une expression ${{ }} mal formée, ou une commande de workflow que GitHub a retirée. La catégorie deprecated-commands est utile lors des migrations, elle repère les vieux ::set-output:: que GitHub ne supporte plus.

CatégorieDescription
syntax-checkErreurs YAML de base et clés manquantes
expressionExpressions ${{ }} invalides ou types incompatibles
deprecated-commandsCommandes de workflow obsolètes (set-output, save-state)

Ces trois catégories couvrent ce qu'actionlint sait détecter côté sécurité sans recouper les scanners de supply chain. L'injection de script n'a pas de catégorie propre : elle est reportée sous expression, avec un message explicite invitant à passer la donnée par env:. Rappel : l'épinglage par SHA n'en fait pas partie.

CatégorieDescription
permissionsScope ou valeur de permissions: inconnus
credentialsMot de passe de conteneur écrit en clair
expressionDonnée non fiable interpolée dans run: (injection de script)

actionlint connaît la structure attendue des événements et des dépendances entre jobs, ce qui lui permet de repérer un needs: qui pointe vers un job inexistant ou un nom d'événement mal orthographié. La catégorie action couvre les entrées with: invalides des actions populaires vues plus haut.

CatégorieDescription
actionEntrées with: invalides, action obsolète (runner trop ancien)
eventsÉvénements de déclenchement invalides
job-needsDépendances needs: incorrectes

Ces catégories vérifient que les valeurs correspondent à ce que GitHub attend réellement : un label de runner qui existe, un motif glob valide dans un filtre paths:, une matrice bien formée. Le contrôle runner-label est celui qui déclenche le plus de faux positifs sur les runners self-hosted, d'où l'intérêt du fichier de configuration décrit plus bas.

CatégorieDescription
runner-labelLabels de runner inconnus
globPatterns glob invalides dans paths:
matrixErreurs dans la définition de matrice

Pour une validation complète de vos workflows :

  1. actionlint : validation statique (syntaxe, types, références)
  2. act : exécution locale (logique, scripts, comportement)
#!/usr/bin/env bash
# Script de validation complet
set -euo pipefail
echo "Validation statique avec actionlint..."
actionlint
echo "Test d'execution a blanc avec act..."
act -n
echo "Workflows valides."

Avec set -euo pipefail, le script s'arrête à la première commande qui échoue : inutile de tester $? après chaque étape, un actionlint ou un act -n en erreur interrompt tout et renvoie un code non nul à la CI.

Voir le guide act pour les tests d'exécution locale.

Créez un fichier .github/actionlint.yaml :

# Configuration actionlint
self-hosted-runner:
labels:
- my-runner
- gpu-runner
config-variables:
- MY_ORG_VAR
- DEPLOYMENT_ENV
paths:
ignore:
- '.github/workflows/deprecated-*.yml'

Si vous utilisez des variables au niveau organisation (${{ vars.ORG_VAR }}), déclarez-les pour éviter les faux positifs :

.github/actionlint.yaml
config-variables:
- ORG_CONFIG
- COMPANY_NAME

Symptôme : le terminal ne trouve pas la commande.

Solutions :

Fenêtre de terminal
# Vérifier l'installation
which actionlint
# Si installé via Go, ajouter au PATH
export PATH="$PATH:$(go env GOPATH)/bin"

Symptôme : actionlint signale des actions locales comme inexistantes.

Solution : les actions locales (./actions/my-action) sont validées si elles existent dans le repo. Vérifiez le chemin.

Symptôme : actionlint s'arrête avec no project was found in any parent directories.

Cause : actionlint cherche la racine d'un dépôt Git au-dessus des fichiers analysés. Lancé hors d'un dépôt (ou dans un dossier extrait sans .git), il ne trouve pas de projet.

Solution : lancez-le depuis la racine du dépôt, ou passez le workflow via stdin :

Fenêtre de terminal
cat ci.yml | actionlint -

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Validation instantanée

Détectez les erreurs en moins d'une seconde, avant le push.

50+ règles

Syntaxe, sécurité, types, références, tout est vérifié.

Intégration IDE

Erreurs affichées directement dans VS Code pendant l'écriture.

CI/CD ready

Formats JSON, SARIF, et intégration reviewdog pour les PRs.

Points clés :

  1. Installez actionlint et utilisez-le avant chaque push
  2. Intégrez-le dans VS Code pour une validation en temps réel
  3. Ajoutez un workflow de lint pour valider les PRs automatiquement
  4. Combinez avec act pour une validation complète
  5. Configurez les règles et variables spécifiques à votre organisation

Le dépôt actionlint documente toutes les règles, et le playground en ligne permet de tester un workflow dans le navigateur.

  • pre-commit : Le cadre qui déclenche actionlint et les autres linters à chaque commit, sur tout le dépôt et pas seulement sur les workflows.
  • MegaLinter : L'agrégateur qui étend la même logique de lint aux 100 langages du dépôt, là où actionlint s'arrête au YAML de GitHub Actions.
  • OpenSSF Scorecard : La note de posture qui mesure ce qu'un linter ne voit pas, de la protection de branche à la signature des releases.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn