refactor: generate Dockerfile from a template

refactor src/ to be just a basic build
add examples
write up help.md and usage subcommand
add test folder for dev testing
improve install script
many numerous improved to build script,
 moved portions of build script to functions in helpers.lib
master
David Kebler 2023-04-14 21:27:40 -07:00
parent 16bc3bd929
commit efe8d0fa2d
56 changed files with 2301 additions and 692 deletions

4
.gitignore vendored
View File

@ -4,4 +4,6 @@ _opt/
.src
TODO.md
mnt/
logs/
logs/
Dockerfile
.env

View File

@ -1,63 +0,0 @@
# syntax=docker/dockerfile:latest
ARG BASE_IMAGE
FROM $BASE_IMAGE
ARG BASE_IMAGE
ARG SYSADMIN_PW
ARG LINUX_DISTRO=alpine
WORKDIR /build
# PACKAGES
RUN --mount=type=bind,source=.src/packages,target=/build/packages \
<<eot
echo -e "\n ************************************************* \n"
echo "Building Image from Base: $BASE_IMAGE"
echo "Distro: $LINUX_DISTRO; *****"
echo " ---- running packages install script ---"
cd packages; /bin/sh ./packages.sh; cd ..
echo -e "\n********************************************************"
eot
# END PACKAGES
# INITIALIZATION
RUN --mount=type=bind,source=.src/init,target=/build/init \
<<eot
echo -e "\n ************************************************* \n"
echo "****** Initializing Image with build source ******"
cd init
pwd; ls -la
if [[ -d env/ ]]; then
echo "copying env/ to /opt/env"
/bin/cp -R -p env/. /opt/env
ls -la /opt/env
fi
if [[ -f env/build.env ]]; then
source /opt/env/build.env
rm -f /opt/env/build.env
echo sourced /opt/env/build.env and deleted
fi
if [[ -d bin/ ]]; then
echo "copying bin/ to /opt/bin"
/bin/cp -R -p bin/. /opt/bin
chmod -R +x /opt/bin
ls -la /opt/bin
fi
echo " ---- running init script init.sh ---"
/bin/bash ./init.sh
echo "############## Finished run init build script(s) ###########################"
if [[ -f /opt/env/run.env ]]; then
echo "sourcing /opt/env/run.env from /etc/profile"
echo "&&&&&& last 10 of /etc/profile &&&&&"
echo -e "# added from image build\nsource /opt/env/run.env" >> /etc/profile
tail /etc/profile
echo "%%%%%%%%%%%%%%%%%%%%%%%%%%%"
fi
echo -e "\n ************* End Initialzation ************************"
eot
# END INITIALIZATION
# default command
CMD ["/bin/bash", "-l"]
# default
WORKDIR /opt

View File

@ -0,0 +1,30 @@
# syntax=docker/dockerfile:latest
ARG BASE_IMAGE
FROM $BASE_IMAGE
ARG BASE_IMAGE
ARG SYSADMIN_PW
ARG VERBOSE
ARG LINUX_DISTRO=alpine
WORKDIR /build
# PACKAGES
RUN --mount=type=bind,source=.src/packages,target=/build/packages \
<<eot
.INCLUDE packages.tpl
eot
# END PACKAGES
# INITIALIZATION
RUN --mount=type=bind,source=.src/init,target=/build/init \
<<eot
.INCLUDE init.tpl
eot
# END INITIALIZATION
# default command
ENTRYPOINT ["/opt/bin/entrypoint"]
# default
WORKDIR /opt

5
Dockerfile.d/create Executable file
View File

@ -0,0 +1,5 @@
pushd $(dirname "$(realpath "$BASH_SOURCE")") > /dev/null || return 1
source <(../lib/bash-tpl Dockerfile.tpl ) | grep -v '^# ' > ../Dockerfile
echo " ************* uci build Dockerfile created *****************"
popd > /dev/null || return 2

86
Dockerfile.d/init.tpl Normal file
View File

@ -0,0 +1,86 @@
#!/bin/bash
%
if [[ $REBUILD == "init" ]]; then
echo "## Busting Cache, Forcing Rebuild $(date)"
fi
%
quiet () {
if [[ $VERBOSE ]]; then $@; fi
}
quiet echo -e "\n ************************************************* \n"
quiet echo "****** Initializing Image with build source ******"
cd init
pwd; quiet ls -la
export BUILDING=true
export BUILD_DIR=$PWD
export SHELL=/bin/bash
export BIN_DIR=/opt/bin
mkdir -p $BIN_DIR
echo "export BIN_DIR=${BIN_DIR}" >> /tmp/profile
echo 'export PATH=$BIN_DIR:$PATH' >> /tmp/profile
echo " ##### creating entrypoint script ###"
cat << EOE >$BIN_DIR/entrypoint
.INCLUDE ./init/entrypoint.tpl
EOE
chmod +x $BIN_DIR/entrypoint
quiet echo '------ default entrypoint -----'
quiet ls -la $BIN_DIR/entrypoint
quiet cat $BIN_DIR/entrypoint
quiet echo "------------"
echo " ##### creating default start script ###"
cat << "EOS" >$BIN_DIR/start
.INCLUDE ./init/start.sh
EOS
chmod -R +x $BIN_DIR/start
quiet echo "--- DEFAULT START SCRIPT in $BIN_DIR/start ---"
quiet cat $BIN_DIR/start
quiet echo "-----------------------------------"
echo " ##### creating map host id script ###"
cat << "EOM" >$BIN_DIR/map-host-id
.INCLUDE ./init/map-host-id.sh
EOM
chmod +x $BIN_DIR/map-host-id
[[ -f image.info ]] && cp image.info /opt
.INCLUDE ./init/dirs.sh
if [[ -f build.env ]]; then
echo "-- sourcing /build/build.env --"
quiet ls -la
quiet cat build.env
quiet echo "----------------------"
source build.env
fi
if [[ -f ./init.sh ]]; then
echo "############## Running Script init.sh of build source #################"
quiet echo "----- build environment ------"
quiet env
quiet echo "----- env ------"
quiet echo "-------------------- init.sh ------------------------------"
quiet cat ./init.sh
quiet echo "-------------------------------------------------------------"
# init.sh must have shebang and be executable
if ! $SHELL ./init.sh; then return 1; fi
echo "############## Finished running init.sh build script #########################"
fi
.INCLUDE ./init/profile.sh
echo "****** creating user and group 'host' with ids 1000 *****"
groupadd -g 1000 host
useradd -r -g host -u 1000 host
# map host id now based on build environment
if [[ $VOLUME_DIRS ]]; then
echo "*** creating and configuring volume directories ***"
echo $VOLUME_DIRS
mkdir -p $VOLUME_DIRS
$BIN_DIR/map-host-id
chmod -R g+rw $VOLUME_DIRS
fi
echo -e "\n ************* End Initialzation ************************"

25
Dockerfile.d/init/dirs.sh Normal file
View File

@ -0,0 +1,25 @@
if [[ -d env/ ]]; then
export ENV_DIR=/opt/env
echo "############## Adding Environment Directroy $ENV_DIR #################"
echo "export ENV_DIR=${ENV_DIR}" >> /tmp/profile
quiet echo "copying env/ to $ENV_DIR"
/bin/cp -R -p env/. $ENV_DIR
quiet ls -la $ENV_DIR
fi
if [[ -d bin/ ]]; then
echo "############## Copying to Binary Directroy $BIN_DIR #################"
quiet echo "copying bin/ to $BIN_DIR"
/bin/cp -R -p bin/. $BIN_DIR
# chmod -R +x $BIN_DIR
quiet ls -la $BIN_DIR
fi
if [[ -d lib/ ]]; then
export LIB_DIR=/opt/lib
echo "############## Adding Library Directroy $LIB_DIR #################"
echo "export LIB_DIR=${LIB_DIR}" >> /tmp/profile
quiet echo "copying lib/ to $LIB_DIR"
/bin/cp -R -p lib/. $LIB_DIR
chmod -R +x $LIB_DIR
quiet ls -la $LIB_DIR
fi

View File

@ -0,0 +1,29 @@
#!/bin/bash
# to maintain variable $ in container script espcape with \$
# otherwise subtitution will happen during build
case "\$1" in
maphostid)
shift 1
/bin/bash -l -c '\$BIN_DIR/map-host-id \$@' \$0 "\$@"
;;
shell)
/bin/bash -c "cd \${INITIAL_DIR:-/opt}; exec bash -l"
;;
help)
.INCLUDE ./init/help.sh
;;
image)
.INCLUDE ./init/image-info.sh
;;
script)
shift 1
cat | /bin/bash -l
;;
${ENTRYPOINT_CMD:-start})
shift 1
/bin/bash -l -c '${ENTRYPOINT_CMD_PATH:-$BIN_DIR/start} \$@' \$0 "\$@"
;;
*)
/bin/bash -l -c '"\$@"' \$0 "\$@"
;;
esac

