# GitLab CI template for semantic-release This project implements a GitLab CI/CD template to automate your versioning and release management with [semantic-release](https://github.com/semantic-release/semantic-release), supporting one or several of the following features: * determine the next release version number, * generate the changelog, * commit any changed resource to the Git repository, * create and push the Git tag, * publish the packages (in [GitLab](https://docs.gitlab.com/ee/user/project/releases/index.html) or any other package repository of your choice), * any additional custom behavior you are able to script, triggered on the [release steps](https://semantic-release.gitbook.io/semantic-release/#release-steps). ## Usage This template can be used both as a [CI/CD component](https://docs.gitlab.com/ee/ci/components/#use-a-component-in-a-cicd-configuration) or using the legacy [`include:project`](https://docs.gitlab.com/ee/ci/yaml/index.html#includeproject) syntax. ### Use as a CI/CD component Add the following to your `gitlab-ci.yml`: ```yaml include: # 1: include the component - component: gitlab.com/to-be-continuous/semantic-release/gitlab-ci-semrel@3.9.1 # 2: set/override component inputs inputs: changelog-enabled: true # ⚠ this is only an example ``` ### Use as a CI/CD template (legacy) Add the following to your `gitlab-ci.yml`: ```yaml include: # 1: include the template - project: 'to-be-continuous/semantic-release' ref: '3.9.1' file: '/templates/gitlab-ci-semrel.yml' variables: # 2: set/override template variables SEMREL_CHANGELOG_ENABLED: "true" # ⚠ this is only an example ``` ## Global configuration The semantic-release template uses some global configuration used throughout all jobs. | Input / Variable | Description | Default value | | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | | `image` / `SEMREL_IMAGE` | The Docker image used to run semantic-release | `registry.hub.docker.com/library/node:lts-slim` | | `version` / `SEMREL_VERSION` | The [semantic-release](https://www.npmjs.com/package/semantic-release) version to use | `latest` | | `exec-version` / `SEMREL_EXEC_VERSION` | The [@semantic-release/exec](https://www.npmjs.com/package/@semantic-release/exec) version to use | `latest` | | :lock: `GITLAB_TOKEN` | A GitLab [project access token](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html) or [personal access token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) with `api`, `read_repository` and `write repository` scopes. :warning: This variable is **mandatory** and [defined by `semantic-release`](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/ci-configuration.md#push-access-to-the-remote-repository) itself. | _none_ | | :lock: `GIT_AUTHOR_EMAIL` | A Git author email address associated with the `GITLAB_TOKEN` [bot user](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#bot-users-for-projects). This is [defined by `semantic-release`](https://semantic-release.gitbook.io/semantic-release/usage/configuration#git-environment-variables) itself, and **required if** the [verify-user push rules](https://docs.gitlab.com/ee/user/project/repository/push_rules.html#verify-users) enabled for the project | _none_ | | :lock: `GIT_COMMITTER_EMAIL` | A Git committer email address associated with the `GITLAB_TOKEN` [bot user](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#bot-users-for-projects). This is [defined by `semantic-release`](https://semantic-release.gitbook.io/semantic-release/usage/configuration#git-environment-variables) itself, and **required if** the [verify-user push rules](https://docs.gitlab.com/ee/user/project/repository/push_rules.html#verify-users) enabled for the project | _none_ | | `config-dir` / `SEMREL_CONFIG_DIR` | directory containing your [semantic-release configuration](https://semantic-release.gitbook.io/semantic-release/usage/configuration#configuration-file) | `.` | | `required-plugins-file` / `SEMREL_REQUIRED_PLUGINS_FILE` | An optional file for additional npm packages to install | `semrel-required-plugins.txt` | Jobs will extract required plugin packages from discovered configuration. If your configuration needs additional packages, add them, one per line, to `SEMREL_REQUIRED_PLUGINS_FILE` file. Each line must be a valid `npm install` package argument. ## Jobs ### `semantic-release` job This job runs `semantic-release` in `ci` mode. :warning: This template supports all [semantic-release configuration files](https://semantic-release.gitbook.io/semantic-release/usage/configuration#configuration-file) __except for__ `release.config.js` and custom CLI arguments. If no configuration is found, the template will generate one with the following options: * `debug`: `true` if the `$TRACE` variable is set, `false` otherwise * `dryRun`: `true` if the `$SEMREL_DRY_RUN` variable is set, `false` otherwise * `tagFormat`: see `$SEMREL_TAG_FORMAT` variable * `plugins`: * [@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer) * [@semantic-release/release-notes-generator](https://github.com/semantic-release/release-notes-generator) * [@semantic-release/gitlab](https://github.com/semantic-release/gitlab) * [@semantic-release/git](https://github.com/semantic-release/git) * optional [@semantic-release/changelog](https://github.com/semantic-release/changelog) if `SEMREL_CHANGELOG_ENABLED` is set to `true` * optional [@semantic-release/exec](https://github.com/semantic-release/exec) if any hook script is found (see [hook scripts](#hook_scripts)) * `branches`: `master` #### Variables As specified in the previous chapter, these variables are only used to generated a `.releaserc` when no configuration is found in the repository. | Input / Variable | Description | Default value | | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `changelog-enabled` / `SEMREL_CHANGELOG_ENABLED` | Add the [@semantic-release/changelog](https://github.com/semantic-release/changelog) plugin which will commit a changelog file in the repository if set to `true`. | _none_ | | `changelog-file` / `SEMREL_CHANGELOG_FILE` | [changelogFile @semantic-release/changelog option](https://github.com/semantic-release/changelog#options). | _none_ (use the plugin default value which is `CHANGELOG.md`). | | `changelog-title` / `SEMREL_CHANGELOG_TITLE` | [changelogTitle @semantic-release/changelog option](https://github.com/semantic-release/changelog#options). You might want to use markdown format (for example `# MyApp Changelog`). | _none_ | | `dry-run` / `SEMREL_DRY_RUN` | Activate the [dryRun semantic-release option](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#dryrun) if present. | _none_ | | `auto-release-enabled` / `SEMREL_AUTO_RELEASE_ENABLED` | When set to `true` the job start automatically. When not set (default), the job is manual. | _none_ | | `branches-ref` / `SEMREL_BRANCHES_REF` | Regular expression pattern matching branches from which releases should happen (should match your [semantic-release configuration](https://semantic-release.gitbook.io/semantic-release/usage/configuration#branches)) | `/^(master|main)$/` | | `tag-format` / `SEMREL_TAG_FORMAT` | [tagFormat semantic-release option](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/configuration.md#tagformat). :warning: don't forget to double the `$` character so it is not interpreted by GitLab. | `$${version}` | | `hooks-dir` / `SEMREL_HOOKS_DIR` | [Hook scripts](#hook_scripts) folder. | `.` | | `commit-message` / `SEMREL_COMMIT_MESSAGE` | Add a custom commit message based on [semantic-release/git option](https://github.com/semantic-release/git#message). | _none_ (uses semantic-release default commit message) | | `release-disabled` / `SEMREL_RELEASE_DISABLED` | Disable this job. | _none_ | #### Hook scripts The generated `.releaserc` will include the [@semantic-release/exec](https://github.com/semantic-release/exec) plugin if any of the following scripts is found in the `$SEMREL_HOOKS_DIR` folder: ##### verify-conditions.sh See [exec verifyConditionsCmd](https://github.com/semantic-release/exec#verifyconditionscmd). Parameters: _none_ ##### verify-release.sh See [exec verifyReleaseCmd](https://github.com/semantic-release/exec#verifyreleasecmd). Parameters: 1. Last release version 2. next release version 3. next release type ##### prepare.sh See [exec prepareCmd](https://github.com/semantic-release/exec#preparecmd). Parameters: 1. Last release version 2. next release version 3. next release type ##### publish.sh See [exec publishCmd](https://github.com/semantic-release/exec#publishcmd). Parameters: 1. Last release version 2. next release version 3. release branch 4. commits count 5. current date ##### success.sh See [exec successCmd](https://github.com/semantic-release/exec#successcmd). Parameters: 1. Last release version 2. next release version ##### fail.sh See [exec failcmd](https://github.com/semantic-release/exec#failcmd). Parameters: 1. Last release version 2. next release version #### Signing release commits with GPG For an introduction on commit signing, see [GitLab documentation](https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/). To make semantic-release sign its commits, use the following variable. | Input / Variable | Description | Default value | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | :lock: `SEMREL_GPG_SIGNKEY` | Path to the GPG signkey exported with `gpg --armor --export-secret-key`<br/>:warning: Declare as a masked [project variable of File type](https://docs.gitlab.com/ee/ci/variables/#cicd-variable-types). | _none_ | ### `semantic-release-info` job This job (disabled by default) runs `semantic-release` with `dry-run` mode in `.pre` stage to save the following variables as [dotenv artifact](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html#artifactsreportsdotenv) making them available for the next pipeline stages: * `SEMREL_INFO_LAST_VERSION`: latest released version * `SEMREL_INFO_NEXT_VERSION`: next release version (based on actual commits and comments) * `SEMREL_INFO_NEXT_VERSION_TYPE`: next release type (`major`|`minor`|`patch`) :warning: `SEMREL_INFO_NEXT_VERSION` and `SEMREL_INFO_NEXT_VERSION_TYPE` **wont** be available when semantic-release commits analysis determine that no release will be performed. This job can be enabled by defining the `SEMREL_INFO_ON` variable: * `prod` to enable on production branch only (`main` or `master` by default with `PROD_REF` environment variable) * `branches-ref` to enable on branches associated with `branches-ref` component configuration or `SEMREL_BRANCHES_REF` environment variable (`main` or `master` by default as it fallbacks on `PROD_REF` environment variable). * `protected` to enable on protected references * `all` to enable on all Git references. :warning: Beware that this job requires the `GITLAB_TOKEN` variable so you must unprotect it (this will make privilege escalation possible from developer to maintainer). ## Secrets management Here are some advices about your **secrets** (variables marked with a :lock:): 1. Manage them as [project or group CI/CD variables](https://docs.gitlab.com/ee/ci/variables/#add-a-cicd-variable-to-a-project): * [**masked**](https://docs.gitlab.com/ee/ci/variables/#mask-a-cicd-variable) to prevent them from being inadvertently displayed in your job logs, * [**protected**](https://docs.gitlab.com/ee/ci/variables/#protected-cicd-variables) if you want to secure some secrets you don't want everyone in the project to have access to (for instance production secrets). 2. In case a secret contains [characters that prevent it from being masked](https://docs.gitlab.com/ee/ci/variables/#mask-a-cicd-variable), simply define its value as the [Base64](https://en.wikipedia.org/wiki/Base64) encoded value prefixed with `@b64@`: it will then be possible to mask it and the template will automatically decode it prior to using it. 3. Don't forget to escape special characters (ex: `$` -> `$$`). 4. You can also manage secrets using Vault variant ## Variants The Docker template can be used in conjunction with template variants to cover specific cases. ### Vault variant This variant allows delegating your secrets management to a [Vault](https://www.vaultproject.io/) server. #### Configuration In order to be able to communicate with the Vault server, the variant requires the additional configuration parameters: | Input / Variable | Description | Default value | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | `TBC_VAULT_IMAGE` | The [Vault Secrets Provider](https://gitlab.com/to-be-continuous/tools/vault-secrets-provider) image to use (can be overridden) | `registry.gitlab.com/to-be-continuous/tools/vault-secrets-provider:latest` | | `vault-base-url` / `VAULT_BASE_URL` | The Vault server base API url | _none_ | | `vault-oidc-aud` / `VAULT_OIDC_AUD` | The `aud` claim for the JWT | `$CI_SERVER_URL` | | :lock: `VAULT_ROLE_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) RoleID | **must be defined** | | :lock: `VAULT_SECRET_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID | **must be defined** | #### Usage Then you may retrieve any of your secret(s) from Vault using the following syntax: ```text @url@http://vault-secrets-provider/api/secrets/{secret_path}?field={field} ``` With: | Parameter | Description | | -------------------------------- | --------------------------------------------------------------------- | | `secret_path` (_path parameter_) | this is your secret location in the Vault server | | `field` (_query parameter_) | parameter to access a single basic field from the secret JSON payload | #### Example ```yaml include: # main template - component: gitlab.com/to-be-continuous/semantic-release/gitlab-ci-semrel@3.9.1 # Vault variant - component: gitlab.com/to-be-continuous/semantic-release/gitlab-ci-semrel-vault@3.9.1 inputs: vault-base-url: "https://vault.acme.host/v1" # audience claim for JWT vault-oidc-aud: "https://vault.acme.host" variables: # Secrets managed by Vault GITLAB_TOKEN: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/semantic-release/token?field=group-access-token" # $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable ```