dk: support pff

This commit is contained in:
Jephté Clain 2019-09-18 10:57:12 +04:00
parent 425e8a5642
commit 5a444a65b9
2 changed files with 416 additions and 49 deletions

164
dk
View File

@ -1,6 +1,7 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/auto" || exit 1
urequire pff
function display_help() {
uecho "$scriptname: outil pour faciliter l'utilisation de docker
@ -181,8 +182,8 @@ OPTIONS build
app_URL= # url du dépôt
app_DEVEL_SRCDIR= # répertoire source en mode devel
app_DEST= # répertoire dans lequel faire le checkout
app_PROFILE_ORIGIN= # origine spécifique à un profil
app_PROFILE_BRANCH= # branche spécifique à un profil
app_PROFILE_ORIGIN= # origine spécifique au profil 'PROFILE'
app_PROFILE_BRANCH= # branche spécifique au profil 'PROFILE'
app_ORIGIN= # ou... origine par défaut de la branche
app_BRANCH= # ou... branche par défaut
app_TYPE= # type de projet (composer par défaut)
@ -220,6 +221,14 @@ VARIABLES de update-apps.conf
- si le chemin est absolu ou relatif, lancer la commande telle quelle
- s'il n'y a pas de chemin, alors ce doit être le nom d'une fonction
existante auquel on enlève le préfixe update_apps_func_
Au moment où la commande est lancée, le répertoire courant est celui du
projet. Les variables suivantes sont disponibles:
URL= # url du dépôt
DEVEL_SRCDIR= # répertoire source en mode devel
DEST= # répertoire dans lequel faire le checkout
ORIGIN= # ou... origine par défaut de la branche
BRANCH= # ou... branche par défaut
TYPE= # type de projet (composer par défaut)
COMPOSER_ACTION
vaut 'install' par défaut. Indique ce qu'il faut faire pour un projet de
type 'composer' après avoir lancé les commandes de AFTER_UPDATE. Les
@ -231,7 +240,19 @@ FONCTIONS de update-apps.conf
SRCDIR/config/sqlmig vers DESTDIR/config/mariadb/sqlmig
Si SRCDIR ne se termine pas par '/config/sqlmig' ou si DESTDIR ne se
termine pas par '/config/mariadb/sqlmig', rajouter ces suffixes
automatiquement, sauf si une valeur NOFIX est spécifiée."
automatiquement, sauf si une valeur NOFIX est spécifiée.
pff [MAPS [PFFDIR]]
Si PFFDIR est un projet pff, mettre à jour le profil pff en fonction du
profil de déploiement.
MAPS détermine le mapping entre profil de déploiement (prod, test,
devel) et le profil pff. Il s'agit d'une liste de valeurs séparées par
des virgules de la forme DEST[:SRC]
- Une valeur de la forme 'DEST:SRC' fait correspondre le profil de
déploiement SRC au profil pff 'DEST'
- Une valeur de la forme 'DEST' force le choix du profil pff DEST quel
que soit le profil de déploiement
- La valeur par défaut si aucun profil de déploiement ne correspond est
de forcer le premier profil défini"
}
function echo_lines() { local IFS=$'\n'; echo "$*"; }
@ -441,7 +462,7 @@ function build_set_options() {
function update_apps_func_sqlmig() {
local destdir="$1" srcdir="$2" nofix="$3"
[ -n "$destdir" ] || destdir=db
[ -n "$srcdir" ] || srcdir="$dest"
[ -n "$srcdir" ] || srcdir="$DEST"
if [ -z "$nofix" ]; then
[ "${destdir%/config/mariadb/sqlmig}" != "$destdir" ] || destdir="$destdir/config/mariadb/sqlmig"
@ -467,6 +488,48 @@ function update_apps_func_sqlmig() {
return 0
}
function update_apps_func_pff() {
local maps="$1" pffdir="${2:-$DEST}"
[ -f "$pffdir/$PFF_CONF" ] || return 0
source "$pffdir/$PFF_CONF"
pff_autofix "$pffdir"
local map src dest
array_split maps "$maps" ,
for map in "${maps[@]}"; do
if [ -z "$map" ]; then
continue
elif [[ "$map" == *:* ]]; then
# mapping de profil
splitpair "$map" dest src
if [ "$src" == "$PROFILE" ]; then
if array_contains PROFILES "$dest"; then
estep "Sélection du profil pff $dest"
pff_select_profile "$dest" "$pffdir"
return 0
fi
eerror "$dest: profil invalide, il a été ignoré"
fi
else
# forcer le profil
dest="$map"
if array_contains PROFILES "$dest"; then
estep "Sélection du profil pff $dest"
pff_select_profile "$dest" "$pffdir"
return 0
fi
eerror "$dest: profil invalide, il a été ignoré"
fi
done
# sélectionner le premier profil
setx dest=pff_get_first_profile "$pffdir"
if [ -n "$dest" ]; then
estep "Sélection du profil pff $dest"
pff_select_profile "$dest" "$pffdir"
fi
}
function build_update_apps() {
[ -n "$BUILD_UPDATE_APPS" ] || return 0
[ -f update-apps.conf ] || return 0
@ -493,84 +556,84 @@ function build_update_apps() {
esac
etitle "Mise à jour des dépendances"
local app var type url devel_srcdir dest branch after_update after_updates composer_action
local app var URL DEVEL_SRCDIR DEST ORIGIN BRANCH TYPE after_update after_updates composer_action
for app in "${APPS[@]}"; do
etitle "$app"
var="${app//-/_}"
url="${var}_URL"; url="${!url}"
[ -n "$url" ] || {
URL="${var}_URL"; URL="${!URL}"
[ -n "$URL" ] || {
ewarn "$app: vous devez définir l'url"
eend; return 1
}
devel_srcdir="${var}_DEVEL_SRCDIR"; devel_srcdir="${!devel_srcdir}"
[ -n "$devel_srcdir" ] || devel_srcdir="$DEFAULT_DEVEL_SRCDIR/${url##*/}"
DEVEL_SRCDIR="${var}_DEVEL_SRCDIR"; DEVEL_SRCDIR="${!DEVEL_SRCDIR}"
[ -n "$DEVEL_SRCDIR" ] || DEVEL_SRCDIR="$DEFAULT_DEVEL_SRCDIR/${URL##*/}"
dest="${var}_DEST"; dest="${!dest}"
[ -n "$dest" ] || dest="$app/b"
mkdir -p "$dest" || { eend; return 1; }
DEST="${var}_DEST"; DEST="${!DEST}"
[ -n "$DEST" ] || DEST="$app/b"
mkdir -p "$DEST" || { eend; return 1; }
origin="${var}_${PROFILE}_ORIGIN"; origin="${!origin}"
[ -n "$origin" ] || { origin="${var}_ORIGIN"; origin="${!origin}"; }
[ -n "$origin" ] || origin="$DEFAULT_ORIGIN"
ORIGIN="${var}_${PROFILE}_ORIGIN"; ORIGIN="${!ORIGIN}"
[ -n "$ORIGIN" ] || { ORIGIN="${var}_ORIGIN"; ORIGIN="${!ORIGIN}"; }
[ -n "$ORIGIN" ] || ORIGIN="$DEFAULT_ORIGIN"
branch="${var}_${PROFILE}_BRANCH"; branch="${!branch}"
[ -n "$branch" ] || { branch="${var}_BRANCH"; branch="${!branch}"; }
[ -n "$branch" ] || branch="$DEFAULT_BRANCH"
BRANCH="${var}_${PROFILE}_BRANCH"; BRANCH="${!BRANCH}"
[ -n "$BRANCH" ] || { BRANCH="${var}_BRANCH"; BRANCH="${!BRANCH}"; }
[ -n "$BRANCH" ] || BRANCH="$DEFAULT_BRANCH"
# calculer le type maintenant, on en a besoin pour le mode devel
type="${var}_TYPE"; type="${!type}"
TYPE="${var}_TYPE"; TYPE="${!TYPE}"
dest="$dest/$app"
DEST="$DEST/$app"
if [ -n "$BUILD_UPDATE_DEVEL" ]; then
# synchronisation en mode devel
local -a rsync_opts; rsync_opts=(-a --delete --exclude .git/ --delete-excluded)
estep "Synchro $devel_srcdir --> $dest"
rsync "${rsync_opts[@]}" "$devel_srcdir/" "$dest" || { eend; return 1; }
estep "Synchro $DEVEL_SRCDIR --> $DEST"
rsync "${rsync_opts[@]}" "$DEVEL_SRCDIR/" "$DEST" || { eend; return 1; }
if [ -z "$type" ]; then
if [ -z "$TYPE" ]; then
# possible de détecter le type quand on a le projet
# en cas de maj ici, mettre à jour aussi le code ci-dessous
if [ -f "$dest/composer.json" ]; then type=composer
else type=inconnu
if [ -f "$DEST/composer.json" ]; then TYPE=composer
else TYPE=inconnu
fi
fi
if [ "$type" == composer ]; then
if [ "$TYPE" == composer ]; then
# Synchronisation des dépendances
local -a depdirs; local depdir pname
setx -a depdirs=ls -d "$dest/vendor/"{lib,ur}/* 2>/dev/null
setx -a depdirs=ls -d "$DEST/vendor/"{lib,ur}/* 2>/dev/null
for depdir in "${depdirs[@]}"; do
[ -L "$depdir" ] && rm "$depdir"
pname="${depdir#$dest/vendor/}"; pname="${pname/\//-}"
pname="${depdir#$DEST/vendor/}"; pname="${pname/\//-}"
estep "Synchro $DEFAULT_DEVEL_SRCDIR/$pname --> $depdir"
rsync "${rsync_opts[@]}" --exclude /vendor/ "$DEFAULT_DEVEL_SRCDIR/$pname/" "$depdir"
done
fi
else
if [ -d "$dest" -a -d "$dest/.git" ]; then
if [ -d "$DEST" -a -d "$DEST/.git" ]; then
# mise à jour
estep "Maj dépôt $url:$branch --> $dest"
estep "Maj dépôt $URL:$BRANCH --> $DEST"
setx cwd=pwd
cd "$dest"
cd "$DEST"
git fetch --all -p -f || { eend; return 1; }
git reset --hard "$origin/$branch" || { eend; return 1; }
git reset --hard "$ORIGIN/$BRANCH" || { eend; return 1; }
cd "$cwd"
else
# reliquat mode devel?
[ -d "$dest" ] && rm -rf "$dest"
[ -d "$DEST" ] && rm -rf "$DEST"
# clonage initial
estep "Clonage $url:$branch --> $dest"
git clone -o "$origin" -b "$branch" "$url" "$dest" || { eend; return 1; }
estep "Clonage $URL:$BRANCH --> $DEST"
git clone -o "$ORIGIN" -b "$BRANCH" "$URL" "$DEST" || { eend; return 1; }
fi
if [ -z "$type" ]; then
if [ -z "$TYPE" ]; then
# possible de détecter le type quand on a le projet
# en cas de maj ici, mettre à jour aussi le code ci-dessus
if [ -f "$dest/composer.json" ]; then type=composer
else type=inconnu
if [ -f "$DEST/composer.json" ]; then TYPE=composer
else TYPE=inconnu
fi
fi
fi
@ -578,20 +641,20 @@ function build_update_apps() {
after_updates="${var}_AFTER_UPDATE"
if is_defined "$after_updates"; then
after_updates="$after_updates[@]"; after_updates=("${!after_updates}")
elif [ "$type" == composer ]; then
elif [ "$TYPE" == composer ]; then
after_updates=(sqlmig)
else
after_updates=()
fi
estep "Type de dépôt: $type"
if [ "$type" == composer ]; then
estep "Type de dépôt: $TYPE"
if [ "$TYPE" == composer ]; then
composer_action="${var}_${PROFILE}_COMPOSER_ACTION"; composer_action="${!composer_action}"
[ -n "$composer_action" ] || { composer_action="${var}_COMPOSER_ACTION"; composer_action="${!composer_action}"; }
[ -n "$composer_action" ] || composer_action="$DEFAULT_COMPOSER_ACTION"
composer=/usr/bin/composer
[ -x "$dest/composer.phar" ] && composer="$dest/composer.phar"
[ -x "$DEST/composer.phar" ] && composer="$DEST/composer.phar"
if [ -z "$BUILD_UPDATE_DEVEL" ]; then
case "${composer_action:-install}" in
@ -602,7 +665,7 @@ function build_update_apps() {
esac
if [ -n "$composer_action" ]; then
estep "Installation des dépendances composer"
"$composer" -d"$dest" "$composer_action" ${PRODUCTION:+--no-dev -o} || { eend; return 1; }
"$composer" -d"$DEST" "$composer_action" ${PRODUCTION:+--no-dev -o} || { eend; return 1; }
fi
fi
fi
@ -610,16 +673,19 @@ function build_update_apps() {
for after_update in "${after_updates[@]}"; do
if [ "${after_update#/}" != "$after_update" ]; then
# commande absolue, la lancer telle quelle
estep "$after_update"
eval "$after_update" || { eend; return 1; }
etitle "$after_update"
eval "$after_update" || { eend; eend; return 1; }
eend
elif [ "${after_update#./}" != "$after_update" ]; then
# commande relative, la lancer telle quelle
estep "$after_update"
eval "$after_update" || { eend; return 1; }
etitle "$after_update"
eval "$after_update" || { eend; eend; return 1; }
eend
else
# c'est une fonction update_apps_func_*
estep "$after_update"
eval "update_apps_func_$after_update" || { eend; return 1; }
etitle "$after_update"
eval "update_apps_func_$after_update" || { eend; eend; return 1; }
eend
fi
done

301
lib/ulib/pff Normal file
View File

@ -0,0 +1,301 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
urequire multiconf
PFF_ORIGEXT=pff
PFF_CONF=.pff.conf # ne pas modifier
DEFAULT_PFF_ORIGEXTS=(".$ORIGEXT" .origine .default)
DEFAULT_PFF_PROTECTS=(/.git/ .svn/ /pff/ "/$PFF_CONF")
PFF_CONFVARS=(
"VERSION//Version actuellement installée"
-a
"PVERSIONS//Versions en attente d'intégration"
"PROFILES//Profils définis"
-s
"DISTTYPE=auto//Type de distribution upstream: full ou patch"
-a
"ORIGEXTS=//Extensions origines"
"PROTECTS=//Fichiers locaux à protéger lors de l'intégration e.g /dir/, /file, etc."
"MKDIRS//Répertoires qui doivent toujours exister"
"FILTERS//Filtres appliqués aux fichiers lors de l'intégration"
"NOMERGES=//Fichiers qu'il ne faut pas chercher à fusionner"
)
# Nomenclature pour le nommage des fichiers traités:
# pfile: le chemin absolu du fichier dans le projet
# rfile: le chemin relatif du fichier dans le projet
# bfile: le chemin absolu du fichier dans pff/Base/
# Cfile: le chemin absolu du fichier dans pff/Common/
# cfile: le chemin absolu du fichier dans pff/Current/
# Pfile: le chemin absolu du fichier dans pff/ANYPROFILE/
# plink: la destination du lien pfile
# clink: la destination du lien cfile
# Plink: la destination du lien Pfile
function flexists() {
[ -e "$1" -o -L "$1" ]
}
function multiups() {
# afficher un chemin vers le haut e.g ../../.. avec autant d'éléments que
# les répertoires du chemin relatif $1.
# méthode: commencer avec la valeur de départ $2 et préfixer avec autant de
# ../ que nécessaire. puis afficher le résultat.
local tmp="$1" link="$2"
setx tmp=dirname -- "$tmp"
while [ "$tmp" != . ]; do
[ -n "$link" ] && link="/$link"
link="..$link"
setx tmp=dirname -- "$tmp"
done
echo "$link"
}
function find_pffdir() {
# trouver le répertoire du projet pff à partir du répertoire $2(=.) et
# mettre le chemin absolu dans la variable $1(=pffdir)
# si le répertoire n'est pas trouvé, retourner 1
local destvar="${1:-pffdir}" pffdir
setx pffdir=abspath "${2:-.}"
while true; do
if [ -f "$pffdir/$PFF_CONF" -a -d "$pffdir/pff" ]; then
local "$destvar"
upvar "$destvar" "$pffdir"
return 0
fi
[ "$pffdir" == / -o "$pffdir" == "$HOME" ] && break
setx pffdir=dirname -- "$pffdir"
done
return 1
}
function ensure_pffdir() {
# trouver le répertoire du projet pff à partir du répertoire $2(=.) et
# mettre le chemin absolu dans la variable $1(=pffdir)
# si le répertoire n'est pas trouvé, arrêter le script avec un code d'erreur
local destvar="${1:-pffdir}" pffdir
if find_pffdir pffdir "$2"; then
conf_init "${PFF_CONFVARS[@]}"
source "$pffdir/$PFF_CONF"
local "$destvar"; upvar "$destvar" "$pffdir"
return
fi
local msg="Projet pff introuvable (utiliser --init ?)"
[ -n "$2" ] && die "$2: $msg" || die "$msg"
}
function pff_get_current_profile() {
# afficher le profil courant du projet pff $1, s'il est défini
local pffdir="$1"
[ -L "$pffdir/pff/.Current" ] && readlink "$pffdir/pff/.Current"
}
function pff_get_profiles() {
# afficher tous les profils valides du projet pff $1
local pffdir="$1"
(for profile in "${PROFILES[@]}"; do echo "$profile"; done
list_dirs "$pffdir/pff") | sort -u | grep -vxF Current
}
function pff_get_user_profiles() {
# afficher tous les profils modifiables du projet pff $1 (c'est à dire tous
# les profils valides excepté Base)
pff_get_profiles "$@" | grep -vxF Base
}
function pff_get_first_profile() {
# afficher le premier profil autre que Base du projet pff $1
local profile
profile="${PROFILES[0]}"
if [ -z "$profile" -o "$profile" == Base ]; then
pff_get_user_profiles "$@" | head -n1
else
echo "$profile"
fi
}
function pff_get_local_files() {
# afficher tous les fichiers locaux exprimés relativement au répertoire du
# projet pff $1
local pffdir="$1"
find "$pffdir/pff/Base" -type f | sed "s|^$pffdir/pff/Base/||" | grep -v '__pv-.*__$'
}
function pff_get_rfile() {
# obtenir le chemin relatif du fichier $1 exprimé par rapport au répertoire
# du projet pff $2. Si c'est un fichier d'un répertoire de profil,
# l'exprimer comme un chemin du répertoire de projet, e.g pff/Profile/path
# devient path
# retourner 1 si le chemin est invalide (est situé en dehors de pffdir ou
# pas dans un répertoire de profil)
local rfile="$1" pffdir="$2"
setx rfile=abspath "$rfile"
[ "${rfile#$pffdir/}" != "$rfile" ] || return 1
rfile="${rfile#$pffdir/}"
if [[ "$rfile" == pff/*/* ]]; then
rfile="${rfile#pff/*/}"
elif [[ "$rfile" == pff/* ]]; then
return 1
fi
echo "$rfile"
}
function pff_get_pfile() {
# obtenir le chemin du fichier $1 exprimé par rapport au répertoire du
# profil $2 dans le répertoire de projet $3
# retourner 1 si le chemin est invalide (est situé en dehors de pffdir ou
# pas dans un répertoire de profil)
local pfile="$1" profile="$2" pffdir="$3"
setx pfile=abspath "$pfile"
[ "${pfile#$pffdir/}" != "$pfile" ] || return 1
pfile="${pfile#$pffdir/}"
if [[ "$pfile" == pff/*/* ]]; then
pfile="${pfile#pff/*/}"
elif [[ "$pfile" == pff/* ]]; then
return 1
fi
echo "$pffdir/pff/$profile/$pfile"
}
function pff_get_bfile() { pff_get_pfile "$1" Base "$2"; }
function pff_get_Cfile() { pff_get_pfile "$1" Common "$2"; }
function pff_get_cfile() { pff_get_pfile "$1" Current "$2"; }
function pff_get_vlfiles_nostrip() {
# afficher tous les fichiers de version
local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
[ -d "$pffdir/pff/$profile" ] || return
if [ -n "$version" ]; then
if [ -n "$rfile" ]; then
find "$pffdir/pff/$profile" \
-type f -path "$pffdir/pff/$profile/${rfile}__pv-${version}__" -o \
-type l -path "$pffdir/pff/$profile/${rfile}__pv-${version}__"
else
find "$pffdir/pff/$profile" \
-type f -name "*__pv-${version}__" -o \
-type l -name "*__pv-${version}__"
fi
else
if [ -n "$rfile" ]; then
find "$pffdir/pff/$profile" \
-type f -path "$pffdir/pff/$profile/${rfile}__pv-*__" -o \
-type l -path "$pffdir/pff/$profile/${rfile}__pv-*__"
else
find "$pffdir/pff/$profile" \
-type f -name "*__pv-*__" -o \
-type l -name "*__pv-*__"
fi
fi
}
function pff_get_vlfiles() {
local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
pff_get_vlfiles_nostrip "$@" | sed "s|^$pffdir/pff/$profile/||"
}
function pff_is_nomerge() {
local file="$1" pffdir="$2"
local nomerge rfile
setx rfile=pff_get_rfile "$file" "$pffdir"
setx file=basename -- "$rfile" # utilisé pour le match sur le nom du fichier
for nomerge in "${NOMERGES[@]}"; do
if [[ "$nomerge" == */* ]]; then
# matcher sur le chemin relatif
if eval "[[ $(qval "$rfile") == $(qwc "$nomerge") ]]"; then
return 0
fi
else
# matcher uniquement sur le nom du fichier
if eval "[[ $(qval "$file") == $(qwc "$nomerge") ]]"; then
return 0
fi
fi
done
return 1
}
function pff_sync_vlfiles() {
# synchroniser les fichiers de version $3..@ dans tous les répertoires de
# profil, ou seulement le répertoire de profil $2 si la valeur n'est pas
# vide.
local pffdir="$1"; shift
local profile="$1"; shift
local -a profiles
if [ -n "$profile" ]; then
profiles=("$profile")
else
array_from_lines profiles "$(pff_get_user_profiles "$pffdir")"
fi
local vlfile rfile prefix pfile plink tmp
for vlfile in "$@"; do
rfile="${vlfile%__pv-*__}"
for profile in "${profiles[@]}"; do
prefix="$pffdir/pff/$profile"
flexists "$prefix/$rfile" || continue
pfile="$prefix/$vlfile"
setx plink=multiups "$profile/$vlfile" "Base/$vlfile"
if [ -L "$pfile" ]; then
# correction éventuelle du lien existant
setx tmp=readlink "$pfile"
[ "$tmp" == "$plink" ] || ln -sfT "$plink" "$pfile"
else
ln -sf "$plink" "$pfile" || return
fi
done
done
}
function pff_select_profile() {
# sélectionner le profil $1 dans le projet pff $2. créer d'abord le profil
# s'il n'existe pas.
local profile="$1" pffdir="$2"
# créer le répertoire de profil si nécessaire
mkdir -p "$pffdir/pff/$profile" || return 1
# mettre à jour les liens
local -a lfiles; local lfile src dest
setx -a lfiles=pff_get_local_files "$pffdir"
for lfile in "${lfiles[@]}"; do
src="$pffdir/pff/Current/$lfile"
if [ -f "$pffdir/pff/$profile/$lfile" ]; then
dest="$profile/$lfile"
elif [ "$profile" != Common -a -f "$pffdir/pff/Common/$lfile" ]; then
dest="Common/$lfile"
else
dest="Base/$lfile"
fi
setx dest=multiups "Current/$lfile" "$dest"
[ -L "$src" ] || mkdirof "$src"
ln -sfT "$dest" "$src"
done
# maj du lien "profil courant"
ln -sfT "$profile" "$pffdir/pff/.Current"
}
function pff_autoinit() {
# vérifications automatiques: créer les répertoires de base nécessaire au
# fonctionnement de pff dans le projet pff $1
local pffdir="$1" profile mkdir
[ -d "$pffdir/pff/Current" ] || mkdir -p "$pffdir/pff/Current"
[ -d "$pffdir/pff/Base" ] || mkdir -p "$pffdir/pff/Base"
# tous les fichiers du profil Base doivent être en lecture seule
find "$pffdir/pff/Base" -type f -perm /222 -exec chmod a-w '{}' +
# Créer les répertoires de MKDIRS
for mkdir in "${MKDIRS[@]}"; do
mkdir -p "$pffdir/$mkdir"
done
return 0
}
function pff_autoselect() {
# vérification automatiques: sélectionner le premier profil défini si aucun
# profil n'est sélectionné dans le projet pff $1
local pffdir="$1" profile
if [ ! -L "$pffdir/pff/.Current" ]; then
setx profile=pff_get_first_profile "$pffdir"
[ -n "$profile" ] || profile=Base
enote "Autosélection du profil $profile"
pff_select_profile "$profile" "$pffdir"
fi
}
function pff_autofix() {
pff_autoinit "$1"
pff_autoselect "$1"
}