14
Dockerfile.d/init/help.sh Normal file
View File

@ -0,0 +1,14 @@
cat <<HELP
--------- ENTRYPOINT HELP ----------
commands are shell,maphostid,help,image and a custom command <start>
otherwise you can pass any shell command such as 'ls -la'
the current container custom command is > ${ENTRYPOINT_CMD:-start}
and the script for that command is in ${ENTRYPOINT_CMD_PATH:-$BIN_DIR/start}
-----
you can replace this start script with your own
your own script in $BIN_DIR/start in your build source directory
or set the \$ENTRYPOINT_CMD and \$ENTRYPOINT_CMD_PATH variables
It is possible to override the container entrypoint with your own
but is not recommmended as then a login shell will not be used
and critical environment variables will not be set
HELP

View File

@ -0,0 +1,5 @@
if [[ -f /opt/image.info ]]; then
echo -e "\n--------- image info found at /opt/image.info----------"
cat /opt/image.info
echo -e "\n****************************"
fi

View File

@ -0,0 +1,9 @@
#!/bin/bash
if [[ $VOLUME_DIRS ]]; then
echo changing ownership of directories $VOLUME_DIRS
echo to ${HOST_MAP:-"host:host"}
declare usesudo
[[ ! $EUID -eq 0 ]] && usesudo=sudo
$usesudo chown -R ${HOST_MAP:-"host:host"} $VOLUME_DIRS
ls -la $VOLUME_DIRS
fi

View File

@ -0,0 +1,12 @@
[[ -f $ENV_DIR/run.env ]] && echo 'source $ENV_DIR/run.env' >> /tmp/profile
while read line; do
if ! grep -q "$line" /etc/profile; then
quiet echo added $line to /etc/profile
echo $line >> /etc/profile
fi
done < /tmp/profile
# echo "echo /etc/profile has been sourced" >> /etc/profile
quiet echo "&&&&&&& last 10 of /etc/profile &&&&&"
quiet tail /etc/profile
quiet echo "%%%%%%%%%%%%%%%%%%%%%%%%%%%"

View File

@ -0,0 +1,17 @@
#!/bin/bash
#***** CONTAINER DEFAULT CUSTOM SCRIPT ******************"
case "$1" in
sub1)
echo this would be a subcommand #1
echo with arguments $@
;;
sub2)
shift 1
echo this would be a subcommand #1
echo with arguments $@
;;
*)
echo "running this command $*"
echo within login shell
/bin/bash -c '"$@"' $0 "$@"
esac

20
Dockerfile.d/packages.tpl Normal file
View File

@ -0,0 +1,20 @@
%
if [[ $REBUILD == "packages" ]]; then
echo "## Busting Cache, Forcing Rebuild $(date)"
fi
%
echo -e "\n ************************************************* \n"
echo "Building Image from Base: $BASE_IMAGE"
echo "Distro: $LINUX_DISTRO"
echo " ---- running packages install script ---"
if [[ $LINUX_DISTRO == "alpine" ]]; then
echo "-------------------------------"
echo "adding shadow bash and bash completion coreutils for alpine"
echo "to be compatible with other distros"
apk add --no-cache shadow bash bash-completion coreutils
echo "-------------------------------"
fi
cd packages
/bin/sh ./packages.sh
cd ..
echo -e "\n********************************************************"

255
build
View File

