nutools/dkbuild

3252 lines
112 KiB
Bash
Executable File

#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname -- "$0")/lib/ulib/auto" || exit 1
urequire ptools xmlsupport
SELF="$script"
TEMPLATEDIR="$scriptdir/lib/dkbuild/templates"
[ -n "$COMPOSE_V1" ] && DOCKER_COMPOSE=(docker-compose) || DOCKER_COMPOSE=(docker compose)
function get_default_phpbuilder_image() {
echo "${REGISTRY:-pubdocker.univ-reunion.fr}/image/phpbuilder:${DIST:-d11}"
}
function get_default_javabuilder_image() {
echo "${REGISTRY:-pubdocker.univ-reunion.fr}/image/javabuilder:d11"
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Aide
function display_help() {
uecho "$scriptname: construire une image docker
USAGE
$scriptname action [options]
OPTIONS
--hdk, --help-dkbuild
Afficher l'aide sur le format du fichier dkbuild
--href, --help-reference
Afficher la référence sur les commandes utilisables dans un fichier
dkbuild
--compose-v1
Forcer l'utilisation de docker-compose v1
ACTIONS
$scriptname templates
lister les templates valides pour 'init --template'
$scriptname init [OPTIONS] [PROJDIR [NAME [GROUP]]]
initialiser un répertoire pour la construction d'une ou plusieurs images
docker, selon un modèle prédéfini
-j, --projdir PROJDIR
Spécifier le répertoire de projet à créer/mettre à jour. le répertoire
par défaut est le répertoire courant.
-t, --template TEMPLATE
Nom du modèle à utiliser. Utiliser 'default' par défaut
-v, --var VAR=VALUE
Spécifier une variable pour la génération des fichiers à partir du
modèle. Le nom de la variable est mis en majuscule avant remplacement
dans les fichiers
-n, --name NAME
-g, --group GROUP
Raccourcis pour respectivement -vname=NAME et -vgroup=GROUP
$scriptname build [OPTIONS] [BUILDVARS...]
construire les images. C'est l'action par défaut
Les arguments BUILDVARS de la forme ARG=VALUE permettent de spécifier des
arguments de build comme avec l'option --arg
-m, --machine MACHINE
Sélectionner la machine spécifiée avant de dérouler le script
-j, --projdir PROJDIR
Spécifier le répertoire de projet. Si cette option n'est pas spécifiée,
remonter la hiérarchie et prendre le premier répertoire qui contient un
fichier nommé dkbuild.
Si PROJDIR est un fichier, c'est ce fichier qui est utilisé comme script
de build. Le répertoire de projet sélectionné est le répertoire qui
contient le script de build.
Si le répertoire PROJDIR contient un fichier du même nom de base que le
fichier de build avec l'extension '.env' (i.e dkbuild.env par défaut),
ce fichier est lu de la même façon qu'un fichier de configuration
-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.
L'ordre de priorité est le suivant:
- d'abord les variables spécifiées avec --env et --arg,
- puis les variables définies dans ce fichier de configuration,
- puis celles définies dans dkbuild.env le cas échéant
- puis celles définies dans le fichier de build courant.
Utiliser la valeur spéciale 'none' pour indiquer qu'aucun fichier de
configuration ne doit être chargé.
-d, --dist DIST
-0, --d10
-1, --d11
-2, --d12
-3, --d13
--r7, --rhel7
--r8, --rhel8
--r9, --rhel9
--o7, --oracle7
--o8, --oracle8
--o9, --oracle9
Ne faire le build que pour la distribution spécifiée. Par défaut, faire
le build pour toutes les distributions définies. Si la distribution
sélectionnée n'est pas valide pour ce build, elle est ignorée
-g, --profile PROFILE
-P, --prod
-T, --test
-E, --dtest
--devel
Spécifier le profil dans lequel construire l'image
--all-profiles
Construire l'image dans tous les profils définis
-e, --env VAR=VALUE
Spécifier la valeur d'une variable d'environnement. Cette valeur
remplace la valeur par défaut spécifiée dans le fichier de build.
--arg ARG=VALUE
Spécifier la valeur d'un argument de build. Cette valeur remplace la
valeur par défaut spécifiée dans le fichier de build.
-u, --clean-update
Avant de faire le build, faire un clean, suivi de git pull. C'est la
méthode préférée pour mettre à jour le dépôt s'il y a des fichiers
synchronisé avec la commande 'copy', parce que sinon les fichiers
sources (mis à jour par git pull) sont désynchronisés d'avec les
fichiers destination.
--clone-src-only
Ne faire que cloner les dépôts sources mentionnés avec la commande
'checkout'
--update-src-only
Ne faire que mettre à jour les dépôts sources mentionnés avec la
commande 'checkout'
--update-src
Avec la commande 'checkout', mettre à jour les dépôts avant de faire le
build. C'est la valeur par défaut.
--no-update-src
Ne pas mettre à jour les dépôts avant de faire le build. La commande
'checkout' devient un NOP si le dépôt existe déjà.
-w, --update-devel-src
Ne pas mettre à jour le dépôt, préférer la synchronisation depuis la
version de développement d'un dépôt
-s, --sync-src
Avec la commande 'copy', effectuer la mise à jour des fichiers. C'est la
valeur par défaut si on construit l'image
--no-sync-src
Ne pas mettre à jour les fichiers. La commande 'copy' devient un NOP si
le fichier destination existe.
-b, --build
Construire les images
--no-cache
Ne pas utiliser le cache lors du build
--plain-output
Afficher la sortie complète des containers 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
$scriptname clean [OPTIONS] [DIRS...]
nettoyer le projet des fichiers créés par 'copy gitignore=', en utilisant la
commande 'git clean -dX'
-j, --projdir PROJDIR
Spécifier le répertoire de projet
-X, --ignored
Utiliser l'option -X de git clean pour ne supprimer que les fichiers
ignorés par git. c'est l'option par défaut.
-x, --untracked
Utiliser l'option -x de git clean pour supprimer aussi les fichiers non
suivis.
-a, --all
Supprimer aussi les fichiers listés par 'git status --ignored'. Cela
permet de supprimer un maximum de fichiers qui ne font pas partie du
projet. Cette option implique --untracked
$scriptname composer DESTDIR [ACTION [PARAMS] [ARGS]]
lancer composer dans le répertoire spécifié
cf la documentation de la commande 'composer' pour la description des
paramètres
$scriptname mvn DESTDIR [ACTION [PARAMS] [ARGS]]
lancer maven dans le répertoire spécifié
cf la documentation de la commande 'mvn' pour la description des paramètres
$scriptname dump [OPTIONS]
afficher les valeurs des variables
Les options suivantes ont la même signification que pour l'action build:
--machine, --projdir, --config, --dist, --profile, --all-profiles, --env,
--arg"
}
function display_help_dkbuild() {
uecho "\
OPTIONS
--help Aide générale
* --hdk Aide sur le format du fichier dkbuild
--href Référence sur les commandes utilisables dans un fichier dkbuild
DKBUILD
=======
Un fichier dkbuild est un script shell utilisé pour construire une ou plusieurs
images docker.
A cause de l'implémentation utilisée pour les directives, le fichier doit être
parcouru (i.e exécuté) à de multiples reprises pour analyser les paramètres et
variables définis. il faut donc \"protéger\" les appels de scripts externes ou
de fonctions gourmandes avec les commandes 'run' et 'call' pour éviter que ces
commandes ne soient exécutées à plusieurs reprises.
Quand un fichier dkbuild est exécuté, le répertoire courant est toujours le
répertoire qui contient le fichier
## Distributions ###############################################################
Une distribution s'entend au sens de la distribution linux utilisée comme base
pour l'image construite. L'idée est de pouvoir construire des images similaires
qui ne diffèrent que par la version de base du système d'exploitation
Pour une même distribution, plusieurs versions d'une image peuvent être
construites. Une version est définie en ajoutant un préfixe à la distribution.
La commande 'setdists' permet de lister explicitement les distributions valides
(et les versions associées le cas échéant). Si la distribution sélectionnée par
l'utilisateur n'est pas dans la liste fournie, le script s'arrête sans erreur.
La première distribution listée est spéciale: c'est la distribution la plus
récente, celle qui reçoit le tag :latest
La distribution 'none' est spéciale aussi: elle n'est pas mentionnée dans les
tags automatiquement attribués aux images.
La commande 'dist' permet de tester si la distribution spécifiée en argument a
été sélectionnée par l'utilisateur. L'argument 'LATEST' permet de tester la
dernière distribution.
La commande 'version' permet de tester si la version spécifiée en argument a été
sélectionnée par l'utilisateur. On part du principe qu'une distribution a déjà
été testée au préalable avec 'dist'
Exemple:
setdists 3.0-d11 3.1-d11 d10 d9
if dist d11; then
if version 3.0; then
...
elif version 3.1; then
...
fi
elif dist d10; then
...
elif dist d9; then
...
fi
Dans une même distribution, les versions doivent être ordonnées de la plus
ancienne à la plus récente. ici, la version 3.1 est listée après la version 3.0
pour que l'image construite aie le tag :latest
Note: 'setdists' ne doit être utilisé qu'une seule fois. Les invocations
suivantes sont ignorées.
## Profils #####################################################################
Un profil correspond à l'environnement de destination de l'image: production,
test, développement.
La commande 'setprofiles' permet de lister explicitement les profils valides.
Si le profil sélectionné par l'utilisateur n'est pas dans la liste fournie, le
script s'arrête avec une erreur. Le premier profil listé est spécial: c'est le
profil par défaut.
La commande 'default_profile' permet de spécifier un profil par défaut à
utiliser, exactement comme s'il avait été spécifié avec l'option --profile.
Cette commande est particulièrement appropriée pour le fichier ~/.dkbuild.env
s'il s'agit de définir le profil à utiliser sur un hôte.
La commande 'profile' permet de tester si le profil spécifié en argument a été
sélectionné par l'utilisateur. L'argument 'DEFAULT' permet de tester le profil
par défaut.
Exemple:
setprofiles prod devel
if profile prod; then
...
elif profile devel; then
...
fi
Si le build est indépendant de la distribution, ou si la distribution est
utilisée sans version, alors il est possible de préfixer le profil d'une
version. Exemple:
setprofiles v1-prod v2-prod
if profile prod; then
if version v1; then
...
elif version v2; then
...
fi
fi
Dans un même profil, les versions doivent être ordonnées de la plus ancienne à
la plus récente.
Si les distributions sont utilisées avec des versions, alors c'est une erreur de
spécifier une version dans le profil
Note: 'setprofiles' et 'default_profile' ne peuvent être utilisés qu'une seule
fois. Les invocations suivantes sont ignorées.
## Versions ####################################################################
Si la version de l'image à construire n'est liée ni à la distribution, ni au
profil, il est possible de la spécifier avec la commande 'setversion'. La
version spécifiée avec 'setversion' est utilisée par défaut pour toutes les
images dont la version n'est pas spécifiée dans la distribution ou le profil.
La commande 'setversion' est évaluée en même temps que les commandes 'setenv',
ainsi il est possible d'utiliser la valeur d'une variable définie au préalable.
## Environnement ###############################################################
La commande 'machine' permet de tester si le build est fait sur la machine
spécifiée. Ce peut être la machine courante, ou la machine spécifiée avec
l'option --machine de la commande build
Exemple:
if machine host{1,2,3}-prod; then
setprofiles prod
elif machine host{1,2,3}-test; then
setprofiles test
else
setprofiles devel test prod
fi
Les variables sont de deux types: argument de build ou variable d'environnement
La commande 'setenv' permet de définir une variable d'environnement. La commande
'setarg' permet de définir un argument de build. Ces valeurs sont alors
automatiquement utilisées à l'endroit approprié.
Ces commandes acceptent une liste d'argument de la forme VAR[=VALUE]
Si la valeur n'est pas spécifiée (e.g 'setarg DESTDIR'), alors la variable doit
être définie dans l'environnement courant. Si la variable n'est pas définie dans
l'environnement, alors le script s'arrête avec une erreur.
Une fois qu'une variable est définie, il n'est plus possible de la modifier. Les
commandes alternatives 'resetenv' et 'resetarg' permettent de pallier cette
limitation.
## Valeurs par défaut ##########################################################
Toutes les commandes ont des arguments requis, mais aussi des arguments
facultatifs qui sont fournis sous la forme d'une liste d'éléments VAR=VALUE
La commande 'default' permet de spécifier les valeurs par défaut de ces
arguments. Exemple:
if profile prod; then
default composer mode=production
elif profile devel; then
default composer mode=development
fi
composer install path/to/project
Une fois qu'une valeur par défaut est définie, il n'est plus possible de la
modifier. La commande alternative 'resetdefault' permet de pallier cette
limitation.
Définir des valeurs par défaut pour la commande 'docker' impacte la commande
'build' et toutes les commandes qui utilisent docker, comme 'composer' ou 'mvn'
## Synchronisation de fichiers #################################################
On peut vouloir s'assurer de la présence de certains fichiers à certains
endroits.
La commande 'checkout URL DESTDIR' permet de s'assurer qu'un checkout du dépôt
spécifié existe dans le répertoire DESTDIR. La branche ou le commit à utiliser,
la source en mode développement, etc. peuvent être spécifiés par des arguments
facultatifs.
La commande 'copy SRC DEST' permet de s'assurer que SRC et DEST sont
synchronisés. Si possible, des liens physiques sont créés pour conserver
l'espace disque. Par défaut, les fichiers ne sont créés que s'ils n'existent
pas.
## Support Composer, Maven, commandes génériques ###############################
La commande 'composer install' permet d'installer les dépendances Composer d'un
projet
La commande 'mvn package' permet de construire un projet Java.
La commande 'run' permet de lancer une commande quelconque. La commande est
cherchée dans le PATH et exécutée avec son chemin complet. celà permet de lancer
des commandes comme 'mvn' ou 'composer' ou dont le nom correspond à une fonction
déjà définie.
La commande 'call' permet de lancer une commande quelconque. La différence avec
'run' est que la commande est lancée telle quelle, sans modifications. Si une
fonction est définie, elle sera utilisée en priorité.
Les commandes 'runb' et 'callb' sont comme 'run' et 'call' respectivement, mais
elles ne sont exécutées que si build est activé.
Si elles sont utilisées sans argument, les commandes 'composer', 'mvn', 'run' et
'call' retournent vrai si la commande doit être exécutée dans le contexte
courant. Celà permet d'implémenter des traitements complexes. Ainsi le script
suivant:
run cmd1
run cmd2
est équivalent à:
if run; then
cmd1
cmd2
fi
En phase d'analyse, ces commandes retournent faux, donc cmd1 et cmd2 ne seront
lancés qu'une seule fois lors de l'invocation de dkbuild. Cela signifie qu'il ne
faut pas utiliser des directives de définition de variables à l'intérieur. Par
exemple, le script suivant ne produit pas forcément l'effet escompté:
if run; then
setenv var=value
default
cmd args...
fi
Bien entendu, si on veut être sûr que des commandes externes soient lancées, on
peut toujours utiliser 'run' à l'intérieur, e.g
if composer; then
run extcmd
func
composer args...
fi
## Support Dockerfile et docker build ##########################################
La commande 'dockerfile OUTPUT' permet de construire de façon incrémentale et
dynamique un fichier Dockerfile. Toutes les commandes d'un fichier dockerfile
traditionnelles sont reconnues et elles doivent être spécifiées après la
commande 'dockerfile'.
La commande 'build' termine la construction du fichier Docker puis lance la
construction de l'image.
* arguments de build: les arguments de build définis dans le script sont passés
à docker pour la construction de l'image
* nom de l'image et tag: par défaut, la variable d'environnement IMAGE est
combinée le cas échéant avec DIST et VERSION pour déterminer les tags que
reçoit l'image construite.
La commande 'cbuild' lance le build de toutes les images mentionnées dans les
fichiers 'docker-compose.yml' et 'docker-compose.PROFILE.yml' le cas échéant
* variables d'environnement: les arguments de build définis sont inscrits dans
un fichier .env qui est utilisé ensuite par docker compose
Si aucune commande 'build' ou 'cbuild' ne figure dans le fichier, 'build' est
lancé automatiquement à la fin
## Autres commandes ############################################################
Il est possible d'organiser le script dans plusieurs fichiers qui sont inclus
avec la commande 'include'. Fonctionnellement, cela se passe comme si le contenu
du fichier inclus était inséré dans le script principal, à ceci près que le
répertoire courant devient temporairement celui du fichier inclus.
Exemple:
setdists d11 d10 d9
if dist d9; then
include d9/dkbuild
elif dist d10; then
include d10/dkbuild
elif dist d11; then
include d11/dkbuild
fi
La commande 'dkbuild' lance le build d'un répertoire destination. La différence
avec 'include' est que cela est fait dans un processus complètement différent,
comme si le build avait été lancé depuis la ligne de commande.
Les commandes 'section', 'note', 'info', 'debug' permettent d'afficher des
messages de différents niveaux
## Variables globales ##########################################################
PROJDIR est le chemin absolu du projet, dans lequel se trouve le fichier dkbuild
initial
La distribution actuellement sélectionnée se trouve dans la variable DIST. La
version actuellement sélectionnée se trouve dans la variable VERSION. Si la
commande 'setdists' n'est pas utilisée, alors ni DIST ni VERSION ne sont définis
Le profil actuellement sélectionné se trouve dans la variable PROFILE. La
version actuellement sélectionnée se trouve dans la variable VERSION (si les
distributions sont utilisées sans le support de la version). Si la commande
'setprofiles' n'est pas utilisée, alors ni PROFILE ni VERSION ne sont définis
IMAGE est le nom de l'image à construire. Si le nom de l'image contient le tag
(e.g IMAGE:TAG) alors le nom est utilisé tel quel. Sinon, DIST et VERSION sont
utilisés comme tags (i.e IMAGE:DIST et IMAGE:VERSION-DIST)"
}
function display_help_reference() {
uecho "\
OPTIONS
--help Aide générale
--hdk Aide sur le format du fichier dkbuild
* --href Référence sur les commandes utilisables dans un fichier dkbuild
REFERENCE
=========
## fonctions d'affichage
USAGE:
section TITLE
note MESSAGE
info MESSAGE
debug MESSAGE
## machine -- vérifier la machine courante
## setdists -- spécifier les distributions valides
## dist -- vérifier la distribution courante
## setprofiles -- spécifier les profils valides
## default_profile -- spécifier le profil à utiliser si aucun profil n'est sélectionné
## profile -- vérifier le profil courant
## setversion -- spécifier la version par défaut
USAGE: setversion VERSION
Les paramètres optionnels sont
* from-file=FILE
prendre la version depuis le fichier spécifié. les formats pom.xml et
VERSION.txt sont supportés
* from-repo=REPO
calculer la version depuis le dépôt git spécifié
* from-glob=GLOB
calculer la version depuis le chemin spécifié
* extract=REGEXP
à partir de la valeur calculée par l'un des paramètres from-*, matcher
l'expression régulière au format AWK, et prendre comme version la valeur de
l'expression \$1
Si from-glob est spécifié, la valeur par défaut de extract est calculée en
remplaçant '*' par '(.*)'
* add-prefix=PREFIX
Ajouter le préfixe spécifié à la version extraite
* add-suffix=SUFFIX
Ajouter le suffixe spécifié à la version extraite
## version -- vérifier la version courante
## setenv -- spécifier une variable d'environnement
## resetenv -- spécifier une variable d'environnement
## setarg -- spécifier une variable de build
## resetarg -- spécifier une variable de build
## default -- spécifier des arguments par défaut
## resetdefault -- spécifier des arguments par défaut
## checkout -- faire et vérifier checkout d'un dépôt git
USAGE: checkout URL [DESTDIR] [PARAMS]
Les paramètres optionnels sont
* checkout
mettre à jour dépôt. c'est la valeur par défaut. utiliser checkout= pour
désactiver la mise à jour du dépôt. utiliser checkout=devel pour synchroniser
depuis le répertoire de développement.
* origin=ORIGIN
spécifier l'origine. Par défaut, prendre 'origin'
* branch=BRANCH
spécifier la branche à utiliser dans l'origine spécifiée. La valeur par défaut
est 'master' si les profils ne sont pas utilisé. Si les profils sont utilisés,
la valeur par défaut est 'develop' pour les profils 'test' et 'devel', sinon
c'est 'master'.
Utiliser la syntaxe ^COMMIT pour ignorer l'origine et sélectionner un commit
en particulier.
* develdir=DIR
spécifier l'emplacement du répertoire de développement, utilisé avec
checkout=devel
* develtype=TYPE
spécifier le type de projet pour la synchronisation depuis le répertoire de
développement. certains projets, notamment les projets composer avec des
dépendances de type 'path', nécessitent une méthode de synchronisation
particulière. Si ce paramètre n'est pas spécifié, il est auto-détecté. Les
valeurs supportées sont:
* composer -- détecté de par la présence d'un fichier composer.json
* maven -- détecté de par la présence d'un fichier pom.xml
* none -- valeur par défaut: pas de type particulier
## copy -- synchroniser des fichiers
USAGE: copy SRC DEST [PARAMS]
Si SRC est un fichier, alors DEST doit être un chemin vers le fichier
destination. Si SRC est un répertoire, alors DEST doit être un chemin vers le
répertoire destination. On peut forcer à considérer SRC et/ou DEST comme un
répertoire en les suffixant de '/'
Par exemple:
* copy SRCDIR/ DESTDIR/
échoue si SRCDIR et/ou DESTDIR sont des fichiers
* les commandes suivantes sont équivalentes:
copy SRC DESTDIR/
copy SRC DESTDIR/SRCNAME
Les paramètres optionnels sont
* copy
synchroniser les fichiers. c'est la valeur par défaut. utiliser copy= pour
désactiver la synchronisation des fichiers.
* overwrite
autoriser l'écrasement des fichiers destination, ce qui permet de rendre la
destination identique à la source. par défaut, un fichier destination est
laissé en place, ce qui permet d'avoir le cas échéant des fichiers locaux
différents de la source.
* gitignore=BASEDIR
maintenir le fichier .gitignore de BASEDIR, qui doit être un répertoire parent
de DESTDIR. Les fichiers synchronisés sont rajouté le cas échéant dans
.gitignore, sauf si le répertoire qui les contient est déjà exclu.
NB: Un fichier n'est considéré pour l'ajout dans .gitignore que s'il a été
copié au préalable. Ainsi, un fichier déjà existant dans la destination ne
sera pas ajouté dans le fichier .gitignore si overwrite=
## genfile -- créer un fichier générique
USAGE: genfile OUTPUT [INPUT] [PARAMS]
Si le fichier INPUT est spécifié, il est utilisé pour créer le contenu initial
du fichier. Sinon, l'entrée standard *doit* être redirigée depuis un fichier, et
elle est lue pour générer le contenu initial du fichier à générer.
Les paramètres optionnels sont
* context=DIR
générer le fichier dans le répertoire spécifié
* sed=SCRIPT
script sed à appliquer au fichier
## dockerfile -- créer un fichier Dockerfile
USAGE: dockerfile [OUTPUT [INPUT]] [PARAMS]
Si le fichier INPUT est spécifié, il est utilisé pour créer le contenu initial
du fichier. Sinon, *si* l'entrée standard est redirigée depuis un fichier, elle
est lue pour générer le contenu initial du fichier.
Les paramètres optionnels sont
* context=DIR
générer le fichier dans le répertoire spécifié
* sed=SCRIPT
script sed à appliquer au fichier initial, construit à partir de l'entrée
standard. Le fichier n'est plus modifié par la suite.
## build -- construire une image avec docker
USAGE: build [PARAMS]
Les paramètres optionnels sont
* build
construire les images, c'est la valeur par défaut. utiliser build= pour
désactiver la construction.
* context=DIR
répertoire de contexte. cette valeur est en principe générée automatiquement
par la commande 'dockerfile'
* dockerfile=FILE
fichier de build à utiliser. cette valeur est en principe générée automatiquement
par la commande 'dockerfile'
* no-cache
ne pas utiliser le cache
* pull
forcer la mise à jour des images dépendantes
* host-mappings=MAPPINGS
définir des mappings d'hôte. si ce paramètre n'est pas spécifié, consulter
aussi la valeur par défaut 'docker host-mappings='
* set-tag=TAGS... ou set-tags=TAGS...
spécifier des tags à rajouter au nom de l'image, séparés par un espace. cette
liste remplace celle calculée automatiquement. ce paramètre est ignoré pour
les noms d'images comportant un tag.
Utiliser le tag spécial LATEST pour rajouter :latest si c'est approprié
* add-tag=TAGS... ou add-tags=TAGS...
spécifier des tags à rajouter à la liste calculée automatiquement, séparés par
un espace. ce paramètre est ignoré pour les noms d'images comportant un tag
* image=IMAGES... ou images=IMAGES...
liste de nom d'images, séparés par un espace. si les noms n'ont pas de tag, le
tag est construit à partir de DIST et VERSION sous la forme [VERSION-]DIST
* push
pousser les images vers le registry après les avoir construites
## cbuild -- construire une image avec docker compose
USAGE: cbuild [SERVICE] [PARAMS]
Les paramètres optionnels sont
* files=FILES...
spécifier les fichiers docker-compose à utiliser. Par défaut, prendre
docker-compose.yml et docker-compose.PROFILE.yml
* project-name=PROJECT_NAME
spécifier le nom du projet
* no-cache
ne pas utiliser le cache
* pull
forcer la mise à jour des images dépendantes
## include -- inclure un autre fichier dkbuild
## dkbuild -- lancer un builder externe
La commande dkbuild qui traite le script de build courant est lancée telle
quelle, avec la même syntaxe qu'en ligne de commande. L'intérêt de cette
commande est qu'on est assuré d'utiliser le même dkbuild que celui qui traite le
script de build courant.
## composer -- gérer projet composer
USAGE: composer DESTDIR [ACTION [PARAMS] [ARGS]]
La destination est obligatoire. Sans arguments, cette commande retourne
simplement vrai
Les actions valides sont
* install -- installer les dépendances. c'est l'action par défaut
* update -- mettre à jour les dépendances
* rshell -- lancer un shell root dans le répertoire du projet
* shell -- lancer un shell utilisateur dans le répertoire du projet
* none -- ne rien faire
L'action est exécutée sur DESTDIR. Juste avant de lancer l'action, le répertoire
courant est modifié pour être DESTDIR, ce qui permet d'utiliser des chemins
relatifs le cas échéant.
La commande 'rshell' lance un shell bash avec l'utilisateur root au lieu de
lancer la commande composer, ce qui permet de faire des opérations plus
complexes si le besoin s'en fait sentir. La commande alternative 'shell' lance
le shell avec le compte utilisateur. Ces commandes sont particulièrement utiles
si composer est paramétré pour être lancé dans un container
Les paramètres optionnels sont
* args=ARGS...
arguments à rajouter à la commande composer. La valeur par défaut dépend du
profil:
* prod: --no-dev -o
* test: --no-dev -o
* autres profils: (pas d'arguments)
* php=VERSION
version de PHP en dessous de laquelle image= est utilisé. En d'autres termes,
c'est la version minimum de PHP nécessaire pour faire tourner composer. L'idée
est que si la version de PHP installée est suffisante, il n'est pas nécessaire
de passer par une image docker.
Cette valeur doit être spécifiée avec le format de PHP_VERSION_ID i.e 70300
pour PHP 7.3
* Spécifier 'any' ou 'force' pour forcer l'utilisation de l'image docker.
* Spécifier 'none' ou 'system' pour lancer directement composer sans passer
par une image docker.
Si php n'est pas disponible dans le PATH, ce paramètre prend par défaut la
valeur 'force'
* php-max=VERSION
version de PHP à partir de laquelle image= est utilisé. En d'autres termes,
c'est la version maximum de PHP, à partir de laquelle il faut passer par une
image docker. L'idée est que si la version de PHP installée est trop récente,
ça peut poser problème avec le calcul des dépendances.
Cette valeur doit être spécifiée avec le format de PHP_VERSION_ID i.e 70300
pour PHP 7.3
Si la valeur n'est pas spécifiée ou vaut 'none', elle est ignorée.
* image=COMPOSER_IMAGE
image docker utilisée pour lancer composer. La valeur par défaut est la valeur
de la variable d'environnement \$COMPOSER_IMAGE
Spécifier 'none' pour lancer directement composer sans passer par une image
docker.
L'image spécifiée doit disposer de la commande 'su-exec' afin de pouvoir
lancer la commande avec l'utilisateur courant. Le répertoire \$HOME est monté
à l'intérieur du container et le script composer.phar du projet est utilisé le
cas échéant.
* machine=MACHINE
nom de la docker machine sur laquelle se connecter pour lancer l'image docker.
La valeur par défaut est -u, ce qui force l'utilisation de l'instance docker
locale. Spécifier 'current' pour ne pas modifier la valeur courante le cas
échéant
* host-mappings=MAPPINGS
définir des mappings d'hôte. si ce paramètre n'est pas spécifié, consulter
aussi la valeur par défaut 'docker host-mappings='
* composer=PATH/TO/COMPOSER
chemin vers l'exécutable composer. Par défaut, utiliser composer.phar s'il
existe dans le répertoire du projet. Sinon utiliser /usr/bin/composer
* setup=CMDS...
liste de commandes à lancer pour configurer le container. Un container ayant
pour base COMPOSER_IMAGE et nommé d'après le nom du projet est préparé et les
commandes spécifiées y sont lancées. Ce container est réutilisé à chaque fois.
Ce paramétrage est utilisé par exemple pour installer certains packages
nécessaire au projet.
* setup-image=SETUP_IMAGE
forcer le nom de l'image pour setup= (la valeur de project-name= est ignorée)
* project-name=PROJECT_NAME
si setup= est défini, nommer l'image sur la base de ce nom. par défaut, le nom
est calculé automatiquement
Si un fichier .composer.yaml existe dans le répertoire du projet, il est analysé
pour obtenir les valeurs par défaut des paramètres suivants:
* composer_php_min: valeur par défaut de php=
* composer_php_max: valeur par défaut de php-max=
* composer_registry: ET composer_image: valeur par défaut de image=
* composer_setup: valeur par défaut de setup=
* composer_setup_image: valeur par défaut de setup-image=
Sinon, si un fichier .composer.conf existe dans le répertoire du projet, il est
sourcé pour obtenir les valeurs par défaut des paramètres:
* COMPOSER_PHP -- valeur par défaut de php=
* COMPOSER_PHP_MAX -- valeur par défaut de php-max=
* COMPOSER_IMAGE -- valeur par défaut de image=
* COMPOSER_MACHINE -- valeur par défaut de machine=
* COMPOSER_CMD -- valeur par défaut de composer=
* COMPOSER_SETUP -- valeur par défaut de setup=
* COMPOSER_SETUP_IMAGE -- valeur par défaut de setup-image=
## mvn -- construire projet maven
USAGE: mvn DESTDIR [ACTION [PARAMS] [ARGS]]
Le répertoire de destination est obligatoire. Sans arguments, cette commande
retourne simplement vrai. L'action par défaut est 'package'
Les actions valides sont
* install -- lance mvn avec les commandes 'clean package install'
* package -- lance mvn avec les commandes 'clean package'
* package_only -- lance mvn avec uniquement la commande 'package'
* rshell -- lancer un shell root dans le répertoire du projet
* shell -- lancer un shell utilisateur dans le répertoire du projet
* java -- lancer java au lieu de lancer mvn. il est possible de spécifier la
version de java directement, e.g java7, java8, java11
L'action est exécutée sur DESTDIR. Juste avant de lancer l'action, le répertoire
courant est modifié pour être DESTDIR, ce qui permet d'utiliser des chemins
relatifs le cas échéant.
La commande 'rshell' lance un shell bash avec l'utilisateur root au lieu de
lancer la commande mvn, ce qui permet de faire des opérations plus complexes si
le besoin s'en fait sentir. La commande alternative 'shell' lance le shell avec
le compte utilisateur. Ces commandes sont particulièrement utiles si mvn est
paramétré pour être lancé dans un container
Les paramètres optionnels sont
* args=ARGS...
arguments à rajouter à la commande mvn
* java=VERSION
version de Java à sélectionner à l'intérieur de l'image docker
* Spécifier 'any' ou 'force' pour prendre la valeur par défaut
* Spécifier 'none' ou 'system' pour ne pas utiliser l'image docker
Si java ou mvn ne sont pas disponibles dans le PATH, ce paramètre prend par
défaut la valeur 'force'
* image=MAVEN_IMAGE
image docker utilisée pour lancer mvn. La valeur par défaut est la valeur
de la variable d'environnement \$MAVEN_IMAGE
Spécifier 'none' pour lancer directement mvn sans passer par une image
docker, même si java= est renseigné
L'image spécifiée doit disposer de la commande 'su-exec' afin de pouvoir
lancer la commande avec l'utilisateur courant. Le répertoire \$HOME est monté
à l'intérieur du container.
* machine=MACHINE
nom de la docker machine sur laquelle se connecter pour lancer l'image docker.
La valeur par défaut est -u, ce qui force l'utilisation de l'instance docker
locale. Spécifier 'current' pour ne pas modifier la valeur courante le cas
échéant
* host-mappings=MAPPINGS
définir des mappings d'hôte. si ce paramètre n'est pas spécifié, consulter
aussi la valeur par défaut 'docker host-mappings='
* mvn=PATH/TO/MVN
chemin vers l'exécutable mvn. Par défaut, utiliser la commande trouvée dans le
PATH
* setup=CMDS...
liste de commandes à lancer pour configurer le container. Un container ayant
pour base MAVEN_IMAGE et nommé d'après le nom du projet est préparé et les
commandes spécifiées y sont lancées. Ce container est réutilisé à chaque fois.
Ce paramétrage est utilisé par exemple pour installer certains packages
nécessaire au projet.
* setup-image=SETUP_IMAGE
forcer le nom de l'image pour setup= (la valeur de project-name= est ignorée)
* project-name=PROJECT_NAME
si setup= est défini, nommer l'image sur la base de ce nom. par défaut, le nom
est calculé automatiquement
Si un fichier .maven.conf existe dans le répertoire du projet, il est sourcé
pour obtenir les valeurs par défaut des paramètres:
* MAVEN_JAVA -- valeur par défaut de java=
* MAVEN_IMAGE -- valeur par défaut de image=
* MAVEN_MACHINE -- valeur par défaut de machine=
* MAVEN_CMD -- valeur par défaut de mvn=
* MAVEN_SETUP -- valeur par défaut de setup=
* MAVEN_SETUP_IMAGE -- valeur par défaut de setup-image=
## run -- lancer des commandes
## runb -- lancer des commandes
## call -- lancer des commandes ou des fonctions
## callb -- lancer des commandes ou des fonctions
"
}
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## 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
[DEFAULT_PROFILE]=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 ALL_PROFILES; local -a TMPENVIRON TMPARGS"
SHARED_ARGS2=(
-d:,--dist: DIST=
-0,--d10 DIST=d10
-1,--d11 DIST=d11
-2,--d12 DIST=d12
-3,--d13 DIST=d13
--r7,--rhel7 DIST=rhel7
--r8,--rhel8 DIST=rhel8
--r9,--rhel9 DIST=rhel9
--ol7,--oracle7 DIST=ol7
--ol8,--oracle8 DIST=ol8
--ol9,--oracle9 DIST=ol9
-g:,--profile: PROFILE=
-P,--prod PROFILE=prod
-T,--test PROFILE=test
-E,--dtest PROFILE=dtest
-J,--jclain PROFILE=jclain
--devel PROFILE=devel
--all-profiles ALL_PROFILES=1
-e:,--env: '$TMPENVIRON+=("$value_")'
--arg: '$TMPARGS+=("$value_")'
)
TFUNCTIONS=(
# dkbuild
section note info debug
setdists dist
setprofiles profile
default_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
runb
call
callb
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" cwd="$(pwd)"
[ -d "$file" ] && file="$file/dkbuild"
[ -f "$file" ] || die "$file: fichier introuvable"
setx file=abspath "$file"
cd "$(dirname "$file")"
source "$file"
cd "$cwd"
}
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 default
for profile in "$@"; do
if [ "$profile" == DEFAULT ]; then
default=1
continue
fi
parse_profile "$profile" profile version
[ "$profile" == "$PROFILE" ] || continue
[ -z "$version" -o "$version" == "$PVERSION" ] || continue
return 0
done
if [ -n "$default" ]; then
# trouver la dernière occurence du premier profil mentionné. en
# effet, les versions doivent être ordonnées de la plus ancienne à
# la plus récente.
local first_profile last_version
for profile in "${SETPROFILES[@]}"; do
parse_profile "$profile" profile version
if [ -z "$first_profile" ]; then
first_profile="$profile"
last_version="$version"
elif [ "$profile" == "$first_profile" ]; then
last_version="$version"
fi
done
if [ "$first_profile" == "$PROFILE" ]; then
if [ -z "$last_version" -o "$last_version" == "$PVERSION" ]; then
return 0
fi
fi
fi
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=()
DEFAULT_PROFILE=
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 default_profile() {
[ -n "$DEFAULT_PROFILE" ] || DEFAULT_PROFILE="$1"
}
function build() {
AUTOBUILD=
}
function cbuild() {
AUTOBUILD=
}
load_dkbuild
if [ -z "$PROFILE" -a -z "$ALL_PROFILES" -a -n "$DEFAULT_PROFILE" ]; then
enote "Auto-sélection du profil $DEFAULT_PROFILE"
PROFILE="$DEFAULT_PROFILE"
fi
local MANUAL_SETPROFILES=
if [ -z "$SETPROFILES_DONE" -a -n "$PROFILE" ]; then
# Si l'utilisateur spécifie un profil mais qu'aucun profil n'a été
# défini dans la configuration, considérer que c'est cet unique profil
# qui a été défini
SETPROFILES=("$PROFILE")
SETPROFILES_DONE=1
MANUAL_SETPROFILES=1
fi
## 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 le profil
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")
elif [ -n "$ALL_PROFILES" ]; then
# prendre tous les profils comme indiqué
for profile in "${SETPROFILES[@]}"; do
parse_profile "$profile"
PROFILES+=("$profile")
done
else
# prendre le profil par défaut
parse_profile "${SETPROFILES[0]}"
PROFILES=("$profile")
fi
}
load_dkbuild
if [ -n "$MANUAL_SETPROFILES" ]; then
setprofiles "$PROFILE"
fi
## 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 "${DEFAULTS[build_build]-1}" ] || return 0
[ -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 "${DEFAULTS[build_build]-1}" ] || return 0
[ -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 "${DEFAULTS[build_build]-1}" ] || return 0
[ -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
if [ "$tag" == LATEST ]; then
if [ -n "$HAVE_VERSION" -a "$VERSION" == "$LAST_VERSION" ]; then
tag=latest
else
# ignorer le tag LATEST s'il n'est pas applicable
continue
fi
fi
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
if [ "$tag" == LATEST ]; then
# toujours ignorer le tag LATEST dans add_tags
continue
fi
imagetags+=("$imagetag:$tag")
done
[ -n "$VERSION" ] && imagetags+=("$imagetag:$VERSION-$DIST")
if [ -n "$DIST" -a "$DIST" != none -a -z "$HAVE_VERSION" ]; then
imagetags+=("$imagetag:$DIST")
fi
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
[ "$DIST" != none ] && 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}
${progress:+--progress "$progress"}
${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 build="${DEFAULTS[build_build]-1}"
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
[ -n "$build" ] || return
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}
${progress:+--progress "$progress"}
${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 setup_image="$setup_image"
-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
local setup_image="$setup_image"
# lancement dans un container docker à préparer
[ -n "$project_name" ] || setx project_name=get_project_name
setx container_name=get_container_name "$project_name"
[ -n "$setup_image" ] || setup_image="dkbuild_composer_${container_name}_image"
# vérifier l'existence de l'image
setx ctid=docker image ls --format '{{.ID}}' "$setup_image"
# créer le container le cas échéant
if [ -z "$ctid" ]; then
estep "Création de l'image $setup_image à partir de $image"
cmd=(
docker create -it --name "${setup_image}_tmpct"
"${basecmd[@]}"
"$image"
bash -c "$setupscript"
)
setx ctid="${cmd[@]}" &&
docker container start -ai "$ctid" &&
docker container commit "$ctid" "$setup_image" &&
docker container rm "$ctid" || die
fi
# prendre comme image le container créé
image="$setup_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
if [ "$action" == none ]; then
cd "$cwd"
return
fi
local build="${DEFAULTS[build_build]-1}"
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 setup_image="${DEFAULTS[composer_setup-image]}"
local project_name="${DEFAULTS[composer_project-name]}"
if [ -f "$destdir/.composer.yaml" ]; then
eval "$(
COMPOSER_PHP=
COMPOSER_PHP_MAX=
COMPOSER_IMAGE="$COMPOSER_IMAGE"
COMPOSER_SETUP=
COMPOSER_SETUP_IMAGE=
eval "$(<"$destdir/.composer.yaml" grep ^composer_ |
sed 's/^composer_php_min: /COMPOSER_PHP=/
s/^composer_php_max: /COMPOSER_PHP_MAX=/
s/^composer_registry: /registry=/
s/^composer_image: \(.*\)/COMPOSER_IMAGE="${registry:-$REGISTRY}\/\1"/
s/^composer_setup: /COMPOSER_SETUP=/
s/^composer_setup_image: /COMPOSER_SETUP_IMAGE=/')"
[ -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 "$setup" ] && echo_setv setup="$COMPOSER_SETUP"
[ -z "$setup_image" ] && echo_setv setup_image="$COMPOSER_SETUP_IMAGE"
)"
elif [ -f "$destdir/.composer.conf" ]; then
eval "$(
COMPOSER_PHP=
COMPOSER_PHP_MAX=
COMPOSER_IMAGE="$COMPOSER_IMAGE"
COMPOSER_MACHINE=-u
COMPOSER_CMD=
COMPOSER_SETUP=
COMPOSER_SETUP_IMAGE=
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"
[ -z "$setup_image" ] && echo_setv setup_image="$COMPOSER_SETUP_IMAGE"
)"
fi
while [ $# -gt 0 ]; do
case "$1" in
build) build=1;;
build=*) build="${1#build=}";;
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=}";;
setup-image=*) setup_image="${1#setup-image=}";;
project-name=*) project_name="${1#project-name=}";;
*=*) ewarn "composer: $1: argument ignoré";;
*) break;;
esac
shift
done
if [ -z "$build" ]; then
cd "$cwd"
return
fi
if [ "$php" != force -a "$php" != any ]; then
# Si php n'est pas disponible dans le PATH, forcer l'utilisation de
# l'image
progexists php || php=force
fi
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
if [ -z "$image" ]; then
# Si l'image n'est pas définie, calculer une valeur par défaut à
# partir REGISTRY et DIST
image="$(get_default_phpbuilder_image)"
fi
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 "mvn: Java $java introuvable"
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 setup_image="$setup_image"
-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
local setup_image="$setup_image"
# lancement dans un container docker à préparer
[ -n "$project_name" ] || setx project_name=get_project_name
setx container_name=get_container_name "$project_name"
[ -n "$setup_image" ] || setup_image="dkbuild_maven_${container_name}_image"
# vérifier l'existence de l'image
setx ctid=docker image ls --format '{{.ID}}' "$setup_image"
# créer le container le cas échéant
if [ -z "$ctid" ]; then
estep "Création de l'image $setup_image à partir de $image"
cmd=(
docker create -it --name "${setup_image}_tmpct"
"${basecmd[@]}"
"$image"
bash -c "$setupscript"
)
setx ctid="${cmd[@]}" &&
docker container start -ai "$ctid" &&
docker container commit "$ctid" "$setup_image" &&
docker container rm "$ctid" || die
fi
# prendre comme image le container créé
image="$setup_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
if [ "$action" == none ]; then
cd "$cwd"
return
fi
local build="${DEFAULTS[build_build]-1}"
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 setup_image="${DEFAULTS[mvn_setup-image]}"
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=
MAVEN_SETUP_IMAGE=
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"
[ -z "$setup_image" ] && echo_setv setup_image="$MAVEN_SETUP_IMAGE"
)"
fi
while [ $# -gt 0 ]; do
case "$1" in
build) build=1;;
build=*) build="${1#build=}";;
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=}";;
setup-image=*) setup_image="${1#setup-image=}";;
project-name=*) project_name="${1#project-name=}";;
*=*) ewarn "mvn: $1: argument ignoré";;
*) break;;
esac
shift
done
if [ -z "$build" ]; then
cd "$cwd"
return
fi
local version
case "$action" in
java*)
version="${action#java}"
[ -n "$version" ] && java="$version"
set java "${@:2}"
;;
esac
if [ "$java" != force -a "$java" != any ]; then
# Si Java et mvn ne sont pas disponibles dans le PATH, forcer
# l'utilisation de l'image
progexists java || java=force
progexists mvn || java=force
fi
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
if [ -z "$image" ]; then
# Si l'image n'est pas définie, calculer une valeur par défaut à
# partir REGISTRY et DIST
image="$(get_default_javabuilder_image)"
fi
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 runb() {
local build="${DEFAULTS[build_build]-1}"
if [ -z "$build" ]; then
[ $# -eq 0 ] && return 1 || return 0
fi
run "$@"
}
function call() {
edebug "call $(qvals "$@")"
[ $# -eq 0 ] && return 0
"$@"
}
function callb() {
local build="${DEFAULTS[build_build]-1}"
if [ -z "$build" ]; then
[ $# -eq 0 ] && return 1 || return 0
fi
call "$@"
}
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 clean_update clean_opt=-X
local clone_src_only update_src_only update_src sync_src
local build no_cache progress pull_image
local push_image
local -a args; args=(
"${SHARED_ARGS1[@]}" "${SHARED_ARGS2[@]}"
-m:,--machine: machine=
-u,--clean-update clean_update=1
-X,--clean-ignored clean_opt=-X
-x,--clean-untracked clean_opt=-x
--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
--plain-output progress=plain
-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
if [ -z "$clean_update" -a -z "$sync_src" -a -z "$build" -a -z "$push_image" ]; then
sync_src=1
build=1
fi
if [ -n "$build" ]; then
[ -n "$update_src" ] || update_src=1
[ "$update_src" == no ] && update_src=
else
update_src=
fi
[ "$sync_src" == no ] && sync_src=
fi
edebug "build_action"
set_machine "$machine"
ensure_projdir
resolve_dists_profiles
setenv "${TMPENVIRON[@]}"
setarg "${TMPARGS[@]}"
setarg "$@"
if [ -n "$clean_update" ]; then
edebug "clean"
_clean_git_clean -f || die
edebug "update"
git pull || die
edebug "sync"
[ -n "$build" ] && sync_src=1
fi
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 [ -n "$DIST" -a "$DIST-$DVERSION" != "$PREV_DIST" ]; then
[ -n "$PREV_PROFILE" ] && eend
PREV_PROFILE=
[ -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 $clean_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 clean_opt=-X all=
local -a args; args=(
"${SHARED_ARGS1[@]}"
-X,--ignored clean_opt=-X
-x,--untracked clean_opt=-x
-a,--all all=1
)
parse_args "$@"; set -- "${args[@]}"
[ -n "$all" ] && clean_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 [ -n "$DIST" -a "$DIST-$DVERSION" != "$PREV_DIST" ]; then
[ -n "$PREV_PROFILE" ] && eend
PREV_PROFILE=
[ -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;;
--compose-v1) 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'
--compose-v1 '$DOCKER_COMPOSE=(docker-compose)'
)
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