Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

CI & Actions

Chaque workflow se trouve dans .github/workflows/. Les sections ci-dessous les regroupent par déclencheur : automatiques lors d’événements git, ou workflows déclenchés par les mainteneurs/advisory via workflow_dispatch et planifications.

Flux de travail automatiques

Portail de qualité (ci.yml)

Se déclenche à chaque PR ciblant master et lors des pushes approuvés vers master. Job composite avec plusieurs branches de matrice :

  • fmt : cargo fmt --all -- --check
  • history-guard : récupère l’historique complet et vérifie le commit testé par rapport à origin/master ; les pull requests utilisent explicitement github.event.pull_request.head.sha, tandis que les pushes de confiance et les exécutions de la merge-queue utilisent github.sha. Le garde-fou et son test de fixture rejettent un résultat vide de git merge-base, empêchant une seconde racine greffée de faire s’effondrer git blame après la fusion
  • lint : cargo clippy --workspace --exclude zeroclaw-desktop --all-targets --features ci-all -- -D warnings, puis cargo doc --no-deps --workspace --exclude zeroclaw-desktop (les avertissements de rustdoc sont fatals via .cargo/config.toml build.rustdocflags ; desktop est exclu pour correspondre à xtask build_api / au déploiement de la documentation et éviter GTK/glib-sys sur l’exécuteur de lint), ainsi que le contrôle d’hygiène des commentaires
  • build : matrice : x86_64-unknown-linux-gnu, aarch64-apple-darwin, x86_64-pc-windows-msvc
  • check : trois passes avec les avertissements traités comme des erreurs sur l’espace de travail (à l’exclusion de zeroclaw-desktop) : toutes les fonctionnalités ; sans fonctionnalités par défaut ; et les fonctionnalités par défaut avec --all-targets, qui est le seul volet à compiler les cibles de test pour l’ensemble de fonctionnalités par défaut
  • check-32bit : i686-unknown-linux-gnu sans les fonctionnalités par défaut
  • bench : vérification de la compilation des benchmarks
  • test : le contrôle autonome de l’hôte du protocole de microprogramme défini par scripts/ci/firmware_protocol_gate.sh et cargo nextest run --locked --workspace --exclude zeroclaw-desktop sous Linux, y compris les garde-fous architecturaux d’isolation des écritures de configuration et de couverture Fluent (aucune chaîne brute destinée à l’utilisateur)
  • parallel-runtime-test : tests répétés d’exécution/canal dans le même processus depuis scripts/ci/parallel_runtime_test_gate.sh, exécutés en parallèle avec la tâche de test principale pour les chemins de PR pertinents et systématiquement lors des poussées sur master et des exécutions de la file d’attente de fusion
  • sécurité : cargo deny check
  • nix-eval : évalue les assertions du module NixOS (vérification de flake nixos-module-eval)
  • docs-style : lint du Markdown, vérification des tirets longs dans le texte et contrôle des liens sur les lignes modifiées via scripts/ci/docs_quality_gate.sh et scripts/ci/docs_links_gate.sh

fmt s’exécute en premier comme point de contrôle série peu coûteux. Chaque autre tâche déclare needs: [fmt] directement ou transitivement et se déploie une fois le formatage validé ; CI Required Gate agrège tous les résultats. La protection de branche épingle la tâche de point de contrôle composite. Une PR ne peut pas être fusionnée tant que ce point n’est pas au vert. L’exécution du push sur master conserve le même signal de qualité tout en amorçant des caches Rust de confiance pour les exécutions de PR ultérieures.

Une exécution CI requise et fraîche constitue généralement la preuve partagée des surfaces de Cargo qu’elle exécute réellement. Une réexécution locale de la même commande Cargo sur le même HEAD, la même cible et le même jeu de fonctionnalités ne constitue qu’une confiance redondante, pas une preuve plus solide. Avant de demander l’exécution supplémentaire de Cargo ou de Clippy, comparez la surface modifiée avec les fichiers de workflow actuels et les vérifications effectives sur la PR. Une validation supplémentaire est justifiée là où la validation requise ne prouve pas l’objet de la revue :

  • une plateforme a reçu des vérifications de compilation mais pas de tests ;
  • une plateforme, un crate ou un chemin est en dehors de la tâche de lint requise ;
  • un changement de bureau n’a pas déclenché le workflow de bureau ;
  • une cible de release est en dehors de la matrice PR et n’est couverte que par les workflows release/manual ;
  • un CI périmé, annulé, ignoré ou indisponible n’est pas une preuve fraîche.

Lorsqu’une définition ou un import est conditionné par une fonctionnalité, comparez son prédicat cfg à celui de chaque utilisateur. Validez à la fois la configuration activée et chaque configuration désactivée pertinente : un passage avec la fonctionnalité activée prouve que l’utilisateur fonctionne toujours, tandis que la vérification no-default-features à l’échelle de l’espace de travail détecte les écarts générant des avertissements, comme les définitions privées ou les imports inutilisés. Cette passe exécute cargo check sans --all-targets, et ne compile donc jamais les cibles de test : un utilitaire conditionné par le simple test, dont les seuls appelants sont conditionnés par une fonctionnalité, est plutôt détecté par l’étape default-features/all-targets. Des combinaisons ciblées de fonctionnalités restent nécessaires lorsque aucune des configurations CI requises n’exerce le prédicat modifié.

