run.sh 37.3 KB
Newer Older
1
#!/bin/bash -e
2
# citbx4gitlab: CI toolbox for Gitlab
Emeric Verschuur's avatar
Emeric Verschuur committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright (C) 2017 ERCOM - Emeric Verschuur <emeric@mbedsys.org>
# 
# 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
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

18
CITBX_VERSION=3.3.1
19

20 21 22 23 24
############################################################
#                  ### COMMON PART ###
#  All environments: Gitlab-CI runner & local workstation
############################################################

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
# display a message
print_log() {
    local level=$1;
    shift || print_log C "Usage print_log <level> message"
    case "${level,,}" in
        c|critical)
            >&2 printf "\e[91m[CRIT] %s\e[0m\n" "$@"
            exit 1
            ;;
        e|error)
            >&2 printf "\e[91m[ERRO] %s\e[0m\n" "$@"
            ;;
        w|warning)
            >&2 printf "\e[93m[WARN] %s\e[0m\n" "$@"
            ;;
        n|note)
            printf "[NOTE] %s\n" "$@"
            ;;
        i|info)
            printf "\e[92m[INFO] %s\e[0m\n" "$@"
            ;;
        *)
            print_log C "Invalid log level: $level"
            ;;
    esac
}

Emeric Verschuur's avatar
Emeric Verschuur committed
52 53
# Print an error message and exit with error status 1
print_critical() {
54
    >&2 printf "\e[91m[CRIT] %s\e[0m\n" "$@"
Emeric Verschuur's avatar
Emeric Verschuur committed
55 56 57 58 59
    exit 1
}

# Print an error message
print_error() {
60
    >&2 printf "\e[91m[ERRO] %s\e[0m\n" "$@"
Emeric Verschuur's avatar
Emeric Verschuur committed
61 62 63 64
}

# Print a warning message
print_warning() {
65
    >&2 printf "\e[93m[WARN] %s\e[0m\n" "$@"
Emeric Verschuur's avatar
Emeric Verschuur committed
66 67 68 69 70 71 72 73 74 75 76 77
}

# Print a note message
print_note() {
    printf "[NOTE] %s\n" "$@"
}

# Pring an info message
print_info() {
    printf "\e[92m[INFO] %s\e[0m\n" "$@"
}

Emeric Verschuur's avatar
Emeric Verschuur committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91
# Get the real core number
ncore() {
    lscpu | awk -F ':' '
        /^Core\(s\) per socket/ {
            nc=$2;
        }
        /^Socket\(s\)/ {
            ns=$2;
        }
        END {
            print nc*ns;
        }'
}

Emeric Verschuur's avatar
Emeric Verschuur committed
92
# Check bash
93
if [ ! "${BASH_VERSINFO[0]}" -ge 4 ]; then
94
    print_critical "This script needs BASH version 4 or greater"
Emeric Verschuur's avatar
Emeric Verschuur committed
95 96 97 98 99
fi

# ci-tools base directory bath
CITBX_ABS_DIR=$(dirname $(readlink -f $0))
# Extract project specific values
100 101
if [ -f $CITBX_ABS_DIR/citbx.properties ]; then
    . $CITBX_ABS_DIR/citbx.properties
Emeric Verschuur's avatar
Emeric Verschuur committed
102 103 104
fi
if [ -z "$CI_PROJECT_DIR" ]; then
    # Find the project directory
105 106 107 108
    CI_PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || true)"
    if [ -z "$CI_PROJECT_DIR" ]; then
        print_critical "Unable to find the project root directory"
    fi
Emeric Verschuur's avatar
Emeric Verschuur committed
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
fi
# Current job script relative directory path
CITBX_DIR=${CITBX_ABS_DIR#${CI_PROJECT_DIR}/}

citbx_register_handler() {
    local list="citbx_job_stage_${2}"
    local func="${1}_${2}"
    if [[ "$(type -t $func)" != "function" ]]; then
        return 0
    fi
    local pattern='\b'"$func"'\b'
    if [[ "${!list}" =~ $pattern ]]; then
        return 0
    fi
    case "$2" in
        define|setup|before|main)
            eval "${list}=\"${!list} $func\""
            ;;
        after)
            eval "${list}=\"$func ${!list}\""
            ;;
        *)
            print_critical "Use: citbx_register_handler <prefix> define|setup|before|main|after"
            ;;
    esac
}

declare -A CITBX_USE_LIST
# Add module in the use list
citbx_use() {
    local module=$1
    if [ -z "$module" ]; then
        print_critical "Usage: citbx_use <module_name>"
    fi
    if [ "${CITBX_USE_LIST[$module]}" == "true" ]; then
        return 0
    fi
    if [ ! -f "$CITBX_ABS_DIR/modules/${module}.sh" ]; then
        print_critical "Module ${module} not found!"
    fi
    . $CITBX_ABS_DIR/modules/${module}.sh
    CITBX_USE_LIST[$module]="true"
    for h in $module_handler_list; do
        citbx_register_handler "citbx_module_${module}" $h
    done
}

156 157 158 159 160 161
citbx_local() {
    if [ -f $CITBX_ABS_DIR/citbx.local ]; then
        . $CITBX_ABS_DIR/citbx.local
    fi
}

Emeric Verschuur's avatar
Emeric Verschuur committed
162 163 164
# Job end handler
citbx_job_finish() {
    local CITBX_EXIT_CODE=$?
165 166 167 168 169
    if [ "$CITBX_JOB_FINISH_CALLED" != "true" ]; then
        CITBX_JOB_FINISH_CALLED="true"
    else
        return 0
    fi
Emeric Verschuur's avatar
Emeric Verschuur committed
170
    for hook in $citbx_job_stage_after; do
171
        $citbx_before_script
172
        cd $CI_PROJECT_DIR
Emeric Verschuur's avatar
Emeric Verschuur committed
173
        $hook $CITBX_EXIT_CODE
174
        $citbx_after_script
Emeric Verschuur's avatar
Emeric Verschuur committed
175 176 177 178 179 180
    done
    if [ "$CITBX_EXIT_CODE" == "0" ]; then
        print_info "CI job success!"
    else
        print_error "CI job failure with exit code $CITBX_EXIT_CODE"
    fi
181
    print_note "Job execution time: $(date +"%H hour(s) %M minute(s) and %S second(s)" -ud @$(($(date +%s) - $CITBX_JOB_START_TIME)))"
Emeric Verschuur's avatar
Emeric Verschuur committed
182 183
}

184 185 186 187
############################################################
#               ### GITLAB-RUNNER PART ###
# If running inside the suitable docker / on gitlab runner
############################################################
Emeric Verschuur's avatar
Emeric Verschuur committed
188 189
if [ "$GITLAB_CI" == "true" ]; then
    # Load job
