Aller au contenu

Testeur d’expressions GitHub Actions · CI/CD

Testez vos conditions if: et vos déclencheurs avant de pousser.

Évaluez les expressions ${{ }} contre un contexte fictif éditable, attrapez le if: qui est silencieusement toujours vrai, et simulez quels jobs s’exécutent pour un push, une PR ou un tag — instantanément, dans votre navigateur. Fini la boucle commit-push-pray.

S’exécute dans votre navigateur — rien de ce que vous collez ne quitte cette page. Comment nous le prouvons

S’exécute dans votre navigateur Sans inscription Sémantique GitHub exacte Mis à jour le 29 juil. 2026

Espace d’essai des expressions et des déclencheurs GitHub Actions

if-condition
context (editable mock)

Results update as you type — press ⌘/Ctrl + Enter to run now. Press Esc to release the editor. Your input never leaves the browser.

Result

Load an example or write an if: condition, then Evaluate to see whether it is true, the returned value, and a token-by-token breakdown.

.github/workflows/ci.yml
Event scenario

Results update as you type — press ⌘/Ctrl + Enter to run now. Press Esc to release the editor. Nothing is uploaded.

Decision

Load an example or paste a workflow, set an event, then Simulate to see which jobs run or are skipped — and why.

100% in your browser · no signup

La faille

Arrêtez de déboguer en poussant.

Tout le monde l’a déjà fait : ajouter un if: à une étape, pousser, le voir s’exécuter alors qu’il ne le devrait pas, ajuster, repousser — vingt commits bidons plus tard, vous découvrez que la condition était une chaîne littérale depuis le début. GitHub n’évalue que ce qui se trouve à l’intérieur des ${{ }} ; tout ce qui est à l’extérieur est du texte littéral, et une chaîne non vide est toujours vraie.

Le côté déclencheur est tout aussi opaque : un workflow qui ne s’exécute pas ne produit aucune sortie du tout — la décision se prend dans le filtrage d’événements de GitHub avant qu’aucun runner ne démarre. Les filtres de branche et de chemin se combinent avec AND, les pushes de tag sont exclus lorsque vous définissez branches mais pas tags, et ** se comporte différemment de *.

Cet outil évalue les deux — une seule expression, ou un workflow entier contre un événement — en utilisant les règles exactes de GitHub, pour que vous obteniez la réponse dans le navigateur. Voir la documentation des expressions et des déclencheurs de workflow de GitHub, ainsi que le ticket du piège du « toujours vrai ».

Accédez à l’aide-mémoire ou à l’espace d’essai en direct ci-dessus.

Le pipeline

Comment ça marche.

Cinq étapes déterministes s’exécutent de bout en bout à chaque évaluation — entièrement dans votre onglet de navigateur, à chaque fois.

  1. Choisissez un onglet.

    Évaluez un seul if: / une seule expression, ou passez au simulateur de déclencheurs pour tester un workflow entier contre un événement.

  2. Définissez le contexte.

    Modifiez le JSON fictif github/env/matrix/steps/needs (ou décrivez l’événement : type, ref, fichiers modifiés) pour qu’il corresponde à l’exécution qui vous intéresse.

  3. Analysez avec la grammaire de GitHub.

    L’expression est tokenisée et analysée avec la même précédence d’opérateurs et le même ensemble de fonctions que le runner utilise — sans appel externe, sans réseau.

  4. Appliquez la sémantique exacte.

    La coercition, l’égalité insensible à la casse, les && / || qui renvoient un opérande, les filtres de glob et la règle ET branche+chemin sont reproduits contre un corpus de conformité versionné.

  5. Affichez le verdict + le pourquoi.

    Vous obtenez le résultat vrai/faux et la valeur renvoyée, l’avertissement du piège « toujours vrai » là où il s’applique, et un tableau RUNS/SKIPPED par job avec la raison décisive.

Aide-mémoire

Les règles qui font trébucher.

Le piège, les surprises de la coercition, les fonctions et les globs de déclencheurs — chacun avec un exemple exécutable que vous pouvez coller dans l’espace d’essai.

Le piège du « toujours vrai »

Le plus courant

