Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
[![Build Status](https://github.com/semantic-release/gitlab/workflows/Test/badge.svg)](https://github.com/semantic-release/gitlab/actions?query=workflow%3ATest+branch%3Amaster) [![npm latest version](https://img.shields.io/npm/v/@semantic-release/gitlab/latest.svg)](https://www.npmjs.com/package/@semantic-release/gitlab)
[![npm next version](https://img.shields.io/npm/v/@semantic-release/gitlab/next.svg)](https://www.npmjs.com/package/@semantic-release/gitlab)

| Step | Description |
|--------------------|-----------------------------------------------------------------------------------------------------------------------|
| `verifyConditions` | Verify the presence and the validity of the authentication (set via [environment variables](#environment-variables)). |
| `publish` | Publish a [GitLab release](https://docs.gitlab.com/ee/user/project/releases/). |
| `success` | Add a comment to each GitLab Issue or Merge Request resolved by the release. |
| Step | Description |
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| `verifyConditions` | Verify the presence and the validity of the authentication (set via [environment variables](#environment-variables)). |
| `publish` | Publish a [GitLab release](https://docs.gitlab.com/ee/user/project/releases/). |
| `success` | Add a comment to each GitLab Issue or Merge Request resolved by the release. |
| `fail` | Open or update a [GitLab Issue](https://docs.gitlab.com/ee/user/project/issues/) with information about the errors that caused the release to fail. |

## Install

Expand Down Expand Up @@ -70,13 +71,17 @@ If your GitLab instance is exposed via plain HTTP (not recommended!) use `HTTP_P

### Options

| Option | Description | Default |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `gitlabUrl` | The GitLab endpoint. | `GL_URL` or `GITLAB_URL` environment variable or CI provided environment variables if running on [GitLab CI/CD](https://docs.gitlab.com/ee/ci) or `https://gitlab.com`. |
| `gitlabApiPathPrefix` | The GitLab API prefix. | `GL_PREFIX` or `GITLAB_PREFIX` environment variable or CI provided environment variables if running on [GitLab CI/CD](https://docs.gitlab.com/ee/ci) or `/api/v4`. |
| `assets` | An array of files to upload to the release. See [assets](#assets). | - |
| `milestones` | An array of milestone titles to associate to the release. See [GitLab Release API](https://docs.gitlab.com/ee/api/releases/#create-a-release). | - |
| `successComment` | The comment to add to each Issue and Merge Request resolved by the release. Set to false to disable commenting. See [successComment](#successComment). | :tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nThe release is available on [GitLab release](<gitlab_release_url>) |
| Option | Description | Default |
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `gitlabUrl` | The GitLab endpoint. | `GL_URL` or `GITLAB_URL` environment variable or CI provided environment variables if running on [GitLab CI/CD](https://docs.gitlab.com/ee/ci) or `https://gitlab.com`. |
| `gitlabApiPathPrefix` | The GitLab API prefix. | `GL_PREFIX` or `GITLAB_PREFIX` environment variable or CI provided environment variables if running on [GitLab CI/CD](https://docs.gitlab.com/ee/ci) or `/api/v4`. |
| `assets` | An array of files to upload to the release. See [assets](#assets). | - |
| `milestones` | An array of milestone titles to associate to the release. See [GitLab Release API](https://docs.gitlab.com/ee/api/releases/#create-a-release). | - |
| `successComment` | The comment to add to each Issue and Merge Request resolved by the release. Set to false to disable commenting. See [successComment](#successComment). | :tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nThe release is available on [GitLab release](<gitlab_release_url>) |
| `failComment` | The content of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. See [failComment](#failcomment). | Friendly message with links to **semantic-release** documentation and support, with the list of errors that caused the release to fail. |
| `failTitle` | The title of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. | `The automated release is failing 🚨` |
| `labels` | The [labels](https://docs.gitlab.com/ee/user/project/labels.html#labels) to add to the issue created when a release fails. Set to `false` to not add any label. | `semantic-release` |
| `assignee` | The [assignee](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#assignee) to add to the issue created when a release fails. | - |

#### assets

Expand Down Expand Up @@ -127,6 +132,23 @@ The message for the issue comments is generated with [Lodash template](https://l
| `mergeRequest` | A [GitLab API Issue object](https://docs.gitlab.com/ee/api/issues.html#single-issue) the comment will be posted to, or `false` when commenting Merge Requests.
| `issue` | A [GitHub API Merge Request object](https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr) the comment will be posted to, or `false` when commenting Issues.

#### failComment

The message for the issue content is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:

| Parameter | Description |
|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `branch` | The branch from which the release had failed. |
| `errors` | An `Array` of [SemanticReleaseError](https://github.com/semantic-release/error). Each error has the `message`, `code`, `pluginName` and `details` properties.<br>`pluginName` contains the package name of the plugin that threw the error.<br>`details` contains a information about the error formatted in markdown. |

##### failComment example

The `failComment` `This release from branch ${branch.name} had failed due to the following errors:\n- ${errors.map(err => err.message).join('\\n- ')}` will generate the comment:

> This release from branch master had failed due to the following errors:
> - Error message 1
> - Error message 2

## Compatibility

The latest version of this plugin is compatible with all currently-supported versions of GitLab, [which is the current major version and previous two major versions](https://about.gitlab.com/support/statement-of-support.html#version-support). This plugin is not guaranteed to work with unsupported versions of GitLab.
Expand Down
12 changes: 11 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const verifyGitLab = require('./lib/verify');
const publishGitLab = require('./lib/publish');
const successGitLab = require('./lib/success');
const failGitLab = require('./lib/fail');

let verified;

Expand All @@ -29,4 +30,13 @@ async function success(pluginConfig, context) {
return successGitLab(pluginConfig, context);
}

module.exports = {verifyConditions, publish, success};
async function fail(pluginConfig, context) {
if (!verified) {
await verifyGitLab(pluginConfig, context);
verified = true;
}

return failGitLab(pluginConfig, context);
}

module.exports = {verifyConditions, publish, success, fail};
4 changes: 3 additions & 1 deletion lib/definitions/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const HOME_URL = 'https://github.com/semantic-release/semantic-release';

const RELEASE_NAME = 'GitLab release';

module.exports = {RELEASE_NAME};
module.exports = {HOME_URL, RELEASE_NAME};
24 changes: 24 additions & 0 deletions lib/definitions/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@ module.exports = {
)}) must be an \`Array\` of \`Strings\` or \`Objects\` with a \`path\` property.
Your configuration for the \`assets\` option is \`${stringify(assets)}\`.`,
}),
EINVALIDFAILTITLE: ({failTitle}) => ({
message: 'Invalid `failTitle` option.',
details: `The [failTitle option](${linkify('README.md#failtitle')}) if defined, must be a non empty \`String\`.

Your configuration for the \`failTitle\` option is \`${stringify(failTitle)}\`.`,
}),
EINVALIDFAILCOMMENT: ({failComment}) => ({
message: 'Invalid `failComment` option.',
details: `The [failComment option](${linkify('README.md#failcomment')}) if defined, must be a non empty \`String\`.

Your configuration for the \`failComment\` option is \`${stringify(failComment)}\`.`,
}),
EINVALIDLABELS: ({labels}) => ({
message: 'Invalid `labels` option.',
details: `The [labels option](${linkify('README.md#labels')}) if defined, must be a non empty \`String\`.

Your configuration for the \`labels\` option is \`${stringify(labels)}\`.`,
}),
EINVALIDASSIGNEE: ({assignee}) => ({
message: 'Invalid `assignee` option.',
details: `The [assignee option](${linkify('README.md#assignee')}) if defined, must be a non empty \`String\`.

Your configuration for the \`assignee\` option is \`${stringify(assignee)}\`.`,
}),
EINVALIDGITLABURL: () => ({
message: 'The git repository URL is not a valid GitLab URL.',
details: `The **semantic-release** \`repositoryUrl\` option must a valid GitLab URL with the format \`<GitLab_URL>/<repoId>.git\`.
Expand Down
62 changes: 62 additions & 0 deletions lib/fail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const {template} = require('lodash');
const urlJoin = require('url-join');
const got = require('got');
const debug = require('debug')('semantic-release:gitlab');
const resolveConfig = require('./resolve-config');
const getRepoId = require('./get-repo-id');
const getFailComment = require('./get-fail-comment');

module.exports = async (pluginConfig, context) => {
const {
options: {repositoryUrl},
branch,
errors,
logger,
} = context;
const {gitlabToken, gitlabUrl, gitlabApiUrl, failComment, failTitle, labels, assignee} = resolveConfig(
pluginConfig,
context
);
const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
const encodedRepoId = encodeURIComponent(repoId);
const apiOptions = {headers: {'PRIVATE-TOKEN': gitlabToken}};

if (failComment === false || failTitle === false) {
logger.log('Skip issue creation.');
} else {
const encodedFailTitle = encodeURIComponent(failTitle);
const description = failComment ? template(failComment)({branch, errors}) : getFailComment(branch, errors);

const issuesEndpoint = urlJoin(gitlabApiUrl, `/projects/${repoId}/issues`);
const openFailTitleIssueEndpoint = urlJoin(issuesEndpoint, `?state=opened&search=${encodedFailTitle}`);

const openFailTitleIssues = await got(openFailTitleIssueEndpoint, {...apiOptions}).json();
const existingIssue = openFailTitleIssues.find(openFailTitleIssue => openFailTitleIssue.title === failTitle);

if (existingIssue) {
debug('comment on issue: %O', existingIssue);

const issueNotesEndpoint = urlJoin(
gitlabApiUrl,
`/projects/${existingIssue.project_id}/issues/${existingIssue.iid}/notes`
);
await got.post(issueNotesEndpoint, {
...apiOptions,
json: {body: description},
});

const {id, web_url} = existingIssue;
logger.log('Commented on issue #%d: %s.', id, web_url);
} else {
const newIssue = {id: encodedRepoId, description, labels, title: failTitle, assignee_id: assignee};
debug('create issue: %O', newIssue);

/* eslint camelcase: off */
const {id, web_url} = await got.post(issuesEndpoint, {
...apiOptions,
json: newIssue,
});
logger.log('Created issue #%d: %s.', id, web_url);
}
}
};
46 changes: 46 additions & 0 deletions lib/get-fail-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const {HOME_URL} = require('./definitions/constants');

const FAQ_URL = `${HOME_URL}/blob/master/docs/support/FAQ.md`;
const GET_HELP_URL = `${HOME_URL}#get-help`;
const USAGE_DOC_URL = `${HOME_URL}/blob/master/docs/usage/README.md`;
const NEW_ISSUE_URL = `${HOME_URL}/issues/new`;

const formatError = error => `### ${error.message}

${error.details ||
`Unfortunately this error doesn't have any additional information.${
error.pluginName
? ` Feel free to kindly ask the author of the \`${error.pluginName}\` plugin to add more helpful information.`
: ''
}`}`;

module.exports = (branch, errors) => `## :rotating_light: The automated release from the \`${
branch.name
}\` branch failed. :rotating_light:

I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again.

You can find below the list of errors reported by **semantic-release**. Each one of them has to be resolved in order to automatically publish your package. I'm sure you can fix this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, **semantic-release** will release your package the next time you push a commit to the \`${
branch.name
}\` branch. You can also manually restart the failed CI job that runs **semantic-release**.

If you are not sure how to resolve this, here are some links that can help you:
- [Usage documentation](${USAGE_DOC_URL})
- [Frequently Asked Questions](${FAQ_URL})
- [Support channels](${GET_HELP_URL})

If those don't help, or if this issue is reporting something you think isn't right, you can always ask the humans behind **[semantic-release](${NEW_ISSUE_URL})**.

---

${errors.map(error => formatError(error)).join('\n\n---\n\n')}

---

Good luck with your project ✨

Your **[semantic-release](${HOME_URL})** bot :package: :rocket:`;
3 changes: 2 additions & 1 deletion lib/get-success-comment.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const HOME_URL = 'https://github.com/semantic-release/semantic-release';
const {HOME_URL} = require('./definitions/constants');

const linkify = releaseInfo =>
`${releaseInfo.url ? `[${releaseInfo.name}](${releaseInfo.url})` : `\`${releaseInfo.name}\``}`;

Expand Down
6 changes: 5 additions & 1 deletion lib/resolve-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const urlJoin = require('url-join');
const {HttpProxyAgent, HttpsProxyAgent} = require('hpagent');

module.exports = (
{gitlabUrl, gitlabApiPathPrefix, assets, milestones, successComment},
{gitlabUrl, gitlabApiPathPrefix, assets, milestones, successComment, failTitle, failComment, labels, assignee},
{
envCi: {service} = {},
env: {
Expand Down Expand Up @@ -45,6 +45,10 @@ module.exports = (
milestones: milestones ? castArray(milestones) : milestones,
successComment,
proxy: getProxyConfiguration(defaultedGitlabUrl, HTTP_PROXY, HTTPS_PROXY),
failTitle: isNil(failTitle) ? 'The automated release is failing 🚨' : failTitle,
failComment,
labels: isNil(labels) ? 'semantic-release' : labels === false ? false : labels,
assignee,
};
};

Expand Down
Loading