Imported from elgabo86/gablue (
.github/AGENTS.md). Install upstream withnpx skills add elgabo86/gablue --skill .github. Copyright stays with the author.
Workflows GitHub Actions (.github/) — gablue
Sous-document du AGENTS.md racine. Couvre .github/ : workflows, composite action, dependabot. Contenu de l'ISO (installer/, local-build/, live) → installer/AGENTS.md. Même règle que la racine : à jour avant tout commit touchant .github/ ou les tags de commit.
gablue-builds.yml
Workflow principal. Déclencheurs : push sur main avec tags ([main], [nvidia], [dx], [all], [all-iso]), PRs, schedule quotidien (02:00 UTC), workflow_dispatch.
paths-ignore:.md/.txtne déclenchent pas de build (les changements.github/**déclenchent bien si le tag est présent — jobs filtrés par tag de commit)concurrencycancel-in-progress : un commit tagué annule la build en cours → pour committer une modif de workflow sans annuler, utiliser[skip ci]- Chaînage ISO (
[all-iso]) : déclenche les 5 variantes (comme[all]), puis le workflow ISO viaworkflow_run. Les sous-chaînes ne collisionnent pas :contains('[all-iso]', '[all]')etcontains('[all-iso]', '[iso]')sont tous deux faux → pas de build ISO immédiat au push
Jobs : build-main, build-nvidia (kernel_type=ogc-lts, nvidia_flavor=nvidia-lts), build-nvidia-open, build-dx (DX_MODE=true), build-nvidia-open-dx (DX_MODE + nvidia-open — restreint à main, jamais sur branche test), update-readme
update-readme: tableau de versions du README depuis les artifactsversions-*(needs sur les 5 builds, ignoré sur PR et branchetest). Download viagh run downloadwrappénick-fields/retry@v4(6×15 s — tolère le 404 transitoire de l'API artifacts) ; présence des 5 JSON vérifiée explicitement avecexit 1(gh CLI peut retourner 0 malgré un 503 partiel) → le retry se déclenche. Commit[skip ci]+ push résilient : boucle 5 tentatives avecgit pull --rebase --autostashentre chaque (absorbe les commits concurrents pendant les ~50 min de build) ; échec du 5e →exit 1(pas de succès silencieux)
reusable-gablue-image.yml
Workflow réutilisable de build d'une image. Inputs : image_name/image_desc/image_variant, source_image (kinoite), fedora_version (44), kernel_type (ogc → KERNEL_FLAVOR), kernel_version (défaut hardcodé, surcharge par job), nvidia_flavor (optionnel), containerfile (optionnel).
Migration rootless (08/2026, Bazzite 033a18f + 625bebb) : tout sans sudo — buildah/podman/skopeo rootless (storage ~/.local/share/containers). Plus de loopback BTRFS ici : le rechunk rootless n'a jamais deux images complètes en storage (raw-img supprimée avant le pull oci-archive), pic ~25-30 G << ~110 G libres. Si saturation un jour (variante DX), réintroduire ./.github/actions/mount-btrfs-storage avec target-dir: ~/.local/share/containers.
Étapes :
- Version kernel auto via
skopeo list-tagssikernel_versionvide — retry 3×10 s (timeouts ghcr.io) - Checkout ; maximisation espace disque
- Build buildah rootless — retry
nick-fields/retry@v4(retry_on: error, timeout 90 min / 120 min DX — miroirs lents) : le script détecte les erreurs réseau (EOF, TLS handshake, DNS, curl timeout…) → exit 1 (retry) ; erreur de build RUN → exit 2 (échec immédiat)retry_on_exit_codeNE DOIT PAS être utilisé : désactive le retry sur timeout (bug nick-fields/retry#145)set +e -o pipefailobligatoire : l'action n'hérite pas du pipefail de GH Actions ; sans lui$?capture le code deteeà travers le pipe| tee→ échec de build masqué (l'étape suivante tentebuildah from raw-imginexistant → pull registres → 404)buildah rmi raw-imgau début de chaque tentative
- Labels OCI préparés dans
${RUNNER_TEMP}/labels.txt(step idrelabel) — ne PAS les appliquer viabuildah config: le rechunk rootless reconstruit le manifeste de zéro (Bazzite625bebb), les labels seraient perdus → passés via--labelàbuild-chunked-ocià l'étape rechunk - SecureBoot check : certificat
/etc/pki/akmods/certs/gablue-secure-boot.derprésent + kmods signés (modinfo | grep sig_id) — échec → image ne boote pas en SecureBoot (enroll côté client :ujust secureboot) - Métriques (durée, disque, taille décompressée raw-img, nb RPMs, kernel, mesa) → JSON
metrics-<image>(rétention 90 j) + step summary en anglais (commentaires YAML en français) - Rechunk rootless rpm-ostree (Bazzite
033a18f) : nettoyage/run+/tmpdans un blocbuildah unshare(unbuildah mountrootless exige le user namespace), puispodman run --privilegedavec--mount=type=image,src=localhost/raw-img,target=/rpm-ostree, sortie--output oci-archive:dans${RUNNER_TEMP}, labels--label(1/ligne),podman rmi -f raw-imgAVANTpodman pull oci-archive:+ taglocalhost/chunked-img. Retry 3×15 s : abandon immédiat sur erreurs I/O (no space left,read-only,disk I/O error) et rpmdb corrompue (database disk image is malformed— présente à chaque tentative, retry inutile) ; retry sur le reste - Tag + push GHCR — retry bash natif (
for attempt in 1 2 3, sleep 15) : push tag version viapodman push --digestfile(préfixecontainers-storage:retiré, Bazzite033a18f)- NE PAS revenir à
skopeo copydepuiscontainers-storage:: transport rootless exige un user namespace — bloqué par AppArmor Ubuntu 24.04+ (Error during unshare(...)) ; podman a un profil AppArmor dédié, skopeo non - Alias tags (
latest, version, pr-N) :skopeo copy docker://…@digest → docker://…:tagAPRÈS signature cosign (Bazziteb97f073) — chaque alias pointe sur le manifeste signé, sans re-upload (transport remote→remote, pas de user namespace) - Deux logins nécessaires :
podman login(push + skopeo, même auth.json) ETdocker login(cosign lit~/.docker/config.json— ne pas retirer) - Pourquoi pas nick-fields/retry@v4 ni wretry.action pour le push : l'action pipe stdout/stderr via Node.js
spawn()— 100+ lignes « Copying blob » (image chunkée) saturent le pipe, l'événement exit n'arrive jamais → hang indéfiniment (wretry.action déprécié Node 20). Boucle bash native dans unrun:hérite du stdio du runner → pas de pipe
- NE PAS revenir à
- Signature Cosign (v2.6.1 — bump v3 +
--use-signing-config=false= chantier séparé, à faire sur ce workflow ET l'ISO en même temps) - Métriques post-push : taille compressée réelle via
skopeo inspect --raw+ jq (somme layers + config blob) → JSON final + summary ; upload des métriques APRÈS cette étape
Version du kernel — cascade à 3 niveaux (chaque niveau validé contre les tags akmods via check_akmods, sinon niveau suivant) :
- Kernel pinné dans le
build.ymlde la branche main de Bazzite — fetchraw.githubusercontent.com/ublue-os/bazzite/main/.github/workflows/build.yml(retry curl 3×10 s) + extraction awk dukernel_versiondu 1er bloc matrix dontkernel_flavorcorrespond exactement (ogc/ogc-ltsancré en fin de clé) etfedora_versioncorrespond ; état du bloc réinitialisé à chaque- item. Suit les kernels préparés en avance par Bazzite - Filet de sécurité : kernel réellement publié sur l'image stable Bazzite — label OCI
ostree.linuxviaskopeo inspect(retry 3×10 s), suffixe.x86_64retiré ; sourceghcr.io/ublue-os/bazzite:stable, oubazzite-nvidia:stablepour nvidia-lts (Bazzite paire nvidia-lts ↔ ogc-lts, commit3eb7b09— akmods-nvidia-lts ne publie plus que des tagsogc-lts-*). Utilisé si le pin main n'a pas ses akmods (kernel RC trop frais) ou fetch/parsing en échec - Fallback : dernière version commune via
skopeo list-tags(retry 3×10 s par repo) — intersection tagsakmodsetakmods-{NVIDIA_FLAVOR}→ filtre{KERNEL_FLAVOR}-{FEDORA_VERSION}-*, exclusion alias non versionnés (test("^[0-9]")), normalisation suffixe.x86_64des alias arch, trisort -V(warning dans les logs)
check_akmods: valideakmods:{FLAVOR}-fc{FEDORA}-{VERSION}et, si NVIDIA,akmods-{NVIDIA_FLAVOR}:{…}(ublue peut arrêter de builder nvidia-lts contre un kernel RC, cf. Bazzitea5897ab)- Manuel :
kernel_versiondans un job → détection skippée
build-gablue-live-isos.yml
Build des ISOs live Plasma complet (tous les 5 jours). Déclencheurs : schedule, workflow_dispatch, push [iso], workflow_run en fin du workflow d'images — ne construit l'ISO que si l'amont a été déclenché par un push (workflow_run.event == 'push'), réussi (conclusion == 'success') et le message contient [all-iso] → images :latest publiées avant les ISOs ; checkout sur workflow_run.head_sha.
- Concurrency ISO : groupe
build-gablue-live-isos-${{ github.run_id }}-isoavecgithub.run_id(pasgithub.ref) — chaque run ISO unique ; sinon unworkflow_rundéclenché par un échec annulerait unworkflow_dispatchISO en cours (même groupe, ses jobs skipped de toute façon) - Titanoboa : installateur bootc générant un squashfs live ;
build_iso.shde l'image Titanoboa (quay.io/fedora/fedora:latest) patché viainstaller/titanoboa_build_iso.sh(bind-mounté, sans-all-root— préserve l'UID 1000 du préfixe Wine) - 5 variantes : main, main-dx, nvidia, nvidia-open, nvidia-open-dx ;
timeout-minutes: 180 - Processus en 2 étapes :
- Image payload via
installer/Containerfile(FROM image Gablue, flatpaks pré-cachés, swap kernel OGC→vanilla pour Secure Boot). Stockage podman sur le loopback BTRFS (actionmount-btrfs-storage) avecimage_copy_tmp_dirredirigé dans le loopback (drop-in/etc/containers/containers.conf.d/) : par défaut podman copie le layer diff du commit (~30 G non compressés) dans/var/tmpsur l'hôte → saturation, loopback sparse affamé, BTRFS read-only (les « corruptions » historiques du loopback n'étaient que ce mécanisme). Chemin explicite/var/lib/containers/image-copy-tmpplutôt que"storage"(résolution buggy, podman#28211). Boucle de build 3 tentatives : pas de retry si log contientno space left on device/read-only file system/disk I/O error(non récupérable — tentative identique, loopback read-only mort) → exit immédiat. Optimisations espace (Bazzite7ecb26a1) :extra-squeeze: "true"sur remove-unwanted-software (~6 G),TMPDIR=/var/lib/containers/image-copy-tmpsur le podman build, stepif: always()affichantdf -h+ taille loopback réelle - Génération ISO via
podman rundirect (remplace l'actionZeglius/titanoboa) : script patché bind-mounté sur/src/build_iso.sh, payloadlocalhost/payload:latestmonté--mount type=image, sortie bind-mountée/output. Image Titanoboa pré-pullée avec retry (5×15 s — timeouts quay.io transitoires)
- Image payload via
- Signature Cosign + attestation de provenance ; upload BuzzHeavier ; release GitHub
latest-live-iso - Job
create-release: artifacts liens/checksums viagh run downloadwrappénick-fields/retry@v4(6×15 s) plutôt qu'actions/download-artifact— l'API artifacts peut renvoyer un 404 « workflow run not found » transitoire juste après la fin des jobs ISO (race de propagation interne) ;actions: readdéclaré ; structure de dossiers identique (merge-multiple: false)
clean-gablue-images.yml
Nettoyage hebdomadaire (dimanche) : images > 90 jours supprimées ; conservation des 7 dernières taggées + 7 dernières non-taggées. Packages : gablue-main, gablue-nvidia, gablue-nvidia-open, gablue-main-dx, gablue-nvidia-open-dx, gablue-main-test, gablue-nvidia-open-test
bump-image-digests.yml
Bump hebdo des images pinnées par digest — mini-ubot maison, équivalent du bot Bazzite ubot-7274 (~1 PR/semaine), mais ouvre une PR au lieu de committer direct.
- Déclencheurs : schedule lundi 04:00 UTC +
workflow_dispatch; concurrency cancel-in-progress - Deps suivies : tableau
DEPSdans le step « Vérifier et bumper » — format"image|fichier", une ligne par image pinnée@sha256:dans un Containerfile. Ajouter une dep = ajouter une ligne (le pin doit exister dans le fichier, sinon warning + skip) - Mécanique :
skopeo inspect(retry 3×10 s, timeouts ghcr.io transitoires) → normalisation du préfixesha256:({{.Digest}}l'inclut selon la version de skopeo — sans strip, faux positif hebdo et sed corrompu@sha256:sha256:…) → comparaison au pin extrait par grep →sedsi différent. Tout à jour → job sans effet - PR : branche
chore/bump-digests-<date>(ou réutilise la branche d'une PR bump ouverte, détectée par préfixe) ; la branche est TOUJOURS main + 1 commit — force-push assumé qui supersede le contenu précédent, jamais de conflit de rebase. Force-push sûr : namespacechore/bump-digests-*réservé au bot, unique écrivain (concurrency) - Validation : l'ouverture de la PR déclenche les builds des 5 variantes (event
pull_request) → merge sur CI verte - Merge sans build : commit/PR sans tag de build → rien au merge ; la build schedule quotidienne (02:00 UTC) ingère le nouveau digest. Push avec
GITHUB_TOKENne déclenche de toute façon jamais de workflow (récursion bloquée par GitHub) - Identité :
github-actions[bot](comme update-readme) ; permissionscontents: write+pull-requests: write; checkout pinné par SHA (convention repo) - Prérequis dépôt : Settings → Actions → General → Workflow permissions → « Allow GitHub Actions to create and approve pull requests » DOIT être coché (réglage
can_approve_pull_request_reviews). Sinongh pr createéchoue :GraphQL: GitHub Actions is not permitted to create or approve pull requests(réglage indépendant despermissions:du workflow). Réglable par API :gh api -X PUT repos/elgabo86/gablue/actions/permissions/workflow -F can_approve_pull_request_reviews=true - Branche orpheline : un run interrompu entre le
git pushet legh pr createlaisse une branchechore/bump-digests-*sans PR ; le step tombe alors en fallback surgit ls-remotepour la réutiliser (sinon nouvelle branche datée à chaque run, anciennes qui traînent)
Composite action mount-btrfs-storage
.github/actions/mount-btrfs-storage/action.yml — remplace ublue-os/container-storage-action : loopback BTRFS zstd:2 sur / + storage podman dessus. losetup --direct-io=on + mount (évite le double cache page-cache hôte+loop ; remplace systemd-mount qui n'expose pas l'option — Bazzite 7ecb26a1) ; mkfs.btrfs -m single -d single (dispositif unique, pas de métadonnées dupliquées).
Runners ubuntu-26.04 depuis 08/2026 (Bazzite dc41cfb) — motivé par des corruptions rpmdb récurrentes (database disk image is malformed) sur ubuntu-24.04. Si ça persiste → cause ailleurs (rpm-6.0/dnf5 de l'image de base).
Pourquoi : les runners ubuntu-24.04 ne montent plus de disque temporaire sur /mnt — l'action amont sautait le montage SILENCIEUSEMENT (simple notice) → storage sur ext4 sans compression → no space left on device sur les gros payload (ISO flatpaks, rechunk DX).
| Input | Défaut | Description |
|---|---|---|
target-dir |
/var/lib/containers |
Répertoire placé sur le loopback BTRFS |
loopback-free |
0.9 |
Fraction de l'espace libre de / (fichier sparse, occupation = contenu compressé) |
mount-opts |
compress-force=zstd:2,discard=async,noatime |
Options de montage |
Contrainte : action locale uses: ./.github/actions/mount-btrfs-storage — dépôt checkouté avant l'appel.
Utilisation :
plus utilisée depuis la migration rootless — conservée comme garde-fou si une variante sature un jour (pointer surreusable-gablue-image.yml~/.local/share/containers)build-gablue-live-isos.yml: libérer espace → checkout → mount → drop-inimage_copy_tmp_dir→ build payload → podman run Titanoboa (script patché)