190
    citbx_local
191
    CITBX_JOB_RUN_FILE_NAME=${CITBX_JOB_RUN_FILE_NAME:-"$CI_JOB_NAME.sh"}
Emeric Verschuur's avatar
Emeric Verschuur committed
192
    module_handler_list="before after"
193 194 195
    CITBX_JOB_RUN_FILE_PATH="$CITBX_ABS_DIR/run.d/${CITBX_JOB_RUN_FILE_NAME}"
    if [ ! -f "$CITBX_JOB_RUN_FILE_PATH" ]; then
        print_critical "Job definition file $CITBX_JOB_RUN_FILE_PATH not found"
Emeric Verschuur's avatar
Emeric Verschuur committed
196
    fi
197
    . "$CITBX_JOB_RUN_FILE_PATH"
198 199
    citbx_register_handler "job" "main"
    citbx_register_handler "job" "after"
200 201 202 203 204 205 206
    if [ "$CITBX_DEBUG_SCRIPT_ENABLED" == "true" ]; then
        citbx_before_script="set -x"
        citbx_after_script="set +x"
    else
        citbx_before_script=""
        citbx_after_script=""
    fi
Emeric Verschuur's avatar
Emeric Verschuur committed
207
    for hook in $citbx_job_stage_before; do
208
        $citbx_before_script
209
        cd $CI_PROJECT_DIR
Emeric Verschuur's avatar
Emeric Verschuur committed
210
        $hook
211
        $citbx_after_script
Emeric Verschuur's avatar
Emeric Verschuur committed
212
    done
213
    CITBX_JOB_START_TIME=$(date +%s)
214
    trap citbx_job_finish EXIT SIGINT SIGTERM
Emeric Verschuur's avatar
Emeric Verschuur committed
215
    print_info "CI job begin"
216
    if [ -z "$citbx_job_stage_main" ]; then
217
        print_critical "Funtion job_main not found in the file $CITBX_JOB_RUN_FILE_PATH"
218
    fi
Emeric Verschuur's avatar
Emeric Verschuur committed
219
    for hook in $citbx_job_stage_main; do
220
        $citbx_before_script
221
        cd $CI_PROJECT_DIR
Emeric Verschuur's avatar
Emeric Verschuur committed
222
        $hook
223
        $citbx_after_script
Emeric Verschuur's avatar
Emeric Verschuur committed
224
    done
225
    exit 0
Emeric Verschuur's avatar
Emeric Verschuur committed
226 227
fi

228 229 230 231 232 233
############################################################
#               ### WORKSTATION PART ###
#      The following line until the end of this file
# is specific and only applicable to the local workstation
############################################################

Emeric Verschuur's avatar
Emeric Verschuur committed
234 235 236 237 238 239 240 241 242 243 244 245
# Force use citbx_run_ext_job to run another job
if [ "$CITBX" == "true" ]; then
    print_critical "You cannot call another CI script (i.e. other external job) into a CI script" \
        "Please use citbx_run_ext_job instead"
fi
export CITBX="true"

# YAML to JSON convertion
yaml2json() {
    cat "$@" | python -c 'import sys, yaml, json; json.dump(yaml.load(sys.stdin), sys.stdout)'
}

246 247 248 249 250 251 252 253 254 255
# Collect the missing binaries and other dependencies
CITBX_MISSING_PKGS=()
for bin in gawk jq dockerd; do
    if ! which $bin > /dev/null 2>&1; then
        CITBX_MISSING_PKGS+=($bin)
    fi
done
if [ "$(echo "true" | yaml2json 2>/dev/null)" != "true" ]; then
    CITBX_MISSING_PKGS+=("python-yaml")
fi
256
if [ "$CITBX_GIT_LFS_SUPPORT_ENABLED" == "true" ] && ! git lfs version > /dev/null 2>&1; then
Emeric Verschuur's avatar
Emeric Verschuur committed
257 258
    CITBX_MISSING_PKGS+=("git-lfs")
fi
259

Emeric Verschuur's avatar
Emeric Verschuur committed
260 261 262
if [ ! -f $CI_PROJECT_DIR/.gitlab-ci.yml ]; then
    print_critical "$CI_PROJECT_DIR/.gitlab-ci.yml file not found"
fi
263 264 265
if [ ${#CITBX_MISSING_PKGS[@]} -eq 0 ]; then
    GITLAB_CI_JSON=$(yaml2json $CI_PROJECT_DIR/.gitlab-ci.yml)
else
266
    print_warning "System setup required (command '$CITBX_TOOL_NAME setup')"
267
fi
Emeric Verschuur's avatar
Emeric Verschuur committed
268 269 270 271 272 273 274 275 276 277

gitlab_ci_query() {
    jq "$@" <<< "$GITLAB_CI_JSON"
}

# Check environment and run setup
citbx_check_env() {
    local os_id
    if [ "$1" != "true" ]; then
        if [ ${#CITBX_MISSING_PKGS[@]} -gt 0 ]; then
278
            print_critical "System setup needed (binary(ies)/component(s) '${CITBX_MISSING_PKGS[*]}' missing): please execute '$CITBX_TOOL_NAME setup' first"
Emeric Verschuur's avatar
Emeric Verschuur committed
279 280 281 282 283 284 285 286 287 288 289 290
        fi
        return 0
    fi
    if which lsb_release > /dev/null 2>&1; then
        os_id=$(lsb_release --id --short)
    elif [ -f /etc/os-release ]; then
        eval "$(sed 's/^NAME=/os_id=/;tx;d;:x' /etc/os-release)"
    fi
    local setupsh="$CITBX_ABS_DIR/env-setup/${os_id,,}.sh"
    if [ ! -f "$setupsh" ]; then
        print_critical "OS variant '$os_id' not supported (missing $setupsh)"
    fi
291 292 293 294 295 296 297 298 299 300 301
    check_dns() {
        case "$1" in
            ::1|127.*)
                print_error "Local $1 DNS server cannot be used with docker containers"
                return 1
                ;;
            *)
                echo "$1"
                ;;
        esac
    }
302 303 304 305 306 307 308
    setup_component_enabled() {
        local pattern='\b'"$1"'\b'
        if [[ "${CITBX_SETUP_COMPONENT[*]}" =~ $pattern ]]; then
            return 0
        fi
        return 1
    }
309
    bashopts_process_option -n CITBX_DOCKER_DNS_LIST -r -k check_dns
Emeric Verschuur's avatar
Emeric Verschuur committed
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
    . "$setupsh"
    print_info "System setup complete" "On a first install, a system reboot may be necessary"
    exit 0
}

# Get the job list
citbx_job_list() {
    local prefix outcmd arg
    prefix='[^\.]'
    outcmd='print $0'
    if ! arglist=$(getopt -o "f:p:s" -n "citbx_list " -- "$@"); then
        print_critical "Usage citbx_list: [options]" \
            "        -f <val>  Output gawk command (default: 'print $0')" \
            "        -s        Suffix list (same as -f 'printf(\" %s\", f[1]);')" \
            "        -p <val>  Prefix string"
    fi
    eval set -- "$arglist";
    while true; do
        arg=$1
        shift
        case "$arg" in
            -f) outcmd=$1;  shift;;
            -p) prefix=$1;  shift;;
            -s) outcmd='printf(" %s", f[1]);';;
            --) break;;
            *)  print_critical "Fatal error";;
        esac
    done
    gitlab_ci_query -r 'paths | select(.[-1] == "script") | .[0]' \
        | gawk 'match($0, /^'"$prefix"'(.*)$/, f) {'"$outcmd"'}'
}

