diff --git a/dk b/dk index f002c3d..b969d9c 100755 --- a/dk +++ b/dk @@ -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 diff --git a/lib/ulib/pff b/lib/ulib/pff new file mode 100644 index 0000000..9a71572 --- /dev/null +++ b/lib/ulib/pff @@ -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" +}