diff --git a/12-github-actions-keywords.md b/12-github-actions-keywords.md new file mode 100644 index 0000000..494c997 --- /dev/null +++ b/12-github-actions-keywords.md @@ -0,0 +1,620 @@ +# 12 - GitHub Actions : Référence des Keywords + +[← 11 - Solutions](11-solutions.md) | [🏠 Accueil](README.md) + +--- + +## Objectifs de cette partie + +- Connaître tous les mots-clés d'un workflow GitHub Actions +- Comprendre leur rôle et leur niveau d'imbrication +- Disposer d'une référence rapide pour écrire ou relire un workflow + +--- + +## Structure globale d'un workflow + +``` +workflow +├── name +├── run-name +├── on +├── permissions +├── env +├── defaults +├── concurrency +└── jobs + └── + ├── name + ├── runs-on + ├── needs + ├── if + ├── environment + ├── concurrency + ├── outputs + ├── env + ├── defaults + ├── timeout-minutes + ├── strategy + ├── container + ├── services + └── steps + └── (step) + ├── id + ├── name + ├── if + ├── uses + ├── with + ├── run + ├── shell + ├── env + ├── working-directory + ├── timeout-minutes + └── continue-on-error +``` + +--- + +## Niveau Workflow (racine) + +### `name` +Nom du workflow affiché dans l'onglet Actions de GitHub. + +```yaml +name: CI/CD Azure +``` + +--- + +### `run-name` +Nom de l'exécution (run) affiché dans la liste des runs. Peut utiliser des expressions dynamiques. + +```yaml +run-name: Deploy ${{ github.actor }} → ${{ github.ref_name }} +``` + +--- + +### `on` +Définit le ou les événements qui déclenchent le workflow. + +```yaml +# Événement simple +on: push + +# Plusieurs événements +on: [push, pull_request] + +# Événements avec filtres +on: + push: + branches: [main, develop] + paths: + - 'src/**' + - '!src/docs/**' # exclusion + tags: + - 'v*' + + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + + schedule: + - cron: '0 8 * * 1-5' # lundi-vendredi à 8h UTC + + workflow_dispatch: # déclenchement manuel + inputs: + environment: + description: 'Environnement cible' + required: true + default: 'staging' + type: choice + options: [dev, staging, prod] + + workflow_call: # appelable depuis un autre workflow + inputs: + version: + type: string + required: true + secrets: + TOKEN: + required: true +``` + +**Événements courants :** + +| Événement | Déclencheur | +|---|---| +| `push` | Commit poussé | +| `pull_request` | PR ouverte, mise à jour, fermée | +| `pull_request_target` | PR depuis un fork (accès aux secrets) | +| `schedule` | Planification cron | +| `workflow_dispatch` | Déclenchement manuel | +| `workflow_call` | Appelé par un autre workflow | +| `workflow_run` | Après l'exécution d'un autre workflow | +| `release` | Création / publication d'une release | +| `issues` | Ouverture, fermeture d'une issue | +| `deployment` | Déploiement créé via l'API | + +--- + +### `permissions` +Définit les permissions du `GITHUB_TOKEN` pour ce workflow (ou par job). + +```yaml +permissions: + contents: read + id-token: write # requis pour OIDC + pull-requests: write # pour commenter les PR + packages: write # pour pousser sur GHCR + actions: read + checks: write + deployments: write + issues: write + statuses: write +``` + +> 💡 Toujours appliquer le **principe du moindre privilège** : ne déclarer que ce qui est nécessaire. + +--- + +### `env` (workflow) +Variables d'environnement disponibles dans tous les jobs et steps. + +```yaml +env: + TF_VERSION: '1.7.0' + APP_NAME: mon-app + NODE_ENV: production +``` + +--- + +### `defaults` +Valeurs par défaut pour tous les `run` du workflow. + +```yaml +defaults: + run: + shell: bash + working-directory: ./src +``` + +--- + +### `concurrency` +Contrôle qu'une seule instance du workflow (ou groupe) s'exécute à la fois. + +```yaml +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true # annule le run précédent + # cancel-in-progress: false # attend que le précédent termine +``` + +--- + +## Niveau Job + +### `` +Identifiant unique du job (snake_case, sans espaces). Référencé par `needs`. + +```yaml +jobs: + build: # ← job_id + runs-on: ubuntu-latest +``` + +--- + +### `name` (job) +Nom affiché dans l'interface GitHub (peut être différent du job_id). + +```yaml +jobs: + build: + name: 🐳 Build & Push image +``` + +--- + +### `runs-on` +Définit le runner (machine d'exécution). + +```yaml +# Runners GitHub-hosted +runs-on: ubuntu-latest +runs-on: ubuntu-22.04 +runs-on: windows-latest +runs-on: macos-latest + +# Self-hosted runner +runs-on: self-hosted +runs-on: [self-hosted, linux, azure] + +# Matrice dynamique +runs-on: ${{ matrix.os }} +``` + +--- + +### `needs` +Dépendances entre jobs : ce job ne démarre que si les jobs listés ont réussi. + +```yaml +jobs: + test: + runs-on: ubuntu-latest + build: + needs: test + deploy: + needs: [test, build] +``` + +--- + +### `if` (job) +Condition d'exécution du job. Utilise les expressions GitHub Actions. + +```yaml +if: github.ref == 'refs/heads/main' +if: github.event_name == 'push' +if: always() # même si un job précédent a échoué +if: failure() # uniquement si un job précédent a échoué +if: success() && github.ref == 'refs/heads/main' +if: contains(github.event.head_commit.message, '[deploy]') +``` + +--- + +### `environment` +Associe le job à un GitHub Environment (avec ses secrets et ses protection rules). + +```yaml +environment: production + +# Avec URL affichée dans l'interface +environment: + name: production + url: https://mon-app.azurewebsites.net +``` + +--- + +### `outputs` (job) +Déclare des sorties que les jobs suivants peuvent consommer. + +```yaml +jobs: + build: + outputs: + image_tag: ${{ steps.meta.outputs.tag }} + steps: + - id: meta + run: echo "tag=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + deploy: + needs: build + steps: + - run: echo "Deploying ${{ needs.build.outputs.image_tag }}" +``` + +--- + +### `env` (job) +Variables d'environnement scoped au job (priorité sur les variables workflow). + +```yaml +jobs: + deploy: + env: + WORKING_DIR: ./environments/prod +``` + +--- + +### `strategy` +Définit une matrice d'exécution pour paralléliser un job sur plusieurs configurations. + +```yaml +strategy: + fail-fast: false # ne pas annuler les autres si l'un échoue + max-parallel: 3 + matrix: + os: [ubuntu-latest, windows-latest] + node: [18, 20] + include: + - os: ubuntu-latest + node: 20 + extra: "valeur supplémentaire" + exclude: + - os: windows-latest + node: 18 +``` + +Accès dans le job : `${{ matrix.os }}`, `${{ matrix.node }}` + +--- + +### `timeout-minutes` (job) +Durée maximale du job avant annulation automatique (défaut : 360 min). + +```yaml +timeout-minutes: 30 +``` + +--- + +### `container` +Exécute tous les steps du job dans un conteneur Docker. + +```yaml +container: + image: node:20-alpine + credentials: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + env: + NODE_ENV: test + ports: + - 3000 + volumes: + - /tmp:/tmp +``` + +--- + +### `services` +Lance des conteneurs annexes (base de données, cache...) disponibles pendant le job. + +```yaml +services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 +``` + +--- + +## Niveau Step + +### `id` +Identifiant unique du step dans le job. Permet de référencer ses outputs. + +```yaml +steps: + - id: build + run: echo "tag=v1.0" >> $GITHUB_OUTPUT + - run: echo ${{ steps.build.outputs.tag }} +``` + +--- + +### `name` (step) +Nom affiché dans les logs du runner. + +```yaml +- name: 🔐 Login Azure (OIDC) +``` + +--- + +### `if` (step) +Condition d'exécution du step. + +```yaml +- name: Notify on failure + if: failure() + run: echo "Something went wrong" + +- name: Deploy only on main + if: github.ref == 'refs/heads/main' && success() +``` + +--- + +### `uses` +Utilise une Action (GitHub Marketplace ou locale). Incompatible avec `run`. + +```yaml +# Action du marketplace (version fixée) +- uses: actions/checkout@v4 +- uses: hashicorp/setup-terraform@v3 + +# Action locale +- uses: ./.github/actions/my-custom-action + +# Action d'un autre repo +- uses: org/repo/path@ref +``` + +--- + +### `with` +Arguments passés à une Action (`uses`). Chaque action définit ses propres inputs. + +```yaml +- uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + +- uses: azure/login@v2 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} +``` + +--- + +### `run` +Commande shell à exécuter. Incompatible avec `uses`. + +```yaml +# Commande simple +- run: terraform init + +# Multi-lignes +- run: | + terraform fmt -check + terraform validate + terraform plan -out=tfplan + +# Avec variable +- run: echo "Deploying version $VERSION" + env: + VERSION: ${{ github.sha }} +``` + +--- + +### `shell` +Shell utilisé pour `run` (surcharge le défaut). + +```yaml +- run: echo "Hello" + shell: bash + +- run: Write-Output "Hello" + shell: pwsh # PowerShell Core + +- run: print("Hello") + shell: python + +# Autres valeurs : sh, cmd, powershell +``` + +--- + +### `env` (step) +Variables d'environnement scoped au step uniquement. + +```yaml +- name: Terraform Apply + run: terraform apply -auto-approve + env: + TF_VAR_environment: prod + ARM_USE_OIDC: "true" +``` + +--- + +### `working-directory` +Répertoire de travail pour `run`. + +```yaml +- run: terraform apply -auto-approve + working-directory: ./environments/prod +``` + +--- + +### `timeout-minutes` (step) +Durée maximale du step (défaut : pas de limite, hérite du job). + +```yaml +- run: ./long-script.sh + timeout-minutes: 10 +``` + +--- + +### `continue-on-error` +Si `true`, le job continue même si ce step échoue. + +```yaml +- name: Lint (non-bloquant) + run: terraform fmt -check + continue-on-error: true +``` + +--- + +## Contextes et expressions + +Les expressions s'écrivent `${{ ... }}` et donnent accès aux contextes suivants : + +| Contexte | Contenu | +|---|---| +| `github` | Infos sur l'événement, le repo, l'acteur, le SHA... | +| `env` | Variables d'environnement déclarées | +| `vars` | Variables de repo/org (non-sensibles) | +| `secrets` | Secrets du repo/org/environment | +| `inputs` | Inputs d'un `workflow_dispatch` ou `workflow_call` | +| `steps..outputs` | Outputs d'un step précédent | +| `needs..outputs` | Outputs d'un job précédent | +| `matrix` | Valeur courante de la matrice | +| `runner` | Infos sur le runner (OS, temp dir...) | +| `job` | Status du job courant | + +**Variables GitHub courantes :** + +| Variable | Valeur | +|---|---| +| `github.sha` | SHA du commit | +| `github.ref` | Ref complète (`refs/heads/main`) | +| `github.ref_name` | Nom court (`main`, `v1.0`) | +| `github.actor` | Utilisateur qui a déclenché le workflow | +| `github.event_name` | Nom de l'événement (`push`, `pull_request`...) | +| `github.repository` | `owner/repo` | +| `github.run_id` | ID unique du run | +| `github.run_number` | Numéro incrémental du run | + +--- + +## Fonctions intégrées + +```yaml +# Tests de condition +if: contains(github.ref, 'main') +if: startsWith(github.ref, 'refs/tags/') +if: endsWith(github.ref, '-rc') + +# Status checks +if: success() +if: failure() +if: cancelled() +if: always() + +# Conversion +${{ toJSON(matrix) }} +${{ fromJSON('{"key":"value"}') }} + +# Formatage +${{ format('Hello {0}!', github.actor) }} + +# Tableaux +${{ join(matrix.os, ', ') }} +``` + +--- + +## Écrire des outputs depuis un step + +```bash +# Depuis un step run, exporter une valeur vers les steps suivants +echo "my_var=ma_valeur" >> $GITHUB_OUTPUT + +# Ajouter une variable d'environnement disponible pour les steps suivants +echo "MY_ENV_VAR=valeur" >> $GITHUB_ENV + +# Ajouter un chemin au PATH +echo "/opt/mon-outil/bin" >> $GITHUB_PATH + +# Écrire dans le résumé du job (visible dans l'interface Actions) +echo "## Résumé du déploiement" >> $GITHUB_STEP_SUMMARY +echo "- Version : $TAG" >> $GITHUB_STEP_SUMMARY +``` + +--- + +[← 11 - Solutions](11-solutions.md) | [🏠 Accueil](README.md)