Publier les preuves
- Leçon 6
- 50 min
- Module 2 : Créer un pipeline CI reproductible
Leçon 6 sur 15 · CI/CD, tests et mises en production sûresPublier les preuves
1. Comprendre le chemin de livraison
2. Créer un pipeline CI reproductible
3. Gérer les environnements et les secrets
4. Mettre en production les changements d'IA et de base de données
5. Rétablir et maintenir le processus de mise en production
Ce que vous saurez faire
Une coche verte indique « réussi », mais ne précise ni ce qui a réussi ni quelle image en résulte. Conservez des preuves qui répondent à ces questions plusieurs mois plus tard, pour quelqu’un qui n’était pas là. Rendez ensuite les vérifications obligatoires, pour qu’aucun changement n’atteigne main sans elles.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Les deux tâches, le test de bon fonctionnement et l’envoi de la même construction, dans Construire et tester : la tâche d’image.
- L’empreinte représente le contenu exact d’une image, et une étiquette peut changer de cible. Voir Registre et déploiement : Registre, dépôt et étiquette dans L’essentiel de Docker et du nuage pour les développeurs en IA.
- Les demandes de tirage (pull requests) et leurs vérifications, dans Les étapes d’une demande de tirage : ce qu’est une demande de tirage dans L’essentiel de Git et du développement en équipe.
Ce qu’il vous faut sur votre ordinateur
- Votre dépôt
ticket-api-ciavec les deux tâches et une exécution verte surmain, tiré de Construire et tester : exercice guidé. - L’interface en ligne de commande GitHub
gh, connectée à votre compte GitHub (gh auth login), pour télécharger les artefacts et vérifier une attestation. Les autres étapes fonctionnent aussi dans les pages Web. - Docker, pour
docker buildx imagetools inspect. - Si votre flux de travail ne fonctionne pas, comparez-le au fichier de référence
ci.ymlàc862935. À la fin de cette leçon, votre fichier est identique. L’instantanéend-of-m02contient tous les fichiers de ce module, mais aucun réglage GitHub : vous créez vous-même l’ensemble de règles.
Exécuté avec GitHub Actions (exécuteurs ubuntu-24.04) dans le dépôt public Nextia-Intelligence/ticket-api-ci, GitHub CLI 2.96.0 et Docker Desktop 4.81.0 sur macOS (Apple silicon). Les étapes pour Windows et Linux n’ont pas été testées.
Les questions auxquelles les preuves doivent répondre
Section intitulée « Les questions auxquelles les preuves doivent répondre »Dans trois mois, la responsable du service de soutien signale une classification étrange. Amira connaît l’image qui fonctionne en production : elle a son empreinte. Pour trouver la cause, elle doit répondre à quatre questions. Le pipeline doit déjà avoir conservé les réponses :
| Question | Preuve | Où la trouver |
|---|---|---|
| Quels tests ont été exécutés sur ce code, et ont-ils réussi? | Le rapport de tests junit.xml |
L’artefact reports de l’exécution |
| Quel commit et quelle exécution ont produit cette empreinte? | build-info.json |
L’artefact build-info de l’exécution |
| Puis-je le vérifier sans faire confiance à un fichier? | Une attestation de provenance signée | Le stockage des attestations de GitHub et le registre |
| Que dit l’image elle-même? | Les étiquettes dans l’image | L’image dans GHCR |
Dans GitHub Actions, un artefact est un fichier ou dossier conservé par une exécution après la disparition de ses machines virtuelles. Toute personne connectée à GitHub peut télécharger les artefacts d’une exécution d’un dépôt public. GitHub les conserve pendant 90 jours par défaut. Les artefacts de l’exécution de référence expirent 90 jours après l’exécution.
Le rapport de tests, même quand les tests échouent
Section intitulée « Le rapport de tests, même quand les tests échouent »Ajoutez une étape à la fin de la tâche de vérification :
- name: Keep the test and evaluation reports if: always() uses: actions/upload-artifact@v7 with: name: reports path: reports/if: always() exécute aussi l’étape lorsqu’une étape précédente a échoué. Un rapport de tests échoués est le rapport le plus utile. (Le nom indique aussi « évaluation » : le module 4 ajoute le rapport d’évaluation de l’IA au même dossier.)
Dans la première exécution, rouge, la vérification du code a échoué avant le démarrage de pytest. Il n’y avait donc aucun rapport à conserver. L’étape l’a indiqué, et GitHub a affiché un avertissement sur la page de l’exécution :
No files were found with the provided path: reports/. No artifacts will be uploaded.Cet avertissement est correct, ce n’est pas un deuxième problème : aucun test n’a été exécuté. Dans l’exécution verte, l’étape a téléversé un fichier de 773 octets. Téléchargez-le et regardez son contenu :
gh run download 37663364319 --repo Nextia-Intelligence/ticket-api-ci -n reportshead -c 300 junit.xmlLa sortie :
<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="25" time="0.418" timestamp="2026-10-07T18:00:12.774920+00:00" hostname="runnervm8df0l">25 tests, 0 échec, 0 test ignoré, avec la durée et le nom de la machine virtuelle de l’exécuteur. Chaque test a aussi sa propre ligne. Pour votre exécution, omettez --repo … et utilisez son numéro : gh run list l’affiche.
build-info.json : empreinte, commit et exécution dans un seul fichier
Section intitulée « build-info.json : empreinte, commit et exécution dans un seul fichier »Ajoutez deux étapes à la fin de la tâche de l’image. Elles s’exécutent seulement si l’image a été envoyée :
- name: Write build-info.json if: env.PUSH == 'true' run: | mkdir -p evidence cat > evidence/build-info.json <<JSON { "image": "${IMAGE}", "digest": "${{ steps.push.outputs.digest }}", "tags": $(jq -c -R -s 'split("\n") | map(select(length > 0))' <<< "${{ steps.meta.outputs.tags }}"), "commit": "${{ github.sha }}", "ref": "${{ github.ref_name }}", "run": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" } JSON cat evidence/build-info.json { echo "### Image" echo "\`${IMAGE}@${{ steps.push.outputs.digest }}\`" echo "Commit \`${{ github.sha }}\`, tags: ${{ steps.meta.outputs.tags }}" | tr '\n' ' ' } >> "$GITHUB_STEP_SUMMARY" - name: Keep the build evidence if: env.PUSH == 'true' uses: actions/upload-artifact@v7 with: name: build-info path: evidence/build-info.jsonL’étape écrit un petit fichier JSON. steps.push.outputs.digest est l’empreinte indiquée par l’étape d’envoi. jq transforme la liste d’étiquettes (une par ligne) en liste JSON. github.sha est l’identifiant complet du commit (enregistrement des changements), et la dernière ligne construit l’adresse de cette exécution. L’étape affiche le fichier, donc les journaux (logs) le contiennent aussi :
{ "image": "ghcr.io/usmanahmed99/ticket-api-ci", "digest": "sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1", "tags": ["ghcr.io/usmanahmed99/ticket-api-ci:sha-c862935"], "commit": "c8629352adc1c51933b0c95cb80b73bf6c97e99f", "ref": "main", "run": "https://github.com/Nextia-Intelligence/ticket-api-ci/actions/runs/37663364319"}Ce fichier relie le registre au dépôt. Un déploiement en a aussi besoin : au module 3, le flux de travail Deploy télécharge build-info depuis l’exécution CI. Il déploie exactement cette empreinte. Le fichier n’est donc pas seulement un relevé : c’est l’entrée de l’étape suivante.
Les dernières lignes ajoutent trois lignes de Markdown à $GITHUB_STEP_SUMMARY. Un résumé de tâche est du « Markdown personnalisé pour chaque tâche ». GitHub l’affiche « sur la page de résumé d’une exécution de flux de travail » (documentation GitHub). Ici, il affiche l’image, son empreinte et le commit en haut de la page de l’exécution. Le jour du test, un visiteur non connecté voyait le graphique des tâches et les artefacts, mais pas les journaux.
Étiquettes : l’image se décrit elle-même
Section intitulée « Étiquettes : l’image se décrit elle-même »Dans l’étape d’envoi, labels: ${{ steps.meta.outputs.labels }} a écrit des étiquettes dans l’image. Ce sont des paires nom-valeur qui l’accompagnent. Tout le monde peut les lire dans le registre public, sans se connecter :
docker buildx imagetools inspect ghcr.io/usmanahmed99/ticket-api-ci:sha-c862935 --format '{{json .Image.Config.Labels}}'La sortie (mise en forme et abrégée) :
{ "org.opencontainers.image.created": "2026-10-07T18:00:42.131Z", "org.opencontainers.image.revision": "c8629352adc1c51933b0c95cb80b73bf6c97e99f", "org.opencontainers.image.source": "https://github.com/Nextia-Intelligence/ticket-api-ci", "org.opencontainers.image.title": "ticket-api-ci", "org.opencontainers.image.version": "sha-c862935", ...}Les noms viennent de l’Open Container Initiative (OCI), donc de nombreux outils les comprennent. revision désigne le commit. source désigne le dépôt. Sans --format, la même commande affiche deux entrées pour l’étiquette : l’image linux/amd64 et une entrée unknown/unknown. Cette dernière est le relevé de construction propre à BuildKit, comme dans le cours sur Docker.
Une étiquette est utile, mais quiconque peut envoyer une image peut y écrire n’importe quelle étiquette. La prochaine preuve ne peut pas être falsifiée ainsi.
Provenance : un relevé signé de l’origine de l’image
Section intitulée « Provenance : un relevé signé de l’origine de l’image »La provenance décrit comment un artefact a été construit et à partir de quoi : quelle source, quel flux de travail, quelle exécution. Une attestation est un tel relevé accompagné d’une signature cryptographique, pour vérifier qui l’a produit. Ajoutez les permissions et une étape à la tâche de l’image :
permissions: contents: read packages: write id-token: write attestations: write - name: Record where the image came from if: env.PUSH == 'true' uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.IMAGE }} subject-digest: ${{ steps.push.outputs.digest }} push-to-registry: trueid-token: write permet à la tâche d’obtenir de GitHub un jeton d’identité de courte durée. Le service de signature l’utilise pour donner à la tâche un certificat. Celui-ci indique « c’est le flux de travail ci.yml de ce dépôt, à ce commit, dans cette exécution ». attestations: write permet à la tâche de stocker le résultat. Aucune clé n’est stockée nulle part : l’identité vient de l’exécution elle-même. Le module 3 utilise le même type de jeton pour se connecter à Azure.
Le journal de l’exécution de référence :
Attestation type: Build ProvenanceAttestation created for ghcr.io/usmanahmed99/ticket-api-ci@sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1Attestation signed using certificate from Public Good Sigstore instanceAttestation signature uploaded to Rekor transparency loghttps://search.sigstore.dev?logIndex=3133904536Attestation uploaded to repositoryhttps://github.com/Nextia-Intelligence/ticket-api-ci/attestations/53659075Attestation uploaded to registryghcr.io/usmanahmed99/ticket-api-ci@sha256:2c0ec56270b121fa5e7be5b05645f29514002c43f1a0c74f27936fda0bdbdb7bSigstore l’a signée, un journal public (Rekor) l’a enregistrée, et GitHub l’a stockée à deux endroits. La page de l’attestation est publique :

