Skip to content
Snippets Groups Projects
Select Git revision
  • 1f997a18c61d7eb4fe8b9af1ee828893ec70929b
  • master default protected
  • 4
  • 4.3
  • 4.3.1
  • 4.3.0
  • 4.2
  • 4.2.0
  • 4.1
  • 4.1.0
  • 4.0
  • 4.0.2
  • 4.0.1
  • 4.0.0
  • 3.11.4
  • 3.11.3
  • 3.11.2
  • 3
  • 3.11
  • 3.11.1
  • 3.11.0
21 results

gitlab-ci-maven.yml

Blame
  • gitlab-ci-maven.yml 30.99 KiB
    # =========================================================================================
    # Copyright (C) 2021 Orange & contributors
    #
    # 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.
    # =========================================================================================
    spec:
      inputs:
        image:
          description: The Docker image used to run Maven - **set the version required by
            your project**
          default: registry.hub.docker.com/library/maven:latest
        project-dir:
          description: Maven project root directory
          default: .
        cfg-dir:
          description: The Maven configuration directory
          default: .m2
        settings-file:
          description: The Maven `settings.xml` file path
          default: $MAVEN_CFG_DIR/settings.xml
        opts:
          description: '[Global Maven options](http://maven.apache.org/configure.html#maven_opts-environment-variable)'
          default: >-
            -Dhttps.protocols=TLSv1.2
            -Dmaven.repo.local=${MAVEN_CFG_DIR}/repository
            -Dorg.slf4j.simpleLogger.showDateTime=true
            -Djava.awt.headless=true
        cli-opts:
          description: Additional [Maven options](https://maven.apache.org/ref/3-LATEST/maven-embedder/cli.html)
            used on the command line
          default: >-
            --no-transfer-progress
            --batch-mode
            --errors
            --fail-at-end
            --show-version
            -DinstallAtEnd=true
            -DdeployAtEnd=true
        build-args:
          description: Maven arguments for the build & test job
          default: org.jacoco:jacoco-maven-plugin:prepare-agent verify org.jacoco:jacoco-maven-plugin:report
        sonar-host-url:
          description: SonarQube server url
          default: ''
        sonar-base-args:
          description: SonarQube [analysis arguments](https://docs.sonarqube.org/latest/analysis/analysis-parameters/)
          default: >-
            sonar:sonar
            -Dsonar.links.homepage=${CI_PROJECT_URL}
            -Dsonar.links.ci=${CI_PROJECT_URL}/-/pipelines
            -Dsonar.links.issue=${CI_PROJECT_URL}/-/issues
        sonar-quality-gate-enabled:
          description: 'Enables SonarQube [Quality Gate](https://docs.sonarqube.org/latest/user-guide/quality-gates/)
            verification.
    
    
            _Uses `sonar.qualitygate.wait` parameter ([see doc](https://docs.sonarqube.org/latest/analysis/ci-integration-overview/#header-1))._'
          type: boolean
          default: false
        dependency-check-disabled:
          description: Disable Dependency-Check
          type: boolean
          default: false
        dependency-check-args:
          description: Maven arguments for Dependency Check job
          default: >-
            org.owasp:dependency-check-maven:aggregate
            -DretireJsAnalyzerEnabled=false
            -DassemblyAnalyzerEnabled=false
            -Dformats=HTML,JSON,XML
        mvn-forbid-snapshot-dependencies-disabled:
          description: Disable Snapshot dependencies verification
          type: boolean
          default: false
        sbom-disabled:
          description: Disable Software Bill of Materials
          type: boolean
          default: false
        sbom-gen-args:
          description: Maven command used for SBOM analysis
          default: org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom
        deploy-enabled:
          description: Enable Publish
          type: boolean
          default: false
        deploy-args:
          description: Maven arguments for the deploy job
          default: deploy -Dmaven.test.skip=true
        deploy-from-unprotected-disabled:
          description: Set to limit snapshot publication to protected branches
          type: boolean
          default: false
        deploy-snapshot-with-slug-enabled:
          description: Enable to inject the Git branch slug in SNAPSHOT versions
          type: boolean
          default: false
        release-args:
          description: Maven arguments for the release job
          default: release:prepare -DtagNameFormat=@{project.version} -Darguments=-Dmaven.test.skip=true
        release-version:
          description: 'Explicit version to use when triggering a release
            _Otherwise uses the current snapshot version from `pom.xml`)_'
          default: ''
        release-scm-comment-prefix:
          description: Maven release plugin [scmCommentPrefix](https://maven.apache.org/maven-release/maven-release-plugin/prepare-mojo.html#scmCommentPrefix)
            parameter
          default: 'chore(maven-release): '
        release-scm-release-comment:
          description: 'Maven release plugin [scmReleaseCommitComment](https://maven.apache.org/maven-release/maven-release-plugin/prepare-mojo.html#scmReleaseCommitComment)
            parameter
            (since Maven `3.0.0-M1`)'
          default: ''
        release-scm-dev-comment:
          description: 'Maven release plugin [scmDevelopmentCommitComment](https://maven.apache.org/maven-release/maven-release-plugin/prepare-mojo.html#scmDevelopmentCommitComment)
            parameter
            (since Maven `3.0.0-M1`)'
          default: ''
        mvn-semrel-release-disabled:
          description: Disable semantic-release integration
          default: ''
    ---
    # default workflow rules: Merge Request pipelines
    workflow:
      rules:
        # prevent MR pipeline originating from production or integration branch(es)
        - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ $PROD_REF || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ $INTEG_REF'
          when: never
        # on non-prod, non-integration branches: prefer MR pipeline over branch pipeline
        - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_COMMIT_REF_NAME !~ $PROD_REF && $CI_COMMIT_REF_NAME !~ $INTEG_REF'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*tag(,[^],]*)*\]/" && $CI_COMMIT_TAG'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*branch(,[^],]*)*\]/" && $CI_COMMIT_BRANCH'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*mr(,[^],]*)*\]/" && $CI_MERGE_REQUEST_ID'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*default(,[^],]*)*\]/" && $CI_COMMIT_REF_NAME =~ $CI_DEFAULT_BRANCH'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*prod(,[^],]*)*\]/" && $CI_COMMIT_REF_NAME =~ $PROD_REF'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*integ(,[^],]*)*\]/" && $CI_COMMIT_REF_NAME =~ $INTEG_REF'
          when: never
        - if: '$CI_COMMIT_MESSAGE =~ "/\[(ci skip|skip ci) on ([^],]*,)*dev(,[^],]*)*\]/" && $CI_COMMIT_REF_NAME !~ $PROD_REF && $CI_COMMIT_REF_NAME !~ $INTEG_REF'
          when: never
        - when: always
    
    # 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
    
    variables:
      # variabilized tracking image
      TBC_TRACKING_IMAGE: "registry.gitlab.com/to-be-continuous/tools/tracking:master"
    
      # Default Maven project root directory
      MAVEN_PROJECT_DIR: $[[ inputs.project-dir ]]
      # Maven image (can be overridden)
      MAVEN_IMAGE: $[[ inputs.image ]]
      # default production ref name (pattern)
      PROD_REF: '/^(master|main)$/'
      # default integration ref name (pattern)
      INTEG_REF: '/^develop$/'
      # default release tag name (pattern)
      RELEASE_REF: '/^v?[0-9]+\.[0-9]+\.[0-9]+$/'
      # default configuration directory
      MAVEN_CFG_DIR: $[[ inputs.cfg-dir ]]
      # default settings.xml file path
      MAVEN_SETTINGS_FILE: $[[ inputs.settings-file ]]
      # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
      MAVEN_OPTS: $[[ inputs.opts ]]
      # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
      # when running from the command line.
      # `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
      MAVEN_CLI_OPTS: $[[ inputs.cli-opts ]]
      # Maven build arguments
      MAVEN_BUILD_ARGS: $[[ inputs.build-args ]]
      # Sonar base analysis default args
      # see: https://docs.sonarqube.org/latest/analysis/analysis-parameters/
      # default uses branch analysis: https://docs.sonarqube.org/latest/branches/overview/
      SONAR_BASE_ARGS: $[[ inputs.sonar-base-args ]]
      # Dependency Check arguments
      MAVEN_DEPENDENCY_CHECK_ARGS: $[[ inputs.dependency-check-args ]]
      # SBOM genenration arguments
      MAVEN_SBOM_GEN_ARGS: $[[ inputs.sbom-gen-args ]]
      # Maven deploy arguments
      MAVEN_DEPLOY_ARGS: $[[ inputs.deploy-args ]]
      # Maven release arguments
      MAVEN_RELEASE_ARGS: $[[ inputs.release-args ]]
      MAVEN_RELEASE_SCM_COMMENT_PREFIX: $[[ inputs.release-scm-comment-prefix ]]
    
      SONAR_HOST_URL: $[[ inputs.sonar-host-url ]]
      SONAR_QUALITY_GATE_ENABLED: $[[ inputs.sonar-quality-gate-enabled ]]
      MAVEN_DEPENDENCY_CHECK_DISABLED: $[[ inputs.dependency-check-disabled ]]
      MVN_FORBID_SNAPSHOT_DEPENDENCIES_DISABLED: $[[ inputs.mvn-forbid-snapshot-dependencies-disabled ]]
      MAVEN_SBOM_DISABLED: $[[ inputs.sbom-disabled ]]
      MAVEN_DEPLOY_ENABLED: $[[ inputs.deploy-enabled ]]
      MAVEN_DEPLOY_FROM_UNPROTECTED_DISABLED: $[[ inputs.deploy-from-unprotected-disabled ]]
      MAVEN_DEPLOY_SNAPSHOT_WITH_SLUG_ENABLED: $[[ inputs.deploy-snapshot-with-slug-enabled ]]
      MAVEN_RELEASE_VERSION: $[[ inputs.release-version ]]
      MAVEN_RELEASE_SCM_RELEASE_COMMENT: $[[ inputs.release-scm-release-comment ]]
      MAVEN_RELEASE_SCM_DEV_COMMENT: $[[ inputs.release-scm-dev-comment ]]
      MVN_SEMREL_RELEASE_DISABLED: $[[ inputs.mvn-semrel-release-disabled ]]
    
    stages:
      - build
      - test
      - package-build
      - package-test
      - infra
      - deploy
      - acceptance
      - publish
      - infra-prod
      - production
    
    .mvn-scripts: &mvn-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() {
        jacoco_reports=$(find . -name "${JACOCO_CSV_REPORT:-jacoco.csv}")
    
        if [[ -n "$jacoco_reports" ]]
        then
          log_info "--- \\e[32mJaCoCo report(s) found\\e[0m (\\e[33;1m${jacoco_reports}\\e[0m): output"
          # shellcheck disable=SC2046,SC2086
          awk -F',' '{ instructions += $4 + $5; covered += $5 } END { print covered"/"instructions " instructions covered"; print 100*covered/instructions "% covered" }' $(find . -name "${JACOCO_CSV_REPORT:-jacoco.csv}")
        else
          log_info "--- \\e[32mJaCoCo report(s) not found\\e[0m: skip"
        fi
      }
    
      function maybe_set_version_from_git() {
        if [[ -n "$MAVEN_RELEASE_VERSION_FROM_GIT" ]]; then
          # compute version as timestamp of latest commit
          cur_version="$(date -d "$(git log -n 1 --pretty=format:%ci)" "+%Y.%m.%d.%H%M%S")"
    
          log_info "--- setting project version from latest git commit: \\e[33;1m${cur_version}\\e[0m"
    
          # change version in pom
          # shellcheck disable=SC2086
          mvn versions:set $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args -DgenerateBackupPoms=false -DnewVersion="$cur_version-SNAPSHOT"
    
          # git commit change
          git commit -am "[ci skip] Prepare release with $cur_version version"
        fi
      }
    
      function configure_scm_auth() {
        log_info "Preparing Git repository for release..."
        # set user info
        git config --global user.email "${GITLAB_USER_EMAIL}"
        git config --global user.name "${GITLAB_USER_NAME}"
        # shellcheck disable=SC2086
        scm_url=$(mvn $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args help:evaluate -Dexpression=project.scm.developerConnection -q -DforceStdout | tail -n 1)
        if [[ $scm_url == "scm:git:https"* ]]; then
          if [[ -n "${GIT_USERNAME}" ]] && [[ -n "${GIT_PASSWORD}" ]]; then
            log_info "--- using SCM credentials from env (\$GIT_USERNAME and \$GIT_PASSWORD)..."
            export scm_auth_args="-Dusername=${GIT_USERNAME} -Dpassword=${GIT_PASSWORD}"
          else
            log_error "--- project scm.developerConnection is using HTTPS protocol but no Git credentials are configured."
            log_error "--- Please specify the \$GIT_USERNAME and \$GIT_PASSWORD variables or change to SSH protocol with a SSH key."
          fi
        else
          # assume project is using SSH protocol
          if [[ -n "${GIT_PRIVATE_KEY}" ]]; then
            log_info "--- using Git SSH key from env (\$GIT_PRIVATE_KEY)..."
            mkdir -m 700 "${HOME}/.ssh"
            ssh-keyscan -H "${CI_SERVER_HOST}" >> ~/.ssh/known_hosts
            eval "$(ssh-agent -s)"
            # Handle file variable
            if [[ -f "${GIT_PRIVATE_KEY}" ]]; then
              tr -d '\r' < "${GIT_PRIVATE_KEY}" | ssh-add -
            else
              echo "${GIT_PRIVATE_KEY}" | tr -d '\r' | ssh-add -
            fi
          else
            log_error "--- project scm.developerConnection is using SSH protocol but no SSH key configured."
            log_error "--- Please either specify the \$GIT_PRIVATE_KEY variable or change to https protocol with username/password credentials."
          fi
        fi
      }
    
      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
    
        # import in Java keystore (if keytool command found)
        if command -v keytool > /dev/null
        then
          # shellcheck disable=SC2046
          javahome=${JAVA_HOME:-$(dirname $(readlink -f $(command -v java)))/..}
          # shellcheck disable=SC2086
          keystore=${JAVA_KEYSTORE_PATH:-$(ls -1 $javahome/jre/lib/security/cacerts 2>/dev/null || ls -1 $javahome/lib/security/cacerts 2>/dev/null || echo "")}
          if [[ -f "$keystore" ]]
          then
            storepass=${JAVA_KEYSTORE_PASSWORD:-changeit}
            nb_certs=$(echo "$certs" | grep -c 'END CERTIFICATE')
            log_info "importing $nb_certs certificates in Java keystore \\e[33;1m$keystore\\e[0m..."
            for idx in $(seq 0 $((nb_certs - 1)))
            do
              # TODO: use keytool option -trustcacerts ?
              if echo "$certs" | awk "n==$idx { print }; /END CERTIFICATE/ { n++ }" | keytool -noprompt -import -alias "imported CA Cert $idx" -keystore "$keystore" -storepass "$storepass"
              then
                log_info "... CA certificate [$idx] successfully imported"
              else
                log_warn "... Failed importing CA certificate [$idx]: abort"
                return
              fi
            done
          else
            log_warn "Java keystore \\e[33;1m$keystore\\e[0m not found: could not import CA certificates"
          fi
        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}__"
            _cmp_val=${_scoped_var#"$_cmp_val_prefix"}
            # 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; 
              fi
              ;;
            startswith*)
              if [[ -z "$_not" ]] && [[ "$_cond_val" != "$_cmp_val"* ]]; then continue; 
              elif [[ "$_not" ]] && [[ "$_cond_val" == "$_cmp_val"* ]]; then continue; 
              fi
              ;;
            endswith*)
              if [[ -z "$_not" ]] && [[ "$_cond_val" != *"$_cmp_val" ]]; then continue; 
              elif [[ "$_not" ]] && [[ "$_cond_val" == *"$_cmp_val" ]]; then continue; 
              fi
              ;;
            contains*)
              if [[ -z "$_not" ]] && [[ "$_cond_val" != *"$_cmp_val"* ]]; then continue; 
              elif [[ "$_not" ]] && [[ "$_cond_val" == *"$_cmp_val"* ]]; then continue; 
              fi
              ;;
            in*)
              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"
      }
      
      # evaluate and export a secret
      # - $1: secret variable name
      function eval_secret() {
        name=$1
        value=$(eval echo "\$${name}")
        case "$value" in
        @b64@*)
          decoded=$(mktemp)
          errors=$(mktemp)
          if echo "$value" | cut -c6- | base64 -d > "${decoded}" 2> "${errors}"
          then
            # shellcheck disable=SC2086
            export ${name}="$(cat ${decoded})"
            log_info "Successfully decoded base64 secret \\e[33;1m${name}\\e[0m"
          else
            fail "Failed decoding base64 secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
          fi
          ;;
        @hex@*)
          decoded=$(mktemp)
          errors=$(mktemp)
          if echo "$value" | cut -c6- | sed 's/\([0-9A-F]\{2\}\)/\\\\x\1/gI' | xargs printf > "${decoded}" 2> "${errors}"
          then
            # shellcheck disable=SC2086
            export ${name}="$(cat ${decoded})"
            log_info "Successfully decoded hexadecimal secret \\e[33;1m${name}\\e[0m"
          else
            fail "Failed decoding hexadecimal secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
          fi
          ;;
        @url@*)
          url=$(echo "$value" | cut -c6-)
          if command -v curl > /dev/null
          then
            decoded=$(mktemp)
            errors=$(mktemp)
            if curl -s -S -f --connect-timeout 5 -o "${decoded}" "$url" 2> "${errors}"
            then
              # shellcheck disable=SC2086
              export ${name}="$(cat ${decoded})"
              log_info "Successfully curl'd secret \\e[33;1m${name}\\e[0m"
            else
              log_warn "Failed getting secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
            fi
          elif command -v wget > /dev/null
          then
            decoded=$(mktemp)
            errors=$(mktemp)
            if wget -T 5 -O "${decoded}" "$url" 2> "${errors}"
            then
              # shellcheck disable=SC2086
              export ${name}="$(cat ${decoded})"
              log_info "Successfully wget'd secret \\e[33;1m${name}\\e[0m"
            else
              log_warn "Failed getting secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")"
            fi
          else
            fail "Couldn't get secret \\e[33;1m${name}\\e[0m: no http client found"
          fi
          ;;
        esac
      }
      
      function eval_all_secrets() {
        encoded_vars=$(env | grep -v '^scoped__' | awk -F '=' '/^[a-zA-Z0-9_]*=@(b64|hex|url)@/ {print $1}')
        for var in $encoded_vars
        do
          eval_secret "$var"
        done
      }
      
      # builds the Java proxy options from Linux env (http_proxy, https_proxy, ftp_proxy and no_proxy)
      function eval_java_proxy_args() {
        # transform no_proxy into Java stype nonProxyHosts
        nph=$(echo "${no_proxy:-$NO_PROXY}" | sed -e 's/\s*//g' -e 's/^\./*./' -e 's/,\./,*./g' -e 's/,/|/g')
        java_proxy_args="$(java_proto_proxy_args http "${http_proxy:-$HTTP_PROXY}" "$nph") $(java_proto_proxy_args https "${https_proxy:-$HTTPS_PROXY}" "$nph") $(java_proto_proxy_args ftp "${ftp_proxy:-$FTP_PROXY}" "$nph")"
        export java_proxy_args
        if [[ "$java_proxy_args" ]]
        then
          log_info "Using Java proxy options (from env):  \\e[33;1m$java_proxy_args\\e[0m"
        fi
      }
    
      function java_proto_proxy_args() {
        proto=$1
        proxy=$2
        non_proxy_hosts=$3
        if [[ "$proxy" ]]
        then
          host_port=$(echo "$proxy" | cut -d'/' -f3)
          host=$(echo "$host_port" | cut -d':' -f1)
          port=$(echo "$host_port" | cut -s -d':' -f2)
          proto_proxy_args="-D$proto.proxyHost=$host -D$proto.proxyPort=${port:-80}"
          if [[ "$non_proxy_hosts" ]]; then proto_proxy_args="$proto_proxy_args -D$proto.nonProxyHosts=$non_proxy_hosts"; fi
          echo "$proto_proxy_args"
        fi
      }
    
      # autodetects any Maven settings file in and builds the Java CLI option accordingly
      function eval_mvn_settings_opt() {
        if [[ -f "$MAVEN_SETTINGS_FILE" ]]
        then
          log_info "Maven settings file found: \\e[33;1m$MAVEN_SETTINGS_FILE\\e[0m"
          mvn_settings_opt="-s $MAVEN_SETTINGS_FILE"
        fi
      }
    
      function mvn_deploy() {
        # shellcheck disable=SC2086
        pom_version=$(mvn $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args help:evaluate -Dexpression=project.version -q -DforceStdout | tail -n 1)
        if [[ "$CI_COMMIT_TAG" ]] && [[ "$pom_version" =~ -SNAPSHOT$ ]]
        then
          log_warn "Can't publish SNAPSHOT version \\e[33;1m${pom_version}\\e[0m from tag: skip"
        elif [[ -z "$CI_COMMIT_TAG" ]] && [[ ! "$pom_version" =~ -SNAPSHOT$ ]]
        then
          log_warn "Can't publish non-SNAPSHOT version \\e[33;1m${pom_version}\\e[0m from branch: skip"
        else
          log_info "Publish version \\e[33;1m${pom_version}\\e[0m..."
          # shellcheck disable=SC2086
          mvn ${TRACE:+-X} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args $MAVEN_DEPLOY_ARGS
        fi
      }
    
      # only on a branch commit, with deploy and "SNAPSHOT with slug" enabled
      function maybe_inject_slug_in_version() {
        if [[ "$CI_COMMIT_BRANCH" ]] && [[ "$MAVEN_DEPLOY_ENABLED" == "true" ]] && [[ "$MAVEN_DEPLOY_SNAPSHOT_WITH_SLUG_ENABLED" == "true" ]]
        then
          # check if on non-prod branch
          prod_ref_expr=${PROD_REF#/}
          prod_ref_expr=${prod_ref_expr%/}
          if [[ ! "$CI_COMMIT_BRANCH" =~ $prod_ref_expr ]]
          then
            # shellcheck disable=SC2086
            pom_version=$(mvn $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args help:evaluate -Dexpression=project.version -q -DforceStdout | tail -n 1)
            altered_pom_version="${pom_version%-SNAPSHOT}-${CI_COMMIT_REF_SLUG}-SNAPSHOT"
            log_info "Inject branch slug into SNAPSHOT version: \\e[33;1m${altered_pom_version}\\e[0m..."
            # shellcheck disable=SC2086
            mvn $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args versions:set -DgenerateBackupPoms=false -DnewVersion="${altered_pom_version}"
          fi
        fi
      }
      
      unscope_variables
      eval_all_secrets
      
      # ENDSCRIPT
    
    # Generic maven job
    .mvn-base:
      image: $MAVEN_IMAGE
      services:
        - name: "$TBC_TRACKING_IMAGE"
          command: ["--service", "maven", "3.11.1"]
      before_script:
        - !reference [.mvn-scripts]
        - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
        - eval_java_proxy_args
        - eval_mvn_settings_opt
        - cd ${MAVEN_PROJECT_DIR}
      # Cache downloaded dependencies and plugins between builds.
      # To keep cache across branches add 'key: "$CI_JOB_NAME"'
      cache:
        key: "$CI_COMMIT_REF_SLUG-maven"
        paths:
          - "${MAVEN_PROJECT_DIR}/${MAVEN_CFG_DIR}/repository"
    
    mvn-build:
      extends: .mvn-base
      stage: build
      script:
        - maybe_inject_slug_in_version
        - mvn ${TRACE:+-X} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args $MAVEN_BUILD_ARGS
        - output_coverage
      # code coverage RegEx
      coverage: '/^(\d+\.?\d*\%) covered$/'
      # keep build artifacts and JUnit reports
      artifacts:
        name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
        expire_in: 1 day
        when: always
        reports:
          junit:
            - "${MAVEN_PROJECT_DIR}/**/target/*-reports/TEST-*.xml"
          coverage_report:
            coverage_format: jacoco
            path: "${MAVEN_PROJECT_DIR}/**/target/site/jacoco/jacoco.xml"
        paths:
          # version may have been altered
          - "${MAVEN_PROJECT_DIR}/**/pom.xml"
          - "${MAVEN_PROJECT_DIR}/**/target"
    
    # Sonar job
    mvn-sonar:
      stage: test
      extends: .mvn-base
      variables:
        # see: https://docs.sonarqube.org/latest/analysis/gitlab-integration/#header-4
        SONAR_USER_HOME: "${CI_PROJECT_DIR}/${MAVEN_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
        GIT_DEPTH: 0 # Tells git to fetch all the branches of the project, required by the analysis task
      cache:
        key: "$CI_COMMIT_REF_SLUG-maven-sonar"
        paths:
          - "${MAVEN_PROJECT_DIR}/${MAVEN_CFG_DIR}/repository"
          - "${MAVEN_PROJECT_DIR}/.sonar/cache"
      script:
        - |
          if [[ "$SONAR_URL" ]] && [[ -z "$SONAR_HOST_URL" ]]
          then 
            log_warn '$SONAR_URL variable defined: use $SONAR_HOST_URL instead (see doc)'
            export SONAR_HOST_URL="$SONAR_URL"
          fi
        - |
          if [[ "$SONAR_AUTH_TOKEN" ]] && [[ -z "$SONAR_TOKEN" ]]
          then 
            log_warn '$SONAR_AUTH_TOKEN variable defined: use $SONAR_TOKEN instead (see doc)'
            export SONAR_TOKEN="$SONAR_AUTH_TOKEN"
          fi
        - >-
          mvn ${TRACE:+-Dsonar.verbose=true} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args
          ${SONAR_LOGIN:+-Dsonar.login=$SONAR_LOGIN}
          ${SONAR_PASSWORD:+-Dsonar.password=$SONAR_PASSWORD}
          ${SONAR_QUALITY_GATE_ENABLED:+-Dsonar.qualitygate.wait=$SONAR_QUALITY_GATE_ENABLED}
          $SONAR_BASE_ARGS
      rules:
        # exclude if $SONAR_URL and $SONAR_HOST_URL not set
        - if: '($SONAR_HOST_URL == null || $SONAR_HOST_URL == "") && ($SONAR_URL == null || $SONAR_URL == "")'
          when: never
        - !reference [.test-policy, rules]
    
    mvn-dependency-check:
      extends: .mvn-base
      stage: test
      # force no dependency
      dependencies: []
      script:
        - mvn ${TRACE:+-X} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args $MAVEN_DEPENDENCY_CHECK_ARGS
      artifacts:
        name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
        expire_in: 1 day
        when: always
        paths:
          - "${MAVEN_PROJECT_DIR}/**/target/dependency-check-report.*"
      rules:
        # exclude if disable
        - if: '$MAVEN_DEPENDENCY_CHECK_DISABLED == "true"'
          when: never
        # on schedule: auto
        - if: '$CI_PIPELINE_SOURCE == "schedule"'
          allow_failure: true
          when: always
        # all other cases: manual & non-blocking
        - when: manual
          allow_failure: true
    
    mvn-no-snapshot-deps:
      extends: .mvn-base
      stage: test
      needs: []
      script:
        - mvn ${TRACE:+-X} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args enforcer:enforce -Denforcer.rules=requireReleaseDeps
      rules:
        # exclude if disabled
        - if: '$MVN_FORBID_SNAPSHOT_DEPENDENCIES_DISABLED == "true"'
          when: never
        - !reference [.test-policy, rules]
    
    mvn-sbom:
      extends: .mvn-base
      stage: test
      # force no dependency
      dependencies: []
      script:
        - mvn ${TRACE+-X} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args $MAVEN_SBOM_GEN_ARGS -DoutputName=mvn-sbom.cyclonedx -DoutputFormat=json
        - chmod a+r target/mvn-sbom.cyclonedx.json
      artifacts:
        name: "SBOM for Maven from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
        expire_in: 1 week
        when: always
        paths:
          - "${MAVEN_PROJECT_DIR}/target/mvn-sbom.cyclonedx.json"
        reports:
          cyclonedx: 
            - "${MAVEN_PROJECT_DIR}/target/mvn-sbom.cyclonedx.json"
      rules:
        # exclude if disabled
        - if: '$MAVEN_SBOM_DISABLED == "true"'
          when: never
        - !reference [.test-policy, rules]
    
    mvn-deploy-snapshot:
      extends: .mvn-base
      stage: publish
      script:
        - mvn_deploy
      rules:
        # exclude if $MAVEN_DEPLOY_ENABLED not set
        - if: '$MAVEN_DEPLOY_ENABLED != "true"'
          when: never
        # on tags: never
        - if: $CI_COMMIT_TAG
          when: never
        # exclude unprotected ref if disabled
        - if: '$MAVEN_DEPLOY_FROM_UNPROTECTED_DISABLED != "true" || $CI_COMMIT_REF_PROTECTED == "true"'
    
    mvn-deploy-release:
      extends: .mvn-base
      stage: publish
      script:
        - mvn_deploy
      rules:
        # exclude if $MAVEN_DEPLOY_ENABLED not set
        - if: '$MAVEN_DEPLOY_ENABLED != "true"'
          when: never
        # on tag with release pattern: auto
        - if: '$CI_COMMIT_TAG =~ $RELEASE_REF'
    
    mvn-release:
      extends: .mvn-base
      stage: publish
      before_script:
        - !reference [ .mvn-base, before_script ]
        - git checkout -B "$CI_COMMIT_REF_NAME"
      script:
        - configure_scm_auth
        - maybe_set_version_from_git
        - |
          if [ "${SEMREL_INFO_ON}" ] && [ "${MVN_SEMREL_RELEASE_DISABLED}" != "true" ]
          then
            if [ -z "${SEMREL_INFO_NEXT_VERSION}" ]
            then
              log_info "No next release version determined by semantic-release info: skip"
              exit 0
            else
              log_info "Use next release version determined by semantic-release info: \\e[1;94m${SEMREL_INFO_NEXT_VERSION}\\e[0m"
              semrel_args="-DreleaseVersion=${SEMREL_INFO_NEXT_VERSION}"
            fi
          fi
        - >-
          mvn ${TRACE:+-X} $MAVEN_CLI_OPTS $mvn_settings_opt $java_proxy_args
          ${scm_auth_args} ${semrel_args}
          ${MAVEN_RELEASE_VERSION:+-DreleaseVersion="$MAVEN_RELEASE_VERSION"}
          ${MAVEN_RELEASE_SCM_COMMENT_PREFIX:+-DscmCommentPrefix="$MAVEN_RELEASE_SCM_COMMENT_PREFIX"}
          ${MAVEN_RELEASE_SCM_RELEASE_COMMENT:+-DscmReleaseCommitComment="$MAVEN_RELEASE_SCM_RELEASE_COMMENT"}
          ${MAVEN_RELEASE_SCM_DEV_COMMENT:+-DscmDevelopmentCommitComment="$MAVEN_RELEASE_SCM_DEV_COMMENT"}
          ${MAVEN_RELEASE_ARGS}
      rules:
        # exclude if $MAVEN_DEPLOY_ENABLED not set
        - if: '$MAVEN_DEPLOY_ENABLED != "true"'
          when: never
        # on production or integration branch: manual, non blocking
        - if: '$CI_COMMIT_REF_NAME =~ $PROD_REF || $CI_COMMIT_REF_NAME =~ $INTEG_REF'
          when: manual
          allow_failure: true