@ -1,46 +1,56 @@
#!/bin/bash
docker_image_build () {
udbuild () {
local targets=(dev arm64 amd64 deploy private multi)
local verbose; local log_dir; local no_prompt
local efile
local targets=(dev arm64 amd64 publish multi default)
local log_dir; local no_prompt
local append_efile
declare OPTION; declare OPTARG; declare OPTIND
BDIR=$(dirname "$(realpath "$BASH_SOURCE")")
export BDIR
# load script library
source $BDIR/lib/load.sh
BUILD_EFILE=""
# check for subcommands first
case "$1" in
try)
shift 1
# type try_container
try_container "$@"
shift 1; try_container "$@"; return $? ;;
load_env_file)
echo -e "@@@@@@ loading build environment file for external use @@@@@@"
BUILD_EFILE=$(echo -- "$@" | grep -oP -- '(?<=-e )[^ ]*')
source_env_file "$BUILD_EFILE"
echo -e "@@@@@@@@@@@@@@@@@ returning to calling script @@@@@@@@@@@@@@@"
return $?
;;
image_name)
build_src) shift 1; get_build_src "$@"; return $? ;;
help)
;&
--help)
;&
-help) shift 1; usage "$@"; return $? ;;
source) type udbuild; return $? ;;
image)
shift 1
image_name "$@"
return $?
;;
tag)
shift 1
image_tag "$@"
return $?
;;
push)
shift 1
image_push "$@"
return $?
;;
info)
shift 1
[[ $1 == "arch" ]] && { shift 1; image_arch "$@"; return $?; }
[[ $1 == "exists" ]] && { shift 1; image_exists "$@"; return $?; }
[[ $1 == "id" ]] && { shift 1; image_id "$@"; return $?; }
image_info "$@"; return $?
case "$1" in
name) shift 1; image_name "$@" ;;
tag) shift 1; image_tag "$@" ;;
push) shift 1; image_push "$@" ;;
delete) shift 1; image_delete "$@" ;;
info)
shift 1
case "$1" in
arch) shift 1; image_arch "$@" ;;
exists) shift 1; image_exists "$@" ;;
tags) shift 1; image_tags "$@" ;;
id) shift 1; image_id "$@" ;;
* ) image_info "$@"
esac
;;
*) echo no image subcommand $1 ;;
esac
return $?
;;
esac
@ -52,20 +62,30 @@ exit_abnormal() { # Function: Exit with error.
[[ -z "$PS1" ]] || no_prompt=true
overwrite=true
while getopts 'g:e:b:d:t:ncr:u:plhs:avo' OPTION; do
while getopts 'fg:e:b:d:t:nc:r:u:lhs:a:voi:p' OPTION; do
# echo processing: option:$OPTION argument:$OPTARG index:$OPTIND remaining:${@:$OPTIND}
case "$OPTION" in
i)
IMAGE_INFO=$OPTARG
;;
e)
if source_env_file $OPTARG; then efile=true; else return 2; fi
BUILD_EFILE=$OPTARG
if ! source_env_file $BUILD_EFILE; then return 2; fi
;;
o)
unset overwrite
;;
v)
verbose=true
VERBOSE=true
;;
a)
# automated - script is to be run without prompt (non-interactive)
append_efile=$OPTARG
;;
f)
REBUILD=init
;;
p)
echo "build script will be run WITHOUT user prompts (i.e. non-interactive)"
no_prompt=true
;;
b)
@ -82,7 +102,7 @@ while getopts 'g:e:b:d:t:ncr:u:plhs:avo' OPTION; do
;;
l)
# append distro name to image name
append_distro=true
APPEND_DISTRO=true
;;
t)
TARGET=$OPTARG
@ -94,10 +114,7 @@ while getopts 'g:e:b:d:t:ncr:u:plhs:avo' OPTION; do
RUSER=$OPTARG
;;
c)
try=true
;;
p)
push=true
TRY_CMD=$OPTARG
;;
n)
nocache="--no-cache"
@ -118,70 +135,27 @@ done
shift $((OPTIND - 1))
[[ ! $efile ]] && source_env_file
[[ ! $BUILD_EFILE ]] && source_env_file
# processing the build source directory
if [[ ! $BUILD_SRC ]]; then
echo no BUILD_SRC directory specified
echo using present directory $PWD
BUILD_SRC=$PWD
if ! get_build_src; then
if [[ $no_prompt ]] ; then
echo aborting the build...
echo -e "\e[1;31mNOTE: use '_default_' to explicitly use build source in uci-docker-build repo\e[1;37m"
return 2
else
echo "Do you want to use the uci-docker-build default build source"
echo "at $BDIR/src "
read -n 1 -p "instead? [y]=>" REPLY
[[ $REPLY != "y" ]] && echo -e "\n" && return 2
BUILD_SRC=$BDIR/src
echo -e "\n\e[1;31mNOTE: use '_default_' to explicitly use build source in uci-docker-build repo\e[1;37m"
fi
fi
if [[ ! $(isAbsPath $BUILD_SRC) ]] ; then
if [[ ${BUILD_SRC} == "_base_" ]]; then
BUILD_SRC=${BDIR}/src
else
BUILD_SRC=$(realpath ${BUILD_SRC})
fi
fi
if [[ ! ( -d $BUILD_SRC/packages && -d $BUILD_SRC/init ) ]]; then
echo -e "\e[1;31minvalid build source directory"
echo $BUILD_SRC
echo -e "it does not contain packages and init subirectories\e[1;37m"
if [[ ! $(basename $BUILD_SRC) == "src" ]]; then
echo checking in src/ subdirectory for source
if [[ ( -d $BUILD_SRC/src/packages && -d $BUILD_SRC/src/init ) ]]; then
BUILD_SRC=$BUILD_SRC/src
echo found source in src/ subdirectory changing to source directory to
echo $BUILD_SRC
else
echo -e "\e[1;31mERROR: no build source directory at $BUILD_SRC</src>"
echo -e "with init/ and packages/ subdirectores was found\e[1;37m"
if [[ $no_prompt ]] ; then
echo aborting the build...
echo -e "\e[1;31mNOTE: use '_base_' to explicitly use build source in uci-docker-build repo\e[1;37m"
return 2
else
echo "Do you want to use the uci-docker-build repo source scripts"
echo "at $BDIR/src "
read -n 1 -p "instead? [y]=>" REPLY
[[ $REPLY != "y" ]] && echo -e "\n" && return 2
BUILD_SRC=$BDIR/src
echo -e "\n\e[1;31mNOTE: use '_base_' to explicitly use build source in uci-docker-build repo\e[1;37m"
fi
fi
fi
fi
# done processing build source directory
log_dir=$PWD/logs
mkdir -p $log_dir
pushd "$BDIR" > /dev/null || return 3
TARGET=${TARGET:-default}
[[ ! "${targets[@]}" =~ $TARGET ]] && echo $TARGET is not a valid target && echo valid targets are: ${targets[@]} && exit 4
LINUX_DISTRO=${LINUX_DISTRO:-alpine}
if [[ $BASE_IMAGE ]]; then
echo determining DISTRO of base image: $BASE_IMAGE
LINUX_DISTRO=$(docker_image_distro $BASE_IMAGE)
[[ ! $LINUX_DISTRO ]] && echo "unable to get base image OS for: $BASE_IMAGE, aborting build" && return 5
echo $BASE_IMAGE is built from distro $LINUX_DISTRO
else
BASE_IMAGE=$LINUX_DISTRO
fi
get_distro
IMAGE_NAME=$(make_image_name $@)
@ -199,90 +173,83 @@ if [[ $(image_exists $IMAGE_NAME) ]]; then
fi
ARCH=$(get_arch)
log_dir=$PWD/logs
mkdir -p $log_dir
[[ $TARGET == "dev" ]] && VERBOSE=true
export BASE_IMAGE
export TAG
export IMAGE_NAME
export LINUX_DISTRO
export BUILD_SRC
export KEEP
export SYSADMIN_PW
export ARCH
export VERBOSE
echo -e "\e[1;37m********************"
echo "Using scripts source directory at $BUILD_SRC"
echo "Building with base image: $BASE_IMAGE"
#todo based on target form image names
echo "Outputing to image name => $IMAGE_NAME<-arch>:${TAG:-latest}"
[[ $push || $TARGET == "private" ]] && echo "Will push image to ${REPO:-hub.docker.com}"
[[ $TARGET == "deploy" ]] && echo "Will build and push both amd64 and arm64 images to hub.docker.com"
[[ $TARGET == "dev" || ! $TARGET ]] && echo "Building image for local machine with architecture $ARCH"
echo "Linux Distro: $LINUX_DISTRO"
echo "Using build target: ${TARGET:-default}"
echo "Build Command: docker buildx --builder ${builder} bake ${nocache} ${TARGET}"
if [[ $verbose ]]; then
echo -e "\n---------------------------------"
docker buildx bake --print $TARGET
echo -e "\n---------------------------------"
echo "build scripts at $BUILD_SRC to be copied to ${BUILD_DIR:-/build} in container ***** "
ls -la $BUILD_SRC
echo -e "\n----- base init script init.sh ------"
cat $BUILD_SRC/init.sh
echo -e "\n---------------------------------"
fi
echo -e "u********************\e[0;37m"
build_info
if [[ ! $no_prompt ]]; then
read -n 1 -p "do you want to continue [y]=>" REPLY
[[ $REPLY != "y" ]] && echo -e "\n" && return 4
fi
builder=default
if [[ $TARGET == "deploy" ]]; then
builder=deploy
if ! docker buildx ls | grep -q deploy ; then
echo multiarch deploy builder does not exist, creating with docker-container driver
docker buildx create --name deploy --driver docker-container >/dev/null
docker buildx ls | grep deploy
fi
if ! source $BDIR/Dockerfile.d/create; then
echo unable to create Dockerfile from template, aborting build
return 3
fi
[[ $TARGET == "private" && ! $REPO ]] && echo "must use '-r <private repo>' if building to private repo" && exit 3
builder=default
if [[ $TARGET == "publish" ]]; then
builder=publish
pushd "$BDIR" > /dev/null || return 3
if ! docker buildx ls | grep -q publish ; then
echo publish builder does not exist, creating with docker-container driver
docker buildx create --name publish --driver docker-container >/dev/null
docker buildx ls | grep publish
fi
popd > /dev/null || return 4
fi
# copy source directory to temporary .src/ subdirectory
# MUST either be readable by all or group readable by docker group
rm -rf $BDIR/.src
# copy or bind build source directory to temporary .src/ subdirectory in build repo
[[ -d $BDIR/.src ]] && rm -rf $BDIR/.src
if [[ $(which rsync 2> /dev/null ) ]]; then
rsync -aAru ${BUILD_SRC:-src}/ $BDIR/.src
ls -la $BDIR/.src
else
echo no rsync copying with cp
/bin/cp -a ${BUILD_SRC:-src}/. $BDIR/.src > /dev/null 2>&1
fi
if [[ -f $append_efile ]]; then
/bin/cp "$append_efile" "$BDIR/.src/init/env/_build.env_"
echo 'source $ENV_DIR/_build.env_' >> $BDIR/.src/init/build.env
fi
pushd "$BDIR" > /dev/null || return 3
######### RUNNING THE DOCKER BUILD COMMAND ######################
echo running build command: docker buildx --builder ${builder} bake ${nocache} ${TARGET}
docker buildx --builder ${builder} bake ${nocache} ${TARGET} 2>&1 | tee "$log_dir/${IMAGE_NAME//\//-}build.log"
[[ $? == 0 ]] && echo succcess building image $IMAGE_NAME || exit_abnormal 5
popd > /dev/null || return 4
rm -rf $BDIR/.src
popd > /dev/null
if [[ ($try || $TARGET == "dev") ]] && [[ ! $no_prompt ]]; then
if [[ ($TRY_CMD || $TARGET == "dev") ]]; then
echo trying newly built image in a container
try_container -m opt $([[ $TARGET == "deploy" ]] && echo -p) $IMAGE_NAME
echo name before try $IMAGE_NAME
try_container build -m opt $([[ $TARGET == "publish" ]] && echo -p) ${TRY_CMD:-shell}
fi
if [[ $TARGET == "private" ]]; then
# echo pushing arm64 image $IMAGE_NAME to ${REPO:-docker hub}
echo pushing arm64 image $IMAGE_NAME to ${REPO:-docker hub}
image_push -a -r $REPO $IMAGE_NAME
# echo pushing amd image $IMAGE_NAME to ${REPO:-docker hub}
echo pushing amd image $IMAGE_NAME to ${REPO:-docker hub}
image_push -r $REPO $IMAGE_NAME
else
if [[ $push && (! $TARGET == "dev") ]];then
# echo pushing $IMAGE_NAME to ${REPO:-docker hub}
image_push $([[ $TARGET == "arm" ]] && echo -a) -r $REPO $IMAGE_NAME
fi
fi
}
# if script was executed then call the function
(return 0 2>/dev/null) || docker_image_build $@
(return 0 2>/dev/null) || udbuild "$@"

View File