342 343
declare -A CITBX_SHELL_ENV
# fetch YAML variables
344
gitlab_ci_variables() {
Emeric Verschuur's avatar
Emeric Verschuur committed
345
    local node=$1
346
    local value
Emeric Verschuur's avatar
Emeric Verschuur committed
347
    test -n "$node" \
348
        || print_critical "Usage: gitlab_ci_variables <node path>"
Emeric Verschuur's avatar
Emeric Verschuur committed
349 350 351 352 353 354 355
    local node_type="$(gitlab_ci_query -r "$node | type")"
    case "$node_type" in
        null)
            return 1
            ;;
        object)
            for k in $(gitlab_ci_query -r "$node | keys[]"); do
356 357
                if ! [[ "$(gitlab_ci_query -r "${node}.$k | type")" =~ ^(string|number)$ ]]; then
                    print_critical "Invalid $node variable (type=$(gitlab_ci_query -r "${node}.$k | type"): $k)"
Emeric Verschuur's avatar
Emeric Verschuur committed
358
                fi
359 360 361 362 363 364 365 366 367 368
                value=$(
                    eval "$k=$(gitlab_ci_query "${node}.$k")"
                    declare | grep "^$k=" | sed -E 's/^[^=]+=//g'
                )
                case "$k" in
                    CITBX_*|GIT_*|CI_*)
                        eval "export $k=$value"
                esac
                CITBX_SHELL_ENV[$k]=$value
                CITBX_DOCKER_RUN_ARGS+=(-e "$k=$(eval echo "$value")")
Emeric Verschuur's avatar
Emeric Verschuur committed
369 370 371 372 373 374 375 376 377
            done
            ;;
        *)
            print_critical "Invalid $node type"
            ;;
    esac
}

# put YAML (array or string) node script content indo CITBX_YAML_SCRIPT_ELTS
378
gitlab_ci_script() {
Emeric Verschuur's avatar
Emeric Verschuur committed
379 380 381
    local node=$1
    local line
    test -n "$node" \
382
        || print_critical "Usage: gitlab_ci_script <node path>"
Emeric Verschuur's avatar
Emeric Verschuur committed
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
    local script_type="$(gitlab_ci_query -r "$node | type")"
    case "$script_type" in
        null)
            return 1
            ;;
        string)
            CITBX_YAML_SCRIPT_ELTS+=(gitlab_ci_query -r "${node}")
            ;;
        array)
            for i in $(seq 0 $(($(gitlab_ci_query "$node | length")-1))); do
                line=$(gitlab_ci_query -r "${node}[$i]")
                if [ "$(gitlab_ci_query -r "${node}[$i] | type")" != "string" ]; then
                    print_critical "Invalid $node line: $line"
                fi
                CITBX_YAML_SCRIPT_ELTS+=("$line")
            done
            ;;
        *)
            print_critical "Invalid $node type"
            ;;
    esac
}

# Run an other job
citbx_run_ext_job() {
    local job_name=$1
    test -n "$job_name" \
        || print_critical "Usage: citbx_run_ext_job <job name>"
    print_note "Starting job $job_name"
    (
        set -e
        unset CITBX
        unset CITBX_COMMAND
416
        unset CITBX_JOB_RUN_FILE_NAME
417
        unset CITBX_GIT_CLEAN
Emeric Verschuur's avatar
Emeric Verschuur committed
418 419 420 421 422 423
        bashopts_export_opts
        export CI_JOB_NAME=$job_name
        exec $0 "$@"
    )
}

424 425 426 427 428
# Export an variable to the job environment
citbx_export() {
    CITBX_ENV_EXPORT_LIST+=("$@")
}

429 430
# Add docker run arguments
citbx_docker_run_add_args() {
431
    CITBX_JOB_DOCKER_RUN_ARGS+=("$@")
432 433
}

Emeric Verschuur's avatar
Emeric Verschuur committed
434 435 436 437 438
# Load bashopts
BASHOPTS_FILE_PATH=${BASHOPTS_FILE_PATH:-"$CITBX_ABS_DIR/3rdparty/bashopts.sh"}
if [ ! -f "$BASHOPTS_FILE_PATH" ]; then
    print_critical "Missing requered file $BASHOPTS_FILE_PATH [\$BASHOPTS_FILE_PATH]"
fi
439
bashopts_log_handler="print_log"
Emeric Verschuur's avatar
Emeric Verschuur committed
440 441 442 443 444
. $BASHOPTS_FILE_PATH
# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Set the setting file path
445 446
if [ -z "$CITBX_RC_PATH" ]; then
    CITBX_RC_PATH="/dev/null"
Emeric Verschuur's avatar
Emeric Verschuur committed
447 448
fi