Tests de plateforme planifiés (platform-tests.yml)

Exécute cargo nextest run --locked --workspace --exclude zeroclaw-desktop --no-fail-fast sur macos-14 et windows-latest après une vérification rapide du formatage sous Linux. La matrice est exécutée pour :

  • les pull requests qui modifient platform-tests.yml lui-même ;
  • déclenchements manuels ; et
  • la planification nocturne à 03:17 UTC.

Les tâches utilisent continue-on-error et ne contribuent pas à CI Required Gate. Elles constituent des éléments attestant la portabilité, et non des exigences de fusion. Les PR de code ordinaires ne lancent pas automatiquement la matrice ; les responsables de maintenance peuvent la déclencher manuellement sur une branche lorsqu’une validation ciblée de la plateforme est utile. Le workflow ne s’exécute pas lors des événements push ou merge_group ordinaires. Les exécutions nocturnes et déclenchées manuellement sur master peuvent écrire dans des caches de confiance ; les exécutions de pull request ne le peuvent pas. --no-fail-fast permet de garder visibles les échecs de chaque plateforme dans une seule exécution.

Analyse quotidienne (daily-audit.yml)

Exécute cargo deny check advisories quotidiennement à 09:00 UTC sur l’arborescence des dépendances. Ouvre une issue en cas de résultats. Aucune action requise sauf si une vulnérabilité est signalée.

Audit npm quotidien (daily-npm-audit.yml)

Exécute npm audit --audit-level=high quotidiennement à 09:23 UTC sur web/package-lock.json. Ouvre une issue security + dependencies dédupliquée lorsque des avis npm de haute sévérité affectent le lockfile web commité.

Analyse d’image Trivy hebdomadaire (trivy-scheduled.yml)

Analyse les images GHCR dist et default-features publiées chaque samedi et charge les résultats HIGH/CRITICAL dans l’onglet Security au format SARIF. L’analyse est en mode rapport uniquement (exit-code: 0 pour les résultats), mais une image attendue manquante fait échouer le job avant la configuration de Trivy, avec le tag absent et le workflow éditeur propriétaire indiqués dans l’erreur.

Canary hebdomadaire du bucket Scoop (scoop-bucket-canary.yml)

Répète le processus de publication Scoop avec la version stable actuelle chaque lundi. Il résout le dernier tag vX.Y.Z et appelle pub-scoop.yml avec dry_run: true et credential_canary: true, afin de tester le véritable SCOOP_BUCKET_TOKEN sur le véritable bucket sans rien écrire.

credential_canary est la partie de ce contrat qui applique le refus par défaut : l’absence de SCOOP_BUCKET_REPO ou de SCOOP_BUCKET_TOKEN fait échouer l’exécution, et les identifiants configurés doivent parvenir à la sonde d’autorisation git push --dry-run. Une exécution manuelle générique de pub-scoop.yml avec uniquement dry_run: true reste permissive pour la génération du manifeste et peut ignorer cette sonde lorsque les identifiants sont indisponibles ; n’utilisez pas le mode générique comme preuve de vérification des identifiants.

Cela existe parce que SCOOP_BUCKET_TOKEN est lié au compte : il expire et perd silencieusement le droit d’écriture lorsque l’autorisation de collaborateur de l’identité propriétaire sur le bucket change. Les deux cas se sont produits. Avant le canary, le seul processus qui utilisait l’identifiant était le job scoop post-publication ; un jeton invalide n’a donc été découvert qu’après la création et l’annonce de la release, et le bucket a dû être mis à jour manuellement.

Le canari détecte l’obsolescence des identifiants. Il n’est délibérément pas chargé de maintenir le bucket dans un état correct, et il n’est pas intégré à Release Stable : des identifiants expirés de gestionnaire de paquets ne doivent jamais bloquer ni retarder une release.

Comment le bucket Scoop reste correct

Aujourd’hui, le module de publication des versions est le seul composant d’écriture automatisé :

  1. pub-scoop.yml publie lors d’une release. Les utilisateurs de Scoop voient immédiatement la nouvelle version lorsque cette opération réussit. Il nécessite le jeton inter-dépôts SCOOP_BUCKET_TOKEN, qui constitue le point fragile.
  2. Les mainteneurs récupèrent les pushes échoués. Faites tourner ou réparez le jeton, déclenchez Scoop Bucket Canary pour le vérifier via le chemin credential_canary en mode fail-closed, relancez le publisher avec dry_run: false, et confirmez que le manifeste du bucket contient la version de la release.

Un Excavator côté bucket est proposé dans scoop-zeroclaw#1. Une fois ce workflow fusionné, que le dépôt du bucket accorde à Actions l’autorisation de lecture/écriture des workflows et qu’un test de fumée effectué par un mainteneur prouve qu’il valide une mise à jour, il pourra devenir une couche de récupération indépendante des identifiants. Tant que ces trois conditions ne sont pas remplies, ne supposez pas qu’un éditeur défaillant se rétablira automatiquement.