@ -14,12 +14,15 @@ variable "BASE_IMAGE" {
variable "SYSADMIN_PW" {
default = ""
}
variable "VERBOSE" {
default = ""
}
variable "ARCH" {
default = ""
}
function "tag" {
params = [suffix]
result = [format("${IMAGE_NAME}%s:${TAG}", notequal("", suffix) ? "-${suffix}" : "")]
result = [format("${IMAGE_NAME}%s:${TAG}", notequal("${ARCH}", suffix) ? "-${suffix}" : "")]
}
# groups
group "dev" {
@ -28,10 +31,7 @@ group "dev" {
group "default" {
targets = ["${ARCH}"]
}
group "deploy" {
targets = ["multi"]
}
group "private" {
group "multi" {
targets = [
"amd64",
"arm64"
@ -39,7 +39,7 @@ group "private" {
}
# intended for use with default local docker builder
# uses 'dev' group in docker-bake.hcl
# assume dev machine is amd64 machine
# assume dev and default build for architecture of local machine
target "amd64" {
context = "."
dockerfile = "Dockerfile"
@ -47,9 +47,10 @@ target "amd64" {
LINUX_DISTRO = "${LINUX_DISTRO}"
BASE_IMAGE = "${BASE_IMAGE}"
TAG = "${TAG}"
VERBOSE = "${VERBOSE}"
SYSADMIN_PW = "${SYSADMIN_PW}"
}
tags = tag("")
tags = tag("amd64")
platforms = ["linux/amd64"]
}
@ -61,28 +62,11 @@ target "arm64" {
platforms = ["linux/arm64"]
}
# must use with docker-container driver for multiarch image deployment to registry
# uses 'deploy' group in docker-bake.hcl
target "multi" {
# must use with docker-container driver for multiarch image publishment to registry
# uses 'publish' group in docker-bake.hcl
target "publish" {
inherits = ["amd64"]
tags = tag("")
tags = ["${IMAGE_NAME}:${TAG}"]
platforms = ["linux/amd64", "linux/arm64"]
output = ["type=registry"]
}
// variable "RUSER" {
// default = ""
// }
// function "user" {
// params = []
// result = [notequal("", RUSER) ? "${RUSER}/" : ""]
// }
// function "tagamd" {
// params = []
// result = [
// format("%s${LINUX_DISTRO}:${TAG}", notequal("", RUSER) ? "${RUSER}/" : ""),
// format("%s${LINUX_DISTRO}-amd64:${TAG}", notequal("", RUSER) ? "${RUSER}/" : "")
// ]
// }
}

View File

@ -1,6 +0,0 @@
SYSADMIN_PW=ucommandit
# default is alpine
# LINUX_DISTRO=alpine
RUSER=ucommandit
# TARGET=deploy
BUILD_SRC=../src

View File

@ -1,13 +0,0 @@
#!/bin/bash
# three ways to invoke with --no-cache
# inline
# NO_CACHE=true ./build "$@"
# with -n option (prefered)
./build -n "$@"
alias rebuild="build -nfunction_list"
# as export
#export NO_CACHE=true
#./build "$@"

3
examples/build Executable file
View File

@ -0,0 +1,3 @@
# invokes try with the example environment file
# assumes image already built
udbuild -e example.env "$@"

View File

@ -1 +0,0 @@
dbuild -e example.env "$@"

View File

@ -1,17 +1,20 @@
# for easy use copy this file to .env and it will be sourced
# otherwise invoke `udbuild -e example.env`
# using a filename of just .env will load it by default
# LINUX_DISTRO ignored if BASE_IMAGE is set
LINUX_DISTRO=alpine
# LINUX_DISTRO=alpine
# BASE_IMAGE="dockerhubuser/mybase"
# tag is 'latest' by default
# TAG=1.0.0
# will be prepended to image name with /, used mostly for deploying
RUSER=dockerhubuser
# will be prepended to image name with /, used mostly for publishing
RUSER=testing
# default is hub.docker.com
# REPO=my.priviate.repo.net
# if using base source this will set the pw for the sysadmin user in the image
SYSADMIN_PW=ucommandit
# default target is dev
# TARGET=deploy
# default target is "default"
# TARGET=publish
BUILD_SRC=../src
# looks for /init and /packages in present directory by default
# also looks in src/ subdirectory
@ -19,9 +22,6 @@ BUILD_SRC=../src
# use '_base_' to force using the uci-docker-build build source
# BUILD_SRC=._base_
# in the image where the build scripts are put /build by default
# BUILD_DIR=/opt/build
# keep the build scripts in the image. default is to remove them after build
# KEEP=true

6
examples/private.env Normal file
View File

@ -0,0 +1,6 @@
# this would push build image to a custome git server that supports packages like gitea
# LINUX_DISTRO=alpine
RUSER=ucommandit
TARGET=private
REPO=git.mygitserver.net
BUILD_SRC=../src

5
examples/publish.env Normal file
View File

@ -0,0 +1,5 @@
# LINUX_DISTRO=alpine
# this will publish both arm and amd version to docker hub
RUSER=ucommandit
TARGET=publish
BUILD_SRC=../src

2
examples/try Executable file
View File

@ -0,0 +1,2 @@
# invokes build with the example environment file
udbuild try -e example.env ${@:-shell}

29
install
View File

@ -11,19 +11,26 @@ declare -a a="(${PATH//:/ })"
for i in ${a[*]}; do [[ $i == $parent ]] && found=true; done
if [[ $found ]]; then
echo creating a link \'$cmd\' in \'$parent\' to \'$builder\'
if ln -ns $builder/build $target; then
if [[ -f $target ]]; then
echo "$target already exists do you want to overwrite? (y/n) "
read -e ans
[[ ! $ans == "y" ]] && exit 1
fi
if ln -fns $builder/build $target; then
[[ ! $(command -v $cmd) ]] && echo FATAL: link failed $cmd not found in path \
|| echo install success: try \'$cmd -h\' now
else
else
echo Error creating link
echo if \': Permission denied\' 'then' run \'sudo ./install\'
fi
else
echo $parent not in current path
echo $PATH
echo link to script not created
echo "add the following export somewhere in your shell (e.g. ~/.bashrc)"
echo "export UDBUILD=$builder/build"
echo 'and then use $UDBUILD to invoke the build script ( e.g $UDBUILD -e mybuild.env)'
echo "or rerun this script using a directory in the system path (e.g ./install /usr/bin build)"
fi
else
echo "Install failed: $parent not in current path"
echo $PATH
echo "link to script not created. your install options are:"
echo "1. add $parent to your PATH"
echo "2. rerun this script using a directory in the system path (e.g ./install /usr/bin build)"
echo "3. add the following export somewhere in your shell (e.g. ~/.bashrc)"
echo " export UDBUILD=$builder/build"
echo ' and then use $UDBUILD to invoke the build script'
echo ' ( e.g $UDBUILD -e Smybuild.env)'
fi

1296
lib/bash-tpl Executable file

File diff suppressed because it is too large Load Diff

View File

@ -1,68 +0,0 @@
#!/bin/bash
image_name () {
local tag; local efile
# generate a full image name with tag
# $1 name, $2 user(or repo), $3 repo
# [[ $# -lt 1 ]] && echo "image base name required" && exit
declare OPTION; declare OPTARG; declare OPTIND
while getopts 'e:ag:r:u:' OPTION; do
# echo processing: option:$OPTION argument:$OPTARG index:$OPTIND remaining:${@:$OPTIND}
case "$OPTION" in
e)
efile=$OPTARG
;;
r)
REPO=$OPTARG
;;
u)
RUSER=$OPTARG
;;
g)
TAG=$OPTARG
;;
a) # add -arm64 to image
arm=arm64
;;
*) echo unknown run option -$OPTARG
echo "USAGE: tag <options>"
echo "available options: -a add -arm64 to tag, -d delete tag "
;;
esac
done
shift $((OPTIND - 1))
echo $efile
if [[ $efile ]]; then
[[ ! -f $efile ]] && efile=$SDIR/$efile
if [[ -f $efile ]]; then
source $efile
[[ ! $? -eq 0 ]] && echo source of $efile failed, exiting && return 2
else
echo no environment file at $efile, exiting
return 2
fi
echo "----------"
echo loaded environment filen $efile
cat $efile
echo "----------"
fi
tag=$( echo $1 | cut -s -d ":" -f2)
tag=${tag:-$TAG}
name=${1%:*}
user=${2:-$RUSER}
repo=${3:-$REPO}
tag=$([[ $repo ]] && echo ${repo}/)$([[ $user ]] && echo ${user}/)$name$([[ $arm ]] && echo -arm64):${TAG:-latest}
echo $tag
}
# if script was executed then call the function
(return 0 2>/dev/null) || image_name $@

50
lib/cmds/01-image-name.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
image_name () {
local tag; local efile; local suffix
# generate a full image name with tag
# $1 name, $2 user(or repo), $3 repo
# [[ $# -lt 1 ]] && echo "image base name required" && exit
declare OPTION; declare OPTARG; declare OPTIND
while getopts 'e:s:g:r:u:' OPTION; do
# echo processing: option:$OPTION argument:$OPTARG index:$OPTIND remaining:${@:$OPTIND}
case "$OPTION" in
e)
efile=$OPTARG
;;
u)
RUSER=$OPTARG
;;
g)
TAG=$OPTARG
;;
s) # add -arm64 to image
suffix=$OPTARG
;;
*) echo unknown image-name option -$OPTARG
echo "USAGE: image_name <options> <name> <repo_user>"
echo "available options: -s <suffix>: add -<suffix> , -g: tag, -u: repo user, -e: env file"
;;
esac
done
shift $((OPTIND - 1))
source_env_file $efile
tag=$( echo $1 | cut -s -d ":" -f2)
TAG=${tag:-$TAG}
name=${1%:*}
shift
get_distro
echo $(make_image_name $name $@)$([[ $suffix ]] && echo -$suffix):${TAG:-latest}
}
# if script was executed then call the function
(return 0 2>/dev/null) || image_name $@

89
lib/cmds/help.md Normal file
View File