449 450 451
if [ ${#CITBX_MISSING_PKGS[@]} -eq 0 ]; then
    eval "$(citbx_job_list -f 'printf("CITBX_JOB_LIST+=(\"%s\");", $0);')"
fi
452

453 454
CITBX_TOOL_NAME=${CITBX_TOOL_NAME:-$0}

455
bashopts_setup -n "$(basename $CITBX_TOOL_NAME)" \
456
    -d "Gitlab-CI job runner tool (version $CITBX_VERSION)" \
457
    -s "$CITBX_RC_PATH"
Emeric Verschuur's avatar
Emeric Verschuur committed
458

459 460 461 462 463 464
if [ "$CITBX_BASHCOMP" == "commands" ]; then
    echo -e "\"help\"\n\"setup\"\n\"update\""
    for j in "${CITBX_JOB_LIST[@]}"; do echo "\"$j\""; done | sort -u
    exit 0
fi

Emeric Verschuur's avatar
Emeric Verschuur committed
465 466 467 468
command=$1
shift || true
case "$command" in
    ''|h|help|-h|--help)
469 470
        bashopts_tool_usage="$CITBX_TOOL_NAME command [command options] [arguments...]
  => type '$CITBX_TOOL_NAME command -h' to display the contextual help
471 472 473 474 475 476 477 478 479

COMMANDS:
    help      : Display this help
    setup     : Setup the environment
    update    : Update this tool (fetch the last version from https://gitlab.com/ercom/citbx4gitlab)
    ... or a job from the job list

JOBS:
$(for j in "${CITBX_JOB_LIST[@]}"; do echo "    $j"; done | sort -u)"
480
        bashopts_diplay_help_delayed
Emeric Verschuur's avatar
Emeric Verschuur committed
481 482
        ;;
    setup)
483 484
        bashopts_tool_usage="$CITBX_TOOL_NAME $command [arguments...]
  => type '$CITBX_TOOL_NAME help' to display the global help"
485 486 487 488 489 490 491 492 493 494 495 496 497 498
        bashopts_declare -n CITBX_SETUP_COMPONENT -l component \
            -t enum -m add -d "Setup only specified components" \
            -e base-pkgs -e docker-cfg -e git-lfs -e ca-certs -e ci-toolbox \
            -x '(base-pkgs docker-cfg git-lfs ca-certs ci-toolbox)'
        check_tool_name() {
            if [[ "$1" =~ ^[a-zA-Z0-9_-]+$ ]]; then
                echo $1
                return 0
            fi
            bashopts_log E "'$1' is not a valid tool name"
            return 1
        }
        bashopts_declare -n CITBX_TOOLBOX_NAME -l toolbox-name \
            -t string -d "CI toolbox name" -k check_tool_name -v ci-toolbox
499 500 501
        bashopts_declare -n CITBX_DOCKER_BIP -l docker-bip -v "$(
            val=$(jq -r '.bip' /etc/docker/daemon.json 2> /dev/null || true)
            echo ${val:-"192.168.255.254/24"}
502
        )" -t string -d "Local docker network IPV4 host adress"
503 504 505
        bashopts_declare -n CITBX_DOCKER_FIXED_CIDR -l docker-cdir -v "$(
            val=$(jq -r '."fixed-cidr"' /etc/docker/daemon.json 2> /dev/null || true)
            echo ${val:-"192.168.255.0/24"}
506
        )" -t string -d "Local docker network IPV4 prefix"
Emeric Verschuur's avatar
Emeric Verschuur committed
507
        bashopts_declare -n CITBX_DOCKER_DNS_LIST -l docker-dns -m add \
508
            -x "($(
509
                if [ "0$(jq -e '.dns | length' /etc/docker/daemon.json 2> /dev/null || true)" -gt 0 ]; then
510 511
                    jq -r '.dns[]' /etc/docker/daemon.json 2> /dev/null | tr '\n' ' '
                else
512
                    RESOLV_CONF_DNS="$(cat /etc/resolv.conf | awk '/^nameserver/ {
513 514 515
                        if ($2 !~ /^127\..*/ && $2 != "::1" ) {
                            printf(" %s", $2);
                        }
516 517
                    }' 2> /dev/null || true)"
                    echo "${RESOLV_CONF_DNS:-${CITBX_DOCKER_DEFAULT_DNS[*]}}"
518 519
                fi
                ) )" \
520
            -t string -d "Docker DNS"
521 522 523 524
        bashopts_declare -n CITBX_DOCKER_STORAGE_DRIVER -l docker-storage-driver -v "$(
            val=$(jq -r '."storage-driver"' /etc/docker/daemon.json 2> /dev/null || true)
            echo ${val:-"overlay2"}
        )" -e 'o|overlay2' -e 'overlay' -e 'a|aufs' -e 'd|devicemapper' -e 'b|btrfs' -e 'z|zfs' \
525
            -t enum -d "Docker storage driver"
Emeric Verschuur's avatar
Emeric Verschuur committed
526
        ;;
527 528
    update)
        ;;
Emeric Verschuur's avatar
Emeric Verschuur committed
529
    *)
530 531 532
        # Properties check
        CITBX_DEFAULT_JOB_SHELL=${CITBX_DEFAULT_JOB_SHELL:-/bin/sh}
        CITBX_DEFAULT_SERVICE_DOCKER_PRIVILEGED=${CITBX_DEFAULT_SERVICE_DOCKER_PRIVILEGED:-false}
Emeric Verschuur's avatar
Emeric Verschuur committed
533
        CITBX_DEFAULT_GIT_LFS_ENABLED=${CITBX_DEFAULT_GIT_LFS_ENABLED:-false}
534
        # Command check
Emeric Verschuur's avatar
Emeric Verschuur committed
535 536
        pattern='\b'"$command"'\b'
        if ! [[ "${CITBX_JOB_LIST[*]}" =~ $pattern ]]; then
537
            print_critical "Unreconized command; type '$CITBX_TOOL_NAME help' to display the help"
Emeric Verschuur's avatar
Emeric Verschuur committed
538 539
        fi
        CI_JOB_NAME=$command
540
        citbx_local
541
        CITBX_JOB_RUN_FILE_NAME=${CITBX_JOB_RUN_FILE_NAME:-"$CI_JOB_NAME.sh"}
542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
        # Read Image property
        for p in '."'"$CI_JOB_NAME"'"' ''; do
            case "$(gitlab_ci_query -r "$p.image | type")" in
                object)
                    if [ "$(gitlab_ci_query -r "$p.image.name | type")" == "string" ]; then
                        CITBX_DEFAULT_DOCKER_IMAGE=$(gitlab_ci_query -r "$p.image.name")
                        if [ "$(gitlab_ci_query -r "$p.image.entrypoint | type")" == "array" ]; then
                            for i in $(seq 0 $(gitlab_ci_query -r "$p.image.entrypoint | length - 1")); do
                                CITBX_DEFAULT_DOCKER_ENTRYPOINT+=("$(gitlab_ci_query -r "$p.image.entrypoint[$i]")")
                            done
                        fi
                        break
                    fi
                    ;;
                string)
                    CITBX_DEFAULT_DOCKER_IMAGE=$(gitlab_ci_query -r "$p.image")
                    break
                    ;;
                *)
                    ;;
            esac
        done
564
        # Read the gitlab-ci variables
565 566
        gitlab_ci_variables ".\"variables\"" || true
        gitlab_ci_variables ".\"$CI_JOB_NAME\".\"variables\"" || true
567
        # Define job usage
568 569
        bashopts_tool_usage="$CITBX_TOOL_NAME $command [arguments...]
  => type '$CITBX_TOOL_NAME help' to display the global help"
Emeric Verschuur's avatar
Emeric Verschuur committed
570
        # Define the generic options
