Aller au contenu
CI/CD & Automatisation medium

Variables et secrets GitHub Actions

20 min de lecture

Les variables et secrets permettent de paramétrer vos workflows sans hardcoder les valeurs. Les variables servent aux configurations non sensibles ; les secrets aux données confidentielles — tokens, mots de passe, clés API.

  • Distinguer variables et secrets et savoir lequel utiliser
  • Définir des variables aux quatre niveaux : workflow, job, step, configuration
  • Consommer un secret sans jamais l'exposer dans les logs
  • Comprendre le GITHUB_TOKEN et les secrets d'environnement
  • Faire circuler des valeurs entre steps et entre jobs via les outputs
  • Éviter les pièges : injection par GITHUB_ENV, secret vide sur les forks

Ce guide suppose que vous savez déjà écrire un workflow. Sinon, commencez par Workflows GitHub Actions.

Variables et secrets se déclarent au même endroit et s'utilisent de façon similaire, mais leur traitement diffère radicalement : une variable s'affiche en clair, un secret est masqué par GitHub partout où il apparaîtrait. Le choix de l'un ou l'autre dépend donc de la sensibilité de la donnée.

AspectVariablesSecrets
VisibilitéEn clair dans les logsMasquées (***)
ModificationUI, API, CLIUI, API, CLI
Accès${{ vars.NAME }}${{ secrets.NAME }}
UsageConfig, feature flagsTokens, passwords, keys

Une variable d'environnement rend une valeur disponible pour les commandes shell d'un step. GitHub Actions permet de les déclarer à plusieurs niveaux de portée, et propose aussi des variables de configuration persistantes.

Les variables peuvent être définies à quatre niveaux, du plus large au plus précis. Le niveau le plus précis surcharge les niveaux supérieurs.

# 1. Niveau workflow
env:
GLOBAL_VAR: 'workflow-level'
jobs:
build:
# 2. Niveau job
env:
JOB_VAR: 'job-level'
runs-on: ubuntu-24.04
steps:
# 3. Niveau step
- name: Étape avec variables
env:
STEP_VAR: 'step-level'
run: |
echo "Global : $GLOBAL_VAR"
echo "Job : $JOB_VAR"
echo "Step : $STEP_VAR"

Les variables de configuration (vars) sont définies dans les paramètres du repository, de l'environnement ou de l'organisation. Elles persistent d'une exécution à l'autre — idéales pour une URL d'API ou un feature flag.

steps:
- name: Lire une variable de configuration
run: |
echo "Environnement : ${{ vars.ENVIRONMENT }}"
echo "URL API : ${{ vars.API_URL }}"
echo "Feature flag : ${{ vars.ENABLE_NEW_FEATURE }}"

Configurer une variable :

  1. Repository → Settings → Secrets and variables → Actions
  2. Onglet « Variables »
  3. « New repository variable »

Quand une valeur n'est connue qu'à l'exécution — version, hash de commit, horodatage — un step la calcule et l'écrit dans $GITHUB_OUTPUT. Les steps suivants la lisent via steps.<id>.outputs.<nom>.

steps:
- name: Générer les variables
id: vars
run: |
echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
echo "timestamp=$(date +%Y%m%d%H%M%S)" >> "$GITHUB_OUTPUT"
- name: Réutiliser les variables
run: |
echo "Version : ${{ steps.vars.outputs.version }}"
echo "SHA : ${{ steps.vars.outputs.sha_short }}"
echo "Horodatage : ${{ steps.vars.outputs.timestamp }}"

Pour qu'un job transmette une valeur à un autre, il l'expose dans son bloc outputs:. Le job destinataire le déclare en needs: et lit la valeur via needs.<job>.outputs.<nom>.

jobs:
setup:
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.version.outputs.value }}
steps:
- name: Déterminer la version
id: version
run: echo "value=1.2.3" >> "$GITHUB_OUTPUT"
build:
needs: setup
runs-on: ubuntu-24.04
steps:
- run: echo "Build de la version ${{ needs.setup.outputs.version }}"