@ -0,0 +1,89 @@
# UCOMMANDIT DOCKER BUILD SCRIPT
Image Build Script: Creates one or more images using a target per the docker-bake.hcl file
## USAGE
`udbuild <option switches> <image_name> <repo_user>`
image_name and repo_name are optional and can also be set by environments NAME and RUSER.
RUSER can also be set by -u option if image_name is not provided.
`udbuild <subcommand> <subcommand option switches> <subcommand arguments>`
## SUBCOMMANDS:
`try` - runs a trial container with image
`load_env_file <path>` - loads an build environment file, useful aid for an external script
`build_src <path>` - attempts to locate a valid build source directory
`help,-help,--help <subcommand>` - view this info or that of a subcommand
`source` - view the source of this script
`image` - image related subcommands
* `name` - generate name from environment/switches
* `tag` - add a name (docker tag) to an existing image
* `push` - push an image to a remote repository
* `delete` - completely delete a local image
* `info` - with no subcommand show all info for image
- `arch` - list machine architecture of image
- `exists` - used mostly for scripting, determines if image exists locally
- `tags` - get the names (docker tags) of an image
-S `id` - gets the id of an image from its name (docker tag)
## BUILD OPTIONS
### setable ONLY via CLI switches
`-h` show this help
`-p` if running interactive suppress the build prompts
`-n` use no-cache, bust the cache and force rebuild the entire image, see `-f:REBUILD`
`-o` do not overwrite an existing image on build (default), instead move it to a temporary timestamp tag
### setable via CLI switches or via environment variable
`-v : VERBOSE=true` - show verbose information about the build, verbose is default for `dev` target
`-a <path> : APPEND_BUILD_ENV=<path>`- append this environment file to baked in build.env for image. Allows easy customizing of image build environment found at src/init/build.env
`-l : APPEND_DISTRO=true` - will append the distro name to the image name. Can help to identify different distros when making distro variation of the same build
`-g <tag> : TAG=<tag>` - tag following : in output image name (i.e. REPO/USER/NAME:TAG), default: latest
`-e <path> : BUILD_EFILE=<path>` - source an environment file. By default will look for .env, in PWD. If used will also try <path> and <path>.env in PWD.
`-d : LINUX_DISTRO=<name>` - supported: alpine, debian, ubuntu, default: alpine; if base image set distro will be determined (and this ignored). Default is alpine
`-i <path> : IMAGE_INFO=<path>` - path to file of information to be included in the image at `/opt/image.info` and visible via `image` command of the entrypoint. Will over write any image.ifo file in src/init/image.info of the build sources
`-a <path> : APPEND_EFILE=<path>` - will include this file in build source and source it during the init RUN. So a way to set custom build environment without editing the build source.
`-b <name> : BASE_IMAGE=<name>` - used in `FROM` in Dockerfile. The default will be an official distro image (e.g.`ubuntu:latest`) based on `LINUX_DISTRO` and the default distro is alpine so if both `BASE_IMAGE` and `LINUX_DISTRO` are unset FROM will use `alpine:latest`
`-t <target> : TARGET=<target>` - the valid build targets for `buildx bake` are `dev arm64 amd64 publish multi default` default is `default`
`-r <repo> : REPO=<repo>` - set a remote private repository for pushing, default is docker hub
`-c <cmd> : TRY_CMD=<cmd>` - a command to use in the try container, default is 'shell`
`-s <path> : BUILD_SRC=<path>` - set the path to the build source directory, default is src/ in current or parent directory`
`-f : REBUILD=init` - force rebuild of only the Dockerfile RUN initialize instruction, note: use `-n` to force rebuild of entire image
`-u <name> : RUSER=<name>` - the remote repository user name to prepend to the image name. This will be needed for pushing to remote repositories like docker hub or a private repository (e.g. gitea)
### setable ONLY via environment variable
## examples:
### commad line
### environment file

View File

@ -1,31 +0,0 @@
#!/bin/bash
image_tag () {
local name; local remove; local id
# tags an image
# -i <imagetag or id> <newimagetag>
[[ $# -lt 1 ]] && echo "image base name required" && exit
[[ $1 == "-r" ]] && remove=true && shift 1
[[ $1 == "-i" ]] && { shift 1; id=$1; } || id=$(image_id $1)
name=$(image_name "$1")
[[ ! $id ]] && { echo "no image with id:$id name:$name"; return 1; }
if [[ $2 ]];then
# echo making tag for $2 $(image_name $2)
docker tag $id $(image_name $2)
else
[[ $remove ]] && docker rmi $name || echo to remove an image tag use -r
fi
image_tags $id
}
image_delete () {
docker rmi -f "$@"
}

37
lib/cmds/image-tag.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash
image_tag () {
local name; local remove; local id; local ntag; local tag
# tags an image
# -i <imagetag or id> <newimagetag>
[[ $1 == "-r" ]] && remove=true && shift 1
if [[ $1 == "-i" ]]; then
shift 1
id=$1
else
id=$(image_id $1)
name=$1
fi
[[ ! $(image_exists $id) ]] && { echo "no image $name $id nothing to tag"; return 1; }
ntag=$2
if [[ $ntag ]];then
# echo making tag for $2 $(image_name $2)
[[ $ntag = :* ]] && ntag=$( echo $name | cut -d ":" -f1)$ntag
docker tag $id $ntag
else
[[ $remove ]] && docker rmi $name || echo to remove an image tag use -r
fi
image_tags $id
}
image_delete () {
local id
id=$(image_id $@)
docker rmi -f $id
}

View File

@ -1,150 +0,0 @@
#!/bin/bash
# starts a trail container with passed image with a bash prompt
# $1 image name, $2 user
# user can be also prepended by using u option
# added tag is "latest" by default, use t option for alternate tag
# if p flag is used script will scrub any local image and attempt to download a deployed to docker image
try_container () {
declare -A arch=( ["x86_64"]="" ["aarch64"]="-arm64")
local mp;local cuser; local hmp; local vname; local prod; local priv
local hostmp; local efile; local entrypoint; local evar
local envf; local image; local append_distro; local options
declare OPTION; declare OPTARG; declare OPTIND
OPTIND=0
while getopts 'lf:o:dpr:t:u:m:h:kv:e:s:' OPTION; do
# echo processing: option:$OPTION argument:$OPTARG index:$OPTIND remaining:${@:$OPTIND}
case "$OPTION" in
s)
entrypoint="--entrypoint $OPTARG"
;;
e)
if ! source_env_file $OPTARG; then return 2; fi
efile=true
;;
f)
envf="--env-file $OPTARG"
;;
d)
dryrun="echo "
;;
u)
cuser=$OPTARG
;;
l)
# append distro name to image name
append_distro=true
;;
k)
keep=true
;;
# s)
# save=true
# ;;
m)
mp=$OPTARG
;;
o)
options=$OPTARG
;;
h)
hmp=$OPTARG
;;
v)
evar="-e $OPTARG"
;;
t)
TAG=$OPTARG
;;
p)
priv=--privileged
;;
r)
prod=$OPTARG
;;
*) echo unknown run option -$OPTARG
echo "USAGE: try <options>"
echo "available options: -t <latest> custom tag "
;;
esac
done
shift $((OPTIND - 1))
[[ ! $efile ]] && source_env_file
image=$(make_image_name $@)
echo image name to try: $image
[[ ! $image ]] && echo must supply an image to try && return 1
if [[ $prod ]]; then
echo removing any local copy of image $image
docker image rm $image
host=prod
else
host=local
# TODO change this
image=${image/:/${arch[$(uname -p)]}:}
fi
name=${image//\//-}
image=$image:${TAG:-latest}
docker rm try-$name > /dev/null 2>&1
if [[ $mp ]]; then
hostmp="${hmp:-${PWD}/mnt/$mp}"
[[ ! $(isAbsPath $hostmp) ]] && hostmp=$PWD/$hostmp
vname="try-$name${dir//\//-}"
mkdir -p "$hostmp"
# echo bind $dir to volume $vname
$dryrun docker volume create --driver local \
--opt type=none \
--opt device=$hostmp \
--opt o=bind "$vname" > /dev/null
[[ ! $dryrun ]] && echo directory $mp in container will be mounted at $hostmp
# else
# vname="try-$name-${mp//\//-}"
# hostmap="-e HOST_MAP=$(id -u):$(id -g)"
# dkpath=$(docker info | grep -i "Docker Root Dir" | cut -d':' -f2)/volumes/$vname/_data
# echo via volume $vname
# echo linking $dkpath to $hostmp
# $dryrun ln -s $dkpath -T $hostmp
# fi
fi
echo starting container with image: $image, and name $name
echo at container prompt type \'exit\' to exit from shell and remove trial container
# --entrypoint /opt/scripts/entrypoint.sh \
$dryrun docker run -i -t --rm $priv $evar $options \
${entrypoint} ${evnf} \
$([[ $cuser ]] && echo "--user $cuser") \
--name try-$name --hostname try-$host-$name \
$([[ $mp ]] && echo "-v $vname:/$mp") \
"$image" \
"$@"
echo "done with session, removing containter try-$name"
if [[ $mp ]] && [[ ! $dryrun ]]; then
# if [[ ! $save ]]; then
echo removing volume $vname used for mapping
docker volume rm $vname > /dev/null
if [[ $keep ]]; then
echo mounted container directory $mp on host at $hostmp will not be removed
else
echo deleting directory at mountpoint $hostmp mapped to $mp in container
echo "use option -k to keep this directory after exiting container"
echo "useful for testing scripts inside the container"
rm -rf $hostmp
fi
# [[ ! $bind ]] && [[ -h $hostmp ]] && echo "removing link at $hostmp" && rm $hostmp
# fi
fi
}
# if script was executed then call the function
(return 0 2>/dev/null) || try_container "$@"

181
lib/cmds/try.sh Executable file
View File

@ -0,0 +1,181 @@
#!/bin/bash
# starts a trail container with passed image with a bash prompt
# $1 image name, $2 user
# user can be also prepended by using u option
# added tag is "latest" by default, use t option for alternate tag
# if p flag is used script will scrub any local image and attempt to download a published to docker image
try_container () {
declare -A arch=( ["x86_64"]="" ["aarch64"]="-arm64")
local mp;local cuser; local hmp; local vname; local prod; local priv
local hostmp; local efile; local entrypoint; local evar
local envf; local image; local options; local dry_run
local build; local cmd; local script; local dcmd
[[ $1 == "build" ]] && build=true && shift
declare OPTION; declare OPTARG; declare OPTIND
OPTIND=0
while getopts "f:o:dpr:t:u:m:h:kv:e:i:c:s:b:y:" OPTION; do
# echo processing: option:$OPTION argument:$OPTARG index:$OPTIND remaining:${@:$OPTIND}
case "$OPTION" in
y)
entrypoint="--entrypoint $OPTARG"
;;
s)
script="$OPTARG"
;;
e)
efile=$OPTARG
;;
c)
cmd=$OPTARG
;;
i)
image="$OPTARG"
;;
f)
envf="--env-file $OPTARG"
;;
b)
# CUSTOM BASE IMAGE
BASE_IMAGE=$OPTARG
;;
d)
dry_run="echo "
;;
u)
cuser=$OPTARG
;;
k)
keep=true
;;
m)
mp=$OPTARG
;;
o)
options=$OPTARG
;;
h)
hmp=$OPTARG
;;
v)
evar="-e $OPTARG"
;;
t)
TAG=$OPTARG
;;
p)
priv=--privileged
;;
r)
prod=$OPTARG
;;
*) echo unknown run option -$OPTARG
echo "USAGE: try <options>"
echo "available options: -t <latest> custom tag "
;;
esac
done
shift $((OPTIND - 1))
image=${image:-$IMAGE_NAME}
if [[ ! ( $build && $image ) ]]; then
echo attempting to getting image name from environment file
get_distro
source_env_file $efile
image=$(make_image_name)
fi
if [[ ! $image ]]; then
echo must supply an image name to try either via -i option
echo or setting $IMAGE_NAME environment variable
echo or from an environment file
return 1
fi
if [[ $prod ]]; then
echo removing any local copy of image $image
docker image rm $image
host=prod
else
host=local
# TODO change this
image=${image/:/${arch[$(uname -p)]}:}
fi
name=${image//\//-}
image=$image:${TAG:-latest}
echo trying image name: $image
docker rm try-$name > /dev/null 2>&1
if [[ $mp ]]; then
hostmp="${hmp:-${PWD}/mnt/$mp}"
[[ ! $(isAbsPath $hostmp) ]] && hostmp=$PWD/$hostmp
vname="try-$name${dir//\//-}"
echo $vname
mkdir -p "$hostmp"
dvcmd=$( tr "\n" " " <<-END
docker volume create --driver local
--opt type=none
--opt device=$hostmp
--opt o=bind $vname
END
)
if [[ $dry_run ]]; then
echo dry run volume creation command
echo $dvcmd
else
if ! $dvcmd > /dev/null; then
echo error creating volume, aborting container try
return 4
fi
fi
echo directory $mp in container will be mounted at $hostmp
fi
if [[ ! $dry_run ]]; then
echo starting container with image: $image, and name $name
echo at container prompt type \'exit\' to exit from shell and remove trial container
fi
dcmd=$( tr "\n" " " <<-END
docker run -i $([[ ! $script ]] && echo -t)
--rm $priv $evar $options ${entrypoint} ${evnf}
$([[ $cuser ]] && echo --user $cuser)
--name try-$name --hostname try-$host-$name
$([[ $mp ]] && echo -v $vname:/$mp)
$image
$([[ $script ]] && echo script || ${cmd} ) $@
END
)
if [[ $dry_run ]]; then
echo dry run, docker command
echo "$([[ -f $script ]] && echo cat || echo "echo") "$script" | $dcmd"
elif [[ $script ]]; then
$([[ -f $script ]] && echo cat || echo "echo") "$script" | $dcmd
else
$dcmd
echo "done with session, removing containter try-$name"
if [[ $mp ]] ; then
echo removing volume $vname used for mapping
docker volume rm $vname > /dev/null
if [[ $keep ]]; then
echo mounted container directory $mp on host at $hostmp will not be removed
else
echo deleting directory at mountpoint $hostmp mapped to $mp in container
echo "use option -k to keep this directory after exiting container"
echo "useful for testing scripts inside the container"
rm -rf $hostmp
fi
fi
fi
}
# if script was executed then call the function
(return 0 2>/dev/null) || try_container "$@"

View File

@ -1,34 +0,0 @@
#!/bin/bash
usage() { # Function: Print a help message.
echo "Image Build Script: Creates one or more images using a target in the docker-bake.hcl file"
echo "USAGE: $0 <subcommand> <options> <build_target> <imagename, imageuser>"
echo "valid build_target: ${targets[*]}; default: dev"
echo "### subcommands:"
echo "try (runs try a container script, see try usage) $0 try -i"
echo "tag (runs image_tag script)"
echo "info get image info, info <arch, exists, id> nothing is all info in json"
echo "--- option switches (no argument):"
echo "-o do not overwrite an existing image (default), instead move it to a temporary timestamp tag"
echo "-i rebuild only the initialization RUN by busting the cache at that point"
echo "-v show verbose information about the build"
echo "-a (auto) do not prompt to continue build, by default will not prompt if non-interactive shell"
echo "-c after build try out the image by starting a container terminal therein, for dev target this is the default;"
echo "-x exclude distro from image name;"
echo "-n for --no_cache"
echo "-k keep the build scripts (see -w) after the build so they are incorporated into the image"
echo "-p push to repository; after build push to repository default is hub.docker.com (not need for deploy target)"
echo "--- options with argument <ARGUMENT>:"
echo "-e <path to environment file> load any or all of options below via a file"
echo "-d <LINUX_DISTRO> supported: alpine, debian, ubuntu, default: alpine; if base image set distro will be determined"
echo "-t <TAG> tag following : in output image name (i.e. REPO/USER/NAME:TAG), default: latest"
echo "-u <USER>; repository user prefix in output image name (i.e. REPO/USER/NAME:TAG)"
echo "-r <REPO>; private repo name, do not use for hub.docker.com (docker.io)"
echo "-b <BASE_IMAGE>; used in FROM in Dockerfile, default is official distro image (e.g. alpine:latest)"
echo "-w <BUILD_DIR>; set a custom WORKDIR in Dockerfile (in image), default is /build, see -k"
echo "--- options set ONLY by environment variable (see -e as well)"
echo "<SYSADMIN_PW> set alternate password for container sysadmin account, default is 'sysadmin'"
echo "NOTE any option with <CAPTIALIZED> above can be set in environment instead"
echo "#### examples:"
echo "$0 -a -d ubuntu -u ucommandit"
echo "build (without prompt) a local ubuntu image from scratch and label it ucommandit/ubuntu:latest"
}

14
lib/cmds/usage.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/bash
usage() {
local help; local force
help=$(dirname "$(realpath "$BASH_SOURCE")")/help.md
[[ $1 == "c" ]] && force=cat
if [[ $(which glow) && ! $force ]]; then
glow $help
else
echo -e "\e[1;31mfor a better experience viewing this help file install glow, https://github.com/charmbracelet/glow\e[1;37m"
sed 's/`//g' < $help
fi
}

