Aller au contenu

Quiz : GitHub Actions

100 Comprendre ⏱ 40 min github-actionsci-cd

Ce quiz valide le niveau 100 (Comprendre) des notions du cours GitHub Actions. Visez au moins 19 bonnes réponses sur 24 ; chaque réponse renvoie à la leçon à relire. Le niveau 200 se valide avec le lab.

Les bases

1. Une étape contient pytest | tee rapport.txt, sans shell: ni bloc defaults. Les tests échouent. Que devient l'étape ?

  • a) Elle échoue, parce que bash -e arrête le script au premier échec
  • b) Elle réussit, parce que le shell par défaut ne pose pas pipefail et que tee réussit
  • c) Elle échoue, parce que GitHub analyse la sortie de pytest
  • d) Elle est ignorée
Réponse

b. Sans shell:, le runner exécute bash -e {0}, sans pipefail : le tube renvoie le code de tee. Avec shell: bash, c'est bash --noprofile --norc -eo pipefail {0}. D'où defaults: run: shell: bash dans chaque workflow. Leçon 1.

2. Pourquoi épingler runs-on: ubuntu-24.04 plutôt qu'ubuntu-latest, en octobre 2026 ?

Réponse

ubuntu-latest bascule d'Ubuntu 24.04 à 26.04 de façon progressive entre le 19 octobre et le 19 novembre 2026 : deux runs du même commit peuvent tourner sur deux systèmes différents. L'étiquette explicite ne change de version majeure que par un commit, testé et annulable. Les correctifs de sécurité arrivent de toute façon chaque semaine dans les deux images. Leçon 1.

Déclencheurs

3. Quel commit vérifie un workflow déclenché par pull_request ?

  • a) Le dernier commit de la branche proposée
  • b) Le dernier commit de la branche cible
  • c) Le commit de fusion temporaire refs/pull/<n>/merge
  • d) Le premier commit de la branche proposée
Réponse

c. GitHub teste le résultat de la fusion de la branche dans sa cible, ce qui révèle les conflits sémantiques. En cas de conflit Git, aucun run pull_request ne démarre. Leçon 2.

4. Le check Lint et tests est exigé avant fusion. On ajoute au workflow paths-ignore: ["docs/**"]. Que se passe-t-il pour une demande de fusion qui ne modifie que docs/ ?

Réponse

Le workflow n'est pas créé, aucun check n'est publié, et la demande de fusion attend indéfiniment un statut qui ne viendra pas. Un job ignoré par une condition if: compte comme réussi, un workflow filtré ne publie rien : on filtre les jobs, pas les workflows exigés. Leçon 2.

Expressions et sorties

5. La condition github.event.inputs.simulation == true est toujours fausse. Pourquoi ?

Réponse

github.event.inputs contient des chaînes : on compare 'true' au booléen true. Les types diffèrent, GitHub convertit en nombres : 'true' donne NaN, true donne 1, et NaN n'est égal à rien. Il faut lire inputs.simulation, qui garde le type booléen. Leçon 3.

6. Laquelle de ces étapes est vulnérable à une injection ?

  • a) run: echo "$TITRE" avec env: { TITRE: ${{ github.event.pull_request.title }} }
  • b) run: echo "${{ github.event.pull_request.title }}"
  • c) if: contains(github.event.pull_request.title, 'urgent')
  • d) env: { SHA: ${{ github.sha }} }
Réponse

b. L'expression est remplacée dans le texte du script avant que le shell ne l'interprète : un titre contenant $(commande) est exécuté. En a, le shell lit la valeur comme une donnée. Une condition (c) est évaluée par GitHub, pas par le shell. Leçon 3.

Jobs, matrices et services

7. Un service PostgreSQL est déclaré sans options --health-*. Que fait le runner avant d'exécuter les étapes ?

Réponse

Rien : il n'attend que les services dont l'image ou les options déclarent un contrôle de santé, et l'image officielle postgres n'en déclare pas. Les tests peuvent démarrer pendant l'initialisation de la base, d'où des échecs connection refused aléatoires. Leçon 4.