571
        bashopts_declare -n GIT_SUBMODULE_STRATEGY -l submodule-strategy \
572 573
            -d "Git submodule strategy (none, normal or recursive)" -t enum -v "${GIT_SUBMODULE_STRATEGY:-none}" \
            -e 'none' -e 'normal' -e 'recursive'
574 575
        bashopts_declare -n CITBX_GIT_CLEAN -l git-clean -o c \
            -d "Perfom a git clean -fdx in the main project and submodules" -t boolean
576 577 578 579
        if [ "$CITBX_GIT_LFS_SUPPORT_ENABLED" == "true" ]; then
            bashopts_declare -n CITBX_GIT_LFS_ENABLED -l git-lfs -v "$CITBX_DEFAULT_GIT_LFS_ENABLED" \
                -d "Enable git LFS support" -t boolean
        fi
Emeric Verschuur's avatar
Emeric Verschuur committed
580 581 582 583 584 585
        declare_opts=()
        if [ -n "$DEFAULT_CI_REGISTRY" ]; then
            declare_opts+=(-v "$DEFAULT_CI_REGISTRY")
        fi
        bashopts_declare -n CI_REGISTRY -l docker-registry -d "Docker registry" -t string -s "${declare_opts[@]}"
        unset declare_opts
586 587
        bashopts_declare -n CITBX_DOCKER_LOGIN_MODE -l docker-login -d "Execute docker login" -t enum \
            -e "enabled" -e "disabled" -e "auto" -v "${CITBX_DEFAULT_DOCKER_LOGIN_MODE:-auto}"
588
        bashopts_declare -n CITBX_JOB_EXECUTOR -l job-executor -o e \
589 590 591
            -d "Job executor type (only docker or shell is sypported yet)" -t enum \
            -v "$(test -n "$CITBX_DEFAULT_DOCKER_IMAGE" && echo "docker" || echo "shell" )" \
            -e 's|shell' -e 'd|docker'
592
        bashopts_declare -n CITBX_DOCKER_IMAGE -l docker-image -d "Docker image name" -t string \
593
            -x "\"$CITBX_DEFAULT_DOCKER_IMAGE\""
594
        bashopts_declare -n CITBX_DOCKER_ENTRYPOINT -l docker-entrypoint -d "Docker entrypoint" -t string -m add \
595
            -x "$(bashopts_get_def CITBX_DEFAULT_DOCKER_ENTRYPOINT)"
Emeric Verschuur's avatar
Emeric Verschuur committed
596 597
        bashopts_declare -n CITBX_UID -l uid -t number \
            -d "Start this script as a specific uid (0 for root)" -v "$(id -u)"
598 599 600
        CITBX_USER_GROUPS=(adm plugdev)
        bashopts_declare -n CITBX_USER_GROUPS -l group -t string -m add \
            -d "User group list"
601 602 603
        bashopts_declare -n CITBX_DEBUG_SCRIPT_ENABLED -o x -l debug-script -t boolean \
            -d "Enable SHELL script debug (set -e)"
        citbx_export CITBX_DEBUG_SCRIPT_ENABLED
604
        bashopts_declare -n CITBX_RUN_SHELL -o s -l run-shell -t boolean \
Emeric Verschuur's avatar
Emeric Verschuur committed
605
            -d "Run a shell instead of run the default command (override CITBX_COMMAND option)"
606 607
        bashopts_declare -n CITBX_JOB_SHELL -l shell -t string -v "$CITBX_DEFAULT_JOB_SHELL" \
            -d "Use a specific shell to run the job"
608 609
        bashopts_declare -n CITBX_WAIT_FOR_SERVICE_START -l wait-srv-started -t number -v 0 \
            -d "Wait for service start (time in seconds)"
610 611
        bashopts_declare -n CITBX_DISABLED_SERVICES -l disable-service -t string \
            -d "Disable a service" -m add
612 613
        bashopts_declare -n CITBX_SERVICE_DOCKER_PRIVILEGED -l service-privileged -t boolean \
            -d "Start service docker container in privileged mode" -v "$CITBX_DEFAULT_SERVICE_DOCKER_PRIVILEGED"
614
        CITBX_DOCKER_USER=${CITBX_DOCKER_USER:-root}
Emeric Verschuur's avatar
Emeric Verschuur committed
615 616 617

        # Load job 
        module_handler_list="define setup"
618
        if [ -f "$CITBX_ABS_DIR/run.d/$CITBX_JOB_RUN_FILE_NAME" ]; then
619
            . "$CITBX_ABS_DIR/run.d/$CITBX_JOB_RUN_FILE_NAME"
Emeric Verschuur's avatar
Emeric Verschuur committed
620 621 622 623
            citbx_register_handler "job" "define"
            citbx_register_handler "job" "setup"
        fi
        for hook in $citbx_job_stage_define; do
624
            cd $CI_PROJECT_DIR
Emeric Verschuur's avatar
Emeric Verschuur committed
625 626 627 628 629
            $hook
        done
        ;;
esac

630
if [ -n "$CITBX_BASHCOMP" ]; then
631 632
    # ### BASH completion specific part ###
    # Used only by the bashcomp tool to generate completion words
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    case "$CITBX_BASHCOMP" in
        opts)
            for o in "${bashopts_optprop_short_opt[@]}"; do
                echo "\"-$o\""
            done | sort -u
            for o in "${bashopts_optprop_long_opt[@]}"; do
                echo "\"--$o\""
            done | sort -u
            ;;
        longopts)
            for o in "${bashopts_optprop_long_opt[@]}"; do
                echo "\"--$o\""
            done | sort -u
            ;;
        --docker-image)
            while read -r line; do
                echo "\"$line\""
            done <<< "$(docker images | tail -n +2 \
                | awk '($1 != "<none>" && $2 != "<none>") {print $1":"$2}')"
            ;;
        -*)
            bashopts_get_valid_value_list $CITBX_BASHCOMP
            ;;
    esac
    exit 0
fi

Emeric Verschuur's avatar
Emeric Verschuur committed
660 661 662 663 664 665 666
# Parse arguments
bashopts_parse_args "$@"

# Process argument
bashopts_process_opts

# check the environment
667
citbx_check_env $(test "$command" != "setup" || echo "true")
Emeric Verschuur's avatar
Emeric Verschuur committed
668

669
if [ "$command" == "update" ]; then
670 671 672 673 674 675
    tmpdir=$(mktemp -d)
    version=${bashopts_commands[0]:-"master"}
    print_note "Downloading $version archive from gitlab.com..."
    curl -fSsL https://gitlab.com/ercom/citbx4gitlab/repository/$version/archive.tar.bz2 | tar -C $tmpdir -xj
    srcdir="$tmpdir/$(ls -1 $tmpdir)"
    cp -av $srcdir/tools/gitlab-ci/run.sh $CITBX_ABS_DIR/run.sh