Un secret est une valeur confidentielle que GitHub chiffre au repos et masque dans les logs. Cette section couvre leurs niveaux de portée, la façon de les consommer et le cas particulier du GITHUB_TOKEN.

Comme les variables, les secrets se déclarent à trois portées. Plus la portée est large, plus la surface d'exposition augmente — choisissez la plus restreinte qui répond au besoin.

NiveauAccèsConfiguration
RepositoryCe repo uniquementSettings → Secrets
EnvironmentJobs avec cet environmentSettings → Environments
OrganizationTous les repos de l'orgOrg Settings → Secrets

Un secret se consomme toujours via un bloc env: : la valeur devient une variable d'environnement que le script lit sans jamais apparaître dans la définition du workflow. Certaines actions acceptent aussi un jeton comme paramètre with:.

steps:
# Méthode recommandée : passer le secret par un bloc env:
- name: Déployer l'application
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh
# Certaines actions attendent un jeton comme paramètre with:
- name: Cloner un dépôt privé
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: mon-org/depot-prive
token: ${{ secrets.REPO_ACCESS_TOKEN }}
persist-credentials: false

Le GITHUB_TOKEN est un secret automatique, généré pour chaque exécution de workflow et révoqué à la fin. Inutile de le créer : il est toujours disponible dans secrets.GITHUB_TOKEN.

steps:
- name: Récupérer le code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Créer une issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh issue create \
--title "Incident détecté par le workflow" \
--body "Issue ouverte automatiquement par la CI."

Ses permissions dépendent de la configuration du workflow et du repository — déclarez-les explicitement avec un bloc permissions:.

Pour les déploiements, les secrets liés à un environment sont plus sûrs : ils ne sont injectés que dans les jobs rattachés à cet environnement, et peuvent exiger une approbation manuelle avant exécution.

jobs:
deploy-staging:
environment: staging
runs-on: ubuntu-24.04
steps:
- name: Déployer en staging
env:
# Secret spécifique à l'environnement "staging"
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: ./deploy.sh
deploy-production:
environment: production
runs-on: ubuntu-24.04
steps:
- name: Déployer en production
env:
# Secret différent, rattaché à l'environnement "production"
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: ./deploy.sh

Quelques réflexes évitent les fuites les plus courantes. Le principe de fond — ne jamais interpoler un secret dans un run: — est introduit dans Sécuriser GitHub Actions : les secrets ; les pratiques ci-dessous l'appliquent au quotidien.

Un secret interpolé directement dans une commande apparaît dans les logs — même masqué, une transformation suffit à le révéler. Passez-le par env: et laissez le script le lire.

# ❌ DANGEREUX : secret visible dans les logs
- run: echo ${{ secrets.API_KEY }}
- run: curl -H "Authorization: Bearer ${{ secrets.NPM_TOKEN }}" https://registry.npmjs.org/-/whoami
# ❌ DANGEREUX : secret copié dans une variable shell affichée
- run: |
KEY=${{ secrets.API_KEY }}
echo "Clé utilisée : $KEY"
# ✅ SÉCURISÉ : passer le secret via env, le script le lit
- name: Appeler l'API
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./script.sh

Une valeur sensible générée à l'exécution n'est pas connue de GitHub : elle n'est donc pas masquée automatiquement. La commande ::add-mask:: l'ajoute à la liste des valeurs censurées dans les logs.

- name: Générer le jeton
id: token
run: |
TOKEN=$(./generate-token.sh)
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> "$GITHUB_OUTPUT"
- name: Utiliser le jeton
env:
TOKEN: ${{ steps.token.outputs.token }}
run: ./use-token.sh

Les secrets ne sont pas exposés aux PR de forks — un contributeur externe ne peut pas les exfiltrer. Pour les jobs sensibles, vérifiez explicitement que l'exécution ne vient pas d'un fork.

