#!/bin/bash quiet () { if [[ $VERBOSE ]]; then $@; fi } get_arch () { local arch="$(uname -m)" case "$arch" in x86_64) arch='amd64' ;; armhf) arch='armv6' ;; armv7) arch='armv7' ;; aarch64) arch='arm64' ;; ppc64el|ppc64le) arch='ppc64le' ;; s390x) arch='s390x';; *) return 1 ;;\ esac; echo $arch } isAbsPath() { if [[ "${1:0:1}" == / || "${1:0:2}" == ~[/a-z] ]] then echo "true" return 0 else return 1 fi } sed_ignore_comments () { cmd="sed -r 'h;s/[^#]*//1;x;s/#.*//;${1};G;s/(.*)\n/\1/'" if (( $# == 2 )) ; then eval $cmd <<< "$2" else eval $cmd < /dev/stdin fi } clean_env_file () { 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 [[ -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 } env_file () { local efile efile=$(echo -- "$@" | grep -oP -- '(?<=-e )[^ ]*') if [[ ! $efile ]]; then [[ ! "$1" == *-* ]] && efile=${1:-.env} fi [[ -f "$(realpath "${efile}.env" 2> /dev/null)" ]] && { echo "${efile}.env"; return 0; } # || echo not ${efile}.env [[ -f "$(realpath "${efile}/.env" 2> /dev/null)" ]] && { echo "${efile}/.env"; return 0; } # || echo not ${efile}/.env [[ "${efile##*.}" == "env" ]] && [[ -f "$(realpath "${efile}" 2> /dev/null)" ]] && { echo $efile; return 0; } # || echo not $env return 1 } read_env_file() { local evar while read line; do evar=$(echo $line | cut -d '=' -f1) if [[ $(echo $(c() { echo $#; }; c $line)) -gt 1 ]];then echo the environment file: $1 echo has a format error on this line echo "--------------" echo $line echo "--------------" echo you must correct this before the build can continue return 1 fi if [[ ! ${!evar} ]]; then # echo DECLARE $evar via $line if declare -gx "$(echo "${line}" | sed 's/\"//g' )"; then quiet echo loaded: ${evar}=${!evar} else quiet echo error setting $evar return 1 fi else 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 "${@:-$BUILD_EFILE}"); then [[ $efile == ".env" ]] && default=true [[ ! $(isAbsPath $efile) ]] && efile=$(realpath $efile) 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 ]] && 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 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 quiet echo unable to find an environment file with passed ${1} else quiet echo unable to find default environment file .env quiet echo using the current environment plus default build options fi return 2 fi } load_csv () { # add newline, remove comments, remove empty lines, remove leading whitepace, remove extra whitespace around , if [[ -f $1 ]]; then sed -e '$a\' "$1" | \ sed -e '/\s*#.*$/d' | \ sed -e '/^\s*$/d' | \ sed 's/^\s*//g' | \ sed 's/\s*,\s*/,/g' else return 1 fi } get_distro_core_image_name () { local distro; local imagename distro=${1:-$LINUX_DISTRO} if [[ $distro ]]; then imagename=$(echo "$(load_csv $BDIR/distros.csv)" | grep "^${distro}," | cut -f2 -d, | sed "s/\s/|/g") [[ $imagename ]] && echo $imagename || return 1 else return 2 fi } get_distro_from_image () { local temp=/tmp/os-release.tmp local distro local keep [[ $1 == "-k" ]] && (keep=true;shift) [[ ! $1 ]] && return 1 if docker create --name dummy $1 > /dev/null 2>&1; then if docker cp -L dummy:${2:-/etc/os-release} $temp > /dev/null 2>&1; then docker rm -f dummy > /dev/null distro=$(cat $temp | grep "ID_LIKE=" | cut -f2 -d=) if [[ ! "$distro" ]]; then distro=$(cat $temp | grep "^ID=" | cut -f2 -d=) fi [[ "$distro" ]] && echo $distro || return 2 fi else return 1 fi [[ ! $keep ]] && docker image rm $1 > /dev/null 2>&1 } validate_distro() { # only valid distros are ones in distros.csv local distro; local set_distro; distro=${1:-$LINUX_DISTRO} if [[ $distro ]]; then if [[ $BASE_IMAGE ]]; then >&2 echo "FATAL: cannot specifiy both BASE_IMAGE ($BASE_IMAGE) and a LINUX_DISTRO ($LINUX_DISTRO), aborting build" return 2 fi else if [[ $BASE_IMAGE ]]; then echo validate from base image if ! distro=$(get_distro_from_image $BASE_IMAGE); then >&2 echo "ERROR: unable to get distro from BASE_IMAGE $BASE_IMAGE, can't validate" return 2 fi set_distro=true else echo "WARNING: neither LINUX_DISTRO nor BASE_IMAGE image specified" echo "Setting LINUX_DISTRO to default (alpine)" set_distro=true distro=alpine fi fi distros=$(echo $(echo "$(load_csv $BDIR/distros.csv)" | grep -Eo "^[^,]+") | sed "s/\s/|/g" | tr '[:upper:]' '[:lower:]') if [[ ! "$distros" == *"${distro}"* ]]; then >&2 echo "distro $distro is not a valid uci-docker-build distro ($distros)" return 1 fi [[ ! $BASE_NAME ]] && BASE_IMAGE=$(get_distro_core_image_name $distro) if [[ $set_distro ]] && [[ ! "$1" ]]; then LINUX_DISTRO=$distro; fi } # delete # get_base_image() { # [[ ! $BASE_IMAGE ]] && BASE_IMAGE=$(get_default_distro_image) # if [[ $BASE_IMAGE ]]; then # quiet echo determining DISTRO of base image: $BASE_IMAGE # if ! validate_image_distro $BASE_IMAGE; then # echo "unable to get or use base image: $BASE_IMAGE, aborting build" && return 5 # fi # quiet echo $BASE_IMAGE is built from distro $LINUX_DISTRO # else # echo unable to determine a base image, aborting build # return 6 # fi # } make_image_name () { local arch # echo making image name $@ # echo name: $NAME # echo APPEND: $APPEND_DISTRO # echo user: $RUSER # echo distro $LINUX_DISTRO if [[ $1 ]]; then # [[ $NAME ]] && echo changing image name from $NAME to $1 NAME=$1 fi if [[ $2 ]]; then # [[ $RUSER ]] && echo changing image username from $RUSER to $2 RUSER=$2 fi if [[ $NAME ]]; then [[ $APPEND_DISTRO ]] && NAME=$NAME-${LINUX_DISTRO} else # echo no image name supplied using distro name ${LINUX_DISTRO:-alpine} NAME=${LINUX_DISTRO:-alpine} fi [[ $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 # will determine if there are any minimal build source files/directories check_dir () { if [ $(ls $1/packages/*system.pkgs 2> /dev/null) ] || \ [ $(ls $1/packages/system/*.pkgs 2> /dev/null) ] || \ [ -f $1/packages/repositories.sh ] || \ [ -f $1/packages/packages.sh ] then _packages_=true return 0 fi [[ -f $1/init/init.sh ]] && return 0 [[ -d $1/rootfs ]] && return 0 return 1 } src=${1:-$BUILD_SRC} [[ $src == "_core_" ]] && return 0 src=${src:-src} unset BUILD_SRC if check_dir $src; then BUILD_SRC=$(realpath $src) echo $BUILD_SRC return 0 fi quiet echo build source path \'$src\' not initially found, echo looking quiet echo in \$PWD,\$PWD/src/,../\$PWD/src for valid build source quiet echo to avoid this search use BUILD_SRC= in an environment file spaths="$PWD $PWD/src $(dirname $PWD)/$src $(dirname $PWD)" for spath in $spaths; do quiet echo checking for source in: $spath if check_dir $spath; then quiet echo found valid source directory! BUILD_SRC=$spath echo $BUILD_SRC return 0 fi done quiet echo -e "\e[1;31mERROR: unable to find a build source directory as $src \e[1;37m" return 1 } build_info () { 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 if [[ $BUILD_SRC == "_core_" ]]; then echo building only core cat $BDIR/core/core.sh ls -la $BDIR/core else 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 ------\n" cat $BUILD_SRC/init/init.sh echo -e "\n----- end base init script init.sh ------" echo -e "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" fi fi echo -e "\e[1;37m**************BUILD PARAMETERS *******************************" echo "Architecture of this machine doing the building: $ARCH" if [[ "$BUILD_SRC" == "_core_" ]] ;then echo Building ONLY the UCI core else echo "Using scripts source directory at $BUILD_SRC" fi echo "Building with base image: $BASE_IMAGE"$([[ "${BASE_IMAGE}" == *":"* ]] || echo :latest) echo "Build logs can be found in directory $log_dir" echo "Linux Distro of Image: $LINUX_DISTRO" echo "Using build target: ${TARGET}" if [[ -f $APPEND_BUILD_ENV ]]; then echo "Will append contents of $APPEND_BUILD_ENV to build envionrment" echo "-------" cat $APPEND_BUILD_ENV echo "-------" fi 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" }