nutools/dkrun

2213 lines
70 KiB
Plaintext
Raw Normal View History

2023-01-14 10:26:28 +04:00
#!/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 ptools xmlsupport
SELF="$script"
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Aide
function display_help() {
uecho "$scriptname: déployer/exécuter une application à base de containers
USAGE
$scriptname [PROJDIR] [OPTIONS] [ENVVARS...]
OPTIONS
-m, --machine MACHINE
2023-01-14 17:48:30 +04:00
Sélectionner la machine spécifiée pour les opérations docker. Cette
option est incompatible avec --context
-M, --context CONTEXT
Sélectionner le contexte spécifié pour les opérations docker et
kubernetes. Cette option est incompatible avec --machine
2023-01-14 10:26:28 +04:00
-n, --no-build
Ne pas reconstruire l'image avant de déployer/exécuter l'application
-b, --build
(Re)construire les images avant de déployer/exécuter l'application
-c, --config CONFIG
Lire un fichier de configuration au format dkbuild. Si cette option
n'est pas spécifiée, les fichiers ~/.dkbuild.env et /etc/dkbuild.env
sont testés dans l'ordre et automatiquement sélectionnés s'ils existent.
-e, --env VAR=VALUE
Spécifier la valeur d'une variable d'environnement.
note: les arguments ENVVARS de la forme VAR=VALUE décrits dans la
section USAGE permettent de spécifier des variables d'environnement
comme avec cette option
--arg ARG=VALUE
Spécifier la valeur d'un argument de build.
--no-cache
Ne pas utiliser le cache lors du build
-u, --pull-image
Essayer de récupérer une version plus récente de l'image source
-p, --push-image
Pousser les images construites vers la registry
--profile PROFILE
-P, --prod
-T, --test
-E, --dtest
--devel
Spécifier le profil dans lequel construire l'image et/ou
déployer/exécuter l'application
--start
-k, --stop
-r, --restart
Démarrer/déployer, Arrêter, ou Redémarrer/forcer un redéploiement de
l'application
"
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## shared
declare -A PROTECTED_VARS=(
[PROTECTED_VARS]=1
[SELF]=1
[SHARED_LOCALS1]=1
[SHARED_ARGS1]=1
[SHARED_LOCALS2]=1
[SHARED_ARGS2]=1
[TFUNCTIONS]=1
[FFUNCTIONS]=1
[PROJDIR]=1
[DKBUILD]=1
[CONFIG]=1
[MACHINE]=1
[SETDISTS_DONE]=1
[SETDISTS]=1
[SETPROFILES_DONE]=1
[SETPROFILES]=1
[SETVERSION_DONE]=1
[SETVERSION]=1
[AUTOBUILD]=1
[DISTS]=1
[PROFILES]=1
[_ENVIRON]=1
[ENVIRON]=1
[ARGS]=1
[DEFAULTS]=1
)
SHARED_LOCALS1="local PROJDIR DKBUILD CONFIG"
SHARED_ARGS1=(
-j:,--projdir: PROJDIR=
-c:,--config: CONFIG=
)
SHARED_LOCALS2="local DIST PROFILE; local -a TMPENVIRON TMPARGS"
SHARED_ARGS2=(
-d:,--dist: DIST=
-9,--d9 DIST=d9
-0,--d10 DIST=d10
-1,--d11 DIST=d11
--r7,--rhel7 DIST=rhel7
--r8,--rhel8 DIST=rhel8
--ol7,--oracle7 DIST=ol7
--ol8,--oracle8 DIST=ol8
-p:,--profile: PROFILE=
-P,--prod PROFILE=prod
-T,--test PROFILE=test
-E,--dtest PROFILE=dtest
--devel PROFILE=devel
-e:,--env: '$TMPENVIRON+=("$value_")'
--arg: '$TMPARGS+=("$value_")'
)
TFUNCTIONS=(
# dkbuild
section note info debug
setdists dist
setprofiles profile
setversion version
setenv resetenv
setarg resetarg
default resetdefault
checkout
copy
genfile
dockerfile
build
cbuild
# dockerfile
FROM
RUN
CMD
LABEL
MAINTAINER
EXPOSE
ENV
ADD
COPY
ENTRYPOINT
VOLUME
USER
WORKDIR
ARG
ONBUILD
STOPSIGNAL
HEALTHCHECK
SHELL
)
FFUNCTIONS=(
# dkbuild
composer
mvn
run
call
dkbuild
)
function set_machine() {
local machine="$1"
if [ "$machine" == -u ]; then
# déselectionner la machine courante
local -x DOCKER_TLS_VERIFY= DOCKER_HOST= DOCKER_CERT_PATH= DOCKER_MACHINE_NAME=
machine=
fi
if [ -n "$machine" ]; then
if [ -f ~/etc/default/dk ]; then
machine="$(
CLUSTERDIRS=()
DM_ALIASES=()
source ~/etc/default/dk
for alias_machine in "${DM_ALIASES[@]}"; do
if [ "${alias_machine%%:*}" == "$machine" ]; then
echo "${alias_machine#*:}"
exit
fi
done
for clusterdir in "${CLUSTERDIRS[@]}"; do
if [ -f "$clusterdir/0config/configure.conf" ]; then
DM_ALIASES=()
source "$clusterdir/0config/configure.conf"
for alias_machine in "${DM_ALIASES[@]}"; do
if [ "${alias_machine%%:*}" == "$machine" ]; then
echo "${alias_machine#*:}"
exit
fi
done
fi
done
echo "$machine"
)"
fi
eval "$(docker-machine env "$machine" || echo false)" || die
else
machine="$DOCKER_MACHINE_NAME"
[ -n "$machine" ] || machine="${HOSTNAME%%.*}"
fi
MACHINE="$machine"
}
function get_project_name() {
local project_name
setx project_name=basename -- "$(pwd)"
if [ "${project_name%.service}" != "$project_name" ]; then project_name="${project_name%.service}"
elif [ "${project_name%.stack}" != "$project_name" ]; then project_name="${project_name%.stack}"
elif [ "${project_name%.network}" != "$project_name" ]; then project_name="${project_name%.network}"
fi
echo "$project_name"
}
function get_container_name() {
local container_name="${1//[^a-zA-Z0-9_-]/}"
[ -n "$PROFILE" ] && container_name="${container_name}_$PROFILE"
echo "$container_name"
}
function reset_functions() {
local func
for func in "${TFUNCTIONS[@]}"; do
eval "function $func() { : echo \"$func \$*\"; return 0; }"
done
for func in "${FFUNCTIONS[@]}"; do
eval "function $func() { : echo \"$func \$*\"; return 1; }"
done
function include() {
edebug "include $(qvals "$@")"
local file="$1"
[ -d "$file" ] && file="$file/dkbuild"
[ -f "$file" ] || die "$file: fichier introuvable"
setx file=abspath "$file"
cd "$(dirname "$file")"
source "$file"
}
function machine() {
local machine version
for machine in "$@"; do
[ "$machine" == "$MACHINE" ] && return
done
return 1
}
}
function _runcmd() {
edebug "\$ $(qvals "$@")"
"$@"
}
function ensure_projdir() {
if [ -z "$PROJDIR" ]; then
local found=
if [ ! -f dkbuild ]; then
# NB: on teste $PROJDIR != $scriptdir parce qu'il ne faut pas qu'on
# prenne le présent script comme un script de build...
PROJDIR="$(pwd)"
if [ "${PROJDIR#$HOME/}" != "$PROJDIR" ]; then
while [ "$PROJDIR" != "$HOME" ]; do
if [ -f "$PROJDIR/dkbuild" -a "$PROJDIR" != "$scriptdir" ]; then
found=1
break
fi
setx PROJDIR=dirname "$PROJDIR"
done
else
while [ "$PROJDIR" != / ]; do
if [ -f "$PROJDIR/dkbuild" -a "$PROJDIR" != "$scriptdir" ]; then
found=1
break
fi
setx PROJDIR=dirname "$PROJDIR"
done
fi
fi
if [ -n "$found" ]; then
enote "Sélection du répertoire de projet $(relpath "$PROJDIR")"
else
PROJDIR=.
fi
fi
if [ -f "$PROJDIR" ]; then
DKBUILD="$PROJDIR"
setx PROJDIR=dirname "$PROJDIR"
else
DKBUILD="$PROJDIR/dkbuild"
fi
[ -d "$PROJDIR" ] || die "$PROJDIR: répertoire de projet introuvable"
setx PROJDIR=abspath "$PROJDIR"
setx DKBUILD=abspath "$DKBUILD"
cd "$PROJDIR" || die
[ -f "$DKBUILD" ] || die "$(ppath "$DKBUILD"): fichier de build introuvable"
if [ "$CONFIG" == none ]; then
edebug "no default config used"
elif [ -n "$CONFIG" ]; then
setx CONFIG=abspath "$CONFIG"
edebug "using config $CONFIG"
else
local config
for config in ~/.dkbuild.env /etc/dkbuild.env; do
if [ -f "$config" ]; then
CONFIG="$config"
edebug "using default config $CONFIG"
break
fi
done
fi
}
function load_dkbuild() {
local dkbuildenv="$PROJDIR/$(basename "$DKBUILD").env"
cd "$PROJDIR"
if [ -n "$CONFIG" ]; then
edebug "loading $CONFIG"
source "$CONFIG"
fi
if [ -f "$dkbuildenv" ]; then
edebug "loading $dkbuildenv"
source "$dkbuildenv"
fi
edebug "loading $DKBUILD"
source "$DKBUILD"
}
function load_environ() {
declare -g -A _ENVIRON
eval "$(declare -p -x | sed -r 's/^declare -x ([^=]+)=/_ENVIRON[\1]=/')"
}
function from_glob() {
local var=value value
[[ "$1" != *=* ]] && { var="$1"; shift; }
local path extract add_prefix add_suffix
while [ $# -gt 0 ]; do
case "$1" in
path=*) path="${1#path=}";;
extract=*) extract="${1#extract=}";;
add-prefix=*) add_prefix="${1#add-prefix=}";;
add-suffix=*) add_suffix="${1#add-suffix=}";;
*=*) ewarn "path: $1: argument ignoré";;
*) break;;
esac
shift
done
value="$(list_all . "$path" | sort -rn | head -1)"
[ -n "$extract" ] || extract="${path//\*/(.*)}"
if [ -n "$extract" ]; then
extract="${extract//\//\\/}"
value="$add_prefix$(awk -v version="$value" "BEGIN {
if (match(version, /$extract/, vs)) { print vs[1] }
else { print version }
}")$add_suffix"
fi
local "$var"; upvar "$var" "$value"
}
function define_functions_env() {
function setversion() {
[ -n "$SETVERSION_DONE" ] && return
# sans argument, retourner 0
[ $# -eq 0 ] && return
local from_file from_repo from_glob extract add_prefix add_suffix
while [ $# -gt 0 ]; do
case "$1" in
from-file|file) from_file=.;;
from-file=*|file=*)
from_file="${1#from-}"; from_file="${from_file#file=}"
;;
from-repo|repo) from_repo=.;;
from-repo=*|repo=*)
from_repo="${1#from-}"; from_repo="${from_repo#repo=}"
;;
from-glob=*|glob=*)
from_glob="${1#from-}"; from_glob="${from_glob#glob=}"
;;
extract=*) extract="${1#extract=}";;
add-prefix=*) add_prefix="${1#add-prefix=}";;
add-suffix=*) add_suffix="${1#add-suffix=}";;
*=*) ewarn "setversion: $1: argument ignoré";;
*) break;;
esac
shift
done
if [ -n "$from_file" ]; then
if [ -d "$from_file" ]; then
setx SETVERSION=pver --sw "$from_file" || die
elif [[ "$from_file" == *.xml ]]; then
setx SETVERSION=pver -E "$from_file" || die
else
setx SETVERSION=pver -F "$from_file" || die
fi
elif [ -n "$from_repo" ]; then
die "setversion from-repo: pas encore implémenté" #XXX
elif [ -n "$from_glob" ]; then
SETVERSION="$(list_all . "$from_glob" | sort -rn | head -1)"
[ -n "$extract" ] || extract="${from_glob//\*/(.*)}"
else
SETVERSION="$1"
fi
if [ -n "$extract" ]; then
extract="${extract//\//\\/}"
SETVERSION="$add_prefix$(awk -v version="$SETVERSION" "BEGIN {
if (match(version, /$extract/, vs)) { print vs[1] }
else { print version }
}")$add_suffix"
fi
SETVERSION_DONE=1
}
function dist() {
local dist version latest
for dist in "$@"; do
if [ "$dist" == LATEST ]; then
latest=1
continue
fi
parse_dist "$dist" dist version
[ "$dist" == "$DIST" ] || continue
[ -z "$version" -o "$version" == "$DVERSION" ] || continue
return 0
done
if [ -n "$latest" ]; then
# trouver la dernière occurence de la première distribution
# mentionnée. en effet, les versions doivent être ordonnées de la
# plus ancienne à la plus récente.
local first_dist last_version
for dist in "${SETDISTS[@]}"; do
parse_dist "$dist" dist version
if [ -z "$first_dist" ]; then
first_dist="$dist"
last_version="$version"
elif [ "$dist" == "$first_dist" ]; then
last_version="$version"
fi
done
if [ "$first_dist" == "$DIST" ]; then
if [ -z "$last_version" -o "$last_version" == "$DVERSION" ]; then
return 0
fi
fi
fi
return 1
}
function version() {
local version
for version in "$@"; do
[ "$version" == "$VERSION" ] && return 0
done
return 1
}
function profile() {
local profile version
for profile in "$@"; do
[ "$profile" == DEFAULT ] && profile="${SETPROFILES[0]}"
parse_profile "$profile" profile version
[ "$profile" == "$PROFILE" ] || continue
[ -z "$version" -o "$version" == "$PVERSION" ] || continue
return 0
done
return 1
}
declare -g -A ENVIRON
function setenv() {
local name value
for name in "$@"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value="${_ENVIRON[$name]-__UNDEFINED__}"
[ "$value" == __UNDEFINED__ ] && die "la variable d'environnement $name doit être définie"
fi
if [ "${ENVIRON[$name]-__UNDEFINED__}" == __UNDEFINED__ ]; then
# Ne spécifier la valeur que si elle n'a pas déjà été définie
_ENVIRON["$name"]="$value"
ENVIRON["$name"]="$value"
[ -z "${PROTECTED_VARS[$name]}" ] && _setv "export $name" "$value"
fi
done
}
function resetenv() {
local name value
for name in "$@"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value="${_ENVIRON[$name]-__UNDEFINED__}"
[ "$value" == __UNDEFINED__ ] && die "la variable d'environnement $name doit être définie"
fi
_ENVIRON["$name"]="$value"
ENVIRON["$name"]="$value"
[ -z "${PROTECTED_VARS[$name]}" ] && _setv "export $name" "$value"
done
}
declare -g -A ARGS
function setarg() {
local name value
for name in "$@"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value="${_ENVIRON[$name]-__UNDEFINED__}"
[ "$value" == __UNDEFINED__ ] && die "la variable d'environnement $name doit être définie"
fi
if [ "${ARGS[$name]-__UNDEFINED__}" == __UNDEFINED__ ]; then
# Ne spécifier la valeur que si elle n'a pas déjà été définie
ARGS["$name"]="$value"
fi
done
}
function resetarg() {
local name value
for name in "$@"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value="${_ENVIRON[$name]-__UNDEFINED__}"
[ "$value" == __UNDEFINED__ ] && die "la variable d'environnement $name doit être définie"
fi
ARGS["$name"]="$value"
done
}
declare -g -A DEFAULTS
function default() {
local command="$1"; shift
local name value
for name in "$@"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value=1
fi
name="${command}_$name"
if [ "${DEFAULTS[$name]-__UNDEFINED__}" == __UNDEFINED__ ]; then
# Ne spécifier la valeur que si elle n'a pas déjà été définie
DEFAULTS["$name"]="$value"
fi
done
}
function resetdefault() {
local command="$1"; shift
local name value
for name in "$@"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value=1
fi
name="${command}_$name"
DEFAULTS["$name"]="$value"
done
}
}
function parse_dist() {
local dist="$1" version
if [[ "$dist" == *-* ]]; then
version="${dist%-*}"
dist="${dist##*-}"
fi
local "${2:-dist}"; upvar "${2:-dist}" "$dist"
local "${3:-version}"; upvar "${3:-version}" "$version"
}
function parse_profile() {
local profile="$1" version
if [[ "$profile" == *-* ]]; then
version="${profile%-*}"
profile="${profile##*-}"
fi
local "${2:-profile}"; upvar "${2:-profile}" "$profile"
local "${3:-version}"; upvar "${3:-version}" "$version"
}
function resolve_dists_profiles() {
## construire d'abord la liste des distributions et profils
edebug "Calcul de la liste des distributions et des profils"
reset_functions
SETDISTS_DONE=
SETDISTS=()
SETPROFILES_DONE=
SETPROFILES=()
SETVERSION_DONE=
SETVERSION=
AUTOBUILD=1
function setdists() {
[ -n "$SETDISTS_DONE" ] && return
SETDISTS=("$@")
SETDISTS_DONE=1
}
function setprofiles() {
[ -n "$SETPROFILES_DONE" ] && return
SETPROFILES=("$@")
SETPROFILES_DONE=1
}
function build() {
AUTOBUILD=
}
function cbuild() {
AUTOBUILD=
}
load_dkbuild
## ensuite vérifier si on est dans la bonne distribution
edebug "Calcul de la distribution courante"
reset_functions
DISTS=()
function setdists() {
local dist version found
# construire la liste des distributions à considérer
if [ -n "$DIST" ]; then
# on a spécifié une distribution en argument
for dist in "${SETDISTS[@]}"; do
if [ "$dist" == "$DIST" ]; then
# matcher avec la version éventuellement
found=1
break
fi
parse_dist "$dist"
if [ "$dist" == "$DIST" ]; then
# ou matcher uniquement la distribution
found=1
break
fi
done
# si aucune distribution ne correspond, arrêter le script
[ -n "$found" ] || exit 0
# forcer à ne construire que cette distribution
DISTS=("$DIST")
else
DISTS=("${SETDISTS[@]}")
fi
}
load_dkbuild
## puis vérifier si on est dans le bon profil
edebug "Calcul du profil courant"
reset_functions
PROFILES=()
function setprofiles() {
local profile version found
# construire la liste des distributions à considérer
if [ -n "$PROFILE" ]; then
# on a spécifié une distribution en argument
for profile in "${SETPROFILES[@]}"; do
if [ "$profile" == "$PROFILE" ]; then
# matcher avec la version éventuellement
found=1
break
fi
parse_profile "$profile"
if [ "$profile" == "$PROFILE" ]; then
# ou matcher uniquement la distribution
found=1
break
fi
done
# si aucune distribution ne correspond, arrêter le script
[ -n "$found" ] || die "$PROFILE: profil invalide"
# forcer à ne construire que cette distribution
PROFILES=("$PROFILE")
else
PROFILES=("${SETPROFILES[@]}")
fi
}
load_dkbuild
## Si pas de distribution ou de profil, remplacer par valeur vide
if [ ${#DISTS[*]} -eq 0 ]; then
SETDISTS=("")
DISTS=("")
fi
if [ ${#PROFILES[*]} -eq 0 ]; then
SETPROFILES=("")
PROFILES=("")
fi
## puis calculer la version par défaut
edebug "Calcul de la version par défaut"
reset_functions
define_functions_env
load_dkbuild
}
function foreach_dists_profiles() {
local each="$1" before="$2" after="$3"
local version dist dversion profile pversion
local VERSION DIST DVERSION PROFILE PVERSION
local HAVE_VERSION LAST_VERSION
declare -A dones
if [ -n "$before" ]; then
"$before"
fi
for dist in "${DISTS[@]}"; do
parse_dist "$dist" dist dversion
# y a-t-il une version dans cette distribution ou ce profil, et si oui, laquelle?
HAVE_VERSION=
LAST_VERSION=
for DIST in "${SETDISTS[@]}"; do
parse_dist "$DIST" DIST DVERSION
[ "$DIST" == "$dist" ] || continue
VERSION="$DVERSION"
for profile in "${PROFILES[@]}"; do
parse_profile "$profile" profile pversion
for PROFILE in "${SETPROFILES[@]}"; do
parse_profile "$PROFILE" PROFILE PVERSION
[ "$PROFILE" == "$profile" ] || continue
[ -n "$DVERSION" ] && PVERSION= || VERSION="$PVERSION"
[ -z "$VERSION" -a -n "$SETVERSION" ] && VERSION="$SETVERSION"
if [ -n "$VERSION" ]; then
HAVE_VERSION=1
LAST_VERSION="$VERSION"
fi
done
done
done
for DIST in "${SETDISTS[@]}"; do
parse_dist "$DIST" DIST DVERSION
[ "$DIST" == "$dist" ] || continue
[ -z "$dversion" -o "$DVERSION" == "$dversion" ] || continue
VERSION="$DVERSION"
for profile in "${PROFILES[@]}"; do
parse_profile "$profile" profile pversion
for PROFILE in "${SETPROFILES[@]}"; do
parse_profile "$PROFILE" PROFILE PVERSION
[ "$PROFILE" == "$profile" ] || continue
if [ -z "$DVERSION" ]; then
[ -z "$pversion" -o "$PVERSION" == "$pversion" ] || continue
VERSION="$PVERSION"
else
PVERSION=
fi
[ -n "${dones[$PVERSION-$PROFILE-$DVERSION-$DIST]}" ] && continue
dones["$PVERSION-$PROFILE-$DVERSION-$DIST"]=1
[ -z "$VERSION" -a -n "$SETVERSION" ] && VERSION="$SETVERSION"
"$each"
done
done
done
done
if [ -n "$after" ]; then
"$after"
fi
}
function define_functions_cmd() {
_IN_SECTION=
function section() {
[ -n "$_IN_SECTION" ] && eend
etitle "$*"
_IN_SECTION=1
}
function note() {
enote "$*"
}
function info() {
estep "$*"
}
function debug() {
edebug "$*"
}
function checkout() {
edebug "checkout $(qvals "$@")"
local url destdir
[[ "$1" != *=* ]] && { url="$1"; shift; }
[[ "$1" != *=* ]] && { destdir="$1"; shift; }
local checkout="${DEFAULTS[checkout_checkout]-1}"
local origin="${DEFAULTS[checkout_origin]}"
local branch="${DEFAULTS[checkout_branch]}"
local develdir="${DEFAULTS[checkout_develdir]}"
local develtype="${DEFAULTS[checkout_develtype]}"
while [ $# -gt 0 ]; do
case "$1" in
checkout) checkout=1;;
checkout=*) checkout="${1#checkout=}";;
origin=*) origin="${1#origin=}";;
branch=*) branch="${1#branch=}";;
develdir=*) develdir="${1#develdir=}";;
develtype=*) develtype="${1#develtype=}";;
*) ewarn "checkout: $1: argument ignoré";;
esac
shift
done
[ -n "$checkout" ] || return
[ -n "$url" -a -n "$destdir" ] || die "checkout: Vous devez spécifier l'url du dépôt et la destination"
[ -n "$origin" ] || origin=origin
if [ -z "$branch" ]; then
case "$PROFILE" in
test|devel) branch=develop;;
*) branch=master;;
esac
fi
if [ "$checkout" == devel ]; then
# synchronisation depuis le répertoire de développement
[ -n "$develdir" ] || die "checkout: vous devez spécifier le répertoire de développement"
[ -d "$develdir" ] || die "checkout: répertoire de développement introuvable"
die "Pas encore implémenté" #XXX
elif [ -d "$destdir" -a -d "$destdir/.git" ]; then
# maj du dépôt
local cwd="$(pwd)"
estep "checkout: maj du dépôt $url --> $destdir (origin=$origin, branch=$branch)"
cd "$destdir"
git fetch --all -p -f || die
if [ "${branch#^}" != "$branch" ]; then
git reset --hard "${branch#^}" || die
else
git reset --hard "$origin/$branch" || die
fi
cd "$cwd"
else
# reliquat checkout=devel?
[ -d "$destdir" ] && rm -rf "$destdir"
# clone
estep "checkout: clone du dépôt $url --> $destdir (origin=$origin, branch=$branch)"
if [ "${BRANCH#^}" != "$BRANCH" ]; then
local cwd="$(pwd)"
git clone -o "$origin" "$url" "$destdir" || die
cd "$destdir"
git reset --hard "${branch#^}" || die
cd "$cwd"
else
git clone -o "$origin" -b "$branch" "$url" "$destdir" || die
fi
fi
}
function copy() {
edebug "copy $(qvals "$@")"
local src dest
[[ "$1" != *=* ]] && { src="$1"; shift; }
[[ "$1" != *=* ]] && { dest="$1"; shift; }
local copy="${DEFAULTS[copy_copy]-1}"
local overwrite="${DEFAULTS[copy_overwrite]}"
local gitignore="${DEFAULTS[copy_gitignore]}"
while [ $# -gt 0 ]; do
case "$1" in
copy) copy=1;;
copy=*) copy="${1#copy=}";;
overwrite) overwrite=1;;
overwrite=*) overwrite="${1#overwrite=}";;
gitignore=*) gitignore="${1#gitignore=}";;
*) ewarn "copy: $1: argument ignoré";;
esac
shift
done
[ -n "$copy" ] || return
[ -n "$src" -a -n "$dest" ] || die "copy: Vous devez spécifier la source et la destination de la copie"
[ -e "$src" ] || {
ewarn "copy: $src: fichier ou répertoire introuvables"
return 1
}
local srcdir destdir
if [ "${src%/}" != "$src" ]; then
[ -d "$src" ] || die "copy: $src: doit être un répertoire"
setx srcdir=abspath "$src"
src=
elif [ -d "$src" ]; then
setx srcdir=abspath "$src"
src=
else
setx src=abspath "$src"
fi
if [ "${dest%/}" != "$dest" ]; then
[ -f "$dest" ] && die "copy: $dest: doit être un répertoire"
setx destdir=abspath "$dest"
dest=
elif [ -d "$dest" ]; then
setx destdir=abspath "$dest"
dest=
elif [ -f "$dest" ]; then
[ -n "$srcdir" ] && die "copy: $dest: doit être un répertoire"
setx dest=abspath "$dest"
elif [ -n "$srcdir" ]; then
setx destdir=abspath "$dest"
dest=
else
setx dest=abspath "$dest"
fi
local -a srcs dests
if [ -n "$srcdir" -a -n "$destdir" ]; then
# copie de répertoire à répertoire
local destpath="$(relpath "$destdir" "$PROJDIR")"
[ -n "$destpath" ] || destpath=.
estep "copy $(relpath "$srcdir" "$PROJDIR")/ --> $destpath/"
array_from_lines srcs "$(find "$srcdir/" -type f -o -type l)"
for src in "${srcs[@]}"; do
dest="$destdir/${src#$srcdir/}"
if [ -n "$overwrite" -o ! -f "$dest" ]; then
mkdirof "$dest" || die
cp -dfl "$src" "$dest" || die
dests+=("$dest")
fi
done
elif [ -n "$src" ]; then
# transformer copie de fichier à répertoire en copie de fichier à fichier
[ -n "$dest" ] || dest="$destdir/$(basename "$src")"
if [ -n "$overwrite" -o ! -f "$dest" ]; then
# copie de fichier à fichier
estep "copy $(relpath "$src" "$PROJDIR") --> $(relpath "$dest" "$PROJDIR")"
mkdirof "$dest" || die
cp -fl "$src" "$dest" || die
setx destdir=dirname "$dest"
dests+=("$dest")
fi
else
# en réalité, on ne devrait jamais arriver ici
die "copy: impossible de copier un répertoire dans un fichier"
fi
if [ -n "$gitignore" ]; then
setx gitignore=abspath "$gitignore"
[ -d "$gitignore" ] && gitignore="$gitignore/.gitignore"
local basedir
setx basedir=dirname "$gitignore"
if [ "${destdir#$basedir/}" == "$destdir" -a "$destdir" != "$basedir" ]; then
ewarn "copy: gitignore ignoré parce que le répertoire n'est pas un parent de destdir"
else
[ -f "$gitignore" ] || { mkdir -p "$basedir"; touch "$gitignore"; }
declare -A ignored_dirs
local ignored_dir
for dest in "${dests[@]}"; do
dest="/${dest#$basedir/}"
if grep -q "^$dest\$" "$gitignore"; then
ignored=1
else
ignored=
setx ignored_dir=dirname "$dest"
while [ "$ignored_dir" != / ]; do
ignored="${ignored_dirs[$ignored_dir/]-compute}"
if [ "$ignored" == compute ]; then
grep -q "^$ignored_dir/\$" "$gitignore" && ignored=1 || ignored=
ignored_dirs["$ignored_dir/"]="$ignored"
fi
if [ -n "$ignored" ]; then
# un répertoire parent est déjà ignoré, on peut
# passer au fichier suivant
break
fi
setx ignored_dir=dirname "$ignored_dir"
done
fi
if [ -z "$ignored" ]; then
# le fichier n'est pas ignoré, ni directement, ni via un
# répertoire parent. il faut donc l'ajouter à .gitignore
echo "$dest" >>"$gitignore"
fi
done
fi
fi
}
function genfile() {
edebug "genfile $(qvals "$@")"
local output input
[[ "$1" != *=* ]] && { output="$1"; shift; }
[[ "$1" != *=* ]] && { input="$1"; shift; }
local context="${DEFAULTS[genfile_context]}"
local sed="${DEFAULTS[genfile_sed]}"
while [ $# -gt 0 ]; do
case "$1" in
context=*) context="${1#context=}";;
sed=*) sed="${1#sed=}";;
*) ewarn "genfile: $1: argument ignoré";;
esac
shift
done
[ -n "$output" ] || die "genfile: Vous devez spécifier le fichier en sortie"
if [ -n "$context" ]; then
mkdir -p "$context" || die
output="$context/$output"
fi
if [ -n "$input" ]; then
cat "$input" >"$output" || die
elif ! tty -s; then
cat >"$output"
else
die "genfile: Vous devez spécifier une source pour le fichier $output"
fi
if [ -n "$sed" ]; then
sed -i "$sed" "$output"
fi
}
function dockerfile() {
edebug "dockerfile $(qvals "$@")"
local input
[[ "$1" != *=* ]] && { DOCKERFILE="$1"; shift; }
[[ "$1" != *=* ]] && { input="$1"; shift; }
local context="${DEFAULTS[dockerfile_context]}"
local sed="${DEFAULTS[dockerfile_sed]}"
while [ $# -gt 0 ]; do
case "$1" in
context=*) context="${1#context=}";;
sed=*) sed="${1#sed=}";;
*) ewarn "dockerfile: $1: argument ignoré";;
esac
shift
done
[ -n "$DOCKERFILE" ] || DOCKERFILE=Dockerfile
DOCKERCONTEXT=.
if [ -n "$context" ]; then
mkdir -p "$context" || die
DOCKERCONTEXT="$context"
DOCKERFILE="$context/$DOCKERFILE"
fi
setx DOCKERFILE=abspath "$DOCKERFILE"
if [ -n "$input" ]; then
cat "$input" >"$DOCKERFILE" || die
elif ! tty -s; then
cat >"$DOCKERFILE" || die
else
echo "# -*- coding: utf-8 mode: dockerfile -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8" >"$DOCKERFILE"
fi
if [ -n "$sed" ]; then
sed -i "$sed" "$DOCKERFILE"
fi
}
function add2dockerfile() {
edebug "$(qvals "$@")"
[ -n "$DOCKERFILE" ] || return
echo "$*" >>"$DOCKERFILE"
}
function FROM() { add2dockerfile FROM "$@"; }
function RUN() { add2dockerfile RUN "$@"; }
function CMD() { add2dockerfile CMD "$@"; }
function LABEL() { add2dockerfile LABEL "$@"; }
function MAINTAINER() { add2dockerfile MAINTAINER "$@"; }
function EXPOSE() { add2dockerfile EXPOSE "$@"; }
function ENV() { add2dockerfile ENV "$@"; }
function ADD() { add2dockerfile ADD "$@"; }
function COPY() { add2dockerfile COPY "$@"; }
function ENTRYPOINT() { add2dockerfile ENTRYPOINT "$@"; }
function VOLUME() { add2dockerfile VOLUME "$@"; }
function USER() { add2dockerfile USER "$@"; }
function WORKDIR() { add2dockerfile WORKDIR "$@"; }
function ARG() { add2dockerfile ARG "$@"; }
function ONBUILD() { add2dockerfile ONBUILD "$@"; }
function STOPSIGNAL() { add2dockerfile STOPSIGNAL "$@"; }
function HEALTHCHECK() { add2dockerfile HEALTHCHECK "$@"; }
function SHELL() { add2dockerfile SHELL "$@"; }
function build() {
edebug "build $(qvals "$@")"
local build="${DEFAULTS[build_build]-1}"
local no_cache="${DEFAULTS[build_no-cache]}"
local pull="${DEFAULTS[build_pull]}"
local host_mappings="${DEFAULTS[build_host-mappings]-__UNDEFINED__}"
[ "$host_mappings" == __UNDEFINED__ ] && host_mappings="${DEFAULTS[docker_host-mappings]}"
local set_tags="${DEFAULTS[build_set-tags]}"
local add_tags="${DEFAULTS[build_add-tags]}"
local images="${DEFAULTS[build_images]}"
local push="${DEFAULTS[build_push]}"
while [ $# -gt 0 ]; do
case "$1" in
build) build=1;;
build=*) build="${1#build=}";;
context=*) DOCKERCONTEXT="${1#context=}";;
dockerfile=*) DOCKERFILE="${1#dockerfile=}";;
no-cache) no_cache=1;;
no-cache=*) no_cache="${1#no-cache=}";;
pull) pull=1;;
pull=*) pull="${1#pull=}";;
host-mappings=*) host_mappings="${1#host-mappings=}";;
set-tag=*) set_tags="${1#set-tag=}";;
set-tags=*) set_tags="${1#set-tags=}";;
add-tag=*) add_tags="${1#add-tag=}";;
add-tags=*) add_tags="${1#add-tags=}";;
image=*) images="${1#image=}";;
images=*) images="${1#images=}";;
push) push=1;;
push=*) push="${1#push=}";;
*) ewarn "build: $1: argument ignoré";;
esac
shift
done
estep "build options:" ${build:+build} ${no_cache:+no-cache} ${pull:+pull} ${push:+push}
[ -n "$images" ] || images="$IMAGE"
eval "set_tags=($set_tags)"
eval "add_tags=($add_tags)"
eval "images=($images)"
local tag imagetag autotag=1
local -a imagetags
if [ ${#set_tags[*]} -gt 0 ]; then
autotag=
for imagetag in "${images[@]}"; do
if [[ "$imagetag" == *:* ]]; then
# le tag est déjà spécifié
imagetags+=("$imagetag")
else
for tag in "${set_tags[@]}" "${add_tags[@]}"; do
imagetags+=("$imagetag:$tag")
done
fi
done
else
for imagetag in "${images[@]}"; do
if [[ "$imagetag" == *:* ]]; then
# le tag est déjà spécifié
autotag=
imagetags+=("$imagetag")
else
for tag in "${add_tags[@]}"; do
imagetags+=("$imagetag:$tag")
done
[ -n "$VERSION" ] && imagetags+=("$imagetag:$VERSION-$DIST")
[ -n "$DIST" -a -z "$HAVE_VERSION" ] && imagetags+=("$imagetag:$DIST")
fi
done
fi
if [ -n "$autotag" ]; then
if [ -n "$DIST" ]; then
if [ -z "$HAVE_VERSION" ]; then
dist LATEST && imagetags+=("$imagetag:latest")
elif [ "$VERSION" == "$LAST_VERSION" ]; then
imagetags+=("$imagetag:$DIST")
dist LATEST && imagetags+=("$imagetag:latest")
fi
elif [ -n "$PROFILE" ]; then
profile DEFAULT && imagetags+=("$imagetag:latest")
else
imagetags+=("$imagetag:latest")
fi
fi
local avar
local -a args; args=(
${no_cache:+--no-cache}
${pull:+--pull}
)
eval "host_mappings=($host_mappings)"
for host_mapping in "${host_mappings[@]}"; do
args+=(--add-host "$host_mapping")
done
for avar in "${!ARGS[@]}"; do
args+=(--build-arg "$avar=${ARGS[$avar]}")
done
for imagetag in "${imagetags[@]}"; do
args+=(-t "$imagetag")
estep "tag $imagetag"
done
[ -n "$DOCKERCONTEXT" ] || DOCKERCONTEXT="${DEFAULTS[build_context]:-.}"
[ -n "$DOCKERFILE" ] || DOCKERFILE="${DEFAULTS[build_dockerfile]:-Dockerfile}"
if [ -n "$build" ]; then
etitle build
_runcmd docker build "${args[@]}" -f "$DOCKERFILE" "$DOCKERCONTEXT" || die
eend
fi
if [ -n "$push" ]; then
etitle push
for imagetag in "${imagetags[@]}"; do
_runcmd docker push "$imagetag" || die
done
eend
fi
DOCKERCONTEXT=
DOCKERFILE=
[ -n "$build" -o -n "$push" ]
}
function cbuild() {
edebug "cbuild $(qvals "$@")"
local files="${DEFAULTS[cbuild_files]}"
local project_name="${DEFAULTS[cbuild_project-name]}"
local no_cache="${DEFAULTS[cbuild_no-cache]}"
local pull="${DEFAULTS[cbuild_pull]}"
while [ $# -gt 0 ]; do
case "$1" in
files=*) files="${1#files=}";;
project-name=*) project_name="${1#project-name=}";;
no-cache) no_cache=1;;
no-cache=*) no_cache="${1#no-cache=}";;
pull) pull=1;;
pull=*) pull="${1#pull=}";;
*=*) ewarn "cbuild: $1: argument ignoré";;
*) break;;
esac
shift
done
if [ -n "$files" ]; then
eval "files=($files)"
else
files=(docker-compose.yml)
if [ -f "docker-compose.override.yml" ]; then
files+=("docker-compose.override.yml")
fi
if [ -n "$PROFILE" -a -f "docker-compose.$PROFILE.yml" ]; then
files+=("docker-compose.$PROFILE.yml")
fi
fi
[ -n "$project_name" ] || setx project_name=get_project_name
estep "cbuild files:" ${files[@]}
estep "cbuild options:" project_name="$project_name" ${no_cache:+no-cache} ${pull:+pull}
local file; local -a args
args=(
-p "$project_name"
)
for file in "${files[@]}"; do
args+=(-f "$file")
done
local avar evar; local -a bargs
bargs=(
${no_cache:+--no-cache}
${pull:+--pull}
)
for avar in "${!ARGS[@]}"; do
bargs+=(--build-arg "$avar=${ARGS[$avar]}")
done
if [ -f .shared_env -o -f ".${MACHINE}_env" -o ${#ENVIRON[*]} -gt 0 ]; then
echo >.env "## fichier auto-généré. ne pas modifier ##"
fi
[ -f .shared_env ] && cat .shared_env >>.env
[ -f ".${MACHINE}_env" ] && cat ".${MACHINE}_env" >>.env
for evar in "${!ENVIRON[@]}"; do
echo_setv "$evar=${ENVIRON[$evar]}" >>.env
done
_runcmd docker-compose "${args[@]}" build "${bargs[@]}" "$@" || die
}
function _local_composer() {
case "$action" in
rootshell|rshell|rootbash|rbash)
shift
estep "Lancement d'un shell root"
sudo bash "$@"
return $?
;;
usershell|shell|userbash|bash)
shift
estep "Lancement d'un shell utilisateur"
bash "$@"
return $?
;;
*)
estep "composer $action"
if [ -n "$composer" ]; then :
elif [ -x composer.phar ]; then composer=./composer.phar
elif [ -x /usr/bin/composer ]; then composer=/usr/bin/composer
else die "Impossible de trouver composer"
fi
"$composer" "$action" $args "$@"
esac
}
function _docker_composer() {
local user group projdir actualcmd
setx user=id -un; setx user=getent passwd "$user"
setx group=id -gn; setx group=getent group "$group"
setx projdir=pwd
case "$action" in
rootshell|rshell|rootbash|rbash)
action=rshell
shift
actualcmd='eval "bash $args"'
;;
usershell|shell|userbash|bash)
action=shell
shift
actualcmd='eval "su-exec \"$user\" bash $args"'
;;
*)
actualcmd='eval "su-exec \"$user\" \"$composer\" $args"'
args="$action${args:+ $args}"
;;
esac
setx args=qvals $args "$@"
local -a basecmd setupscript runscript cmd
basecmd=(
-e user="$user"
-e group="$group"
-e projdir="$projdir"
-e setup="$setup"
-e composer="$composer"
-e args="$args"
)
eval "host_mappings=($host_mappings)"
for host_mapping in "${host_mappings[@]}"; do
basecmd+=(--add-host "$host_mapping")
done
basecmd+=(-v "$HOME:$HOME")
if [ "${projdir#$HOME/}" == "$projdir" ]; then
# si le répertoire de projet ne se trouve pas dans $HOME, le monter aussi
cmd+=(-v "$projdir:$projdir")
fi
setupscript='eval "$setup"'
runscript='
echo "$user" >>/etc/passwd; user="${user%%:*}"
echo "$group" >>/etc/group; group="${group%%:*}"
cd "$projdir"
if [ -n "$composer" ]; then :
elif [ -x composer.phar ]; then composer=./composer.phar
elif [ -x /usr/bin/composer ]; then composer=/usr/bin/composer
else
echo "ERROR: Impossible de trouver composer"
exit 1
fi
'"$actualcmd"
if [ -n "$setup" ]; then
local project_name container_name ctid
# lancement dans un container docker à préparer
[ -n "$project_name" ] || setx project_name=get_project_name
setx container_name=get_container_name "$project_name"
container_name="dkbuild_composer_${container_name}"
# vérifier l'existence de l'image
setx ctid=docker image ls --format '{{.ID}}' "${container_name}_image"
# créer le container le cas échéant
if [ -z "$ctid" ]; then
estep "Création du container $container_name à partir de l'image $image"
cmd=(
docker create -it --name "${container_name}_ct"
"${basecmd[@]}"
"$image"
bash -c "$setupscript"
)
setx ctid="${cmd[@]}" &&
docker container start -ai "$ctid" &&
docker container commit "$ctid" "${container_name}_image" &&
docker container rm "$ctid" || die
fi
# prendre comme image le container créé
image="${container_name}_image"
fi
case "$action" in
rshell) estep "Lancement d'un shell root (avec l'image $image)";;
shell) estep "Lancement d'un shell utilisateur (avec l'image $image)";;
*) estep "composer $action (avec l'image $image)";;
esac
cmd=(
docker run -it --rm
"${basecmd[@]}"
"$image"
bash -c "$runscript"
)
"${cmd[@]}"
}
function composer() {
edebug "composer $(qvals "$@")"
[ $# -eq 0 ] && return 0
local action destdir
[[ "$1" != *=* ]] && { destdir="$1"; shift; }
[[ "$1" != *=* ]] && { action="$1"; shift; }
[ -n "$destdir" ] || destdir=.
[ -d "$destdir" ] || die "composer: $destdir: répertoire introuvable"
local cwd="$(pwd)"
cd "$destdir" || die
[ -n "$action" ] || action=install
[ "$action" == none ] && return
local args
case "$PROFILE" in
prod|test) args="${DEFAULTS[composer_args]---no-dev -o}";;
*) args="${DEFAULTS[composer_args]}";;
esac
local php="${DEFAULTS[composer_php]}"
local php_max="${DEFAULTS[composer_php-max]}"
local image="${DEFAULTS[composer_image]}"
local machine="${DEFAULTS[composer_machine]}"
local host_mappings="${DEFAULTS[composer_host-mappings]-__UNDEFINED__}"
[ "$host_mappings" == __UNDEFINED__ ] && host_mappings="${DEFAULTS[docker_host-mappings]}"
local composer="${DEFAULTS[composer_composer]}"
local setup="${DEFAULTS[composer_setup]}"
local project_name="${DEFAULTS[composer_project-name]}"
if [ -f "$destdir/.composer.conf" ]; then
eval "$(
COMPOSER_PHP=
COMPOSER_PHP_MAX=
COMPOSER_IMAGE="$COMPOSER_IMAGE"
COMPOSER_MACHINE=-u
COMPOSER_CMD=
COMPOSER_SETUP=
source "$destdir/.composer.conf"
[ -z "$php" ] && echo_setv php="$COMPOSER_PHP"
[ -z "$php_max" ] && echo_setv php_max="$COMPOSER_PHP_MAX"
[ -z "$image" ] && echo_setv image="$COMPOSER_IMAGE"
[ -z "$machine" ] && echo_setv machine="$COMPOSER_MACHINE"
[ -z "$composer" ] && echo_setv composer="$COMPOSER_CMD"
[ -z "$setup" ] && echo_setv setup="$COMPOSER_SETUP"
)"
fi
while [ $# -gt 0 ]; do
case "$1" in
args=*) args="${1#args=}";;
php=*) php="${1#php=}";;
php-max=*) php_max="${1#php-max=}";;
image=*) image="${1#image=}";;
machine=*) machine="${1#machine=}";;
host-mappings=*) host_mappings="${1#host-mappings=}";;
composer=*) composer="${1#composer=}";;
setup=*) setup="${1#setup=}";;
project-name=*) project_name="${1#project-name=}";;
*=*) ewarn "composer: $1: argument ignoré";;
*) break;;
esac
shift
done
local use_image
if [ "$php" == force -o "$php" == any ]; then
use_image=1
elif [ "$php" == none -o "$php" == system ]; then
php=none
use_image=
elif [ -n "$php_max" -a "$php_max" != none ]; then
# Vérifier la version de PHP
php -r '
$version = $argv[1];
if (strpos($version, ".") !== false) {
$version = explode(".", $version);
$version = $version[0] * 10000 + $version[1] * 100 + (isset($version[2])? $version[2]: 0);
}
exit((PHP_VERSION_ID > $version)? 0: 1);
' -- "$php_max"
case $? in
0) use_image=1;;
1) use_image=;;
*) ewarn "Erreur lors du lancement de PHP: est-il installé? Sinon, utilisez php=any";;
esac
fi
if [ -n "$use_image" -o "$php" == none ]; then
: # ok, on a déjà décidé
elif [ -z "$php" ]; then
# pas de version minimum, tester simplement la valeur de image
[ "$image" != none ] && use_image=1
else
# Vérifier la version de PHP
php -r '
$version = $argv[1];
if (strpos($version, ".") !== false) {
$version = explode(".", $version);
$version = $version[0] * 10000 + $version[1] * 100 + (isset($version[2])? $version[2]: 0);
}
exit((PHP_VERSION_ID < $version)? 0: 1);
' -- "$php"
case $? in
0) use_image=1;;
1) use_image=;;
*) ewarn "Erreur lors du lancement de PHP: est-il installé? Sinon, utilisez php=any";;
esac
fi
if [ -n "$use_image" ]; then
local orig_machine
[ "$image" != none ] || die "Vous devez spécifier l'image à utiliser pour composer"
if [ -n "$machine" -a "$machine" != current -a "$DOCKER_MACHINE_NAME" != "$machine" ]; then
orig_machine="$DOCKER_MACHINE_NAME"
set_machine "$machine"
fi
_docker_composer "$@"
if [ -n "$_orig_machine" ]; then
set_machine "$orig_machine"
fi
else
_local_composer "$@"
fi
# restaurer le répertoire courant
cd "$cwd"
}
function _local_mvn() {
if [ -n "$java" ]; then
urequire java
select_java_exact "$java" || die
export MVN_JAVA_VERSION="$java"
fi
case "$action" in
rootshell|rshell|rootbash|rbash)
shift
estep "Lancement d'un shell root"
sudo bash "$@"
return $?
;;
usershell|shell|userbash|bash)
shift
estep "Lancement d'un shell utilisateur"
bash "$@"
return $?
;;
java)
shift
estep "java"
java "$@"
;;
*)
estep "mvn $action"
[ -n "$mvn" ] || setx mvn=which mvn 2>/dev/null
[ -n "$mvn" ] || die "mvn: commande introuvable"
set -- "$action" $args "$@"
case "$1" in
install) set clean package install "${@:2}";;
package) set clean package "${@:2}";;
package_only) set package "${@:2}";;
esac
"$mvn" "$@"
esac
}
function _docker_mvn() {
local user group projdir actualcmd
setx user=id -un; setx user=getent passwd "$user"
setx group=id -gn; setx group=getent group "$group"
setx projdir=pwd
case "$action" in
rootshell|rshell|rootbash|rbash)
action=rshell
shift
actualcmd='eval "bash $args"'
;;
usershell|shell|userbash|bash)
action=shell
shift
actualcmd='eval "su-exec \"$user\" bash $args"'
;;
java)
shift
actualcmd='eval "su-exec \"$user\" java $args"'
;;
*)
actualcmd='eval "su-exec \"$user\" \"$mvn\" $args"'
set -- "$action" $args "$@"
case "$1" in
install) set clean package install "${@:2}";;
package) set clean package "${@:2}";;
package_only) set package "${@:2}";;
esac
args=
;;
esac
setx args=qvals $args "$@"
local -a basecmd setupscript runscript cmd
basecmd=(
-e user="$user"
-e group="$group"
-e projdir="$projdir"
-e setup="$setup"
-e mvn="$mvn"
-e args="$args"
${java:+-e JAVA="$java"}
)
eval "host_mappings=($host_mappings)"
for host_mapping in "${host_mappings[@]}"; do
basecmd+=(--add-host "$host_mapping")
done
basecmd+=(-v "$HOME:$HOME")
if [ "${projdir#$HOME/}" == "$projdir" ]; then
# si le répertoire de projet ne se trouve pas dans $HOME, le monter aussi
cmd+=(-v "$projdir:$projdir")
fi
setupscript='eval "$setup"'
runscript='
echo "$user" >>/etc/passwd; user="${user%%:*}"
echo "$group" >>/etc/group; group="${group%%:*}"
[ -n "$mvn" ] || mvn=mvn
cd "$projdir"
'"$actualcmd"
if [ -n "$setup" ]; then
local project_name container_name ctid
# lancement dans un container docker à préparer
[ -n "$project_name" ] || setx project_name=get_project_name
setx container_name=get_container_name "$project_name"
container_name="dkbuild_maven_${container_name}"
# vérifier l'existence de l'image
setx ctid=docker image ls --format '{{.ID}}' "${container_name}_image"
# créer le container le cas échéant
if [ -z "$ctid" ]; then
estep "Création du container $container_name à partir de l'image $image"
cmd=(
docker create -it --name "${container_name}_ct"
"${basecmd[@]}"
"$image"
bash -c "$setupscript"
)
setx ctid="${cmd[@]}" &&
docker container start -ai "$ctid" &&
docker container commit "$ctid" "${container_name}_image" &&
docker container rm "$ctid" || die
fi
# prendre comme image le container créé
image="${container_name}_image"
fi
case "$action" in
rshell) estep "Lancement d'un shell root (avec l'image $image)";;
shell) estep "Lancement d'un shell utilisateur (avec l'image $image)";;
java) estep "java (avec l'image $image)";;
*) estep "mvn $action (avec l'image $image)";;
esac
cmd=(
docker run -it --rm
"${basecmd[@]}"
"$image"
bash -c "$runscript"
)
"${cmd[@]}"
}
function mvn() {
edebug "mvn $(qvals "$@")"
[ $# -eq 0 ] && return 0
local action destdir
[[ "$1" != *=* ]] && { destdir="$1"; shift; }
[[ "$1" != *=* ]] && { action="$1"; shift; }
[ -n "$destdir" ] || destdir=.
[ -d "$destdir" ] || die "mvn: $destdir: répertoire introuvable"
local cwd="$(pwd)"
cd "$destdir" || die
[ -n "$action" ] || action=package
[ "$action" == none ] && return
local args="${DEFAULTS[mvn_args]}"
local java="${DEFAULTS[mvn_java]}"
local image="${DEFAULTS[mvn_image]}"
local machine="${DEFAULTS[mvn_machine]}"
local host_mappings="${DEFAULTS[mvn_host-mappings]-__UNDEFINED__}"
[ "$host_mappings" == __UNDEFINED__ ] && host_mappings="${DEFAULTS[docker_host-mappings]}"
local mvn="${DEFAULTS[mvn_mvn]}"
local setup="${DEFAULTS[mvn_setup]}"
local project_name="${DEFAULTS[mvn_project-name]}"
if [ -f "$destdir/.maven.conf" ]; then
eval "$(
MAVEN_JAVA=
MAVEN_IMAGE="$MAVEN_IMAGE"
MAVEN_MACHINE=-u
MAVEN_CMD=
MAVEN_SETUP=
source "$destdir/.maven.conf"
[ -z "$java" ] && echo_setv java="$MAVEN_JAVA"
[ -z "$image" ] && echo_setv image="$MAVEN_IMAGE"
[ -z "$machine" ] && echo_setv machine="$MAVEN_MACHINE"
[ -z "$mvn" ] && echo_setv mvn="$MAVEN_CMD"
[ -z "$setup" ] && echo_setv setup="$MAVEN_SETUP"
)"
fi
while [ $# -gt 0 ]; do
case "$1" in
args=*) args="${1#args=}";;
java=*) java="${1#java=}";;
image=*) image="${1#image=}";;
machine=*) machine="${1#machine=}";;
host-mappings=*) host_mappings="${1#host-mappings=}";;
mvn=*) mvn="${1#mvn=}";;
setup=*) setup="${1#setup=}";;
project-name=*) project_name="${1#project-name=}";;
*=*) ewarn "mvn: $1: argument ignoré";;
*) break;;
esac
shift
done
local version
case "$action" in
java*)
version="${action#java}"
[ -n "$version" ] && java="$version"
set java "${@:2}"
;;
esac
local use_image
if [ "$java" == force -o "$java" == any ]; then
java=
use_image=1
elif [ "$java" == none -o "$java" == system ]; then
java=
use_image=
elif [ -n "$image" -a "$image" != none ]; then
use_image=1
fi
if [ -n "$use_image" ]; then
local orig_machine
[ "$image" != none ] || die "Vous devez spécifier l'image à utiliser pour mvn"
if [ -n "$machine" -a "$machine" != current -a "$DOCKER_MACHINE_NAME" != "$machine" ]; then
orig_machine="$DOCKER_MACHINE_NAME"
set_machine "$machine"
fi
_docker_mvn "$@"
if [ -n "$_orig_machine" ]; then
set_machine "$orig_machine"
fi
else
_local_mvn "$@"
fi
# restaurer le répertoire courant
cd "$cwd"
}
function run() {
edebug "run $(qvals "$@")"
[ $# -eq 0 ] && return 0
local cmd="$1"; shift
if [ "${cmd#/}" != "$cmd" ]; then :
elif [ "${cmd#./}" != "$cmd" ]; then :
elif [ "${cmd#../}" != "$cmd" ]; then :
else
local abscmd
setx abscmd=which "$cmd" 2>/dev/null
[ -n "$abscmd" ] || die "run: $cmd: commande introuvable"
edebug "'$cmd' resolved as '$abscmd'"
cmd="$abscmd"
fi
"$cmd" "$@" || die
}
function call() {
edebug "call $(qvals "$@")"
[ $# -eq 0 ] && return 0
"$@"
}
function dkbuild() {
edebug "dkbuild $(qvals "$@")"
"$SELF" "$@" || die
}
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## init
function templates_action() {
declare -A TEMPLATES
[ -f "$TEMPLATEDIR/templates.conf" ] && source "$TEMPLATEDIR/templates.conf"
etitle "Templates valides"
local -a templates
setx -a templates=list_dirs "$TEMPLATEDIR"
for template in "${templates[@]}"; do
[ "$template" == default ] && continue
desc="${TEMPLATES[$template]}"
estep "$template${desc:+ - "$desc"}"
done
eend
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## init
function init_action() {
eval "$SHARED_LOCALS1"
local template; local -a defaultvars addvars
local -a args; args=(
"${SHARED_ARGS1[@]}"
-t:,--template: template=
-v:,--var: '$addvars+=("$value_")'
-n:,--name: '$addvars+=(name="$value_")'
-g:,--group: '$addvars+=(group="$value_")'
)
parse_args "$@"; set -- "${args[@]}"
defaultvars=(name=name group=group)
[ $# -gt 0 ] && { [ -n "$1" ] && PROJDIR="$1"; shift; }
[ $# -gt 0 ] && { [ -n "$1" ] && addvars+=(name="$1"); shift; }
[ $# -gt 0 ] && { [ -n "$1" ] && addvars+=(group="$1"); shift; }
declare -A vars
local name value
for name in "${defaultvars[@]}" "${addvars[@]}"; do
if [[ "$name" == *=* ]]; then
value="${name#*=}"
name="${name%%=*}"
else
value=
fi
name="${name^^}"
vars[$name]="$value"
done
[ -n "$template" ] || template=default
[ -d "$TEMPLATEDIR/$template" ] || die "$template: template introuvable"
[ -n "$PROJDIR" ] || PROJDIR=.
setx PROJDIR=abspath "$PROJDIR"
if [ ! -d "$PROJDIR" ]; then
ask_yesno "Voulez-vous créer le nouveau projet $(ppath "$PROJDIR")?" O || die
fi
mkdir -p "$PROJDIR"
local sedscript
for name in "${!vars[@]}"; do
value="${vars[$name]}"
[ -n "$sedscript" ] && sedscript="$sedscript; "
sedscript="${sedscript}s/${name//\//\\\/}/${value//\//\\\/}/g"
done
local src mode dest link
template="$TEMPLATEDIR/$template"
enote "Initialisation de $(ppath "$PROJDIR")"
find "$template/" -type f -o -type l | while read src; do
dest="$PROJDIR/${src#$template/}"
if [ -L "$src" ]; then
setx link=readlink "$src"
if [ -L "$dest" ]; then
if [ "$(readlink "$dest")" != "$link" ]; then
estep "${src#$template/} [link, updated]"
ln -sf "$link" "$dest"
else
edebug "${src#$template/} [exists, ignored]"
fi
elif [ -e "$dest" ]; then
estepe "${src#$template/} [destination is not a link]"
else
estep "${src#$template/} [link]"
ln -s "$link" "$dest"
fi
elif [ -f "$dest" ]; then
edebug "${src#$template/} [exists, ignored]"
else
estep "${src#$template/}"
mkdirof "$dest"
sed <"$src" >"$dest" "$sedscript"
setx mode=stat -c %a "$src"
chmod "$mode" "$dest"
fi
done
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## build
function build_action() {
eval "$SHARED_LOCALS1; $SHARED_LOCALS2"
local action=build
local machine
local clone_src_only update_src_only update_src sync_src
local build no_cache pull_image
local push_image
local -a args; args=(
"${SHARED_ARGS1[@]}" "${SHARED_ARGS2[@]}"
-m:,--machine: machine=
--clone-src-only clone_src_only=1
--update-src-only update_src_only=1
--update-src update_src=1
--no-update-src update_src=no
-w,--update-devel-src update_src=devel
-s,--sync-src sync_src=1
--no-sync-src sync_src=no
-b,--build build=1
--no-cache no_cache=1
-u,--pull-image pull_image=1
-p,--push-image push_image=1
)
parse_args "$@"; set -- "${args[@]}"
if [ -n "$clone_src_only" ]; then
action=clone_src
elif [ -n "$update_src_only" ]; then
action=update_src
else
action=build
[ -n "$update_src" ] || update_src=1
[ "$update_src" == no ] && update_src=
if [ -z "$sync_src" -a -z "$build" -a -z "$push_image" ]; then
sync_src=1
build=1
fi
[ "$sync_src" == no ] && sync_src=
fi
edebug "build_action"
set_machine "$machine"
ensure_projdir
resolve_dists_profiles
setenv "${TMPENVIRON[@]}"
setarg "${TMPARGS[@]}"
setarg "$@"
case "$action" in
clone_src)
die "Pas encore implémenté" #XXX
;;
update_src)
die "Pas encore implémenté" #XXX
;;
build)
default checkout checkout="$update_src"
default copy copy="$sync_src"
default build build="$build" ${no_cache:+no-cache} ${pull_image:+pull} ${push_image:+push}
[ $# -gt 0 ] && default build "$@"
define_functions_cmd
foreach_dists_profiles _build_each _build_before _build_after
;;
esac
}
function _build_before() {
PREV_DIST=
PREV_PROFILE=
}
function _build_each() {
if [ "$PROFILE-$PVERSION" != "$PREV_PROFILE" ]; then
[ -n "$PREV_PROFILE" ] && eend
fi
if [ -n "$DIST" -a "$DIST-$DVERSION" != "$PREV_DIST" ]; then
[ -n "$PREV_DIST" ] && eend
PREV_DIST="$DIST-$DVERSION"
etitle "Distribution ${DVERSION:+$DVERSION-}$DIST"
fi
if [ -n "$PROFILE" -a "$PROFILE-$PVERSION" != "$PREV_PROFILE" ]; then
PREV_PROFILE="$PROFILE-$PVERSION"
etitle "Profil ${PVERSION:+$PVERSION-}$PROFILE"
fi
load_dkbuild
if [ -n "$AUTOBUILD" ]; then
if [ -f docker-compose.yml ]; then
cbuild
else
build
fi
fi
}
function _build_after() {
if [ -n "$PREV_PROFILE" ]; then eend; fi
if [ -n "$PREV_DIST" ]; then eend; fi
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## clean
function _clean_git_clean() {
LANG=C git clean -d $opt "$@" |
grep -vE '^(Would skip|Skipping) ' |
sed -r 's/^Would remove //'
}
function _clean_git_status() {
git status --porcelain --ignored |
grep '^!! ' |
sed 's/^...//'
}
function clean_action() {
eval "$SHARED_LOCALS1"
local opt=-X all=
local -a args; args=(
"${SHARED_ARGS1[@]}"
-X,--ignored opt=-X
-x,--untracked opt=-x
-a,--all all=1
)
parse_args "$@"; set -- "${args[@]}"
[ -n "$all" ] && opt=-x
edebug "clean_action"
ensure_projdir
local cleans
setx cleans=_clean_git_clean -n "$@"
if [ -n "$cleans" ]; then
if check_interaction -c; then
einfo "via git clean"
eecho "$cleans"
ask_yesno "Voulez-vous supprimer ces fichiers?" O || die
fi
_clean_git_clean -f "$@" || die
fi
if [ -n "$all" ]; then
setx cleans=_clean_git_status
if [ -n "$cleans" ]; then
if check_interaction -c; then
einfo "via git status"
eecho "$cleans"
ask_yesno "Voulez-vous supprimer ces fichiers supplémentaires?" O || die
fi
sed 's/^/Removing /' <<<"$cleans"
eval "cleans=($cleans);"' rm -rf "${cleans[@]}"' || die
fi
fi
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## composer
function composer_action() {
local -a args; args=(
)
parse_args "$@"; set -- "${args[@]}"
[ $# -gt 0 ] || set .
edebug "composer_action"
define_functions_cmd
composer "$@"
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## mvn
function mvn_action() {
local -a args; args=(
)
parse_args "$@"; set -- "${args[@]}"
[ $# -gt 0 ] || set .
edebug "mvn_action"
define_functions_cmd
mvn "$@"
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## dump
function dump_action() {
eval "$SHARED_LOCALS1; $SHARED_LOCALS2"
local machine
local -a args; args=(
"${SHARED_ARGS1[@]}" "${SHARED_ARGS2[@]}"
-m:,--machine: machine=
)
parse_args "$@"; set -- "${args[@]}"
edebug "dump_action"
set_machine "$machine"
ensure_projdir
resolve_dists_profiles
setenv "${TMPENVIRON[@]}"
setarg "${TMPARGS[@]}"
setarg "$@"
foreach_dists_profiles _dump_each _dump_before _dump_after
}
function _dump_before() {
PREV_DIST=
PREV_PROFILE=
}
function _dump_each() {
if [ "$PROFILE-$PVERSION" != "$PREV_PROFILE" ]; then
[ -n "$PREV_PROFILE" ] && eend
fi
if [ -n "$DIST" -a "$DIST-$DVERSION" != "$PREV_DIST" ]; then
[ -n "$PREV_DIST" ] && eend
PREV_DIST="$DIST-$DVERSION"
etitle "Distribution ${DVERSION:+$DVERSION-}$DIST"
fi
if [ -n "$PROFILE" -a "$PROFILE-$PVERSION" != "$PREV_PROFILE" ]; then
PREV_PROFILE="$PROFILE-$PVERSION"
etitle "Profil ${PVERSION:+$PVERSION-}$PROFILE"
fi
load_dkbuild
etitle "Variables d'environnement"
for name in "${!ENVIRON[@]}"; do
estep "$name=${ENVIRON[$name]}"
done
eend
etitle "Variables de build"
for name in "${!ARGS[@]}"; do
estep "$name=${ARGS[$name]}"
done
eend
etitle "Valeurs par défaut"
for name in "${!DEFAULTS[@]}"; do
estep "$name=${DEFAULTS[$name]}"
done
eend
}
function _dump_after() {
if [ -n "$PREV_PROFILE" ]; then eend; fi
if [ -n "$PREV_DIST" ]; then eend; fi
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
##
# faire une copie de l'environnement original
load_environ
# si aucune action n'est spécifiée, il faut inférer build pour que ses options
# soient reconnues
args=()
while [ $# -gt 0 ]; do
case "$1" in
--help) args+=("$1"); shift; continue;;
--hdk|--help-dkbuild) args+=("$1"); shift; continue;;
--href|--help-reference) args+=("$1"); shift; continue;;
-*|*=*) # option quelconque: inférer build
args+=(build)
break
;;
*) # argument quelconque: on s'arrête ici
break
;;
esac
done
set -- "${args[@]}" "$@"
args=(+
--help '$exit_with display_help'
--hdk,--help-dkbuild '$exit_with display_help_dkbuild'
--href,--help-reference '$exit_with display_help_reference'
)
parse_args "$@"; set -- "${args[@]}"
# aliases
case "$1" in
ci) set composer "$2" install "${@:3}";;
cu) set composer "$2" update "${@:3}";;
cr) set composer "$2" rshell "${@:3}";;
cs) set composer "$2" shell "${@:3}";;
mvr) set mvn "$2" rshell "${@:3}";;
mvs) set mvn "$2" shell "${@:3}";;
java) set mvn "$2" java "${@:3}";;
esac
# actions
action="${1:-build}"; shift
case "$action" in
templates|t) templates_action "$@";;
init|i|0) init_action "$@";;
build|b) build_action "$@";;
clean|k) clean_action "$@";;
composer|c) composer_action "$@";;
maven|mvn|m) mvn_action "$@";;
dump|d) dump_action "$@";;
*) die "$action: action invalide";;
esac