nutools/uproject

673 lines
22 KiB
Plaintext
Raw Normal View History

2013-08-27 15:14:44 +04:00
#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
function display_help() {
uecho "$scriptname: Outil pour gérer des projets
USAGE
$scriptname cmd [args]
COMMANDS
getvcs [dir]
Afficher le type de VCS pour dir.
getroot [dir]
Si dir est un répertoire versionné, retourner le répertoire racine du
projet versionné.
getrepos [dir]
Si dir est un répertoire versionné, retourner l'url du repository du
projet versionné.
geturl [dir]
Si dir est un répertoire versionné, retourner son url dans le
repository.
fold [dir]
unfold [dir]
Utiliser uinc pour défaire (resp. refaire) toutes les inclusions des
fichiers de dir. Cela nécessite qu'un fichier .udir soit configuré à la
racine du projet avec uinc=true
2013-08-27 15:14:44 +04:00
vcs [args]
Appeler le gestionnaire de gestion approprié avec les arguments donnés.
add files...
Ajouter les fichiers files dans le gestionnaire de version.
remove files...
Supprimer les fichiers versionnés files.
copy from to
Copier le fichier versionné from vers le fichier to.
move from to
Renommer le fichier versionné from vers le fichier to.
mkdir dir
Créer un nouveau répertoire versionné.
commit message [files...]
Enregistrer les modifications (par défaut sur tous les fichiers
modifiés) avec le commentaire message.
status
Afficher l'état des fichiers versionnés et non versionnés.
update [-x]
Mettre à jour la copie locale avec la copie sur le serveur.
2018-03-01 10:22:29 +04:00
-x Ne pas mettre à jour les références externes (uniquement pour svn)
2018-02-26 16:28:41 +04:00
-c, --clean-before
Avant de faire git pull, supprimer toutes les branches locales qui
sont à jour par rapport à l'origine. La copie de travail doit être
propre. Basculer sur la branche develop avant de supprimer les
branches. S'il n'y a pas de branche develop, c'est un NOP.
-n, --no-autoff
Ne pas faire de fast-forward automatique pour toutes les branches
traquées. Par défaut, s'il n'y a pas de modifications locales,
essayer de fast-fowarder toutes les branches locales traquées.
2013-08-27 15:14:44 +04:00
diff [options]
Afficher les différences.
-l Afficher les différences non commitées (par défaut)
-c Afficher les différences en passe d'être commitées (si appliquable)
-r REV
Afficher les différences depuis la révision REV.
-R Afficher les modifications effectuées depuis la dernière release.
2014-05-21 22:28:43 +04:00
clone git@host:path/to/repo [destdir]
2017-02-12 11:17:17 +04:00
clone http://host/gituser/path/to/repo [destdir]
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
2017-04-27 23:06:30 +04:00
-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]
2017-02-12 11:17:17 +04:00
crone http://host/gituser/path/to/repo [destdir]
Créer un dépôt sur un serveur gitolite, puis le cloner. La commande
'create' doit avoir été activée sur ce serveur.
2014-01-28 18:07:27 +04:00
develop
release
hotfix
Démarrer le travail sur une branche respectivement de développement, de
release, ou de correction de bugs. Lancer chaque commande avec --help
pour les détails. Nécessite git.
archive
Créer une archive du projet courant. Nécessite git.
2014-05-21 22:28:43 +04:00
annex [args]
Lancer git annex avec les arguments spécifiés.
xadd
xunlock
xdrop
xwhereis
2015-06-21 10:26:44 +04:00
xwebapp
2014-05-21 22:28:43 +04:00
Chacune de ces commandes est un raccourci vers la commande
correspondante de git annex, sans le préfixe 'x'
xsync
Sur un dépot où git-annex est activé, lancer 'git annex sync' si on est
en mode indirect ou 'git annex sync --content' si on est en mode direct.
Sur un dépôt où git-annex n'est pas activé, faire l'équivalent des
commandes 'git add -A && git commit && git pull && git push'
xcopy
xmove
xget
Comme ci-dessus, mais si la commande s'exécute sans erreur, lancer
aussi 'git annex sync'
xinitial
Sur un dépôt fraichement cloné, initialiser le dépôt avec 'annex init'
s'il contient des fichiers annexés. Récupérer aussi ces fichiers avec
'annex get'
xconfig-export [dir]
Installer des hooks pour qu'un dépôt puisse être utilisé pour servir des
fichiers, par exemple avec un serveur web. Plus précisément, un hook
post-receive est créé avec la commande 'git annex merge', et un hook
post-update est créé avec la commande 'git update-server-info'
2014-05-29 15:09:08 +04:00
2013-08-27 15:14:44 +04:00
printml [-t TYPE]
Afficher le modeline pour un fichier du type spécifié
addml [-t TYPE] file
Ajouter un modele pour le fichier spécifié, s'il n'en a pas déjà un.
Si nécessaire, forcer le type du fichier au lieu de l'autodétecter
new [options] file [template options]
Créer un nouveau fichier à partir d'un modèle.
Avant le nom du fichier, les options suivantes sont valides:
-t TEMPLATE
Spécifier le modèle de fichier à utiliser. Par défaut, le modèle
à utiliser est déduit de l'extension ou du nom du fichier.
-e Editer le fichier après l'avoir créé.
Après le nom du fichier, toutes les options sont spécifiques au modèle
utilisé pour créer le nouveau fichier. Utiliser l'option --help pour
avoir une description des options disponibles."
}
SCRIPT_ALIASES=(
pv:vcs
pa:add prm:remove pcp:copy pmv:move pmd:mkdir
2014-02-12 05:18:57 +04:00
pci:commit pu:update pp:push pdiff:diff
pclone:clone
pcrone:crone
2014-05-21 22:28:43 +04:00
pxx:annex
2014-10-31 09:21:00 +04:00
pxa:xadd pxu:xunlock pxc:xcopy pxd:xdrop pxm:xmove
2015-06-21 10:26:44 +04:00
pxg:xget pxs:xsync pxw:xwhereis pxwa:xwebapp
pxinitial:xinitial
2013-08-27 15:14:44 +04:00
pnew:new
pgr:grep
paddml:addml
)
CMD_ALIASES=(
getrepo:getrepos repo:getrepos repos:getrepos
url:geturl
a:add
rm:remove del:remove delete:remove
cp:copy
mv:move ren:move rename:move
md:mkdir
ci:commit
s:status st:status
u:update upd:update
2014-02-12 05:18:57 +04:00
p:push
2015-03-05 13:59:39 +04:00
version:pver ver:pver
2014-01-28 18:07:27 +04:00
develop:pdev dev:pdev release:prel rel:prel hotfix:pfix fix:pfix
archive:pz arch:pz
2014-05-21 22:28:43 +04:00
xx:annex
xa:xadd
xu:xunlock
xc:xcopy
xd:xdrop
xm:xmove
xg:xget
xs:xsync
xw:xwhereis
2015-06-21 10:26:44 +04:00
xwa:xwebapp
xce:xconfig-export
2013-08-27 15:14:44 +04:00
gr:grep
)
DEFAULT_CMD=status
PY_CMDS=(new)
2014-02-12 05:18:57 +04:00
VCS_CMDS=(getvcs getroot getrepos geturl vcs add remove copy move mkdir commit status update push diff tag)
SH_CMDS=(pver pdev prel pfix pz)
2015-06-21 10:26:44 +04:00
GITANNEX_CMDS=(annex xadd xunlock xcopy xdrop xmove xget xsync xwhereis xwebapp xinitial)
2013-08-27 15:14:44 +04:00
ML_CMDS=(printml addml)
if [ "$#" -eq 1 -a "$1" == --nutools-makelinks ]; then
# créer les liens
scriptname="$(basename "$0")"
for alias in p "${SCRIPT_ALIASES[@]}"; do
alias="${alias%:*}"
ln -s "$scriptname" "$alias"
done
exit 0
fi
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
urequire modeline vcs
2013-08-27 15:14:44 +04:00
# Traduire le nom du script
for script_alias in "${SCRIPT_ALIASES[@]}"; do
splitpair "$script_alias" src dest
if [ "$scriptname" == "$src" ]; then
eval "set -- $dest \"\$@\""
break
fi
done
# Parser les options de uproject
parse_opts + "${PRETTYOPTS[@]}" \
--help '$exit_with display_help' \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
# Traduire la commande
[ -n "$*" ] || set -- "$DEFAULT_CMD"
CMD=
found_cmd=
while [ -z "$found_cmd" ]; do
CMD="$1"; shift; found_cmd=1
[ -n "$CMD" ] || break
for cmd_alias in "${CMD_ALIASES[@]}"; do
splitpair "$cmd_alias" src dest
if [ "$CMD" == "$src" ]; then
eval "set -- $dest \"\$@\""
found_cmd=
break
fi
done
done
################################################################################
# Traiter les commandes
function cxone_init() {
repourl="$1"
[ -n "$repourl" ] || return
rname=
rtype=gitolite
rprefix=
REPO_PREFIXES=()
REPO_TYPES=()
2019-09-28 12:33:03 +04:00
set_defaults repoctl
# Traduire les aliases éventuels
local asrcdest asrc adest
for asrcdest in "${REPO_PREFIXES[@]}"; do
splitfsep "$asrcdest" = asrc adest
if [ "${repourl#$asrc}" != "$repourl" ]; then
newurl="$adest${repourl#$asrc}"
if [ "$newurl" != "$repourl" ]; then
enote "$repourl --> $newurl"
repourl="$newurl"
break
fi
fi
done
local rnametypeprefix tmp found
for rnametypeprefix in "${REPO_TYPES[@]}"; do
splitfsep "$rnametypeprefix" : rname tmp
splitfsep "$tmp" : rtype rprefix
if [ "${repourl#$rprefix}" != "$repourl" ]; then
found=1
break
fi
done
if [ -z "$found" ]; then
rname=
rtype=gitolite
rprefix=
fi
}
function curlto() {
local url="$1"; shift
local payload="$1"; shift
local outfile="$1"; shift
local tmpfile
if [ -z "$outfile" ]; then
ac_set_tmpfile tmpfile
outfile="$tmpfile"
fi
local -a args
local r http_code
args=(-s -w '%{http_code}' -o "$outfile")
[ -n "$payload" ] && args+=(-d "$payload")
args+=("$@" "$url")
setx http_code=curl "${args[@]}"
case "$http_code" in
2*) r=0;;
4*) r=1;;
5*) r=3;;
*) r=11;;
esac
if [ -n "$tmpfile" ]; then
cat "$tmpfile"
ac_clean "$tmpfile"
fi
upvar http_code "$http_code"
return "$r"
}
2013-08-27 15:14:44 +04:00
if [ "$CMD" == "grep" ]; then
## grep
if [ $# -eq 1 -a "$1" == "--help" ]; then
uecho "uproject grep: Lancer une recherche récursive en ignorant les répertoire de controle de version
USAGE
uproject grep [grep options]"
exit 0
fi
EXCLUDES=(--exclude-dir .svn --exclude-dir CVS --exclude-dir .git --exclude "*.pyc")
exec grep -r "${EXCLUDES[@]}" "$@"
2014-01-28 18:07:27 +04:00
elif array_contains SH_CMDS "$CMD"; then
exec "$scriptdir/$CMD" "$@"
2013-08-27 15:14:44 +04:00
elif array_contains ML_CMDS "$CMD"; then
"$CMD" "$@"
elif array_contains VCS_CMDS "$CMD"; then
"vcs_$CMD" "$@"
2014-05-21 22:28:43 +04:00
elif array_contains GITANNEX_CMDS "$CMD"; then
function xsync() {
if ! git_have_annex; then
setyesval offline "$UTOOLS_VCS_OFFLINE"
2023-04-12 15:59:23 +04:00
if git_commit -Al "modifs.mineures sans commentaires"; then
[ -n "$offline" ] && return 0
git_have_remote || return 0
git pull && git_push
fi
elif is_yes "$(git config --get annex.direct)"; then
git annex add &&
git annex sync &&
git annex sync --content &&
git annex sync
else
git annex sync
fi
}
git_annex_use_ssh_wrapper
case "$CMD" in
annex) git annex "$@";;
xsync) xsync;;
xcopy|xmove|xget) git annex "${CMD#x}" "$@" && git annex sync;;
xinitial) git_annex_initial "$@";;
*) git annex "${CMD#x}" "$@";;
esac
2014-05-21 22:28:43 +04:00
elif [ "$CMD" == clone ]; then
git_annex_use_ssh_wrapper
function pclone() {
estep "$1 --> $(ppath "$2")"
mkdirof "$2" || return 1
git clone "$1" "$2" || return 1
2017-04-27 23:06:30 +04:00
if [ -z "$3" ]; then
(
cd "$2"
if git_have_rbranch develop; then
git checkout develop || exit 1
fi
) || return 1
fi
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=
2017-04-27 23:06:30 +04:00
nodevelop=
recursive=
parse_opts "${PRETTYOPTS[@]}" \
-n,--no-clone no_clone=1 \
-u,--update update=1 \
2017-04-27 23:06:30 +04:00
-m,--master nodevelop=1 \
-r,--recursive recursive=1 \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
cxone_init "$@"
[ -n "$repourl" ] && set -- "$repourl" "${@:2}"
if [ -n "$recursive" ]; then
repobase="$1"
[ -n "$repobase" ] || die "Vous devez spécifier l'url de base des dépôts à cloner"
[ "$rtype" == gitolite ] || die "Le clonage récursif n'est supporté que pour les dépôts de type gitolite"
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
2017-04-27 23:06:30 +04:00
pclone "$repourl" "$destdir" "$nodevelop" || die
fi
done
else
repourl="$1"
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt git"
[ "$rtype" == gogs -o "$rtype" == gitea ] && repourl="${repourl%.git}.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
2017-04-27 23:06:30 +04:00
pclone "$repourl" "$destdir" "$nodevelop" || die
fi
fi
2014-05-29 15:09:08 +04:00
elif [ "$CMD" == crone ]; then
no_clone=
parse_opts "${PRETTYOPTS[@]}" \
-n,--no-clone no_clone=1 \
@ args -- "$@" && set -- "${args[@]}" || die "$args"
cxone_init "$@"
2014-05-29 15:09:08 +04:00
[ -n "$repourl" ] || die "Vous devez spécifier l'url du dépôt git"
if [ "$rtype" == "gitolite" ]; then
if [ "${repourl#http://}" != "$repourl" -o "${repourl#https://}" != "$repourl" ]; then
# accès par http
mode=gitolite_http
splitfsep "$repourl" :// scheme hostuserpath
splitfsep "$hostuserpath" / host userpath
splitfsep "$userpath" / user path
[ -n "$host" -a -n "$user" ] || die "Vous devez spécifier l'hôte e.g http://host/git/repo"
hostuser="$scheme://$host/$user"
else
# accès par ssh
mode=gitolite_ssh
splitfsep "$repourl" : userhost path
splituserhost "$userhost" user host
[ -n "$user" ] || user=git
[ -n "$host" ] || die "Vous devez spécifier l'hôte"
userhost="$user@$host"
fi
elif [ "$rtype" == gogs -o "$rtype" == gitea ]; then
mode=gogs_http
gogs_url="${rname}_GOGS_URL"; gogs_url="${!gogs_url}"
gogs_user="${rname}_GOGS_USER"; gogs_user="${!gogs_user}"
gogs_key="${rname}_GOGS_KEY"; gogs_key="${!gogs_key}"
userpath="${repourl#$rprefix}"
2017-02-12 11:17:17 +04:00
splitfsep "$userpath" / user path
else
die "$rtype: type de dépôt non supporté"
2017-02-12 11:17:17 +04:00
fi
[ -n "$path" ] || die "Vous devez spécifier le chemin du dépôt git"
destdir="$2"
if [ -z "$destdir" ]; then
setx destdir=basename -- "$path"
destdir="${destdir%.git}"
fi
tmpdestdir=
if [ -d "$destdir" -a -z "$no_clone" ]; then
[ -d "$destdir/.git" ] && die "$(ppath2 "$destdir"): un dépôt existe déjà"
ac_set_tmpdir tmpdestdir
fi
if [ "$mode" == gitolite_http ]; then
2017-02-12 11:17:17 +04:00
setx result=curl -fs "$hostuser/create?$path" || die
echo "$result"
[[ "$result" == FATAL:* ]] && die
[ -n "$no_clone" ] && exit 0
2017-02-12 11:17:17 +04:00
if [ -n "$tmpdestdir" ]; then
setxx destname=abspath "$destdir" // basename
git clone "$hostuser/$path" "$tmpdestdir/$destname" || die
mv "$tmpdestdir/$destname/.git" "$destdir" || die
ac_clean "$tmpdestdir"
else
git clone "$hostuser/$path" "$destdir" || die
fi
elif [ "$mode" == gitolite_ssh ]; then
2017-02-12 11:17:17 +04:00
git_annex_use_ssh_wrapper
ssh "$userhost" create "$path" || die
[ -n "$no_clone" ] && exit 0
2017-02-12 11:17:17 +04:00
if [ -n "$tmpdestdir" ]; then
setxx destname=abspath "$destdir" // basename
git clone "$userhost:$path" "$tmpdestdir/$destname" || die
mv "$tmpdestdir/$destname/.git" "$destdir" || die
ac_clean "$tmpdestdir"
else
git clone "$userhost:$path" "$destdir" || die
fi
elif [ "$mode" == gogs_http ]; then
payload='{"private":true,"name":"'"$path"'"}'
if [ "$gogs_user" != "$user" ]; then
url="$gogs_url/api/v1/org/$user/repos"
else
url="$gogs_url/api/v1/user/repos"
fi
setx result=curlto "$url" "$payload" "" \
-H 'Content-Type: application/json' \
-H "Authorization: token $gogs_key" || \
die "Une erreur s'est produite lors de la tentative de création du dépôt
url: $url
payload: $payload
result: $result"
echo "$result"
[ -n "$no_clone" ] && exit 0
if [ -n "$tmpdestdir" ]; then
setxx destname=abspath "$destdir" // basename
git clone "${repourl%.git}.git" "$tmpdestdir/$destname" || die
mv "$tmpdestdir/$destname/.git" "$destdir" || die
ac_clean "$tmpdestdir"
else
git clone "${repourl%.git}.git" "$destdir" || die
fi
else
2017-02-12 11:17:17 +04:00
die "bug: mode non prévu"
fi
if [ -f "$destdir/.gitignore" ]; then
:
elif ask_yesno "Voulez-vous créer le fichier initial .gitignore?" O; then
echo >"$destdir/.gitignore" "\
2018-09-10 07:54:53 +04:00
.~lock*#
.*.swp"
fi
if [ -f "$destdir/.gitattributes" ]; then
:
elif ask_yesno "Voulez-vous créer le fichier initial .gitattributes?" N; then
echo >"$destdir/.gitattributes" "\
*.zip -delta
2020-10-06 04:02:19 +04:00
*.jar -delta
*.gz -delta
*.bz2 -delta
*.whl -delta
*.exe -delta"
fi
2014-05-29 15:09:08 +04:00
elif [ "$CMD" == xconfig-export ]; then
unset GIT_DIR; unset GIT_WORK_TREE
dir="${1:-.}"
[ -d "$dir" ] || die "$dir: répertoire introuvable"
setx dir=abspath "$dir"
setx repodir=ppath "$dir"
cd "$dir"
git rev-parse 2>/dev/null || die "$repodir: n'est pas un dépôt git"
[ -n "$(git config --get annex.uuid)" ] || die "$repodir: n'est pas un dépôt git-annex"
cd "$(__vcs_find_root "$dir")"
[ -d .git ] || die "$repodir: est un dépôt nu"
prhook=.git/hooks/post-receive
prscript='if [ -n "$GIT_DIR" ]; then cd "$GIT_DIR"; cd ..; unset GIT_DIR; fi
git annex merge'
puhook=.git/hooks/post-update
puscript='git update-server-info'
if [ -f "$prhook" ]; then
ewarn "Le fichier $prhook existe déjà dans $repodir
Vérifiez qu'il contient les commandes suivantes:
--------8<--------
$prscript
--------8<--------"
else
estep "post-receive"
echo "#!/bin/bash
$prscript" >"$prhook"
chmod +x "$prhook"
fi
if [ -f "$puhook" ]; then
ewarn "Le fichier $puhook existe déjà dans $repodir
Vérifiez qu'il contient les commandes suivantes:
--------8<--------
$puscript
--------8<--------"
else
estep "post-update"
echo "#!/bin/bash
$puscript" >"$puhook"
chmod +x "$puhook"
fi
2013-08-27 15:14:44 +04:00
elif array_contains PY_CMDS "$CMD"; then
exec "$scriptdir/lib/pywrapper" uproject.py "$CMD" "$@"
else
die "$CMD: commande inconnue"
fi