jobs:
build:
# Ce job s'exécute sur le dépôt principal : secrets disponibles
runs-on: ubuntu-24.04
steps:
- name: Étape avec secret
env:
SECRET: ${{ secrets.MY_SECRET }}
run: echo "Secret disponible"
build-fork:
# On vérifie que la PR ne provient pas d'un fork
if: github.event.pull_request.head.repo.fork == false
runs-on: ubuntu-24.04
steps:
- name: Étape conditionnée
env:
SECRET: ${{ secrets.MY_SECRET }}
run: echo "Origine sûre, secret disponible"

Un secret qui ne tourne jamais finit par fuiter. Un workflow planifié peut rappeler l'échéance en ouvrant une issue. Comme il écrit dans le dépôt, il déclare la permission minimale issues: write — rien de plus.

name: Rappel de rotation des secrets
on:
schedule:
- cron: '0 9 1 */3 *' # Le 1er de chaque trimestre, à 9h
permissions: {}
jobs:
remind:
runs-on: ubuntu-24.04
permissions:
issues: write
steps:
- name: Créer l'issue de rappel
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh issue create \
--title "Rotation des secrets requise" \
--body "Il est temps de faire tourner les secrets du dépôt."

Variables et secrets se combinent dans quelques schémas récurrents : configuration par environnement, feature flags, secrets multi-lignes et chargement depuis un fichier.

Une matrice sur les environnements permet de déployer staging et production avec le même job, chacun recevant ses propres vars et secrets.

env:
APP_NAME: my-app
jobs:
deploy:
runs-on: ubuntu-24.04
strategy:
matrix:
environment: [staging, production]
environment: ${{ matrix.environment }}
steps:
- name: Déployer sur la cible
env:
TARGET_ENV: ${{ matrix.environment }}
# Variables spécifiques à l'environnement (définies dans vars)
API_URL: ${{ vars.API_URL }}
REPLICAS: ${{ vars.REPLICAS }}
# Secret spécifique à l'environnement
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
echo "Déploiement de $APP_NAME vers $TARGET_ENV"
echo "URL API : $API_URL"
echo "Replicas : $REPLICAS"

Des variables de configuration servent d'interrupteurs de build : on les lit via env:, puis le script compose les options en conséquence.

jobs:
build:
runs-on: ubuntu-24.04
steps:
- name: Récupérer le code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Construire avec les options actives
env:
ENABLE_FEATURE_X: ${{ vars.ENABLE_FEATURE_X }}
ENABLE_FEATURE_Y: ${{ vars.ENABLE_FEATURE_Y }}
run: |
BUILD_FLAGS=""
if [ "$ENABLE_FEATURE_X" = "true" ]; then
BUILD_FLAGS="$BUILD_FLAGS --feature-x"
fi
if [ "$ENABLE_FEATURE_Y" = "true" ]; then
BUILD_FLAGS="$BUILD_FLAGS --feature-y"
fi
npm run build -- $BUILD_FLAGS

Une clé SSH, un certificat ou un JSON se collent tels quels dans l'interface GitHub. Dans le workflow, on les écrit dans un fichier via env:, avec des permissions strictes.

- name: Préparer la clé SSH
env:
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: |
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
- name: Préparer les identifiants GCP
env:
GCP_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }}
run: |
echo "$GCP_CREDENTIALS" > /tmp/gcp-key.json
chmod 600 /tmp/gcp-key.json
export GOOGLE_APPLICATION_CREDENTIALS=/tmp/gcp-key.json

Un fichier .env versionné peut alimenter $GITHUB_ENV. Mais ne le copiez jamais en aveugle : un contenu non maîtrisé peut y glisser des variables sensibles (PATH, NODE_OPTIONS) et faire exécuter du code au runner.

