diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..485dee64bcfb48793379b200a1afd14e85a8aaf4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..f40668c0e1a6b1fd49df9314ae163fd957378232 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,29 @@ +include: + - project: 'to-be-continuous/tools/gitlab-ci' + ref: 'master' + file: '/templates/extract.yml' + - project: 'to-be-continuous/tools/gitlab-ci' + ref: 'master' + file: '/templates/validation.yml' + - project: 'to-be-continuous/kicker' + ref: 'master' + file: '/templates/validation.yml' + - project: 'to-be-continuous/bash' + ref: '3.2' + file: 'templates/gitlab-ci-bash.yml' + - project: 'to-be-continuous/semantic-release' + ref: '3.6' + file: '/templates/gitlab-ci-semrel.yml' + +stages: + - build + - publish + +variables: + GITLAB_CI_FILES: "templates/gitlab-ci-renovate.yml" + BASH_SHELLCHECK_FILES: "*.sh" + +semantic-release: + rules: + # on production branch(es): auto if SEMREL_AUTO_RELEASE_ENABLED + - if: '$TMPL_RELEASE_ENABLED == "true" && $CI_COMMIT_REF_NAME =~ $PROD_REF' diff --git a/.gitlab/issue_templates/feature_request.md b/.gitlab/issue_templates/feature_request.md index b75185104c139e05d39a2a28db6ac9a20acbcc8b..666c5fb191c2f1ad04c45718ba64b6f4dd4762a6 100644 --- a/.gitlab/issue_templates/feature_request.md +++ b/.gitlab/issue_templates/feature_request.md @@ -5,7 +5,7 @@ ## Implementation ideas (If you have any implementation ideas, they can go here.) -(Any design change proposal could be also discussed on the _to be continuous_ Google Group: https://groups.google.com/g/tbc-dev.) +(Any design change proposal could be also discussed on the _to be continuous_ Discord server.) /label ~"kind/enhancement" ~"status/needs-investigation" diff --git a/.releaserc.yml b/.releaserc.yml new file mode 100644 index 0000000000000000000000000000000000000000..4360313a707b37d81d052068e6f2e41587172843 --- /dev/null +++ b/.releaserc.yml @@ -0,0 +1,22 @@ +plugins: [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/gitlab", + "@semantic-release/changelog", + [ + "@semantic-release/exec", + { + "prepareCmd": "./bumpversion.sh \"${lastRelease.version}\" \"${nextRelease.version}\" \"${nextRelease.type}\"", + "successCmd": "./post-release.sh \"${nextRelease.version}\"" + } + ], + [ + "@semantic-release/git", + { + "assets": ["*.md", "templates/*.yml"] + } + ] +] +branches: + - "master" +tagFormat: "${version}" \ No newline at end of file diff --git a/CONTRIBUTE.md b/CONTRIBUTING.md similarity index 97% rename from CONTRIBUTE.md rename to CONTRIBUTING.md index 5a24023bf705aac9821202c2ef30e06071db25e5..590000261af6cc09c99120f6ec802e87bba1460f 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTING.md @@ -5,6 +5,8 @@ We try to make it easy, and all contributions, even the smaller ones, are more t This includes bug reports, fixes, documentation, examples... But first, read this page (including the small print at the end). +Contributions are available on https://gitlab.com/to-be-continuous/renovate + ## Legal All original contributions to _to be continuous_ are licensed under the diff --git a/README.md b/README.md index 2821555683fa462a5c81a13d6712091d86e899fa..779799b7724dbef1757a92f9da42e6a5b571765f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,39 @@ -# GitLab CI template Skeleton +# GitLab CI template for Renovate -This is a skeleton project for starting a new _to be continuous_ template. +Automate your dependency updates with [Renovate](https://www.mend.io/renovate/). -You shall fork it when you want to start developing a new template. +## Usage -Based on the kind of template (build, analyse, hosting, acceptance, ...), you should start working from one of the available `initial-xxx` branches, that each implement basic stuff. +In order to include this template in your project, add the following to your `gitlab-ci.yml`: +```yaml +include: + - project: 'to-be-continuous/renovate' + ref: '1.0.0' + file: '/templates/gitlab-ci-renovate.yml' +``` + +## Configuration + +The Renovate template uses some global configuration used throughout all jobs. + +| Name | description | default value | +|------------------------|---------------------------------------------------------------------------------|-------------------| +| `RENOVATE_IMAGE` | The Docker image used to run Renovate | `registry.hub.docker.com/renovate/renovate:latest` | +| :lock: `RENOVATE_TOKEN`| A GitLab access token to allow Renovate crawl your projects. [See doc](https://docs.renovatebot.com/modules/platform/gitlab/#authentication) | _none_ | +| :lock: `GITHUB_COM_TOKEN`| A GitHub access token to allow Renovate fetch changelogs. [See doc](https://docs.renovatebot.com/getting-started/running/#githubcom-token-for-changelogs) | _none_ | + +This template will help you using [Renovate](https://www.mend.io/renovate/) from a GitLab project to +automate your dependency updates within your groups or projects. +On the contrary to other to-be-continuous templates, this one should be used in a separate project that +will be in charge of crawling all your other projects. + +Upon including the template, carefuly follow [Renovate's documentation](https://docs.renovatebot.com/) to +configure the bot accordingly. Pay attention to the following: + +* Remember to set the [platform](https://docs.renovatebot.com/self-hosted-configuration/#platform) parameter + to `gitlab` in your configuration. +* [GitLab platform integration](https://docs.renovatebot.com/modules/platform/gitlab/) requires that you + declare a `RENOVATE_TOKEN` variable with an access token. +* You'll also probaly need to declare a `GITHUB_COM_TOKEN` variable, holding a GitHub access token + (for [fetching changelogs](https://docs.renovatebot.com/getting-started/running/#githubcom-token-for-changelogs)) diff --git a/Renovate.r2.yml b/Renovate.r2.yml new file mode 100644 index 0000000000000000000000000000000000000000..c9abda55a0313a1f0b723d23ee094f97a20b2c81 --- /dev/null +++ b/Renovate.r2.yml @@ -0,0 +1,13 @@ +files: + template: ./templates/gitlab-ci-renovate.yml + documentation: ./README.md + changelog: ./CHANGELOG.md +data: + description: "Automate your dependency updates with Renovate" + public: true + labels: + - to be continuous + - Renovate + - Dependency Updates + license: LGPL v3 + deprecated: false \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..7829ff13716e85ffb1dfd13503b2093b1ee93235 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +## Supported Versions + +Security fixes and updates are only applied to the latest released version. So always try to be up to date. + +## Reporting a Vulnerability + +In order to minimize risks of attack while investigating and fixing the issue, any vulnerability shall be reported by +opening a [**confidential** issue on gitlab.com](https://gitlab.com/to-be-continuous/renovate/-/issues/new?issue[confidential]=true&issue[description]=%28type+in+the+vulnerability+details+here%29%0A%0A%2Flabel%20~%22kind%3A%3Avulnerability%22). + +Follow-up and fixing will be made on a _best effort_ basis. + +If you have doubts about a potential vulnerability, please reach out one of the maintainers on Discord. diff --git a/bumpversion.sh b/bumpversion.sh new file mode 100755 index 0000000000000000000000000000000000000000..f06829a406ca8da98e570e8ad7d8bb22367b668d --- /dev/null +++ b/bumpversion.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +function log_info() { + >&2 echo -e "[\\e[1;94mINFO\\e[0m] $*" +} + +function log_warn() { + >&2 echo -e "[\\e[1;93mWARN\\e[0m] $*" +} + +function log_error() { + >&2 echo -e "[\\e[1;91mERROR\\e[0m] $*" +} + +# check number of arguments +if [[ "$#" -le 2 ]]; then + log_error "Missing arguments" + log_error "Usage: $0 <current version> <next version>" + exit 1 +fi + +curVer=$1 +nextVer=$2 +relType=$3 + +if [[ "$curVer" ]]; then + log_info "Bump version from \\e[33;1m${curVer}\\e[0m to \\e[33;1m${nextVer}\\e[0m (release type: $relType)..." + + # replace in README + sed -e "s/ref: '$curVer'/ref: '$nextVer'/" README.md > README.md.next + mv -f README.md.next README.md + + # replace in template and variants + for tmpl in templates/*.yml + do + sed -e "s/\"$curVer\"/\"$nextVer\"/" "$tmpl" > "$tmpl.next" + mv -f "$tmpl.next" "$tmpl" + done +else + log_info "Bump version to \\e[33;1m${nextVer}\\e[0m (release type: $relType): this is the first release (skip)..." +fi diff --git a/kicker.json b/kicker.json new file mode 100644 index 0000000000000000000000000000000000000000..c1cb213f22bdc5b9ee3c2fbc69b09297b17165bb --- /dev/null +++ b/kicker.json @@ -0,0 +1,23 @@ +{ + "name": "Renovate", + "description": "Automate your dependency updates with [Renovate](https://www.mend.io/renovate/)", + "template_path": "templates/gitlab-ci-renovate.yml", + "kind": "misc", + "variables": [ + { + "name": "RENOVATE_IMAGE", + "description": "The Docker image used to run Renovate", + "default": "registry.hub.docker.com/renovate/renovate:latest" + }, + { + "name": "RENOVATE_TOKEN", + "description": "A GitLab access token to allow Renovate crawl your projects. [See doc](https://docs.renovatebot.com/modules/platform/gitlab/#authentication)", + "secret": true + }, + { + "name": "GITHUB_COM_TOKEN", + "description": "A GitHub access token to allow Renovate fetch changelogs. [See doc](https://docs.renovatebot.com/getting-started/running/#githubcom-token-for-changelogs)", + "secret": true + } + ] +} diff --git a/logo.png b/logo.png index 1d49ae4c533c608703bafbc7d380cac36a97fc9f..70753a173ed09089752a12e8a2006dc8dfc927c2 100644 Binary files a/logo.png and b/logo.png differ diff --git a/post-release.sh b/post-release.sh new file mode 100755 index 0000000000000000000000000000000000000000..6d197865fe21a418897824f758d127af2b59ef98 --- /dev/null +++ b/post-release.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +function log_info() { + >&2 echo -e "[\\e[1;94mINFO\\e[0m] $*" +} + +function log_warn() { + >&2 echo -e "[\\e[1;93mWARN\\e[0m] $*" +} + +function log_error() { + >&2 echo -e "[\\e[1;91mERROR\\e[0m] $*" +} + +# check number of arguments +if [[ "$#" -lt 1 ]]; then + log_error "Missing arguments" + log_error "Usage: $0 <next version>" + exit 1 +fi + +nextVer=$1 +minorVer=${nextVer%\.[0-9]*} +majorVer=${nextVer%\.[0-9]*\.[0-9]*} + +log_info "Creating minor version tag alias \\e[33;1m${minorVer}\\e[0m from $nextVer..." +git tag --force -a "$minorVer" "$nextVer" -m "Minor version alias (targets $nextVer)" + +log_info "Creating major version tag alias \\e[33;1m${majorVer}\\e[0m from $nextVer..." +git tag --force -a "$majorVer" "$nextVer" -m "Major version alias (targets $nextVer)" + +log_info "Pushing tags..." +git_base_url=$(echo "$CI_REPOSITORY_URL" | cut -d\@ -f2) +git_auth_url="https://token:${GITLAB_TOKEN}@${git_base_url}" +git push --tags --force "$git_auth_url" diff --git a/templates/gitlab-ci-renovate.yml b/templates/gitlab-ci-renovate.yml new file mode 100644 index 0000000000000000000000000000000000000000..fb607937959b4659bb776282b8c0480a4c91604c --- /dev/null +++ b/templates/gitlab-ci-renovate.yml @@ -0,0 +1,265 @@ +# ========================================================================================= +# 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. +# ========================================================================================= +# default workflow rules: Merge Request pipelines +workflow: + rules: + # prevent branch pipeline when an MR is open (prefer MR pipeline) + - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' + 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 + + +variables: + # variabilized tracking image + TBC_TRACKING_IMAGE: "$CI_REGISTRY/to-be-continuous/tools/tracking:master" + + RENOVATE_IMAGE: "registry.hub.docker.com/renovate/renovate:latest" + RENOVATE_ENDPOINT: $CI_API_V4_URL + + RENOVATE_LOG_FILE: renovate-log.ndjson + RENOVATE_AUTODISCOVER_FILTER: '${CI_PROJECT_ROOT_NAMESPACE}/**' + RENOVATE_BINARY_SOURCE: install + RENOVATE_LOG_FILE_LEVEL: debug + LOG_LEVEL: info + +.renovate-scripts: &renovate-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 assert_defined() { + if [[ -z "$1" ]] + then + log_error "$2" + exit 1 + fi + } + + function install_ca_certs() { + certs=$1 + if [[ -z "$certs" ]] + then + return + fi + + if [[ ! "$(whoami)" == "root" ]] + then + log_warn "can't install custom CA certificates (not root user); make sure to handle it" + return + fi + + # List of typical bundles + bundles="/etc/ssl/certs/ca-certificates.crt" # Debian/Ubuntu/Gentoo etc. + bundles="${bundles} /etc/ssl/cert.pem" # Alpine Linux + bundles="${bundles} /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" # CentOS/RHEL 7 + bundles="${bundles} /etc/pki/tls/certs/ca-bundle.crt" # Fedora/RHEL 6 + bundles="${bundles} /etc/ssl/ca-bundle.pem" # OpenSUSE + bundles="${bundles} /etc/pki/tls/cacert.pem" # OpenELEC + + # Try to find the right bundle to update it with custom CA certificates + for bundle in ${bundles} + do + # import if bundle exists + if [[ -f "${bundle}" ]] + then + # Import certificates in bundle + echo "${certs}" | tr -d '\r' >> "${bundle}" + + log_info "Custom CA certificates imported in \\e[33;1m${bundle}\\e[0m" + ca_imported=1 + break + fi + done + + if [[ -z "$ca_imported" ]] + then + log_warn "Could not import custom CA certificates !" + 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" + } + + unscope_variables + + # ENDSCRIPT + +stages: + - build + - test + - package-build + - package-test + - infra + - deploy + - acceptance + - publish + - infra-prod + - production + +.renovate-base: + image: $RENOVATE_IMAGE + services: + - name: "$TBC_TRACKING_IMAGE" + command: ["--service", "renovate", "1.0.0" ] + variables: + RENOVATE_BASE_DIR: $CI_PROJECT_DIR + RENOVATE_CACHE_DIR: $CI_PROJECT_DIR/.cache/renovate + # Cache downloaded dependencies and plugins between builds. + # To keep cache across branches add 'key: "$CI_JOB_NAME"' + # TODO (if necessary): define cache policy here + cache: + key: ${CI_COMMIT_REF_SLUG}-renovate + paths: + - .cache/renovate/** + before_script: + - *renovate-scripts + - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}" + +# (example) validator job +renovate-validator: + extends: .renovate-base + stage: build + # force no dependency + dependencies: [] + script: + - renovate-config-validator + +# dependency check job: on manual or schedule (dry-run otherwise) +renovate-depcheck: + extends: .renovate-base + stage: test + # force no dependency + dependencies: [] + variables: + # dry-run by default + RENOVATE_DRY_RUN: "true" + script: + - renovate $RENOVATE_EXTRA_FLAGS + artifacts: + when: always + expire_in: 1d + paths: + - "$RENOVATE_LOG_FILE" + rules: + # not dry run on manual or schedule + - if: '$CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"' + variables: + RENOVATE_DRY_RUN: "false" + - if: $RENOVATE_TOKEN