Les blocs checkver et autoupdate sont déjà indispensables pour le parcours Excavator prévu. Le parcours de push actuel utilise également scripts/release/scoop_metadata.sh pour déduire le modèle d’URL de publication à partir de autoupdate, de sorte que les deux parcours partagent un même contrat de manifeste. Ne supprimez pas ces blocs et ne les retirez pas manuellement de dist/scoop/zeroclaw.json.

Étiqueteur de chemin de PR (pr-path-labeler.yml)

Applique automatiquement les libellés de chemin et de portée en fonction des fichiers modifiés. Il s’exécute à l’ouverture, à la réouverture et à chaque mise à jour poussée sur la branche de la PR. Comme sync-labels: true est activé, les libellés définis dans .github/labeler.yml sont recalculés à partir de l’ensemble actuel des fichiers de la PR.

Ce workflow n’applique pas actuellement les étiquettes risk:*, size:*, type:*, contributor-tier, status, resolution, stale ou pickup. Si une étiquette de chemin/portée est manquante sur une PR, vérifiez si les chemins dans .github/labeler.yml couvrent les modifications.

Dependabot dispose d’une configuration d’étiquettes distincte dans .github/dependabot.yml pour ses propres PR. Les PR de mise à jour Cargo commencent par dependencies ; les PR de mise à jour GitHub Actions et Docker commencent par ci et dependencies.

Planificateur de tableau de bord de projet (project-dashboard-plan.yml)

S’exécute manuellement pour un numéro d’issue unique. Il lit l’état et les labels de l’issue, puis écrit un résumé d’étape purement informatif proposant la valeur existante de Project Status qui correspond le mieux à l’issue.

Ce workflow ne s’exécute pas automatiquement sur les événements d’issues, n’écrit pas les champs ProjectV2, ne modifie pas les issues, n’ajoute pas de labels, ne publie pas de commentaires et ne recalcule pas les labels PR risk:*, size:* ou type:*. La mutation live de ProjectV2 ou la planification automatique des événements d’issues nécessite un mappage de champs, une politique de déclenchement et un credential à portée de projet, approuvés séparément.

Valider le titre de la PR (pr-title.yml)

S’exécute à chaque ouverture/modification/synchronisation de PR. Exécute les tests unitaires du validateur (scripts/check-pr-title.test.sh) et vérifie que le titre de la PR respecte les Conventional Commits (scripts/check-pr-title.sh).

Déployer la documentation mdBook sur Pages (docs-deploy.yml)

Déclenché lors d’un push de tag (et via workflow_dispatch) ; compile et publie la documentation versionnée sur la branche gh-pages. Voir Release Runbook → Versioned documentation deployment pour les règles de version minimale et d’amorçage.

Vérification PR de l’image Docker (docker-image-pr.yml)

S’exécute uniquement lorsque des fichiers d’image Docker, Compose ou de contexte Docker de release sont modifiés. Elle valide la configuration Compose fusionnée par défaut et Alpine et, pour les modifications qui ne concernent pas uniquement Compose, génère les images smoke précompilées par défaut et Debian ainsi que les Dockerfiles source, sans les publier. Les images source par défaut et Alpine sont générées pour linux/amd64 et linux/arm64 ; l’image source Debian est générée pour linux/amd64. Des voies distinctes Alpine et Debian linux/amd64 activent plugins-wasm-runtime-only, afin que leurs contextes de build vérifient en continu que le contrat WIT du dépôt est disponible pour les builds source avec plugins activés.

L’image source all-features du Containerfile est construite pour linux/amd64 lorsque ce fichier ou le workflow Docker est modifié. Elle utilise une portée de cache isolée et n’est ni chargée ni poussée. La variante Alpine amd64 exécute les deux binaires, démarre l’image construite via la configuration Compose fusionnée et vérifie l’état de santé de la passerelle ainsi que les interfaces du tableau de bord. La variante Alpine arm64 assure uniquement la couverture de la compilation et de l’assemblage de l’image. Les modifications concernant uniquement Compose utilisent une matrice Alpine amd64 réduite afin de continuer à tester le contrat d’exécution sans reconstruire les images sans rapport. Tous les jobs disposent d’autorisations de dépôt en lecture seule et d’aucune autorisation d’écriture dans le registre.

Publication Docker (docker-publish.yml)

Construit, signe et analyse la matrice à quatre variantes générée depuis dev/ci/docker-tags.toml : minimal, default-features, dist et all-features. Un tag v* créé manuellement déclenche ce workflow directement. Une release stable démarrée avec workflow_dispatch crée son tag via GITHUB_TOKEN, ce qui n’émet pas de nouvel événement de push de tag ; ainsi, release-stable-manual.yml appelle Docker Publish de manière synchrone sur le tag de release immuable une fois que les jobs de release canonique et Docker ont réussi.

Cette matrice complète plutôt qu’elle ne remplace les images préconstruites latest, versionnées et debian de la version stable. Les deux chemins utilisent des entrées de build différentes et publient des tags distincts.

Publication Discord (discord-release.yml)

