Intégration de la branche release-6.4.0

This commit is contained in:
Jephté Clain 2017-05-05 16:50:31 +04:00
commit 4f5d3794fe
24 changed files with 2233 additions and 277 deletions

View File

@ -1,3 +1,48 @@
## Version 6.4.0 du 05/05/2017-16:50
* `c25160c` fndate: maj doc et améliorations ergonomiques
* `d5f4ae2` pclone: filtrer les projets de type wildcard
* `f8f731e` support de la complétion pour rwoinst -w
* `e4b7714` tailor.py et chrono.py: améliorer l'affichage de l'aide
* `6f1d5e5` Intégration de la branche rwoinst-updates
* `4aaded5` support des répertoires source maven et de l'interrogation de deploy pour: profil, déploiement des resources web, variables bounce, notag, exec, dbconfig, dbconfigmap
* `f20cc48` rwoinst: ajouter l'option -w qui peut être spécifiée en parallèle de -h pour déployer les resources web vers un serveur différent
* `9e4ccae` woinst: support des préfixes dans les variables
* `8f9ddb5` uinst.sh et woinst.sh: améliorer la comptabilité
* `e01cabb` makeself: retourner le code d'erreur même avec un auto-extractible temporaire
* `8482d61` pclone: ajouter l'option --master
* `a264b3e` pclone: implémenter le clonage récursif avec -r ainsi que les options -n et -u
* `262048f` ufile: possibilité de classer vers plusieurs destinations
* `8c22012` ufile: copier en local sans utiliser scp si possible
* `dec100b` ufile: support du classement par scp
* `c82c908` ufile: support des inclusions, d'un fichier de configuration alternatif
* `d535df3` ufile: le code de retour de renamef peut annuler une règle
* `0365c54` améliorer l'ergonomie de ruinst, rwoinst, toinst: pas la peine de confirmer le répertoire courant s'il est déployé sur un hôte
* `8ce9821` fndate: améliorer l'ergonomie de l'option -@
* `5b0f13d` ufile: changer la sémantique de renamef
* `fec6b2f` pu: support du scenario ou la branche de feature distante a été supprimée
* `151433b` ufile: ajout des options --list et --edit
* `a99f313` ufile: classement automatique de fichiers
* `540e718` base.core: qwc supporte les classes de caractères
* `78e0a8e` cosmetic
* `027b416` ppath2() est plus approprié dans certaines circonstances
* `c034115` revert 9d8b32bcd118ca9194b2d134e51a078d16b1c18d: la correction du bug était incorrecte
* `1dbe02f` Intégration de la branche tomcat-deploy
* `2394edf` toinst: implémenter la sauvegarde
* `fc467cb` finaliser le support de .toinst.conf et traiter les options --exclude, --protect et --rsync-option
* `9d8b32b` parse_args: bug avec la fonction reset
* `9084e7e` début du support de .toinst.conf
* `a84775c` support des options --wamap et --protect depuis la configuration locale de déploiement
* `3bbabbf` deploy: support des options --of, --df, --lf
* `c1a536b` deploy: support des options -old pour afficher des informations supplémentaires
* `2daf190` quelques améliorations dont support du profil de déploiement
* `0b46830` implémenter toinst et rtoinst
* `836c933` maj doc ruinst et désactiver déploiement de clé par rwoinst
* `2991e63` deploy: requête récursive par défaut
* `52bb0d5` base.core: ajout de la fonction echo_setv2() pendant de echo_seta2()
* `bab9c50` tailor.py: suivre un fichier est optionnel
* `4593ad8` ajout de tailor.py: affichage d'un fichier en mettant en surbrillance certains patterns
## Version 6.3.1 du 06/04/2017-22:34
* `b3e6ff6` cgilsxml.py: maj doc

View File

@ -1 +1 @@
6.3.1
6.4.0

View File

@ -273,26 +273,55 @@ def run_chronometre(timeout=None, autostart=False):
Application(timeout, autostart).mainloop()
if __name__ == '__main__':
from argparse import ArgumentParser, RawTextHelpFormatter
from argparse import ArgumentParser, HelpFormatter
if sys.argv[1:2] == ['--compat']:
# Avec l'argument --compat, désactiver la classe FancyHelpFormatter qui
# se base sur une API non documentée
sys.argv = sys.argv[0:1] + sys.argv[2:]
FancyHelpFormatter = HelpFormatter
else:
class FancyHelpFormatter(HelpFormatter):
"""Comme HelpFormatter, mais ne touche pas aux lignes qui commencent par les
caractères '>>>'. Cela permet de mixer du texte formaté et du texte non
formaté.
"""
def _fill_text(self, text, width, indent):
return ''.join([indent + line for line in text.splitlines(True)])
def _split_lines(self, text, width):
lines = ['']
for line in text.splitlines():
if line.startswith('>>>'):
lines.append(line)
lines.append('')
else:
lines[-1] += '\n' + line
lines = filter(None, lines)
texts = []
for line in lines:
if line.startswith('>>>'):
texts.append(line[3:])
else:
texts.extend(super(FancyHelpFormatter, self)._split_lines(line, width))
return texts
AP = ArgumentParser(
formatter_class=RawTextHelpFormatter,
usage=u"%(prog)s [options] [TIMEOUT]",
description=u"Afficher un chronomètre",
epilog=u"Si TIMEOUT est spécifié, par défaut le décompte démarre automatiquement."
epilog=u"Si TIMEOUT est spécifié, par défaut le décompte démarre automatiquement.",
formatter_class=FancyHelpFormatter,
)
AP.set_defaults(autostart=None, timeout=None)
AP.add_argument('timeout', metavar='TIMEOUT', nargs='?',
help=u"""\
(valeur vide)
chronomètre qui démarre à 0:00 et ne s'arrête pas
H:M:S (heures:minutes:secondes)
ou M:S (minutes:secondes)
ou M (minutes)
minuteur qui démarre à H:M:S et fait un décompte jusqu'à 0:00. A la fin
du décompte, une sonnerie retentit.
@H[:M[:S]]
minuteur qui fonctionne comme précédemment, sauf qu'on spécifie l'heure
d'arrivée, et que la durée est calculée automatiquement""")
>>> (valeur vide)
chronomètre qui démarre à 0:00 et ne s'arrête pas
>>> H:M:S (heures:minutes:secondes)
>>> ou M:S (minutes:secondes)
>>> ou M (minutes)
minuteur qui démarre à H:M:S et fait un décompte jusqu'à 0:00. A la fin
du décompte, une sonnerie retentit.
>>> @H[:M[:S]]
minuteur qui fonctionne comme précédemment, sauf qu'on spécifie l'heure
d'arrivée, et que la durée est calculée automatiquement""")
AP.add_argument('-n', '--no-autostart', dest='autostart', action='store_false',
help=u"Ne pas démarrer automatiquement le décompte même si TIMEOUT est spécifié.")
AP.add_argument('-s', '--autostart', dest='autostart', action='store_true',

57
fndate
View File

@ -60,9 +60,17 @@ OPTIONS
PLACEHOLDER vaut ~~. Si le nom spécifié ne contient pas le PLACEHOLDER,
il est placé au début.
-@, --force-date DATE
Dans le nom spécifié, si la date est déjà présente, forcer son
remplacement par la valeur spécifiée. Par défaut, le fichier n'est pas
modifié si la date est déjà présente.
Si une date du format attendu est déjà présente dans le nom du fichier,
forcer son remplacement par la valeur spécifiée. Par défaut, le fichier
n'est pas modifié si la date est déjà présente.
Si le premier ou le dernier argument de ce script ressemblent à une date
(ie. d/m[/y] ou @d[/m[/y]]) et qu'aucun fichier de ce nom n'existe dans
le répertoire courant, activer automatiquement cette option, sauf si
l'option --no-auto-force-date est spécifiée aussi. Ainsi, les commandes
suivantes sont équivalentes:
$scriptname -@ d/m/y file
$scriptname d/m/y file
$scriptname file @d/m/y
-F, --format FORMAT
Spécifier le format de la date à insérer dans le nom du fichier. Par
défaut, FORMAT vaut 'YYMMDD-'
@ -227,6 +235,7 @@ dmove) action=move;;
dcmd) action=cmd;;
esac
placeholder=
auto_force_date=1
force_date=
format=
autof=
@ -240,6 +249,7 @@ parse_opts "${PRETTYOPTS[@]}" \
-c,--cmd action=cmd \
--string action=string \
-P:,--placeholder: placeholder= \
--no-auto-force-date auto_force_date= \
-@:,--force-date: force_date= \
-F:,--format: format= \
-s,--short '$format=YYMMDD; autof=1' \
@ -247,6 +257,27 @@ parse_opts "${PRETTYOPTS[@]}" \
--autof autof=1 \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
if [ -z "$force_date" -a -n "$auto_force_date" ]; then
# si le premier ou le dernier argument ressemblent à une date
f="$1"; f2="${f#@}"
if [[ "$f" == */* ]] && [ -z "${f//[0-9\/]}" -a ! -e "$f" ]; then
force_date="$f"
shift
elif [[ "$f" == @* ]] && [ -z "${f2//[0-9\/]}" -a ! -e "$f2" ]; then
force_date="$f2"
shift
elif [ $# -gt 1 ]; then
l="${@:$#}"; l2="${l#@}"
if [[ "$l" == */* ]] && [ -z "${l//[0-9\/]}" -a ! -e "$l" ]; then
force_date="$l"
set -- "${@:1:$(($# - 1))}"
elif [[ "$l" == @* ]] && [ -z "${l2//[0-9\/]}" -a ! -e "$l2" ]; then
force_date="$l2"
set -- "${@:1:$(($# - 1))}"
fi
fi
fi
[ -n "$create" ] || create=file
[ -n "$placeholder" ] || placeholder="~~"
[ -n "$force_date" ] && setx force_date=parse_date "$force_date"
@ -329,14 +360,24 @@ elif [ "$action" == create ]; then
setx dir=dirname -- "$src"
setx srcname=basename -- "$src"
if [ -e "$src" ]; then
estep "Renommage de $(ppath "$src")"
setx destname=replace_ph "$srcname" "$force_date"
setx dest=abspath "$dir/$destname"
[ "$src" != "$dest" ] || ewarn "La source et la destination sont identiques"
read_value "Veuillez confirmer le nom" destname "$destname"
confirm_do=1
if [ "$src" == "$dest" ]; then
if [ -n "$force_date" ]; then
ewarn "La source et la destination sont identiques"
else
estepi "$(ppath "$src"): aucun renommage nécessaire"
confirm_do=
fi
fi
if [ -n "$confirm_do" ]; then
estep "Renommage de $(ppath "$src")"
read_value "Veuillez confirmer le nom" destname "$destname"
setx dest=abspath "$dir/$destname"
[ "$src" != "$dest" ] && mv -i "$src" "$dir/$destname"
setx dest=abspath "$dir/$destname"
[ "$src" != "$dest" ] && mv -i "$src" "$dir/$destname"
fi
else
setx destname=replace_ph "$srcname" "$force_date"
setx dest=abspath "$dir/$destname"

View File

@ -1,13 +1,21 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
function __nutools_host_completion() {
local cur prev
_get_comp_words_by_ref cur prev
if [ "$prev" == "-h" -o "$prev" == "-H" ]; then
local cur prev; _get_comp_words_by_ref cur prev
[ "$prev" == "-h" -o "$prev" == "-H" -o "$prev" == "--host" ] && _known_hosts_real "$cur"
return 0
}
function __nutools_whost_completion() {
local cur prev; _get_comp_words_by_ref cur prev
if [ "$prev" == "-h" -o "$prev" == "-H" -o "$prev" == "--host" ]; then
_known_hosts_real "$cur"
elif [ "$prev" == "-w" -o "$prev" == "--whost" ]; then
_known_hosts_real "$cur"
fi
return 0
}
complete -F __nutools_host_completion -o default uinst ruinst runs rruns
complete -F __nutools_host_completion -o default uinst ruinst runs rruns rwoinst rtoinst
complete -F __nutools_whost_completion -o default rwoinst
if __bash_completion_module_enabled ssh; then
shopt -u hostcomplete

9
lib/default/ufile Normal file
View File

@ -0,0 +1,9 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# Fichiers externes à inclure. Chacun de ces fichiers peut contenir des
# définitions de fonctions et de la variables RULES
INCLUDES=()
# Règles pour le classement des fichiers. Chaque règle est de la forme
# pattern:destdir[:renamef]
RULES=()

View File

@ -412,7 +412,7 @@ if test "\$keep" = n; then
fi
eval \$finish;
if test x"\$tmp_archive" = xy; then
exec /bin/rm -f "\$thisarch"
/bin/rm -f "\$thisarch"; exit \$res
else
exit \$res
fi

View File

