nutools/pff

1608 lines
58 KiB
Plaintext
Raw Normal View History

2018-02-01 14:54:28 +04:00
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
SCRIPT_ALIASES=(
pf0:-0
pfn:-N
pfg:-g
2018-02-01 14:54:28 +04:00
pfb:-b
pfs:-s
pfa:-a
2018-02-01 14:54:28 +04:00
pfe:-e
2018-02-07 16:22:50 +04:00
pfd:-d
2018-02-01 14:54:28 +04:00
)
2018-02-01 14:26:30 +04:00
ORIGEXT=pff
DEFAULT_ORIGEXTS=(".$ORIGEXT" .origine .default)
PFFCONF=.pff.conf # ne pas modifier
DEFAULT_PROTECTS=(/.git/ .svn/ /pff/ "/$PFFCONF")
PFFCONFVARS=(
"VERSION//Version actuellement installée"
-a
"PVERSIONS//Versions en attente d'intégration"
"PROFILES//Profils définis"
2018-02-01 14:26:30 +04:00
"ORIGEXTS=//Extensions origines"
"PROTECTS=//Fichiers locaux à protéger lors de l'intégration e.g /dir/, /file, etc."
2018-03-08 14:54:40 +04:00
"MKDIRS//Répertoires qui doivent toujours exister"
"FILTERS//Filtres appliqués aux fichiers lors de l'intégration"
2018-02-01 14:26:30 +04:00
)
2018-02-01 14:54:28 +04:00
if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
# créer les liens
scriptname="$(basename -- "$0")"
for alias in "${SCRIPT_ALIASES[@]}"; do
alias="${alias%:*}"
ln -s "$scriptname" "$alias"
done
exit 0
elif [ $# -eq 1 -a "$1" == --nutools-completion ]; then
echo '
function __pff_profiles() {
2018-02-01 14:26:30 +04:00
local cwd="$(pwd)"
local pffdir="$cwd"
while true; do
if [ -f "$pffdir/'"$PFFCONF"'" -a -d "$pffdir/pff" ]; then
cd "$pffdir/pff"
/bin/ls -1d * | while read f; do
[ -d "$f" -a "$f" != Current ] || continue
f="$1$f"
if [[ "${f:0:2}" == -[eEn] ]]; then
echo -n -
echo "${f:1}"
else
echo "$f"
fi
done
cd "$cwd"
break
fi
[ "$pffdir" == / -o "$pffdir" == "$HOME" ] && break
pffdir="$(dirname -- "$pffdir")"
done
2018-02-01 14:54:28 +04:00
}
function __pfe_profiles() {
echo ALL
__pff_profiles "$@" | grep -vxF Base
}
2018-02-01 14:54:28 +04:00
function __pfs_completion() {
2018-02-01 14:26:30 +04:00
local cur
_get_comp_words_by_ref cur
COMPREPLY=($(compgen -W "$(__pff_profiles)" "$cur"))
2018-02-01 14:54:28 +04:00
}
2018-02-01 14:26:30 +04:00
complete -F __pfs_completion -o bashdefault -o default pfs
function __pfe_completion() {
local cur prev opt comp
_get_comp_words_by_ref cur prev
if [[ "$prev" == -*p ]]; then
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
elif [ "$prev" == --profile ]; then
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
elif [[ "$cur" == -*p* ]]; then
comp="${cur#-*p}"; opt="${cur:0:$((${#cur}-${#comp}))}"
COMPREPLY=($(compgen -W "$(__pfe_profiles "$opt")" -- "$cur"))
fi
}
complete -F __pfe_completion -o bashdefault -o default pfe
2018-02-01 14:26:30 +04:00
function __pff_completion() {
local cur prev opt comp
_get_comp_words_by_ref cur prev
if [ "${COMP_WORDS[1]}" == -e -o "${COMP_WORDS[1]}" == --edit ]; then
# ne compléter -p que si on est en mode --edit
if [[ "$prev" == -*p ]]; then
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
elif [ "$prev" == --profile ]; then
COMPREPLY=($(compgen -W "$(__pfe_profiles)" -- "$cur"))
elif [[ "$cur" == -*p* ]]; then
comp="${cur#-*p}"; opt="${cur:0:$((${#cur}-${#comp}))}"
COMPREPLY=($(compgen -W "$(__pfe_profiles "$opt")" -- "$cur"))
fi
elif [[ "$prev" == -*s ]]; then
COMPREPLY=($(compgen -W "$(__pff_profiles)" -- "$cur"))
elif [ "$prev" == --switch ]; then
2018-02-01 14:26:30 +04:00
COMPREPLY=($(compgen -W "$(__pff_profiles)" -- "$cur"))
elif [[ "$cur" == -*s* ]]; then
comp="${cur#-*s}"; opt="${cur:0:$((${#cur}-${#comp}))}"
COMPREPLY=($(compgen -W "$(__pff_profiles "$opt")" -- "$cur"))
fi
}
complete -F __pff_completion -o bashdefault -o default pff
2018-02-01 14:54:28 +04:00
'
exit 0
fi
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS multiconf vcs javaproperties
2018-02-01 14:54:28 +04:00
function display_help() {
uecho "$scriptname: gestion de modifications locales
Un produit est distribué par un partenaire, et il faut maintenir certaines
modifications locales tout en continuant d'accepter des modififications de
l'upstream. Ce script aide à maintenir un tel scénario.
En général, la distribution upstream identifie les fichiers modifiables en leur
donnant une extension particulière, par exemple 'file.origine'. On peut aussi
décider de modifier des fichiers qui n'ont pas été prévus comme tels par
l'upstream. On peut aussi avoir plusieurs ensembles de modifications, rassemblés
en profils, e.g prod ou test pour les modifications à déployer en prod ou en
test.
Terminologie: Les fichiers pour lesquels il faut maintenir une version locale
sont appelés 'fichiers locaux', qu'ils viennent de la distribution upstream ou
non. Les autres fichiers qui proviennent de la distribution sont appelés
'fichiers upstream'. Les fichiers livrés dans la distribution upstream avec une
extension particulière pour indiquer qu'ils sont modifiables sont appelés
'fichiers origine'.
2018-02-07 12:03:51 +04:00
Les fichiers sont classés dans des profils spécifiques. Les profils reconnus
sont:
- Base est le profil des fichiers upstream non modifiés. Les fichiers origine
sont intégrés dans ce profil.
- Common est le profil des fichiers upstream modifiés. Tous les fichiers
devraient être modifiés dans ce profil.
- Les autres profils sont basés sur Common en priorité puis sur Base en cas de
non existence dans Common. Il peut s'agir de profils comme prod ou test, qui
contiennent des modifications spécifiques à différents cas d'utilisation.
2018-02-01 14:54:28 +04:00
USAGE
$scriptname [options]
COMMANDES / OPTIONS
Les arguments du script dépendent de la commande utilisée. Les commandes
supportées sont:
-0, --init [WORKDIR [ARCHIVE]]
Initialiser un répertoire pour le suivi des distributions upstream. Le
fichier $PFFCONF contient toutes les informations paramétrables sur la
gestion du projet.
--s, --origext .EXT
Ajouter une extension à la liste des extensions origines, c'est à dire
les fichiers identifiés dans la distribution upstream comme contenus
modifiables. Par défaut, les extensions suivantes sont reconnues:
${DEFAULT_ORIGEXTS[*]}
Cette option peut être utilisée autant de fois que nécessaire.
--k, --clear-origexts
Supprimer la liste par défaut des extensions origines. Cette option doit
être spécifiée avant l'option --origext pour construire une nouvelle
liste. La liste des extensions ne doit pas être vide. Si c'est le cas,
elle est modifiée pour contenir l'unique élément (.$ORIGEXT)
-N, --new ARCHIVE [WORKDIR]
-M, --new-only ARCHIVE [WORKDIR]
Intégrer une nouvelle distribution upstream, sous forme d'une archive ou
d'un répertoire. Les fichiers origine et les fichiers locaux sont placés
en attente d'intégration. Les autres sont intégrés sans modification.
La variante --new appelle automatiquement --patch après l'intégration de
l'archive.
-V, --version VERSION
Spécifier la version de l'archive qui est intégrée avec l'option --new.
Normalement, cette information est calculée automatiquement à partir du
nom de l'archive. Si la source est un répertoire ou une archive sans
numéro de version, cette option est requise.
-F, --full-archive
Intégrer une distribution complète. Par défaut, les archives avec
l'extension .war sont considérées commes des livraisons complètes. Si
la source est un répertoire ou une archive sans extension, l'une des
options -F ou -H est requise.
-H, --patch-archive
2018-02-01 14:54:28 +04:00
Intégrer un patch. Par défaut, les archives avec l'extension .zip sont
considérées comme des patches. Si la source est un répertoire ou une
archive sans extension, l'une des options -F ou -H est requise.
2018-02-01 14:54:28 +04:00
Avec une distribution de type patch, on considère que les fichiers
livrés ne sont pas des fichiers origines. Il n'y a donc aucun traitement
particulier: l'archive est simplement intégrée telle quelle.
--auto-unwrap
Si l'archive ne contient qu'un seul répertoire à la racine nommé d'après
le nom de base de l'archive, c'est le contenu de ce répertoire qui est
considéré. C'est l'option par défaut.
Le test est effectué avec et sans la version. Par exemple, si l'archive
est product-x.y.zip, et qu'elle contient un unique répertoire nommé
'product-x.y' ou 'product' alors intégrer le contenu de ce répertoire.
-E, --unwrap
Si l'archive ne contient qu'un seul répertoire à la racine, intégrer
inconditionellement le contenu de se répertoire.
--no-unwrap
Intégrer tel quel le contenu de l'archive.
-g, --patch [WORKDIR]
2018-02-01 14:54:28 +04:00
Intégrer les modifications entrantes sur les fichiers nouvellement
arrivés via l'option --new
-o, --ours
Utiliser la stratégie '-s recursive -X ours' en cas de conflit lors de
l'intégration de la version.
2018-02-24 08:36:27 +04:00
--ask-commit
-c, --commit
--no-commit
Après l'intégration avec succès d'un patch, demander à l'utilisateur
s'il veut faire commit & push dans git (--ask-commit, la valeur par
défaut), le faire sans confirmation (--commit), ou ne jamais le faire
(--no-commit)
2018-02-01 14:54:28 +04:00
-b, --add-global FILES...
2018-02-01 14:54:28 +04:00
Ajouter/Identifier un fichier comme un fichier local pour tous les
2018-02-07 12:03:51 +04:00
profils. Le fichier est copié dans le profil Base.
2018-02-01 14:54:28 +04:00
2018-02-07 16:22:50 +04:00
--locals [WORKDIR]
2018-02-01 14:54:28 +04:00
Lister les fichiers locaux
2018-02-07 16:22:50 +04:00
--profiles [WORKDIR]
2018-02-01 14:54:28 +04:00
Lister les profils valides
-s, --switch PROFILE [WORKDIR]
Basculer le profil courant
-a, --add-local FILES...
2018-02-01 14:54:28 +04:00
Ajouter/Identifier un fichier comme un fichier local et le rendre
spécifique au profil courant.
-e, --edit FILES...
Editer des fichiers, implique --add-local
2018-02-24 08:36:27 +04:00
-p, --profile PROFILE
Pour l'option --edit, sélectionner le profil spécifié comme celui
concerné pour les fichier mentionnés après cette option. Par exemple:
2018-02-24 08:36:27 +04:00
$scriptname -e A -pprod B C -ptest D
Edite le fichier A du profil courant, les fichiers B et C du profil prod
et le fichier D du profil test.
Le profil ALL est spécial et permet d'éditer le fichier dans tous les
profils *où il existe*, excepté Base parce que ce profil contient des
fichiers qui ne devraient pas être modifiés.
Pour tous les autres profils, si le fichier n'existe pas dans le profil
spécifié il est rajouté dans le profil avant son édition.
Attention: pour que l'option -p/--profile soit correctement reconnue
avec l'option -e/--edit, il faut que cette dernière option soit
mentionnée en premier sur la ligne de commande.
-P, --prod
-T, --test
-A, --all-profiles
Raccourcis pour respectivement -pprod, -ptest et -pALL
2018-02-01 14:54:28 +04:00
2018-02-07 16:22:50 +04:00
-d, --diff [DESTP [WORKDIR]]
-d, --diff [SRCP DESTP [WORKDIR]]
Afficher la différence entre entre deux profils. Avec la première
2018-03-05 23:01:56 +04:00
syntaxe, comparer le profil DESTP au profil courant. Avec la deuxième
syntaxe, comparer le profil DESTP au profil SRCP.
-l, --list-names
N'afficher que les noms des fichiers qui sont différents
2018-02-07 16:22:50 +04:00
2018-02-01 14:54:28 +04:00
--infos [WORKDIR]
Afficher des informations sur le projet courant: profils, fichiers
locaux, profil courant, etc. C'est la commande par défaut.
Les fichiers locaux sont taggés avec les valeurs suivantes
${COULEUR_ROUGE}P${COULEUR_NORMALE} il existe un patch pour ce fichier dans le profil courant
${COULEUR_BLEUE}*${COULEUR_NORMALE} ce fichier local est spécifique à ce profil
2018-02-07 16:22:50 +04:00
$(get_color YELLOW)C${COULEUR_NORMALE} ce fichier local est spécifique au profil Common
-l, --show-all
2018-02-07 16:22:50 +04:00
Afficher tous les fichiers locaux au lieu de se contenter des fichiers
modifiés dans le profil courant."
2018-02-01 14:54:28 +04:00
}
# 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/
2018-02-07 12:32:46 +04:00
# Cfile: le chemin absolu du fichier dans pff/Common/
2018-02-01 14:54:28 +04:00
# 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
2018-02-07 16:22:50 +04:00
function flexists() {
[ -e "$1" -o -L "$1" ]
}
2018-02-01 14:54:28 +04:00
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/$PFFCONF" -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 "${PFFCONFVARS[@]}"
source "$pffdir/$PFFCONF"
local "$destvar"; upvar "$destvar" "$pffdir"
return
fi
local msg="Projet pff introuvable (utiliser --init ?)"
[ -n "$2" ] && die "$2: $msg" || die "$msg"
}
function 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 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
2018-02-01 14:54:28 +04:00
}
2018-03-02 13:09:29 +04:00
function get_user_profiles() {
# afficher tous les profils modifiables du projet pff $1 (c'est à dire tous
# les profils valides excepté Base)
get_profiles "$@" | grep -vxF Base
}
2018-02-01 14:54:28 +04:00
function 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
get_user_profiles "$@" | head -n1
else
echo "$profile"
fi
2018-02-01 14:54:28 +04:00
}
function 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 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 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 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 get_bfile() { get_pfile "$1" Base "$2"; }
2018-02-07 12:32:46 +04:00
function get_Cfile() { get_pfile "$1" Common "$2"; }
2018-02-01 14:54:28 +04:00
function get_cfile() { get_pfile "$1" Current "$2"; }
function get_vlfiles_nostrip() {
2018-02-01 14:54:28 +04:00
# afficher tous les fichiers de version
2018-03-02 13:09:29 +04:00
local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
[ -d "$pffdir/pff/$profile" ] || return
2018-03-02 13:09:29 +04:00
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}__"
2018-03-02 13:09:29 +04:00
else
find "$pffdir/pff/$profile" \
-type f -name "*__pv-${version}__" -o \
-type l -name "*__pv-${version}__"
2018-03-02 13:09:29 +04:00
fi
2018-02-01 14:54:28 +04:00
else
2018-03-02 13:09:29 +04:00
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-*__"
2018-03-02 13:09:29 +04:00
else
find "$pffdir/pff/$profile" \
-type f -name "*__pv-*__" -o \
-type l -name "*__pv-*__"
2018-03-02 13:09:29 +04:00
fi
2018-02-01 14:54:28 +04:00
fi
}
function get_vlfiles() {
local pffdir="$1" rfile="$2" profile="${3:-Base}" version="$4"
get_vlfiles_nostrip "$@" | sed "s|^$pffdir/pff/$profile/||"
}
2018-02-01 14:54:28 +04:00
function 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
2018-03-02 13:09:29 +04:00
array_from_lines profiles "$(get_user_profiles "$pffdir")"
2018-02-01 14:54:28 +04:00
fi
local vlfile rfile prefix pfile plink tmp
for vlfile in "$@"; do
rfile="${vlfile%__pv-*__}"
for profile in "${profiles[@]}"; do
prefix="$pffdir/pff/$profile"
2018-02-07 16:22:50 +04:00
flexists "$prefix/$rfile" || continue
2018-02-01 14:54:28 +04:00
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 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=get_local_files "$pffdir"
for lfile in "${lfiles[@]}"; do
src="$pffdir/pff/Current/$lfile"
if [ -f "$pffdir/pff/$profile/$lfile" ]; then
dest="$profile/$lfile"
2018-02-07 12:32:46 +04:00
elif [ "$profile" != Common -a -f "$pffdir/pff/Common/$lfile" ]; then
dest="Common/$lfile"
2018-02-01 14:54:28 +04:00
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 autoinit() {
# vérifications automatiques: créer les répertoires de base nécessaire au
# fonctionnement de pff dans le projet pff $1
2018-03-08 14:54:40 +04:00
local pffdir="$1" profile mkdir
2018-02-01 14:54:28 +04:00
[ -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 '{}' +
2018-03-08 14:54:40 +04:00
# Créer les répertoires de MKDIRS
for mkdir in "${MKDIRS[@]}"; do
mkdir -p "$pffdir/$mkdir"
done
return 0
2018-02-01 14:54:28 +04:00
}
function 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=get_first_profile "$pffdir"
[ -n "$profile" ] || profile=Base
enote "Autosélection du profil $profile"
select_profile "$profile" "$pffdir"
fi
}
function autofix() {
autoinit "$1"
autoselect "$1"
}
################################################################################
# Filtres
function pff_filter_normalize_properties() {
if [ $# -eq 0 ]; then
__norm_properties
else
local mode r
mode="$(fix_mode "$1")"
norm_properties "$1"; r=$?
unfix_mode "$1" "$mode"
return $r
fi
}
function apply_filter() {
# Appliquer les filtres définis au fichier $1 dans le projet pff $2
2018-04-30 13:36:32 +04:00
# $3 est le nom du fichier sans préfixe, pour la sélection du filtre
# retourner 0 si un filtre a été appliqué avec succès, 1 si une erreur s'est
# produite, 2 si aucun filtre n'existe pour ce fichier
local pfile="$1" pffdir="$2"
2018-04-30 13:36:32 +04:00
local realfile="${3:-$pfile}"
local rfile ffile filter r=2
2018-04-30 13:36:32 +04:00
setx rfile=get_rfile "$realfile" "$pffdir"
for filter in "${FILTERS[@]}"; do
splitpair "$filter" ffile filter
if [ "$ffile" == "$rfile" ]; then
"pff_filter_$filter" "$pfile" && r=0 || r=1
fi
done
return $r
}
2018-02-01 14:54:28 +04:00
################################################################################
# Commandes
#===========================================================
# pff --init
function init_cmd() {
local workdir="$1" archive="$2"
local pffdir
if [ -z "$workdir" ]; then
estep "Initialisation d'un nouveau projet pff"
read_value "Veuillez entrer le répertoire du projet" workdir
fi
if find_pffdir pffdir "$workdir"; then
enote "$(ppath "$pffdir"): est déjà un projet pff"
if ask_yesno "Voulez-vous vérifier la configuration et la mettre à jour?" O; then
conf_init "${PFFCONFVARS[@]}"
source "$pffdir/$PFFCONF"
conf_upgrade "$workdir/$PFFCONF"
conf_update -n "$workdir/$PFFCONF" ORIGEXTS
fi
return 0
fi
if [ ! -d "$workdir" ]; then
ewarn "$workdir: ce répertoire n'existe pas"
ask_yesno "Voulez-vous le créer?" O || return 1
mkdir -p "$workdir" || return 1
fi
enote "Initialisation d'un nouveau projet pff dans $workdir"
# fichier de configuration
conf_upgrade "$workdir/$PFFCONF" "${PFFCONFVARS[@]}"
if [ "${ORIGEXTS[*]}" != "${DEFAULT_ORIGEXTS[*]}" ]; then
conf_update "$workdir/$PFFCONF" ORIGEXTS
fi
array_addu PROFILES Common
conf_update "$workdir/$PFFCONF" PROFILES
2018-02-01 14:54:28 +04:00
# répertoire pff
mkdir -p "$workdir/pff"
[ -f "$workdir/pff/.gitignore" ] || echo >"$workdir/pff/.gitignore" "\
/Current/
/.Current"
autofix "$workdir"
return 0
}
#===========================================================
# pff --new
function new__prepare_archive() {
# préparer l'archive fournie en la copiant dans un répertoire temporaire
# initialiser la variable $1(=srcdir) avec le chemin du répertoire résultat
local destvar="${1:-srcdir}"; shift
local destver="${1:-version}"; shift
local archive="$1" version="$2" disttype="$3" unwrap="$4"
local srcdir
if [ -f "$archive" ]; then
is_archive "$archive" || die "$archive: doit être un fichier archive"
[ -n "$version" ] || version="$(get_archive_version "$archive")"
archive="$(abspath "$archive")"
elif [ -d "$archive" ]; then
srcdir="$(abspath "$archive")"
archive=
else
die "$archive: fichier introuvable"
fi
read_value "Entrez un identifiant pour cette version" version "$version" || die
local tmpd
if [ -n "$archive" ]; then
if [ "$disttype" == auto ]; then
case "$archive" in
*.war)
enote "Auto-sélection du type distribution complète sur la base de l'extension .war"
disttype=full
;;
*.zip)
enote "Auto-sélection du type distribution de patch sur la base de l'extension .zip"
disttype=patch
;;
*)
die "L'extension de l'archive n'est pas reconnue. Vous devez spécifier l'une des options -F ou -H"
2018-02-01 14:54:28 +04:00
;;
esac
fi
ac_set_tmpdir tmpd
estep "Extraction de $(ppath "$archive" "$cwd")"
extract_archive "$archive" "$tmpd" || die
srcdir="$tmpd"
if [ -n "$unwrap" ]; then
local -a files
array_lsall files "$srcdir" "*" ".*"
if [ "${#files[*]}" -eq 1 ]; then
local file="${files[0]}"
if [ "$unwrap" == auto ]; then
# nom de l'archive avec la version
local banv="$(get_archive_basename "$archive")"
# nom de l'archive sans la version
local ban="${banv%$(get_archive_versionsuffix "$archive")}"
local filename="$(basename "$file")"
[ "$filename" == "$banv" -o "$filename" == "$ban" ] || unwrap=
fi
[ -n "$unwrap" -a -d "$file" ] && srcdir="$file"
fi
fi
elif [ -d "$srcdir" ]; then
if [ "$disttype" == auto ]; then
die "La source est un répertoire. Vous devez spécifier l'une des option -F ou -H"
2018-02-01 14:54:28 +04:00
fi
ac_set_tmpdir tmpd
estep "Création d'une copie de travail de $(ppath "$srcdir" "$cwd")"
rsync ${QUIET:+-q} -a "$srcdir/" "$tmpd" || die
srcdir="$tmpd"
fi
local "$destvar"; upvar "$destvar" "$srcdir"
local "$destver"; upvar "$destver" "$version"
}
function new_cmd() {
local autopatch="$1" version="$2" disttype="$3" unwrap="$4" merge_strategy="$5" commit_policy="$6"; shift; shift; shift; shift; shift; shift
2018-02-01 14:54:28 +04:00
local archive="$1" pffdir="$2"
ensure_pffdir pffdir "$pffdir"
## préparer la source: les fichiers entrants
local srcdir full
new__prepare_archive srcdir version "$archive" "$version" "$disttype" "$unwrap"
[ "$disttype" == full ] && full=1
## lister les fichiers dans la source et la destination
local workdir
ac_set_tmpdir workdir
# fichiers sources
local fnsrc0="$workdir/nsrc0" fosrc0="$workdir/osrc0"
>"$fnsrc0"
>"$fosrc0"
find "$srcdir" -type f | awkrun ORIGEXTS[@] prefix="$srcdir/" fnsrc="$fnsrc0" fosrc="$fosrc0" '{
found = 0
for (i = 1; i <= ORIGEXTS_count; i++) {
sub("^" prefix, "")
if ($0 ~ ORIGEXTS[i] "(/|$)") {
print >fosrc
found = 1
break
}
}
if (!found) {
print >fnsrc
}
}'
# fichiers destination
local -a find; local p
local fndest0="$workdir/ndest0" fldest0="$workdir/ldest0"
get_local_files "$pffdir" >"$fldest0"
for p in "${PROTECTS[@]}" "${DEFAULT_PROTECTS[@]}"; do
2018-02-01 14:54:28 +04:00
[ ${#find[*]} -gt 0 ] && find=("${find[@]}" -o)
if [ "${p#/}" != "$p" ]; then
# les fichiers à la racine
p="${p#/}"
if [ "${p%/}" != "$p" ]; then
find=("${find[@]}" -type d -path "$pffdir/${p%/}" -prune)
else
find=("${find[@]}" -type f -path "$pffdir/$p" -o -type l -path "$pffdir/$p")
fi
else
# fichiers n'importe où dans l'arborescence
if [ "${p%/}" != "$p" ]; then
find=("${find[@]}" -type d -name "${p%/}" -prune)
else
find=("${find[@]}" -type f -name "$p" -o -type l -name "$p")
fi
fi
done
[ ${#find[*]} -gt 0 ] && find=("${find[@]}" -o)
find=(find "$pffdir" "${find[@]}" -type f -print -o -type l -print)
"${find[@]}" | sed "s|^$pffdir/||" | grep -vxf "$fldest0" >"$fndest0"
## Traiter les fichiers normaux
local fnsrc="$workdir/nsrc" fndest="$workdir/ndest"
grep -vxf "$fldest0" "$fnsrc0" | csort >"$fnsrc"
csort "$fndest0" >"$fndest"
local fcreates="$workdir/creates" fdeletes="$workdir/deletes" fcds="$workdir/cds" fupdates="$workdir/updates"
>"$fcreates"
>"$fdeletes"
diff "$fnsrc" "$fndest" | awkrun fcreates="$fcreates" fupdates="$fupdates" fdeletes="$fdeletes" '{
c = substr($0, 1, 1)
f = substr($0, 3)
if (c == "<") print f >fcreates
else if (c == ">") print f >fdeletes
}'
cat "$fcreates" "$fdeletes" >"$fcds"
grep -vxf "$fcds" "$fnsrc" >"$fupdates"
local -a creates updates deletes r i have_creates have_updates have_deletes src dest
array_from_lines creates "$(<"$fcreates")"; [ ${#creates[*]} -gt 0 ] && have_creates=1 || have_creates=
array_from_lines updates "$(<"$fupdates")"; [ ${#updates[*]} -gt 0 ] && have_updates=1 || have_updates=
array_from_lines deletes "$(<"$fdeletes")"; [ ${#deletes[*]} -gt 0 ] && have_deletes=1 || have_deletes=
enote "Fichiers normaux: $((${#creates[*]} + ${#updates[*]})) au total, ${#creates[*]} nouveau(x)${full:+", ${#deletes[*]} à supprimer"}"
ask_any -i "Voulez-vous continuer?" +Od; r=$?
if [ $r == 2 ]; then
for i in "${updates[@]}"; do
echo " $i"
done
for i in "${creates[@]}"; do
echo "+$i"
done
if [ -n "$full" ]; then
for i in "${deletes[@]}"; do
echo "-$i"
done
fi
ask_yesno "Voulez-vous continuer?" O || return 1
elif [ $r == 1 ]; then
return 1
fi
if [ -n "$have_creates" -o -n "$have_updates" -o -n "$have_deletes" ]; then
ebegin "Copie des fichiers"
r=0
for i in "${creates[@]}"; do
src="$srcdir/$i"
dest="$pffdir/$i"
mkdirof "$dest"; r=$?
edot $r "mkdirof $i"; [ $r -eq 0 ] || break
cp "$src" "$dest"; r=$?
edot $r "create $i"; [ $r -eq 0 ] || break
apply_filter "$dest" "$pffdir"; r=$?
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
2018-02-01 14:54:28 +04:00
done
[ $r -eq 0 ] || { eend $r; return 1; }
2018-02-01 14:54:28 +04:00
for i in "${updates[@]}"; do
src="$srcdir/$i"
dest="$pffdir/$i"
if diff -q "$src" "$dest" >&/dev/null; then
# pas la peine de copier si les fichiers sont identiques
show_debug && edot 0 "$i: non modifié"
continue
fi
cat "$src" >"$dest"; r=$?
edot $r "update $i"; [ $r -eq 0 ] || break
apply_filter "$dest" "$pffdir"; r=$?
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
2018-02-01 14:54:28 +04:00
done
[ $r -eq 0 ] || { eend $r; return 1; }
2018-02-01 14:54:28 +04:00
if [ -n "$full" ]; then
# ne faire les suppression qu'en mode full
for i in "${deletes[@]}"; do
rm "$pffdir/$i"; r=$?
edot $r "delete $i"; [ $r -eq 0 ] || break
2018-02-01 14:54:28 +04:00
done
fi
eend $r
fi
## Traiter les fichiers origines
local ftmp="$workdir/tmp"
local fosrc="$workdir/osrc" flosrc="$workdir/losrc" flsrc="$workdir/lsrc" fldest="$workdir/ldest"
csort "$fosrc0" >"$fosrc"
>"$flsrc"
>"$flosrc"
awkrun <"$fosrc0" ORIGEXTS[@] flsrc="$flsrc" flosrc="$flosrc" '{
for (i = 1; i <= ORIGEXTS_count; i++) {
if ($0 ~ ORIGEXTS[i] "(/|$)") {
orig = $0
local = gensub(ORIGEXTS[i] "(/|$)", "\\1", 1, $0)
print local ":" orig >flosrc
print local >flsrc
break
}
}
}'
csort "$flsrc" >"$ftmp"; cat "$ftmp" >"$flsrc"
csort "$fldest0" >"$fldest"
local losrc lsrc osrc; local -a tmpa; declare -A losrcs
array_from_lines tmpa "$(<"$flosrc")"
for losrc in "${tmpa[@]}"; do
splitpair "$losrc" lsrc osrc
losrcs["$lsrc"]="$osrc"
done
>"$fcreates"
>"$fdeletes"
diff "$flsrc" "$fldest" | awkrun fcreates="$fcreates" fupdates="$fupdates" fdeletes="$fdeletes" '{
c = substr($0, 1, 1)
f = substr($0, 3)
if (c == "<") print f >fcreates
else if (c == ">") print f >fdeletes
}'
# contrairement aux fichiers normaux, ajouter le contenu de fdeletes à fupdates
# les fichiers de fdeletes sont des fichiers locaux non identifiés comme tels dans l'origine
{ grep -vxf "$fcreates" "$flsrc"; cat "$fdeletes"; } >"$fupdates"
2018-02-01 14:54:28 +04:00
array_from_lines creates "$(<"$fcreates")"; [ ${#creates[*]} -gt 0 ] && have_creates=1 || have_creates=
array_from_lines updates "$(<"$fupdates")"; [ ${#updates[*]} -gt 0 ] && have_updates=1 || have_updates=
enote "Fichiers origines: $((${#creates[*]} + ${#updates[*]})) au total, ${#creates[*]} nouveau(x)"
ask_any -i "Voulez-vous continuer?" +Od; r=$?
if [ $r == 2 ]; then
for i in "${updates[@]}"; do
echo " $i"
done
for i in "${creates[@]}"; do
echo "+$i"
done
ask_yesno "Voulez-vous continuer?" O || return 1
elif [ $r == 1 ]; then
return 1
fi
local vlfile; local -a vlfiles
if [ -n "$have_creates" -o -n "$have_updates" ]; then
ebegin "Copie des fichiers"
r=0
for i in "${creates[@]}"; do
src="$srcdir/${losrcs[$i]}"
dest="$pffdir/$i"
mkdirof "$dest"; r=$?
edot $r "mkdirof $i"; [ $r -eq 0 ] || break
2018-02-01 14:54:28 +04:00
cp "$src" "$dest"; r=$?
edot $r "create $i"; [ $r -eq 0 ] || break
apply_filter "$dest" "$pffdir"; r=$?
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $i"
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
2018-02-01 14:54:28 +04:00
add_global__link "$dest" "$pffdir"
edot $r "add_global $i"; [ $r -eq 0 ] || break
2018-02-01 14:54:28 +04:00
done
[ $r -eq 0 ] || { eend $r; return 1; }
2018-02-01 14:54:28 +04:00
for i in "${updates[@]}"; do
# du fait qu'on intègre fdeletes dans fupdates, il est possible que
# le fichier n'existe pas dans la source. de plus, l'association
# dans losrcs n'existe pas non plus. dans ce cas, il suffit de
# d'ignorer le fichier
if [ -n "${losrcs[$i]+set}" ]; then
src="$srcdir/${losrcs[$i]}"
else
src="$srcdir/$i"
fi
2018-02-01 14:54:28 +04:00
vlfile="${i}__pv-${version}__"
dest="$pffdir/pff/Base/$vlfile"
if flexists "$src"; then
mkdirof "$dest"; r=$?
edot $r "mkdirof $vlfile"; [ $r -eq 0 ] || break
cp "$src" "$dest"; r=$?
edot $r "create $vlfile"; [ $r -eq 0 ] || break
2018-04-30 13:36:32 +04:00
apply_filter "$dest" "$pffdir" "$pffdir/pff/Base/$i"; r=$?
[ $r -eq 0 -o $r -eq 1 ] && edot $r "filter $vlfile"
[ $r -eq 2 ] && r=0; [ $r -eq 0 ] || break
array_add vlfiles "$vlfile"
fi
2018-02-01 14:54:28 +04:00
done
[ $r -eq 0 ] || { eend $r; return 1; }
2018-02-01 14:54:28 +04:00
eend $r
fi
sync_vlfiles "$pffdir" "" "${vlfiles[@]}"
array_addu PVERSIONS "$version"
conf_update "$pffdir/$PFFCONF" PVERSIONS
## Fin du traitement
ac_clean "$srcdir" "$workdir"
if [ -n "$autopatch" ]; then
# lancer la commande patch pour intégrer les modifications
ask_yesno "Voulez-vous passer à l'intégration de cette version?" O &&
patch_cmd "$merge_strategy" "$commit_policy" "$pffdir"
2018-02-01 14:54:28 +04:00
fi
}
#===========================================================
# pff --patch
function patch_cmd() {
local merge_strategy="$1" commit_policy="$2"; shift; shift
local pffdir="$1"
local gitproject was_patched eop_version
local version profile rcfile curdir workdir controlfile control tmpfile
local -a profiles vlfiles tmpfiles
2018-02-01 14:54:28 +04:00
ensure_pffdir pffdir "$pffdir"
# est-ce un projet suivi dans git?
(cd "$pffdir"; git_check_gitvcs) && gitproject=1 || gitproject=
2018-03-02 13:09:29 +04:00
array_from_lines profiles "$(get_user_profiles "$pffdir")"
if array_contains profiles Common; then
# toujours traiter Common en dernier
array_del profiles Common
array_add profiles Common
fi
case "$merge_strategy" in
ours) merge_strategy=(-s recursive -X ours);;
theirs) merge_strategy=(-s recursive -X theirs);;
*) merge_strategy=();;
esac
while true; do
# si pas de version en cours, il n'y a rien à patcher
[ ${#PVERSIONS[*]} -gt 0 ] || break
eop_version=
for version in "${PVERSIONS[@]}"; do
2018-03-02 13:09:29 +04:00
have_profile_vlfiles=
have_base_vlfiles=
for profile in "${profiles[@]}"; do
array_from_lines vlfiles "$(get_vlfiles_nostrip "$pffdir" "" "$profile" "$version")"
2018-03-02 13:09:29 +04:00
if [ ${#vlfiles[*]} -gt 0 ]; then
have_profile_vlfiles=1
break
fi
done
[ -n "$have_profile_vlfiles" ] && break
array_from_lines vlfiles "$(get_vlfiles_nostrip "$pffdir" "" Base "$version")"
have_base_vlfiles=1
break
2018-03-02 13:09:29 +04:00
done
if [ -n "$have_profile_vlfiles" ]; then
# il faut patcher les fichiers du profil
etitle "Intégration de la version $version dans le profil $profile"
[ -n "$rcfile" ] || ac_set_tmpfile rcfile
[ -n "$controlfile" ] || ac_set_tmpfile controlfile
[ -n "$workdir" ] && ac_clean "$workdir"
ac_set_tmpdir workdir
curdir="$(pwd)"
cd "$workdir"
git init -q .
git checkout -q --orphan upstream
# rajouter les fichiers de Base dans la branche upstream
for vlfile in "${vlfiles[@]}"; do
setx bfile=get_bfile "${vlfile%__pv-${version}__}" "$pffdir"
setx rfile=get_rfile "$bfile" "$pffdir"
mkdirof "$rfile"
cp -a "$bfile" "$rfile"
chmod +w "$rfile"
done
git add -A
[ -n "$(git status --porcelain)" ] && git commit -qm "Base"
# créer la branche v$version depuis upstream
git checkout -qb "v$version"
# rajouter les fichiers de Common s'ils existent (sauf si la branche c'est Common ^^)
if [ "$profile" != Common ]; then
for vlfile in "${vlfiles[@]}"; do
setx Cfile=get_Cfile "${vlfile%__pv-${version}__}" "$pffdir"
if flexists "$Cfile"; then
setx rfile=get_rfile "$Cfile" "$pffdir"
mkdirof "$rfile"
cp -a "$Cfile" "$rfile"
fi
done
git add -A
[ -n "$(git status --porcelain)" ] && git commit -qm "Common"
fi
# rajouter les fichiers du profil
for vlfile in "${vlfiles[@]}"; do
setx pfile=get_pfile "${vlfile%__pv-${version}__}" "$profile" "$pffdir"
setx rfile=get_rfile "$pfile" "$pffdir"
mkdirof "$rfile"
cp -a "$pfile" "$rfile"
done
git add -A
[ -n "$(git status --porcelain)" ] && git commit -qm "$profile"
# rebasculer vers upstream et rajouter les fichiers de patch
git checkout -q upstream
for vlfile in "${vlfiles[@]}"; do
setx rfile=get_rfile "${vlfile%__pv-${version}__}" "$pffdir"
mkdirof "$rfile"
cp -L "$vlfile" "$rfile"
done
git add -A
[ -n "$(git status --porcelain)" ] && git commit -qm "$version"
# basculer vers la branche de version et tenter de merger upstream dans version
git checkout -q "v$version"
if git merge -q "${merge_strategy[@]}" --no-commit upstream; then
# tout s'est bien passé
[ -n "$(git status --porcelain)" ] && git commit -qm "v$version --> $profile"
else
# il y a eu une erreur. laisser l'utilisateur décider quoi faire
echo >"$rcfile" "#
[ -f /etc/bash.bashrc ] && . /etc/bash.bashrc
[ -f ~/.bashrc ] && . ~/.bashrc
$(qvals source "$ULIBDIR/ulib")
urequire DEFAULTS
function Commit() { echo COMMIT >$(qvals "$controlfile"); exit; }
# raccourcis pour Commit
function C() { Commit \"\$@\"; }; function c() { Commit \"\$@\"; }; function commit() { Commit \"\$@\"; }
function Abort() { echo ABORT >$(qvals "$controlfile"); exit; }
# raccourcis pour Abort
function A() { Abort \"\$@\"; }; function a() { Abort \"\$@\"; }; function abort() { Abort \"\$@\"; }
$(qvals cd "$workdir")
$(qvals eerror "Une erreur s'est produite: consultez les fichiers concernés et faites les corrections nécessaires. En cas de conflit, vous devez les résoudre en examinant les lignes marquées
<<<<<<< HEAD
=======
>>>>>>> upstream
puis en gardant la partie HEAD pour les modifications locales et upstream pour les modifications entrantes")
$(qvals eimportant "Puis tapez ${COULEUR_VERTE}Commit${COULEUR_NORMALE} pour valider l'intégration de cette version")
$(qvals eimportant "Sinon, tapez ${COULEUR_ROUGE}Abort${COULEUR_NORMALE} pour arrêter l'intégration de cette version")
"
>"$controlfile"
"${SHELL:-bash}" --rcfile "$rcfile"
control="$(<"$controlfile")"
if [ -z "$control" ]; then
# demander à l'utilisateur sa décision
eerror "Vous n'avez pas terminé votre session par ${COULEUR_VERTE}Commit${COULEUR_NORMALE} ou ${COULEUR_ROUGE}Abort${COULEUR_NORMALE}"
ask_yesno "Voulez-vous valider l'intégration de la version?" N && control=COMMIT || control=ABORT
fi
if [ "$control" == COMMIT ]; then
array_from_lines tmpfiles "$(git status --porcelain | awk '{print substr($0, 4)}')"
for tmpfile in "${tmpfiles[@]}"; do
if grep -qE '^(<<<<<<<|=======|>>>>>>>)' "$tmpfile"; then
ewarn "Vous avez choisi de valider l'intégration des modifications mais..."
ewarn "$tmpfile: ce fichier semble encore contenir des marques de conflit"
if ! ask_yesno -y "Etes-vous sûr d'avoir correctement édité le fichier?" N; then
eecho "Annulation de l'intégration de la version"
control=ABORT
break
fi
fi
done
fi
if [ "$control" == ABORT ]; then
enote "Vous pouvez reprendre l'intégration de la version $version avec la commande pff -g"
break # abort
fi
fi
# récupérer les versions modifiées et supprimer les fichiers de patch
for vlfile in "${vlfiles[@]}"; do
setx pfile=get_pfile "${vlfile%__pv-${version}__}" "$profile" "$pffdir"
setx rfile=get_rfile "$pfile" "$pffdir"
cp -a "$rfile" "$pfile"
rm "$vlfile"
done
cd "$curdir"
eend
2018-03-02 13:09:29 +04:00
elif [ -n "$have_base_vlfiles" ]; then
# il faut intégrer la nouvelle version dans Base
etitle "Finaliser intégration de la version $version"
for vlfile in "${vlfiles[@]}"; do
bfile="${vlfile%__pv-${version}__}"
chmod +w "$bfile"
mv "$vlfile" "$bfile"
chmod a-w "$bfile"
done
eop_version=1
VERSION="$version"
array_del PVERSIONS "$VERSION"
conf_update "$pffdir/$PFFCONF" VERSION PVERSIONS
eend
2018-03-02 13:09:29 +04:00
fi
if [ -n "$gitproject" ]; then
local commit default
if [ -n "$eop_version" ]; then
msg="Intégration de la version $version"
else
msg="Correction du profil $profile pour la version $version"
fi
commit="$commit_policy"
if [ "$commit" == ask ]; then
if [ -n "$eop_version" ]; then
enote "Vous avez terminé l'intégration des patches de la version $version"
default=O
else
einfo "Vous avez intégré les patches de la version $version pour le profil $profile"
default=N
fi
ask_yesno "Voulez-vous enregistrer les modifications dans git?" $default || commit=
fi
if [ -n "$commit" ]; then
git add -A && git commit -m "$msg" || return
if [ -z "$UTOOLS_VCS_OFFLINE" -a -n "$(git_get_branch_remote)" ]; then
git push
fi
fi
fi
done
2018-02-01 14:54:28 +04:00
}
#===========================================================
# pff --add-global
function add_global__link() {
local pfile="$1" pffdir="$2"
local rfile bfile cfile plink clink tmp
setx rfile=get_rfile "$pfile" "$pffdir" || return
2018-02-07 16:22:50 +04:00
flexists "$pffdir/$rfile" || touch "$pffdir/$rfile"
2018-02-01 14:54:28 +04:00
setx bfile=get_bfile "$pfile" "$pffdir"
setx cfile=get_cfile "$pfile" "$pffdir"
setx plink=multiups "$rfile" "pff/Current/$rfile"
setx clink=multiups "Current/$rfile" "Base/$rfile"
2018-02-07 16:22:50 +04:00
if flexists "$bfile" && [ -L "$pfile" -a -L "$cfile" ]; then
2018-02-01 14:54:28 +04:00
: # ok pour les liens
else
# Création des liens pour $rfile
mkdirof "$bfile" || return
mv "$pfile" "$bfile" || return
chmod a-w "$bfile" || return
2018-02-01 14:54:28 +04:00
[ -L "$pfile" ] || ln -sf "$plink" "$pfile" || return
mkdirof "$cfile" || return
[ -L "$cfile" ] || ln -sf "$clink" "$cfile" || return
fi
# correction éventuelle du lien existant
setx tmp=readlink "$pfile"
[ "$tmp" == "$plink" ] || ln -sfT "$plink" "$pfile"
return 0
}
function add_global_cmd() {
local pffdir file pfile
ensure_pffdir pffdir
for file in "$@"; do
if [ -d "$file" ]; then
ewarn "$file: est un répertoire. argument ignoré"
continue
2018-02-07 16:22:50 +04:00
elif flexists "$file"; then
2018-02-01 14:54:28 +04:00
:
else
ewarn "$file: fichier introuvable"
ask_yesno "Voulez-vous le créer?" O || continue
fi
setx pfile=abspath "$file"
setx rfile=get_rfile "$pfile" || {
eerror "$file: chemin invalide. argument ignoré"
continue
}
add_global__link "$pfile" "$pffdir" || return
done
}
#===========================================================
# pff --locals
function list_locals_cmd() {
local pffdir="$1"
ensure_pffdir pffdir "$pffdir"
get_local_files "$pffdir"
}
#===========================================================
# pff --profiles
function list_profiles_cmd() {
local pffdir="$1"
ensure_pffdir pffdir "$pffdir"
get_profiles "$pffdir"
}
#===========================================================
# pff --switch
function switch_cmd() {
local profile="$1" pffdir="$2"
local -a profiles
ensure_pffdir pffdir "$pffdir"
autoinit "$pffdir"
[ -n "$profile" ] || setx profile=get_first_profile "$pffdir"
2018-02-01 14:54:28 +04:00
setx -a profiles=get_profiles "$pffdir"
if ! array_contains profiles "$profile"; then
if ! array_contains PROFILES "$profile"; then
ewarn "$profile: ce profil n'existe pas"
ask_yesno "Voulez-vous le créer?" O || return 1
array_addu PROFILES "$profile"
conf_update "$pffdir/$PFFCONF" PROFILES
fi
2018-02-01 14:54:28 +04:00
fi
enote "Sélection du profil $profile"
select_profile "$profile" "$pffdir"
}
#===========================================================
# pff --add-local
function add_local__link() {
local pfile="$1" profile="$2" pffdir="$3"
local rfile bfile pfile Pfile plink Plink
# vérifier validité du chemin
setx rfile=get_rfile "$pfile" "$pffdir" || return
2018-02-07 16:22:50 +04:00
flexists "$pffdir/$rfile" || touch "$pffdir/$rfile"
2018-02-01 14:54:28 +04:00
# vérifier la présence du lien global
setx bfile=get_bfile "$pfile" "$pffdir"
2018-02-07 16:22:50 +04:00
if ! flexists "$bfile"; then
2018-02-01 14:54:28 +04:00
add_global__link "$pfile" "$pffdir" || return
fi
# vérifier la présence du lien local
setx Pfile=get_pfile "$pfile" "$profile" "$pffdir"
2018-02-07 16:22:50 +04:00
flexists "$Pfile" && return 0
2018-02-01 14:54:28 +04:00
# créer le fichier local
2018-02-07 12:32:46 +04:00
setx Cfile=get_Cfile "$pfile" "$pffdir"
2018-02-01 14:54:28 +04:00
mkdirof "$Pfile"
2018-02-07 16:22:50 +04:00
if flexists "$Cfile"; then
2018-02-07 12:32:46 +04:00
# copier depuis le profil Common par défaut
cp "$Cfile" "$Pfile"
else
cp "$bfile" "$Pfile"
chmod +w "$Pfile"
2018-02-07 12:32:46 +04:00
fi
2018-02-01 14:54:28 +04:00
# mettre à jour le profil courant
setx cfile=get_cfile "$pfile" "$pffdir"
setx clink=multiups "Current/$rfile" "$profile/$rfile"
ln -sf "$clink" "$cfile"
# intégrer les fichiers de version
local -a vlfiles
array_from_lines vlfiles "$(get_vlfiles "$pffdir" "$rfile")"
sync_vlfiles "$pffdir" "$profile" "${vlfiles[@]}"
return 0
}
function add_local_cmd() {
local pffdir file rfile pfile
local profile r
ensure_pffdir pffdir
setx profile=get_current_profile "$pffdir"
r=0
for file in "$@"; do
if [ -d "$file" ]; then
ewarn "$file: est un répertoire. argument ignoré"
continue
2018-02-07 16:22:50 +04:00
elif flexists "$file"; then
2018-02-01 14:54:28 +04:00
:
else
ewarn "$file: fichier introuvable"
ask_yesno "Voulez-vous le créer?" O || continue
fi
setx pfile=abspath "$file"
setx rfile=get_rfile "$pfile" || {
eerror "$file: chemin invalide. argument ignoré"
r=1
continue
}
add_local__link "$pfile" "$profile" "$pffdir" || {
ewarn "$file: erreur lors de la création du lien local"
r=1
continue
}
done
return $r
}
#===========================================================
# pff --edit
function edit_cmd() {
local profile="$1"; shift
local pffdir file rfile pfile Pfile r
local -a profiles edits args
2018-02-01 14:54:28 +04:00
ensure_pffdir pffdir
2018-03-02 13:09:29 +04:00
array_from_lines profiles "$(get_user_profiles "$pffdir")"
[ -n "$profile" ] || setx profile=get_current_profile "$pffdir"
enote "Dans le profil $profile:"
2018-02-01 14:54:28 +04:00
r=0
while [ $# -gt 0 ]; do
if [[ "$1" == -* ]]; then
# nouvelle option
local prev_profile="$profile"
args=(+ -p:,--profile: profile=)
parse_args "$@"; set -- "${args[@]}"
[ "$profile" != "$prev_profile" ] && enote "Dans le profil $profile:"
continue
fi
file="$1"; shift
2018-02-01 14:54:28 +04:00
if [ -d "$file" ]; then
ewarn "$file: est un répertoire. argument ignoré"
continue
fi
setx pfile=abspath "$file"
setx rfile=get_rfile "$pfile" || {
eerror "$file: chemin invalide. argument ignoré"
r=1
continue
}
if [ "$profile" == ALL ]; then
local tmpp
for tmpp in "${profiles[@]}"; do
setx Pfile=get_pfile "$pfile" "$tmpp" "$pffdir"
[ -e "$Pfile" ] || continue
estep "Edition de $(ppath "$Pfile")"
array_add edits "$Pfile"
done
else
add_local__link "$pfile" "$profile" "$pffdir" || {
ewarn "$file: erreur lors de la création du lien local"
r=1
continue
}
setx Pfile=get_pfile "$pfile" "$profile" "$pffdir"
estep "Edition de $(ppath "$Pfile")"
array_add edits "$Pfile"
fi
2018-02-01 14:54:28 +04:00
done
if [ ${#edits[*]} -gt 0 ]; then
"${EDITOR:-vi}" "${edits[@]}" || r=$?
else
r=2
fi
2018-02-01 14:54:28 +04:00
return $r
}
2018-02-07 16:22:50 +04:00
#===========================================================
# pff --diff
function diff_cmd() {
local list_names="$1"; shift
2018-02-07 16:58:54 +04:00
local pffdir profile srcp destp desc
2018-02-07 16:22:50 +04:00
case $# in
0)
ensure_pffdir pffdir
setx srcp=get_current_profile "$pffdir"
destp=
;;
1)
if withpath "$1"; then
ensure_pffdir pffdir "$1"
setx srcp=get_current_profile "$pffdir"
destp=
else
ensure_pffdir pffdir
setx srcp=get_current_profile "$pffdir"
destp="$1"
fi
;;
2)
if withpath "$2"; then
ensure_pffdir pffdir "$2"
setx srcp=get_current_profile "$pffdir"
destp="$1"
else
ensure_pffdir pffdir
srcp="$1"
destp="$2"
fi
;;
*)
ensure_pffdir pffdir "$3"
srcp="$1"
destp="$2"
;;
esac
2018-02-07 16:58:54 +04:00
[ "$destp" == "Common|Base" ] && destp=
if [ -z "$destp" ]; then
if [ "$srcp" == Base -o "$srcp" == Common ]; then
desc="pff --diff $srcp Base"
else
desc="pff --diff $srcp ${destp:-"Common|Base"}"
fi
else
desc="pff --diff $srcp $destp"
fi
2018-02-07 16:22:50 +04:00
local -a lfiles; local rfile bfile Cfile srcfile destfile
setx -a lfiles=get_local_files "$pffdir"
local -a diffcolor
isatty && diffcolor=(--color) || diffcolor=(--no-color)
2018-02-07 16:22:50 +04:00
for rfile in "${lfiles[@]}"; do
setx srcfile=get_pfile "$pffdir/$rfile" "$srcp" "$pffdir"
flexists "$srcfile" || continue
if [ -n "$destp" ]; then
setx destfile=get_pfile "$pffdir/$rfile" "$destp" "$pffdir"
else
setx Cfile=get_Cfile "$pffdir/$rfile" "$pffdir"
setx bfile=get_bfile "$pffdir/$rfile" "$pffdir"
# si on précise pas le profil destination, sélectionner dans l'ordre
# Common ou Base
if [ "$srcp" == Base -o "$srcp" == Common ]; then
destfile="$bfile"
elif flexists "$Cfile"; then
destfile="$Cfile"
else
destfile="$bfile"
fi
fi
if [ -n "$list_names" ]; then
2018-03-05 23:01:56 +04:00
diff -q "$destfile" "$srcfile" >&/dev/null || echo "$srcfile"
else
2018-02-07 16:58:54 +04:00
[ -n "$desc" ] && echo "$desc"
desc=
if [ -n "$PFF_USE_REGULAR_DIFF" ]; then
# au cas où git n'est pas disponible
diff -ur "$destfile" "$srcfile"
else
git diff "${diffcolor[@]}" --no-index "$destfile" "$srcfile"
fi
fi
2018-02-07 16:22:50 +04:00
done | page_maybe
}
2018-02-01 14:54:28 +04:00
#===========================================================
# pff --infos
function infos_cmd() {
local show_all="$1"; shift
2018-02-01 14:54:28 +04:00
local pffdir="$1"
local -a profiles vlfiles
local rfile Pfile flag
if find_pffdir pffdir "$pffdir"; then
ensure_pffdir pffdir "$pffdir"
autofix "$pffdir"
setx -a lfiles=get_local_files "$pffdir"
setx profile=get_current_profile "$pffdir"
setx -a profiles=get_profiles "$pffdir"
if [ ${#lfiles[*]} -gt 0 ]; then
[ ${#lfiles[*]} -gt 1 ] && estep "${#lfiles[*]} fichiers locaux" || estep "1 fichier local"
for rfile in "${lfiles[@]}"; do
setx -a vlfiles=get_vlfiles "$pffdir" "$rfile" "$profile"
setx Pfile=get_pfile "$pffdir/$rfile" "$profile" "$pffdir"
setx Cfile=get_Cfile "$pffdir/$rfile" "$pffdir"
if [ ${#vlfiles[*]} -gt 0 ]; then
flag="${COULEUR_ROUGE}P${COULEUR_NORMALE} "
elif [ -f "$Pfile" ]; then
flag="${COULEUR_BLEUE}*${COULEUR_NORMALE} "
elif [ "$profile" != Common -a -f "$Cfile" ]; then
2018-02-07 16:22:50 +04:00
flag="$(get_color YELLOW)C${COULEUR_NORMALE} "
elif [ -z "$show_all" ]; then
2018-02-07 16:22:50 +04:00
continue
else
flag=" "
fi
uecho " $flag$rfile"
done
else
estep "Pas de fichiers locaux définis"
fi
estep "Version courante: ${COULEUR_BLEUE}$VERSION${COULEUR_NORMALE}"
estep "Versions en attente: ${COULEUR_ROUGE}${PVERSIONS[*]}${COULEUR_NORMALE}"
[ -n "$profile" ] && estep "Profil courant: ${COULEUR_BLEUE}$profile${COULEUR_NORMALE}" || estep "${COULEUR_JAUNE}Pas de profil courant${COULEUR_NORMALE}"
[ ${#profiles[*]} -gt 0 ] && estep "Profils valides: ${profiles[*]}" || estep "Pas de profils définis"
elif [ -f "${pffdir:-.}/$PFFCONF" ]; then
: "${pffdir:=.}"
conf_init "${PFFCONFVARS[@]}"
source "$pffdir/$PFFCONF"
estep "[Projet pff déployé] Version courante: ${COULEUR_BLEUE}$VERSION${COULEUR_NORMALE}"
2018-02-01 14:54:28 +04:00
fi
}
################################################################################
# Programme principal
for script_alias in "${SCRIPT_ALIASES[@]}"; do
splitpair "$script_alias" src option
if [ "$scriptname" == "$src" ]; then
eval "set -- $option \"\$@\""
break
fi
done
QUIET=1 # masquer les messages de git et rsync?
VERYQUIET=1 # masquer les messages de git commit?
parse_mode=
if [ "$1" == -e -o "$1" == --edit ]; then
parse_mode=+
fi
2018-02-01 14:54:28 +04:00
action=infos
autopatch=1
version=
disttype=auto
ORIGEXTS=("${DEFAULT_ORIGEXTS[@]}")
unwrap=auto
merge_strategy=
commit_policy=ask
profile=
alternate=
args=($parse_mode
2018-02-01 14:54:28 +04:00
--help '$exit_with display_help'
-0,--init action=init
--s:,--origext: '$add@ ORIGEXTS'
--k,--clear-origexts '$ORIGEXTS=()'
-N,--new action=new
-M,--new-only '$action=new; autopatch='
-V:,--version: version=
--auto-archive disttype=auto
-F,--full-archive disttype=full
-H,--patch-archive disttype=patch
2018-02-01 14:54:28 +04:00
--auto-unwrap unwrap=auto
--no-unwrap unwrap=
-E,--unwrap unwrap=1
-g,--patch action=patch
-o,--ours merge_strategy=ours
--theirs merge_strategy=theirs
--ask-commit commit_policy=ask
-c,--comit commit_policy=1
--no-commit commit_policy=
-b,--add-global action=add-global
2018-02-07 16:22:50 +04:00
--locals action=list-locals
--profiles action=list-profiles
2018-02-01 14:54:28 +04:00
-s,--switch action=switch
-a,--add-local action=add-local
2018-02-01 14:54:28 +04:00
-e,--edit action=edit
-p:,--profile: profile=
-P,--prod profile=prod
-T,--test profile=test
-A,--all-profiles profile=ALL
2018-02-07 16:22:50 +04:00
-d,--diff action=diff
2018-02-01 14:54:28 +04:00
--infos action=infos
-l,--list-names,--show-all alternate=1
2018-02-01 14:54:28 +04:00
)
parse_args "$@"; set -- "${args[@]}"
array_fix_paths ORIGEXTS
case "$action" in
init) init_cmd "$@";;
new) new_cmd "$autopatch" "$version" "$disttype" "$unwrap" "$merge_strategy" "$commit_policy" "$@";;
patch) patch_cmd "$merge_strategy" "$commit_policy" "$@";;
2018-02-01 14:54:28 +04:00
add-global) add_global_cmd "$@";;
list-locals) list_locals_cmd "$@";;
list-profiles) list_profiles_cmd "$@";;
switch) switch_cmd "$@";;
add-local) add_local_cmd "$@";;
edit) edit_cmd "$profile" "$@";;
diff) diff_cmd "$alternate" "$@";;
infos) infos_cmd "$alternate" "$@";;
2018-02-01 14:54:28 +04:00
esac