Maîtriser umask et les permissions du CI Mac cloud

Maîtriser umask et les permissions du CI Mac cloud

Un même script de build peut fonctionner parfaitement dans un terminal SSH, puis échouer avec Permission denied une fois confié à un CI Mac cloud. L’erreur de diagnostic la plus fréquente consiste à penser qu’il suffit de relancer chmod +x. En réalité, le problème se situe généralement à quatre niveaux : le umask appliqué au lancement de la tâche, le bit d’exécution enregistré dans l’index Git, la manière dont les outils de copie et d’extraction traitent les permissions, et l’interpréteur réellement utilisé par le script. Corriger un seul fichier dans l’espace de travail ne suffit pas : le problème réapparaîtra au prochain checkout propre.

Commencer par classer les problèmes de permissions en quatre catégories

Avant toute investigation, consignez l’objet concerné et le type d’opération qui échoue. Un script impossible à exécuter, un répertoire inaccessible en écriture, une clé dont les permissions sont trop larges ou des modes modifiés après extraction d’une archive nécessitent des traitements très différents.

Utilisez stat pour afficher simultanément le mode octal, le propriétaire et le type de fichier :

target="${1:?path required}"
stat -f 'mode=%Sp octal=%OLp owner=%Su group=%Sg type=%HT path=%N' "$target"
ls -ldeO@ "$target"

La seconde commande révèle également les ACL, les indicateurs de fichier et les attributs étendus. Si les permissions classiques semblent correctes mais que l’opération échoue toujours, n’augmentez pas immédiatement les privilèges. Vérifiez d’abord que chaque répertoire parent peut être traversé, que le volume autorise l’exécution et que le processus utilise bien le compte attendu.

rwx n’est pas le seul critère. Chaque niveau du chemin doit disposer des permissions appropriées, et une ACL peut prendre le pas sur les bits de mode classiques. Utiliser systématiquement sudo ne fait que mélanger les propriétaires dans l’espace de travail, ce qui rend les tâches suivantes plus difficiles à reproduire.

Capturer un minimum d’éléments de diagnostic

Conservez au minimum la sortie de id, la valeur de umask, le répertoire courant, le résultat de stat pour le fichier concerné et les informations de montage. N’enregistrez pas l’ensemble des variables d’environnement, car elles peuvent contenir des jetons. Les éléments de diagnostic doivent se concentrer sur l’identité, les chemins et les permissions, sans collecter le contenu des identifiants.

Fixer umask au lancement de la tâche

umask ne s’applique qu’aux nouveaux objets et ne modifie pas rétroactivement les fichiers existants. La valeur de référence courante 022 produit par défaut des fichiers ordinaires en 644 et des répertoires en 755. Pour un répertoire contrôlé partagé au sein d’un même groupe, 002 peut être envisagé, mais ne doit pas servir de correctif universel. Définissez explicitement la valeur au lancement de la tâche, puis vérifiez-la en créant un objet témoin :

set -euo pipefail
umask 022

probe_dir="$(mktemp -d)"
probe_file="$probe_dir/probe"
: > "$probe_file"

file_mode="$(stat -f '%OLp' "$probe_file")"
dir_mode="$(stat -f '%OLp' "$probe_dir")"

test "$file_mode" = "644"
test "$dir_mode" = "700"
rm -rf "$probe_dir"

Afin de protéger les données temporaires, mktemp -d crée généralement un répertoire en 700. Il ne permet donc pas d’affirmer qu’un répertoire ordinaire devrait être créé en 755. Pour tester le mode de référence des répertoires, exécutez mkdir dans un répertoire parent connu, puis contrôlez séparément le résultat. Cette différence explique de nombreux faux positifs dans les tests de permissions.

Faire enregistrer à Git la bonne intention d’exécution

Git suit principalement le caractère exécutable ou non d’un fichier, sans enregistrer l’intégralité des permissions Unix. Modifier manuellement un script en 755 sur une machine ne signifie pas que ce changement a été inscrit dans l’index. Contrôlez l’index plutôt que de vous limiter à l’espace de travail :

git ls-files --stage |
awk '$1 == "100755" {print $4}' |
sort

Pour enregistrer le bit d’exécution dans un commit, utilisez :

git update-index --chmod=+x scripts/build.sh
git diff --summary
git diff --cached --summary

À l’inverse, si un fichier de configuration ou un document a reçu accidentellement le bit d’exécution, corrigez-le avec git update-index --chmod=-x. Pour les fichiers .sh, contrôlez également la première ligne. Utilisez un interpréteur dont la présence dans l’environnement est garantie, par exemple #!/bin/zsh ou #!/usr/bin/env bash, et évitez les fins de ligne CRLF dans les scripts.