Se déclenche après une version stable réussie. Publie les notes de version sur le Discord communautaire.

Publication de tweet (tweet-release.yml)

Se déclenche après une version stable réussie. Publie un tweet d’annonce.

Vérification hebdomadaire de la fraîcheur d’AUR (aur-freshness-check.yml)

Compare la version AUR publiée de zeroclawlabs à la version stable actuelle publiée sur GitHub chaque lundi, et échoue si l’AUR est en retard.

La publication vers l’AUR fonctionne sans suivi : si pub-aur.yml échoue, aucune nouvelle vérification n’est effectuée, et le paquet prend donc du retard sans que personne ne s’en aperçoive. C’est exactement ce qui s’est produit après v0.8.4. Une fenêtre de maintenance de aur.archlinux.org a coïncidé avec la publication ; l’unique clonage, qui n’a fait l’objet d’aucune nouvelle tentative, a échoué avec The AUR is down due to maintenance, et le paquet est resté trois semaines en retard sans aucun signalement. Le système de publication n’autorise désormais pas plus d’une publication active réelle et effectue des nouvelles tentatives pour résister à une brève interruption ; GitHub peut remplacer une publication réelle mise en file d’attente plus tôt dans le même groupe de concurrence, tandis que les exécutions à blanc utilisent un groupe distinct. À chaque tentative, l’état de référence du paquet est cloné à nouveau, et le système refuse de remplacer un tuple epoch:pkgver-pkgrel plus récent par un autre plus ancien. Un budget de nouvelles tentatives ne peut toujours pas couvrir tous les échecs ; ce contrôle constitue donc le garde-fou qui rend visible un échec passé inaperçu ou une exécution remplacée.

Si le RPC de l’AUR est inaccessible, la vérification émet un avertissement et réussit au lieu d’échouer. Une panne de l’AUR est un problème de disponibilité en amont, et non d’obsolescence des paquets, et la prochaine exécution planifiée effectue une nouvelle vérification. L’obsolescence est durable, donc une détection retardée est acceptable ; une page hebdomadaire consacrée à la fenêtre de maintenance de quelqu’un d’autre ne l’est pas.

La documentation est générée et publiée dans le cadre du pipeline de publication plutôt qu’à chaque push sur master. La traduction est un workflow local uniquement pour les PR dédiées au cache de traduction, les nouvelles locales et les passes de traduction de publication. Les PR de documentation courantes en anglais peuvent différer les nombreuses modifications générées dans les fichiers .po. Consultez Documentation et traductions pour les recommandations aux contributeurs et le guide opérationnel de publication pour la procédure de publication.

Flux de travail manuels et consultatifs

Analyse mensuelle des obsolétés (monthly-outdated.yml)

Scan mensuel planifié le 1er de chaque mois à 09:00 UTC. Exécute cargo outdated --workspace sur tous les membres du workspace. Ouvre une issue dependencies-labeled lorsque des deps obsolètes sont trouvées. Permissions : contents: read + issues: write. La garde de déduplication empêche l’accumulation si l’issue précédente est toujours ouverte.

Première étape de triage pour une nouvelle issue : vérifiez si les crates obsolètes signalées présentent des changements de version incompatibles avec semver et si l’API de la crate consommatrice a changé. Si ce changement est trivial (patch/mineur), créez une PR courte portant uniquement sur les dépendances. Si la mise à niveau est bloquée par des cassures de compatibilité semver, fermez l’issue avec une note et le nom de la crate bloquante.

Génération multiplateforme (cross-platform-build-manual.yml)

Déclenchement manuel de la compilation des binaires de publication pour l’ensemble de la matrice de cibles : Linux x86_64/aarch64 GNU et MUSL, ainsi que armv7 et arm hard-float, macOS Intel/ARM, Windows x86_64 et aarch64-linux-android (compilé avec le NDK). Utilisez ceci pour vérifier qu’une branche se compile sans erreur sur les cibles non Linux avant de créer le tag.

Chaque déclenchement exécute également une petite matrice de tests de fumée des outils de release, indépendamment des builds. Définissez release_tools_only lorsque seules ces vérifications sont nécessaires ; les jobs web et release-build sont alors ignorés. Sur Linux x86_64 hébergé par GitHub et de confiance, le test de fumée installe l’archive de cross dans la version épinglée, confirme la présence de cross et de cross-util, puis consigne cross --version. Sur Windows x86_64 hébergé par GitHub et de confiance, il utilise la même version de Rust et la même structure de chemin de Bash vers Cargo que le workflow de release stable, puis consigne cargo-tauri.exe --version et cargo tauri --version. Chaque exécution de la matrice consigne le commit exact testé et l’architecture du runner dans le résumé public du job. Le test de fumée utilise des autorisations en lecture seule sur le dépôt et ne comporte aucun job de publication, environnement, secret ni téléversement d’artefact.

Les jobs de compilation MUSL installent également cross via scripts/ci/install_release_tool.sh, qui télécharge l’artefact de version amont exact épinglé et en vérifie la somme SHA-256 avant de l’installer. Le job obligatoire Repository Structure teste la correspondance prise en charge entre le runner et l’artefact, ainsi que le contrat du workflow smoke, sans effectuer d’appels réseau.

Clippy multiplateforme (cross-platform-clippy.yml)

Couverture manuelle et hebdomadairement planifiée du linter d’avis sur les cibles macOS aarch64 et Windows x86_64. Elle correspond à la commande de linter PR requise avec --target défini pour chaque plateforme, mais ne s’exécute pas intentionnellement sur les PR et ne fait pas partie de CI Required Gate.

Clippy Linux obligatoire, Clippy multiplateforme à titre indicatif et Clippy Windows ciblé appellent scripts/ci/run_clippy.sh. Cet exécuteur prend en charge les formats de commande pris en charge, la propagation des codes de sortie de Cargo ainsi que les diagnostics partagés relatifs à la durée, au cache, au nombre de compilations et au nombre de téléchargements. Les fichiers de workflow continuent de gérer les déclencheurs, les exécuteurs, les chaînes d’outils, les caches, les délais d’expiration et l’inclusion dans les contrôles obligatoires.

Version Stable (release-stable-manual.yml)

Déclenchement manuel du pipeline de publication complet. Compile toutes les cibles, crée la GitHub Release, pousse les images Docker préconstruites latest, versionnées et debian vers GHCR, appelle la matrice générée des variantes Docker sur le tag de publication, déclenche le redéploiement du site web et invoque les sous-workflows de distribution (Scoop, AUR, Discord, tweet). Homebrew Core détecte les nouvelles versions via son propre service autobump. Deux barrières d’environnement nécessitent l’approbation d’un mainteneur en cours d’exécution : github-releases (le job publish) et docker.

Les ressources téléchargeables utilisent des attestations Build Level 2 hébergées par GitHub. Les bundles hors ligne et le matériel de racine de confiance sont fournis dans une seule archive de vérification, et les deux formats SBOM font l’objet d’une somme de contrôle et d’une attestation avant la création de la version. Cosign reste limité à la signature des images GHCR.

Consultez le Runbook de publication pour la procédure complète.

Les outils de compilation utilisés uniquement pour les releases ne sont pas compilés depuis les sources à chaque exécution. Le workflow installe les binaires de release en amont épinglés de cross et de Tauri CLI via scripts/ci/install_release_tool.sh ; ce script vérifie le SHA-256 stocké dans le dépôt pour chaque archive propre au runner avant de placer le binaire dans le répertoire bin de Cargo. La mise à jour de l’un ou l’autre outil nécessite de mettre à jour simultanément sa version, le nom de son artefact et sa somme de contrôle, puis d’exécuter scripts/ci/install_release_tool.test.sh.

Éditeurs de paquets

Chacun se déclenche sur workflow_dispatch avec un paramètre de version. Ils sont également invoqués depuis le workflow de publication après une publication réussie.

Flux de travailCe que cela fait
pub-aur.ymlMet à jour le PKGBUILD de l’Arch User Repository et le pousse vers l’AUR
pub-crates.ymlEmpaquette et vérifie la version coordonnée de l’espace de travail, puis la publie sur crates.io dans l’ordre des dépendances, derrière la barrière d’environnement crates-io
pub-scoop.ymlMet à jour le manifeste Scoop pour Windows

Le service officiel d’autobump de Homebrew Core découvre les versions stables GitHub et ouvre des mises à jour de formules de manière indépendante. Ne restaurez pas un éditeur Homebrew appartenant au projet ni un jeton de fork ; cela duplique l’automatisation faisant autorité de Homebrew.

Secrets requis

SecretUtilisé par
AUR_SSH_KEYpub-aur.yml
CARGO_REGISTRY_TOKENSecret du dépôt transmis explicitement à pub-crates.yml et référencé uniquement par son job de publication protégé ; la v0.8.5 nécessite publish-new pour zerorelay, zeroclaw-relay-proto et zeroclaw-tls, tandis que les mises à jour coordonnées ultérieures nécessitent publish-update
DISCORD_WEBHOOK_URLdiscord-release.yml
TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET, TWITTER_CONSUMER_API_KEY, TWITTER_CONSUMER_API_SECRET_KEYtweet-release.yml
SCOOP_BUCKET_TOKENpub-scoop.yml, release-stable-manual.yml, scoop-bucket-canary.yml ; PAT à granularité fine limité à zeroclaw-labs/scoop-zeroclaw avec Contents en lecture/écriture
WEBSITE_REPO_PATrelease-stable-manual.yml (déclenche le redéploiement du dépôt du site web)
GITHUB_TOKEN (automatique)Tous les workflows qui poussent des commits, ouvrent des PR ou poussent des images vers GHCR

Les images Docker sont poussées vers GHCR à l’aide du GITHUB_TOKEN automatique ; il n’existe aucun jeton de registre distinct. Stockez CARGO_REGISTRY_TOKEN en tant que secret du dépôt et ne transmettez que ce secret nommé au workflow de publication réutilisable. Le workflow appelé ne le référence que dans l’étape de publication irréversible, dont le job nécessite une approbation via l’environnement crates-io ; la pré-vérification sans jeton ne le référence ni ne l’exporte. La pré-vérification empaquette le même commit immuable de la version avant qu’un approbateur puisse démarrer le job de publication.

