Newer
Older
# =========================================================================================
# This program is free software; you can redistribute it and/or modify it under the terms
# of the GNU Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with this
# program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
# Floor, Boston, MA 02110-1301, USA.
# =========================================================================================
# default workflow rules: Merge Request pipelines
# prevent branch pipeline when an MR is open (prefer MR pipeline)
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
# test job prototype: implement adaptive pipeline rules
.test-policy:
rules:
# on tag: auto & failing
- if: $CI_COMMIT_TAG
# on ADAPTIVE_PIPELINE_DISABLED: auto & failing
- if: '$ADAPTIVE_PIPELINE_DISABLED == "true"'
# on production or integration branch(es): auto & failing
- if: '$CI_COMMIT_REF_NAME =~ $PROD_REF || $CI_COMMIT_REF_NAME =~ $INTEG_REF'
# early stage (dev branch, no MR): manual & non-failing
- if: '$CI_MERGE_REQUEST_ID == null && $CI_OPEN_MERGE_REQUESTS == null'
when: manual
allow_failure: true
# Draft MR: auto & non-failing
- if: '$CI_MERGE_REQUEST_TITLE =~ /^Draft:.*/'
allow_failure: true
# else (Ready MR): auto & failing
- when: on_success
# variabilized tracking image
TBC_TRACKING_IMAGE: "$CI_REGISTRY/to-be-continuous/tools/tracking:master"
# Default Go project root directory
GO_PROJECT_DIR: .
# Default flags for 'build' command
GO_BUILD_FLAGS: >-
# Default packages for 'build' command
GO_BUILD_PACKAGES: >-
# Default flags for 'test' command
GO_TEST_FLAGS: >-
-race
# Default packages for 'test' command
GO_TEST_PACKAGES: >-
./...
# Default arguments for 'list' command
GO_LIST_ARGS : >-
list
-u
-m
-mod=readonly
-json all
# Default arguments for go-mod-outdated command
GO_MOD_OUTDATED_ARGS: '-update -direct -style markdown -ci'
# Default golangci-lint Docker image (can be overridden)
GO_CI_LINT_IMAGE: "golangci/golangci-lint:latest-alpine"
# Default arguments for golangci-lint command
GO_CI_LINT_ARGS: '-E gosec,goimports ./...'
# default production ref name (pattern)
PROD_REF: '/^(master|main)$/'
# default integration ref name (pattern)
INTEG_REF: '/^develop$/'
# allowed stages depend on your template type (see: to-be-continuous.gitlab.io/doc/dev-guidelines/#stages)
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
stages:
- build
- test
.go-scripts: &go-scripts |
# BEGSCRIPT
set -e
function log_info() {
echo -e "[\\e[1;94mINFO\\e[0m] $*"
}
function log_warn() {
echo -e "[\\e[1;93mWARN\\e[0m] $*"
}
function log_error() {
echo -e "[\\e[1;91mERROR\\e[0m] $*"
}
function output_coverage() {
coverage_out=reports/coverage.out
if [[ -f "$coverage_out" ]]
then
log_info "--- \\e[32mCoverage report(s) found\\e[0m (\\e[33;1m${coverage_out}\\e[0m): output"
percent=$(go tool cover -func="$coverage_out" | tail -1 | awk -F" " '{print $NF}')
echo "${percent} covered"
go tool cover -html="$coverage_out" -o "reports/coverage.html"
else
log_info "--- \\e[32mCoverage report(s) not found\\e[0m: skip"
fi
}
function go_build() {
GO_TARGET_OS="${GO_TARGET_OS:-$GOOS}"
GO_TARGET_ARCH="${GO_TARGET_ARCH:-$GOARCH}"
target_dir="$GOBIN/$GO_TARGET_OS/$GO_TARGET_ARCH"
mkdir -p "$target_dir"
# shellcheck disable=SC2086
GOOS="$GO_TARGET_OS" GOARCH="$GO_TARGET_ARCH" go build $GO_BUILD_FLAGS -o "$target_dir" $GO_BUILD_PACKAGES
}
function go_test() {
mkdir reports
local go_tests_report="reports/tests-output.txt"
local junit_tests_report="reports/junit-tests-report.xml"
local coverage_report_opts=-coverprofile=reports/coverage.out
# shellcheck disable=SC2086
go test $GO_TEST_FLAGS "$coverage_report_opts" $GO_TEST_PACKAGES | tee "$go_tests_report"
output_coverage
install_go_junit_report
# shellcheck disable=SC2002
cat "$go_tests_report" | "$GOBIN/go-junit-report" > "$junit_tests_report"
sonar_tests_report
}
function install_go_junit_report() {
cd "$(mktemp -d)"
go mod init go-junit-report
go install github.com/jstemmer/go-junit-report@latest
cd -
}
function sonar_tests_report() {
if [[ -n "$SONAR_URL" ]]
then
log_info "SonarQube template detected (\$SONAR_URL): generate tests report for SonarQube (\\e[33;1mreports/sonar-tests-report.json\\e[0m)"
mkdir -p reports
# shellcheck disable=SC2086
go test $GO_TEST_FLAGS -json $GO_TEST_PACKAGES > reports/sonar-tests-report.json
if [[ "${SONAR_HOST_URL:-$SONAR_URL}" ]]
log_info "SonarQube template detected (\$SONAR_HOST_URL or \$SONAR_URL): generate golangci-lint report for SonarQube (\\e[33;1mreports/golangci-lint-report.xml\\e[0m)"
# shellcheck disable=SC2086
golangci-lint run --out-format checkstyle $GO_CI_LINT_ARGS > reports/golangci-lint-report.xml
fi
}
function install_go_mod_outdated() {
cd "$(mktemp -d)"
go mod init go-mod-outdated
go install github.com/psampaz/go-mod-outdated@latest
function install_ca_certs() {
certs=$1
if [[ -z "$certs" ]]
then
return
fi
# import in system
if echo "$certs" >> /etc/ssl/certs/ca-certificates.crt
then
log_info "CA certificates imported in \\e[33;1m/etc/ssl/certs/ca-certificates.crt\\e[0m"
fi
if echo "$certs" >> /etc/ssl/cert.pem
then
log_info "CA certificates imported in \\e[33;1m/etc/ssl/cert.pem\\e[0m"
fi
}
function unscope_variables() {
_scoped_vars=$(env | awk -F '=' "/^scoped__[a-zA-Z0-9_]+=/ {print \$1}" | sort)
if [[ -z "$_scoped_vars" ]]; then return; fi
log_info "Processing scoped variables..."
for _scoped_var in $_scoped_vars
do
_fields=${_scoped_var//__/:}
_condition=$(echo "$_fields" | cut -d: -f3)
case "$_condition" in
if) _not="";;
ifnot) _not=1;;
*)
log_warn "... unrecognized condition \\e[1;91m$_condition\\e[0m in \\e[33;1m${_scoped_var}\\e[0m"
continue
;;
esac
_target_var=$(echo "$_fields" | cut -d: -f2)
_cond_var=$(echo "$_fields" | cut -d: -f4)
_cond_val=$(eval echo "\$${_cond_var}")
_test_op=$(echo "$_fields" | cut -d: -f5)
case "$_test_op" in
defined)
if [[ -z "$_not" ]] && [[ -z "$_cond_val" ]]; then continue;
elif [[ "$_not" ]] && [[ "$_cond_val" ]]; then continue;
fi
;;
equals|startswith|endswith|contains|in|equals_ic|startswith_ic|endswith_ic|contains_ic|in_ic)
# comparison operator
# sluggify actual value
_cond_val=$(echo "$_cond_val" | tr '[:punct:]' '_')
# retrieve comparison value
_cmp_val_prefix="scoped__${_target_var}__${_condition}__${_cond_var}__${_test_op}__"
# manage 'ignore case'
if [[ "$_test_op" == *_ic ]]
then
# lowercase everything
_cond_val=$(echo "$_cond_val" | tr '[:upper:]' '[:lower:]')
_cmp_val=$(echo "$_cmp_val" | tr '[:upper:]' '[:lower:]')
fi
case "$_test_op" in
equals*)
if [[ -z "$_not" ]] && [[ "$_cond_val" != "$_cmp_val" ]]; then continue;
elif [[ "$_not" ]] && [[ "$_cond_val" == "$_cmp_val" ]]; then continue;
if [[ -z "$_not" ]] && [[ "$_cond_val" != "$_cmp_val"* ]]; then continue;
elif [[ "$_not" ]] && [[ "$_cond_val" == "$_cmp_val"* ]]; then continue;
if [[ -z "$_not" ]] && [[ "$_cond_val" != *"$_cmp_val" ]]; then continue;
elif [[ "$_not" ]] && [[ "$_cond_val" == *"$_cmp_val" ]]; then continue;
if [[ -z "$_not" ]] && [[ "$_cond_val" != *"$_cmp_val"* ]]; then continue;
elif [[ "$_not" ]] && [[ "$_cond_val" == *"$_cmp_val"* ]]; then continue;
if [[ -z "$_not" ]] && [[ "__${_cmp_val}__" != *"__${_cond_val}__"* ]]; then continue;
elif [[ "$_not" ]] && [[ "__${_cmp_val}__" == *"__${_cond_val}__"* ]]; then continue;
fi
;;
esac
;;
*)
log_warn "... unrecognized test operator \\e[1;91m${_test_op}\\e[0m in \\e[33;1m${_scoped_var}\\e[0m"
continue
;;
esac
# matches
_val=$(eval echo "\$${_target_var}")
log_info "... apply \\e[32m${_target_var}\\e[0m from \\e[32m\$${_scoped_var}\\e[0m${_val:+ (\\e[33;1moverwrite\\e[0m)}"
_val=$(eval echo "\$${_scoped_var}")
export "${_target_var}"="${_val}"
done
log_info "... done"
}
unscope_variables
# ENDSCRIPT
# job prototype
# defines default default docker image, tracking probe, cache policy and tags
.go-base:
image: $GO_IMAGE
services:
command: ["--service", "golang", "3.0.0" ]
variables:
# The directory where 'go install' will install a command.
# The directory where the go command will store cached information for reuse in future builds.
GOCACHE: "$CI_PROJECT_DIR/$GO_PROJECT_DIR/.cache"
cache:
key: "$CI_COMMIT_REF_SLUG-golang"
paths:
- git config --global url.https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}.insteadOf https://${CI_SERVER_HOST}
- install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
go-build:
extends: .go-base
stage: build
script:
artifacts:
name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
expire_in: 1 day
paths:
rules:
# if $GO_TEST_IMAGE set
- if: '$GO_TEST_IMAGE'
go-test:
extends: .go-base
image: $GO_TEST_IMAGE
stage: build
script:
coverage: '/^(\d+.\d+\%) covered$/'
artifacts:
name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
expire_in: 1 day
- if: '$GO_TEST_IMAGE == null'
when: never
- !reference [.test-policy, rules]
go-build-test:
extends: .go-base
stage: build
script:
- go_build
- go_test
coverage: '/^(\d+.\d+\%) covered$/'
artifacts:
name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
- $GO_PROJECT_DIR/bin/
- $GO_PROJECT_DIR/reports/
rules:
# if $GO_TEST_IMAGE not set
- if: '$GO_TEST_IMAGE == null'
go-ci-lint:
extends: .go-base
stage: build
image: $GO_CI_LINT_IMAGE
script:
- mkdir -p reports
# maybe produce Sonar lint report (not failing)
- sonar_lint_report || true
# console output report (failing)
- golangci-lint run $GO_CI_LINT_ARGS
artifacts:
name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
expire_in: 1 day
when: always
paths:
# exclude if GO_CI_LINT_DISABLED set
- if: '$GO_CI_LINT_DISABLED == "true"'
when: never
go-mod-outdated:
extends: .go-base
stage: test
dependencies: []
script:
- mkdir -p reports
- install_go_mod_outdated
- go $GO_LIST_ARGS | $GOBIN/go-mod-outdated $GO_MOD_OUTDATED_ARGS > reports/go-mod-outdated-report.md
artifacts:
name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
expire_in: 1 day
when: always
paths:
rules:
# on schedule: auto
- if: '$CI_PIPELINE_SOURCE == "schedule"'
allow_failure: true
# else manual & non-blocking
- when: manual
allow_failure: true