#!/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 when encountering an error -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 ..." 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 ..." podman create --name tmp-$epoch $runopts tmp-$epoch podman start tmp-$epoch echo "Copying script to container ..." podman cp Initfile tmp-$epoch:/root/ echo "Running script ..." podman exec tmp-$epoch bash -c "chmod +x /root/Initfile && /root/Initfile" echo "Committing container to image ..." podman commit tmp-$epoch "$name:$today" else echo "Initfile not found, skipping temporary container step ..." # tag image we already built with appropriate tag, and untag with tmp podman tag tmp-$epoch "$name:$today" podman rmi tmp-$epoch fi # tag image as latest podman tag "$name:$today" "$name:latest" # assign any extra tags for tag in "${opt_tags[@]}"; do podman tag "$name:$today" "$name:$tag" done echo "Done!"