#!/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