La plupart des crates de l’ensemble de publication coordonnée existent déjà et sont éligibles à la publication approuvée sur crates.io. La version v0.8.5 crée en plus zerorelay, zeroclaw-relay-proto et zeroclaw-tls; son jeton d’amorçage doit donc inclure publish-new. Le jeton d’environnement reste la méthode d’amorçage jusqu’à ce que chaque crate dispose d’une entrée de publication approuvée pour ce flux de travail. Une fois ces entrées configurées, migrez le job afin que GitHub échange l’identité OIDC contre un jeton à courte durée de vie au lieu de conserver CARGO_REGISTRY_TOKEN.

L’organisation désactive actuellement les clés de déploiement sur le bucket Scoop, et le GITHUB_TOKEN automatique ne peut pas écrire dans un autre dépôt. Limitez la portée de SCOOP_BUCKET_TOKEN strictement au bucket ; ne réutilisez pas le jeton CLI étendu d’un mainteneur. Le publieur vérifie l’accès en écriture avec git push --dry-run, puis utilise le même transport Git pour la mise à jour réelle.

Rotation de SCOOP_BUCKET_TOKEN

Comme les clés de déploiement ne sont pas disponibles, cet identifiant d’authentification est un jeton d’accès personnel et présente donc deux modes de défaillance indépendants, qui ont tous deux déjà perturbé une mise en production :

  1. Le jeton expire. Les PAT à granularité fine ont une durée de vie maximale ; ce problème se reproduit donc selon une périodicité fixe, indépendamment de tout autre changement.
  2. L’identité propriétaire perd le droit d’écriture sur le bucket. Le token peut toujours être valide alors que le compte qui lui est associé n’est qu’un collaborateur read. Cela produit remote: Permission to zeroclaw-labs/scoop-zeroclaw.git denied to <account> et HTTP 403, et non une erreur d’authentification ; le problème semble donc venir du code alors qu’il s’agit d’un problème d’autorisations.

Associez le token au compte ZeroClaw-Bot, jamais à un compte personnel, afin que le processus de publication ne dépende pas des identifiants d’un seul responsable de maintenance. Pour effectuer une rotation :

  1. En tant que ZeroClaw-Bot, créez un PAT à granularité fine avec Propriétaire de la ressource zeroclaw-labs, Accès aux dépôts limité au seul dépôt zeroclaw-labs/scoop-zeroclaw, et Autorisations du dépôt → Contenu : Lecture et écriture. Rien d’autre.
  2. Confirmez que l’organisation a approuvé le jeton. Les PAT à granularité fine dont le propriétaire de ressources est une organisation restent en attente jusqu’à leur approbation, et un jeton en attente permet de s’authentifier, mais pas d’effectuer un push.
  3. Confirmez que ZeroClaw-Bot dispose toujours de l’autorisation write sur le bucket : gh api repos/zeroclaw-labs/scoop-zeroclaw/collaborators/ZeroClaw-Bot/permission --jq '.role_name'. L’étape 1 n’accorde pas d’accès au dépôt ; elle définit uniquement ce que le jeton peut utiliser. Un jeton ne peut pas dépasser les autorisations dont dispose déjà son propriétaire.
  4. Définissez le secret : gh secret set SCOOP_BUCKET_TOKEN --repo zeroclaw-labs/zeroclaw.
  5. Vérifiez sans toucher au bucket en déclenchant Scoop Bucket Canary. Une exécution réussie prouve que le nouveau jeton peut effectuer un push.

Notez la date d’expiration dans un emplacement durable lors de la rotation. Le canary détectera de toute façon un jeton expiré dans un délai d’une semaine, mais seulement après qu’il aura déjà cessé de fonctionner.

Propriété du paquet AUR

Le paquet appartenant au projet est actuellement zeroclawlabs, maintenu par zeroclaw-bot. Le paquet portant le nom canonique zeroclaw est un paquet tiers et ne peut pas être récupéré en effectuant une rotation de AUR_SSH_KEY. Si ce mainteneur reste inactif, suivez la procédure de demande d’orphelinage AUR avant de modifier pkgname ou la cible de clonage du workflow. Une fois la propriété transférée, coordonnez le renommage ou la fusion du paquet en une seule modification révisée.

Comportement du cache de construction