676
    chmod +x $CITBX_ABS_DIR/run.sh
677 678 679 680
    mkdir -p $CITBX_ABS_DIR/env-setup
    cp -av $srcdir/tools/gitlab-ci/env-setup/* $CITBX_ABS_DIR/env-setup/
    cp -av $srcdir/tools/gitlab-ci/3rdparty/bashopts.sh $BASHOPTS_FILE_PATH
    rm -rf $tmpdir
681
    print_info "Update done!"
682 683 684
    exit 0
fi

Emeric Verschuur's avatar
Emeric Verschuur committed
685 686 687 688 689 690
if [ "$(gitlab_ci_query -r '."'"$CI_JOB_NAME"'".script | type')" == "null" ]; then
    print_critical "Unable to find a valid job with tne name \"$CI_JOB_NAME\" in the .gitlab-ci.yml"
fi

# Login to the registry if needed
if [ -n "$CI_REGISTRY" ] \
691 692
    && ( ( [ -z "$(jq -r '."auths"."'$CI_REGISTRY'" | select(.auth != null)' $HOME/.docker/config.json 2> /dev/null)" ] \
            && [ "$CITBX_DOCKER_LOGIN_MODE" == "auto" ] ) \
693 694 695 696 697
        || [ "$CITBX_DOCKER_LOGIN_MODE" == "enabled" ] ); then
    print_info "You seem to be not authenticated against the gitlab docker registry" \
        "> You can disable this feature by using --docker-login=disabled" \
        "> Or force this feature permanently by setting CITBX_DEFAULT_DOCKER_LOGIN_MODE into $CITBX_ABS_DIR/citbx.properties" \
        "Please enter your gitlab user id and password:"
Emeric Verschuur's avatar
Emeric Verschuur committed
698 699 700 701 702
    docker login $CI_REGISTRY
fi

# Compute commands from before_script script and after_script
CITBX_JOB_SCRIPT='
703 704 705
if [ -f $HOME/.bashrc ]; then
    . $HOME/.bashrc
fi
Emeric Verschuur's avatar
Emeric Verschuur committed
706 707 708 709 710 711 712 713 714
print_info() {
    printf "\e[1m\e[92m%s\e[0m\n" "$@"
}
print_error() {
    printf "\e[1m\e[91m%s\e[0m\n" "$@"
}
print_cmd() {
    printf "\e[1m\e[92m$ %s\e[0m\n" "$@"
}
715 716 717
'$(if [ "$CITBX_DEBUG_SCRIPT_ENABLED" == "true" ]; then
    echo "set -x"
fi)'
Emeric Verschuur's avatar
Emeric Verschuur committed
718 719 720
__job_exit_code__=0
(
'
721 722
gitlab_ci_script ".\"$CI_JOB_NAME\".\"before_script\"" \
    || gitlab_ci_script ".\"before_script\"" \
Emeric Verschuur's avatar
Emeric Verschuur committed
723
    || true
724
gitlab_ci_script ".\"$CI_JOB_NAME\".\"script\"" \
725
    || print_critical "script \"$CI_JOB_NAME\".script node nor found!"
Emeric Verschuur's avatar
Emeric Verschuur committed
726 727 728 729 730 731 732 733 734 735
for line in "${CITBX_YAML_SCRIPT_ELTS[@]}"; do
    CITBX_JOB_SCRIPT="$CITBX_JOB_SCRIPT
print_cmd $(bashopts_get_def line)
$line || exit \$?
"
done
CITBX_JOB_SCRIPT="$CITBX_JOB_SCRIPT"'
) || __job_exit_code__=$?
'
unset CITBX_YAML_SCRIPT_ELTS
736 737
if gitlab_ci_script ".\"$CI_JOB_NAME\".\"after_script\"" \
    || gitlab_ci_script ".\"after_script\""; then
Emeric Verschuur's avatar
Emeric Verschuur committed
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754

    CITBX_JOB_SCRIPT="$CITBX_JOB_SCRIPT"'
print_info "Running after script..."
'
    for line in "${CITBX_YAML_SCRIPT_ELTS[@]}"; do
        CITBX_JOB_SCRIPT="$CITBX_JOB_SCRIPT
print_cmd $(bashopts_get_def line)
$line
"
    done
fi
CITBX_JOB_SCRIPT="$CITBX_JOB_SCRIPT"'
if [ $__job_exit_code__ -eq 0 ]; then
    print_info "Job succeeded"
else
    print_error "ERROR: Job failed: exit code $__job_exit_code__"
fi
755
exit $__job_exit_code__
Emeric Verschuur's avatar
Emeric Verschuur committed
756 757 758
'
CITBX_JOB_SCRIPT="'"${CITBX_JOB_SCRIPT//\'/\'\\\'\'}"'"

759
# Fetch git submodules
Emeric Verschuur's avatar
Emeric Verschuur committed
760
if [ "$GIT_SUBMODULE_STRATEGY" != "none" ]; then
761 762 763 764 765 766 767 768
    GIT_SUBMODULE_ARGS=()
    case "$GIT_SUBMODULE_STRATEGY" in
        normal)
            ;;
        recursive)
            GIT_SUBMODULE_ARGS+=("--recursive")
            ;;
        *)
769
            print_critical "Invalid value for GIT_SUBMODULE_STRATEGY: $GIT_SUBMODULE_STRATEGY"
770 771 772
            ;;
    esac
    print_info "Fetching git submodules..."
773
    git submodule --quiet sync "${GIT_SUBMODULE_ARGS[@]}"
774 775 776
    git submodule update --init "${GIT_SUBMODULE_ARGS[@]}"
fi

777 778
if [ "$CITBX_GIT_CLEAN" == "true" ]; then
    git clean -fdx
Emeric Verschuur's avatar
Emeric Verschuur committed
779
    if [ "$GIT_SUBMODULE_STRATEGY" != "none" ]; then
780
        git submodule --quiet foreach "${GIT_SUBMODULE_ARGS[@]}" git clean -fdx
Emeric Verschuur's avatar
Emeric Verschuur committed
781 782 783
    fi
fi

784
# TODO: seems to be not needed anymore with recent git version
Emeric Verschuur's avatar
Emeric Verschuur committed
785 786 787
if [ "$CITBX_GIT_LFS_ENABLED" == "true" ]; then
    git lfs pull
    if [ "$GIT_SUBMODULE_STRATEGY" != "none" ]; then
788
        git submodule --quiet foreach "${GIT_SUBMODULE_ARGS[@]}" git lfs pull
789 790 791
    fi
fi

Emeric Verschuur's avatar
Emeric Verschuur committed
792 793
# Git SHA1
CI_COMMIT_REF_NAME=${CI_COMMIT_REF_NAME:-$(cd $CI_PROJECT_DIR && git rev-parse --abbrev-ref HEAD)}
794
CITBX_JOB_DOCKER_RUN_ARGS+=(-e CI_COMMIT_REF_NAME="$CI_COMMIT_REF_NAME")
Emeric Verschuur's avatar
Emeric Verschuur committed
795

796 797 798 799 800 801 802 803 804 805 806 807 808
# If not set, fill the CI_SERVER_TLS_CA_FILE with local CA certificates
if ! [[ -v CITBX_TLS_CA_SEARCH_DIR_LIST ]]; then
    CITBX_TLS_CA_SEARCH_DIR_LIST=("/usr/local/share/ca-certificates/")
fi
if ! [[ -v CI_SERVER_TLS_CA_FILE ]]; then
    CI_SERVER_TLS_CA_FILE="$(
        for dir in "${CITBX_TLS_CA_SEARCH_DIR_LIST[@]}"; do
            test ! -d "$dir" \
                || find "$dir" -iregex '.*\.\(pem\|crt\)$' -exec openssl x509 -in '{}' \;
        done
    )"
fi

809
# Add variable to the environment list
810
CITBX_ENV_EXPORT_LIST+=(CI_JOB_NAME CI_REGISTRY CI_PROJECT_DIR CI_SERVER_TLS_CA_FILE)
811

812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
if [ "$CITBX_DEBUG_SCRIPT_ENABLED" == "true" ]; then
    citbx_before_script="set -x"
    citbx_after_script="set +x"
else
    citbx_before_script=""
    citbx_after_script=""
fi

# Run the job setup hooks
for hook in $citbx_job_stage_setup; do
    $citbx_before_script
    $hook
    $citbx_after_script
done

827 828
case "$CITBX_JOB_EXECUTOR" in
    shell)
829
        print_info "Running the job \"$CI_JOB_NAME\" into the shell $CITBX_JOB_SHELL..."
830 831 832
        (
            unset CITBX
            export GITLAB_CI=true
833 834 835
            for e in ${CITBX_ENV_EXPORT_LIST[@]}; do
                export $e
            done
836 837 838
            for e in "${!CITBX_SHELL_ENV[@]}"; do
                eval "export $e=${CITBX_SHELL_ENV[$e]}"
            done
839 840 841 842 843
            eval "$CITBX_JOB_SHELL -c $CITBX_JOB_SCRIPT"
        )
        ;;
    docker)
        # Setup docker environment
844
        if [ -z "$CITBX_DOCKER_IMAGE" ] || [ "$CITBX_DOCKER_IMAGE" == "null" ]; then
845
            print_critical "No image property found in .gitlab-ci.yml for the job \"$CI_JOB_NAME\""
846
        fi
847 848
        CITBX_ID=$(head -c 8 /dev/urandom | od -t x8 -An | grep -oE '\w+')
        CITBX_DOCKER_PREFIX="citbx-$CITBX_ID"
849
        if [ -f "$HOME/.docker/config.json" ]; then
850
            CITBX_JOB_DOCKER_RUN_ARGS+=(-v $HOME/.docker/config.json:/root/.docker/config.json:ro)
851 852
        fi
        CITBX_JOB_SHELL=${CITBX_JOB_SHELL:-"/bin/sh"}
853
        if [ "$CITBX_UID" -eq 0 ] || [ "$CITBX_DOCKER_USER" != "root" ]; then
854 855 856 857 858 859 860
            if [ "$CITBX_RUN_SHELL" == "true" ]; then
                CITBX_COMMANDS=$CITBX_JOB_SHELL
            else
                CITBX_COMMANDS="$CITBX_JOB_SHELL -c $CITBX_JOB_SCRIPT"
            fi
        else
            if [ -f "$HOME/.docker/config.json" ]; then
861
                CITBX_JOB_DOCKER_RUN_ARGS+=(-v $HOME/.docker/config.json:$HOME/.docker/config.json:ro)
862
            fi
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877
            CITBX_COMMANDS='
                if which useradd > /dev/null 2>&1; then
                    useradd -o -u '"$CITBX_UID"' -s /bin/sh -d '"$HOME"' -M ci-user;
                elif readlink -f "$(which adduser)" | grep -q /busybox$ > /dev/null 2>&1; then
                    busybox adduser -u '"$CITBX_UID"' -s /bin/sh -h '"$HOME"' -H -D ci-user
                else
                    echo "[!!] No usual tool found to add an user"
                    exit 1
                fi
                chown '"$CITBX_UID"':'"$CITBX_UID"' '"$HOME"'
                for group in '"${CITBX_USER_GROUPS[*]}"'; do
                    if grep -q ^$group /etc/group; then
                        addgroup ci-user $group > /dev/null;
                    fi;
                done;
878
                if [ -f /etc/sudoers ]; then
879 880
                    sed -i "/^ci-user /d" /etc/sudoers;
                    echo "ci-user ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers;
881
                fi;
882 883 884 885
                echo "export PATH=\"$PATH\"" >> "'"$HOME"'/.bashrc"
                su ci-user -s '"$CITBX_JOB_SHELL"' '"$( test "$CITBX_RUN_SHELL" == "true" \
                    || echo "-c $CITBX_JOB_SCRIPT" )"';
            '
886
        fi
Emeric Verschuur's avatar
Emeric Verschuur committed
887

888
        if [ -n "$CITBX_DOCKER_USER" ]; then
889
            CITBX_JOB_DOCKER_RUN_ARGS+=(-u "$CITBX_DOCKER_USER")
890
        fi
Emeric Verschuur's avatar
Emeric Verschuur committed
891

892 893
        # Compute the environment variables
        for e in ${CITBX_ENV_EXPORT_LIST[@]}; do
894
            CITBX_DOCKER_RUN_ARGS+=(-e $e="${!e}")
895
        done
Emeric Verschuur's avatar
Emeric Verschuur committed
896

897 898 899
        CITBX_PRE_COMMANDS=()
        # Entrypoint override management
        if [ -n "$CITBX_DOCKER_ENTRYPOINT" ]; then
900
            CITBX_JOB_DOCKER_RUN_ARGS+=(--entrypoint "$CITBX_DOCKER_ENTRYPOINT")
901 902 903 904 905
            for e in "${CITBX_DOCKER_ENTRYPOINT[@]:1}"; do
                CITBX_PRE_COMMANDS+=("$e")
            done
        fi

906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
        # hook executed on exit
        executor_docker_exit_hook() {
            test -n "$CITBX_DOCKER_PREFIX" || print_critical "Assert: empty CITBX_DOCKER_PREFIX"
            for d in $(docker ps -a --filter "label=$CITBX_DOCKER_PREFIX" -q); do
                docker rm -f $d > /dev/null 2>&1 || true
            done
        }
        trap executor_docker_exit_hook EXIT SIGINT SIGTERM

        wait_before_run_job=0

        # Start a service
        start_docker_service() {
            local args=()
            local image=$1
            local name=$2
            local ip
923 924 925 926 927
            local pattern='\b'"$name"'\b'
            if [[ "${CITBX_DISABLED_SERVICES[*]}" =~ $pattern ]]; then
                print_note "Skipping $name service start"
                return 0
            fi
928 929 930 931 932
            args+=(--name "$CITBX_DOCKER_PREFIX-$name" --label "$CITBX_DOCKER_PREFIX")
            shift 2
            if [ -n "$1" ]; then
                args+=(--entrypoint "$1")
            fi
933
            if [ "$CITBX_SERVICE_DOCKER_PRIVILEGED" == "true" ]; then
934 935
                args+=(--privileged)
            fi
936
            shift || true
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
            print_info "Starting service $name..."
            docker run -d "${args[@]}" "${CITBX_DOCKER_RUN_ARGS[@]}" "$image" "$@"
            # Get container IP and add --add-host options
            ip=$(docker inspect $CITBX_DOCKER_PREFIX-$name | jq -r .[0].NetworkSettings.Networks.bridge.IPAddress)
            CITBX_JOB_DOCKER_RUN_ARGS+=(--add-host "$name:$ip")
            wait_before_run_job=$CITBX_WAIT_FOR_SERVICE_START
        }

        # Start services
        for p in '."'"$CI_JOB_NAME"'"' ''; do
            for s in $([ "$(gitlab_ci_query -r "$p.services | type")" != "array" ] \
                || seq 0 $(($(gitlab_ci_query -r "$p.services | length") - 1))); do
                unset service_image service_alias service_commands
                service_commands=()
                case "$(gitlab_ci_query -r "$p.services[$s] | type")" in
                    object)
                        # Read the service name/image property
                        if [ "$(gitlab_ci_query -r "$p.services[$s].name | type")" == "string" ]; then
955
                            service_image="$(eval echo "$(gitlab_ci_query "$p.services[$s].name")")"
956 957 958 959 960 961
                        else
                            print_critical "$s: property 'name' not found"
                        fi
                        # Read entrypoint property
                        if [ "$(gitlab_ci_query -r "$p.services[$s].entrypoint | type")" == "array" ]; then
                            for i in $(seq 0 $(gitlab_ci_query -r "$p.services[$s].entrypoint | length - 1")); do
962
                                service_commands+=("$(eval echo "$(gitlab_ci_query "$p.services[$s].entrypoint[$i]")")")
963
                            done
964 965 966
                        else
                            # Empty: NO entrypoint
                            service_commands+=("")
967 968 969 970
                        fi
                        # Read command property
                        if [ "$(gitlab_ci_query -r "$p.services[$s].command | type")" == "array" ]; then
                            for i in $(seq 0 $(gitlab_ci_query -r "$p.services[$s].command | length - 1")); do
971
                                service_commands+=("$(eval echo "$(gitlab_ci_query "$p.services[$s].command[$i]")")")
972 973 974 975
                            done
                        fi
                        # Read service alias property
                        if [ "$(gitlab_ci_query -r "$p.services[$s].alias | type")" == "string" ]; then
976
                            service_alias="$(eval echo "$(gitlab_ci_query "$p.services[$s].alias")")"
977 978 979 980 981 982 983
                        else
                            service_alias="$(echo "$service_image" | sed -E 's/:[^:\/]+//g' | sed -E 's/[^a-zA-Z0-9\._-]/__/g')"
                        fi
                        # Start service
                        start_docker_service "$service_image" "$service_alias" "${service_commands[@]}"
                        ;;
                    string)
984
                        service_image="$(eval echo "$(gitlab_ci_query "$p.services[$s]")")"
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
                        # Start service
                        start_docker_service "$service_image" "$(echo "$service_image" | sed -E 's/:[^:\/]+//g' | sed -E 's/[^a-zA-Z0-9\._-]/__/g')"
                        ;;
                    *)
                        ;;
                esac
            done
        done

        # Wait time
        if [ $wait_before_run_job -gt 0 ]; then
            print_note "Waiting $wait_before_run_job seconds before run the job..."
            sleep $wait_before_run_job
        fi

1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011
        # Add project dir mount
        CITBX_JOB_DOCKER_RUN_ARGS+=(-v "$CI_PROJECT_DIR:$CI_PROJECT_DIR:rw")
        GIRDIR_PATH=$(readlink -f $(git rev-parse --git-common-dir))
        if [ "${GIRDIR_PATH#$CI_PROJECT_DIR}" == "$GIRDIR_PATH" ]; then
            # If the git dir is ouside the project dir
            CITBX_JOB_DOCKER_RUN_ARGS+=(-v "$GIRDIR_PATH:$GIRDIR_PATH:rw")
        fi

        if [ "$CITBX_RUN_SHELL" == "true" ]; then
            print_info "Running a shell into the $CITBX_DOCKER_IMAGE docker container..."
            CITBX_JOB_DOCKER_RUN_ARGS+=(-w "$PWD")
        else
1012
            print_info "Running the job \"$CI_JOB_NAME\" into the $CITBX_DOCKER_IMAGE docker container..."
1013 1014
            CITBX_JOB_DOCKER_RUN_ARGS+=(-w "$CI_PROJECT_DIR")
        fi
1015

1016
        # Run the docker
1017 1018
        docker run --rm -ti --name="$CITBX_DOCKER_PREFIX-build" --hostname="$CITBX_DOCKER_PREFIX-build" \
            -e CI=true -e GITLAB_CI=true -v /var/run/docker.sock:/var/run/docker.sock \
1019
            "${CITBX_DOCKER_RUN_ARGS[@]}" --label "$CITBX_DOCKER_PREFIX" "${CITBX_JOB_DOCKER_RUN_ARGS[@]}" \
1020
            -e DOCKER_RUN_EXTRA_ARGS="$(bashopts_get_def bashopts_extra_args)" "${bashopts_extra_args[@]}" \
1021 1022
            $CITBX_DOCKER_IMAGE "${CITBX_PRE_COMMANDS[@]}" $CITBX_JOB_SHELL -c "$CITBX_COMMANDS" \
            || exit $?
1023 1024 1025 1026 1027
        ;;
    *)
        print_critical "Invalid or unsupported '$CITBX_JOB_EXECUTOR' executor"
        ;;
esac