#!/bin/bash ################################################################################ set -eEuo pipefail err=0 trap 'err=$?' ERR trap 'cleanup' EXIT epoch=$(date +%s.%3N) today=$(date +%Y-%m-%d-T%H%M) notopt() { case $1 in 1) return 0;; 0) return 1;; *) return $1;; esac } badarg() { echo -n "$(basename $0): " >&2 echo "$1" >&2 echo "Try '$(basename $0) -h' for more information." >&2 exit 2 } cleanup() { if [[ $err -eq 0 || $opt_debug -eq 0 ]]; then podman rm -i -f tmp-$epoch 2>&1 > /dev/null fi } help="Usage: $(basename $0) [-sdh] [-t tag] [-v vol] [directory] [name] Builds an image from files in a directory, and assigns it a name. Files used are 'Containerfile' and optionally 'Initfile'. If first argument is omitted, script assumes files can be found in the current working directory. If second argument is omitted, the directory where the files were found is used as the image name. Options: -d Debug mode: don't delete the temporary container created by the script -r Redo build from scratch instead of using cached layers -s Squash all layers in the image into a single layer -t tag Tag the image with the given string. Can be used multiple times to assign multiple tags -v vol Mount ~/vol as /vol while building -h Display this help and exit" # Handle options opt_squash=1 opt_redo=0 opt_debug=0 opt_tags=() opt_vols=() while getopts ':srdt:v:h' arg; do case $arg in s) opt_squash=$(notopt ${opt_squash});; r) opt_redo=$(notopt ${opt_redo});; d) opt_debug=$(notopt ${opt_debug});; t) opt_tags+=("${OPTARG}");; v) opt_vols+=("${OPTARG}");; h) echo "$help"; exit 0;; :) badarg "Argument missing for option '-$OPTARG'";; ?) badarg "Invalid option '-$OPTARG'";; esac done shift $((OPTIND -1)) # Handle non-option arguments if [[ $# -gt 2 ]]; then badarg "Too many arguments" fi if [[ $# -ge 1 ]]; then directory="$1" else directory=$(pwd) fi if [[ $# -ge 2 ]]; then name="$2" else name=$(basename "$directory") fi # Main if [[ ! -d "$directory" ]]; then echo "Error: directory '$directory' not found" exit 1 else cd "$directory" fi buildopts="--build-arg EXT_HOME=$HOME" runopts="" if [[ $opt_squash -eq 1 ]]; then buildopts="$buildopts --squash-all" fi if [[ $opt_redo -eq 1 ]]; then buildopts="$buildopts --no-cache" fi for vol in "${opt_vols[@]}"; do buildopts="$buildopts -v $HOME/vol/${name}/${vol}:/vol/${vol}" runopts="$runopts -v $HOME/vol/${name}/${vol}:/vol/${vol}" done # tell buildah to build images in docker format instead of the default OCI format # because only docker-format images can use the SHELL directive in Containerfiles ### export BUILDAH_FORMAT=docker # build image echo "Building image tmp-$epoch ..." podman build -f Containerfile -t tmp-$epoch $buildopts # Initfile is for commands that need systemd to execute if [[ -f Initfile ]]; then echo "Running initialization ..." echo "Creating temporary container tmp-$epoch ..." podman create --name tmp-$epoch $runopts tmp-$epoch podman start tmp-$epoch echo "Copying script to container tmp-$epoch ..." podman cp Initfile tmp-$epoch:/root/ echo "Running Initfile script ..." podman exec tmp-$epoch bash -c "chmod +x /root/Initfile && /root/Initfile" echo "Committing container tmp-$epoch to image $name:$today ..." podman commit tmp-$epoch "$name:$today" if [[ $opt_debug -eq 0 ]]; then echo "Removing temporary container tmp-$epoch ..." podman rm -i -f tmp-$epoch fi else echo "Initfile not found, skipping temporary container step ..." # tag image we already built with appropriate tag, and untag with tmp echo "Tagging image tmp-$epoch as $name:$today ..." podman tag tmp-$epoch "$name:$today" fi if ! podman container exists tmp-$epoch ; then echo "Removing temporary image tmp-$epoch ..." podman rmi tmp-$epoch fi # tag image as latest echo "Adding latest tag to image $name:$today ..." podman tag "$name:$today" "$name:latest" # assign any extra tags for tag in "${opt_tags[@]}"; do echo "Adding tag $tag to image $name:$today ..." podman tag "$name:$today" "$name:$tag" done echo "... Done!"