La plupart des tâches de ci.yml qui utilisent principalement Rust mettent en cache via le composite local ./.github/actions/rust-cache, qui sélectionne le backend de cache à partir du même commutateur CI_USE_BLACKSMITH que celui qui sélectionne le runner : useblacksmith/rust-cache (disque NVMe persistant de Blacksmith) lorsque la tâche s’exécute sur un runner Blacksmith, et Swatinem/rust-cache dans le cas contraire. Toute valeur du commutateur autre que true (y compris lorsqu’il n’est pas défini, ainsi que pour toute PR provenant d’un fork) revient à Swatinem/rust-cache sur des runners hébergés par GitHub, de sorte que la mise en cache n’est jamais perdue lorsque Blacksmith est désactivé. Les deux références d’action figurent dans le composite quel que soit le commutateur ; elles doivent donc toutes deux rester dans la liste d’autorisation. Les configurations de compilation macOS et Windows restent sur Swatinem/rust-cache, et les tâches fmt, nix-eval et docs-style (dont aucune ne compile l’espace de travail) n’utilisent aucun cache Rust. Ces comportements sont utiles à connaître lors du triage des échecs intermittents liés au cache :

  • Les écritures dans le cache sont réservées à master. save-if est conditionné par github.ref == 'refs/heads/master', donc les exécutions de PR lisent le cache alimenté par master mais ne le mettent jamais à jour. Les branches de PR ne peuvent pas polluer le cache partagé avec des artefacts spécifiques à une branche. Le déclencheur push sur master est ce qui donne au workflow une exécution de confiance capable d’écrire dans le cache après les fusions.
  • Le cache est conservé en cas d’échec. L’option cache-on-failure: true est définie pour chaque job, de sorte qu’une exécution partielle permet de réutiliser le cache pour la tentative suivante.
  • Le cache de build Windows est activé. L’étape de build Windows exécute la même action de cache Rust épinglée que Linux et macOS. Si le comportement du cache Windows devient instable ou régresse, annulez la modification du workflow et documentez les preuves d’échec de restauration/sauvegarde dans le ticket consacré au cache.
  • La compilation incrémentale est désactivée. CARGO_INCREMENTAL: 0 au niveau du workflow. Les builds incrémentaux augmentent la taille du cache et produisent des artefacts non reproductibles dans des conditions de partielle périmée.
  • cargo-deny et cargo-nextest sont installés à neuf à chaque exécution. Le job security exécute cargo install cargo-deny --locked ; le job test Linux et les deux exécutions planifiées de platform-tests.yml récupèrent le binaire cargo-nextest approprié depuis get.nexte.st. Aucun de ces deux outils n’est mis en cache, si bien que chaque installation ajoute un coût fixe à son job. Remplacer l’un ou l’autre par taiki-e/install-action permettrait de les mettre en cache, mais cette action ne figure pas encore dans la liste blanche.

Lorsque le feu passe au rouge

SymptômePremière chose à vérifier
Release Stable s’arrête à startup_failure avec zéro job après la modification d’une réf uses:Consultez le récapitulatif de l’exécution et la politique Actions du dépôt. Si GitHub signale un rejet de type selected-actions, comparez la référence modifiée avec la liste d’autorisation, ajoutez uniquement le motif rejeté, attendez la propagation des paramètres, puis lancez une nouvelle exécution. Sinon, examinez la définition du workflow ou une autre politique du dépôt ; startup_failure seul n’identifie pas la cause
CI Required Gate rougeCommencez par fmt, puis lint, puis test, puis build
La validation de la version a échouéLa version de Cargo.toml ne correspond pas à l’entrée du workflow, ou la balise existe déjà.
La phase de build de la version de production a échoué.Le journal d’exécution de la cible spécifique. Android est expérimental et s’exécute avec continue-on-error.
Le délai d’attente de la porte d’environnement est dépasséRelancer uniquement la tâche ayant expiré depuis la page d’exécution du workflow
Échec de l’éditeur de distributionRelancez manuellement le sous-flux de travail correspondant avec dry_run: true en premier.

Actions autorisées

Le dépôt exécute les Actions en mode selected, seules les actions de cette liste d’autorisation peuvent s’exécuter. La liste d’autorisation doit rester restreinte ; les nouvelles actions tierces nécessitent l’approbation explicite d’un mainteneur avant d’être ajoutées.

Toutes les références tierces sont épinglées à un SHA de commit complet avec un commentaire de version en fin de ligne ; la colonne de version ci-dessous reprend ce commentaire.

