diff --git a/.gitignore b/.gitignore index cc7707c..295ace7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # app ##### -/bin/ +/dist/ # editors ######### diff --git a/Makefile b/Makefile index f1c4ab2..4d67add 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,4 @@ # -# dex makefile -# # Makefile reference vars : # https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables # @@ -8,19 +6,19 @@ # # common targets # - -CWD:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) NAMESPACE:=dex -prefix = $(DESTDIR)/usr/local -bindir = $(prefix)/bin +prefix = $(DESTDIR)/usr/local/bin -.PHONY: all clean clean-tests install uninstall dockerbuild-% +.PHONY: all clean clean-tests clean-tests dockerbuild-% install uninstall all: $(NAMESPACE) clean: - rm -rf bin/$(NAMESPACE) + rm -rf $(CURDIR)/dist -clean-dockerbuilds: clean +clean-tests: clean + rm -rf $(CURDIR)/tests/bats/tmp + +clean-dockerbuilds: for id in $$(docker images -q makefile-$(NAMESPACE)-*) ; do docker rmi $$id ; done dockerbuild-%: @@ -31,13 +29,13 @@ dockerbuild-%: $*/ install: $(NAMESPACE) - $(info * installing $(bindir)/$(NAMESPACE)) + $(info * installing into $(prefix)) # use mkdir vs. install -D/d (macos portability) - @mkdir -p $(bindir) - @install bin/$(NAMESPACE) $(bindir)/$(NAMESPACE) + @mkdir -p $(prefix) + @install dist/$(NAMESPACE) $(prefix)/$(NAMESPACE) uninstall: - rm -rf $(bindir)/$(NAMESPACE) + rm -rf $(prefix)/$(NAMESPACE) # # app targets @@ -49,39 +47,37 @@ RELEASE_SHA ?= $(shell git rev-parse --short HEAD) DOCKER_SOCKET ?= /var/run/docker.sock DOCKER_GROUP_ID ?= $(shell ls -ln $(DOCKER_SOCKET) | awk '{print $$4}') -# for docker-for-mac, we also add group-id of 50 ("authedusers") as moby seems to auto bind-mount /var/run/docker.sock w/ this ownership -# @TODO investigate and remove this docker-for-mac kludge -DOCKER_FOR_MAC_WORKAROUND := $(shell if [[ "$$OSTYPE" == darwin* ]] || [[ "$$OSTYPE" == macos* ]]; then echo "--group-add=50" ; fi) +# for docker-for-mac, we also add group-id of 50 ("authedusers") as moby distro seems to auto bind-mount /var/run/docker.sock w/ this ownership +DOCKER_FOR_MAC_WORKAROUND := $(shell [[ "$$OSTYPE" == darwin* || "$$OSTYPE" == macos* ]] && echo "--group-add=50") TEST ?= SKIP_NETWORK_TEST ?= .PHONY: $(NAMESPACE) tests -$(NAMESPACE): - $(info * building bin/$(NAMESPACE) ...) +$(NAMESPACE): clean + $(info * building monolithic dist/$(NAMESPACE)) @( \ - mkdir -p $(CWD)/bin ; \ + cd $(CURDIR) ; \ + mkdir dist; \ sed \ - -e '/\@start/,/\@end/d' \ -e 's|@VERSION@|$(RELEASE_TAG)|' \ -e 's|@BUILD@|$(shell echo "$(RELEASE_SHA)" | cut -c1-7)|' \ - $(CWD)/dex.sh > $(CWD)/bin/$(NAMESPACE) ; \ - find $(CWD)/lib.d/ -type f -name "*.sh" -exec cat {} >> $(CWD)/bin/$(NAMESPACE) + ; \ - echo 'main "$$@"' >> $(CWD)/bin/$(NAMESPACE) ; \ - chmod +x $(CWD)/bin/$(NAMESPACE) ; \ + -e '/\@start/,/\@end/d' \ + main.sh > dist/$(NAMESPACE) ; \ + find lib.d/ -type f -name "*.sh" -exec cat {} >> dist/$(NAMESPACE) + ; \ + echo 'main "$$@"' >> dist/$(NAMESPACE) ; \ + chmod +x dist/$(NAMESPACE) ; \ ) -tests: dockerbuild-tests - @rm -rf $(CWD)/tests/bats/tmp +tests: dockerbuild-tests clean-tests docker run -it --rm -u $$(id -u):$$(id -g) $(DOCKER_FOR_MAC_WORKAROUND) \ --group-add=$(DOCKER_GROUP_ID) \ --device=/dev/tty0 --device=/dev/console \ - -v $(CWD)/:/$(CWD) \ + -v $(CURDIR):/$(CURDIR) \ -v $(DOCKER_SOCKET):/var/run/docker.sock \ -e SKIP_NETWORK_TEST=$(SKIP_NETWORK_TEST) \ - --workdir $(CWD) \ + --workdir $(CURDIR) \ makefile-$(NAMESPACE)-tests bats tests/bats/$(TEST) - rm -rf $(CWD)/tests/bats/tmp # # release targets @@ -94,7 +90,7 @@ RELEASE_VERSION ?= GH_TOKEN ?= GH_URL ?= https://api.github.com GH_UPLOAD_URL ?= https://uploads.github.com -GH_PROJECT:=dockerland/dex +GH_PROJECT:=briceburg/shell-helpers REMOTE_GH:=origin REMOTE_LOCAL:=local @@ -109,23 +105,39 @@ release: MERGE_BRANCH = master release: PRERELEASE = false release: _mkrelease -_mkrelease: RELEASE_SHA = $(shell git rev-parse $(MERGE_BRANCH)) _mkrelease: RELEASE_TAG = v$(RELEASE_VERSION)$(shell $(PRERELEASE) && echo '-pr') _mkrelease: _release_check $(NAMESPACE) - [[ "$$PATH" == *badevops/bin* ]] && docker-machine scp $(CWD)/bin/$(NAMESPACE) node-c:/docker-volumes/files.badevops.com/get.blueacorn.net/$(NAMESPACE)-$(MAKECMDGOALS) - git push $(REMOTE_LOCAL) $(MERGE_BRANCH):$(BRANCH) git push $(REMOTE_GH) $(BRANCH) + $(eval RELEASE_SHA=$(shell git rev-parse $(BRANCH))) $(eval CREATE_JSON=$(shell printf '{"tag_name": "%s","target_commitish": "%s","draft": false,"prerelease": %s}' $(RELEASE_TAG) $(RELEASE_SHA) $(PRERELEASE))) @( \ + cd $(CURDIR) ; \ echo " * attempting to create release $(RELEASE_TAG) ..." ; \ id=$$(curl -sLH "Authorization: token $(GH_TOKEN)" $(GH_URL)/repos/$(GH_PROJECT)/releases/tags/$(RELEASE_TAG) | jq -Me .id) ; \ - [ $$id = "null" ] && id=$$(curl -sLH "Authorization: token $(GH_TOKEN)" -X POST --data '$(CREATE_JSON)' $(GH_URL)/repos/$(GH_PROJECT)/releases | jq -Me .id) ; \ - [ $$id = "null" ] && echo " !! unable to create release -- perhaps it exists?" && exit 1 ; \ - echo " * uploading $(CWD)/bin/$(NAMESPACE) to release $(RELEASE_TAG) ($$id) ..." ; \ - curl -sL -H "Authorization: token $(GH_TOKEN)" -H "Content-Type: text/x-shellscript" --data-binary @"$(CWD)/bin/$(NAMESPACE)" -X POST $(GH_UPLOAD_URL)/repos/$(GH_PROJECT)/releases/$$id/assets?name=$(NAMESPACE).sh &>/dev/null ; \ + if [ $$id = "null" ]; then \ + echo " * attempting to create release $(RELEASE_TAG) ..." ; \ + id=$$(curl -sLH "Authorization: token $(GH_TOKEN)" -X POST --data '$(CREATE_JSON)' $(GH_URL)/repos/$(GH_PROJECT)/releases | jq -Me .id) ; \ + else \ + echo " * attempting to update release $(RELEASE_TAG) ..." ; \ + git push $(REMOTE_GH) :$(RELEASE_TAG) ; \ + curl -sLH "Authorization: token $(GH_TOKEN)" -X PATCH --data '$(CREATE_JSON)' $(GH_URL)/repos/$(GH_PROJECT)/releases/$$id ; \ + fi ; \ + [ $$id = "null" ] && echo " !! unable to create release" && exit 1 ; \ + echo " * uploading dist/$(NAMESPACE) to release $(RELEASE_TAG) ($$id) ..." ; \ + curl -sL -H "Authorization: token $(GH_TOKEN)" -H "Content-Type: text/x-shellscript" --data-binary @"dist/$(NAMESPACE)" -X POST $(GH_UPLOAD_URL)/repos/$(GH_PROJECT)/releases/$$id/assets?name=$(NAMESPACE) &>/dev/null ; \ ) + $(info * publishing to get.iceburg.net/$(NAMESPACE)/latest-$(MAKECMDGOALS)/) + @( \ + cd $(CURDIR)/dist ; \ + for file in * ; do \ + echo "# @$(NAMESPACE)_UPDATE_URL=http://get.iceburg.net/$(NAMESPACE)/latest-$(MAKECMDGOALS)/$$file" >> $$file ; \ + done ; \ + drclone sync . iceburg_s3:get.iceburg.net/$(NAMESPACE)/latest-$(MAKECMDGOALS) ; \ + ) + + # # sanity checks .PHONY: _release_check _gh_check _wc_check diff --git a/README.md b/README.md index c60f90a..3d9d386 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,27 @@ [![Build Status](https://travis-ci.org/dockerland/dex.svg?branch=master)](https://travis-ci.org/dockerland/dex) -Dex runs applications _without_ the need to install them or their dependencies by leveraging [docker](https://www.docker.com/). Dex also makes it easier to containerize and _consistently_ execute -applications, no matter the OS. See our [HOWTO](docs/HOWTO.md#containerize-your-application) to learn about containing -your application for dex. +Dex makes it easy to run applications _without_ the need to install them or +their dependencies by leveraging [docker](https://www.docker.com/) containers. + +It also makes it [easy-peasy](docs/HOWTO.md#containerize-your-application) for tool authors to containerize, distribute, and +execute applications in a _consistent_ and _intentional_ way. + +We provide conveniences around utility _installation_ and +_execution_ -- think `git` and `npm` as opposed to long-running daemons like +`httpd` and `crond` -- and wrote dex to improve our tooling [bootstrap](). It is now; + * non intrusive + * does not conflict with [**or depend upon**] system installed commands + * our developers literally need nothing. not even git, python, or java. + * and super easy to update and install. + ``` + dex install --pulll acme-tools/ + ``` Windowed/X11 applications are supported, so expect `dex run firefox`. [Pipes](https://en.wikipedia.org/wiki/Redirection_%28computing%29#Piping) and [redirection](https://en.wikipedia.org/wiki/Redirection_%28computing%29) behave, so expect _pong_ from `echo 'ping' | docker run sed 's/ping/pong/'`. -Dex is plain [bash](https://www.gnu.org/software/bash/manual/bash.html). In fact -it's a _bashlication_ with a Makefile, modular design, and complete [bats](https://github.com/sstephenson/bats) testing. It can run +Dex is plain [bash](https://www.gnu.org/software/bash/manual/bash.html). It's actually a _bashlication_ with a Makefile, modular design, and complete [bats](https://github.com/sstephenson/bats) testing. It can run _anywhere docker works_, including Windows 10. ## installing dex @@ -33,7 +45,7 @@ docker info && echo "Docker appears working. Lets install dex..." git clone git@github.com:dockerland/dex.git cd dex # run dex, -./dex.sh +./main.sh # -or- install to /usr/local/bin/dex sudo make install ``` @@ -47,44 +59,40 @@ are managed in [source repositories](#source-repositories). ### quickstart +##### run 'ag' (the grep replacement!) from the "extra" repository ```sh -# get help -dex help - -# run 'debian' from any repository -# (first image matching 'debian' is built & executed) -dex run debian - -# run 'ag' (the grep replacement!) from the "extra" repository -dex run extra/ag +echo "hello" > world.txt +dex run extra/ag "hello" +``` -# play sed pong +##### play sed pong +```sh echo 'ping' | dex run sed 's/ping/pong/' +``` + +##### add a custom source repository and install all images from it +```sh +dex repo add acme-tools git@github.com/acme-tools/dex.git +dex install acme-tools/ +``` -# install gitk (defaults to /usr/local/bin/dgitk) and execute it -# (gitk is a windowed application and requires e.g. X) -sudo dex install gitk && dgitk - -# add a local source repository named "dev" and install all images from it -dex source add dev /path/to/my-dex-repo -dex install 'dev/*' - -# install macos-sed to an alternative path, without the 'd' prefix -export PATH=~/bin/macos:$PATH -( - export DEX_BIN_DIR=~/bin/macos/ - dex install --global sed:macos -) -sed +##### install macos-sed to an alternative path, without the 'd' prefix +```sh +DEX_BIN_DIR=/usr/local/bin sudo dex install --global sed:macos +sed --help # ^^^ yay 1993 +``` -# use DOS like a boss +##### use DOS like a boss +```sh +export PATH="~/.dex/bin:$PATH" dex install --global deltree mkdir -p /tmp/dex-makes-it/possible deltree /tmp/dex-makes-it # ^^^ yay 1983 ``` + [docs/HOWTO.md](docs/HOWTO.md) for more. ### source repositories @@ -93,18 +101,21 @@ Dex consults source repositories for the Dockerfile to build application images similar to how yum and apt consult package sources. Thus, __applications available to dex are dictated by source repository checkouts__. -Source Repositories are defined one-per-line in `$DEX_HOME/sources.list` as ` `. URLs may point to the _remote URL_ or _local path_ of a [git repository](https://git-scm.com/) with a `dex-images/` tree containing applications. Use `dex source add` to add additional sources, and `dex help source` for additional information. +Source Repositories are defined one-per-line in `$DEX_HOME/sources.list` as ` `. URLs may point to the _remote URL_ or _local path_ of a [git repository](https://git-scm.com/). Each repository _must_ have a `dex-images/` tree containing images. -[sources.list example](sources.list) - [repository example](https://github.com/dockerland/dex-dockerfiles-core) +Use `dex repo` to manage source repositories. on. -Repository checkouts are performed __once__ when a source is added. Checkouts go to `~/.dex/checkouts/` by default. Use `dex source pull '*'` to checkout the latest from all sources, or `dex run --pull ...` to checkout on-the-fly when running an application. +[sources.list example](sources.list) - [repository example](https://github.com/dockerland/dex-dockerfiles-core) +> Repository checkouts are performed __once__ when a source is added. Use `dex repo pull` to refresh checkout(s), or pass the `--pull` flag to run/ls/install/image commands to perform a checkout _on-the-fly_. ### environmental variables +You may override _default_ application runtime and dex command behavior by specifying environmental variables. [Learn about environmental variables](https://github.com/dockerland/charleston-containers/blob/master/docs/02-concepts.md#environmental-variables) if `export a=b` vs `a=b` is foreign. + #### dex command -variables that globally effect the dex command, e.g. +variables effecting command behavior, e.g. ```sh DEX_BIN_DIR=~/bin/ DEX_BIN_PREFIX=acme- dex install ag @@ -113,7 +124,7 @@ DEX_BIN_DIR=~/bin/ DEX_BIN_PREFIX=acme- dex install ag var | default | description --- | --- | --- -DEX_BIN_DIR | /usr/local/bin | dexecutable installation target directory +DEX_BIN_DIR | $DEX_HOME/bin | dexecutable installation target directory DEX_BIN_PREFIX | d | dexecutable installation prefix DEX_HOME | ~/.dex | dex workspace, where checkouts and sources.list are kept. DEX_NAMESPACE | dex/v1 | prefix used when tagging image builds @@ -123,11 +134,11 @@ DEX_RUNTIME | v1 | runtime api version #### dex runtime -variables that effect dex execution, e.g. +variables effecting runtime behavior, e.g. ```sh -dex install ansible -DEX_DOCKER_ENTYPOINT=bash dansible +dex install --global ansible +DEX_DOCKER_ENTYPOINT=bash ansible # ...we're now in the ansible container's bash shell... ``` @@ -147,8 +158,7 @@ DEX_WINDOW_FLAGS | _runtime_ | applied to windowed containers, typically `-v /tm ### containerize your application -The process is no different than providing a regular -Dockerfile that includes your application and its dependencies. If you have already containerized your application, all you need to do is add dex specific [label(s)](https://docs.docker.com/engine/reference/builder/#/label). +The process is no different than providing a normal Dockerfile including your application and it's dependencies. If you have already containerized your application, all you need to do is add dex specific [label(s)](https://docs.docker.com/engine/reference/builder/#/label). See [docs/HOWTO.md](docs/HOWTO.md#containerize-your-application) for details diff --git a/TODO b/TODO new file mode 100644 index 0000000..ed74201 --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +* use p/ and die/ helpers in v1 runtime +* importance of avoiding real user home -- + * allows for quick distribution of your configurations, for instance through + a "dotfiles" repo + > if using defaults, all application homes get written to ~/.dex/homes diff --git a/dex.sh b/dex.sh deleted file mode 100755 index 6c32b76..0000000 --- a/dex.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -# -# dex -# - -main(){ - - __cmd="main" - __entrypoint="$0 $@" - __build="@BUILD@" - __version="@VERSION@" - - # DEX_BIN_DIR: location where dex installs : /usr/local/bin - # DEX_BIN_PREFIX: prefix of dexecutabls : d - # DEX_HOME: dex workspace : ~/.dex - # DEX_NAMESPACE: prefix used when tagging image builds : dex/v1 - # DEX_NETWORK: enables network fetching : true - # DEX_RUNTIME: runtime api version: v1 - - DEX_VARS=( DEX_BIN_DIR DEX_BIN_PREFIX DEX_HOME DEX_NETWORK DEX_RUNTIME DEX_NAMESPACE ) - dex-vars-init ${DEX_VARS[@]} - - if [ $# -eq 0 ]; then - display_help 2 - else - while [ $# -ne 0 ]; do - case $1 in - image|install|source|run|uninstall|update|vars) - __cmd=$1 ; shift ; main_$__cmd "$@" ;; - - ping) shift ; echo "${@:-pong}" ; exit 0 ;; - help) __cmd=${2:-$__cmd} ; display_help ;; - runfunc) shift ; runfunc "$@" ; exit $? ;; - -h|--help) display_help ;; - -v|--version) log "Dex version $__version build $__build" ;; - -*) unrecognized_flag "$1" ;; - *) unrecognized_arg "$1" ;; - esac - shift - done - fi - - exit $? -} - -#@start dev-mode -# replaced by make (lib.d/ shell scripts get expanded inline) -__cwd=$(dirname $0) -for helper in $(find $__cwd/lib.d/ -type f -name "*.sh"); do - #@TODO check for errors when sourcing here - . $helper -done -main "$@" -#@end dev-mode diff --git a/lib.d/conf.sh b/lib.d/conf.sh new file mode 100644 index 0000000..274f17f --- /dev/null +++ b/lib.d/conf.sh @@ -0,0 +1,86 @@ +main_conf(){ + local operand + local list=() + + [ $# -eq 0 ] && die/help 1 + set -- $(args/normalize_flags_first "" "$@") + while [ $# -ne 0 ]; do + case "$1" in + -h|--help) + die/help ;; + -d|--defaults) + dex/conf-reset ;; + --) + shift ; list=( "$@" ) ; break ;; + -*) + args/unknown "$1" "flag" ;; + vars) + operand="dex/conf-print" ;; + *) + args/unknown "$1" ;; + esac + shift + done + shell/execfn "$operand" "${list[@]}" +} + +dex/conf-init(){ + DEX_HOME="${DEX_HOME:-$HOME/.dex}" + DEX_RUNTIME="${DEX_RUNTIME:-v1}" + DEX_BIN_DIR="${DEX_BIN_DIR:-$DEX_HOME/bin}" + DEX_BIN_PREFIX="${DEX_BIN_PREFIX:-d}" + DEX_NAMESPACE="${DEX_NAMESPACE:-dex/$DEX_RUNTIME}" + DEX_NETWORK=${DEX_NETWORK:-true} + DEX_REGISTRY="${DEX_NETWORK:-dexbuilds/}" + + # ensure DEX_HOME is absolute + is/absolute "$DEX_HOME" || DEX_HOME="$(pwd)/$DEX_HOME" + + # bootstrap internal vars + __checkouts="$DEX_HOME/checkouts" + __sources="$DEX_HOME/sources.list" + __sources_url="${DEX_SOURCES_URL:-https://raw.githubusercontent.com/dockerland/dex/master/${DEX_RUNTIME}-sources.list}" + + __defaults=false + __format= + __force=false + __pull=false + __pulled_repos=() + + mkdir -p $__checkouts || die/perms "unable to create DEX_HOME - $__checkouts" + + # seed sources list if it's missing + [ -e "$__sources" ] || { + __force=true dex/repo-reset + __force=true dex/repo-pull + } +} + +dex/conf-print(){ + [ -n "$1" ] || set -- ${!DEX_*} + local var + + p/comment \ + "DEX_BIN_DIR: installation location (add this to your PATH)" \ + "DEX_BIN_PREFIX: installation prefix. Not applied on 'global' installs." \ + "DEX_HOME: user workspace where checkouts and image homes are stored" \ + "DEX_NAMESPACE: tag prefix used by dex images" \ + "DEX_NETWORK: enables network fetching" \ + "DEX_REGISTRY: default registry prefix used when pulling pre-built images" \ + "DEX_RUNTIME: runtime api version" + + for var in "$@"; do + eval "shell/evaluable_export \"$var\" \"\$$var\"" + done + + shell/evaluable_entrypoint +} + +dex/conf-reset(){ + local var + for var in "${!DEX_*}"; do + unset $var + done + + dex/conf-init +} diff --git a/lib.d/dex-detect.sh b/lib.d/dex-detect.sh deleted file mode 100644 index 3c948e2..0000000 --- a/lib.d/dex-detect.sh +++ /dev/null @@ -1,75 +0,0 @@ - -# dex-detect-imgstr accepts <[repo/]image|*> and sets -# __source_match (source checkout, e.g. 'core', '*') -# __image_match (source checkout image name, e.g. 'alpine', '*') -# __image_tag (source checkout image tag, e.g. 'latest') -dex-detect-imgstr(){ - local vars=( __source_match __image_match __image_tag ) - local debug=${2:-false} - - for var in ${vars[@]}; do - eval $var= - done - - [ -z "$1" ] && return 1 - - IFS='/' - read -r source imagestr <<< "$1" - unset IFS - - if [ -z "$imagestr" ]; then - __source_match="*" - __image_match="$1" - else - __source_match="$source" - __image_match="$imagestr" - - if [ ! -d $DEX_HOME/checkouts/$__source_match ]; then - log "warning, $source is not checked out." - fi - fi - - IFS=':' - read -r image tag <<< "$__image_match" - unset IFS - - if [ -z "$tag" ]; then - __image_tag="latest" - else - __image_match="$image" - __image_tag="$tag" - fi - - # if $2 is true, echo lines for evaluation - $debug && dex-vars-print ${vars[@]} - return 0 -} - - -# dex-detect-sourcestr sets __sources (array of sources.list matches in "name url" format) -# usage: dex-detect-sourcestr -# ex: dex-detect-sourcestr core => 0: __sources=( "core git@github.com:dockerland/dex-dockerfiles-core.git" ) -# ex: dex-detect-sourcestr git@github.com:dockerland/dex-dockerfiles-core.git => 0: __sources=( "core git@github.com:dockerland/dex-dockerfiles-core.git" ) -# ex: dex-detect-sourcestr * => 0: __sources=( "core git@github.com:dockerland/dex-dockerfiles-core.git" "extra:git@github.com dockerland/dex-dockerfiles-extra.git" ) -# ex: dex-detect-sourcestr * => 1: __sources=( ) -dex-detect-sourcestr(){ - __sources=() - local retval=1 - - [ -e $DEX_HOME/sources.list ] || error_noent "missing $DEX_HOME/sources.list" - - while read name url junk ; do - - # skip blank, malformed, or comment lines - if [ -z "$name" ] || [ -z "$url" ] || [[ $name = \#* ]]; then - continue - fi - - if [ "$1" = "*" ] || [ "$name" = "$1" ] || [ "$url" = "$1" ]; then - __sources+=( "$name $url" ) - retval=0 - fi - done < $DEX_HOME/sources.list - - return $retval -} diff --git a/lib.d/dex-image.sh b/lib.d/dex-image.sh deleted file mode 100644 index 917c2f6..0000000 --- a/lib.d/dex-image.sh +++ /dev/null @@ -1,151 +0,0 @@ - - -# dex-image-build expects __imsgstr and builds images(s) from detected sources. -# accepts [optional] namespace, sets __built_images array. -# -# usage: dex-image-build [namespace] -# ex: __imgstr="alpine" ; dex-image-build => 1: __built_images=( "dex/v1/alpine:latest" ) -# ex: __imgstr="alpine" ; dex-image-build dex/v1-install => 1: __built_images=( "dex/v1-install/alpine:latest" ) -# ex: __imgstr="invalid-image-name" ; dex-image-build => 1: __built_images=( ) - -dex-image-build(){ - # when installing, we prefix with "dex/$DEX_RUNTIME-install" - local namespace=${1:-$DEX_NAMESPACE} - __built_images=() - - [ -z "$__imgstr" ] && error_exception \ - "image-build requires an [repository/][:tag] imgstr" - - dex-detect-imgstr $__imgstr || error "lookup failed to parse $__imgstr" - - - log "* building $__source_match/$__image_match images..." - - for repo_dir in $(ls -d $DEX_HOME/checkouts/$__source_match 2>/dev/null); do - for image_dir in $(ls -d $repo_dir/dex-images/$__image_match 2>/dev/null); do - if [ "$__image_tag" = "latest" ]; then - dockerfile="Dockerfile" - else - dockerfile="Dockerfile-$__image_tag" - fi - [ -e $image_dir/$dockerfile ] || continue - - local image=$(basename $image_dir) - local source=$(basename $repo_dir) - local tag="$namespace/$image:$__image_tag" - local random=$(LC_CTYPE=C tr -dc 'a-zA-Z0-9-_' < /dev/urandom | head -c10) - local cachebust= - local pull= - - log "- building $tag" - ( - set -e - cd $image_dir - - # add cachebusting argument if requested/used in Dockerfile - grep -q "^ARG DEXBUILD_NOCACHE" $dockerfile && \ - cachebust="--build-arg DEXBUILD_NOCACHE=$random" - - $__pull_flag && \ - pull="--pull" - - __local_docker build -t $tag $cachebust $pull \ - --label=org.dockerland.dex.build-api=$DEX_RUNTIME \ - --label=org.dockerland.dex.build-imgstr="$__imgstr" \ - --label=org.dockerland.dex.build-tag="$__image_tag" \ - --label=org.dockerland.dex.image=$image \ - --label=org.dockerland.dex.namespace=$namespace \ - --label=org.dockerland.dex.source=$source \ - -f $dockerfile . - ) && __built_images+=( "$tag" ) - - done - done - - if [ ${#__built_images[@]} -gt 0 ]; then - for __image in ${__built_images[@]}; do - # force re-create "build" container - dex-image-build-container $__image true &>/dev/null - log "+ built $__image" - done - return 0 - else - return 1 - fi -} - - -dex-image-ls(){ - local namespace=${1:-$DEX_NAMESPACE} - if $__skip_namespace; then - local filters="--filter=label=org.dockerland.dex.namespace" - else - local filters="--filter=label=org.dockerland.dex.namespace=$namespace" - fi - - if [ ! -z "$__imgstr" ]; then - dex-detect-imgstr $__imgstr - - [ ! "$__source_match" = "*" ] && \ - filters="$filters --filter=label=org.dockerland.dex.source=$__source_match" - - #@TODO support wildcards in image_match by switching to repository:tag form - [ ! "$__image_match" = "*" ] && \ - filters="$filters --filter=label=org.dockerland.dex.image=$__image_match" - - [ ! "$__image_tag" = "*" ] && \ - filters="$filters --filter=label=org.dockerland.dex.build-tag=$__image_tag" - fi - - __local_docker images $QUIET_FLAG $filters -} - - -dex-image-rm(){ - local namespace=${1:-$DEX_NAMESPACE} - local removed_image=false - local force_flag= - $__force_flag && force_flag="--force" - - [ -z "$__imgstr" ] && error_exception \ - "image-rm requires an [repository/][:tag] imgstr" - - QUIET_FLAG="-q" - for image in $(dex-image-ls $namespace); do - # remove any 'build' containers - for container in $(__local_docker ps -aq --filter "ancestor=$image" --filter "name=_dexbuild"); do - __local_docker rm --force $container &>/dev/null - done - - # remove image - __local_docker rmi $force_flag $image && removed_image=true - done - - $removed_image && { - log "removed $__source_match/$__image_match" - exit 0 - } - - error "failed to remove any images matching $__imgstr" -} - -# dex-image-build-container - ensure a container is accessible for an image -# expects image name, prints container sha or returns 1 if no missing. -# -# build containers are useful for pulling files out of a container during -# runtime, e.g. to augment /etc/passwd. prints the sha of build container. -# -# usage: dex-image-build-container [force-recreate] -dex-image-build-container(){ - local name=$(docker_safe_name "$1" "dexbuild") - local recreate=${2:-false} - __image_container= - ( - exec &>/dev/null - $recreate && __local_docker rm --force $name - __local_docker inspect --type container $name || { - __local_docker run --entrypoint=false --name=$name $1 - } - ) - __local_docker inspect -f "{{ .Id }}" --type container $name || return 1 -} diff --git a/lib.d/dex-install.sh b/lib.d/dex-install.sh deleted file mode 100644 index dff38eb..0000000 --- a/lib.d/dex-install.sh +++ /dev/null @@ -1,98 +0,0 @@ -# -# lib.d/dex-install.sh for dex -*- shell-script -*- -# - -dex-install(){ - [ -z "$__imgstr" ] && error_exception \ - "dex-install requires an [repository/][:tag] imgstr" - - [ -d "$DEX_BIN_DIR" ] || error_noent "\$DEX_BIN_DIR $DEX_BIN_DIR is missing" - [ -w "$DEX_BIN_DIR" ] || error_perms "\$DEX_BIN_DIR $DEX_BIN_DIR is not writable" - - local namespace="$DEX_NAMESPACE-install" - - if $__pull_flag; then - dex-detect-imgstr $__imgstr || error "lookup failed to parse $__imgstr" - $__pull_flag && dex-source-pull "$__source_match" - fi - - dex-image-build $namespace || error_exception \ - "failed to build any images matching $__imgstr" - - log "* installing $__source_match/$__image_match images..." - - for imgname in ${__built_images[@]}; do - - local api=$(__local_docker inspect --type image --format "{{ index .Config.Labels \"org.dockerland.dex.api\" }}" $imgname) - local image=$(__local_docker inspect --type image --format "{{ index .Config.Labels \"org.dockerland.dex.image\" }}" $imgname) - local tag=$(__local_docker inspect --type image --format "{{ index .Config.Labels \"org.dockerland.dex.build-tag\" }}" $imgname) - local bin="$DEX_BIN_DIR/${DEX_BIN_PREFIX}${image}-${tag}" - local runtimeFn="$api-runtime" - - if [ -z "$api" ]; then - log "skipping $imgname -- org.dockerland.dex.api label not provided" - continue - elif [ ! "$(type -t $runtimeFn)" = "function" ]; then - log "skipping $imgname -- missing api runtime function ($runtimeFn)" - continue - elif [ -z "$image" ]; then - log "skipping $imgname -- org.dockerland.dex.image label not provided" - continue - elif [ -z "$tag" ]; then - log "skipping $imgname -- org.dockerland.dex.build-tag label not provided" - continue - else - $__force_flag && rm -rf $bin - - if [ -e $bin ]; then - log \ - "! $bin exists" \ - " skipping $image installation" \ - " use --force to overwrite" - else - echo "#!/usr/bin/env bash" > $bin - declare -f __local_docker >> $bin - declare -f __deactivate_machine >> $bin - declare -f dex-image-build-container >> $bin - declare -f docker_safe_name >> $bin - declare -f get_group_id >> $bin - declare -f $runtimeFn >> $bin - echo "__image=\"$imgname\"" >> $bin - echo "$runtimeFn \$@" >> $bin - chmod +x $bin || error_exception "unable to mark $bin executable" - log "+ installed $(basename $bin)" - - dex-install-link $bin ${DEX_BIN_PREFIX}${image} || \ - error_exception "unable to create link to $bin" - fi - fi - - if $__global_flag ; then - dex-install-link $bin $image || \ - error_exception "unable to create global link to $bin" - fi - done -} - -# dex-install-link -dex-install-link(){ - [ -e $1 ] || error_exception "install-link: source $1 does not exist" - local __src_dir=$(dirname $1) - local __src_file=$(basename $1) - ( - cd $__src_dir || exit 1 - - $__force_flag && rm -rf $2 - - if [ -e $2 ] || [ -L $2 ]; then - log \ - "! $__src_dir/$2 exists" \ - " skipped linking $2 to $__src_file" \ - " use --force to overwrite" - else - ln -s $__src_file $2 || exit 1 - log "+ linked $__src_dir/$2 to $__src_file" - fi - ) - return $? -} diff --git a/lib.d/dex-run.sh b/lib.d/dex-run.sh deleted file mode 100644 index e12bdbf..0000000 --- a/lib.d/dex-run.sh +++ /dev/null @@ -1,39 +0,0 @@ -# -# lib.d/dex-run.sh for dex -*- shell-script -*- -# - -dex-run(){ - - [ -z "$__imgstr" ] && error_exception \ - "dex-run requires an [repository/][:tag] imgstr" - - dex-detect-imgstr $__imgstr || error "lookup failed to parse $__imgstr" - __image="$DEX_NAMESPACE/$__image_match:$__image_tag" - - #@TODO test special errcode for wildcard handling - [[ $__image == *"*"* ]] && error "dex-run does not allow wildcards" - - # build image if it is missing - image_api=$(__local_docker inspect --type image --format "{{ index .Config.Labels \"org.dockerland.dex.api\" }}" $__image) - if [ $? -ne 0 ] || $__build_flag ; then - $__pull_flag && dex-source-pull "$__source_match" - dex-image-build || error \ - "unable to build $__image" \ - "is $__image_match:$__image_tag provided by a source?" - - else - [ -z "$image_api" ] && error \ - "the $__image image is missing a org.dockerland.dex.api label" \ - "please ensure you're up to date, rebuild it, or consult image maintainer" - - [ "$image_api" = "$DEX_RUNTIME" ] || log \ - "warning, the $__image image is labeled for a different api." \ - "please ensure you're up to date, rebuild it, or consult image maintainer" \ - "current api: $DEX_RUNTIME" \ - "$__image api: $image_api" - fi - - # __image is built and ready - v1-runtime $@ - return $? -} diff --git a/lib.d/dex-source.sh b/lib.d/dex-source.sh deleted file mode 100644 index 32cbb59..0000000 --- a/lib.d/dex-source.sh +++ /dev/null @@ -1,119 +0,0 @@ -# -# lib.d/dex-remote.sh for dex -*- shell-script -*- -# - -dex-source-add(){ - - { [ -z "$__lookup_name" ] || [ -z "$__lookup_url" ]; } && error_exception \ - error "source-add requires NAME and URL" - - if $__force_flag; then - dex-source-rm "$__lookup_name" - dex-source-rm "$__lookup_url" - elif dex-detect-sourcestr "$__lookup_name" || dex-detect-sourcestr "$__lookup_url" ; then - error_exception "refusing to add $__lookup_name -- duplicate name or url" - fi - - [ -e $DEX_HOME/checkouts/$__lookup_name ] && error_exception \ - "refusing to add $__lookup_name" \ - "checkout $DEX_HOME/checkouts/$__lookup_name already exists" \ - "use --force flag to overwrite" - - clone_or_pull "$__lookup_url" "$DEX_HOME/checkouts/$__lookup_name" || error \ - "unable to add respository" - - echo "$__lookup_name $__lookup_url" >> $DEX_HOME/sources.list || error \ - "unable to update sources.list" - - log "$__lookup_name added" -} - -dex-source-ls(){ - [ ! -e $DEX_HOME/sources.list ] && error_noent \ - "missing $DEX_HOME/sources.list" - - cat $DEX_HOME/sources.list | - while read __source_name __source_url junk ; do - - # skip blank, malformed, or comment lines - if [ -z "$__source_name" ] || [ -z "$__source_url" ] || [[ $__source_name = \#* ]]; then - continue - fi - - printf "$__source_name $__source_url\n" - done -} - -# dex-source-pull updates a source checkout. updates all sources if * is passed. -# usage: dex-source-pull -# ex: dex-source-pull core -# ex: dex-source-pull git@github.com:dockerland/dex-dockerfiles-core.git -# ex: dex-remote pull * -dex-source-pull(){ - [ -z "$1" ] || __sourcestr="$1" - - [ -z "$__sourcestr" ] && error_exception \ - "source-pull requires a repository name or URL" - - dex-detect-sourcestr "$__sourcestr" || { - [ -z "$1" ] && error "no match for $__sourcestr in sources.list" - log "$__sourcestr not found, skipping pull..." - return 1 - } - - for __source in "${__sources[@]}"; do - read -r __source_name __source_url <<< "$__source" - - if ! $__force_flag && is_dirty $__checkouts/$__source_name ]; then - error "$DEX_HOME/checkouts/$__source_name has local changes" \ - "pass --force to force update, or reset/upstream your changes" - fi - - ! $DEX_NETWORK && [[ ! "$__source_url" == /* ]] && { - log "skipping $__source_name -- networking disabled" - continue - } - - - clone_or_pull $__source_url $__checkouts/$__source_name $__force_flag || \ - error "error pulling $__source_name" - - log "$__source_name updated" - done -} - - -dex-source-rm(){ - [ -z "$1" ] || __sourcestr=$1 - - [ -z "$__sourcestr" ] && error_exception \ - "source-rm requires a repository name or URL" - - dex-detect-sourcestr "$__sourcestr" || { - [ -z "$1" ] && error "no match for $__sourcestr in sources.list" - log "$__sourcestr not found, skipping removal..." - return 1 - } - - for __source in "${__sources[@]}"; do - read -r __source_name __source_url <<< "$__source" - - if $__force_flag; then - rm -rf $DEX_HOME/checkouts/$__source_name 2>/dev/null - elif [ -d $DEX_HOME/checkouts/$__source_name ]; then - - [ ! -w $DEX_HOME/checkouts/$__source_name ] && error_perms \ - "$DEX_HOME/checkouts/$__source_name" is not writable - - is_dirty $DEX_HOME/checkouts/$__source_name ] && error \ - "$DEX_HOME/checkouts/$__source_name has local changes" \ - "pass --force to force removal, or reset/upstream your changes" - - rm -rf $DEX_HOME/checkouts/$__source_name - fi - - sed_inplace $DEX_HOME/sources.list "/$__source_name /d" - log "removed $__source_name" - - done -} diff --git a/lib.d/dex-vars.sh b/lib.d/dex-vars.sh deleted file mode 100644 index 10a739c..0000000 --- a/lib.d/dex-vars.sh +++ /dev/null @@ -1,48 +0,0 @@ -# -# lib.d/dex-vars.sh for dex -*- shell-script -*- -# - -dex-vars-print(){ - while [ $# -ne 0 ]; do - eval "printf \"$1=\$$1\n\"" - shift - done -} - - -dex-vars-reset(){ - for var in "${DEX_VARS[@]}"; do - unset $var - done -} - - -dex-vars-init(){ - while [ $# -ne 0 ]; do - case $1 in - DEX_BIN_DIR) eval "$1=\${$1:-/usr/local/bin}" ;; - DEX_BIN_PREFIX) eval "$1=\${$1:-d}" ;; - DEX_HOME) eval "$1=\${$1:-~/.dex}" ;; - DEX_NAMESPACE) eval "$1=\${$1:-dex/\$DEX_RUNTIME}" ;; - DEX_NETWORK) eval "$1=\${$1:-true}" ;; - DEX_RUNTIME) eval "$1=\${$1:-v1}" ;; - *) error_exception "$1 has no default configuration value" ;; - esac - shift - done - - # bootstrap internal vars - __checkouts=$DEX_HOME/checkouts -} - - -dex-vars-shellprint(){ - # @TODO -- shell detection for fish|export - while [ $# -ne 0 ]; do - eval "printf \"export $1=\$$1\n\"" - shift - done - - printf "# Run this command to configure your shell: \n" - printf "# eval \$($__entrypoint)\n\n" -} diff --git a/lib.d/dex.sh b/lib.d/dex.sh deleted file mode 100644 index 3929753..0000000 --- a/lib.d/dex.sh +++ /dev/null @@ -1,55 +0,0 @@ -# usage: dex-fetch -dex-fetch(){ - - ! $DEX_NETWORK && \ - log "refused to fetch $2 from $1" "networking disabled" && \ - return 1 - - fetch-url $1 $2 -} - -dex-init(){ - - [ -d $DEX_HOME ] || mkdir -p $DEX_HOME || error_perms \ - "could not create working directory \$DEX_HOME" - - [ -d $DEX_HOME/checkouts ] || mkdir -p $DEX_HOME/checkouts || error_perms \ - "could not create checkout directory under \$DEX_HOME" - - ( type docker >/dev/null 2>&1 ) || error_noent \ - "dex requires docker" - - [ -e $DEX_HOME/sources.list ] || dex-init-sources - - for path in $DEX_HOME $DEX_HOME/checkouts $DEX_HOME/sources.list; do - [ -w $path ] || error_perms "$path is not writable" - done - - return 0 -} - -dex-init-sources(){ - rm -rf $DEX_HOME/sources.list.fetched &>/dev/null - - if [ ! -e $DEX_HOME/sources.list ]; then - if dex-fetch "https://raw.githubusercontent.com/dockerland/dex/master/sources.list" $DEX_HOME/sources.list.fetched ; then - cat $DEX_HOME/sources.list.fetched > $DEX_HOME/sources.list || error_perms \ - "error writing sources.list from fetched file" - else - dex-sources-cat > $DEX_HOME/sources.list || error_perms \ - "error creating $DEX_HOME/sources.list" - fi - fi -} - -dex-sources-cat(){ - cat <<-EOF -# -# dex sources.list -# - -core git@github.com:dockerland/dex-dockerfiles-core.git -extra git@github.com:dockerland/dex-dockerfiles-extra.git - -EOF -} diff --git a/lib.d/display_help/display_help.sh b/lib.d/display_help/display_help.sh deleted file mode 100644 index 32809a0..0000000 --- a/lib.d/display_help/display_help.sh +++ /dev/null @@ -1,12 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help() { - [ "$(type -t display_help_$__cmd)" = "function" ] || error \ - "missing helpfile for $__cmd" "is $__cmd a valid command?" - - display_help_$__cmd - [ -z "$1" ] && exit 0 - exit $1 -} diff --git a/lib.d/display_help/display_help_image.sh b/lib.d/display_help/display_help_image.sh deleted file mode 100644 index 8803093..0000000 --- a/lib.d/display_help/display_help_image.sh +++ /dev/null @@ -1,46 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help_image(){ - cat <<-EOF - -Piping hot docker executables to your door. - -images don't make themselves, dex will. - -Usage: dex image [options] - - # build the macos version of sed from any repository (first found) - dex build sed:macos - - # build 'sed' from the "extras" repository - dex image build extras/sed - - # build all images from the "extras" repository - dex image build extras/* - -Commands: - - build * Builds an image. Optionally slash-pass repository. - rm * Remove an image. Optionally slash-pass repository. - ls Lists images dex has built. - -* is a multi-form string defined as "[source/][:tag]" and is - used to lookup image(s), optionally filtering by source name and/or tag - -Options: - - -h|--help|help Display help - - -a|--all Return images from all namespaces (all api versions, - all installed states) when searching for images - - -f|--force When removing, persisted runs will be deleted. - When building, ignore API version check. - - -q|--quiet When listing images, only show numeric IDS - - -EOF -} diff --git a/lib.d/display_help/display_help_install.sh b/lib.d/display_help/display_help_install.sh deleted file mode 100644 index 5a04b54..0000000 --- a/lib.d/display_help/display_help_install.sh +++ /dev/null @@ -1,48 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help_install(){ - cat <<-EOF -Piping hot docker executables to your door. - -Installing dexecutables builds their image and copies their runtime script to -\$DEX_BIN_DIR (usually /usr/local/bin). The script is prefixed using -\$DEX_BIN_PREFIX (usually 'd'), so installing 'sed' creates /usr/local/bin/dsed. - -You may 'globally' install, which will create a symlink to the dexecutable -without a prefix, so /usr/local/bin/sed points to /usr/local/bin/dsed and -can be executed as plain old 'sed' as if it were installed locally. - -Be sure your PATH prioritizes \$DEX_BIN_DIR if you want to run dexecutable -versions instead of OS installed ones. - -Installed executables - * launch faster than 'dex run ', as they avoid the repository search. - * always execute the same [version] of the image built when installed -- - whereas 'dex run ' attempts to run the latest after a repository pull - -Usage: dex install [options] * - - # Install a dexecutable (creates 'dsed' and 'dsed-macos' in /usr/local/bin) - dex install sed - dex install sed:macos - - # Install a dexecutable from a specific source repository - dex install extras/sed - - # Globally install a dexecutable (creates 'sed' in /usr/local/bin) - dex install --global sed:macos - -* is a multi-form string defined as "[source/][:tag]" and is - used to lookup image(s), optionally filtering by source name and/or tag - -Options: - - -h|--help|help Display help - -p|--pull Refresh checkout(s) before building+installing - -g|--global Globally install the dexecutable - -f|--force Overwrite target(s) if they already exist. - -EOF -} diff --git a/lib.d/display_help/display_help_main.sh b/lib.d/display_help/display_help_main.sh deleted file mode 100644 index 6168527..0000000 --- a/lib.d/display_help/display_help_main.sh +++ /dev/null @@ -1,34 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help_main(){ - cat <<-EOF - -Piping hot docker executables to your door. - -Usage: dex [options] - - dex run --pull sed - dex run sed s/foo/bar/ <(echo 'foo') - dex run sed:macos -h - -Commands: - - help Display help for a particular command - image [options] Build and maintain images - install * [options] Install a dexecutable to \$DEX_BIN_DIR - source [options] Manage source repositories - run * [options] Execute an image - uninstall * [options] Uninstall a dexecutable - vars [options] print configuration variables (and/or defaults) - -* is a multi-form string defined as "[source/][:tag]" and is - used to lookup image(s), optionally filtering by source name and/or tag - -Options: - - -h|--help Display help - -EOF -} diff --git a/lib.d/display_help/display_help_run.sh b/lib.d/display_help/display_help_run.sh deleted file mode 100644 index 82b2f16..0000000 --- a/lib.d/display_help/display_help_run.sh +++ /dev/null @@ -1,47 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help_run(){ - cat <<-EOF - -Piping hot docker executables to your door. - -'dex run ' executes images as if the contained application was locally -installed. We call these images "docker executables" or "dexecutables". - -Images are built from Dockerfiles kept in "source repositories" in the -\$DEX_HOME/sources.list file and managed by the 'dex source' command. - -Dex run searches images from source checkouts matching and executes the -first found. Limit searching by slash-passing a source and/or tag. Using -'dex install' will bypass it entirely. - -'dex run' will automatically build the matching image from its Dockerfile on -first run -- introducing a delay. Use 'dex image' to build and maintain images. - -Usage: dex run * [options] - - # Run a dexecutable (below examples run sed) - dex run sed - echo 'foo' | dex run sed s/foo/bar/ - dex run sed s/foo/bar/ <(echo 'foo') - - # Run a tagged version of a dexecutable (below maps to sed/Dockerfile.macos) - dex run sed:macos -h - - # Run a dexecutable from a particular source repository - dex run extra/gitk - -* is a multi-form string defined as "[source/][:tag]" and is - used to lookup image(s), optionally filtering by source name and/or tag - -Options: - - -h|--help Display help - -b|--build Always build the image before executing - -p|--pull Refresh checkout(s) before executing, implies --build - --persist Persist the container after it exits - -EOF -} diff --git a/lib.d/display_help/display_help_source.sh b/lib.d/display_help/display_help_source.sh deleted file mode 100644 index 6b00db5..0000000 --- a/lib.d/display_help/display_help_source.sh +++ /dev/null @@ -1,49 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help_source(){ - cat <<-EOF - -Piping hot docker executables to your door. - -"dexecutables" are images built and executed from Dockerfiles contained within -git "source repositories" defined in \$DEX_HOME/sources.list. - -The sources.list file lists repositories, one per line, in space-delimited -" " format. "name" must be alphanumeric and "url" must be the -repository URL (remote or a local path). E.g. - - reponame git@github.com:User/reponame.git - reponame /path/to/reponame - -Repositories are cloned under \$DEX_HOME/checkouts. If a local path is used, -a shared clone will be made. - -Removing or adding repositories will not effect built images, allowing -installed dexecutables to function as normal. Use the "dex image" command to -manage images built from sources. - -Usage: dex source [options] - - # Add an additional dexecutable source repository named "extras" - dex source add extras git@github.com:dockerland/dex-dockerfiles-extra.git - -Commands: - - add Add (and pulls) a dexecutable source repository. - pull [sourcestr] Pull (refresh) sources, optionally matching name || url - rm Remove source repository matching name || url. - ls List available source repositories - -* is a multi-form string written as and is used to - lookup source(s) by name, url, or wildcard ('*') to match all. - -Options: - - -h|--help|help Display help - -f|--force When removing or pulling, discard working copy changes - When adding, first remove any matching name - -EOF -} diff --git a/lib.d/display_help/display_help_vars.sh b/lib.d/display_help/display_help_vars.sh deleted file mode 100644 index 4dd675b..0000000 --- a/lib.d/display_help/display_help_vars.sh +++ /dev/null @@ -1,36 +0,0 @@ -# -# lib.d/display_help.sh for dex -*- shell-script -*- -# - -display_help_vars(){ - cat <<-EOF - -Piping hot docker executables to your door. - -Variable Defaults: - DEX_HOME: ~/.dex - DEX_BIN_DIR: '/usr/local/bin' - DEX_BIN_PREFIX: 'd' - DEX_NETWORK: true - DEX_RUNTIME: v1 - DEX_NAMESPACE: dex/$DEX_RUNTIME - -Usage: dex vars [var] [options] - - # print all dex configuration vars and their resolved value - dex vars all - - # print specific vars - dex vars DEX_BIN_DIR DEX_BIN_PREFIX - - # print variable defaults (evalute output to restore) - dex vars -d all - dex vars -d DEX_HOME - -Options: - - -h|--help|help Display help - -d|--defaults evaluate output to set/restore defaults. - -EOF -} diff --git a/lib.d/help/help_conf.sh b/lib.d/help/help_conf.sh new file mode 100644 index 0000000..06d2976 --- /dev/null +++ b/lib.d/help/help_conf.sh @@ -0,0 +1,30 @@ +p/help_conf(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +Usage: + dex conf [options...] + +Options: + -h|--help + Displays help + + -d|--defaults + Temporarily resets the current environment and prints default values + +Commands: + vars [-d|--defaults] [--] [list...] + Prints configuration variables as evaluable output + Examles: + # print current environment configuration variables + dex conf vars + + # print configuration variable defaults + dex conf vars --defaults + + # prints the default value for DEX_BIN_PREFIX + dex conf --defaults vars -- DEX_BIN_PREFIX + +EOF +} diff --git a/lib.d/help/help_image.sh b/lib.d/help/help_image.sh new file mode 100644 index 0000000..af599e8 --- /dev/null +++ b/lib.d/help/help_image.sh @@ -0,0 +1,50 @@ +p/help_image(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +About image: + The image command is a plumbing command to build and maintain images. It's + used by the dex 'run' and 'install' commands. + +Usage: + dex image [options...] + +Options: + -h|--help + Displays help + +Commands: + build [-p|--pull] <[repository]/image[:tag]...> + Build image(s) from source repositories. Use [repository]/ prefix to specify + a repository. If no repository is specified, dex builds first match found. + + Build all images in a repository by leaving off image name. + + Examles: + dex image build sed:macos ansible edit + dex image build extra/sed + dex image build extra/ + + ls [-q|--quiet] [-a|--all] [[repository]/image[:tag]...] + List built image(s). Use [repository]/ prefix to specify a repository. + --quiet limits output to name only. + --all additionally lists installed images across runtimes + + Examples: + dex image ls -q + dex image ls extra/ + + rm [-f|--force] [-a|--all] [[repository]/image[:tag]...] + Removes built image(s). Use [repository]/ prefix to specify a repository. + Remove all images built from a repository by leaving off image name. + Force skips prompts and force removes images. + Use --all to remove all images (across runtimes and installed images) + + Examples: + dex image rm sed:macos ansible edit + dex image rm extra/ + yes "y" | dex image rm + +EOF +} diff --git a/lib.d/help/help_install.sh b/lib.d/help/help_install.sh new file mode 100644 index 0000000..f2e0c1e --- /dev/null +++ b/lib.d/help/help_install.sh @@ -0,0 +1,39 @@ +p/help_install(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +About install: + +Images are built and installed as prefixed scripts to \$DEX_BIN_DIR. Installing +'sed' will create \${DEX_BIN_DIR}/\${DEX_BIN_PREFIX}sed, e.g. + $DEX_BIN_DIR/${DEX_BIN_PREFIX}sed + +Prioritize \$DEX_BIN_DIR in your \$PATH to prefer dex installed images to system +installed binaries. + +Installed images launch faster than 'dex run ' as they avoid a repository +search. They also guarantee execution of the same image built when installed. + +Usage: + dex install [options...] <[repository]/image[:tag]...> + +Options: + -h|--help + Displays help + -f|--force + Overwrites targets in \$DEX_BIN_DIR if they already exist. + -g|--global + Creates a non-prefixed symlink to the installed script. + -p|--pull + Pull (refresh) repositories before building. + +Examples: + # Install sed (creates 'dsed' and 'dsed-macos' in /usr/local/bin) + dex install sed sed:macos + + # Globally install all images from the extra repository + dex install --global extra/ + +EOF +} diff --git a/lib.d/help/help_ls.sh b/lib.d/help/help_ls.sh new file mode 100644 index 0000000..ed8baa1 --- /dev/null +++ b/lib.d/help/help_ls.sh @@ -0,0 +1,33 @@ +p/help_ls(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +About ls: + +Lists images available in source repositories. + +Usage: + dex ls [options...] [[repository]/image[:tag]...] + +Options: + -h|--help + Displays help + -p|--pull + pull (refresh) repositories. + +Examples: + # List all available images + dex ls + + # List images in the extra repository, pulling it first + dex ls --pull extra/ + + # List 'sed' images across all repositories + dex ls sed + + # list all available macos variants + dex ls :macos + +EOF +} diff --git a/lib.d/help/help_main.sh b/lib.d/help/help_main.sh new file mode 100644 index 0000000..44c60f0 --- /dev/null +++ b/lib.d/help/help_main.sh @@ -0,0 +1,33 @@ +p/help_main() { + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +Usage: + dex [options...] + +Examles: + # edit (from DOS!) + dex run --pull edit a.txt + + # piping to SED (from darwin/macos) + echo "foo" | dex run sed:macos s/foo/bar/ + +Options: + -h|--help + Displays help + + -v|--version|version + Print version and exit + +Commands: + conf Prints configurations + help Display help for a particulat command + image Build and maintain images from source repositories + install Installs an image to \$DEX_BIN_DIR + ls Lists images avaialbe in source repositories + repo Manage source repositories + run Executes an image + +EOF +} diff --git a/lib.d/help/help_ps.sh b/lib.d/help/help_ps.sh new file mode 100644 index 0000000..2341d60 --- /dev/null +++ b/lib.d/help/help_ps.sh @@ -0,0 +1,29 @@ +p/help_ls(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +About ps: + +Lists executing and persisted and dex containers + +Usage: + dex ps [options...] [[repository]/image[:tag]...] + +Options: + -h|--help + Displays help + + -a|--all + additionally lists containers across runtimes + + -q|--quiet + limit output to container shas + +Examples: + + # List running and persisted containers from the extra/ repository + dex ps extra/ + +EOF +} diff --git a/lib.d/help/help_repo.sh b/lib.d/help/help_repo.sh new file mode 100644 index 0000000..efe293e --- /dev/null +++ b/lib.d/help/help_repo.sh @@ -0,0 +1,55 @@ +p/help_repo(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +About repo: + dex images are built from Dockerfiles in git repositories. + + Repositories are defined one-per-line in \$DEX_HOME/sources.list in a + space-delimited " " format -- and this command manages them. + + Repositories are cloned to \$DEX_HOME/checkouts when added, and refreshed when + the --pull flag is passed to dex run|install, or explicitly via dex repo pull. + + Removing or adding repositories will not effect installed images, allowing + dexecutables to function as normal. Use the "dex image" command to + manage images built from sources. + +Usage: + dex repo [options...] + +Options: + -h|--help + Displays help + +Commands: + add [-f|--force] + Add a source repository. must be alphanumeric, and can be + a git remote or local path. Forcing overwrites existing . + Examples: + dex repo add extra git@github.com:dockerland/dex-dockerfiles-extra.git + dex repo add local /path/to/my/repo + + ls [-d|--defaults] [name(s)...] + Print repositories from \$DEX_HOME/sources.list. Passing name(s) limits the + listing. Passing defaults prints the $DEX_RUNTIME default sources.list. + + pull [-f|--force] [name(s)...] + Pull (refresh) named repositories, or all repositories if no name passed. + Forcing overwrites any working copy changes in \$DEX_HOME/checkouts/ + Examples: + dex repo pull + dex repo pull local extra + + rm [-f|--force] + Remove named source repositories. Forcing disregards working copy changes. + Examples: + dex repo rm local --force + + reset [-f|--force] + Reset sources.list to $DEX_RUNTIME default. Forcing supresses prompts and + enables networking. + +EOF +} diff --git a/lib.d/help/help_run.sh b/lib.d/help/help_run.sh new file mode 100644 index 0000000..4eb9fc2 --- /dev/null +++ b/lib.d/help/help_run.sh @@ -0,0 +1,56 @@ +p/help_run(){ + cat <<-EOF + +dex - run applications without installing them or their dependencies. + +About run: + +Dex searches repository checkouts for a matching image and executes the first +found. Limit searching by passing a repo and/or tag. + +Dex builds images the first time they are run -- introducing a delay. Use 'dex +install' to circumvent this. Force a re-build by passing --build or --pull. + +Usage: + dex run [options...] <[repository]/image[:tag]...> + +Options: + -h|--help + Displays help + -b|--build + Force a re-build + -p|--pull + Force a rebuild, and pull (refresh) repositories. + --persist + Persist the container (do not remove it after it exits) + -i|-t|-it|--interactive + Force an interactive TTY + --cmd + Provide an alternative CMD + --entrypoint + Provide an alternative ENTRYPOINT + --home + Provide an alternative home directory (on the host machine) + Defaults to $DEX_HOME/homes/ (or what is provided by container label) + --log-driver + Provide an alternative log driver + Defaults to none (or what is provided by container label) + --gid|--group + Provide an alternative GID to run container as + --uid|--user + Provide an alternative UID to run container as + --workspace + Provide an alternative workspace directory (on the host machine) + Defaults to CWD ($(pwd)) + +Examples: + dex run debian ls + dex run ag "the quick brown fox" + dex run extra/gitk + dex run sed:macos -h + + # piping and redirection + echo 'foo' | dex run sed s/foo/bar/ + dex run sed s/foo/bar/ <(echo 'foo') +EOF +} diff --git a/lib.d/display_help/display_help_uninstall.sh b/lib.d/help/help_uninstall.sh similarity index 85% rename from lib.d/display_help/display_help_uninstall.sh rename to lib.d/help/help_uninstall.sh index 70cd22c..ddfc715 100644 --- a/lib.d/display_help/display_help_uninstall.sh +++ b/lib.d/help/help_uninstall.sh @@ -1,10 +1,9 @@ # -# lib.d/display_help.sh for dex -*- shell-script -*- +# lib.d/p/help.sh for dex -*- shell-script -*- # -display_help_uninstall(){ +p/help_uninstall(){ cat <<-EOF -Piping hot docker executables to your door. Leave not a trace diff --git a/lib.d/helpers/cli.sh b/lib.d/helpers/cli.sh deleted file mode 100644 index 379d94b..0000000 --- a/lib.d/helpers/cli.sh +++ /dev/null @@ -1,117 +0,0 @@ -# -# lib.d/helpers/cli.sh for dex -*- shell-script -*- -# - -# normalize_flags - normalize POSIX short and long flags for easier parsing -# usage: normalize_flags [...] -# : string of short flags requiring an argument. -# : flag string(s) to normalize, typically passed as "$@" -# examples: -# normalize_flags "" "-abc" -# => -a -b -c -# normalize_flags "om" "-abcooutput.txt" "--def=jam" "-mz" -# => -a -b -c -o output.txt --def jam -m z" -# normalize_flags "om" "-abcooutput.txt" "--def=jam" "-mz" "--" "-abcx" "-my" -# => -a -b -c -o output.txt --def jam -m z -- -abcx -my" -normalize_flags(){ - local fargs="$1" - local passthru=false - local output="" - shift - for arg in $@; do - if $passthru; then - output+=" $arg" - elif [ "--" = "$arg" ]; then - passthru=true - output+=" --" - elif [ "--" = ${arg:0:2} ]; then - output+=" ${arg%=*}" - [[ "$arg" == *"="* ]] && output+=" ${arg#*=}" - elif [ "-" = ${arg:0:1} ]; then - local p=1 - while ((p++)); read -n1 flag; do - [ -z "$flag" ] || output+=" -$flag" - if [[ "$fargs" == *"$flag"* ]]; then - output+=" ${arg:$p}" - break - fi - done < <(echo -n "${arg:1}") - else - output+=" $arg" - fi - done - printf "%s" "${output:1}" -} - -# normalize_flags_first - like normalize_flags, but outputs flags first. -# usage: normalize_flags [...] -# : string of short flags requiring an argument. -# : flag string(s) to normalize, typically passed as "$@" -# examples: -# normalize_flags_first "" "-abc command -xyz otro" -# => -a -b -c -x -y -z command otro -# normalize_flags_first "" "-abc command -xyz otro -- -def xyz" -# => -a -b -c -x -y -z command otro -- -def xyz - -normalize_flags_first(){ - local fargs="$1" - local output="" - local cmdstr="" - local passthru=false - shift - for arg in $(normalize_flags "$fargs" "$@"); do - [ "--" = "$arg" ] && passthru=true - if $passthru || [ ! "-" = ${arg:0:1} ]; then - cmdstr+=" $arg" - continue - fi - output+=" $arg" - done - printf "%s%s" "${output:1}" "$cmdstr" -} - -# set_cmd: loops through a list of commands, prefering the "prefixed" version(s) -# sets `__cmd` to first-found matching command. uses __cmd_prefix -# returns 1 if no suitable command found. -set_cmd(){ - __cmd= - local path= - for lookup in $@; do - type ${__cmd_prefix}${lookup} &>/dev/null && { - __cmd=${__cmd_prefix}${lookup} - return 0 - } - done - - for lookup in $@; do - type $lookup &>/dev/null && { - __cmd=$lookup - return 0 - } - done - - return 1 -} - -runfunc(){ - [ "$(type -t $1)" = "function" ] || error \ - "$1 is not a valid runfunc target" - - eval "$@" -} - -unrecognized_flag(){ - printf "\n\n$1 is an unrecognized flag\n\n" - display_help 2 -} - -unrecognized_arg(){ - - if [ $__cmd = "main" ]; then - printf "\n\n$1 is an unrecognized command\n\n" - else - printf "\n\n$1 is an unrecognized argument\n\n" - fi - - display_help 2 -} diff --git a/lib.d/helpers/crossplatform.sh b/lib.d/helpers/crossplatform.sh deleted file mode 100644 index 26758c6..0000000 --- a/lib.d/helpers/crossplatform.sh +++ /dev/null @@ -1,31 +0,0 @@ - -# get_group_id accepts and outputs group id, empty if not found. -get_group_id(){ - if type getent &>/dev/null; then - getent group $1 | cut -d: -f3 - elif type dscl &>/dev/null; then - dscl . -read /Groups/$1 PrimaryGroupID 2>/dev/null | awk '{ print $2 }' - else - python -c "import grp; print(grp.getgrnam(\"$1\").gr_gid)" 2>/dev/null - fi -} - - -# sed_inplace : in place file substitution -############################################ -# -# usage: sed_inplace "file" "sed regex pattern" -# ex: sed_inplace "/tmp/file" "s/CLIENT_CODE/ACME/g" -# ex: sed_inplace "/tmp/file" "/pattern_to_remove/d" -# -sed_inplace(){ - local sed= - local sed_flags="-r -i" - - for sed in gsed /usr/local/bin/sed sed; do - type $sed &>/dev/null && break - done - - [ "$sed" = "sed" ] && [[ "$OSTYPE" =~ darwin|macos* ]] && sed_flags="-i '' -E" - $sed $sed_flags "$2" $1 -} diff --git a/lib.d/helpers/docker.sh b/lib.d/helpers/docker.sh deleted file mode 100644 index cf1eb4e..0000000 --- a/lib.d/helpers/docker.sh +++ /dev/null @@ -1,29 +0,0 @@ -__local_docker(){ - ( - __deactivate_machine - exec docker "$@" - ) -} - -__local_docker_compose(){ - ( - __deactivate_machine - exec docker-compose "$@" - ) -} - -__deactivate_machine(){ - # @TODO support boot2docker / concept of "default" machine - type docker-machine &>/dev/null && { - eval $(docker-machine env --unset --shell bash) - return - } - # lets be safe and unset if missing docker-machine - unset DOCKER_HOST DOCKER_TLS_VERIFY DOCKER_CERT_PATH DOCKER_MACHINE_NAME -} - -docker_safe_name(){ - local name="$@" - set -- "${name:0:1}" "${name:1}" - printf "%s%s" "${1//[^a-zA-Z0-9]/0}" "${2//[^a-zA-Z0-9_.-]/_}" -} diff --git a/lib.d/helpers/downstream-helpers b/lib.d/helpers/downstream-helpers new file mode 100755 index 0000000..bb7a909 --- /dev/null +++ b/lib.d/helpers/downstream-helpers @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# shell-helpers downstreamer version v1 +# https://github.com/briceburg/shell-helpers +# Copyright 2016-present Brice Burgess, Licensed under the Apache License 2.0 + +# * requires curl +# +# place in the directory containing shell-helpers library files and execute to +# fetch the latest release of all shell-helpers files. +# +# alternatively pass the path of directory containing shell-helpers files, e.g. +# ./downstream-helpers /path/to/shell-helper-files/ +# +# an example at bottom demonstrates hardcoding the helpers path +# + +main(){ + set -eo pipefail + readonly CWD="$( cd $(dirname $0) ; pwd -P )" + local workdir="${1:-$CWD}" + local pattern="# @shell-helpers_UPDATE_URL=" + local match= + local file= + local url= + + cd "$workdir" + for file in *.sh; do + echo "tyring $workdir/$file" + match=$(grep -m1 "^$pattern" "$file") || continue + url=${match//$pattern/} + echo " updating $file from $url ..." + curl --silent -LfO $url || echo " ! failed to download $url" + done + exit +} + +# :: example hardcoding of workdir [uncomment below lines] +# readonly SCRIPT_CWD=$( cd $(dirname "${BASH_SOURCE[0]}") ; pwd -P ) +# main "$SCRIPT_CWD/common/lib.d/helpers" + +main "$@" diff --git a/lib.d/helpers/find.sh b/lib.d/helpers/find.sh new file mode 100644 index 0000000..e6b39f2 --- /dev/null +++ b/lib.d/helpers/find.sh @@ -0,0 +1,34 @@ +# dex helpers + +dex/find-dockerfiles(){ + local repostr="$1" + local default_tag="$2" + local repo + local image + local tag + IFS="/:" read repo image tag <<< "$(dex/get-repostr $1)" + + local found=false + local search_image + local search_repo + + for search_repo in $(__format="\$name" dex/repo-ls $repo); do + + $__pull && dex/repo-pull "$search_repo" + + if [ -n "$image" ]; then + local path="$__checkouts/$search_repo/dex-images/$image" + docker/find/dockerfiles "$path" "${tag:-$default_tag}" || continue + found=true + else + for search_image in $(find/dirs "$__checkouts/$search_repo/dex-images"); do + local path="$__checkouts/$search_repo/dex-images/$search_image" + docker/find/dockerfiles "$path" "${tag:-$default_tag}" || continue + found=true + done + fi + done + + $found && return 0 + return 127 +} diff --git a/lib.d/helpers/get.sh b/lib.d/helpers/get.sh new file mode 100644 index 0000000..ee53bf4 --- /dev/null +++ b/lib.d/helpers/get.sh @@ -0,0 +1,81 @@ +# dex helpers +# return first found built image matching repostr +dex/get-image(){ + local repo + local image + local tag + IFS="/:" read repo image tag <<< "$(dex/get-repostr "$1")" + + [ -z "$image" ] && return 2 + + local flags=( + "-q" + "--filter dangling=false" + "--filter label=org.dockerland.dex.namespace=$DEX_NAMESPACE" + "--filter label=org.dockerland.dex.image=$image" + ) + + [ -n "$repo" ] && flags+=( "--filter=\"label=org.dockerland.dex.repo=$repo\"" ) + [ -n "$tag" ] && flags+=( "--filter=\"label=org.dockerland.dex.tag=$tag\"" ) + + docker/local images ${flags[@]} | head -n1 +} + +# normalizes a repostr +dex/get-repostr(){ + local repostr="$1" + local default_tag="$2" + local imagestr + local repo + local image + local tag + local junk + + IFS="/" read repo imagestr junk <<< "$repostr" + + [ -n "$junk" ] && { + p/warn "malformed repostr $repostr" + return 2 + } + + [[ -z "$imagestr" && "$repostr" != "$repo/" ]] && { + # no repo was specified. + imagestr="$repo" + repo= + } + + IFS=":" read image tag junk <<< "$imagestr" + + [ -n "$junk" ] && { + p/warn "malformed imagestr $imagestr" + return 2 + } + + # tag images with default tag if image is specified and tag is empty + [[ -z "$tag" && -n "$image" ]] && tag=$default_tag + + echo "$repo/$image:$tag" +} + +# given a Dockerfile path in checkouts, print a fully qualified repostr +dex/get-repostr-from-dockerfile(){ + local Dockerfile="$1" + local tag=$(docker/get/dockerfile-tag $Dockerfile) + local repo=${Dockerfile//$__checkouts\//} + repo=${repo%%/*} + local image=${Dockerfile//$__checkouts\/$repo\/dex-images\//} + image=${image%%/*} + + [[ -z "$repo" || -z "$image" || -z "$tag" ]] && { + p/warn "failed determining repostr from $Dockerfile" + return 1 + } + + echo "$repo/$image:$tag" +} + + +# output path containing reference files from an image/repotag +dex/get/reference-path(){ + echo "$DEX_HOME/references/$(docker/get/safe-name "$1")" +} diff --git a/lib.d/helpers/git.sh b/lib.d/helpers/git.sh deleted file mode 100644 index cee0041..0000000 --- a/lib.d/helpers/git.sh +++ /dev/null @@ -1,65 +0,0 @@ -# -# lib.d/helpers/git.sh for dex -*- shell-script -*- -# - -# usage: clone_or_pull -clone_or_pull(){ - local force=${3:-false} - if [ -d $2 ]; then - # pull - ( - cd $2 - $force && git reset --hard HEAD - git pull - ) || { - log "error pulling changes from git" - return 1 - } - else - # clone - - #@TODO support reference repository - # [detect if local repo is a bare repo -- but how to find remote?] - - local SHARED_FLAG= - - [ -w $(dirname $2) ] || { - log "destination directory not writable" - return 126 - } - - if [[ $1 == /* ]]; then - # perform a shared clone (URL is a local path starting with '/...' ) - [ -d $1/.git ] || { - log "$1 is not a path to a local git repository" - return 1 - } - SHARED_FLAG="--shared" - fi - - git clone $SHARED_FLAG $1 $2 || { - log "error cloning $1 to $2" - return 1 - } - fi - - return 0 -} - - -# checks git working copy. -# return 1 if clean (not dirty), 0 if dirty (changes exist) -is_dirty(){ - - [ -d $1/.git ] || { - log "$1 is not a git repository. continuing..." - return 1 - } - - ( - set -e - cd $1 - [ ! -z "$(git status -uno --porcelain)" ] - ) - return $? -} diff --git a/lib.d/helpers/io.sh b/lib.d/helpers/io.sh deleted file mode 100644 index 647487e..0000000 --- a/lib.d/helpers/io.sh +++ /dev/null @@ -1,75 +0,0 @@ -# -# lib.d/helpers/git.sh for dex -*- shell-script -*- -# - -error(){ - [ -z "$1" ] && set -- "general exception. halting..." - - printf "\e[31m%b\n\e[0m" "$@" >&2 - exit ${__error_code:-1} -} - -error_noent() { - __error_code=127 - error "$@" -} - -error_perms() { - __error_code=126 - error "$@" -} - -error_exception() { - __error_code=2 - error "$@" -} - - -log(){ - printf "\e[33m%b\n\e[0m" "$@" >&2 -} - -warn(){ - printf "\e[35m%b\n\e[0m" "$@" >&2 -} - -# prompt_echo - helper for assigning variable values -# usage: prompt_echo [default fallback] -# example: -# name=$(prompt_echo "name to encrypt") -# port=$(prompt_echo "port [8080]" 8080) - -prompt_echo() { - while true; do - # read always from /dev/tty, use `if [ -t 0 ]` upstream to avoid prompt - read -r -p " ${1:-input} : " INPUT &2 - done -} - -prompt_confirm() { - while true; do - echo - # read always from /dev/tty, use `if [ -t 0 ]` upstream to avoid prompt - read -r -n 1 -p " ${1:-Continue?} [y/n]: " REPLY &2 - esac - done -} - -# line_in_file : ensure a line exists in a file -############################################### -# -# usage: line_in_file "file" "match" "line" -# ex: line_in_file "varsfile" "^VARNAME=.*$" "VARNAME=value" -# -line_in_file(){ - local delim=${4:-"|"} - grep -q "$2" $1 2>/dev/null && sed_inplace $1 "s$delim$2$delim$3$delim" || echo $3 >> $1 -} diff --git a/lib.d/helpers/network.sh b/lib.d/helpers/network.sh deleted file mode 100644 index e2dcec3..0000000 --- a/lib.d/helpers/network.sh +++ /dev/null @@ -1,23 +0,0 @@ -# -# lib.d/helpers/network.sh for dex -*- shell-script -*- -# - -# usage: fetch-url -fetch-url(){ - local WGET_PATH=${WGET_PATH:-wget} - local CURL_PATH=${CURL_PATH:-curl} - - if ( type $WGET_PATH &>/dev/null ); then - $WGET_PATH $1 -qO $2 || ( rm -rf $2 ; exit 1 ) - elif ( type $CURL_PATH &>/dev/null ); then - $CURL_PATH -Lfso $2 $1 - else - log "failed to fetch $2 from $1" "missing both curl and wget" - return 2 - fi - - [ $? -eq 0 ] && return 0 - - log "failed to fetch $2 from $1" - return 126 -} diff --git a/lib.d/helpers/shell-helpers.sh b/lib.d/helpers/shell-helpers.sh new file mode 100644 index 0000000..8423553 --- /dev/null +++ b/lib.d/helpers/shell-helpers.sh @@ -0,0 +1,834 @@ +# +# shell-helpers version v2.0.0-pr build e8c11f5 +# https://github.com/briceburg/shell-helpers +# Copyright 2016-present Brice Burgess, Licensed under the Apache License 2.0 +# +# shell-helpers - unfurl your arguments +# https://github.com/briceburg/shell-helpers + + +# args/normalize - normalize POSIX short and long flags for easier parsing +# usage: args/normalize [...] +# : string of short flags requiring an argument. +# : flag string(s) to normalize, typically passed as "$@" +# examples: +# args/normalize "" "-abc" +# => -a -b -c +# args/normalize "om" "-abcooutput.txt" "--def=jam" "-mz" +# => -a -b -c -o output.txt --def jam -m z" +# args/normalize "om" "-abcooutput.txt" "--def=jam" "-mz" "--" "-abcx" "-my" +# => -a -b -c -o output.txt --def jam -m z -- -abcx -my" +args/normalize(){ + local fargs="$1" ; shift || true + local passthru=false + local output="" + for arg in $@; do + if $passthru; then + output+=" $arg" + elif [ "--" = "$arg" ]; then + passthru=true + output+=" --" + elif [ "--" = ${arg:0:2} ]; then + output+=" ${arg%=*}" + [[ "$arg" == *"="* ]] && output+=" ${arg#*=}" + elif [ "-" = ${arg:0:1} ]; then + local p=1 + while ((p++)); read -n1 flag; do + [ -z "$flag" ] || output+=" -$flag" + if [[ "$fargs" == *"$flag"* ]]; then + output+=" ${arg:$p}" + break + fi + done < <(echo -n "${arg:1}") + else + output+=" $arg" + fi + done + printf "%s" "${output:1}" +} + +# args/normalize_flags_first - like args/, but outputs flags first. +# usage: args/normalize_flags_first [...] +# : string of short flags requiring an argument. +# : flag string(s) to normalize, typically passed as "$@" +# examples: +# normalize_flags_first "" "-abc command -xyz otro" +# => -a -b -c -x -y -z command otro +# normalize_flags_first "" "-abc command -xyz otro -- -def xyz" +# => -a -b -c -x -y -z command otro -- -def xyz + +args/normalize_flags_first(){ + local fargs="$1" ; shift || true + local output="" + local cmdstr="" + local passthru=false + for arg in $(args/normalize "$fargs" "$@"); do + [ "--" = "$arg" ] && passthru=true + if $passthru || [ ! "-" = ${arg:0:1} ]; then + cmdstr+=" $arg" + continue + fi + output+=" $arg" + done + printf "%s%s" "${output:1}" "$cmdstr" +} + +args/unknown(){ + p/shout "\e[1m$1\e[21m is an unrecognized ${2:-argument}" + die/help 10 +} +# shell-helpers - the art of killing your script +# https://github.com/briceburg/shell-helpers + +die(){ + p/error "${@:-halting...}" + exit ${__exit_code:-1} +} + +die/noent(){ + __exit_code=127 + die "$@" +} + +die/perms(){ + __exit_code=126 + die "$@" +} + +die/exception() { + __exit_code=2 + die "$@" +} + +# die/help +# calls p/help_[cmd] function. (e.g. calls p/help_main from main() fn) +# help messages are prefixed w/ any message text, such as warnings about +# about missing arguments. +die/help(){ + local status="$1" ; shift || true + + # functions starting with main_ indicate command name. + # attempt to auto-detect by examining call stack + local fn + for fn in "${FUNCNAME[@]}"; do + [ "main" = "${fn:0:4}" ] && { + cmd="${fn//main_/}" + is/fn "p/help_$cmd" || continue + [ -z "$@" ] || p/shout "$@" + p/help_$cmd + exit $status + } + done + + die/exception "failed to detect helpfile from function stack" "${FUNCNAME[@]}" +} + +# example p/help_ function +# p/help_cmd(){ +# cat <<-EOF +# +# util - because you need util +# +# Usage: +# util cmd [options...] +# +# Options: +# -h|--help +# Displays help +# +# -d|--defaults +# Temporarily resets the current environment and prints default values +# +# Commands: +# vars [-d|--defaults] [--] [list...] +# Prints configuration variables as evaluable output +# +# EOF +# } +# shell-helpers - docker the things +# https://github.com/briceburg/shell-helpers + +docker/deactivate-machine(){ + # @TODO support boot2docker / concept of "default" machine + is/cmd docker-machine && { + eval $(docker-machine env --unset --shell bash) + return + } + # lets be safe and unset if missing docker-machine + unset DOCKER_HOST DOCKER_TLS_VERIFY DOCKER_CERT_PATH DOCKER_MACHINE_NAME +} + +# docker/local - run docker against the local engine +docker/local()( + docker/deactivate-machine + exec docker "$@" +) + +# docker/local-compose - run docker-compose against the local engine +docker/local-compose()( + docker/deactivate-machine + exec docker-compose "$@" +) + + +# print Dockerfiles found in a path. filter by tag and/or extension. +# follows symlinks to resolve extension validity. legal default examples; +# /path/Dockerfile +# /path/Dockerfile-1.2.0 +# /path/Dockerfile-1.3.0.j2 +docker/find/dockerfiles(){ + local path="${1:-.}" ; shift || true + local filter_tag="$1" ; shift || true + local filter_extensions=( "${@:-j2 Dockerfile}" ) + + ( + found=false + cd $path 2>/dev/null + + for Dockerfile in Dockerfile* ; do + [ -e "$Dockerfile" ] || continue + + filename="$Dockerfile" + tag="$(docker/get/dockerfile-tag $Dockerfile)" + + # skip tags not matching our filter + [[ -n "$filter_tag" && "$tag" != "$filter_tag" ]] && continue + + # resolve extension + extension="${filename##*.}" + while [ -L "$path/$filename" ]; do + filename=$(readlink $path/$filename) + extension=${filename##*.} + done + + # skip files not matching our extension filter + [ -n "$extension" ] && is/in_list "$extension" "${filter_extensions[@]}" && continue + + echo "$path/$Dockerfile" + found=true + done + + $found + ) +} + +# docker/find/labels [type (container|image)] +# outputs labels one per line as "