View File

@ -1,5 +1,9 @@
#!/bin/bash
quiet () {
if [[ $VERBOSE ]]; then $@; fi
}
get_arch () {
local arch="$(uname -m)"
case "$arch" in
@ -34,14 +38,15 @@ sed_ignore_comments () {
}
clean_env_file () {
local compact
local compact; local lines
# -c will also remove comments and all empty lines
[[ $1 == "-c" ]] && compact=true && shift 1
#
# remove trailing spaces | remove double quotes
# | remove blanks after equals and quote value | remove any spaces before afer =
# remove blank lines | remove comment lines if requested
cat $1 | sed_ignore_comments s/\\s*$//g | sed_ignore_comments s/\"//g \
[[ -f $1 ]] && lines=$(<$1) || lines="$1"
echo "$lines" | sed_ignore_comments s/\\s*$//g | sed_ignore_comments s/\"//g \
| sed_ignore_comments s/\(=[[:blank:]]*\)\(.*\)/\\1\"\\2\"/ \
| sed_ignore_comments s/\\s*=\\s*/=/g \
| sed -rz 's/^\n+//; s/\n+$/\n/g' | if [[ $compact ]]; then grep -v '^#' | grep -v "^$" ; else cat; fi
@ -62,48 +67,50 @@ read_env_file() {
if [[ ! ${!evar} ]]; then
# echo DECLARE $evar via $line
if declare -gx "$(echo "${line}" | sed 's/\"//g' )"; then
echo loaded: ${evar}=${!evar}
quiet echo loaded: ${evar}=${!evar}
else
echo error setting $evar
quiet echo error setting $evar
return 1
fi
else
echo $evar already set to ${!evar}
quiet echo $evar already set to ${!evar}
fi
done < <(clean_env_file -c $1)
}
source_env_file () {
local default; local efile
if efile=$(env_file $1); then
local default;local efile
if efile=$(env_file ${1:-$BUILD_EFILE}); then
[[ $efile == ".env" ]] && default=true
[[ ! $(isAbsPath $efile) ]] && efile=$(realpath $efile)
echo -e "\e[1;37m********************\e[0;37m"
echo loading build environment with environment file
echo $efile
echo ----------------
cat $efile
echo ----------------
quiet echo -e "\e[1;37m********************\e[0;37m"
quiet echo loading build environment with environment file
quiet echo $efile
quiet echo ----------------
quiet cat $efile
quiet echo ----------------
if read_env_file "$efile"; then
[[ $default ]] && echo -e "\e[1;31mNOTE: sourced default .env file in present directory\e[0;37m"
echo -e "\e[1;37m********************\e[0;37m"
[[ $default ]] && quiet echo -e "\e[1;31mNOTE: sourced default .env file in present directory\e[0;37m"
quiet echo -e "\e[1;37m********************\e[0;37m"
BUILD_EFILE=$efile
else
echo error occured while loading environment file
echo $efile
echo exiting
echo -e "\e[1;37m********************\e[0;37m"
quiet echo error occured while loading environment file
quiet echo $efile
quiet echo exiting
quiet echo -e "\e[1;37m********************\e[0;37m"
return 2
fi
else
if [[ $efile ]]; then
echo unable to find an environment file with passed ${1}
quiet echo unable to find an environment file with passed ${1}
else
echo unable to find default environment file .env
echo using passed and default build options
quiet echo unable to find default environment file .env
quiet echo using the current environment plus default build options
fi
return 2
fi
}
docker_image_distro() {
@ -115,10 +122,27 @@ echo $(cat $temp | tr [:upper:] [:lower:] | grep -Poi '(debian|ubuntu|red hat|ce
rm $temp
}
get_distro() {
LINUX_DISTRO=${LINUX_DISTRO:-alpine}
if [[ $BASE_IMAGE ]]; then
quiet echo determining DISTRO of base image: $BASE_IMAGE
LINUX_DISTRO=$(docker_image_distro $BASE_IMAGE)
[[ ! $LINUX_DISTRO ]] && quiet echo "unable to get base image OS for: $BASE_IMAGE, aborting build" && return 5
quiet echo $BASE_IMAGE is built from distro $LINUX_DISTRO
else
BASE_IMAGE=$LINUX_DISTRO
fi
}
make_image_name () {
local arch
# echo making image name $@
# echo name: $NAME
# echo APPEND: $APPEND_DISTRO
# echo user: $RUSER
# echo distro $LINUX_DISTRO
@ -131,12 +155,114 @@ if [[ $2 ]]; then
RUSER=$2
fi
if [[ $NAME ]]; then
[[ $append_distro ]] && NAME=$NAME-${LINUX_DISTRO}
[[ $APPEND_DISTRO ]] && NAME=$NAME-${LINUX_DISTRO}
else
# echo no image name supplied using distro name ${LINUX_DISTRO:-alpine}
NAME=${LINUX_DISTRO:-alpine}
fi
echo $([[ $RUSER ]] && echo ${RUSER}/)${NAME}
[[ $TARGET == "arm64" && ! $TARGET == $(get_arch) ]] && arch=-$TARGET
[[ $TARGET == "amd64" && ! $TARGET == $(get_arch) ]] && arch=-$TARGET
echo $([[ $RUSER ]] && echo ${RUSER}/)${NAME}$arch
}
get_build_src () {
# processing the build source directory
local src; local spath; local spaths
check_dir () {
[[ ( -d $1/packages && -d $1/init ) ]] || return 1
}
src=${1:-$BUILD_SRC}
[[ $src == "_default_" ]] && src=${BDIR}/src
src=${src:-src}
unset BUILD_SRC
if check_dir $src; then
BUILD_SRC=$(realpath $src)
return 0
fi
echo build source path \'$src\' not initially found
echo looking in parent directories for a valid build source directory
echo to avoid this search use BUILD_SRC= in an environment file
spaths="$PWD $(dirname $PWD)/$src $(dirname $PWD)"
for spath in $spaths; do
echo checking for source in: $spath
if check_dir $spath; then
echo found valid source directory!
echo using $spath
BUILD_SRC=$spath
return 0
fi
done
echo -e "\e[1;31mERROR: unable to find a build source directory as $src \e[1;37m"
return 1
}
build_info () {
echo "Build Command: docker buildx --builder ${builder} bake ${nocache} ${TARGET}"
if [[ $VERBOSE ]]; then
echo -e "\n!!!!!!!!!!!!!!!!EXTRA BUILD INFO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
pushd "$BDIR" > /dev/null || return 3
docker buildx bake --print $TARGET
popd > /dev/null || return 4
echo -e "\n---------------------------------"
echo "build source at $BUILD_SRC to be mounted to /build in container ***** "
ls -la $BUILD_SRC
echo -e "\n----- base init script init.sh ------"
cat $BUILD_SRC/init/init.sh
echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
fi
echo -e "\e[1;37m**************BUILD PARAMETERS *******************************"
echo "Architecture of this machine doing the building: $ARCH"
echo "Using scripts source directory at $BUILD_SRC"
echo "Building with base image: $BASE_IMAGE"
echo "Build logs can be found in directory $log_dir"
echo "Linux Distro of Image: $LINUX_DISTRO"
echo "Using build target: ${TARGET}"
echo "----- output --------"
echo creating image
case $TARGET in
multi)
echo "names for both amd64 and arm64, image without architecture suffix is $ARCH"
echo -e "\e[1;31m$IMAGE_NAME$([[ ! $ARCH == amd64 ]] && echo -amd64):${TAG:-latest}\n$IMAGE_NAME$([[ ! $ARCH == arm64 ]] && echo -arm64):${TAG:-latest}\e[1;37m"
echo "building locally and then pushing to ${REPO:-hub.docker.com}"
if [[ ! $REPO ]]; then
echo "NOTE: use target 'publish' to build and push these both"
echo "to docker hub without architecture suffix or local copy"
fi
;;
dev)
;&
default)
echo -e "name => \e[1;31m$IMAGE_NAME:${TAG:-latest}\e[1;37m"
echo "Will build this single image on this machine with architecture $ARCH"
;;
arm64)
echo -e "name => \e[1;31m$IMAGE_NAME$([[ ! $ARCH == arm64 ]] && echo -arm64):${TAG:-latest}\e[1;37m"
echo "Will build this single arm64 image on this machine"
;;
amd64)
echo -e "name => \e[1;31m$IMAGE_NAME$([[ ! $ARCH == amd64 ]] && echo -amd64):${TAG:-latest}\e[1;37m"
echo "Will build this single amd64 image on this machine"
;;
publish)
echo -e "image name \e[1;31m$IMAGE_NAME:${TAG:-latest}\e[1;37m"
echo "Will build and push both amd64 and arm64"
echo "architecture images to hub.docker.com with this single name"
echo "and no local copy will remain"
;;
esac
echo "------------------"
echo -e "********************\e[0;37m"
}