ActionUtilisé dansObjectif
actions/checkout (v6.0.2)La plupart des workflowsExtraction du dépôt
actions/cache (v4.2.3, v5.0.5)docker-image-pr.yml, tweet-release.ymlMise en cache des dépendances génériques et de la base de données Trivy
actions/setup-node (v7.0.0)ci-sbom.yml, ci.yml, cross-platform-build-manual.yml, daily-npm-audit.yml, pub-crates.yml, release-stable-manual.ymlChaîne d’outils Node pour la génération de SBOM npm, les tests et l’audit web, ainsi que les builds web/de bureau
actions/upload-artifact (v7.0.1)release-stable-manual.yml, cross-platform-build-manual.yml, docker-publish.yml, trivy-scheduled.ymlTéléverser les artefacts de build et les artefacts de handoff SARIF Trivy
actions/download-artifact (v8.0.1)release-stable-manual.yml, cross-platform-build-manual.yml, docker-publish.ymlTélécharger les artefacts de build et les artefacts de transfert SARIF Trivy
actions/attest (v4.2.2)release-stable-manual.ymlGénérer une provenance de build de niveau 2 hébergée par GitHub pour les ressources de release
actions/labeler (v6.1.0)pr-path-labeler.ymlAppliquez les étiquettes de chemin/portée depuis .github/labeler.yml
dtolnay/rust-toolchain (stable, v1)ci.yml, platform-tests.yml, pub-crates.yml, release-stable-manual.yml, cross-platform-build-manual.yml, cross-platform-clippy.yml, daily-audit.yml, docs-deploy.yml, codeql.ymlInstaller la chaîne d’outils Rust
Swatinem/rust-cache (v2.9.2)ci.yml (chemin hébergé sur GitHub de ./.github/actions/rust-cache), platform-tests.yml, pub-crates.yml, release-stable-manual.yml, cross-platform-build-manual.yml, cross-platform-clippy.yml, docs-deploy.ymlMise en cache de la compilation/des dépendances de Cargo sur les exécuteurs hébergés par GitHub
useblacksmith/rust-cache (v3.0.1)ci.yml (chemin Blacksmith de ./.github/actions/rust-cache)Mise en cache de la compilation et des dépendances Cargo sur le disque sticky de Blacksmith ; sélectionnée uniquement lorsque CI_USE_BLACKSMITH=true
docker/setup-buildx-action (v3.11.1, v4.0.0)release-stable-manual.yml, docker-publish.ymlConfiguration de Docker Buildx
docker/login-action (v3.4.0, v4.1.0)release-stable-manual.yml, docker-publish.yml, trivy-scheduled.ymlAuthentification GHCR
docker/build-push-action (v6.18.0, v7.1.0)release-stable-manual.yml, docker-publish.ymlConstruction et envoi d’images multi-plateformes
sigstore/cosign-installer (v3.8.1)release-stable-manual.yml, docker-publish.ymlInstaller cosign pour la signature sans clé des images de conteneur GHCR
anchore/sbom-action (v0.24.0)release-stable-manual.ymlGénérer des SBOM SPDX et CycloneDX pour chaque version
aquasecurity/trivy-action (v0.36.0)docker-image-pr.yml, docker-publish.yml, trivy-scheduled.ymlAnalyse des vulnérabilités des conteneurs en mode rapport uniquement
github/codeql-action/upload-sarif (v3.36.2)docker-publish.yml, trivy-scheduled.yml, ci-code-analysis.ymlTéléverser les rapports SARIF de Trivy et Semgrep dans l’onglet Security
github/codeql-action/init (v3.36.2)codeql.ymlInitialiser l’analyse CodeQL (Rust et JS/TS)
github/codeql-action/analyze (v3.36.2)codeql.ymlTéléverser CodeQL SARIF vers l’onglet Sécurité

La GitHub Release elle-même est créée avec gh release create dans le job publish, et non avec une action de release.

Modèles de liste blanche équivalents (intentionnellement restreints) :

actions/*
dtolnay/rust-toolchain@*
Swatinem/rust-cache@*
useblacksmith/rust-cache@*
docker/*
sigstore/cosign-installer@*
anchore/sbom-action@*
aquasecurity/trivy-action@*
github/codeql-action/upload-sarif@*
github/codeql-action/init@*
github/codeql-action/analyze@*

Exporter la politique effective actuelle :

sh

gh api repos/zeroclaw-labs/zeroclaw/actions/permissions
gh api repos/zeroclaw-labs/zeroclaw/actions/permissions/selected-actions

Toute PR qui ajoute ou modifie une source d’action uses: doit inclure une note d’impact sur la liste d’autorisation dans son corps. Évitez les exceptions larges avec des jokers ; n’élargissez la liste d’autorisation que pour les actions manquantes vérifiées.

Règles de maintenance

  • Gardez le CI Required Gate déterministe et petit. L’ajout de jobs au gate nécessite un argument de qualité clair.
  • Toutes les références d’actions tierces doivent être épinglées à un SHA de commit complet (conformément à la politique de liste d’autorisation ci-dessus).
  • Maintenez ci.yml, dev/ci.sh et .githooks/pre-push alignés. Les contrôles partagés doivent résider dans scripts/ci/ ; chaque appelant invoque l’assistant au lieu de copier ses commandes. Pour le contrôle autonome du protocole du firmware, le point d’entrée local documenté est ./dev/ci.sh firmware-protocol.
  • Maintenez scripts/ci/prepare_docker_context.sh, docker-image-pr.yml et le job Docker dans release-stable-manual.yml alignés afin que la validation des PR exerce la même forme de contexte que celle publiée par le workflow de release.
  • Exécutez python3 scripts/ci/release_attestation_contract_test.py après avoir modifié l’attestation de publication, la somme de contrôle, le SBOM ou la séquence d’archivage de vérification.
  • Le job de gate docs-style exécute bash scripts/ci/docs_quality_gate.sh (lint markdown + vérification de prose des tirets cadratins) et bash scripts/ci/docs_links_gate.sh (gate de liens sur les lignes modifiées). Exécutez les deux scripts localement avant de pousser des modifications de documentation.

Retour arrière d’urgence

Si la liste d’autorisation bloque une action critique en cours d’incident :

  1. Rétablir temporairement la politique des Actions sur all.
  2. Restaurez la liste autorisée selected après avoir identifié l’entrée manquante.
  3. Enregistrez l’incident et le delta final de la liste d’autorisation.

C’est le seul chemin justifié vers le mode all, et il ne devrait jamais perdurer au-delà de l’incident.