Skip to content

Instantly share code, notes, and snippets.

@allex
Last active September 2, 2022 06:21
Show Gist options
  • Save allex/1ca3d49d817928ccab0a3317a18354c1 to your computer and use it in GitHub Desktop.
Save allex/1ca3d49d817928ccab0a3317a18354c1 to your computer and use it in GitHub Desktop.
#!/bin/bash
# vim: set ft=sh fdm=marker ts=2 sw=2 sts=2 tw=85 et:
set -eu
# ================================================
# Description: Enhanced docker build with some helper parameters, such as --env, --build-arg-file
#
# Usage:
# docker-build [ -t tag_list ] [ -f Dockerfile ] [ ... ] [ --env <env_file> ] [ --build-arg-file <args_file> ]
#
# Last Modified: Sat May 14, 2022 14:46
# Author: Allex Wang ([email protected])
#
# GistID: 1ca3d49d817928ccab0a3317a18354c1
# GistURL: https://gist.github.com/1ca3d49d817928ccab0a3317a18354c1
# ================================================
PROG=$(basename "$0")
SH_DIR=$(cd -P -- "$(dirname -- "$(readlink -f "$0")")" && pwd -P)
WORKDIR=$(mktemp -d)
BUILDER_EXEC="docker buildx"
tag_list=
platform=linux/amd64,linux/arm64
env_file=
build_arg_list=()
docker_file=./Dockerfile
base_image=
patch_image=
# verbose for trace logs
verbose=
### Helper & ARGS {{{
die () {
[ "${1-}" ] && echo >&2 "fatal: ${1}"
exit "${2-1}"
}
log () {
echo "${*}"
}
error () {
echo >&2 "Error: ${*}"
}
show_usage () {
cat <<-HELP
Enhanced docker build based on buildx, add some shortcut options, such as --env, --build-arg-file
Usage: ${PROG} [ -t tag_list ] [ -f Dockerfile ] [ ... ] [ --env <env_file> ] [ --build-arg-file <args_file> ]
Options:
--env, --env-file <ENV_FILE> Specify an alternate environment file 'ENV' instruction
--build-arg-file <arg_file> A file with k/v touples transform to docker build '--build-arg'
--base-image <base_image> The base image, only to be used when none Dockerfile
--patch-image <patch_image> The patch image to be apply to final Dockerfile
--platform <platform_list> Set platform, multiple platforms sep by comma[,] (default "linux/amd64,linux/arm64")
-h Show this help info
For more options please refer to 'docker buildx build -h'
HELP
exit 1
}
handle_exit () {
local ec=$?
if [ -d "$WORKDIR" ]; then rm -rf "$WORKDIR"; fi
trap - INT TERM EXIT
# declare -F on_exit, not avaiable in alipine/sh
if type on_exit >/dev/null 2>&1; then on_exit; fi
exit $ec
}
trap handle_exit 0 1 2 3 6 15
if [ $# -eq 0 ]; then
show_usage
fi
args=()
while [ $# -gt 0 ]; do
OPT=$1
[ "$OPT" = "--" ] && break
[ "$OPT" = "${OPT##-*}" ] && {
args+=("$OPT")
shift
continue
}
if [ "${OPT##=*}" = "$OPT" ]; then
OPTARG="${2-}"
if [ "${OPTARG##-*}" = "${OPTARG}" ]; then
shift
else
OPTARG=
fi
else
OPT=$(echo "$1" | awk -F= '{print $1}')
OPTARG=$(echo "$1" | awk -F= '{print $2}')
fi
case $OPT in
# builtin parameters
-t | --tag)
tag_list="${OPTARG}"
;;
-f)
docker_file=${OPTARG}
;;
--platform)
platform=${OPTARG}
;;
# enhanced parameters
--base-image)
base_image=${OPTARG}
;;
--patch-image)
patch_image=${OPTARG}
;;
--env | --env-file)
env_file=${OPTARG}
;;
--build-arg)
build_arg_list+=(--build-arg "${OPTARG}")
;;
--build-arg-file)
[ -n "$OPTARG" ] || die "Invalid parameter \"$OPT <arg_file>\" value"
while read -r line; do build_arg_list+=(--build-arg "\"${line%=*}=${line#*=}\""); done < "$OPTARG"
;;
--builder)
[ -n "$OPTARG" ] && BUILDER_EXEC=$OPTARG
;;
# misc
-x | --debug) verbose=1 ;;
-h | --help) show_usage ;;
-*)
args+=("$OPT")
[ "$OPTARG" ] && args+=("\"$OPTARG\"")
;;
esac
[ $# -gt 0 ] && shift
done
[ -n "$tag_list" ] || die "Invalid parameter \"-t <tag_list>\" value"
### }}}
gen_dockerfile () {
log "Build Dockerfile ..."
#1 copy supplied Dockerfile if exists.
#2 build from base image if not #1
#3 add patch image stuffs by `patch_image`
docker_file_spec=
if [ -f "$docker_file" ]; then
docker_file_spec=$(cat "${docker_file}")
elif [ "$base_image" ]; then
docker_file_spec="FROM $base_image"
fi
# transform .env file
env_spec=
env_patch=
[ -f "$env_file" ] && {
env_spec=$(sed -e '/^#/d;/^\s*$/d' "$env_file" | tr '\n' ' ')
}
if [ "$patch_image" ]; then
log "Extract patch image ENV... ($patch_image)"
docker pull "${patch_image}" > /dev/null
env_patch="$(docker inspect "${patch_image}" --format='{{range $k, $v := .Config.Env}}{{$v}}{{"\n"}}{{end}}'|while read -r line; do if [ -n "$line" ]; then echo "${line%=*}=\"${line#*=}\""; fi; done |tr '\n' ' ')"
docker rmi -f "${patch_image}" >/dev/null
log "Parse image ENV success."
fi
tmp="$(cat <<-__DOCKERFILE__ |sed '{/^$/d}'
${patch_image:+FROM ${patch_image}}
$docker_file_spec
${env_patch:+ENV $env_patch}
${env_spec:+ENV $env_spec}
${patch_image:+COPY --from=0 / /}
__DOCKERFILE__
)"
# evaluate environs
vars="$(envsubst -v "$tmp" | sort | uniq | awk '{if(ENVIRON[$0])print "${"$0"}"}')"
echo "$tmp" | envsubst "$vars" > "$WORKDIR/Dockerfile"
echo "-----------"
cat "$WORKDIR/Dockerfile"
echo "-----------"
echo
}
build () {
log "Start to build docker image ..."
local specfile=$WORKDIR/Dockerfile
# fix buildx args
local platform=${platform}
[ "$platform" ] && args+=("--platform" "$platform")
export BUILDX_NO_DEFAULT_LOAD=false
eval "(${verbose:+set -x;}${BUILDER_EXEC} build -t $tag_list ${build_arg_list:+${build_arg_list[@]}} -f $specfile ${args:+${args[@]}})"
log "Build done${tag_list:+ => ${tag_list}}"
}
gen_dockerfile \
&& build "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment