From f523998e395b0b3985354cf4bd8892e7292a8836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20B=C3=A9raud?= <adrien.beraud@savoirfairelinux.com> Date: Mon, 22 Jul 2019 16:07:55 -0400 Subject: [PATCH] Revert "cqfd: bump cqfd to version 5.1.0" This reverts commit 96221fb6c177a1cf7c480c74d51cd23029f2fd1c. Change-Id: I6217a0c3435d7e682105905c5edbcfc0d0f045d1 --- cqfd | 530 +++++++++++++++++++++++------------------------------------ 1 file changed, 207 insertions(+), 323 deletions(-) diff --git a/cqfd b/cqfd index a7a274dc..60e7c8d0 100755 --- a/cqfd +++ b/cqfd @@ -2,7 +2,7 @@ # # cqfd - a tool to wrap commands in controlled Docker containers # -# Copyright (C) 2015-2018 Savoir-faire Linux, Inc. +# Copyright (C) 2015-2016 Savoir-faire Linux, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,28 +20,22 @@ set -e PROGNAME=`basename $0` -VERSION=5.1.0 -dockerfile=".cqfd/docker/Dockerfile" +cqfd_dir=".cqfd" +dockerfile="$cqfd_dir/docker/Dockerfile" cqfdrc=".cqfdrc" -cqfd_user='builder' -cqfd_user_home='/home/builder' -cqfd_user_cwd="$cqfd_user_home/src" ## usage() - print usage on stdout usage() { - cat <<EOF + cat <<EOF Usage: $PROGNAME [OPTION ARGUMENT] [COMMAND] [ARGUMENTS] Options: - -f <file> Use file as config file (default .cqfdrc). + -f <file> Use file as config file (default .cqfdrc) + -d <dir> Use directory as cqfd directory (default .cqfd) -b <flavor_name> Target a specific build flavor. - -q Turn on quiet mode - -v or --version Show version. - -h or --help Show this help text. Commands: init Initialize project build container - flavors List flavors from config file to stdout run Run argument(s) inside build container release Run argument(s) and release software help Show this help text @@ -49,7 +43,7 @@ Commands: By default, run is assumed, and the run command is the one configured in .cqfdrc. - cqfd is Copyright (C) 2015-2018 Savoir-faire Linux, Inc. + cqfd is Copyright (C) 2015-2016 Savoir-faire Linux, Inc. This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the terms @@ -57,140 +51,116 @@ Commands: EOF } -# parse_ini_config_file() +# cfg_parser() - parse ini-style config files +# Will parse a ini-style config file, and evaluate it to a bash array. # Ref: http://theoldschooldevops.com/2008/02/09/bash-ini-parser/ # arg$1: path to ini file -parse_ini_config_file() { - # bash 4.3 and later break compatibility - local is_compatibility_mode=false - if [ $BASH_VERSINFO -ge 4 -a ${BASH_VERSINFO[1]} -gt 2 ]; then - is_compatibility_mode=true - shopt -s compat42 - fi - - if ! ini="$(<$1)"; then # read the file - die "$1: No such file!" - fi - ini="${ini//[/\\[}" # escape [ - ini="${ini//]/\\]}" # escape ] - IFS=$'\n' && ini=( ${ini} ) # convert to line-array - ini=( ${ini[*]//;*/} ) # remove comments with ; - ini=( ${ini[*]/\ =/=} ) # remove tabs before = - ini=( ${ini[*]/=\ /=} ) # remove tabs be = - ini=( ${ini[*]/\ =\ /=} ) # remove anything with a space around = - ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix - ini=( ${ini[*]/%\\]/ \(} ) # convert text2function (1) - ini=( ${ini[*]/%\(/ \( \)} ) # close array parenthesis - ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick - ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2) - ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis - ini[0]="" # remove first element - ini[${#ini[*]} + 1]='}' # add the last brace - if ! eval "$(echo "${ini[*]}")" 2>/dev/null; then # eval the result - die "$1: Invalid ini-file!" - fi - - # restore previous bash behaviour - if $is_compatibility_mode; then - shopt -u compat42 - fi +cfg_parser() { + # bash 4.3 and later break compatibility + if [ $BASH_VERSINFO -ge 4 -a ${BASH_VERSINFO[1]} -gt 2 ]; then + local compat=1 + shopt -s compat42 + fi + + if ! ini="$(<$1)"; then # read the file + die "$1: No such file!" + fi + ini="${ini//[/\[}" # escape [ + ini="${ini//]/\]}" # escape ] + IFS=$'\n' && ini=( ${ini} ) # convert to line-array + ini=( ${ini[*]//;*/} ) # remove comments with ; + ini=( ${ini[*]/\ =/=} ) # remove tabs before = + ini=( ${ini[*]/=\ /=} ) # remove tabs be = + ini=( ${ini[*]/\ =\ /=} ) # remove anything with a space around = + ini=( ${ini[*]/#\\[/\}$'\n'cfg.section.} ) # set section prefix + ini=( ${ini[*]/%\\]/ \(} ) # convert text2function (1) + ini=( ${ini[*]/=/=\( } ) # convert item to array + ini=( ${ini[*]/%/ \)} ) # close array parenthesis + ini=( ${ini[*]/%\\ \)/ \\} ) # the multiline trick + ini=( ${ini[*]/%\( \)/\(\) \{} ) # convert text2function (2) + ini=( ${ini[*]/%\} \)/\}} ) # remove extra parenthesis + ini[0]="" # remove first element + ini[${#ini[*]} + 1]='}' # add the last brace + if ! eval "$(echo "${ini[*]}")" 2>/dev/null; then # eval the result + die "$1: Invalid ini-file!" + fi + + # restore previous bash behaviour + [ "$compat" = "1" ] && shopt -u compat42 } ## die() - exit when an error occured # $@ messages and variables shown in the error message die() { - echo "cqfd: fatal: $@" 1>&2 - exit 1 + echo "cqfd: fatal: $@" 1>&2 + exit 1 } # docker_build() - Initialize build container docker_build() { - if [ ! -f $dockerfile ]; then - die "no Dockerfile found at location $dockerfile" - fi - if [ -z "$project_build_context" ]; then - docker build ${quiet:+-q} -t "$docker_img_name" "$(dirname "$dockerfile")" - else - docker build ${quiet:+-q} -t "$docker_img_name" "${project_build_context}" -f "$dockerfile" - fi + config_load + if [ ! -f $dockerfile ]; then + die "no Dockerfile found at location $dockerfile" + fi + docker build -q -t "$docker_img_name" `dirname $dockerfile` } # docker_run() - run command in configured container # A few implementation details: # # - The user executing the build commands inside the container is -# named after $cqfd_user, with the same uid/gid as your user to keep +# named 'builder', with the same uid/gid as your user to keep # filesystem permissions in sync. # -# - Your project's source directory is always mapped to $cqfd_user_cwd +# - Your project's source directory is always mapped to ~builder/src/ # -# - Your ~/.ssh directory is mapped to ~${cqfd_user}/.ssh to provide -# access to the ssh keys (your build may pull authenticated git -# repos for example). +# - Your ~/.ssh directory is mapped to ~builder/.ssh to provide access +# to the ssh keys (your build may pull authenticated git repos for +# example). # -# arg$1: the command string to execute as $cqfd_user +# arg$1: the command string to execute as builder # docker_run() { - local interactive_options - - if tty -s; then - interactive_options="-ti" - fi - - # If possible, map cqfd_user from the calling user's - if [ -n "$USER" ]; then - cqfd_user="$USER" - fi - - if [ -n "$HOME" ]; then - cqfd_user_home="$(cd $HOME; pwd)" - cqfd_user_cwd="$(pwd)" - fi - - # Display a warning message if using no more supported options - if [ -n "$CQFD_EXTRA_VOLUMES" ]; then - die 'Warning: CQFD_EXTRA_VOLUMES is no more supported, use - CQFD_EXTRA_RUN_ARGS="-v <local_dir>:<container_dir>"' - fi - if [ -n "$CQFD_EXTRA_HOSTS" ]; then - die 'Warning: CQFD_EXTRA_HOSTS is no more supported, use - CQFD_EXTRA_RUN_ARGS="--add-host <hostname>:<IP_address>"' - fi - if [ -n "$CQFD_EXTRA_ENV" ]; then - die 'Warning: CQFD_EXTRA_ENV is no more supported, use - CQFD_EXTRA_RUN_ARGS="-e <var_name>=<value>"' - fi - if [ -n "$CQFD_EXTRA_PORTS" ]; then - die 'Warning: CQFD_EXTRA_PORTS is no more supported, use - CQFD_EXTRA_RUN_ARGS="-p <host_port>:<docker_port>"' - fi - - # The user may set the CQFD_EXTRA_RUN_ARGS environment variables - # to pass custom run arguments to his development container. - - # Set HOME variable for the $cqfd_user, except if it was - # explicitely set via CQFD_EXTRA_RUN_ARGS - local home_env_var="-e HOME=$cqfd_user_home" - if echo "$CQFD_EXTRA_RUN_ARGS" | egrep -q "(-e[[:blank:]]*|--env[[:blank:]]+)HOME="; then - home_env_var="" - fi - - tmp_launcher=$(make_launcher) - - docker run --privileged \ - $CQFD_EXTRA_RUN_ARGS \ - --rm \ - --log-driver=none \ - -v $tmp_launcher:/bin/cqfd_launch \ - -v ~/.ssh:$cqfd_user_home/.ssh \ - -v "$PWD":$cqfd_user_cwd \ - $home_env_var \ - $interactive_options \ - ${SSH_AUTH_SOCK:+ -v $SSH_AUTH_SOCK:$cqfd_user_home/.sockets/ssh} \ - ${SSH_AUTH_SOCK:+ -e SSH_AUTH_SOCK=$cqfd_user_home/.sockets/ssh} \ - $docker_img_name cqfd_launch "$@" 2>&1 - - rm -f $tmp_launcher + [ -z "$JENKINS_URL" ] && local nojenkins=1 + + # The user may set the CQFD_EXTRA_VOLUMES environment variable + # to map custom volumes inside his development container. + if [ -n "$CQFD_EXTRA_VOLUMES" ]; then + local map extravol + for map in $CQFD_EXTRA_VOLUMES; do + extravol+="-v $map " + done + fi + + if [ -n "$CQFD_EXTRA_HOSTS" ]; then + local map extrahosts + for map in $CQFD_EXTRA_HOSTS; do + extrahosts+="--add-host $map " + done + fi + + # CQFD_EXTRA_ENV is a space-separated list like VAR=value + if [ -n "$CQFD_EXTRA_ENV" ]; then + local map extraenv + for map in $CQFD_EXTRA_ENV; do + extraenv+="-e $map " + done + fi + + docker run --privileged -v "$PWD":/home/builder/src \ + -v ~/.ssh:/home/builder/.ssh \ + --rm \ + $extravol \ + $extrahosts \ + $extraenv \ + ${nojenkins:+ -ti} \ + ${SSH_AUTH_SOCK:+ -v $SSH_AUTH_SOCK:/home/builder/.sockets/ssh} \ + ${SSH_AUTH_SOCK:+ -e SSH_AUTH_SOCK=/home/builder/.sockets/ssh} \ + $docker_img_name \ + /bin/bash -c "groupadd -og $GROUPS -f builders && \ + useradd -s /bin/bash -u $UID -g $GROUPS builder && \ + chown $UID:$GROUPS /home/builder && \ + su builder -p -c \"cd ~builder/src/ && $1\" 2>&1" } # make_archive(): Create a release package. @@ -199,221 +169,135 @@ docker_run() { # include two files with the same name in the list of files to # archive. make_archive() { - if [ -z "$release_files" ]; then - die "No files to archive, check files in $cqfdrc" - fi - - for file in $release_files; do - if [ ! -e $file ]; then - die "Cannot release: can't find $file" - fi - done - - # template the generated archive's filename - local git_short=`git rev-parse --short HEAD 2>/dev/null` - local git_long=`git rev-parse HEAD 2>/dev/null` - local date_rfc3339=`date --rfc-3339='date'` - - # default name for the archive if not set - if [ -z "$release_archive" ]; then - release_archive="%Po-%Pn.tar.xz" - fi - - release_archive=`echo $release_archive | - sed -e 's!%%!%!g; - s!%Gh!'$git_short'!g; - s!%GH!'$git_long'!g; - s!%D3!'$date_rfc3339'!g; - s!%Po!'$project_org'!g; - s!%Pn!'$project_name'!g; - s!%Cf!'$flavor'!g;'` - - # also replace variable names - beware with eval - eval release_archive=`echo $release_archive` - - # setting tar_transform=yes will move files to the root of a tar archive - if [ "$release_transform" = "yes" ]; then - local tar_opts='--transform s/.*\///g' - fi - - # support the following archive formats - case "$release_archive" in - *.tar.xz) - XZ_OPT=-9 tar $tar_opts -cJf \ - "$release_archive" $release_files - ;; - *.tar.gz) - tar $tar_opts -czf \ - "$release_archive" $release_files - ;; - *.zip) - zip -q -9 -r "$release_archive" $release_files - ;; - *) - ;; - esac -} - -# make_launcher - generate in-container launcher script -# return: the path to the launcher script on stdout -make_launcher() -{ - local tmpfile=$(mktemp /tmp/tmp.XXXXXX) - - chmod 0755 $tmpfile - cat >$tmpfile <<EOF -#!/bin/sh -# create container user to match expected environment - -die () { - echo "error: \$*" - exit 1 -} - -test_cmd () { - command -v "\$1" > /dev/null 2>&1 -} - -debug () { - test -n "\$CQFD_DEBUG" && echo "debug: \$*" -} - -# Check container requirements -test -x /bin/bash || { failed=1 && echo "error: /bin/bash does not exist or is not executable"; } -test_cmd groupadd || { failed=1 && echo "error: Missing command: groupadd"; } -test_cmd useradd || { failed=1 && echo "error: Missing command: useradd"; } -test_cmd chown || { failed=1 && echo "error: Missing command: chown"; } -test_cmd sudo && has_sudo=1 || test_cmd su || - { failed=1 && echo "error: Missing command: su or sudo"; } -test -n "\$failed" && - die "Some dependencies are missing from the container, see above messages." - -# Add the host's user and group to the container, and adjust ownership. -groupadd -og $GROUPS -f builders || die "groupadd command failed." -useradd -s /bin/bash -ou $UID -g $GROUPS -d "$cqfd_user_home" $cqfd_user \ - || die "useradd command failed." -chown $UID:$GROUPS $cqfd_user_home || die "chown command failed." - -# run the provided command in the working directory -cd $cqfd_user_cwd || die "Changing directory to \"$cqfd_user_cwd\" failed." -if [ -n "\$has_sudo" ]; then - # Use sudo to provide a controlling TTY for the executed command - debug "Using \"sudo\" to execute command \"\$@\" as user \"$cqfd_user\"" - sudo -E -u $cqfd_user sh -c "\$@" -else - debug "Using \"su\" to execute command \"\$@\" as user \"$cqfd_user\"" - su $cqfd_user -p -c "\$@" -fi -EOF - echo $tmpfile + if [ -z "$release_files" ]; then + die "No files to archive, check files in $cqfdrc" + fi + + for file in $release_files; do + [ -f $file ] || die "Cannot create release: missing $file" + done + + # template the generated archive's filename + local git_short=`git rev-parse --short HEAD 2>/dev/null` + local git_long=`git rev-parse HEAD 2>/dev/null` + local date_rfc3339=`date --rfc-3339='date'` + + # default name for the archive if not set + [ -z "$release_archive" ] && release_archive="%Po-%Pn.tar.xz" + + release_archive=`echo $release_archive | + sed -e 's!%%!%!g; + s!%Gh!'$git_short'!g; + s!%GH!'$git_long'!g; + s!%D3!'$date_rfc3339'!g; + s!%Po!'$project_org'!g; + s!%Pn!'$project_name'!g; + s!%Cf!'$flavor'!g;'` + + # also replace variable names - beware with eval + eval release_archive=`echo $release_archive` + + XZ_OPT=-9 tar --transform "s/.*\///g" -cJf \ + "$release_archive" $release_files } # config_load() - load build settings from cqfdrc # $1: optional "flavor" of the build, is a suffix of command. config_load() { - IFS="$IFS" parse_ini_config_file "$cqfdrc" - - cfg.section.project # load the [project] section - project_org="$org" - project_name="$name" - project_build_context="$build_context" - - cfg.section.build # load the [build] section - - # build parameters may be overriden by a flavor defined in the - # build section's 'flavors' parameter. - local flavor="$1" - if [ -n "$flavor" ]; then - if grep -qw "$flavor" <<< "$flavors"; then - cfg.section."$flavor" # load the [$flavor] section - else - die "flavor \"$flavor\" not found in flavors list" - fi - fi - - build_cmd="$command" - release_files="`eval echo $files`" - release_archive="$archive" - release_transform="$tar_transform" - - # This will look like fooinc_reponame - if [ -n "$project_org" -a -n "$project_name" ]; then - docker_img_name="cqfd_${project_org}_${project_name}" - else - die "project.org and project.name not configured" - fi - - # Adapt things for a specific container - if [ -n "$distro" ]; then - dockerfile=".cqfd/$distro/Dockerfile" - docker_img_name+="_$distro" - fi + local p_flavor="$1" + + IFS="$IFS" cfg_parser "$cqfdrc" + + cfg.section.project # load the [project] section + project_org="$org" + project_name="$name" + + cfg.section.build # load the [build] section + + # build parameters may be overriden by a flavor defined in the + # build section's 'flavors' parameter. + if [ -n "$p_flavor" ]; then + for flavor in $flavors; do + if [ "$flavor" = "$p_flavor" ]; then + local _found=1 + break + fi + done + + if [ -n "$_found" ]; then + cfg.section."$p_flavor" # load the [$p_flavor] section + else + die "flavor \"$p_flavor\" not found in flavors list" + fi + fi + + build_cmd="$command" + if [ -n "$distro" ]; then + dockerfile="$cqfd_dir/$distro/Dockerfile" + fi + + release_files="`eval echo $files`" + release_archive="$archive" + + # This will look like fooinc_reponame + if [ -n "$project_org" -a -n "$project_name" ]; then + docker_img_name="${project_org}_${project_name}" + else + die "project.org and project.name not configured" + fi } -has_to_release=false while [ $# -gt 0 ]; do - case "$1" in - help|-h|"--help") - usage - exit 0 - ;; - version|-v|"--version") - echo $VERSION - exit 0 - ;; - init) - config_load $flavor - docker_build - exit $? - ;; - flavors) - config_load - echo $flavors - exit 0 - ;; - -b) - shift - flavor="$1" - ;; - -f) - shift - cqfdrc="$1" - ;; - -q) - quiet=true - ;; - run|release) - if [ "$1" = "release" ]; then - has_to_release=true - fi - if [ $# -gt 1 ]; then - shift - build_cmd_alt="$@" - fi - break - ;; - ?*) - echo "Unknown command: $1" - usage - exit 1 - ;; - *) - # empty or no argument case - ;; - esac - shift + case "$1" in + help|-h|"--help") + usage + exit 0 + ;; + init) + docker_build + exit $? + ;; + -b) + shift + flavor="$1" + ;; + -f) + shift + cqfdrc="$1" + ;; + -d) + shift + cqfd_dir="$1" + dockerfile="$cqfd_dir/docker/Dockerfile" + ;; + run|release) + [ "$1" = "release" ] && make_archive=1 + if [ $# -gt 1 ]; then + shift + build_cmd_alt="$@" + fi + break + ;; + ?*) + die "Unknown command: $1" + ;; + *) + # empty or no argument case + ;; + esac + shift done config_load $flavor if [ -n "$build_cmd_alt" ]; then - build_cmd=$build_cmd_alt + build_cmd=$build_cmd_alt elif [ -z "$build_cmd" ]; then - die "No build.command defined in $cqfdrc !" + die "No build.command defined in $cqfdrc !" fi docker_run "$build_cmd" -if $has_to_release; then - make_archive +if [ "$make_archive" = "1" ]; then + make_archive fi -- GitLab