#!/bin/bash ### do not use '-e' ### @accetto, September 2022 ### updated: July 2023 ### depends on the script 'builder.sh' ### set the environment variables first, e.g. 'source .secrets' ### usage: './ ', where =[all|all-no-push] die() { local message="${1:-(unknown)}" local -i code=${2:-1} local place="${3:-$0}" echo -e "\nEXITING '${place}' with code ${code}: ${message}\n" >&2 exit ${code} } clear_log() { ### just for debugging # cp -f "${_ci_builder_log}" "${_ci_builder_log}_copy" > "${_ci_builder_log}" echo -e "\n==> EXECUTING @$(date -u +'%Y-%m-%d_%H-%M-%S'): ${0} $@\n" } execute_smart() { if [[ -z "${_option_logall}" ]] ; then _flag_skip_footer="1" exec 1>/dev/null { ### execute without logging $@ } >&3 else ### execute with logging $@ fi } show_error() { ### don't output to 'stderr' (>&2) here! echo -e "\nERROR: ${@:-(unknown)}\n" } show_log_digest() { echo -e "\n--> Log digest:\n" grep -Po "${_regex_log_digest}" "${_ci_builder_log}" | sort -u echo } show_log_stickers() { echo -e "\n--> Version stickers:\n" grep "${_regex_log_stickers}" "${_ci_builder_log}" | sort -u echo } show_log_timing() { echo -e "\n--> Building timing:\n" grep -P "${_regex_log_timing}" "${_ci_builder_log}" echo } show_log_errors() { echo -e "\n--> Building errors:\n" grep -iPn "${_regex_log_errors}" "${_ci_builder_log}" echo } show_unlogged_help() { # help is never logged exec 1>&- { cat < []... ${0} [] group []... ${0} [] family []... ${0} [--log-all] log get (digest|stickers|timing|errors) := (--log-all|--no-cache) := (all|all-no-push) := (group|family) := pivotal |(complete[-latest|-chromium|-firefox]) |(latest[-chromium|-firefox]) := (complete)|(latest[-chromium|-firefox]) := depends on context, e.g. '-ver1|-ver2' (currently none supported) Group mode : All images are processed independently. Family mode: The children are skipped if a new parent image was not actually built. Remark: Currently are both modes equivalent, because there are no child suffixes supported. The command and the blend are passed to the builder script. The result "" must be a blend supported by the builder script. The script creates a complete execution log. EOT } >&3 # Examples of family mode: # Build and publish all blends, not using the Docker builder cache: # ${0} --no-cache all family complete # Build the 'latest' and 'latest-ver2' blends, but skip the publishing: # ${0} all-no-push family latest -ver2 # Build and publish only the 'latest-firefox' and 'latest-firefox-ver2' blends: # ${0} all family latest-firefox -ver2 # Build and publish only the 'latest' and 'latest-firefox-ver2' blends: # ${0} all family latest -firefox-ver2 # Examples of group mode: # Build and publish all blends: # ${0} all group complete # Build the 'latest' and 'latest-ver2' blends, but skip the publishing: # ${0} all-no-push group latest latest-ver2 # Build and publish the images containing Firefox: # ${0} all group complete-firefox # Other examples: # Build only the 'latest' blend and skip the publishing: # ${0} all-no-push family latest # ${0} all-no-push group latest # Display log digest: # ${0} log get digest # Display version stickers of newly built images: # ${0} log get stickers # Display building timing: # ${0} log get timing # Display building errors: # ${0} log get errors } build_single_image() { local command="${1?Expected command}" local blend="${2?Expected blend}" local option_nocache="${3}" local -i exit_code=0 echo -e "${_log_mark} Building image '${_builder_project}:${blend}'" ### call builder script ./"${_builder_script}" "${blend}" "${command}" "${option_nocache}" exit_code=$? if [[ ${exit_code} -ne 0 ]] ; then die "Script '${_builder_script}' failed with code ${exit_code}." ${exit_code} ; fi if [[ $(tail "${_builder_log}" | grep -c "==> No build needed for '${blend}'") -eq 1 ]] ; then echo -e "${_log_mark} No build needed for '${_builder_project}:${blend}'." else case "${command}" in all-no-push ) if [[ $(tail "${_builder_log}" | grep -c "==> Built '${blend}'") -eq 1 ]] ; then echo -e "${_log_mark} Built new '${_builder_project}:${blend}'." else echo -e "${_log_mark} Failed to build new '${_builder_project}:${blend}'." fi ;; all ) if [[ $(tail "${_builder_log}" | grep -c "==> Published '${blend}'") -eq 1 ]] ; then echo -e "${_log_mark} Published new '${_builder_project}:${blend}'." else echo -e "${_log_mark} Failed to publish new '${_builder_project}:${blend}'." fi ;; * ) die "Unknown command: '${command}'" ;; esac fi } build_family() { local command="${1?Expected command}" local parent="${2?Expected parent blend}" if [[ $# -ge 2 ]] ; then shift 2 ; fi ### note that the option '--no-cache' is passed in by the parent image build_single_image "${command}" "${parent}" "${_option_nocache}" ### if the parent probe succeeded then probe the children if [[ $(tail "${_builder_log}" | grep -cE "==> (Published|Built) '${parent}'") -eq 1 ]] ; then for child in $@ ; do # note that we do not pass in the option '--no-cache' by children build_single_image "${command}" "${parent}${child}" done fi } build_group() { local command="${1?Expected command}" if [[ $# -gt 0 ]] ; then shift ; fi for blend in $@ ; do # note that the option '--no-cache' is passed in by each image build_single_image "${command}" ${blend} "${_option_nocache}" done } main() { if [[ $# -eq 0 ]] ; then show_unlogged_help return 0 fi while [[ $# -gt 0 && "${1}" =~ "--" ]] ; do case "${1}" in --no-cache ) _option_nocache="${1}" ;; --log-all ) _option_logall="${1}" ;; *) execute_smart show_error "Unknown option '${1}'" return 1 ;; esac shift done local command="${1}" local mode="${2}" local subject="${3}" if [[ $# -ge 3 ]] ; then shift 3 ; fi case "${command}" in help | --help | -h ) show_unlogged_help ;; log ) case "${mode}" in get ) case "${subject}" in digest ) execute_smart show_log_digest ;; stickers ) execute_smart show_log_stickers ;; timing ) execute_smart show_log_timing ;; errors ) execute_smart show_log_errors ;; * ) execute_smart show_error "Unknown 'log get' command argument '${subject}'" ;; esac ;; * ) execute_smart show_error "Unknown 'log' command '${mode}'" ;; esac ;; all-no-push | all ) case "${mode}" in group ) case "${subject}" in pivotal ) clear_log build_group "${command}" "latest" "latest-firefox" "latest-chromium" ;; complete | complete-latest ) clear_log build_group "${command}" "latest" "latest-firefox" "latest-chromium" ;; complete-chromium ) clear_log build_group "${command}" "latest-chromium" ;; complete-firefox ) clear_log build_group "${command}" "latest-firefox" ;; latest | latest-chromium | latest-firefox ) clear_log build_group "${command}" "${subject}" $@ ;; * ) execute_smart show_error "Unknown blend '${subject}'" ;; esac ;; family ) case "${subject}" in complete ) clear_log build_family "${command}" "latest" build_family "${command}" "latest-firefox" build_family "${command}" "latest-chromium" ;; latest | latest-chromium | latest-firefox ) clear_log build_family "${command}" "${subject}" $@ ;; * ) execute_smart show_error "Unknown parent blend '${subject}'" ;; esac ;; * ) execute_smart show_error "Unknown mode '${mode}'" ;; esac ;; *) execute_smart show_error "Unknown command '${command}'" ;; esac if [[ -z "${_flag_skip_footer}" ]] ; then echo -e "\n==> FINISHED @$(date -u +'%Y-%m-%d_%H-%M-%S'): ${0} $@\n" fi } declare _builder_project="${BUILDER_REPO:-headless-debian-g3}" declare _builder_log="scrap_builder.log" declare _builder_script="builder.sh" declare _ci_builder_log="scrap_ci-builder.log" declare _log_mark="\n[CI-BUILDER]" declare _regex_log_digest="(?<=\[CI-BUILDER\] ).+" declare _regex_log_stickers="Current version sticker of " declare _regex_log_timing="==> (EXECUTING @.*builder\.sh.*|FINISHED @.*builder\.sh.*)" declare _regex_log_errors="\berror\b" declare _flag_skip_footer="" declare _option_nocache="" declare _option_logall="" ### duplicate 'stdout' so we can close it when needed exec 3>&1 ### main entry point declare -i __exit_code=0 main $@ 2>&1 | tee -a "${_ci_builder_log}" __exit_code=${PIPESTATUS[0]} exit ${__exit_code}