@ -548,6 +548,19 @@ function ppath() {
recho "$path"
}
function ppath2() {
# Comme ppath() mais afficher '.' comme '../$dirname'
local path="$1" cwd="$2"
path="$(abspath "$path")" # essayer de normaliser le chemin
[ -n "$cwd" ] || cwd="$(pwd)"
[ "$path" = "$cwd" ] && path="../$(basename -- "$path")"
[ "$cwd" != "/" -a "$cwd" != "$HOME" ] && path="${path#$cwd/}"
[ "${path#$HOME/}" != "$path" ] && path="~${path#$HOME}"
recho "$path"
}
function relpath() {
# Afficher le chemin relatif de $1 par rapport à $2. Si $2 n'est pas spécifié,
# on prend le répertoire courant. Si $1 ou $2 ne sont pas des chemins absolus,

View File

@ -86,9 +86,10 @@ function __po_parse_optdescs() {
elif [[ "$optdesc_" == *:: ]]; then
option_="${optdesc_%::}"
if [[ "$2" == *=* ]]; then
# la valeur mentionnée est toujours ignorée
# la valeur mentionnée est toujours ignorée. le '=' à la
# fin est utilisé par __po_process_options() ci-dessous.
name_="${2%%=*}="
[ -n "$reset_" ] && eval "$name_="
[ -n "$reset_" ] && eval "$name_"
else
name_="$2"
[ -n "$reset_" ] && eval "$name_=()"
@ -97,9 +98,10 @@ function __po_parse_optdescs() {
elif [[ "$optdesc_" == *: ]]; then
option_="${optdesc_%:}"
if [[ "$2" == *=* ]]; then
# la valeur mentionnée est toujours ignorée
# la valeur mentionnée est toujours ignorée. le '=' à la
# fin est utilisé par __po_process_options() ci-dessous.
name_="${2%%=*}="
[ -n "$reset_" ] && eval "$name_="
[ -n "$reset_" ] && eval "$name_"
else
name_="$2"
[ -n "$reset_" ] && eval "$name_=()"
@ -108,7 +110,7 @@ function __po_parse_optdescs() {
else
option_="$optdesc_"
name_="$2"
[ -n "$reset_" ] && eval "$name_="
[ -n "$reset_" ] && eval "${2%%=*}="
flag_=
fi

View File

@ -107,7 +107,7 @@ function qvals() {
}
function qwc() {
# Dans la chaine $*, remplacer \ par \\, " par \", $ par \$, ` par \`, puis
# quoter la chaine avec ", sauf les wildcards * et ?
# quoter la chaine avec ", sauf les wildcards *, ? et [class]
# Cela permet de quoter une chaine permettant de glober des fichiers, e.g
# eval "ls $(qwc "$value")"
# Note: la protection de ! n'est pas effectuée, parce que le comportement du
@ -119,44 +119,45 @@ function qwc() {
s="${s//\"/\\\"}"
s="${s//\$/\\\$}"
s="${s//\`/\\\`}"
local r a b
local r a b c
while [ -n "$s" ]; do
if [[ "$s" == *\** ]]; then
if [[ "$s" == *\?* ]]; then
a="${s%%\**}"
b="${s%%\?*}"
if [ ${#a} -lt ${#b} ]; then
s="${s#*\*}"
r="$r\"$a\"*"
else
s="${s#*\?}"
r="$r\"$b\"?"
fi
else
a="${s%%\**}"
s="${s#*\*}"
r="$r\"$a\"*"
fi
elif [[ "$s" == *\?* ]]; then
if [[ "$s" == *\** ]]; then
a="${s%%\**}"
b="${s%%\?*}"
if [ ${#a} -lt ${#b} ]; then
s="${s#*\*}"
r="$r\"$a\"*"
else
s="${s#*\?}"
r="$r\"$b\"?"
fi
else
a="${s%%\?*}"
s="${s#*\?}"
r="$r\"$a\"?"
fi
else
a=; b=; c=
a=; [[ "$s" == *\** ]] && { a="${s%%\**}"; a=${#a}; }
b=; [[ "$s" == *\?* ]] && { b="${s%%\?*}"; b=${#b}; }
c=; [[ "$s" == *\[* ]] && { c="${s%%\[*}"; c=${#c}; }
if [ -z "$a" -a -z "$b" -a -z "$c" ]; then
r="$r\"$s\""
break
fi
if [ -n "$a" ]; then
[ -n "$b" ] && [ $a -lt $b ] && b=
[ -n "$c" ] && [ $a -lt $c ] && c=
fi
if [ -n "$b" ]; then
[ -n "$a" ] && [ $b -lt $a ] && a=
[ -n "$c" ] && [ $b -lt $c ] && c=
fi
if [ -n "$c" ]; then
[ -n "$a" ] && [ $c -lt $a ] && a=
[ -n "$b" ] && [ $c -lt $b ] && b=
fi
if [ -n "$a" ]; then # PREFIX*
a="${s%%\**}"
s="${s#*\*}"
[ -n "$a" ] && r="$r\"$a\""
r="$r*"
elif [ -n "$b" ]; then # PREFIX?
a="${s%%\?*}"
s="${s#*\?}"
[ -n "$a" ] && r="$r\"$a\""
r="$r?"
elif [ -n "$c" ]; then # PREFIX[class]
a="${s%%\[*}"
b="${s#*\[}"; b="${b%%\]*}"
s="${s:$((${#a} + ${#b} + 2))}"
[ -n "$a" ] && r="$r\"$a\""
r="$r[$b]"
fi
done
recho_ "$r"
}
@ -191,6 +192,22 @@ function echo_setv() {
fi
echo "$__s_var=$(qvalr "$*")"
}
function echo_setv2() {
# Afficher la commande qui recrée la variable $1
# Cette fonction est équivalente à echo_setv "$1=${!1}"
# Si d'autres arguments que le nom de la variable sont spécifiés, cette fonction
# se comporte comme echo_setv()
local __s_var="$1"; shift
if [[ "$__s_var" == *=* ]]; then
set -- "${__s_var#*=}" "$@"
__s_var="${__s_var%%=*}"
fi
if [ $# -eq 0 ]; then
echo_setv "$__s_var" "${!__s_var}"
else
echo_setv "$__s_var" "$@"
fi
}
function seta() {
# initialiser le tableau $1 avec les valeurs $2..@
# note: en principe, la syntaxe est 'seta array values...'. cependant, la
@ -219,8 +236,8 @@ function echo_seta() {
}
function echo_seta2() {
# Afficher la commande qui recrée le tableau $1
# Si des arguments sont spécifiés, cette fonction se comporte comme
# echo_seta()
# Si d'autres arguments que le nom de tableau sont spécifiés, cette fonction se
# comporte comme echo_seta()
local __s_var="$1"; shift
if [[ "$__s_var" == *=* ]]; then
set -- "${__s_var#*=}" "$@"

View File

@ -6,15 +6,44 @@
# API publique
function deploy_query() {
deploy_query_once "$@" && return 0
if [ -n "$QUERYVARS" ]; then
deploy_query_recursive "$@" && return 0
fi
return 1
}
function deploy_query_recursive() {
local DFILE
for DFILE in "${QUERYVARS[@]}"; do
(
deploy_loadconf --no-auto-update
deploy_query_once "$@" && exit 0
if [ -n "$QUERYVARS" ]; then
deploy_query_recursive "$@" && exit 0
fi
exit 1
) && return 0
done
return 1
}
function deploy_query_once() {
# Afficher le résultat d'une requête
local alv
eval "set -- $(getopt -ov: -lallow-vars: -- "$@")"
local alv sov sdv slv sof sdf slf
eval "set -- $(getopt -ov:odl -lallow-vars:,object-vars,dest-vars,link-vars,of:,object-func:,df:,dest-func:,lf:,link-func: -- "$@")"
while [ -n "$1" ]; do
case "$1" in
-v|--allow-vars)
array_split alv "$2" ,
shift; shift
;;
-o|--object-vars) sov=1; shift;;
-d|--dest-vars) sdv=1; shift;;
-l|--link-vars) slv=1; shift;;
--of|--object-func) sof="$2"; shift; shift;;
--df|--dest-func) sdf="$2"; shift; shift;;
--lf|--link-func) slf="$2"; shift; shift;;
--) shift; break;;
*) break;;
esac
@ -27,7 +56,7 @@ function deploy_query() {
DEST|SOURCE)
if [ -n "$otype" ]; then
deploy_is_type "$otype" || {
qvals false "$otype: invalid type"
[ "$format" == shell ] && qvals false "$otype: invalid type"
return 2
}
fi
@ -35,7 +64,7 @@ function deploy_query() {
*)
if [ -n "$qtype" ]; then
deploy_is_type "$qtype" || {
qvals false "$qtype: invalid type"
[ "$format" == shell ] && qvals false "$qtype: invalid type"
return 3
}
fi
@ -43,12 +72,12 @@ function deploy_query() {
esac
if [ -n "$ltype" ]; then
deploy_is_link "$ltype" || {
qvals false "$ltype: invalid link type"
[ "$format" == shell ] && qvals false "$ltype: invalid link type"
return 4
}
fi
[ -n "$term" ] || {
qvals false "search term required"
[ "$format" == shell ] && qvals false "search term required"
return 5
}
@ -57,23 +86,40 @@ function deploy_query() {
edebug "=== search type DEST"
edebug "term=$term, otype=$otype"
deploy_search_artifact object type "$term" "$otype" host || {
qvals false "artifact not found"
[ "$format" == shell ] && qvals false "artifact not found"
return 1
}
edebug "object=$object, type=$type"
deploy_search_link link "$type" "$ltype" || {
qvals false "link not found"
[ "$format" == shell ] && qvals false "link not found"
return 1
}
deploy_show_values object object "$format" shell alv
deploy_show_values type otype "$format" shell alv
deploy_show_values link ltype "$format" shell alv
deploy_show_links "$object" "$link" "$profile" "$format" "" alv
if [ -n "$sov" ]; then
deploy_copy_object_values vs "$object" "$type"
deploy_show_values vs "$type" "$format" shell alv
deploy_show_attrs "$object" "$type" "$format" alv vars "$sof"
fi
if [ -n "$sdv" ]; then
local -a pons pgns ons gns ans ovs os; local an av o
deploy_prepare_copy_link_attrs pons pgns ons gns "$object" "$link" "$profile"
deploy_copy_link_attr_values hvs host "$object" "$link" "$profile" pons pgns ons gns
for hv in "${hvs[@]}"; do
deploy_copy_object_values hs "$hv" host
deploy_show_values hs host "$format" shell alv
deploy_show_attrs "$hn" host "$format" alv vars "$sdf"
done
fi
deploy_show_links "$object" "$link" "$profile" "$format" "" alv vars "$slf"
[ "$format" == shell ] && echo true
return 0
;;
SOURCE)
# XXX implémenter
[ "$format" == shell ] && echo true
return 0
;;
*)
@ -83,9 +129,10 @@ function deploy_query() {
deploy_show_values type otype "$format" shell alv
deploy_show_values vs "$type" "$format" "" alv
deploy_show_attrs "$object" "$type" "$format" alv
[ "$format" == shell ] && echo true
return 0
fi
qvals false "artifact not found"
[ "$format" == shell ] && qvals false "artifact not found"
return 1
;;
esac
@ -224,10 +271,7 @@ function deploy_show_values() {
[ -n "$ff" -a "$f" != "$ff" ] && return
case "$f" in
shell)
[ -n "$5" ] && echo "$t=()"
deploy_dump_values "$t" vs
;;
shell) deploy_dump_values "$t" vs;;
line) array_to_lines vs;;
*) array_join vs "$f";;
esac
@ -244,16 +288,17 @@ function deploy_show_attrs() {
local -a ons gns ans vs; local an
deploy_prepare_copy_attrs ons gns "$o" "$t"
deploy_copy_attr_names ans "$o" "$t" ons gns
[ -n "$5" ] && echo_seta "${t}_vars" "${ans[@]}"
for an in "${ans[@]}"; do
if [ -n "$4" ]; then
if ! array_isempty "$4"; then
array_contains alv "$an" || continue
fi
[ "$f" == shell ] && echo "$an=()"
fi
deploy_copy_attr_values vs "$an" "$o" "$t" ons
deploy_dump_values "$an" vs
done
[ -n "$6" ] && echo "$6"
}
function deploy_show_links() {
@ -272,6 +317,7 @@ function deploy_show_links() {
deploy_prepare_copy_link_attrs pons pgns ons gns "$o" "$l" "$p"
deploy_copy_link_attr_names ans "$o" "$l" "$p" pons pgns ons gns
[ -n "$7" -a "$f" == shell ] && echo_seta "${l}_vars" "${ans[@]}"
for an in "${ans[@]}"; do
deploy_copy_link_attr_values avs "$an" "$o" "$l" "$p" pons pgns ons gns
if [ "$an" == host ]; then
@ -287,6 +333,7 @@ function deploy_show_links() {
deploy_show_values avs "$an" "$f" shell alv
fi
done
[ -n "$8" -a "$f" == shell ] && echo "$8"
done
}

View File

@ -456,11 +456,64 @@ function git_update() {
return $?
fi
local orig_branch restore_branch remote rbranch pbranch
local -a branches
local branch orig_branch restore_branch remote rbranch pbranch
local -a branches prbranches crbranches dbranches
array_from_lines prbranches "$(git_list_rbranches)"
git fetch -p "$@" || return
array_from_lines crbranches "$(git_list_rbranches)"
# vérifier s'il n'y a pas des branches distantes qui ont été supprimées
for branch in "${prbranches[@]}"; do
if ! array_contains crbranches "$branch"; then
array_add dbranches "${branch#*/}"
fi
done
if [ ${#dbranches[*]} -gt 0 ]; then
eimportant "One or more distant branches where deleted"
for branch in "${dbranches[@]}"; do
if git_have_branch "$branch"; then
if ! ask_yesno "Do you want to delete local branch $branch?" X; then
array_del dbranches "$branch"
fi
fi
done
fi
if [ ${#dbranches[*]} -gt 0 ]; then
array_from_lines branches "$(git_list_branches)"
branch="$(git_get_branch)"
if array_contains dbranches "$branch"; then
# si la branche courante est l'une des branches à supprimer, il faut
# basculer vers develop ou master
local swto
if [ -z "$swto" ] && array_contains branches develop && ! array_contains dbranches develop; then
swto=develop
fi
if [ -z "$swto" ] && array_contains branches master && ! array_contains dbranches master; then
swto=master
fi
if ! git_check_cleancheckout; then
echo "* There are uncommitted local changes. However current branch is slated for removal.
Make your verifications then delete the local branches:
${swto:+$(qvals git checkout "$swto")
}$(qvals git branch -D "${dbranches[@]}")"
return 1
fi
if [ -n "$swto" ]; then
git checkout -q "$swto"
else
echo "* Current branch is slated for removal but I don't know to which branch I should switch first.
Make your choice then delete the local branches:
$(qvals git branch -D "${dbranches[@]}")"
return 1
fi
fi
for branch in "${dbranches[@]}"; do
git branch -D "$branch"
done
fi
# intégrer les modifications dans les branches locales
if ! git_check_cleancheckout; then
branch="$(git_get_branch)"
remote="$(git_get_branch_remote "$branch")"

View File

@ -410,27 +410,52 @@ OPTIONS
PREFIX=value
Spécifier une valeur pour un préfixe, plutôt que de laisser uprefix
l'autodétecter. Utiliser uprefix -l pour une liste de préfixes valides.
-b Redémarrer les instances en mode bounce.
Par défaut, les instances sont arrêtées avant le déploiement, et
redémarrées après
-W Ne déployer que les resources web. Implique -n
-n Ne pas tagger les bundles déployés avec un numéro de version. Par
défaut, l'utilisateur est invité à compléter des informations telles
que n° de version et date de release si ces informations ne sont pas
--prefix
Corriger les chemins des variables qui commencent par des préfixes
valides (c'est la valeur par défaut). Utiliser 'uprefix -l' pour avoir
une liste de préfixes valides
--no-prefix
Ne jamais corriger un chemin.
-W, --webres
Ne déployer que les resources web. Implique -n
-n, --no-tag
Ne pas tagger les bundles déployés avec un numéro de version. En temps
normal, l'utilisateur est invité à compléter des informations telles que
n° de version et date de release si ces informations ne sont pas
disponible.
-x CMD
-d, --active-dbconfig DBCONFIG
Spécifier le profil de connexion à utiliser par défaut pour tous les
eomodels du bundle. S'il faut un profil différent en fonction de l'eomodel, utiliser l'option -m
-m, --active-dbconfig-map EOMODEL:DBCONFIG
Spécifier un mapping entre un nom d'eomodel et le profil de connexion à
utiliser. Pour les eomodels qui ne sont pas spécifiés, la valeur par
défaut est utilisée si elle existe. Il est possible de spécifier
plusieurs mappings en les séparant par des virgules.
--stop-start
Redémarrer les instances de la manière classique: les instances sont
arrêtées avant le déploiement, et redémarrées après. C'est la valeur par
défaut.
-b, --bounce
Redémarrer les instances en mode bounce.
-x, --exec CMD
Exécuter la commande CMD après avoir effectué le déploiement"
}
local bounce webinst notag scripts istmpdir tmpdir
scripts=()
local istmpdir tmpdir autoprefix=1 bounce webinst notag default_dbconfig
local -a dbconfigs scripts
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with __woinst_display_help' \
-b,--bounce bounce=1 \
-W,--webres webinst=1 \
-n,--notag notag=1 \
-x:,--exec: scripts \
--is-tmpdir '$istmpdir=1; tmpdir=.' \
--prefix autoprefix=1 \
--no-prefix autoprefix= \
-W,--webres webinst=1 \
--tag notag= \
-n,--no-tag notag=1 \
-d:,--active-dbconfig: default_dbconfig= \
-m:,--active-dbconfig-map: dbconfigs \
--stop-start bounce= \
-b,--bounce bounce=1 \
-x:,--exec: scripts \
@ args -- "$@" &&
set -- "${args[@]}" || {
eerror "$args"
@ -457,7 +482,8 @@ OPTIONS
for src in "${@:-.}"; do
if [[ "$src" == *=* ]]; then
splitvar "$src" name value
array_add variables "$name"
array_addu variables "$name"
[ -n "$autoprefix" ] && value="$(expand_prefix "$value")"
_setv "$name" "$value"
else
src="$(abspath "$src")"
@ -469,6 +495,19 @@ OPTIONS
copied=1
fi
if [ -d "$src" ]; then
setx name=basename -- "$src"
case "$src" in
*.woa|*.framework) ;;
*) # support projet maven
if [ ! -f "$src/pom.xml" ]; then
: # non, pas un projet maven
elif [ -d "$src/target/$name.woa" ]; then
src="$src/target/$name.woa"
elif [ -d "$src/target/$name.framework" ]; then
src="$src/target/$name.framework"
fi
;;
esac
if endswith "$src" .framework; then
is_wofwkdir "$src" || {
eerror "Framework invalide: $(ppath "$src")"
@ -504,6 +543,68 @@ OPTIONS
done
eend
# Corriger les eomodelds
array_fix_paths dbconfigs ,
if [ -n "$default_dbconfig" -o ${#dbconfigs[*]} -gt 0 ]; then
etitle "Configuration des eomodels"
local -a eonames eomodels tmpeomodels
local eom dbc eoname eomodel dbconfig found
estep "Calcul de la liste des eomodels"
if [ ${#dbconfigs[*]} -eq 0 ]; then
for src in "${frameworks[@]}"; do
array_from_lines tmpeomodels "$(find "$src" -type d -name "*.eomodeld")"
for eom in "${tmpeomodels[@]}"; do
array_addu eonames "$(basename -- "$eom")"
done
done
for src in "${applications[@]}"; do
array_from_lines tmpeomodels "$(find "$src" -type d -name "*.eomodeld")"
for eom in "${tmpeomodels[@]}"; do
array_addu eonames "$(basename -- "$eom")"
done
done
else
for dbc in "${dbconfigs[@]}"; do
splitpair "$dbc" eom dbc
[ "${eom%.eomodeld}" != "$eom" ] || eom="$eom.eomodeld"
array_addu eonames "$(basename -- "$eom")"
done
fi
for eom in "${eonames[@]}"; do
for src in "${frameworks[@]}"; do
array_from_lines tmpeomodels "$(find "$src" -type d -name "$eom")"
array_extendu eomodels tmpeomodels
done
for src in "${applications[@]}"; do
array_from_lines tmpeomodels "$(find "$src" -type d -name "$eom")"
array_extendu eomodels tmpeomodels
done
done
for eomodel in "${eomodels[@]}"; do
setx eoname=basename -- "$eomodel"
if [ ! -f "$eomodel/index.eomodeld" ]; then
ewarn "$eomodel: eomode invalide"
continue
fi
found=
for dbc in "${dbconfigs[@]}"; do
splitpair "$dbc" eom dbconfig
[ "${eom%.eomodeld}" != "$eom" ] || eom="$eom.eomodeld"
if [ "$(basename -- "$eom")" == "$eoname" ]; then
found="$dbconfig"
break
fi
done
[ -z "$found" -a -n "$default_dbconfig" ] && found="$default_dbconfig"
if [ -n "$found" ]; then
estep "$eomodel [$found]"
sed -i "/^ *activeDatabaseConfigName *=/s/=.*;/=\"$found\";/" "$eomodel/index.eomodeld"
fi
done
fi
# Tagger les bundles
if [ -z "$webinst" -a -z "$notag" ]; then
etitle "Tagger les bundles"

267
rtoinst Executable file
View File

@ -0,0 +1,267 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS pyulib/pyulib
function display_help() {
uecho "$scriptname: Déploiement distant avec toinst
USAGE
$scriptname [-h host] [-T tmproot] <archive|dir> [-- options de toinst]
note: à cause d'une limitation de makeself, les options de toinst ne devraient
pas contenir d'espaces ni de caractères spéciaux. L'échappement de ces
caractères n'est pas garanti.
OPTIONS
-T, --tmproot TMPROOT
Spécifier le répertoire temporaire sur l'hôte distant, comme par exemple
/var/tmp. Cette option est utile pour les vservers, qui ont par défaut
un /tmp minuscule de 16 Mo.
-S, --ssh SSH
Spécifier le programme à utiliser pour la connection par ssh.
-h, --host hosts
-h, --host @hostsfile
Spécifier un ou plusieurs hôtes sur lequels faire le déploiement. Pour
spécifier plusieurs hôtes, il est possible d'utiliser plusieurs fois
l'option -h, ou spécifier en une seule fois plusieurs hôtes en les
séparant par un espace ou le caractère ':', e.g. 'host1 host2' ou
'host1:host2'. Si la spécification contient les caractères { et },
l'expansion est effectuée, e.g
-h 'root@{host1,host2}.univ.run'
Par défaut, la connexion sur l'hôte distant se fait avec l'utilisateur
root. Il est possible de spécifier un autre utilisateur avec la syntaxe
user@host, e.g -h user@host
La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
hostsfile, à raison d'un hôte par ligne.
--deploy
--no-deploy
Autoriser (respectivement interdire) l'utilisation de la configuration
locale de déploiement pour identifier la destination si aucun hôte n'est
spécifié.
Par défaut, si aucun hôte n'est spécifié, la configuration locale de
déploiement est interrogée pour avoir cette information.
-c, --deploy-config CONFNAME
Spécifier le nom de la configuration à utiliser pour l'interrogation de
la configuration locale de déploiement. Par défaut, utiliser le nom
'rtoinst'
-p, --deploy-profile PROFILE
Spécifier le profil à utiliser pour l'interrogation de la configuration
locale de déploiement. Par défaut, aucun profil n'est sélectionné."
}
__PARSED_HOSTS=()
__PARSED_FILES=()
function parse_hostsfile() {
# Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste
# des hôtes mentionnés dans les fichiers.
local inputfile basedir inputs input
for inputfile in "$@"; do
inputfile="$(abspath "$inputfile")"
array_contains __PARSED_FILES "$inputfile" && {
ewarn "$(ppath "$inputfile"): inclusion récursive"
continue
}
array_add __PARSED_FILES "$inputfile"
basedir="$(dirname "$inputfile")"
array_from_lines inputs "$(<"$inputfile" filter_conf)" || {
ewarn "$inputfile: fichier ingnoré"
continue
}
for input in "${inputs[@]}"; do
if [ "${input#@}" != "$input" ]; then
# fichier inclus
parse_hostsfile "$(abspath "${input#@}" "$basedir")"
else
array_addu __PARSED_HOSTS "$input"
fi
done
done
}
function __expand_braces() {
if [[ "$1" == *{* ]] && [[ "$1" == *}* ]]; then
eval "echo $1"
else
echo "$1"
fi
}
function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; }
function fix_hosts() {
# Si hosts contient des éléments multiple, comme a:b, séparer ces
# éléments. i.e (a b:c) --> (a b c)
# Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes
# depuis un fichier.
# Remplacer aussi les '.' par 'localhost'
array_map hosts __expand_braces
array_fix_paths hosts ":"
array_fix_paths hosts " "
local -a _hosts _tmphosts host
for host in "${hosts[@]}"; do
host="${host%/}"
if [ "${host#@}" != "$host" ]; then
__PARSED_HOSTS=()
parse_hostsfile "${host#@}"
array_fix_paths __PARSED_HOSTS
array_extendu _hosts __PARSED_HOSTS
else
array_addu _hosts "$host"
fi
done
array_copy hosts _hosts
array_map hosts __dot_is_localhost
}
action=deploy
tmproot=
SSH=
hosts=()
deploy_enable=1
deploy_confname=rtoinst
deploy_profile=
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-T:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
-h:,-H:,--host: hosts \
--deploy deploy_enable=1 \
--no-deploy deploy_enable= \
-c:,--deploy-config: deploy_confname= \
-p:,--deploy-profile: deploy_profile= \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
: "${SSH:=ssh}"
## webapp à déployer et hôtes sur lesquels faire le déploiement
confirm_webapp=
if [ $# -eq 0 ] || [[ "$1" == -* ]]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de toinst)
[ -d WEB-INF ] && webapp=. || webapp=
confirm_webapp=1
else
webapp="$1"
fi
toinst_options=()
if array_isempty hosts && [ -n "$deploy_enable" -a -n "$webapp" ]; then
urequire deploy
deploy_setconf "$deploy_confname"
if deploy_loadconf; then
setxx waname=abspath "$webapp" // basename --
if eval "$(deploy_query -v host,wamap,protect DEST webapp rtoinst_deploy "$deploy_profile" shell "$waname")"; then
msg="$waname: cette webapp sera déployée vers les hôtes suivants:
$(array_to_lines host "" " ")"
if [ -n "$wamap" -o -n "$protect" ]; then
msg="$msg
avec le(s) paramètre(s) suivant(s):"
[ -n "$wamap" ] && msg="$msg
--wamap $(qval "$wamap")"
[ -n "$protect" ] && msg="$msg
--protect $(qval "$protect")"
fi
check_interaction -c && einfo "$msg"
ask_any "Voulez-vous continuer?" Oq || die
array_copy hosts host
[ -n "$wamap" ] && array_add toinst_options --wamap "$wamap"
[ -n "$protect" ] && array_add toinst_options --protect "$protect"
if [ -n "$confirm_webapp" ]; then
confirm_webapp=
set -- "$webapp" "$@"
fi
fi
fi
fi
if [ -n "$confirm_webapp" ]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de toinst)
read_value "Veuillez entrer la webapp à déployer" webapp "$webapp" O
set -- "$webapp" "$@"
fi
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fix_hosts
## Création de l'archive
etitle "Création du répertoire de travail"
ac_set_tmpdir workdir
webapps=()
while [ $# -gt 0 ]; do
if [ "$1" == "--" ]; then
# début des options de toinst
shift
break
elif [[ "$1" == -* ]]; then
# début des options de toinst
break
fi
osrc="$1"; shift
setx src=abspath "$osrc"
setx srcname=basename -- "$src"
if [ -f "$src" ] && is_archive "$src"; then
setx waname=get_archive_appname "$src"
estep "$(ppath "$src") --> $waname"
wadir="$workdir/$waname"
mkdir "$wadir"
extract_archive "$src" "$wadir" || die
src="$wadir"
elif [ -d "$src" ]; then
estep "$(ppath2 "$src")"
cp_R "$src" "$workdir/$srcname" || die
elif [ -e "$src" ]; then
die "$osrc: fichier invalide"
else
die "$osrc: fichier ou répertoire introuvable"
fi
array_add webapps "$srcname"
done
estep "Copie de l'environnement de déploiement"
mkdir "$workdir/lib"
ulibsync "$workdir/lib"
cp "$scriptdir/toinst" "$workdir"
chmod +x "$workdir/toinst"
eend
ac_set_tmpfile archive
archivename="$(basename "$archive")"
etitle "Création de l'archive pour le déploiement"
"$scriptdir/mkusfx" --bare --tmp-archive -o "$archive" "$workdir" -- ./toinst --is-tmpdir || die
eend
## Déploiement
# sur l'hôte distant, ne rendre non interactif qu'à partir de -yy
rinteraction=$__interaction
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
for host in "${hosts[@]}"; do
if [ "$host" == "localhost" ]; then
etitle "Déploiement sur l'hôte local"
"$archive" ${tmproot:+--tmproot "$tmproot"} -- "${webapps[@]}" "$@" "${toinst_options[@]}"
eend
else
splituserhost "$host" user host
[ -n "$user" ] || user=root
etitle "Déploiement sur $user@$host"
estep "Copie de l'archive"
scp -S "$SSH" "$archive" "$user@$host:" || die
estep "Lancement du script de déploiement"
"$SSH" -qt "$user@$host" "\
__interaction=$rinteraction
__estack=$(qval "$__estack")
__tlevel=$(qval "$__tlevel")
export __interaction __estack __tlevel
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- "${webapps[@]}" "$@" "${toinst_options[@]}")"
eend
fi
done

98
ruinst
View File

@ -56,37 +56,11 @@ OPTIONS
Par défaut, si aucun hôte n'est spécifié, la configuration locale de
déploiement est interrogée pour avoir cette information.
-c, --deploy-config CONFNAME
Cette option permet de spécifier le nom de la configuration à utiliser
pour effectuer la requête. Par défaut, utiliser le nom 'ruinst'"
Cette option permet de spécifier le nom de la configuration locale de
déploiement à utiliser pour effectuer la requête. Par défaut, utiliser
le nom 'ruinst'"
}
set_defaults pubkeys
action=deploy
confuser=
uses_su=
tmproot=
SSH=
force_make_archive=
hosts=()
deploy_enable=1
deploy_confname=ruinst
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-C,--configure-user action=configure \
--configure: '$set@ confuser;action=configure' \
--uses-su uses_su=1 \
-T:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
--force-make-archive force_make_archive=1 \
-h:,-H:,--host: hosts \
--deploy deploy_enable=1 \
--no-deploy deploy_enable= \
-c:,--deploy-config deploy_confname= \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
: "${SSH:=ssh}"
__PARSED_HOSTS=()
__PARSED_FILES=()
function parse_hostsfile() {
@ -150,6 +124,33 @@ function fix_hosts() {
array_map hosts __dot_is_localhost
}
set_defaults pubkeys
action=deploy
confuser=
uses_su=
tmproot=
SSH=
force_make_archive=
hosts=()
deploy_enable=1
deploy_confname=ruinst
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-C,--configure-user action=configure \
--configure: '$set@ confuser;action=configure' \
--uses-su uses_su=1 \
-T:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
--force-make-archive force_make_archive=1 \
-h:,-H:,--host: hosts \
--deploy deploy_enable=1 \
--no-deploy deploy_enable= \
-c:,--deploy-config deploy_confname= \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
: "${SSH:=ssh}"
################################################################################
# Configuration de l'accès par clé aux hôtes
@ -218,36 +219,43 @@ fi
################################################################################
# Déploiement
make_archive=
## Répertoire à déployer
src="$1"
if [ -z "$src" ] || [[ "$src" == -* ]]; then
## Répertoires à déployer et hôtes sur lesquels faire le déploiement
confirm_src=
if [ $# -eq 0 ] || [[ "$1" == -* ]] || [[ "$1" == *=* ]]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de uinst)
read_value "Entrez le répertoire à déployer" src "."
src=.
confirm_src=1
else
# Enlever le répertoire à déployer. Le reste est pour uinst
src="$1"
shift
fi
## Hôtes sur lesquels faire le déploiement
if array_isempty hosts && [ -n "$deploy_enable" ]; then
if array_isempty hosts && [ -n "$deploy_enable" -a -n "$src" ]; then
urequire deploy
deploy_setconf "$deploy_confname"
if deploy_loadconf; then
setxx module=abspath "$src" // basename --
if eval "$(deploy_query -v host DEST module ruinst_deploy "" shell "$module")"; then
check_interaction -c && einfo "Ce module sera déployé vers les hôtes suivants:
setxx srcname=abspath "$src" // basename --
if eval "$(deploy_query -v host DEST module ruinst_deploy "" shell "$srcname")"; then
check_interaction -c && einfo "$srcname: ce module sera déployé vers les hôtes suivants:
$(array_to_lines host "" " ")"
ask_any "Voulez-vous continuer?" Oq || die
array_copy hosts host
confirm_src=
fi
fi
fi
if [ -n "$confirm_src" ]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de uinst)
read_value "Entrez le répertoire à déployer" src "$src" O
fi
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fix_hosts
## Création de l'archive?
make_archive=
if [ -n "$force_make_archive" ]; then
make_archive=1
else
@ -265,13 +273,14 @@ if [ -n "$make_archive" ]; then
ac_set_tmpfile archive
archivename="$(basename "$archive")"
etitle "Création de l'archive pour le déploiement" \
"$scriptdir/mkusfx" --tmp-archive --self-contained -o "$archive" "$src" || die
etitle "Création de l'archive pour le déploiement"
"$scriptdir/mkusfx" --tmp-archive --self-contained -o "$archive" "$src" || die
eend
fi
## Déploiement
# sur l'hôte distant, ne rendre interactif qu'à partir de -yy
# sur l'hôte distant, ne rendre non interactif qu'à partir de -yy
rinteraction=$__interaction
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
@ -289,8 +298,10 @@ for host in "${hosts[@]}"; do
[ -n "$user" ] || user=root
etitle -s "Déploiement sur $user@$host"
estep "Copie de l'archive"
scp -S "$SSH" "$archive" "$user@$host:" || die
estep "Lancement du script de déploiement"
"$SSH" -qt "$user@$host" "\
__interaction=$rinteraction
@ -299,6 +310,7 @@ __tlevel=$(qval "$__tlevel")
export __interaction __estack __tlevel
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- MYHOST="$host" "$@")"
eend
fi
done

292
rwoinst
View File

@ -9,21 +9,11 @@ function display_help() {
USAGE
$scriptname [-H host] [-T tmproot] <file|archive|dir>... [-- options de woinst]
note: à cause d'une limitation de makeself, les options de toinst ne devraient
pas contenir d'espaces ni de caractères spéciaux. L'échappement de ces
caractères n'est pas garanti.
OPTIONS
-C, --configure-user
--configure USER [--uses-su]
Ne pas faire le déploiement. Configurer uniquement la connexion par clé
sur les hôtes distants spécifiés pour le user spécifié. Il faut pouvoir
se connecter par mot de passe pour configurer la connexion par clé.
Si l'on veut configurer la connexion par clé pour le user root, mais que
ce n'est pas possible de se connecter par mot de passe avec le user root
sur l'hôte distant, et qu'il existe un user sudoer sur l'hôte distant,
il est possible de faire la configuration avec '--configure root'. La
commande serait alors
$scriptname -H user@host --configure root
Si l'hôte distant n'a pas sudo ou si sudo n'est pas configuré, il faut
rajouter l'option --uses-su, e.g:
$scriptname -h user@host --configure root --uses-su
-T, --tmproot TMPROOT
Spécifier le répertoire temporaire sur l'hôte distant, comme par exemple
/var/tmp. Cette option est utile pour les vservers, qui ont par défaut
@ -32,7 +22,6 @@ OPTIONS
Spécifier le programme à utiliser pour la connection par ssh.
-h, --host hosts
-h, --host @hostsfile
-H host
Spécifier un ou plusieurs hôtes sur lequels faire le déploiement. Pour
spécifier plusieurs hôtes, il est possible d'utiliser plusieurs fois
l'option -h, ou spécifier en une seule fois plusieurs hôtes en les
@ -45,6 +34,17 @@ OPTIONS
user@host, e.g -h user@host
La forme @hostsfile permet de lire la liste des hôtes depuis le fichier
hostsfile, à raison d'un hôte par ligne.
-w, -W, --whost host[:htdocs]
Comme --host, mais pour le déploiement des resources web. Contrairement
à --host, un seul hôte peut être spécifié par occurence de l'option,
pour permettre de spécifier éventuellement le répertoire vers lequel
copier les resources web. Ainsi, les deux commandes suivantes sont
équivalentes:
rwoinst -h host.tld -- -W HTDOCSDIR=HTDOCSBASE/wo
rwoinst -w host.tld:HTDOCSBASE/wo
Il est possible de spécifier en une seule invocation --host et --whost
pour déployer l'application sur un hôte et les resources web sur un
autre.
--deploy
--no-deploy
Autoriser (respectivement interdire) l'utilisation de la configuration
@ -54,34 +54,12 @@ OPTIONS
déploiement est interrogée pour avoir cette information.
-c, --deploy-config CONFNAME
Cette option permet de spécifier le nom de la configuration à utiliser
pour effectuer la requête. Par défaut, utiliser le nom 'rwoinst'"
pour effectuer la requête. Par défaut, utiliser le nom 'rwoinst'
-p, --deploy-profile PROFILE
Spécifier le profil à utiliser pour l'interrogation de la configuration
locale de déploiement. Par défaut, aucun profil n'est sélectionné."
}
set_defaults pubkeys
action=deploy
confuser=
uses_su=
tmproot=
SSH=
hosts=()
deploy_enable=1
deploy_confname=rwoinst
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-C,--configure-user action=configure \
--configure: '$set@ confuser;action=configure' \
--uses-su uses_su=1 \
-T:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
-h:,-H:,--host: hosts \
--deploy deploy_enable=1 \
--no-deploy deploy_enable= \
-c:,--deploy-config deploy_confname= \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
SSH="${SSH:-ssh}"
__PARSED_HOSTS=()
__PARSED_FILES=()
function parse_hostsfile() {
@ -145,47 +123,149 @@ function fix_hosts() {
array_map hosts __dot_is_localhost
}
################################################################################
# Configuration de l'accès par clé aux hôtes
function deploy_to() {
local archive="$1" host="$2" tmproot="$3"
shift; shift; shift
if [ "$action" == "configure" ]; then
args=(${confuser:+--configure "$confuser"} ${uses_su:+--uses-su} -S "$SSH")
for host in "${hosts[@]}"; do
args=("${args[@]}" -H "$host")
done
exec "$scriptdir/ruinst" -C "${args[@]}"
local r=
if [ "$host" == "localhost" ]; then
etitle "Sur l'hôte local"
"$archive" ${tmproot:+--tmproot "$tmproot"} -- "$@" || r=1
eend
else
local user
local archivename="$(basename "$archive")"
splituserhost "$host" user host
[ -n "$user" ] || user=root
# sur l'hôte distant, ne rendre non interactif qu'à partir de -yy
rinteraction=$__interaction
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
etitle "Vers $user@$host"
estep "Copie de l'archive"
scp -S "$SSH" "$archive" "$user@$host:" || r=1
if [ -z "$r" ]; then
estep "Lancement du script de déploiement"
"$SSH" -qt "$user@$host" "\
__interaction=$rinteraction
__estack=$(qval "$__estack")
__tlevel=$(qval "$__tlevel")
export __interaction __estack __tlevel
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- MYHOST="$host" "$@")" || r=1
fi
eend
fi
return ${r:-0}
}
action=deploy
tmproot=
SSH=
hosts=()
whosts=()
deploy_enable=1
deploy_confname=rwoinst
deploy_profile=
parse_opts "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
-T:,--tmproot: tmproot= \
-S:,--ssh: SSH= \
-h:,-H:,--host: hosts \
-w:,-W:,--whost: whosts \
--deploy deploy_enable=1 \
--no-deploy deploy_enable= \
-c:,--deploy-config deploy_confname= \
-p:,--deploy-profile: deploy_profile= \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
: "${SSH:=ssh}"
## Bundle à déployer et hôtes sur lesquels faire le déploiement
confirm_bundle=
if [ $# -eq 0 ] || [[ "$1" == -* ]] || [[ "$1" == *=* ]]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de woinst)
case "$(basename -- "$(pwd)")" in
*.woa|*.framework) bundle=.;;
*) bundle=;;
esac
confirm_bundle=1
else
bundle="$1"
fi
################################################################################
# Déploiement
## Bundle à déployer
if [ -z "$1" ] || [[ "$1" == -* ]] || [[ "$1" == *=* ]]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de
# woinst)
die "Vous devez spécifier le bundle à déployer"
fi
## Hôtes sur lesquels faire le déploiement
if array_isempty hosts && [ -n "$deploy_enable" ]; then
woinst_options=()
if array_isempty hosts && [ -n "$deploy_enable" -a -n "$bundle" ]; then
urequire deploy
deploy_setconf "$deploy_confname"
if deploy_loadconf; then
setxx wobundle=abspath "$1" // basename --
if eval "$(deploy_query -v host DEST wobundle rwoinst_bundle "" shell "$wobundle")"; then
check_interaction -c && einfo "Ce bundle sera déployé vers les hôtes suivants:
$(array_to_lines host "" " ")"
setxx bundlename=abspath "$bundle" // basename --
host=(); bounce=; notag=; exec=; dbconfig=; dbconfigmap=
if eval "$(deploy_query -v host,bounce,notag,exec,dbconfig,dbconfigmap DEST wobundle rwoinst_bundle "$deploy_profile" shell "$bundlename")"; then
array_copy deploy_hosts host
deploy_options=()
[ -n "$bounce" ] && array_add deploy_options -b
[ -n "$notag" ] && array_add deploy_options -n
if [ -n "$exec" ]; then
for x in "${exec[@]}"; do
array_add deploy_options -x "$x"
done
fi
[ -n "$dbconfig" ] && array_add deploy_options -d "$dbconfig"
if [ -n "$dbconfigmap" ]; then
for m in "${dbconfigmap[@]}"; do
array_add deploy_options -m "$m"
done
fi
deploy_whosts=()
host=(); destdir=
if eval "$(deploy_query -v host,destdir DEST wobundle rwoinst_webres "$deploy_profile" shell "$bundlename")"; then
for wh in "${host[@]}"; do
array_add deploy_whosts "$wh:$destdir"
done
fi
msg="$bundlename: ce bundle sera déployé vers les hôtes suivants:
$(array_to_lines deploy_hosts "" " ")"
if [ ${#deploy_whosts[*]} -gt 0 -o ${#deploy_options[*]} -gt 0 ]; then
msg="$msg
avec le(s) paramètre(s) suivant(s):"
[ ${#deploy_whosts[*]} -gt 0 ] && msg="$msg
$(array_to_lines deploy_whosts "" " --whost ")"
[ ${#deploy_options[*]} -gt 0 ] && msg="$msg
$(qvals "${deploy_options[@]}")"
fi
check_interaction -c && einfo "$msg"
ask_any "Voulez-vous continuer?" Oq || die
array_copy hosts host
array_extend hosts deploy_hosts
array_extend woinst_options deploy_options
array_extend whosts deploy_whosts
if [ -n "$confirm_bundle" ]; then
confirm_bundle=
set -- "$bundle" "$@"
fi
fi
fi
fi
if [ -n "$confirm_bundle" ]; then
# pas d'argument, ou c'est une option (qui fait donc partie des arguments de woinst)
read_value "Veuillez entrer la bundle à déployer" bundle "$bundle" O
set -- "$bundle" "$@"
fi
array_isempty hosts && read_value "Entrez une liste d'hôtes séparés par ':'" hosts "localhost"
fix_hosts
## Création de l'archive
etitle -s "Création du répertoire de travail"
etitle "Création du répertoire de travail"
ac_set_tmpdir workdir
bundles=()
while [ -n "$1" ]; do
if [ "$1" == "--" ]; then
@ -203,6 +283,19 @@ while [ -n "$1" ]; do
cp_R "$src" "$workdir" || die
elif [ -d "$src" ]; then
src="$(abspath "$src")"
setx srcname=basename -- "$src"
case "$src" in
*.woa|*.framework) ;;
*) # support projet maven
if [ ! -f "$src/pom.xml" ]; then
: # non, pas un projet maven
elif [ -d "$src/target/$srcname.woa" ]; then
src="$src/target/$srcname.woa"
elif [ -d "$src/target/$srcname.framework" ]; then
src="$src/target/$srcname.framework"
fi
;;
esac
if endswith "$src" .framework; then
is_wofwkdir "$src" || die "Framework invalide: $(ppath "$src")"
elif endswith "$src" .woa; then
@ -210,7 +303,7 @@ while [ -n "$1" ]; do
else
die "N'est pas un bundle valide: $(ppath "$src")"
fi
estep "$(ppath "$src")"
estep "$(ppath2 "$src")"
cp_R "$src" "$workdir" || die
else
die "Fichier ou répertoire introuvable: $src"
@ -221,55 +314,34 @@ while [ -n "$1" ]; do
done
estep "Copie de l'environnement de déploiement"
ulibsync "$workdir"
echo '#!/bin/sh
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
if . `dirname "$0"`/ulib/ulibsh; then
urequire DEFAULTS woinst
else
exit 1
fi
OENC="$UTF8"
woinst "$@"' >"$workdir/woinst"
chmod +x "$workdir/woinst"
mkdir "$workdir/lib"
ulibsync "$workdir/lib"
cp "$scriptdir/woinst.sh" "$workdir"
chmod +x "$workdir/woinst.sh"
eend
etitle "Création de l'archive pour le déploiement"
ac_set_tmpfile archive
archivename="$(basename "$archive")"
etitle "Création de l'archive pour le déploiement" \
"$scriptdir/mkusfx" --bare --tmp-archive -o "$archive" "$workdir" -- ./woinst --is-tmpdir "${bundles[@]}" || die
"$scriptdir/mkusfx" --bare --tmp-archive -o "$archive" "$workdir" -- ./woinst.sh --is-tmpdir "${bundles[@]}" || die
eend
## Déploiement
# sur l'hôte distant, ne rendre interactif qu'à partir de -yy
rinteraction=$__interaction
[ $rinteraction -lt 2 ] && rinteraction=$(($rinteraction + 1))
etitle "Déploiement des bundles"
for host in "${hosts[@]}"; do
if [ "$host" == "localhost" ]; then
etitle "Déploiement sur l'hôte local" \
"$archive" ${tmproot:+--tmproot "$tmproot"} -- "$@"
else
if [[ "$host" == *@* ]]; then
user="${host%%@*}"
host="${host#*@}"
else
user=root
fi
etitle -s "Déploiement sur $user@$host"
estep "Copie de l'archive"
scp -S "$SSH" "$archive" "$user@$host:" || die
estep "Lancement du script de déploiement"
"$SSH" -qt "$user@$host" "\
__interaction=$rinteraction
__estack=$(qval "$__estack")
__tlevel=$(qval "$__tlevel")
export __interaction __estack __tlevel
${UTOOLS_LANG:+UTOOLS_LANG='$UTOOLS_LANG'; export UTOOLS_LANG
}$(qvals "./$archivename" ${tmproot:+--tmproot "$tmproot"} -- MYHOST="$host" "$@")"
eend
fi
deploy_to "$archive" "$host" "$tmproot" "$@" "${woinst_options[@]}" || die
done
eend
if [ ${#whosts[*]} -gt 0 ]; then
etitle "Déploiement des resources web"
for hostdestdir in "${whosts[@]}"; do
splitpair "$hostdestdir" host destdir
deploy_to "$archive" "$host" "$tmproot" \
-y -W ${destdir:+HTDOCSDIR="$destdir"} "$@" "${woinst_options[@]}" || die
done
eend
fi

171
tailor.py Executable file
View File

@ -0,0 +1,171 @@
#!/usr/bin/env python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
u"""Afficher les lignes d'un fichier en mettant en surbrillance certains patterns"""
import sys, subprocess, re
from collections import OrderedDict
def bluef(line, *ignored): return '\x1B[34m%s\x1B[0m' % line
def greenf(line, *ignored): return '\x1B[32m%s\x1B[0m' % line
def yellowf(line, *ignored): return '\x1B[33m%s\x1B[0m' % line
def redf(line, *ignored): return '\x1B[31m%s\x1B[0m' % line
def nonef(line, *ignored): return re.sub('\x1B\[.*?m', '', line)
DEFAULT_PATTERNS = OrderedDict([
(r'(?i)error', redf),
(r'(?i)warn(ing)?', yellowf),
(r'(?i)info', bluef),
(None, nonef),
])
FORMATS = OrderedDict([
('blue', bluef),
('green', greenf),
('yellow', yellowf),
('red', redf),
('none', nonef),
])
FORMAT_ALIASES = {
'b': 'blue',
'g': 'green',
'y': 'yellow',
'r': 'red',
'': 'none',
}
def strip_nl(s):
if s is None: return None
elif s.endswith("\r\n"): s = s[:-2]
elif s.endswith("\n"): s = s[:-1]
elif s.endswith("\r"): s = s[:-1]
return s
def run_tailor(inputfile=None, follow=False, patterns=None):
if inputfile is None or not follow:
if inputfile is None:
inf = sys.stdin
close = False
else:
inf = open(inputfile, 'rb')
close = True
def next_line():
try:
while True:
try: line = inf.readline()
except: break
if line == '': break
yield line
finally:
if close: inf.close()
else:
def next_line():
p = subprocess.Popen(
['tail', '-f', inputfile],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
try: line = p.stdout.readline()
except: break
if line == '': break
yield line
if patterns is None: patterns = DEFAULT_PATTERNS
for line in next_line():
line = nonef(strip_nl(line))
func = False
mo = None
for p, f in patterns.items():
if p is None:
func = f
mo = None
break
mo = re.search(p, line)
if mo is not None:
func = f
break
if func is not False:
line = func(line, mo)
sys.stdout.write(line)
sys.stdout.write('\n')
if __name__ == '__main__':
from argparse import ArgumentParser, HelpFormatter
if sys.argv[1:2] == ['--compat']:
# Avec l'argument --compat, désactiver la classe FancyHelpFormatter qui
# se base sur une API non documentée
sys.argv = sys.argv[0:1] + sys.argv[2:]
FancyHelpFormatter = HelpFormatter
else:
class FancyHelpFormatter(HelpFormatter):
"""Comme HelpFormatter, mais ne touche pas aux lignes qui commencent par les
caractères '>>>'. Cela permet de mixer du texte formaté et du texte non
formaté.
"""
def _fill_text(self, text, width, indent):
return ''.join([indent + line for line in text.splitlines(True)])
def _split_lines(self, text, width):
lines = ['']
for line in text.splitlines():
if line.startswith('>>>'):
lines.append(line)
lines.append('')
else:
lines[-1] += '\n' + line
lines = filter(None, lines)
texts = []
for line in lines:
if line.startswith('>>>'):
texts.append(line[3:])
else:
texts.extend(super(FancyHelpFormatter, self)._split_lines(line, width))
return texts
AP = ArgumentParser(
usage=u"%(prog)s [-f] [INPUTFILE]",
description=__doc__,
formatter_class=FancyHelpFormatter,
)
AP.set_defaults(inputfile=None, follow=False, patterns=None, defaults=True)
pattern_help = u"""\
Ajouter une spécification de pattern et le format dans lequel il doit être affiché.
Le format par défaut est red. Les formats valides sont:
>>> %(formats)s""" % {
'formats': ', '.join(FORMATS.keys()),
}
AP.add_argument('-e', '--pattern', action='append', dest='patterns', metavar='PATTERN:FORMAT',
help=pattern_help)
default_patterns = [u"%s:%s" % (p or '', f.__name__[:-1]) for (p, f) in DEFAULT_PATTERNS.items()]
no_defaults_help = u"""\
Ne pas ajouter les patterns par défaut. Sans cette option, les patterns par défaut sont:
%(default_patterns)s""" % {
'default_patterns': '\n'.join([u">>> %s" % pattern for pattern in default_patterns]),
}
AP.add_argument('-z', '--no-defaults', action='store_false', dest='defaults',
help=no_defaults_help)
AP.add_argument('-f', '--follow', action='store_true', dest='follow',
help=u"Suivre le contenu du fichier spécifié")
AP.add_argument('inputfile', metavar='INPUTFILE', nargs='?',
help=u"Fichier qu'il faut afficher ou dont il faut suivre le contenu")
o = AP.parse_args()
if o.patterns is None:
patterns = DEFAULT_PATTERNS
else:
patterns = OrderedDict()
for pf in o.patterns:
mo = re.match('(.*):([a-zA-Z]*)$', pf)
if mo is not None:
p = mo.group(1)
of = mo.group(2)
else:
p = pf
of = 'red'
if p == '': p = None
f = of.lower()
f = FORMAT_ALIASES.get(f, f)
if f not in FORMATS:
raise ValueError("%s: format invalide" % of)
patterns[p] = FORMATS[f]
if o.defaults:
for p, f in DEFAULT_PATTERNS.items():
patterns.setdefault(p, f)
run_tailor(o.inputfile, o.follow, patterns)

663
toinst Executable file
View File

@ -0,0 +1,663 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS xmlsupport
function display_help() {
uecho "$scriptname: Déployer une ou plusieurs webapps vers Tomcat
USAGE
$scriptname [options] <archive|dir>...
OPTIONS
-c, --config CONFIG
Spécifier un fichier de configuration qui contient les informations
spécifiées par les options ci-dessous: TOMCAT_PROFILE, CATALINA_BASE,
TOMCAT_USER, TOMCAT_GROUP, TOMCAT_VERSION, MANAGER_URL, MANAGER_USER,
MANAGER_PASSWORD, WAMAP, BACKUP, RESTART
-C, --config-profile TOMCAT_PROFILE
Spécifier le profil de configuration à utiliser. Cette valeur détermine
la façon dont sont calculés CATALINA_BASE, TOMCAT_USER, TOMCAT_GROUP et
TOMCAT_VERSION. Les valeurs valides sont:
* debian:tomcatV
Tomcat installé par 'apt-get install tomcatV'. L'alias 'd:V' est
aussi supporté, e.g 'debian:tomcat7' ou 'd:8'
* runs:NAME
Tomcat installé par 'runs tomcat/V name=NAME'. Le suffixe NAME est
le nom de base des fichiers de tomcat. Pour spécifier le cas échéant
la version de Tomcat, il faut utiliser l'option -V ci-dessous.
L'alias NAME sans le préfixe runs: est supporté aussi.
Le profil par défaut est calculé comme suit:
* Si un répertoire de la forme /var/lib/tomcatV existe alors il s'agit
de debian:tomcatV.
* Si le lien /opt/tomcat --> /opt/apache-tomcat-V existe, vérifier
l'existence du script /etc/init.d/tomcatV. Si le script existe, alors
utiliser runs:tomcatV comme profil. Sinon, utiliser runs:tomcat.
* Sinon, aucun profil n'est sélectionné, et il faut spécifier
manuellement les options -d, -U, -G, -V
-d, --catalina-base CATALINA_BASE
Spécifier le répertoire de base des configurations de Tomcat. La valeur
par défaut est celle de CATALINA_HOME. Cette option n'a normalement pas
besoin d'être spécifiée.
-U, --tomcat-user TOMCAT_USER
-G, --tomcat-group TOMCAT_GROUP
Spécifier l'utilisateur et le groupe propriétaires des fichiers de
Tomcat. Ces options n'ont normalement pas besoin d'être spécifiées.
-V, --tomcat-version TOMCAT_VERSION
Spécifier la version de tomcat installée, e.g 8.0.37 pour une version
complète ou simplement 8 si on ne donne que la version majeure.
Cette option peut s'avérer nécessaire avec le profil runs:tomcat bien
que les meilleurs efforts soient faits pour détecter la valeur
effective.
-g, --manager-url MANAGER_URL
Spécifier l'url du manager, e.g http://localhost:8080/manager/text
Par défaut, le fichier server.xml est consulté pour connaitre sur quel
port écoute le serveur.
-u, --user MANAGER_USER
-p, --password MANAGER_PASSWORD
Spécifier le compte et le mot de passe à utiliser pour piloter le
manager Tomcat, notamment pour redémarrer l'application venant d'être
déployée. Ce compte doit avoir le rôle manager-script à partir de Tomcat
6.0.30, et le rôle manager-gui dans les version antérieurs. Par défaut,
le fichier tomcat-users.xml est consulté pour avoir cette information.
-m, --wamap WAMAPS
Ajouter un ou plusieurs mappings de la forme src:dest. Avec ce mapping,
la webapp src est déployée avec le nom dest. Plusieurs mappings peuvent
être spécifiés en les séparant par des virgules. Si src est mentionné
plusieurs fois, e.g src:dest1,src:dest2 alors la webapp src est déployée
plusieurs fois avec des noms distincts.
--exclude EXCLUDES
--replace-excludes EXCLUDES
Spécifier un ensemble de fichier à exclure de la source lors du
déploiement. La variante --replace-excludes permet de construire la
liste des fichiers à exclure depuis zéro. La liste par défaut est:
${DEFAULT_EXCLUDES[*]}
--protect PROTECTS
Spécifier un ensemble de fichier à protéger dans la destination: ils ne
sont jamais écrasés par les fichiers sources. Cela permet de gérer des
applications qui écrivent des fichiers dans leur répertoire.
--rsync-option RSYNC_OPTIONS
--replace-rsync-options RSYNC_OPTIONS
Spécifier les options de rsync à utiliser pour le déploiement. La
variante --replace-rsync-options permet de construire la liste des
options depuis zéro. La liste par défaut est:
${DEFAULT_RSYNC_OPTIONS[*]}
-b, --backup
--no-backup
Avant le déploiement du service numérique, faire (resp. ne pas faire)
une sauvegarde des fichiers actuellement déployés.
-B, --backup-only
Ne pas faire le déploiement. Faire uniquement la sauvegarde.
-n, --no-restart
Après le déploiement de la webapp, ne pas redémarrer l'application.
-r, --restart-only
Ne pas faire le déploiement. Redémarrer uniquement l'application.
--fake
Afficher simplement ce qui serait fait. Ne pas le faire réellement.
--config-template
Créer un modèle de configuration dans le répertoire des webapp
spécifiées. Si aucune webapp n'est spécifiée, créer le fichier dans le
répertoire courant."
}
DEFAULT_EXCLUDES=(/.git/ /.toinst.conf)
DEFAULT_RSYNC_OPTIONS=(-rptL --delete)
VARS=(
TOMCAT_PROFILE
CATALINA_BASE TOMCAT_USER TOMCAT_GROUP TOMCAT_VERSION
MANAGER_URL MANAGER_USER MANAGER_PASSWORD
BACKUP RESTART
)
ARRAYS=(
WAMAPS
EXCLUDES REPLACE_EXCLUDES
PROTECTS
RSYNC_OPTIONS REPLACE_RSYNC_OPTIONS
)
function __reset_vars() {
local var
for var in "${VARS[@]}"; do
eval "$var="
done
for var in "${ARRAYS[@]}"; do
eval "$var=()"
done
}
function __dump_vars() {
local var
for var in "${VARS[@]}"; do
eecho "$(echo_setv2 "$var")"
done
for var in "${ARRAYS[@]}"; do
eecho "$(echo_seta2 "$var")"
done
}
function use_manager_script() {
local v="$TOMCAT_VERSION"
if [ -z "$v" ]; then
return 0
elif [[ "$v" == 6.0.* ]]; then
if [ "${v#6.0.}" -ge 30 ]; then
return 0
else
return 1
fi
elif [ "${v%%.*}" -gt 6 ]; then
return 0
else
return 1
fi
}
function parse_server() {
local -a lines
local serverxml port
serverxml="$CATALINA_BASE/conf/server.xml"
array_from_lines lines "$(xpathtool -g "/Server/Service[@name='Catalina']/Connector/@port" "$serverxml")"
port="${lines[0]}"
if [ -n "$port" ]; then
if use_manager_script; then
MANAGER_URL="http://localhost:$port/manager/text"
else
MANAGER_URL="http://localhost:$port/manager/html"
fi
fi
}
function parse_tomcat_users() {
local -a lines
local usersxml role
usersxml="$CATALINA_BASE/conf/tomcat-users.xml"
if use_manager_script; then
role=manager-script
else
role=manager-gui
fi
array_from_lines lines "$(xpathtool -g "/tomcat-users/user[contains(@roles,'$role')]/@username" "$usersxml")"
MANAGER_USER="${lines[0]}"
if [ -n "$MANAGER_USER" ]; then
array_from_lines lines "$(xpathtool -g "/tomcat-users/user[@username='$MANAGER_USER']/@password" "$usersxml")"
MANAGER_PASSWORD="${lines[0]}"
fi
}
function build_rsync_options() {
local -a excludes protects; local exclude protect
if [ ${#REPLACE_EXCLUDES[*]} -gt 0 ]; then
array_copy excludes REPLACE_EXCLUDES
else
array_copy excludes DEFAULT_EXCLUDES
fi
array_extend excludes EXCLUDES
array_copy protects PROTECTS
if [ ${#REPLACE_RSYNC_OPTIONS[*]} -gt 0 ]; then
array_copy rsync_options REPLACE_RSYNC_OPTIONS
else
array_copy rsync_options DEFAULT_RSYNC_OPTIONS
fi
array_extend rsync_options RSYNC_OPTIONS
local toinstconf="$1/.toinst.conf"
if [ -f "$toinstconf" ]; then
eval "$(
EXCLUDES=()
PROTECTS=()
RSYNC_OPTIONS=()
source "$toinstconf"
array_extend excludes EXCLUDES
array_extend protects PROTECTS
array_extend rsync_options RSYNC_OPTIONS
echo_seta2 excludes
echo_seta2 protects
echo_seta2 rsync_options
)"
fi
for exclude in "${excludes[@]}"; do
array_add rsync_options --exclude "$exclude"
done
for protect in "${PROTECTS[@]}"; do
array_add rsync_options -f "P $protect"
done
}
args=(%
--help '$exit_with display_help'
--config-template action=config-template
--private-parse-server '$action=parse; parse_server=1'
--private-parse-tomcat-users '$action=parse; parse_tomcat_users=1'
--is-tmpdir '$istmpdir=1; tmpdir=.'
-c:,--config: config=
-C:,--config-profile: tomcat_profile=
-d:,--catalina-base:,--catalina-home: catalina_base=
-U:,--tomcat-user: tomcat_user=
-G:,--tomcat-group: tomcat_group=
-V:,--tomcat-version: tomcat_version=
-g:,--manager-url: manager_url=
-u:,--user: manager_user=
-p:,--password: manager_password=
-m:,--wamap: wamaps
--exclude: excludes
--replace-excludes: replace_excludes
--protect: protects
--rsync-option: rsync_options
--replace-rsync-options: replace_rsync_options
-b,--backup backup=1
--no-backup backup=0
-B,--backup-only action=backup
--restart restart=1
-n,--no-restart restart=0
-r,--restart-only action=restart
--fake fake=1
)
parse_args "$@"; set -- "${args[@]}"
[ -n "$action" ] || action=deploy
if [ "$action" == parse ]; then
CATALINA_BASE="$1"
TOMCAT_VERSION="$2"
if [ -n "$parse_server" ]; then
parse_server
echo_setv2 MANAGER_URL
fi
if [ -n "$parse_tomcat_users" ]; then
parse_tomcat_users
echo_setv2 MANAGER_USER
echo_setv2 MANAGER_PASSWORD
fi
exit 0
fi
# Charger la configuration
__reset_vars
if [ -n "$config" ]; then
eval "$(
__reset_vars
CATALINA_HOME=
source "$config" 2>/dev/null || exit 1
[ -n "$CATALINA_BASE" ] || CATALINA_BASE="$CATALINA_HOME"
__dump_vars
)"
fi
# Traiter la ligne de commande
for var in "${VARS[@]}"; do
lvar="${var,,}"
if [ -n "${!lvar}" ]; then
setv "$var" "${!lvar}"
fi
done
[ -n "$RESTART" ] || RESTART=1 # valeur par défaut
normyesvals BACKUP RESTART
for var in "${ARRAYS[@]}"; do
lvar="${var,,}"
if ! array_isempty "$lvar"; then
array_copy "$var" "$lvar"
fi
done
array_fix_paths WAMAPS ,
# Calculer le profil de connexion
if [ -z "$TOMCAT_PROFILE" ]; then
for v in 8 7; do
if [ -d /var/lib/tomcat$v ]; then
TOMCAT_PROFILE=debian:tomcat$v
break
fi
done
fi
if [ -z "$TOMCAT_PROFILE" ]; then
v=
if [ -L /opt/tomcat ]; then
setx v=readlink /opt/tomcat
setx v=basename -- "$v"
else
array_lsdirs ts /opt "apache-tomcat-*"
if [ ${#ts[*]} -gt 0 ]; then
[ -n "$CATALINA_BASE" ] || CATALINA_BASE="${ts[0]}"
v="$CATALINA_BASE"
[ -L "$v" ] && setx v=readlink "$v"
setx v=basename -- "$v"
fi
fi
if [ -n "$v" ] && [[ "$v" == apache-tomcat-* ]]; then
v=${v#apache-tomcat-}
mv=${v%%.*}
if [ -f /etc/init.d/tomcat$mv ]; then
TOMCAT_PROFILE=runs:tomcat$mv
else
TOMCAT_PROFILE=runs:tomcat
fi
[ -z "$TOMCAT_VERSION" ] && TOMCAT_VERSION="$v"
fi
fi
if [ -n "$TOMCAT_PROFILE" ]; then
case "$TOMCAT_PROFILE" in
debian:*|runs:*) ;;
d:*) TOMCAT_PROFILE="debian:tomcat${TOMCAT_PROFILE#d:}";;
*) TOMCAT_PROFILE="runs:$TOMCAT_PROFILE";;
esac
fi
# Calculer les valeurs dérivées du profil de connexion
case "$TOMCAT_PROFILE" in
debian:*)
t="${TOMCAT_PROFILE#debian:}"
v="${t#tomcat}"
[ -z "$CATALINA_BASE" ] && CATALINA_BASE="/var/lib/$t"
eval "$(
setv "${t^^}_USER"
setv "${t^^}_GROUP"
source "/etc/default/$t" 2>/dev/null
eval "echo tomcat_user=\"\$${t^^}_USER\""
eval "echo tomcat_group=\"\$${t^^}_GROUP\""
)"
[ -z "$TOMCAT_USER" ] && TOMCAT_USER="$tomcat_user"
[ -z "$TOMCAT_GROUP" ] && TOMCAT_GROUP="$tomcat_group"
[ -z "$TOMCAT_VERSION" ] && TOMCAT_VERSION="$v"
;;
runs:*)
v="${TOMCAT_PROFILE#runs:tomcat}"
if [ -z "$CATALINA_BASE" -a "$v" != "$TOMCAT_PROFILE" ]; then
array_lsdirs ts /opt "apache-tomcat-$v*"
CATALINA_BASE="${ts[0]}"
fi
if [ -z "$CATALINA_BASE" ]; then
if [ -L /opt/tomcat ]; then
setx CATALINA_BASE=readlink /opt/tomcat
else
array_lsdirs ts /opt "apache-tomcat-*"
CATALINA_BASE="${ts[0]}"
fi
fi
name="${TOMCAT_PROFILE#runs:}"
eval "$(
TOMCAT_USER=
source "/etc/default/$name" 2>/dev/null
echo tomcat_user="$TOMCAT_USER"
)"
[ -z "$TOMCAT_USER" ] && TOMCAT_USER="$tomcat_user"
if [ -z "$TOMCAT_VERSION" ]; then
v="$CATALINA_BASE"
[ -L "$v" ] && setx v=readlink "$v"
setx v=basename -- "$v"
if [[ "$v" == apache-tomcat-* ]]; then
TOMCAT_VERSION="${v#apache-tomcat-}"
fi
fi
;;
esac
# Calculer les valeurs dérivées des fichiers de $CATALINA_BASE
if [ -n "$CATALINA_BASE" -a -n "$TOMCAT_VERSION" ]; then
if [ -z "$MANAGER_URL" -o -z "$MANAGER_USER" ]; then
# lire les fichiers de configuratione et déterminer MANAGER_URL,
# MANAGER_USER et MANAGER_PASSWORD
if is_root; then
[ -z "$MANAGER_URL" ] && parse_server
[ -z "$MANAGER_USER" ] && parse_tomcat_users
else
args=()
[ -z "$MANAGER_URL" ] && array_add args --private-parse-server
[ -z "$MANAGER_USER" ] && array_add args --private-parse-tomcat-users
array_add args "$CATALINA_BASE" "$TOMCAT_VERSION"
eval "$(run_as root --noexec "${args[@]}")"
fi
fi
fi
# A partir de la liste des webapps et de WAMAPS, construire une liste de couples
# waname:src pour la sauvegarde et/ou le déploiement
[ -n "$fake" -o "$action" == config-template ] && will_deploy= || will_deploy=1
if [ $# -eq 0 -a -n "$will_deploy" ]; then
[ -d WEB-INF ] && src=. || src=
read_value "Veuillez entrer la webapp à déployer" src "$src" O
set -- "$src"
elif [ $# -eq 0 -a "$action" == config-template ]; then
[ -d WEB-INF ] && set .
fi
srcs=()
[ -n "$istmpdir" ] || ac_set_tmpdir tmpdir
for src in "$@"; do
if [ -f "$src" ] && is_archive "$src"; then
setx waname=get_archive_appname "$src"
wadir="$tmpdir/$waname"
mkdir "$wadir"
if extract_archive "$src" "$wadir"; then
array_add srcs "$wadir"
else
eerror "$src: erreur lors de l'extraction de l'archive"
fi
elif [ -d "$src" ]; then
array_add srcs "$(abspath "$src")"
elif [ -e "$src" ]; then
eerror "$src: fichier invalide"
else
eerror "$src: fichier ou répertoire introuvable"
fi
done
[ ${#srcs[*]} -gt 0 -o -z "$will_deploy" ] || die
deploymaps=()
for src in "${srcs[@]}"; do
setx waname=basename -- "$src"
found=
for wamap in "${WAMAPS[@]}"; do
splitpair "$wamap" from to
if [ "$waname" == "$from" ]; then
found=1
array_add deploymaps "$to:$src"
fi
done
if [ -z "$found" ]; then
array_add deploymaps "$waname:$src"
fi
done
################################################################################
# Maintenant, nous pouvons faire les actions
if [ "$action" == config-template ]; then
if [ ${#deploymaps[*]} -gt 0 ]; then
toinstconfname=.toinst.conf
else
toinstconfname=toinst.conf
deploymaps=(:.)
fi
for deploymap in "${deploymaps[@]}"; do
splitpair "$deploymap" waname src
toinstconf="$src/$toinstconfname"
if [ -f "$toinstconf" ]; then
estepi "$(ppath "$toinstconf"): configuration déjà existante"
else
estep "$(ppath "$toinstconf")"
echo >"$toinstconf" "# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# Fichiers à exclure de la source lors du déploiement
#EXCLUDES=()
# Fichiers à protéger dans la destination lors du déploiement
#PROTECTS=()
# Options supplémentaires de rsync à utiliser pour le déploiement
#RSYNC_OPTIONS=()"
fi
done
exit 0
fi
## Sauvegarde
if [ -n "$BACKUP" -o "$action" == backup ]; then
[ -n "$CATALINA_BASE" -o -z "$will_deploy" ] || die "Vous devez spécifier l'option --catalina-base"
home_bckdir="$HOME/tomcat-backups"
if [ -n "$istmpdir" ]; then
# déploiement distant: sauvegarder dans le répertoire utilisateur
default_bckdir="$home_bckdir"
else
# déploiement local: ça dépend de la webapp
default_bckdir=
fi
setx cwd=pwd
etitle "Sauvegarde des webapps"
for deploymap in "${deploymaps[@]}"; do
splitpair "$deploymap" waname src
wadir="$CATALINA_BASE/webapps/$waname"
if [ ! -d "$wadir" ]; then
einfo "$waname: la webapp n'a pas encore été déployée. elle ne sera pas sauvegardée"
continue
fi
bckdir="$default_bckdir"
if [ -z "$bckdir" ]; then
if withinpath "$tmpdir" "$src"; then
bckdir="$home_bckdir"
elif withinpath "$src" "$cwd"; then
setx bckdir=dirname "$src"
else
bckdir=.
fi
fi
mkdir -p "$bckdir"
setx base=date +%y%m%d
i=0
bckname="$base.$profile$i-$waname"
while [ -d "$bckdir/$bckname" ]; do
let i=$i+1
bckname="$base.$profile$i-$waname"
done
if [ -n "$fake" ]; then
evalx qvals rsync -a "$wadir/" "$bckdir/$bckname" // eecho
else
estep "$wadir --> $(ppath "$bckdir/$bckname")"
if ! rsync -a "$wadir/" "$bckdir/$bckname"; then
ewarn "Une erreur s'est produite pendant la sauvegarde"
ask_yesno "Voulez-vous continuer le déploiement?" N || die
fi
fi
done
eend
fi
## Déploiement
if [ "$action" == deploy ]; then
[ -n "$CATALINA_BASE" -o -z "$will_deploy" ] || die "Vous devez spécifier l'option --catalina-base"
if [ -n "$fake" ]; then
# Afficher ce qu'il faut faire
etitle "Configuration globale" __dump_vars
etitle "Déploiements des webapps"
for deploymap in "${deploymaps[@]}"; do
splitpair "$deploymap" waname src
setx srcname=basename -- "$src"
dest="${CATALINA_BASE:-CATALINA_BASE}/webapps/$waname"
etitle "$waname"
if [ -f "$src/.toinst.conf" ]; then
etitle "Configuration de la webapp"
(
EXCLUDES=()
PROTECTS=()
RSYNC_OPTIONS=()
source "$src/.toinst.conf"
for var in EXCLUDES PROTECTS RSYNC_OPTIONS; do
eecho "$(echo_seta2 "$var")"
done
)
eend
fi
build_rsync_options "$src"
evalx qvals rsync "${rsync_options[@]}" "$src/" "$dest" // eecho
evalx qvals chmod -R u=rwX,g=rX,o=rX "$dest" // eecho
if [ -n "$TOMCAT_USER" ]; then
evalx qvals chown -R "$TOMCAT_USER:$TOMCAT_GROUP" "$dest" // eecho
fi
eend
done
eend
elif ! is_root; then
# le déploiement doit être fait en root
args=(
-C "$TOMCAT_PROFILE"
-d "$CATALINA_BASE"
-U "$TOMCAT_USER"
-G "$TOMCAT_GROUP"
-V "$TOMCAT_VERSION"
-g "$MANAGER_URL"
-u "$MANAGER_USER"
-p "$MANAGER_PASSWORD"
-m "$(array_join WAMAPS ,)"
--no-backup
--no-restart
)
run_as root --noexec "${args[@]}" "${srcs[@]}" || die
else
# faire le déploiement
etitle "Déploiements des webapps"
for deploymap in "${deploymaps[@]}"; do
splitpair "$deploymap" waname src
setx srcname=basename -- "$src"
dest="$CATALINA_BASE/webapps/$waname"
etitle "$srcname"
estepi "Déploiement vers $dest"
estep "Copie des fichiers"
build_rsync_options "$src"
if ! rsync "${rsync_options[@]}" "$src/" "$dest"; then
eerror "Une erreur s'est produite pendant la copie des fichiers"
else
error=
estep "Correction des permissions et du propriétaire"
chmod -R u=rwX,g=rX,o=rX "$dest" || error=1
if [ -n "$TOMCAT_USER" ]; then
chown -R "$TOMCAT_USER:$TOMCAT_GROUP" "$dest" || error=1
fi
if [ -n "$error" ]; then
eeror "Une erreur s'est produite pendant la mise à jour"
fi
fi
eend
done
eend
fi
fi
## Redémarrage
if [ -n "$RESTART" -o "$action" == restart ]; then
etitle "Redémarrage des webapps"
if [ -z "$MANAGER_URL" -o -z "$MANAGER_USER" ]; then
ewarn "Impossible de recharger la webapp parce que l'url du manager et/ou le compte admin n'ont pas pu être déterminés."
[ -z "$fake" ] && enote "\
Par défaut, les webapps ne sont rechargées automatiquement que si le fichier web.xml est modifié.
Utilisez les options -g, -u, -p pour spécifier les informations manquantes nécessaires au pilotage du manager."
elif [ -n "$fake" ]; then
estepi "Utilisation du manager à l'adresse $MANAGER_URL"
for deploymap in "${deploymaps[@]}"; do
splitpair "$deploymap" waname src
etitle "$waname"
evalx qvals curl -fsS -u "$MANAGER_USER:$MANAGER_PASSWORD" "$MANAGER_URL/reload?path=/$waname" // eecho
eend
done
else
estepi "Utilisation du manager à l'adresse $MANAGER_URL"
for deploymap in "${deploymaps[@]}"; do
splitpair "$deploymap" waname src
ebegin "$waname"
curl -fsS -u "$MANAGER_USER:$MANAGER_PASSWORD" "$MANAGER_URL/reload?path=/$waname" >/dev/null
eend $?
done
fi
eend
fi

276
ufile Executable file
View File

@ -0,0 +1,276 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS
function display_help() {
uecho "$scriptname: classer des fichiers selon certains règles
Les règles sont spécifiées dans le fichier ~/etc/default/ufile
Dans ce fichier, le tableau RULES contient des règles qui sont chacune de la
forme pattern:destdir[:renamef]
- pattern est au format glob et identifie les fichiers auxquels s'applique la
règle
- destdir est le répertoire de destination dans lequel classer le fichier
- renamef est une fonction qui permet de supporter le renommage d'un fichier
lors de son classement. Sa signature est 'renamef filename pf destdir' où
filename est le nom du fichier source, pf son chemin complet et destdir la
valeur de destdir mentionnée dans la règle.
La fonction doit définir la variable dest qui est le nouveau nom. Si la
fonction retourne un code d'erreur autre que zéro, la règle est ignorée.
Si le nouveau nom contient un chemin, destdir est ignoré et le fichier est
déplacé dans le répertoire spécifié avec le nom spécifié. Si dest est un
tableau avec plusieurs destinations, alors le fichier est copié en plusieurs
fois.
Si dest est de la forme [user@]host:path alors le fichier est copié par scp
sur l'hôte spécifié vers la destination spécifiée, sauf si l'hôte courant est
déjà celui mentionné dans la valeur, auquel cas la copie est faite directement
dans le répertoire spécifié.
Si le user et l'hôte courant sont déjà à la valeur spécifiée, alors la copie
est faite en local sans utiliser scp sauf si l'option --force-scp est utilisée
Le chemin spécifié, en local ou distant, est toujours le chemin complet vers
le fichier destination. Si on veut copier le fichier sans le renommer vers un
répertoire, il faut mettre un slash e.g destdir/ ou user@host:destdir/
variables pouvant être définies mais non documentées: interaction(=-i)
USAGE
$scriptname [options] <files...>
OPTIONS
-c, --config CONFIG
Utiliser le fichier de configuration spécifié au lieu de la valeur par
défaut ~/etc/default/ufile
--file
Classer les fichiers spécifiés. C'est l'action par défaut
-S, --ssh SSH
S'il faut classer sur un hôte distant avec scp, utiliser le programme
spécifié pour la connexion par ssh
--force-scp
Toujours utiliser scp pour une copie distante. Par défaut s'il est
déterminé que l'hôte distant est en réalité l'hôte courant, alors la
copie est effectuée directement.
-n, --fake
Afficher les opérations qui seraient faites
-l, --list
Lister les règles définies
-e, --edit
Lancer un éditeur sur le fichier de configuration"
}
function joinp() {
# afficher le chemin $1/$2
local pf="$1"
[ -n "$2" -a "${pf%/}" == "$pf" ] && pf="$pf/"
pf="$pf${2#/}"
echo "$pf"
}
function __check_destdir() {
local destdir="$1" rule="$2"
if [ -z "$destdir" ]; then
eerror "$rule: règle invalide: destdir est vide"
return 1
fi
return 0
}
function __set_dest() {
local dest="$1" destdir="$2" filename="$3" force_scp="$4"
local userhost remotedir destname
if [[ "$dest" == *:* ]]; then
splitpair "$dest" userhost remotedir
if [ -z "$force_scp" ] && check_userhostname "$userhost"; then
# on est déjà avec le bon user sur le bon hôte
if [ -n "$remotedir" ]; then
splitpath "$remotedir" destdir destname
setx destdir=abspath "$destdir" "$HOME"
[ -n "$destname" ] || destname="$filename"
setx dest=joinp "$destdir" "$destname"
else
setx dest=joinp "$HOME" "$filename"
fi
fi
elif [[ "$dest" == */* ]]; then
splitpath "$dest" destdir destname
[ -n "$destname" ] || destname="$filename"
setx dest=joinp "$destdir" "$destname"
setx dest=abspath "$dest"
else
__check_destdir "$destdir" "$rule" || return 1
setx dest=joinp "$destdir" "$filename"
fi
upvar dest "$dest"
return 0
}
args=(%
--help '$exit_with display_help'
-c:,--config: config=
--file action=file
-S:,--ssh: SSH=
--force-scp force_scp=1
-n,--fake fake=1
-l,--list action=list
-e,--edit action=edit
)
parse_args "$@"; set -- "${args[@]}"
[ -n "$action" ] || action=file
## charger toutes les règles
RULES=()
INCLUDES=()
if [ -n "$config" ]; then
if [ "$action" != edit ]; then
# le fichier doit exister, sauf en mode édition où il sera créé s'il
# n'existe pas déjà
[ -f "$config" ] || die "$config: fichier introuvable"
fi
if [ -f "$config" ]; then
source "$config" || die "$config: erreur lors de la lecture du fichier"
fi
else
set_defaults ufile
fi
array_copy rules RULES
for include in "${INCLUDES[@]}"; do
if [ -f "$include" ]; then
RULES=()
source "$include"
array_extend rules RULES
else
ewarn "$include: fichier introuvable"
fi
done
array_copy RULES rules
## actions particulières
if [ "$action" == list ]; then
echo "# $(echo_seta2 INCLUDES)"
array_to_lines RULES
exit 0
elif [ "$action" == edit ]; then
[ -n "$config" ] || setx config=get_user_defaults_file ufile
if [ ! -f "$config" ]; then
einfo "Le fichier $(ppath "$config") n'existe pas. Il sera créé avec un contenu par défaut"
mkdirof "$config" || die
cp "$scriptdir/lib/default/ufile" "$config"
fi
"${EDITOR:-vi}" "$config"
exit $?
elif [ "$action" != file ]; then
die "bug: $action: action non implémentée"
fi
## classement des fichiers
if [ -n "$fake" ]; then
function docmd() { qvals "$@"; }
else
function docmd() { "$@"; }
fi
[ $# -gt 0 ] || die "Vous devez spécifier des fichiers à classer"
[ ${#RULES[*]} -gt 0 ] || die "Vous devez spécifier des règles pour le classement des fichiers dans ~/etc/default/ufile"
# vérifier les règles
for rule in "${RULES[@]}"; do
splitpair "$rule" pattern r2
splitpair "$r2" destdir r3
splitpair "$r3" renamef r4
if [ -z "$destdir" -o "${destdir#"~/"}" != "$destdir" ]; then
:
elif [ "${destdir#/}" == "$destdir" ]; then
ewarn "$rule: règle potentiellement problématique: destdir devrait être absolu"
fi
done
# faire le classement effectif
r=
for file in "$@"; do
[ -f "$file" -o -n "$fake" ] || {
eerror "$file: fichier introuvable. il sera ignoré"
continue
}
setx pf=abspath "$file"
setx dir=dirname -- "$pf"
setx filename=basename -- "$pf"
found=
for rule in "${RULES[@]}"; do
splitpair "$rule" pattern r2
splitpair "$r2" odestdir r3
splitpair "$r3" renamef r4
if [ "${odestdir#"~/"}" != "$odestdir" ]; then
odestdir="$HOME/${odestdir#"~/"}"
elif [ "$odestdir" == "~" ]; then
odestdir="$HOME"
fi
eval "[[ \"\$filename\" == $(qwc "$pattern") ]]" || continue
unset dest
interaction=--DEFAULT--
if [ -n "$renamef" ]; then
"$renamef" "$filename" "$pf" "$odestdir" || continue
fi
if is_array dest; then
array_copy tmpdests dest
dests=()
for dest in "${tmpdests[@]}"; do
__set_dest "$dest" "$odestdir" "$filename" "$force_scp" || break
array_add dests "$dest"
done
elif is_defined dest; then
__set_dest "$dest" "$odestdir" "$filename" "$force_scp" || break
dests=("$dest")
else
__check_destdir "$odestdir" "$rule" || break
setx dest=joinp "$odestdir" "$filename"
dests=("$dest")
fi
i=1
mvi=${#dests[*]}
for dest in "${dests[@]}"; do
if [[ "$dest" == *:* ]]; then
[ "$interaction" == --DEFAULT-- ] && int= || int="$interaction"
estep "$filename --> $dest"
ask_yesno $int "Voulez-vous continuer?" O || {
r=1; found=x; break
}
docmd scp ${SSH:+-S "$SSH"} "$file" "$dest" || die "problème lors de la copie du fichier"
else
[ "$interaction" == --DEFAULT-- ] && int=-i || int="$interaction"
estep "$filename --> $dest"
ask_yesno $int "Voulez-vous continuer?" O || {
r=1; found=x; break
}
setx destdir=dirname -- "$dest"
docmd mkdir -p "$destdir" || die "$destdir: impossible de créer le répertoire"
if [ $i -eq $mvi ]; then
mvdesc="du déplacement"
mvcmd=mv
else
mvdesc="de la copie"
mvcmd=cp
fi
docmd "$mvcmd" -i "$file" "$dest" || die "problème lors $mvdesc du fichier"
fi
i=$(($i + 1))
done
[ -n "$found" ] || found=1
break
done
if [ -z "$found" ]; then
ewarn "$file: aucune correspondance n'a été trouvée"
fi
done
[ -n "$r" ] || r=0
exit $r

2
uinst
View File

@ -1,6 +1,6 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
source "$(dirname -- "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS uinst
uinst_nolocal "$@"

View File

@ -2,7 +2,7 @@
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# Version de uinst qui fonctionne avec /bin/sh. Le contrôle est transmis à
# bash, qui est recherché dans une liste de répertoires standards
. `dirname "$0"`/lib/ulib/ulibsh || exit 1
. `dirname -- "$0"`/lib/ulib/ulibsh || exit 1
urequire DEFAULTS uinst
uinst_nolocal "$@"

162
uproject
View File

@ -61,10 +61,27 @@ COMMANDS
Cloner un dépôt distant. Basculer sur la branche develop si elle existe.
Initialiser git annex si le dépôt contient des fichiers annexés.
Récupérer aussi ces fichiers avec 'git annex get'
-n, --no-clone
Ne pas cloner, afficher simplement ce qui serait fait
-u, --update
Si le dépôt a déjà été cloné, le mettre à jour avec git pull
-m, --master
Ne pas chercher à basculer sur la branche develop, même si elle
existe.
-r, --recursive
Cloner récursivement tous les dépôt à partir du chemin spécifié
depuis un serveur gitolite. La signature est alors sensiblement
différente:
clone -r git@host:basepath [destdir]
clone -r http://host/gituser/basepath [destdir]
L'arborescence en dessous de basepath est recréée à partir de
destdir, e.g le dépôt git@host:basepath/to/repo est cloné dans
destdir/to/repo
crone git@host:path/to/repo [destdir]
crone http://host/gituser/path/to/repo [destdir]
Créer un dépôt distant sur gitolite, puis le cloner
Créer un dépôt sur un serveur gitolite, puis le cloner. La commande
'create' doit avoir été activée sur ce serveur.
develop
release
@ -268,26 +285,131 @@ elif array_contains GITANNEX_CMDS "$CMD"; then
esac
elif [ "$CMD" == clone ]; then
repourl="${1%.git}"
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt git"
splitfsep "$repourl" : userhost path
destdir="$2"
if [ -z "$destdir" ]; then
setx destdir=basename -- "$path"
destdir="${destdir%.git}"
fi
[ -d "$destdir" ] && die "$(ppath "$destdir"): répertoire existant"
git_annex_use_ssh_wrapper
git clone "$repourl" "$destdir" || die
(
cd "$destdir"
if git_have_rbranch develop; then
git checkout develop || exit 1
function pclone() {
estep "$1 --> $(ppath "$2")"
mkdirof "$2" || return 1
git clone "$1" "$2" || return 1
if [ -z "$3" ]; then
(
cd "$2"
if git_have_rbranch develop; then
git checkout develop || exit 1
fi
) || return 1
fi
) || die
git_annex_initial "$destdir" || die
git_annex_initial "$2" || return 1
}
function gitolite_info() {
local mode="$1" urlbase="$2" pattern="$3"
case "$mode" in
http) curl -fs "$urlbase/info${pattern:+"?$pattern"}";;
ssh) ssh -q "$urlbase" info ${pattern:+"$pattern"} 2>/dev/null;;
esac
}
function filter_repos() {
awkrun prefix="$1" '
NR <= 2 { next }
{
# filtrer les projets qui ne sont pas encore créés
if (substr($0, 5, 2) == " C") next
repo = substr($0, 6)
# filtrer les projets de type wildcard
if (repo ~ /[\[\]\*]/) next
# enlever le prefixe
if (prefix != "" && substr(repo, 1, length(prefix)) != prefix) next
print repo
}
'
}
no_clone=
update=
nodevelop=
recursive=
parse_opts "${PRETTYOPTS[@]}" \
-n,--no-clone no_clone=1 \
-u,--update update=1 \
-m,--master nodevelop=1 \
-r,--recursive recursive=1 \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
if [ -n "$recursive" ]; then
repobase="$1"
[ -n "$repobase" ] || die "Vous devez spécifier l'url de base des dépôts à cloner"
if [ "${repobase#http://}" != "$repobase" -o "${repobase#https://}" != "$repobase" ]; then
# accès par http
mode=http
splitfsep "$repobase" :// scheme hostuserpath
splitfsep "$hostuserpath" / host userpath
splitfsep "$userpath" / user basepath
[ -n "$host" -a -n "$user" ] || die "Vous devez spécifier l'hôte e.g http://host/git/basepath"
urlbase="$scheme://$host/$user"
else
# accès par ssh
mode=ssh
splitfsep "$repobase" : userhost basepath
splituserhost "$userhost" user host
[ -n "$user" ] || user=git
[ -n "$host" ] || die "Vous devez spécifier l'hôte"
urlbase="$user@$host"
fi
basepath="${basepath%/}"
destbase="${2:-.}"
git_annex_use_ssh_wrapper
prefix="${basepath:+$basepath/}"
array_from_lines repos "$(set -o pipefail; gitolite_info "$mode" "$urlbase" "$prefix" | filter_repos "$prefix")" || die
for repo in "${repos[@]}"; do
case "$mode" in
http) repourl="$urlbase/$repo";;
ssh) repourl="$urlbase:$repo";;
esac
setx destdir=abspath "$destbase/${repo#$prefix}"
if [ -d "$destdir" ]; then
if [ -n "$update" ]; then
(
${no_clone:+qvals} cd "$destdir"
${no_clone:+qvals} git pull
) || die
else
estepe "$(ppath2 "$destdir"): répertoire existant"
fi
elif [ -n "$no_clone" ]; then
qvals git clone "$repourl" "$destdir"
else
pclone "$repourl" "$destdir" "$nodevelop" || die
fi
done
else
repourl="${1%.git}"
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt git"
destdir="$2"
if [ -z "$destdir" ]; then
splitfsep "$repourl" : userhost path
setx destdir=basename -- "$path"
destdir="${destdir%.git}"
fi
setx destdir=abspath "$destdir"
git_annex_use_ssh_wrapper
if [ -d "$destdir" ]; then
if [ -n "$update" ]; then
(
${no_clone:+qvals} cd "$destdir"
${no_clone:+qvals} git pull
) || die
else
estepe "$(ppath2 "$destdir"): répertoire existant"
fi
elif [ -n "$no_clone" ]; then
qvals git clone "$repourl" "$destdir"
else
pclone "$repourl" "$destdir" "$nodevelop" || die
fi
fi
elif [ "$CMD" == crone ]; then
repourl="${1%.git}"
@ -318,7 +440,7 @@ elif [ "$CMD" == crone ]; then
fi
tmpdestdir=
if [ -d "$destdir" ]; then
[ -d "$destdir/.git" ] && die "$(ppath "$destdir"): répertoire existant"
[ -d "$destdir/.git" ] && die "$(ppath2 "$destdir"): un dépôt existe déjà"
ac_set_tmpdir tmpdestdir
fi

2
woinst
View File

@ -1,6 +1,6 @@
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
source "$(dirname -- "$0")/lib/ulib/ulib" || exit 1
urequire DEFAULTS woinst
woinst "$@"

8
woinst.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
# Version de woinst qui fonctionne avec /bin/sh. Le contrôle est transmis à
# bash, qui est recherché dans une liste de répertoires standards
. `dirname -- "$0"`/lib/ulib/ulibsh || exit 1
urequire DEFAULTS woinst
woinst "$@"