Newer
Older
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).
This template can be used both as a [CI/CD component](https://docs.gitlab.com/ee/ci/components/#use-a-component) or using the legacy [`include:project`](https://docs.gitlab.com/ee/ci/yaml/index.html#includeproject) syntax.
Add the following to your `.gitlab-ci.yml`:
```yaml
include:
# 1: include the component
- component: $CI_SERVER_FQDN/to-be-continuous/semantic-release/gitlab-ci-semrel@3.11.5
# 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`:
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.
Kilian PAQUIER
committed
| Input / Variable | Description | Default value |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `image` / `SEMREL_IMAGE` | The Docker image used to run semantic-release | `registry.hub.docker.com/library/node:lts-slim` |
Kilian PAQUIER
committed
| `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.
Kilian PAQUIER
committed
| 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)$/` |
Kilian PAQUIER
committed
| `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_ |
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
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
156
157
158
159
160
#### 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/signed_commits/gpg.html).
To make semantic-release sign its commits, use the following variable.
Kilian PAQUIER
committed
| 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_ |
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/yaml/artifacts_reports.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:
Kilian PAQUIER
committed
* `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).
Timothy Stone
committed
#### Semantic Release Commit Analyzer and Release Notes Configuration
Semantic Release determines the semantic version, major.minor.patch, with the use of `@semantic-release/commit-analyzer` and `@semantic-release/release-notes-generator` `presets`. The [**default** preset is `angular`](https://github.com/semantic-release/semantic-release?tab=readme-ov-file#commit-message-format).
The default _may_ lead to unexpected versioning or release notes, especially when not in an Angular project nor using the Angular standard.
The commit message parser may be changed by defining the `commit-spec` / `SEMREL_COMMIT_SPEC` variable:
| Input / Variable | Description | Default |
| ------------------------------------------- | ----------- | ------- |
| `commit-spec` / `SEMREL_COMMIT_SPEC` | commit specification `preset` (possible values: `angular`, `codemirror`, `ember`, `eslint`, `express`, `jquery`, `jshint`, `conventionalcommits` (`cc` short form supported)) | `angular` |
**Conventional Commit Specification**
The `preset` of `conventionalcommits` (or `cc`) is a good option for most users. The [specification is well defined and documented](https://www.conventionalcommits.org/en/v1.0.0/) and compatible with tools like [Husky](https://typicode.github.io/husky/) and [commitlint](https://commitlint.js.org/). Semantic Release has plans to make `conventionalcommits` the default in the future.
**Commit Message Controls**
The `commit-spec` / `SEMREL_COMMIT_SPEC` value installs the parser requirement for Semantic Release only. Adherence to a specification with commit message controls is not provided. Angular and Conventional Commits are widely supported by commitlint and [commitizen](https://github.com/commitizen), though additional `devDependencies` and configuration files may be required, please review the tooling documentation for more information.
**Note on supporting Semantic Release versions**
If the version of Semantic Release is pinned using [`SEMREL_VERSION`](#global-configuration) prior to v24, automated versioning via commit messaging may fail in unexpected ways. See [conventional-changelog-conventionalcommits v8.0.0 breaks semantic release](https://github.com/semantic-release/release-notes-generator/issues/633) or consider upgrading the pinned version to v24 or better to restore behaviors.
## 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/#for-a-project):
* [**masked**](https://docs.gitlab.com/ee/ci/variables/#mask-a-cicd-variable) to prevent them from being inadvertently
* [**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
Pierre Smeyers
committed
## Using semantic-release with other to-be-continuous templates
The semantic-release template has been designed to interoperate gracefully with release-capable to-be-continuous templates.
Unfortunaltely, there isn't one single configuration that fits all needs.
Pierre Smeyers
committed
Instead, the semantic-release template configuration will have to be adapted to your case.
There are actually 2 questions that will determine the required configuration:
1. which [Delivery Mode](https://to-be-continuous.gitlab.io/doc/understand/#delivery-modes) are you using in your project?
* _Application Deployment_ mode should trigger the release directly from your production branch (`main` or `master` by default),
* _Software Distribution_ mode should trigger the release through a tag pipeline.
2. does the release involve **changing files in your repository** (and therefore creating a Git commit)?
* that will be the case if you use plugins such as [@semantic-release/changelog](https://github.com/semantic-release/changelog) or [semantic-release-replace](https://github.com/jpoehnelt/semantic-release-replace-plugin)
Pierre Smeyers
committed
### Case 1: _Application Deployment_ mode
When using the _Application Deployment_ delivery mode in your project, the release should be triggered directly from the production branch (`main` or `master` by default).
Pierre Smeyers
committed
In that case you'll need to:
1. enable the [semantic-release info job](#semantic-release-info-job),<br/>
_this is the job that will provide next release information to the other template(s)_
* by setting `info-on` input / `SEMREL_INFO_ON` variable to `prod` (or any suitable non-empty value)
2. disable the [semantic-release job](#semantic-release-job),<br/>
_the release will be handled by other template(s) directly from the production branch_
* by setting `release-disabled` input / `SEMREL_RELEASE_DISABLED` variable to `true`
Pierre Smeyers
committed
3. make sure the other template(s) provide a semantic-release integration to perform the release from the semantic-release info job.
Templates supporting it:
* Docker,
* Helm,
* Maven,
* Python,
* S2I.
Pierre Smeyers
committed
### Case 2: _Software Distribution_ mode without any Git commit
This is the easiest case as nothing specific has to be done to address it:
* by default the semantic-release will analyse each commit on your production branch, and will possibly create a Git tag (but no Git commit) if it determined a release has to be performed,
* the Git tag will trigger a tag pipeline during which every to-be-continuous template will take care of publishing its versioned package (using the Git tag as the version) to an appropriate packages repository.
### Case 3: _Software Distribution_ mode with a Git commit
This case will occur if you configure semantic-release to modify one or several files in your Git repository (ex: `pom.xml`, `pyproject.toml`, `CHANGELOG.md`, `README.md`...)
In that case, when semantic-release determines a release is required, it will:
* modify the files,
* create a Git commit with the changes,
* create a Git tag with the next release version,
* push the commit + the tag.
Problem: by default, semantic-release creates a Git commit with comment `chore(release): ${nextRelease.version} [skip ci]`.\
:information_source: The `[skip ci]` part is problematic as it prevents GitLab from triggering the tag pipeline, therefore preventing other to-be-continuous templates from publishing their versioned packages.
Pierre Smeyers
committed
To fix this, you'll have to override the default semantic-release Git commit comment in order not to prevent the tag pipeline from being triggered.
With this done:
* the semantic-release will analyse each commit on your production branch, and will possibly create a Git tag if it determined a release has to be performed,
* the Git tag will trigger a tag pipeline during which every to-be-continuous template will take care of publishing its versioned package (using the Git tag as the version) to an appropriate packages repository.
#### How to override the Git commit comment
In most cases it is recommended to use `chore(release): ${nextRelease.version} [skip ci on prod]` as message template.\
:information_source: the important part is `[skip ci on prod]` that prevents GitLab from triggering the pipeline on your production branch only, **but not the tag pipeline**.
#### Using a configuration file
Pierre Smeyers
committed
If you're configuring semantic-release with a configuration file in your repository, then the Git commit message has to be configured in the [@semantic-release/git](https://github.com/semantic-release/git#message) plugin section.
Here is a `.releaserc.yaml` configuration example that auto-generates the changelog file, and also replaces the project version in the `pyproject.toml` using the [semantic-release-replace](https://github.com/jpoehnelt/semantic-release-replace-plugin) plugin:
Pierre Smeyers
committed
```yaml
plugins:
# GitLab support
- '@semantic-release/gitlab'
# analyses the Git commits
Pierre Smeyers
committed
- '@semantic-release/commit-analyzer'
# generates the release note from the Git commit messages
Pierre Smeyers
committed
- '@semantic-release/release-notes-generator'
# generates the CHANGELOG.md file
Pierre Smeyers
committed
- '@semantic-release/changelog'
# emulates bumpversion (replaces 'version' in pyproject.toml)
Pierre Smeyers
committed
- - semantic-release-replace-plugin
- replacements:
- files:
- pyproject.toml
from:
- ^version *= *"\d+\.\d+\.\d+"
to: 'version = "${nextRelease.version}"'
countMatches: true
# git commit/push modified files
Pierre Smeyers
committed
- - '@semantic-release/git'
- assets:
- 'CHANGELOG.md'
- 'pyproject.toml'
Pierre Smeyers
committed
# the commit MUST trigger a pipeline on tag (to perform publish jobs)
# can be skipped on prod branch
message: 'chore(release): ${nextRelease.version} [skip ci on prod]'
Pierre Smeyers
committed
branches:
- main
- master
tagFormat: '${version}'
```
#### Using implicit configuration
If you're not configuring semantic-release with a configuration file (but using implicit configuration provided by the template), then the Git commit message can be configured with the `commit-message` input / `SEMREL_COMMIT_MESSAGE` variable:
Pierre Smeyers
committed
```yaml
variables:
# the '$' has to be doubled to prevent GitLab from expanding it as a variable
SEMREL_COMMIT_MESSAGE: 'chore(release): $${nextRelease.version} [skip ci on prod]'
Pierre Smeyers
committed
```
Pierre Smeyers
committed
The semantic-release 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:
Kilian PAQUIER
committed
| 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 | **must be defined** |
Kilian PAQUIER
committed
| `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 | _none_ |
| :lock: `VAULT_SECRET_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID | _none_ |
By default, the variant will authentifacte using a [JWT ID token](https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html). To use [AppRole](https://www.vaultproject.io/docs/auth/approle) instead the `VAULT_ROLE_ID` and `VAULT_SECRET_ID` should be defined as secret project variables.
#### 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:
Kilian PAQUIER
committed
| 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: $CI_SERVER_FQDN/to-be-continuous/semantic-release/gitlab-ci-semrel@3.11.5
- component: $CI_SERVER_FQDN/to-be-continuous/semantic-release/gitlab-ci-semrel-vault@3.11.5
inputs:
vault-base-url: "https://vault.acme.host/v1"
# audience claim for JWT
vault-oidc-aud: "https://vault.acme.host"
# Secrets managed by Vault
GITLAB_TOKEN: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/semantic-release/token?field=group-access-token"