Vérifiez-la maintenant dans le terminal. gh attestation verify télécharge l’attestation, vérifie la signature et compare le certificat à une politique :
gh attestation verify oci://ghcr.io/usmanahmed99/ticket-api-ci@sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1 --owner usmanahmed99La sortie :
Loaded digest sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1 for oci://ghcr.io/usmanahmed99/ticket-api-ci@sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1Loaded 1 attestation from GitHub API
The following policy criteria will be enforced:- Predicate type must match:................ https://slsa.dev/provenance/v1- Source Repository Owner URI must match:... https://github.com/usmanahmed99- Subject Alternative Name must match regex: (?i)^https://github.com/usmanahmed99/- OIDC Issuer must match:................... https://token.actions.githubusercontent.com
✓ Verification succeeded!
The following 1 attestation matched the policy criteria
- Attestation #1 - Build repo:..... usmanahmed99/ticket-api-ci - Build workflow:. .github/workflows/ci.yml@refs/heads/main - Signer repo:.... usmanahmed99/ticket-api-ci - Signer workflow: .github/workflows/ci.yml@refs/heads/mainLa commande exige une connexion à GitHub (avec n’importe quel compte). Une politique peut être plus stricte. Faites une prédiction : la même image réussit-elle si la politique exige la branche hotfix/1.2.1?
gh attestation verify oci://ghcr.io/usmanahmed99/ticket-api-ci@sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1 --owner usmanahmed99 --source-ref refs/heads/hotfix/1.2.1La sortie se termine par :
✗ Policy verification failed
Error: expected SourceRepositoryRef to be refs/heads/hotfix/1.2.1, got refs/heads/mainLe code de sortie est 1. Voilà l’utilité d’une attestation : un outil de déploiement peut refuser toute image non construite par un flux de travail donné sur main.
Trois remarques du jour du test. Premièrement, dans un script, demandez du JSON. Sans terminal, la commande n’affichait rien et seul son code de sortie indiquait le résultat. --format json donne le commit et l’exécution, par exemple avec --jq '.[0].verificationResult.signature.certificate | {sourceRepositoryDigest, runInvocationURI}'. Deuxièmement, avec les forfaits GitHub Free, Pro et Team, les attestations fonctionnent seulement dans les dépôts publics. Troisièmement, la page de procédure de GitHub montre maintenant l’action actions/attest@v4. L’action actions/attest-build-provenance@v4 du flux de référence exécutait actions/attest à l’intérieur, comme son journal le montre. Les deux fonctionnent.
Exemple détaillé : retrouver le commit à partir d’une empreinte
Section intitulée « Exemple détaillé : retrouver le commit à partir d’une empreinte »La vérification de fin de module vous demande de retrouver la source d’une construction. Amira a l’empreinte sha256:6c9bb4ed…. Elle suit les preuves :
Description textuelle du diagramme
Commencez par l’empreinte. L’artefact build-info de l’exécution qui l’a envoyée contient la même empreinte, le commit et l’adresse de l’exécution. L’exécution montre ce qui a été vérifié : les deux tâches, les 25 tests dans reports et les journaux. Le commit est le code source exact. L’attestation est un deuxième chemin que vous pouvez vérifier séparément : son sujet est l’empreinte. Son certificat signé nomme l’exécution et le commit. Si les deux chemins donnent le même commit, la trace est complète.
-
Obtenez l’empreinte de l’image en cours d’exécution ou que vous récupérez :
Terminal docker buildx imagetools inspect ghcr.io/usmanahmed99/ticket-api-ci:sha-c862935La sortie commence par
Digest: sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1. -
Demandez au relevé signé l’exécution et le commit :
Terminal gh attestation verify oci://ghcr.io/usmanahmed99/ticket-api-ci@sha256:6c9bb4ed7c7cffc27f0c5489ba867d4d5db16def1c59c8bcbaeb3d26c45ce2e1 --owner usmanahmed99 --format json --jq '.[0].verificationResult.signature.certificate | {sourceRepositoryDigest, runInvocationURI}'La sortie :
{"runInvocationURI":"https://github.com/usmanahmed99/ticket-api-ci/actions/runs/37663364319/attempts/1","sourceRepositoryDigest":"c8629352adc1c51933b0c95cb80b73bf6c97e99f"}. -
Téléchargez l’artefact
build-infode cette exécution et comparez l’empreinte :Terminal gh run download 37663364319 --repo Nextia-Intelligence/ticket-api-ci -n build-infocat build-info.jsonL’empreinte dans le fichier est
sha256:6c9bb4ed…, donc identique. Le commit estc8629352…, le même que dans l’attestation. -
Ouvrez l’exécution : 37663364319. Les deux tâches sont vertes, et l’artefact
reportscontient les 25 tests. -
Lisez le commit :
c862935, ougit show c862935dans une copie clonée. C’est la correction de vérification du code de Configurer l’exécuteur.
La trace est complète : empreinte → build-info.json → exécution → commit, et le chemin signé concorde.
Protéger main avec un ensemble de règles
Section intitulée « Protéger main avec un ensemble de règles »Le pipeline vérifie chaque changement, mais rien n’empêche un envoi vers main qui le contourne. Jusqu’ici, Amira envoyait directement vers main. Un ensemble de règles regroupe des règles que GitHub impose aux branches. Une vérification obligatoire doit réussir avant qu’une demande de tirage puisse être fusionnée.
Créez l’ensemble de règles après la première exécution verte, car GitHub propose seulement les noms de vérifications déjà rencontrés. Dans le dépôt de référence, Amira l’a créé à 18 h 02 UTC, une minute après la fin de l’exécution verte. Sur GitHub, ouvrez votre dépôt, puis :
- Sélectionnez Settings (paramètres). Sous « Code, planning, and automation » (code, planification et automatisation), sélectionnez Rulesets (ensembles de règles).
- Sélectionnez New ruleset (nouvel ensemble de règles) → New branch ruleset (nouvel ensemble de règles de branche).
- Nommez-le
Protect main(protéger main). Réglez Enforcement status (état d’application) sur Active (actif). - Sous Target branches (branches ciblées), sélectionnez Add a target (ajouter une cible), puis ajoutez la branche par défaut.
- Sélectionnez Restrict deletions (restreindre les suppressions) et Block force pushes (bloquer les envois forcés).
- Sélectionnez Require a pull request before merging (exiger une demande de tirage avant la fusion). Réglez les approbations obligatoires à
0. (Vous travaillez seul. Le module 3 explique le rôle des réviseurs.) - Sélectionnez Require status checks to pass before merging (exiger la réussite des vérifications d’état avant la fusion), puis Require branches to be up to date before merging (exiger des branches à jour avant la fusion).
- Ajoutez les deux vérifications :
Lint, test, audit(vérifier le code, tester, auditer) etBuild, smoke-test and push the image(construire, tester le bon fonctionnement et envoyer l’image). - Sélectionnez Create (créer).
Ces noms sont les valeurs name: des deux tâches, pas leurs identifiants. Require branches to be up to date (exiger des branches à jour) impose qu’une demande de tirage contienne le dernier main avant la fusion. L’API de GitHub appelle cela « strict ». Sinon, deux demandes de tirage peuvent réussir séparément, mais quand même casser main ensemble.
Vérifiez le résultat dans le terminal. Utilisez votre propre nom de propriétaire et relevez le numéro de l’ensemble de règles dans la première commande :
gh api repos/<you>/ticket-api-ci/rulesetsgh api repos/<you>/ticket-api-ci/rulesets/<number>L’ensemble de règles actuel du dépôt de référence (abrégé) :
{ "name": "Protect main", "enforcement": "active", "conditions": { "ref_name": { "include": ["~DEFAULT_BRANCH"] } }, "rules": [ { "type": "deletion" }, { "type": "non_fast_forward" }, { "type": "pull_request", "parameters": { "required_approving_review_count": 0 } }, { "type": "required_status_checks", "parameters": { "strict_required_status_checks_policy": true, "required_status_checks": [ { "context": "Lint, test, audit, evaluate" }, { "context": "Build, smoke-test and push the image" } ] } } ], "bypass_actors": []}Regardez la première vérification : « Lint, test, audit, evaluate » (vérifier le code, tester, auditer, évaluer). Le jour de ce module, elle s’appelait Lint, test, audit. Le module 4 renomme la tâche, et l’ensemble de règles a dû suivre, comme le montre l’erreur fréquente ci-dessous. bypass_actors est vide, donc même le propriétaire ne peut pas contourner les règles.
Désormais, chaque changement atteint main par une demande de tirage. Le changement suivant du dépôt de référence était la demande de tirage no 1, de feature/deploy vers main. Son exécution 37664331105 a construit et testé l’image, sans rien envoyer.
Exercice guidé : conserver les preuves
Section intitulée « Exercice guidé : conserver les preuves »- Ajoutez Keep the test and evaluation reports (conserver les rapports de tests et d’évaluation) à la tâche de vérification.
- Ajoutez
id-token: writeetattestations: writeaux permissions de la tâche de l’image. - Ajoutez Record where the image came from (enregistrer l’origine de l’image), Write build-info.json (écrire build-info.json) et Keep the build evidence (conserver les preuves de construction).
- Comparez votre
ci.ymlau fichier de référence àc862935. Exécutez actionlint. - Créez un commit et envoyez-le vers
main. C’est votre dernier envoi direct. - Ouvrez l’exécution. Vérifiez qu’elle contient les artefacts
reportsetbuild-info, ainsi que le résumé de tâche avec l’image. - Téléchargez les deux artefacts avec
gh run download. - Vérifiez votre image avec
gh attestation verify oci://ghcr.io/<you>/ticket-api-ci@<digest> --owner <you>. - Créez l’ensemble de règles
Protect main.
Afficher le résultat attendu
diff entre votre fichier et le fichier de référence n’affiche aucune différence, ou seulement les commentaires. La page de l’exécution liste quatre artefacts : reports (moins de 1 Ko), build-info (moins de 1 Ko) et deux relevés de construction .dockerbuild. build-info.json contient votre image, une empreinte, l’étiquette sha-<7>, votre commit complet, main et l’adresse de votre exécution. gh attestation verify se termine par ✓ Verification succeeded! (vérification réussie) et nomme .github/workflows/ci.yml@refs/heads/main. gh api repos/<you>/ticket-api-ci/rulesets/<number> affiche "enforcement": "active", "strict_required_status_checks_policy": true et les deux noms de vérifications Lint, test, audit et Build, smoke-test and push the image.
À vous de jouer : retracer une deuxième image
Section intitulée « À vous de jouer : retracer une deuxième image »La fusion de la demande de tirage (pull request) no 1 dans le dépôt de référence a poussé une autre image : sha256:a8af875a60ca868f018e5d83a60382acac691bbfbb7d33a46fe4cbf9942e513f. Cette demande modifiait seulement les fichiers de flux de travail et de déploiement, pas le code de l’application. Faites d’abord une prédiction : cette empreinte est-elle identique à sha256:6c9bb4ed…? Retrouvez ensuite son exécution et son commit (changement enregistré), par les deux chemins.
Afficher la réponse
L’empreinte est différente, même si le code dans l’image n’a pas changé. Les étiquettes font partie de l’image, et elles ont changé : une nouvelle revision (le commit), une nouvelle heure created (création) et une nouvelle étiquette version. Ici, un commit différent donne toujours une empreinte différente. Le chemin signé :
gh attestation verify oci://ghcr.io/usmanahmed99/ticket-api-ci@sha256:a8af875a60ca868f018e5d83a60382acac691bbfbb7d33a46fe4cbf9942e513f --owner usmanahmed99 --format json --jq '.[0].verificationResult.signature.certificate | {sourceRepositoryDigest, runInvocationURI}'La sortie : {"runInvocationURI":"https://github.com/usmanahmed99/ticket-api-ci/actions/runs/37664536375/attempts/1","sourceRepositoryDigest":"43de57baa70c998494d38ad0adda9a7152cfdb96"}. Le chemin du registre : gh run download 37664536375 --repo Nextia-Intelligence/ticket-api-ci -n build-info donne la même empreinte, l’étiquette sha-43de57b et le commit 43de57baa70c…. Ce commit est le commit de fusion de la demande de tirage no 1, « Merge pull request #1 from usmanahmed99/feature/deploy » (fusionner la demande de tirage no 1 de usmanahmed99/feature/deploy).
Erreur courante
Les deux vérifications sont vertes, mais la demande de tirage ne peut toujours pas être fusionnée
Symptôme : dans le dépôt de référence, la demande de tirage no 3 avait deux vérifications vertes, mais GitHub empêchait la fusion. gh pr view 3 --json mergeStateStatus indiquait BLOCKED (bloqué).
Cause : la demande de tirage renommait la première tâche en Lint, test, audit, evaluate (analyse statique, test, audit, évaluation). L’ensemble de règles exigeait encore une vérification nommée Lint, test, audit (analyse statique, test, audit). Aucune tâche ne signalait plus ce nom. GitHub l’attendait donc indéfiniment. Une vérification obligatoire est un nom. GitHub ne sait pas que le nouveau nom désigne la même tâche.
Solution : mettez à jour l’ensemble de règles : Settings (paramètres) → Rulesets (ensembles de règles) → Protect main (protéger main) → dans les vérifications d’état obligatoires, retirez l’ancien nom et ajoutez le nouveau. La demande de tirage peut alors être fusionnée. Mieux encore : modifiez l’ensemble de règles en même temps que le nom de la tâche. Indiquez-le dans la description de la demande de tirage. Barrière d’évaluation de l’IA : une vérification renommée présente toute l’histoire.
Pour un vrai service d’IA : ne « corrigez » pas une demande de tirage bloquée en désactivant les règles ou en vous ajoutant à la liste d’exemption. Chaque fois que les règles sont désactivées, un changement non vérifié peut atteindre main, puis la production.
Vérifiez votre compréhension
build-info.json et l'attestation de provenance relient tous deux une empreinte à un commit et à une exécution. Pourquoi conserver les deux?
Afficher la réponse
Ils ont des usages différents. build-info.json est un simple fichier que le pipeline lui-même lit. Le flux de travail Deploy du module 3 y prend l’empreinte. Mais ce n’est qu’un fichier : une tâche pourrait y écrire n’importe quoi. L’attestation porte une signature issue d’un certificat validé par le jeton d’identité de GitHub. Un journal public l’enregistre. Une personne ou un outil peut donc la vérifier sans faire confiance aux fichiers du pipeline. Quand les deux donnent le même commit, la trace est solide. Les étiquettes sont un troisième indice, plus faible : quiconque peut pousser une image peut écrire n’importe quelle étiquette.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Pourquoi l'étape qui téléverse les rapports utilise-t-elle if : always(), tandis que les étapes build-info utilisent if : env.PUSH == 'true'?
Afficher la réponse
Le rapport de test est surtout utile quand les tests échouent. Il faut donc le téléverser même après l’échec d’une étape. always() le permet. (Si l’analyse statique échoue avant pytest, il n’y a aucun rapport, et l’étape affiche seulement un avertissement.) build-info.json décrit une image dans le registre. Sans envoi, comme dans une demande de tirage, il n’y a aucune empreinte à enregistrer. Ces étapes s’exécutent donc seulement quand PUSH vaut true.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Exercice du module
Section intitulée « Exercice du module »Exercice guidé → tâche autonome. Vous avez construit le pipeline avec Amira en trois leçons. Terminez-le maintenant par vous-même et documentez-le, en deux parties.
Partie 1 : le pipeline propre au fournisseur. Votre dépôt ticket-api-ci contient .github/workflows/ci.yml, avec les deux tâches et toutes les étapes de preuves. Il a une exécution verte sur main et l’ensemble de règles Protect main. Faites un autre changement par une demande de tirage : ajoutez une ligne à README.md sur la branche docs/pipeline-badge. Ouvrez ensuite une demande de tirage de docs/pipeline-badge vers main. Montrez qu’elle a exécuté les deux vérifications, n’a rien poussé et a été fusionnée seulement après leur réussite. Montrez ensuite l’exécution déclenchée sur main par la fusion, avec ses preuves.
Partie 2 : un guide de correspondance Azure DevOps. Plusieurs équipes utilisent Azure Pipelines (une composante d’Azure DevOps) plutôt que GitHub Actions. Les idées sont les mêmes, mais les noms diffèrent. Rédigez un tableau qui associe chaque partie de votre pipeline à son nom dans Azure Pipelines. Incluez le fichier, les déclencheurs, les tâches et leur ordre, les étapes, les exécuteurs, le cache et le rapport de test. Incluez aussi les artefacts, les secrets et les variables, les environnements et les approbations, ainsi que la connexion au nuage. Ajoutez un pipeline qui démarre après un autre et les vérifications obligatoires. Vérifiez chaque nom Azure DevOps sur learn.microsoft.com, et inscrivez la date de votre vérification.
Afficher un modèle de réponse
Partie 1. Dans le dépôt de référence, la demande de tirage no 1 sert de modèle. Son exécution 37664331105 a exécuté Lint, test, audit (43 s) et Build, smoke-test and push the image (construire, tester sommairement et pousser l’image). Cette deuxième tâche a pris 32 s. Chaque étape du Dockerfile indiquait CACHED (en cache), sans envoi, donc sans artefact build-info. Après la fusion, l’exécution 37664536375 sur main a poussé sha256:a8af875a… avec l’étiquette sha-43de57b, son build-info et son attestation. La personne qui révise note la partie 1 démontrée si l’exécution de la demande de tirage n’a rien poussé. La fusion doit avoir attendu les deux vérifications obligatoires. L’exécution sur main doit aussi avoir les artefacts reports et build-info, ainsi qu’une attestation vérifiée.
Partie 2. Le cours a vérifié ces noms sur learn.microsoft.com. Les pages : concepts clés, tâches, dépôts GitHub et déclencheurs, agents, agents hébergés par Microsoft, mise en cache, résultats des tests, artefacts de pipeline, commandes de journalisation, groupes de variables, variables secrètes, approbations et vérifications, connexion à Azure, déclencheurs de pipeline, stratégies de branche.
| Ce cours (GitHub Actions) | Azure Pipelines | Note |
|---|---|---|
Fichier de flux de travail .github/workflows/ci.yml |
Fichier de pipeline YAML, souvent azure-pipelines.yml |
Un pipeline comporte des phases; « chaque phase contient une ou plusieurs tâches ». Un pipeline à une phase peut directement énumérer jobs:. |
on: pull_request et on: push avec branches: |
Déclencheur pr: et trigger: (déclencheur CI) |
Les deux énumèrent des branches, par exemple trigger: avec - main. |
Tâche, runs-on: ubuntu-24.04 |
Tâche, pool: avec vmImage: |
Les exemples de la documentation utilisent vmImage: 'ubuntu-latest'. |
needs: checks |
dependsOn: checks |
Sans cela, les tâches Azure Pipelines « s’exécutent en parallèle ». Les tâches GitHub sans needs s’exécutent aussi en parallèle. |
if: env.PUSH == 'true', if: always() |
condition:, par exemple succeeded(), failed(), always() |
Par défaut, une tâche s’exécute seulement si les tâches dont elle dépend ont réussi. |
Étape : run: ou uses: pour une action |
Étape : un script ou une task (tâche prédéfinie) | « Une tâche prédéfinie est un script préassemblé qui effectue une action », par exemple PublishTestResults@2. |
| Exécuteur hébergé par GitHub, une nouvelle machine virtuelle par tâche | Microsoft-hosted agent (agent hébergé par Microsoft) | « Vous obtenez une nouvelle machine virtuelle pour chaque tâche du pipeline. » |
| Exécuteur autohébergé | Self-hosted agent (agent autohébergé), dans un groupe d’agents | Le même avertissement s’applique : il conserve son état entre les tâches. |
setup-python avec cache: pip |
Tâche prédéfinie Cache@2 |
« Les caches expirent après sept jours sans activité. » |
pytest --junitxml + artefact reports |
PublishTestResults@2 avec testResultsFormat: 'JUnit' |
Azure DevOps affiche aussi les résultats dans un onglet de tests. |
Artefact (upload-artifact, download-artifact) |
Pipeline artifact (artefact de pipeline) : mots-clés publish / download, ou PublishPipelineArtifact@1 / DownloadPipelineArtifact@2 |
build-info.json serait un artefact de pipeline. |
$GITHUB_STEP_SUMMARY |
Commande de journalisation ##vso[task.uploadsummary] |
Elle ajoute du Markdown au résumé de construction. |
GITHUB_TOKEN avec permissions: |
System.AccessToken |
Le jeton de l’exécution. |
Secrets, variables (secrets.*, vars.*) |
Variable groups (groupes de variables, dans Library) et secret variables (variables secrètes) | Une icône de cadenas marque un secret, qui est masqué dans les journaux. Par défaut, les constructions de demandes de tirage provenant de dépôts dérivés ne reçoivent pas les secrets. |
| Environnements avec personnes approbatrices obligatoires (module 3) | Environments (environnements) avec approvals and checks (approbations et vérifications) | La vérification qui exige une personne s’appelle Approvals (approbations). Les autres sont Branch control (contrôle des branches) et Exclusive lock (verrou exclusif). |
Connexion OIDC avec azure/login et un identifiant fédéré (module 3) |
service connection (connexion de service) Azure Resource Manager avec workload identity federation (fédération d’identité de charge de travail) | « La fédération d’identité de charge de travail élimine le besoin de secrets et de gestion des secrets. » |
workflow_run : Deploy démarre après CI (module 3) |
Pipeline resource trigger (déclencheur de ressource de pipeline) : resources: → pipelines: avec trigger: true |
Il démarre « quand une version du pipeline source se termine ». |
| Ensemble de règles avec vérifications obligatoires | Pour Azure Repos : branch policies (stratégies de branche) avec Build validation (validation de construction) réglée sur Required (obligatoire). Pour un dépôt GitHub : la vérification d’état du pipeline dans les règles GitHub | Les stratégies de branche signifient aussi que « tous les changements doivent passer par des demandes de tirage ». |
| Mode strict : les branches doivent être à jour | Expiration de construction de Build validation : Immediately when main is updated (immédiatement quand main est mis à jour) | La construction de la demande de tirage s’exécute de nouveau après un changement sur main. |
Attestation de provenance, gh attestation verify |
Aucune correspondance dans ce guide | Consultez la documentation Azure DevOps actuelle avant de la planifier. |
La personne qui révise note la partie 2 démontrée si chaque ligne du pipeline de l’apprenant a un nom Azure Pipelines et une date. Chaque nom doit avoir un lien vers la page que l’apprenant a vérifiée. Une supposition sans source est partiellement démontrée.
Vérification du module
Section intitulée « Vérification du module »Vérifiez votre compréhension
Rappel : que reste-t-il après la fin d'une tâche sur un exécuteur hébergé par GitHub, et que perd-on? Donnez un exemple de chaque cas dans ce pipeline.
Afficher la réponse
La machine virtuelle et tout son contenu sont perdus : le code extrait, les paquets installés, l’image ticket-api:candidate et le conteneur actif. Ce que le flux de travail a enregistré hors de la machine virtuelle reste. Cela comprend les caches : les téléchargements pip, 44 Mo, et les couches BuildKit avec type=gha. Les artefacts (reports et build-info) restent aussi. Il en va de même pour l’image dans GHCR, l’attestation, les journaux et le résultat de la tâche. needs: utilise ce résultat pour démarrer la tâche suivante.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Prédiction : Sam fusionne une demande de tirage qui modifie seulement README.md. L'exécution sur main pousse-t-elle une nouvelle image, et son empreinte est-elle identique à la précédente? Qu'indique alors build-info.json?
Afficher la réponse
Oui, l’exécution sur main exécute les deux tâches et pousse une nouvelle image. Son étiquette contient sha- suivi du commit de fusion. Ses couches viennent du cache, car le Dockerfile ne copie pas README.md. Mais l’empreinte est différente. Les étiquettes font partie de l’image. Elles contiennent le nouveau commit (revision), une nouvelle heure created et la nouvelle étiquette. Le dépôt de référence l’a montré avec la demande de tirage no 1 : aucun changement dans l’application, mais quand même sha256:a8af875a… plutôt que sha256:6c9bb4ed…. build-info.json enregistre la nouvelle empreinte avec le nouveau commit et la nouvelle exécution. C’est correct : chaque empreinte mène à exactement un commit.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Vérification finale : reproduisez une vérification en échec de votre pipeline sur votre ordinateur. Retrouvez ensuite le commit source de votre image la plus récente sur main. Montrez les commandes et le résultat de chacune.
Afficher la réponse
Reproduire : par exemple, retirez from None dans ticket_api/config.py et exécutez scripts/check.sh (ou ruff check .). L’exécution s’arrête à Lint (analyse statique) avec B904 à ticket_api/config.py:28:13, Found 1 error. (une erreur trouvée) et le code de sortie 1. C’est le même constat que dans la première exécution rouge sur GitHub. Exécutez ensuite git restore ticket_api/config.py. Retracer : docker buildx imagetools inspect ghcr.io/<you>/ticket-api-ci:sha-<7> donne l’empreinte. gh attestation verify oci://ghcr.io/<you>/ticket-api-ci@<digest> --owner <you> réussit et, avec --format json, indique l’exécution et le commit. gh run download <run> -n build-info donne la même empreinte et le même commit. La page de l’exécution montre les deux tâches vertes et l’artefact reports. git show <commit> montre le code. La personne qui révise note le résultat démontré si l’échec a été reproduit localement avec le même code de règle qu’en CI. Les deux chemins doivent aussi indiquer le même commit.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Les preuves répondent aux questions d’une personne qui n’était pas là. Conservez le rapport de test (
reports), le registre de l’empreinte, du commit et de l’exécution (build-info), l’attestation signée et les étiquettes. if: always()conserve le rapport d’une exécution en échec. Les preuves de construction existent seulement si une image a été poussée.gh attestation verifyvérifie la signature et une politique. Une politique plus stricte, comme une branche, peut refuser une image.- Retracez toute empreinte :
build-info.json→ exécution → commit, avec l’attestation comme deuxième chemin signé. - L’ensemble de règles
Protect mainexige une demande de tirage et les deux vérifications, avec la branche à jour. Une vérification obligatoire est un nom : si vous renommez une tâche, mettez à jour l’ensemble de règles.
Réinitialisation. Sur votre ordinateur : docker image rm ticket-api:check, puis rm -rf reports evidence build-info.json junit.xml dans le projet. Pour réinitialiser les fichiers du projet, copiez l’instantané end-of-m02 par une demande de tirage, comme dans Utiliser un instantané. Un instantané ne contient aucun paramètre GitHub : recréez l’ensemble de règles Protect main s’il manque. Pour recommencer le module avec un nouveau dépôt, supprimez l’ancien sur GitHub (Settings, au bas de la page). Supprimez ensuite son paquet dans Package settings (paramètres du paquet).
Le module. Vous avez commencé avec des vérifications qui s’exécutaient seulement sur le Mac d’Amira. Maintenant, chaque changement lance l’analyse statique, 25 tests et une vérification des vulnérabilités sur un exécuteur propre. Le pipeline construit ensuite une image, la teste sommairement et la pousse par empreinte. Chaque image a des preuves qui mènent à son commit, et main n’accepte aucun changement sans les vérifications. Le module 3 prend cette empreinte et la promeut, sans modification, en préproduction, puis en production.
Leçon suivante : Séparer la configuration