View File

@ -2,7 +2,7 @@
declare libdir
libdir=$(dirname "$(realpath "$BASH_SOURCE")")
source $libdir/helpers.lib
for f in $libdir/cmds/*; do
for f in $libdir/cmds/*.sh; do
# echo sourcing: $f >&2
source "$f"
done

View File

@ -1,52 +1,32 @@
# UCI Docker Image Builder
Prerequistes:
Easily build docker images for local use and publishing without messing around with a Dockerfile. Just some knowledge of BASH scripting is needed.
see https://hacking.kebler.net/Linux/Arch/docker/
The "guts" of this repo is a single BASH shell script plus associated library to facilitate building docker linux images using any of four of these distros (arch,alpine,debian,ubuntu) and either architecture (amd64/arm64)
## About
## Prequisites
You need docker daemon installed with some other installation particulars in order to build images
The Dockerfile is assembled at build time is essentially two RUN commands. One to install packages (you specify) via a distro's package manager and then one to run a BASH init script that you write with specifics of initializing the image with whatever software the image is tasked to do. You specify those by setting a BUILD_SRC build source directory. This repo comes with a very basic build source which can handle any of the four distros mentioned. To build something more complicated you need to create your or src/ directory (see docs) or use one already developed (e.g. uci-docker-caddy repo)
<!-- assuming your current (dev) user is in the docker group
for linking into volumes for the try command
```
sudo chown -R :docker /var/lib/docker
sudo chmod -R g+r docker /var/lib/docker
``` -->
Baked into the initilization RUN is an entrypoint script that makes interacting with the image/container easy and also to launch your "custom" app within th container.
The build makes user of docker's "buildx bake" commands and a docker-bake.hcl file to make local images
A build script plus associated library to facilitate building docker linux images using any of four distros (arch,alpine,debian,ubuntu) and either architecture (amd64/arm64)
The Dockerfile is minimal copying a directory and then calling a packages script and and init script in two RUN statements calling a set of distro specific scripts and common scripts in order to build the image
The build environment makes user of docker's "buildx bake" commands and a docker-bake.hcl file
The master branch is configured to build base images from the docker hub distro base images
The main script is "build" At the very minimum run as just `./build` it will build an alpine image from the docker hub official alpine latest image with a minimal set of packages installed (e.g. git) and a custom uci shell environment.
The default build is simply a basic alpine image saved locally to alpine:lastest.
To run the ./build script from other repos install it using the install script
'./install
The repo also supports (with scripts) pushing to alternate private repositories packages (like a self hosted gitea or github)
One can make decendent images in one of two ways.
One can make images in one of two ways.
1. Make your base images on the master branch (or a branch if you need to customize)
2. User the installer script (./install) to link the build script into your system path. Then clone the template branch
3.
1. edit the /src directory on this master branch (or a better a branch)
2. Use the installer script (./install) to link the build script into your system path. THen you can develop a BUILD_SRC src/ directory anywhere and call the build script from there
the build script requires an init.sh file in a source directory for the scripts it will run while building a new image
by default that is `<source directory>/init.sh`. by default that source directory is `src/` relative to the current directory
run
`./build -h`
for help
also see examples folder for example build environment file (more help details)
It is recommended to do the later.

View File

@ -1,6 +0,0 @@
#!/bin/bash
export SCRIPTS_DIR="$(dirname $(realpath "${BASH_SOURCE:-$0}"))"
# echo arguments in entry $@
# execute the start script in a login shell so that the uci shell will be sourced
# see https://stackoverflow.com/a/1711985/4695378 passing $@
/bin/bash -l -c '${SCRIPTS_DIR}/start.sh $@' $0 "$@"

View File

@ -1,19 +0,0 @@
#!/bin/bash
# echo arguments in start script: "$@"
source /opt/env/run.env
source /opt/bin/host-id-map.sh
case "$1" in
cmd)
shift 1
"$@"
;;
script)
shift 1
module_load path
script=$([[ $(isAbsPath $1) ]] && echo $1 || echo opt/bin/$1)
shift 1
/bin/bash $script "$@"
;;
*)
/bin/bash -c "cd ${INITIAL_DIR:-$HOME}; exec bash -l"
esac

View File

@ -1 +0,0 @@
base-entrypoint

View File

@ -1,7 +0,0 @@
#!/bin/bash
if [[ $HOST_MAP ]]; then
echo changing ownership of $VOLUME_DIRS to $HOST_MAP
declare usesudo
[[ ! $EUID -eq 0 ]] && usesudo=sudo
$usesudo chown -R $HOST_MAP $VOLUME_DIRS
fi

View File

@ -1 +0,0 @@
base-start

7
src/init/build.env Normal file
View File

@ -0,0 +1,7 @@
# anything in here will be sourced during build
# allows easy custom environment variables
# by sourcing run.env in build will available in build
[[ $ENV_DIR && -f $ENV_DIR/run.env ]] && source $ENV_DIR/run.env
# export VOLUME_DIRS="/opt"

View File

@ -5,27 +5,20 @@ echo -e "\n##################################"
# which useradd
# which /bin/bash
# echo SHELL $SHELL
# groupadd -g 1000 host
# useradd -r -g host -u 1000 host
# git clone https://git.kebler.net/bash/shell-base.git /shell/base
# export SHELL=/bin/bash
# mkdir -p /home/sysadmin/shell
# /bin/bash /shell/base/install/install.sh sysadmin
# echo "******** uci shell install complete **********"
# # make essential directories
# mkdir -p $VOLUME_DIRS /opt/bin
# chown -R :host /home/sysadmin/shell
# chown -R host:host $VOLUME_DIRS
# chmod -R g+rw /home/sysadmin/shell $VOLUME_DIRS
# ls -la /home/sysadmin/shell $VOLUME_DIRS
groupadd -g 1000 host
useradd -r -g host -u 1000 host
groupadd -g 1001 sysadmin
useradd -rm -s /bin/bash -G host,$([[ $(getent group sudo) ]] && echo sudo || echo wheel) -g sysadmin -u 1001 sysadmin
echo sysadmin groups: $(groups sysadmin)
chpasswd <<<"sysadmin:${SYSADMIN_PW:-sysadmin}"
cp permitmod /etc/sudoers.d
chmod 440 /etc/sudoers.d/permitmod
git clone https://git.kebler.net/bash/shell-base.git /shell/base
export SHELL=/bin/bash
mkdir -p /home/sysadmin/shell
/bin/bash /shell/base/install/install.sh sysadmin
echo "******** uci shell install complete **********"
# make essential directories
mkdir -p $VOLUME_DIRS /opt/bin
chown -R :host /home/sysadmin/shell
chown -R host:host $VOLUME_DIRS
chmod -R g+rw /home/sysadmin/shell $VOLUME_DIRS
ls -la /home/sysadmin/shell $VOLUME_DIRS
echo installing and running image distro info
/bin/bash install-os-info.sh
/opt/bin/os-info

View File

@ -1,8 +0,0 @@
# allow admins to change group and user ids
sysadmin ALL = NOPASSWD : ALL
# sysadmin ALL = NOPASSWD:/sbin/groupmod
# sysadmin ALL = NOPASSWD:/sbin/usermod
# sysadmin ALL = NOPASSWD:/sbin/adduser
# sysadmin ALL = NOPASSWD:/sbin/addgroup
# sysadmin ALL = NOPASSWD:/sbin/useradd
# sysadmin ALL = NOPASSWD:/sbin/groudadd

View File

@ -1,4 +0,0 @@
# anything in here will be sourced during build
# allows easy custom environment variables
source /opt/env/run.env
# export SOMEVAR="something"

View File

@ -1,3 +1,3 @@
# added /opt/run.env to the end of /etc/profile
export VOLUME_DIRS="/shell /opt /data"
export SHELL=/bin/bash
export INITIAL_DIR=/opt

5
src/init/image.info Normal file
View File

@ -0,0 +1,5 @@
If you create/edit a image.info file in your source
at the init/ subfolder then it will be including in the build
as /opt/image.info.
So this text comes from the default image.info file
in the src/init folder of the default build source

2
src/init/init.sh Normal file → Executable file
View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
echo "running base init.sh script in /build"
# remove other distro files
# find $PWD -maxdepth 1 -type d ! -path $PWD ! -name ${LINUX_DISTRO} ! -name common -exec rm -rf {} +

View File

@ -3,7 +3,6 @@ echo install packages for alpine distro
apk update; apk upgrade;
pwd; ls -la
echo ">>>> installing packages => alpine: $(cat ./packages); common: $(cat ../common/packages)"
apk add --no-cache bash bash-completion $(cat ./packages) $(cat ../common/packages)
# needed for useradd and groupadd
apk add shadow --no-cache --repository=http://dl-2.alpinelinux.org/alpine/edge/community
# apk add bindfs --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing
apk add --no-cache $(cat ./packages) $(cat ../common/packages)

View File

@ -4,4 +4,6 @@ git
rsync
sudo
nano
coreutils
coreutils
bash
bash-completion

2
test/build.env Normal file
View File

@ -0,0 +1,2 @@
export VOLUME_DIRS="/opt/bin"
# export HOST_MAP="1001:1001"

1
test/test Executable file
View File

@ -0,0 +1 @@
udbuild -e test.env $@

11
test/test.env Normal file
View File

@ -0,0 +1,11 @@
# if SYSADMIN_PW is set a sysadmin user with uid of 1001 will be creted
# SYSADMIN_PW=ucommandit
# default is alpine
# LINUX_DISTRO=alpine
# prepend image name with this user, typically your docker hub user
# RUSER=ucommandit
RUSER=testing
# default is default target, use dev for running a test container after build
# TARGET=dev
# by default will look in PWD directory then parent
BUILD_SRC=../src