8. Pourquoi l'agrégateur ci-terminee utilise-t-il if: always() et non ${{ !cancelled() }} ?

Réponse

Avec !cancelled(), un run annulé ignorerait l'agrégateur, et un job ignoré compte comme réussi pour la protection de branche : on pourrait fusionner sans vérification. Avec always(), l'agrégateur s'exécute aussi après une annulation, voit cancelled dans needs.*.result, et échoue. Leçon 4.

Cache et artefacts

9. Une clé de cache est pip-${{ hashFiles('requirement.txt') }} dans un dépôt qui contient requirements.txt. Conséquence ?

Réponse

Le motif ne correspond à aucun fichier : hashFiles renvoie une chaîne vide, la clé devient constante (pip-), le premier run l'enregistre, et comme une entrée est immuable, le cache n'est plus jamais mis à jour. Leçon 5.

10. Pourquoi un workflow qui publie une version sur un registre public ne devrait-il pas restaurer de cache ?

  • a) Parce que le cache ralentit la publication
  • b) Parce que le contenu du cache n'est ni signé ni vérifié, et qu'un autre run a pu l'empoisonner
  • c) Parce que le cache n'est pas disponible sur les étiquettes
  • d) Parce que le cache contient les secrets
Réponse

b. C'est le mécanisme des compromissions d'Ultralytics (2024) et de TanStack (2026). cache-mode: none sur le job de publication, et des dépendances verrouillées avec empreintes. Leçon 5.

Images multi-architectures

11. Citez les trois façons de construire une image pour une architecture que la machine n'a pas, et l'avantage principal des runners natifs.

Réponse

Émulation (QEMU), construction croisée, runners natifs. Les runners natifs sont rapides et permettent de tester l'image sur son architecture, ce que la construction croisée ne permet pas (exec format error) et que l'émulation ne fait que de façon lente et moins fidèle. Leçon 6.

12. Pourquoi les empreintes de chaque architecture passent-elles par des artefacts, et non par une sortie de job ?

Réponse

Une sortie de job n'a qu'une valeur pour l'ensemble de la matrice : on ne peut pas y recueillir une empreinte par architecture. Chaque job de la matrice téléverse un artefact au nom distinct (un fichier vide nommé d'après l'empreinte), que le job d'assemblage récupère. Leçon 6.

Réutilisation et actions maison

13. Un workflow réutilisable lit env.REGION, défini dans l'env du workflow appelant. Que vaut-elle ?

Réponse

Elle est vide : l'env de l'appelant n'est pas transmis au workflow appelé. On passe la valeur en entrée (with:), ou par une variable de configuration (vars). Leçon 7.

14. Quelle différence entre uses: ./.github/actions/x et uses: $/.github/actions/x ?

Réponse

./ désigne un chemin de l'espace de travail : il faut un checkout préalable, et c'est ce qui a été récupéré qui est utilisé. $/ (juillet 2026) désigne le même dépôt au commit exact du workflow en cours, sans checkout, ce qui garde un workflow réutilisable cohérent avec ses propres actions quand il est appelé par empreinte. Leçon 7.

15. Le runner exécute-t-il npm install avant une action JavaScript ? Quelle conséquence pour le dépôt de l'action ?

Réponse

Non. Le runner exécute directement le fichier désigné par main, avec son Node 24 embarqué. Toutes les dépendances doivent être dans le dépôt, en pratique empaquetées dans dist/, versionné, et la CI de l'action doit vérifier que dist/ correspond aux sources. Leçon 8.

16. Comment une action JavaScript reçoit-elle l'entrée fichier ?

Réponse

Dans la variable d'environnement INPUT_FICHIER, que core.getInput("fichier") lit. Les entrées sont toujours des chaînes ; getBooleanInput les convertit selon les valeurs true, True, TRUE, false, False, FALSE. C'est ce qui permet de tester une action localement, comme le runner l'exécute. Leçon 8.

Environnements, secrets et OIDC

17. Une branche de travail modifie un workflow pour afficher un secret encodé en base64. Que se passe-t-il si ce secret est (a) un secret de dépôt ; (b) un secret de l'environnement production restreint aux étiquettes v* ?

Réponse

(a) Le job reçoit le secret et l'affiche : le masquage ne reconnaît pas la valeur transformée. (b) Le job qui déclare environment: production échoue avant d'obtenir un runner, car refs/heads/<branche> n'est pas une référence autorisée ; un job qui ne le déclare pas ne reçoit pas le secret. Leçon 9.

18. Sur un dépôt privé d'une organisation au plan gratuit, de quoi dispose-t-on ?

  • a) Environnements avec relecteurs requis
  • b) Environnements sans relecteurs
  • c) Ni environnements ni secrets d'environnement : seulement des secrets de dépôt et d'organisation
  • d) Tout, comme sur un dépôt public
Réponse

c. Les environnements sur dépôt privé demandent au moins Pro ou Team, et les relecteurs requis Enterprise. C'est la situation des dépôts de Lyneko : la compensation se fait chez le fournisseur (identité dédiée, portée minimale, expiration). Leçon 9.

19. Un job de l'environnement production, déclenché par l'étiquette v1.2.0, demande un jeton OIDC. Quel est son sujet (sub), au format par défaut ?

Réponse

repo:<propriétaire>/<dépôt>:environment:production. L'environnement l'emporte sur la référence ; l'étiquette n'apparaît pas dans le sujet (elle est dans la revendication ref). Leçon 10.

20. Pourquoi une politique de confiance OIDC devrait-elle vérifier repository_id plutôt que seulement le nom du dépôt dans le sujet ?

Réponse

Un nom peut être réutilisé : après le renommage ou la suppression d'une organisation, quelqu'un peut reprendre le nom et obtenir des jetons dont le sujet correspond. Les identifiants numériques ne changent jamais. Le format de sujet immuable (dépôts créés, renommés ou transférés après le 15 juillet 2026) les inclut. Leçon 10.

Sécurité et exploitation

21. Qu'est-ce qu'une pwn request ?

Réponse

Un workflow pull_request_target (ou workflow_run), qui s'exécute avec les secrets et un jeton en écriture, récupère et exécute le code d'une demande de fusion venue d'une bifurcation (installation de dépendances, tests, construction). L'auteur de la demande de fusion exécute alors son code avec les privilèges du dépôt. checkout v7 refuse ce schéma par défaut depuis juin 2026. Leçon 11.

22. Pourquoi une étiquette de version exacte comme @v45.0.7 ne suffit-elle pas à figer une action ?

Réponse

Une étiquette est un pointeur que l'on peut déplacer, quel que soit son nom. Les étiquettes v45.0.7, v45.0.8 et v45.0.9 de tj-actions/changed-files désignent aujourd'hui le même commit : elles ont été déplacées par l'attaquant en mars 2025, puis par les mainteneurs. Seule l'empreinte complète d'un commit désigne un contenu immuable. Leçon 11.

23. Pourquoi les runners auto-hébergés ne doivent-ils presque jamais servir à un dépôt public ?

Réponse

N'importe qui peut ouvrir une demande de fusion dont le workflow s'exécutera sur la machine, dans votre réseau. Les approbations des contributeurs externes réduisent le risque sans le supprimer, et un runner persistant laisse en plus au job suivant tout ce que le précédent a laissé. Leçon 12.

24. Un run de trois jobs de 9, 60 et 9 secondes, sur un dépôt privé : combien de minutes sont facturées ? Regrouper le premier job avec le deuxième en économise-t-il une ?

Réponse

Trois minutes : chaque job est facturé à la minute entamée. Regrouper les deux premiers n'économise rien : 69 secondes font deux minutes. Seul un job unique de 78 secondes descendrait à deux minutes, au prix de la séparation des privilèges si l'un des jobs avait des droits d'écriture. Leçon 12.

Plan du cours