# ❌ Dangereux : tout le fichier injecté sans contrôle
- run: cat .env >> "$GITHUB_ENV"
# ✅ Sûr : lire explicitement les clés attendues
- name: Charger la version depuis le fichier
run: |
VERSION=$(grep '^VERSION=' .env | cut -d= -f2)
echo "APP_VERSION=$VERSION" >> "$GITHUB_ENV"

Alimenter GITHUB_ENV avec des données non fiables est une injection d'environnement — le mécanisme et ses parades sont détaillés dans Contexts et expressions.

Quand une variable arrive vide ou inattendue, deux réflexes aident : inspecter l'environnement du runner et activer les logs détaillés.

Un step de diagnostic affiche le contexte GitHub et les variables d'environnement. Les valeurs du contexte transitent par env: — c'est aussi la règle pour ne pas exposer le runner à une injection.

- name: Inspecter l'environnement
env:
GH_REPOSITORY: ${{ github.repository }}
GH_REF: ${{ github.ref }}
GH_EVENT: ${{ github.event_name }}
ALL_VARS: ${{ toJSON(vars) }}
run: |
echo "=== Contexte GitHub ==="
echo "Dépôt : $GH_REPOSITORY"
echo "Ref : $GH_REF"
echo "Événement : $GH_EVENT"
echo "=== Variables de configuration ==="
echo "$ALL_VARS"
echo "=== Variables d'environnement ==="
env | sort

Deux variables font passer GitHub Actions en mode verbeux : l'une pour le runner, l'autre pour les steps. Réservez-les au diagnostic — elles alourdissent fortement les logs.

env:
ACTIONS_RUNNER_DEBUG: true # Logs détaillés du runner
ACTIONS_STEP_DEBUG: true # Logs détaillés des steps

Trois confusions reviennent sans cesse autour des variables et des secrets. Les reconnaître évite des heures de débogage.

Sur une PR de fork, les secrets sont volontairement vides. Un step qui en dépend doit le vérifier plutôt que d'échouer silencieusement.

# ❌ Le secret est vide sur les PR de forks
- run: echo "Secret : ${{ secrets.MY_SECRET }}"
# Résultat : "Secret : " (vide)
# ✅ Vérifier la présence avant utilisation
- name: Vérifier la présence du secret
if: secrets.MY_SECRET != ''
run: echo "Secret disponible"

Une variable de configuration absente renvoie une chaîne vide, pas une erreur. Prévoyez une valeur par défaut avec l'opérateur ||.

# ❌ Chaîne vide si vars.OPTIONAL n'existe pas
- run: echo "${{ vars.OPTIONAL }}"
# ✅ Valeur par défaut explicite
- run: echo "${{ vars.OPTIONAL || 'default-value' }}"

vars désigne la configuration du repository (onglet Settings) ; env désigne une variable d'environnement déclarée dans le workflow. Les deux notations se ressemblent mais ne pointent pas vers la même chose.

# vars = configuration repository (définie dans Settings)
- run: echo "${{ vars.API_URL }}"
# env = variable d'environnement du workflow
env:
MY_VAR: value
steps:
- run: echo "${{ env.MY_VAR }}"
- run: echo "$MY_VAR" # Accès shell direct
  • Variables = configuration non sensible, visible dans les logs ; secrets = données confidentielles, masquées automatiquement par GitHub.
  • Les variables se définissent à quatre niveaux — workflow, job, step, configuration (vars) — le plus précis l'emporte.
  • Un secret se consomme toujours via un bloc env:, jamais interpolé en clair dans run: ni passé en argument visible.
  • Le GITHUB_TOKEN est généré automatiquement pour chaque exécution ; déclarez ses permissions explicitement.
  • Ne jamais faire cat fichier >> $GITHUB_ENV en aveugle : un contenu non maîtrisé injecte des variables et peut exécuter du code.
  • Sur les PR de forks, les secrets sont vides — préférez les secrets d'environnement et conditionnez les jobs sensibles.

Pour la référence complète, consultez la documentation officielle sur les variables.

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn