#!/usr/bin/env bash # # Create a new docker file and build docker according to the options. # # Author: donkey # Stop script on NZEC set -e # Stop script if unbound variable found (use ${var:-} if intentional) set -u # By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success # This is causing it to fail set -o pipefail # standard output may be used as a return value in the functions # we need a way to write text on the screen in the functions so that # it won't interfere with the return value. # Exposing stream 3 as a pipe to standard output of the script itself exec 3>&1 function errtrap() { error "[EXCEPTION:$1] Error: Command or function exited with status $?" } # Catch the exception if command -v "trap" >/dev/null 2>&1; then # Get the exception information when program was breaked trap 'errtrap $LINENO' ERR fi # Prepare the color variables for your terminal if test -t 1; then # if terminal ncolors=$(which tput > /dev/null && tput colors) # supports color if test -n "$ncolors" && test $ncolors -ge 8; then termcols=$(tput cols) bold="$(tput bold)" underline="$(tput smul)" standout="$(tput smso)" normal="$(tput sgr0)" black="$(tput setaf 0)" red="$(tput setaf 1)" green="$(tput setaf 2)" yellow="$(tput setaf 3)" blue="$(tput setaf 4)" magenta="$(tput setaf 5)" cyan="$(tput setaf 6)" white="$(tput setaf 7)" fi fi readonly VERBOSE=${VERBOSE:-false} readonly CUR_DIR=$(cd `dirname $0`; pwd) readonly PROJ_DIR=`dirname $CUR_DIR` readonly DOCKER_DIR="$PROJ_DIR/docker" readonly DEFAULT_LTS_VERSION="22.04" declare -A readonly UBUNTU_LTS_DICT=(["22.04"]="jammy" ["20.04"]="focal" ["18.04"]="bionic") ### Helper log functions ### function error() { if [ "$VERBOSE" = true ]; then printf "%b" "[${yellow}$(date +'%Y-%m-%dT%H:%M:%S%z')${normal}]" >&2 fi printf "%b\n" "[ ${red}ERROR${normal} ]: $@" >&2 } function warning() { if [ "$VERBOSE" = true ]; then printf "%b" "[${yellow}$(date +'%Y-%m-%dT%H:%M:%S%z')${normal}]" >&2 fi printf "%b\n" "[${yellow}WARNING${normal}]: $@" >&3 } function info() { if [ "$VERBOSE" = true ]; then printf "%b" "[${yellow}$(date +'%Y-%m-%dT%H:%M:%S%z')${normal}]" >&2 fi printf "%b\n" "[ ${cyan}INFOM${normal} ]: $@" >&3 } function debug() { if [ "$VERBOSE" = true ]; then printf "%b" "[${yellow}$(date +'%Y-%m-%dT%H:%M:%S%z')${normal}]" >&2 fi printf "%b\n" "[ ${magenta}DEBUG${normal} ]: $@" >&3 } function verbose() { if [ "$VERBOSE" = true ]; then printf "%b\n" "[${yellow}$(date +'%Y-%m-%dT%H:%M:%S%z')${normal}][${green}VERBOSE${normal}]: $@" >&3 fi } ####################################### # Join array by a char # Globals: # None # Arguments: # $1 -- Delimiter char # $2 -- Element array # Returns: # Spliced string # Eamples: # join_by_char , 1 2 3 -> 1,2,3 # eles=(1 2 3); join_by_char '|' "${eles[@]}" -> 1,2,3 ####################################### function join_by_char() { local IFS="$1" shift echo "$*" } ####################################### # Check element is in a set or not # Globals: # None # Arguments: # $1 -- String whould be checked # $2 -- Element array # Examples: # arr=("Linux" "WebGL" "Windows") # if ele_in "Linux" "${arr[@]}"; then; echo "in"; fi ####################################### function ele_in() { local _PATTERN="$1" shift local _SS=`join_by_char '|' "$@"` if [[ "$_PATTERN" =~ ^(${_SS})$ ]]; then return 0 fi return 1 } ####################################### # Check the key exists in dict or not # Globals: # None # Arguments: # $1 -- String whould be checked # $2 -- A helper string, 'in' # $3 -- Element array # Examples: # declare -A dict=(["Linux"]="linux" ["Darwin"]="macos" ["Windows"]="windows") # if ! key_exists "FreeBSD" in dict; then echo "No such key"; fi ####################################### function key_exists() { if [ "$2" != in ]; then error "Incorrect usage." error "Correct usage: key_exists {key} in {array}" return fi eval '[ ${'$3'[$1]+exists} ]' } function usage() { printf "%b\n" "Generate ${green}Dockerfile${normal} and do ${cyan}docker build${normal} for a specific ${magenta}Ubuntu LTS${normal} image as the ${blue}GitLab Runner${normal}" >&3 printf "%b\n" " ${green}-t, --target${normal} The target Ubuntu LTS version, default: ${yellow}${DEFAULT_LTS_VERSION}${normal}." >&3 printf "%b\n" " ${green}-n, --name${normal} The tag name of the image, default: ${yellow}builder-ubuntu-$(uname -m):${DEFAULT_LTS_VERSION}${normal}." >&3 printf "%b\n" " ${green}-a, --arch${normal} The architecture for cross compiling: ${yellow}$(uname -m)${normal}." >&3 } function main() { local _ARCH=`uname -m` local _CODE=$DEFAULT_LTS_VERSION local _NAME="" # Parse the options while [ $# -gt 0 ]; do case $1 in -t | --target ) shift if [ $# -gt 0 ]; then _CODE=$1 fi ;; -n | --name ) shift if [ $# -gt 0 ]; then _NAME=$1 fi ;; -a | --arch ) shift if [ $# -gt 0 ]; then _ARCH=$1 fi ;; -h | --help ) usage exit ;; * ) warning "Unknown options: $1" ;; esac shift done if ! key_exists "$_CODE" in UBUNTU_LTS_DICT; then error "The version ${red}${_CODE}${normal} is not supported." exit -1 fi if [ -z "$_NAME" ]; then _NAME="builder-ubuntu-${_ARCH}:${_CODE}" fi local _DF_NAME="Dockerfile.ubuntu.${_ARCH}.${_CODE}" info "Generating ${green}${_DF_NAME}${normal} ..." cp $DOCKER_DIR/Dockerfile.ubuntu.template $CUR_DIR/${_DF_NAME} sed -i'.bak' "s|__UBUNTU_TAG__|${_CODE}|g" $CUR_DIR/${_DF_NAME} sed -i'.bak' "s|__UBUNTU_BRANCH_NAME__|${UBUNTU_LTS_DICT[$_CODE]}|g" $CUR_DIR/${_DF_NAME} # Cross compile env if [ ${_ARCH} == "$(uname -m)" ]; then sed -i'.bak' "s|__UBUNTU_CROSS__||g" $CUR_DIR/${_DF_NAME} else local _EMBEDDED_REPO="deb [arch=${_ARCH}] \${EMBEDDED_BASE_URL} \${BRANCH} \${CATATORIES}\\\\ndeb [arch=${_ARCH}] \${EMBEDDED_BASE_URL} \${BRANCH}-updates \${CATATORIES}\\\\ndeb [arch=${_ARCH}] \${EMBEDDED_BASE_URL} \${BRANCH}-backports \${CATATORIES}\\\\ndeb [arch=${_ARCH}] \${EMBEDDED_BASE_URL} \${BRANCH}-security \${CATATORIES}" sed -i'.bak' "s|__UBUNTU_CROSS__|${_EMBEDDED_REPO}|g" $CUR_DIR/${_DF_NAME} fi if [ -f "$CUR_DIR/${_DF_NAME}.bak" ]; then rm -rf "$CUR_DIR/${_DF_NAME}.bak" fi info "Building ${green}${_NAME}${normal} ..." docker build -f $CUR_DIR/${_DF_NAME} -t ${_NAME} . } main $@