GitHub n’évalue que ce qui se trouve à l’intérieur des ${{ }}. Les opérateurs laissés à l’extérieur deviennent du texte littéral après substitution — une chaîne non vide, qui est toujours vraie. L’évaluateur le signale (actions/runner#1173).

Toujours vrai

.github/workflows/ci.yml
# ALWAYS TRUE — operators sit OUTSIDE ${{ }}
jobs:
  deploy:
    if: ${{ github.ref }} == 'refs/heads/main'
    # after substitution this is the literal string
    #   refs/heads/main == 'refs/heads/main'
    # a non-empty string => truthy => runs on EVERY branch

Corrigé

.github/workflows/ci.yml
# CORRECT — wrap the WHOLE condition in one ${{ }}
jobs:
  deploy:
    if: ${{ github.ref == 'refs/heads/main' }}
    # now GitHub evaluates the comparison, not a literal string

Opérateurs & coercition

YAML
# GitHub's coercion is JS-like and case-insensitive:
${{ 'ABC' == 'abc' }}      # true  (strings compare case-insensitively)
${{ null == 0 }}           # true  (both coerce to the number 0)
${{ '' || 'fallback' }}    # 'fallback'  (|| returns an OPERAND, not a bool)
${{ github.x && 'yes' }}   # 'yes' when github.x is truthy
${{ 'true' == true }}      # false (string 'true' coerces to NaN)

Fonctions

YAML
${{ contains('refs/heads/main', 'main') }}     # true (substring, case-insensitive)
${{ startsWith(github.ref, 'refs/tags/') }}    # is this a tag push?
${{ format('{0}-{1}', 'app', github.sha) }}    # 'app-d6cd1e2…'
${{ fromJSON('["a","b"]') }}                   # a real array
${{ always() }}  ${{ success() }}  ${{ failure() }}  # status checks

Déclencheurs, branches & paths

YAML
on:
  push:
    branches: [main]      # AND
    paths: ['src/**']     # — both must match to trigger
# feature/* matches feature/a but NOT feature/a/b
# feature/** matches both (** crosses '/')

Fidélité & portée

Il s’agit d’un espace d’essai sémantique, pas d’un runner. Il reproduit le comportement documenté des expressions et des déclencheurs de GitHub contre un corpus de conformité versionné (affiché sous l’étiquette semantics: dans l’outil), mais il n’exécute pas les jobs, ne récupère pas le contenu en direct des actions référencées, et ne calcule pas un véritable hashFiles() — cela nécessite des fichiers que seul le runner peut voir, il est donc affiché comme un espace réservé. La charge utile complète de github.event est modélisée comme un contexte fictif éditable que vous contrôlez. Considérez un résultat propre comme une forte confiance avant le push, et associez-le tout de même à actionlint pour la syntaxe.

FAQ

Vos questions, nos réponses.

Appuyez sur une question pour afficher la réponse.

Il comporte deux onglets. L’évaluateur d’expressions exécute vos expressions ${{ }} contre un contexte fictif éditable (github, env, matrix, steps, needs) en utilisant la sémantique exacte de GitHub Actions — les opérateurs, l’égalité de chaînes == insensible à la casse, les règles de coercition à la manière de JavaScript, et les fonctions documentées comme contains, startsWith, endsWith, format, join, toJSON, fromJSON, success(), failure(), always() et cancelled(). Le simulateur de déclencheurs vous permet de décrire un événement push, pull_request ou tag et affiche un tableau RUN ou SKIPPED par job qui explique exactement quel filtre on: branches, tags, paths ou paths-ignore a décidé du résultat. Ensemble, ils répondent aux questions « ce if: sera-t-il vrai ? » et « ce workflow va-t-il seulement se déclencher ? » avant de pousser.

Presque toujours parce que le if: contient du texte littéral en dehors des ${{ }} — par exemple if: "${{ github.event_name }}" == 'push' ou if: always-deploy. GitHub n’évalue pas toute la ligne comme une seule expression ; les caractères littéraux font de la condition une chaîne non vide, et une chaîne non vide est vraie (truthy), donc l’étape s’exécute à chaque fois. Enveloppez la condition entière dans un seul ${{ }} (par ex. if: ${{ github.event_name == 'push' }}) et l’évaluateur d’expressions signalera le piège (actions/runner#1173) et vous montrera le résultat corrigé, vrai ou faux.

Collez l’expression dans l’onglet de l’évaluateur d’expressions et modifiez le contexte fictif github/env/matrix/steps/needs pour correspondre à l’exécution qui vous intéresse — aucun commit, aucun push, aucune attente d’un runner. Vous obtenez la valeur évaluée instantanément, ainsi qu’un avertissement si la syntaxe se transformait silencieusement en toujours-vrai. C’est le moyen le plus rapide de vérifier une condition if: ou une interpolation ${{ }} avant qu’elle n’atteigne GitHub Actions.

Ce sont des fonctions de contrôle de statut que vous utilisez dans un if:. success() n’est vrai que lorsque chaque étape ou job requis précédent a réussi (c’est la valeur par défaut implicite dès que vous n’écrivez aucun if:), failure() est vrai lorsque l’un d’eux a échoué, cancelled() est vrai lorsque le workflow a été annulé, et always() force l’étape à s’exécuter quel que soit le statut précédent — y compris en cas d’annulation. Le piège est qu’écrire un if: personnalisé supprime la garde implicite success(), donc if: env.DEPLOY == 'true' s’exécutera même après l’échec d’une étape précédente, sauf si vous ajoutez && success(). L’évaluateur vous permet de basculer le statut fictif et de voir chaque fonction se résoudre.

Les causes habituelles sont une ref qui ne correspond pas à votre glob on: branches ou tags, le fichier de workflow qui n’existe pas encore sur la branche cible, ou un filtre paths qui exclut chaque fichier modifié. Une cause subtile : lorsque vous définissez à la fois branches et paths sous le même événement, ils se combinent par ET — l’événement doit correspondre aux deux, pas à l’un ou l’autre. Décrivez votre événement dans le simulateur de déclencheurs et il rejoue les filtres on: et vous indique la raison décisive pour chaque job.

Au sein d’un même événement, branches (ou tags) et paths sont combinés par ET : un push doit être sur une branche correspondante et toucher un chemin correspondant pour que le workflow s’exécute. À l’intérieur d’un seul filtre, les motifs sont combinés par OU — un seul glob de branche ou un seul chemin modifié qui correspond suffit. Le simulateur de déclencheurs rend cela explicite en affichant séparément la décision de branche et la décision de chemin, puis le verdict combiné RUN ou SKIPPED.

* correspond à n’importe quels caractères sauf le séparateur de chemin /, tandis que ** correspond à travers les séparateurs y compris /, donc feature/** correspond à feature/a/b mais feature/* ne correspond qu’à feature/a. Le moteur de glob honore aussi +, ?, la négation !, et l’échappement \ de la même manière que GitHub. Le simulateur de déclencheurs utilise une réimplémentation fidèle de ces règles pour que vous puissiez tester un motif tel que 'release/**' ou '!**/*.md' contre une ref ou une liste de fichiers réelle et voir ce qui correspond.

Dans le simulateur de déclencheurs, vous fournissez le type d’événement, le nom de la ref et la liste des fichiers modifiés ; l’outil évalue alors les filtres on: de chaque job et tout if: au niveau du job contre cet événement simulé, puis affiche une ligne RUN ou SKIPPED avec la raison décisive — « la branche correspondait mais pas le chemin », « pas de filtre paths », « if: évalué à faux », et ainsi de suite. Il reproduit l’ordre de décision de GitHub Actions plutôt que de deviner, de sorte que le verdict correspond à ce que ferait le véritable runner pour cet événement.

actionlint est un linter statique pour la syntaxe des workflows et le typage des expressions, et act exécute réellement vos jobs dans des conteneurs Docker locaux — tous deux sont excellents et valent la peine d’être utilisés. Cet outil ne fait ni l’un ni l’autre : il n’exécute pas vos étapes et ce n’est pas un vérificateur de types. C’est un espace d’essai sémantique qui évalue une seule expression ${{ }} ou simule le filtrage des déclencheurs contre un contexte que vous contrôlez, dans le navigateur, pour que vous puissiez raisonner sur pourquoi un if: est vrai ou pourquoi un workflow s’est déclenché ou non, sans lancer de conteneurs ni pousser de commits.

Tout ce qui dépend de l’état réel du runner. hashFiles() a besoin des fichiers réels sur le disque, donc l’évaluateur le traite comme un espace réservé opaque plutôt que de calculer un véritable hachage ; la charge utile complète du webhook est bien plus volumineuse que le contexte github fictif que nous exposons ; et le contenu en direct des actions, les secrets et les étiquettes de runner ne sont pas résolus. Considérez un résultat propre comme une forte confiance avant le push concernant la logique des expressions et des déclencheurs — et non comme une garantie sur le hachage des fichiers ou la charge utile complète de l’événement.

Non. Les deux onglets s’exécutent à 100 % côté client. Les expressions, le contexte fictif, les filtres de workflow et les charges utiles d’événements que vous saisissez sont évalués à l’intérieur de votre onglet de navigateur — rien n’est envoyé, il n’y a pas de compte, et il n’y a pas d’inscription. Vous pouvez coller en toute sécurité des workflows internes ou propriétaires, y compris des noms de secrets, des étiquettes de runner privées et de vraies listes de branches et de chemins.

Non. Il s’agit d’un outil communautaire indépendant qui n’est ni affilié à GitHub, Inc., ni approuvé ou sponsorisé par celle-ci. « GitHub » et « GitHub Actions » ne sont utilisés ici qu’à titre descriptif, pour identifier le format d’expression et de déclencheur de workflow que l’outil évalue.

More free, private DevOps tools.

Le testeur d’expressions GitHub Actions est l’un des outils de OpsCanopy — une canopée grandissante de validateurs, de convertisseurs et de testeurs basés sur le navigateur qui ne touchent jamais à un serveur.

39 outils gratuits, tous utilisables hors ligne — opscanopy.com fonctionne sans inscription et sans rien téléverser.

Outils connexes : le validateur GitHub Actions et AlertLint, le testeur de règles Loki. Parcourez le répertoire complet des outils.

Non affilié à GitHub, Inc., ni approuvé ou sponsorisé par celle-ci. GitHub et GitHub Actions sont des marques de GitHub, Inc., utilisées ici uniquement à titre descriptif pour identifier le format d’expression et de déclencheur de workflow que cet outil évalue.