Contrôler les scripts avant la fusion

Vous pouvez maintenir une liste explicite des scripts exécutables, puis la comparer au contenu de l’index. N’imposez pas simplement que « tous les fichiers .sh doivent être exécutables » : les fichiers de bibliothèque chargés avec source n’ont pas nécessairement besoin du bit d’exécution. La règle doit exprimer l’usage du fichier plutôt que le déduire de son extension.

Contrôler la gestion des permissions lors des copies et des archives

cp, ditto, rsync et les différents formats d’archive ne traitent pas de la même manière les bits de mode, les ACL et les attributs étendus. Un cache de build ne doit conserver que des données reproductibles. Pour un paquet livré, les permissions doivent être contrôlées à nouveau après extraction.

Avant de copier l’espace de travail, déterminez si les métadonnées doivent être préservées. Si seul le contenu des fichiers sources est nécessaire, évitez d’hériter involontairement d’anciens propriétaires, ACL ou attributs étendus. Si le bit d’exécution doit impérativement être conservé, effectuez un test aller-retour dans un répertoire temporaire :

src="scripts/build.sh"
tmp="$(mktemp -d)"
ditto "$src" "$tmp/build.sh"

before="$(stat -f '%OLp' "$src")"
after="$(stat -f '%OLp' "$tmp/build.sh")"
test "$before" = "$after"

rm -rf "$tmp"

La validation d’une archive ne doit pas se limiter à vérifier la présence des fichiers. Contrôlez au minimum que le script d’entrée est exécutable, que les fichiers de configuration ordinaires ne le sont pas et que les fichiers sensibles ne peuvent pas être lus par le groupe ou les autres utilisateurs. Extrayez l’archive dans un répertoire entièrement neuf afin que les modes d’anciens fichiers ne masquent pas les défauts propres à l’archive.

Transformer le correctif en politique exécutable

Une solution fiable ne consiste pas à lancer massivement chmod -R 777 après chaque échec, mais à établir un petit nombre d’assertions compréhensibles. Le contrôle suivant refuse les fichiers accessibles en écriture aux autres utilisateurs et vérifie que le script d’entrée est exécutable :

set -euo pipefail

entry="scripts/build.sh"
test -f "$entry"
test -x "$entry"

while IFS= read -r -d '' file; do
  mode="$(stat -f '%OLp' "$file")"
  other_write=$((8#$mode & 2))
  if (( other_write != 0 )); then
    printf 'world-writable file: %s mode=%s\n' "$file" "$mode" >&2
    exit 1
  fi
done < <(find . -type f -not -path './.git/*' -print0)

Placez le script de politique dans le dépôt afin de le partager entre les contrôles locaux et le CI. En cas d’échec, affichez uniquement le chemin, le mode attendu et le mode réel : ces informations facilitent le diagnostic sans révéler le contenu des fichiers. Lorsque les tâches s’exécutent sur les nœuds physiques dédiés de RentMini, relancez également ces assertions dans chaque nouvel espace de travail au lieu de supposer que l’environnement est resté inchangé depuis l’exécution précédente.

Terminez par une validation sur un checkout propre : supprimez l’espace de travail temporaire, clonez de nouveau le dépôt et lancez directement le contrôle des permissions ainsi qu’un build minimal, sans aucune correction manuelle. Ce n’est que lorsque ce parcours réussit que le correctif des permissions est réellement entré dans le contrôle de version, au lieu de rester limité à une session distante particulière.

Questions fréquentes

Pourquoi le CI affiche-t-il Permission denied après chmod +x ?

Vérifiez le chemin réellement exécuté, les options du volume, l’existence de l’interpréteur indiqué par le shebang et toute copie ou extraction ultérieure susceptible de retirer le bit exécutable.

Quel umask utiliser pour un CI Mac dans le cloud ?

022 constitue une base raisonnable pour la plupart des builds mono-utilisateur. Réservez 002 aux espaces partagés maîtrisés et contrôlez toujours les permissions réellement produites.

Comment bloquer une dérive de permissions avant la fusion ?

Contrôlez les modes de l’index Git, les shebangs, les fichiers sensibles et les artefacts extraits, puis terminez le job avec un code non nul dès qu’une règle est violée.

Utilisez un Mac dans le cloud à la demande

Préparez votre prochain build sur un Mac mini physique dédié

Choisissez la configuration M4, la durée de location et le nœud. Chaque commande correspond à un appareil physique dédié ; la disponibilité réelle est indiquée en temps réel dans la console.

Choisir une configuration et louer