Repository: semantic-release/gitlab
Pull request #730 (2024-06-02 14:17:40)

feat: allow to skip success and fail comments based on provided condition
Could solve #480 and #636. This is based on the idea of @fgreinacher [here](https://github.com/semantic-release/gitlab/pull/678#issuecomment-1952644489).
I added documentation with some highlighting on how to use this functionality and provided some unit tests.

I introduced a new variable `defaultComment` to ease up the filtering for issues and merge requests, but keeping the default comment message.

`semantic-release/github` has at least [one PR](https://github.com/semantic-release/github/pull/360) which shall have the same result, but is a different approach. I like the templating idea @fgreinacher brought up and think this would be a good solution there too - also to keep the plugins similar.

I am aware that this project has an open [PR](https://github.com/semantic-release/gitlab/pull/678). As there was no update since February I took the liberty to create this one, hope this is ok for you @hakihiro.

fgreinacher: I like it a lot, thanks for picking it up @JonasSchubert!

It's a very flexible approach that does not add much complexity to the code base. 

@travi Any objections on going this way? Would probably be quite easy to handle it the same way in the GitHub plugin.
fgreinacher: Thanks for the review @gr2m!

It is already possible to disable commenting by statically setting the `successComment` option to `false`. Therefore I think it's more consistent to not handle the use case of dynamic disabling differently.
gr2m: Sorry if I miss something obvious, but why not do an option like `condition` that can be set to something like `"issue.labels?.includes('semantic-release-relevant')"` instead?
fgreinacher: We could certainly also do that! My only small concern is that we then have two ways two disable the success comment:

1. set the existing `successComment` to `false`.
2. set the new `commentCondition` option to an expression that evaluates to `false`.

But it's probably not a big issue if we manage to describe it properly :)
gr2m: independent of what we have today, what would the ideal arguments look like? We shouldn't be afraid of breaking things, it's better than ending up with arguments that are unnecessary complicated due to legacy reasons.

Maybe we could deprecate the possibility of setting `successComment` to `false`, so that it keeps working but we start logging a deprecation message. And we introduce a new argument like `commentCondition` which would be the single place to decide whether a comment done or not?
fgreinacher: Yes, that sounds like a good plan!

I'm not entirely sure whether `lodash` templating supports evaluating a boolean expression. That would need some investigation.
JonasSchubert: > We could certainly also do that! My only small concern is that we then have two ways two disable the success comment:
> 
> 1. set the existing `successComment` option to `false`.
> 2. set the new `commentCondition` option to an expression that evaluates to `false`.
> 
> But it's probably not a big issue if we manage to describe it properly :)
> 
> @JonasSchubert WDYT about this idea?

Sounds like a good idea to separate the responsibilities of these configuration options. 👍 We had at least three attempts to filter success comments now (https://github.com/semantic-release/github/pull/360, https://github.com/semantic-release/gitlab/pull/678 and this PR) and this suggestion sounds reasonable.

Should this then also be changed for the `fail` step? It has two flags (`failComment` and `failTitle`) which can either be `false` to disable the `fail` step. Shall this also be handled with a condition or do we keep that logic? I would prefer if we can align the handling of `success` and `fail`.

A proper description is definitely needed, but from experience I know description and documentation might not be read by everybody. 😏 This is why I think we have to make it as understandable as possible without the need for too much documentation.
fgreinacher: Good point, I agree we should align success and fail comments.

What about `successCommentCondition` and `failCommentCondition`?
gr2m: > Good point, I agree we should align success and fail comments.
> 
> What about `successCommentCondition` and `failCommentCondition`?

Alternatively we can just make a single `commentCondition`, but expose context to that conditions to differentiate between success and failer?
fgreinacher: > > Good point, I agree we should align success and fail comments.
> > What about `successCommentCondition` and `failCommentCondition`?
> 
> Alternatively we can just make a single `commentCondition`, but expose context to that conditions to differentiate between success and failer?

I also thought about that, but in the end the reasons for skipping a success comment are quite different from the reasons for skipping a fail comment. The former is usually done to reduce noise, e.g. not spamming irrelevant MRs. The later should IMO only be done in exceptional situations, e.g. when the issue tracker is disabled. I therefore believe mixing both would increase complexity unnecessarily.
travi: > Would probably be quite easy to handle it the same way in the GitHub plugin (see also [semantic-release/github#359 (comment)](https://github.com/semantic-release/github/issues/359#issuecomment-2143877794))

sorry for my slow responses here. the last few weeks have been especially crazy for me. i appreciate the callout to this detail. i think this is the biggest consideration for me. like @gr2m, i'm open to breakages to get to an api we are comfortable with and think solves the problem well, but would like to make sure we take both github and gitlab into consideration with that design. i still hope that we can eventually extract some of the common behaviors to a more "git-host core" like package to reduce some of the duplication, but that is something that likely is a bit further into the future still.
fgreinacher: Thanks for all your comments!

Let me summarize:

1. we introduce
   a. _either_ a new option `commentCondition` that can be used to disable both success and fail comments.
   b. _or_ (my favorite) two new options `successCommentCondition` and `failCommentCondition` that can be used to disable success and fail comments respectively
2. we log a warning when people are setting `successComment`. `failComment` or `failTitle` to `false`, announcing this to be removed in future major versions
3. in one of next major releases we can then remove the deprecated functionality

@JonasSchubert Now that the scope has grown a bit are you still willing to work on it? 
JonasSchubert: > Thanks for all your comments!
> 
> Let me summarize:
> 
> 1. we introduce
>    a. _either_ a new option `commentCondition` that can be used to disable both success and fail comments.
>    b. _or_, my favorite, two new options `successCommentCondition` and `failCommentCondition` that can be used to disable success and fail comments respectively
> 2. we log a warning when people are setting `successComment`. `failComment` or `failTitle` to `false`, announcing this to be removed in future major versions
> 3. in one of next major releases we can then remove the deprecated functionality
> 
> @JonasSchubert Now that the scope has grown a bit are you still willing to work on it? 

@fgreinacher I would still be willing to work on it, yes. As soon as we agreed on the way forward I will adjust my PR.

I would then also prefer 1.b. as 1.a. will be very complex to configure for the end user.
fgreinacher: @JonasSchubert Great, thanks a ton! 

Let's go on with 1b (separate options) then!
gr2m: summery sounds good, 1b is good with me, thank you 👍🏼 

Patch:
diff --git a/README.md b/README.md
index 129df9f..12c7b21 100644
--- a/README.md
+++ b/README.md
@@ -82,17 +82,19 @@ If you need to bypass the proxy for some hosts, configure the `NO_PROXY` environ
 
 ### 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)                        |
-| `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. Labels should be comma-separated as described in the [official docs](https://docs.gitlab.com/ee/api/issues.html#new-issue), e.g. `"semantic-release,bot"`. | `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.                                                                                                                                                                             | -                                                                                                                                                                       |
+| 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. 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)                        |
+| `successCommentCondition` | Use this as condition, when to comment on issues or merge requests. See [successCommentCondition](#successCommentCondition).                                                                                                                                                                                               | -                                                                                                                                                                       |
+| `failComment`             | The content of the issue created 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.                                                                                                                                                                                                                                                                       | `The automated release is failing 🚨`                                                                                                                                   |
+| `failCommentCondition`    | Use this as condition, when to comment on or create an issues in case of failures. See [failCommentCondition](#failCommentCondition).                                                                                                                                                                                      | -                                                                                                                                                                       |
+| `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. Labels should be comma-separated as described in the [official docs](https://docs.gitlab.com/ee/api/issues.html#new-issue), e.g. `"semantic-release,bot"`. | `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
 
@@ -146,6 +148,29 @@ The message for the issue comments is generated with [Lodash template](https://l
 | `issue`        | 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.          |
 | `mergeRequest` | A [GitLab 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. |
 
+#### successCommentCondition
+
+The success comment condition is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
+
+| Parameter      | Description                                                                                                                          |
+| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
+| `branch`       | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done.           |
+| `lastRelease`  | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release.                                                      |
+| `nextRelease`  | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done.                                       |
+| `commits`      | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`.                                                   |
+| `releases`     | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`.                    |
+| `issue`        | A [GitLab API Issue object](https://docs.gitlab.com/ee/api/issues.html#single-issue) the comment will be posted to.                  |
+| `mergeRequest` | A [GitLab API Merge Request object](https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr) the comment will be posted to. |
+
+##### successCommentCondition example
+
+- do not create any comments at all: set to `false` or templating: `"<% return false; %>"`
+- to only comment on issues: `"<% return issue %>"`
+- to only comment on merge requests: `"<% return mergeRequest %>"`
+- you can use labels to filter issues: `"<% return issue.labels?.includes('semantic-release-relevant') %>"`
+
+> check the [GitLab API Merge Request object](https://docs.gitlab.com/ee/api/merge_requests.html#get-single-mr) or the [GitLab API Issue object](https://docs.gitlab.com/ee/api/issues.html#single-issue) for properties which can be used for the filter
+
 #### failComment
 
 The message for the issue content is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
@@ -164,6 +189,27 @@ The `failComment` `This release from branch ${branch.name} had failed due to the
 > - Error message 1
 > - Error message 2
 
+#### failCommentCondition
+
+The fail comment condition is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
+
+| Parameter     | Description                                                                                                                                                  |
+| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `branch`      | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done.                                   |
+| `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release.                                                                              |
+| `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done.                                                               |
+| `commits`     | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`.                                                                           |
+| `releases`    | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`.                                            |
+| `issue`       | A [GitLab API Issue object](https://docs.gitlab.com/ee/api/issues.html#single-issue) the comment will be posted to - only available if an open issue exists. |
+
+##### failCommentCondition example
+
+- do not create any comments at all: set to `false` or templating: `"<% return false; %>"`
+- to only comment on main branch: `"<% return branch.name === 'main' %>"`
+- you can use labels to filter issues, i.e. to not comment if the issue is labeled with `wip`: `"<% return !issue.labels?.includes('wip') %>"`
+
+> check the [GitLab API Issue object](https://docs.gitlab.com/ee/api/issues.html#single-issue) for properties which can be used for the filter
+
 ## 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.
diff --git a/lib/fail.js b/lib/fail.js
index d270623..018f451 100644
--- a/lib/fail.js
+++ b/lib/fail.js
@@ -14,16 +14,18 @@ export default async (pluginConfig, context) => {
     errors,
     logger,
   } = context;
-  const { gitlabToken, gitlabUrl, gitlabApiUrl, failComment, failTitle, labels, assignee } = resolveConfig(
-    pluginConfig,
-    context
-  );
+  const { gitlabToken, gitlabUrl, gitlabApiUrl, failComment, failTitle, failCommentCondition, 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.");
+    logger.error(`Failure reporting should be disabled via 'failCommentCondition'.
+Using 'false' for 'failComment' or 'failTitle' is deprecated and will be removed in a future major version.`);
+  } else if (failCommentCondition === false) {
+    logger.log("Skip issue creation.");
   } else {
     const encodedFailTitle = encodeURIComponent(failTitle);
     const description = failComment ? template(failComment)({ branch, errors }) : getFailComment(branch, errors);
@@ -34,32 +36,39 @@ export default async (pluginConfig, context) => {
     const openFailTitleIssues = await got(openFailTitleIssueEndpoint, { ...apiOptions }).json();
     const existingIssue = openFailTitleIssues.find((openFailTitleIssue) => openFailTitleIssue.title === failTitle);
 
-    if (existingIssue) {
-      debug("comment on issue: %O", existingIssue);
+    const canCommentOnOrCreateIssue = failCommentCondition
+      ? template(failCommentCondition)({ ...context, issue: existingIssue })
+      : true;
+    if (canCommentOnOrCreateIssue) {
+      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 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);
+        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,
-        })
-        .json();
-      logger.log("Created issue #%d: %s.", id, web_url);
+        /* eslint camelcase: off */
+        const { id, web_url } = await got
+          .post(issuesEndpoint, {
+            ...apiOptions,
+            json: newIssue,
+          })
+          .json();
+        logger.log("Created issue #%d: %s.", id, web_url);
+      }
+    } else {
+      logger.log("Skip commenting on or creating an issue.");
     }
   }
 };
diff --git a/lib/resolve-config.js b/lib/resolve-config.js
index e28a3ae..295fce3 100644
--- a/lib/resolve-config.js
+++ b/lib/resolve-config.js
@@ -3,7 +3,19 @@ import urlJoin from "url-join";
 import { HttpProxyAgent, HttpsProxyAgent } from "hpagent";
 
 export default (
-  { gitlabUrl, gitlabApiPathPrefix, assets, milestones, successComment, failTitle, failComment, labels, assignee },
+  {
+    gitlabUrl,
+    gitlabApiPathPrefix,
+    assets,
+    milestones,
+    successComment,
+    successCommentCondition,
+    failTitle,
+    failComment,
+    failCommentCondition,
+    labels,
+    assignee,
+  },
   {
     envCi: { service } = {},
     env: {
@@ -45,9 +57,11 @@ export default (
     assets: assets ? castArray(assets) : assets,
     milestones: milestones ? castArray(milestones) : milestones,
     successComment,
+    successCommentCondition,
     proxy: getProxyConfiguration(defaultedGitlabUrl, HTTP_PROXY, HTTPS_PROXY, NO_PROXY),
     failTitle: isNil(failTitle) ? "The automated release is failing 🚨" : failTitle,
     failComment,
+    failCommentCondition,
     labels: isNil(labels) ? "semantic-release" : labels === false ? false : labels,
     assignee,
   };
diff --git a/lib/success.js b/lib/success.js
index bc697b9..910f020 100644
--- a/lib/success.js
+++ b/lib/success.js
@@ -15,43 +15,64 @@ export default async (pluginConfig, context) => {
     commits,
     releases,
   } = context;
-  const { gitlabToken, gitlabUrl, gitlabApiUrl, successComment, proxy } = resolveConfig(pluginConfig, context);
+  const { gitlabToken, gitlabUrl, gitlabApiUrl, successComment, successCommentCondition, proxy } = resolveConfig(
+    pluginConfig,
+    context
+  );
   const repoId = getRepoId(context, gitlabUrl, repositoryUrl);
   const encodedRepoId = encodeURIComponent(repoId);
   const apiOptions = { headers: { "PRIVATE-TOKEN": gitlabToken } };
 
   if (successComment === false) {
     logger.log("Skip commenting on issues and pull requests.");
+    logger.error(`Issue and pull request comments should be disabled via 'successCommentCondition'.
+Using 'false' for 'successComment' is deprecated and will be removed in a future major version.`);
+  } else if (successCommentCondition === false) {
+    logger.log("Skip commenting on issues and pull requests.");
   } else {
     const releaseInfos = releases.filter((release) => Boolean(release.name));
     try {
       const postCommentToIssue = (issue) => {
-        const issueNotesEndpoint = urlJoin(gitlabApiUrl, `/projects/${issue.project_id}/issues/${issue.iid}/notes`);
-        debug("Posting issue note to %s", issueNotesEndpoint);
-        const body = successComment
-          ? template(successComment)({ ...context, issue, mergeRequest: false })
-          : getSuccessComment(issue, releaseInfos, nextRelease);
-        return got.post(issueNotesEndpoint, {
-          ...apiOptions,
-          ...proxy,
-          json: { body },
-        });
+        const canCommentOnIssue = successCommentCondition
+          ? template(successCommentCondition)({ ...context, issue, mergeRequest: false })
+          : true;
+        if (canCommentOnIssue) {
+          const issueNotesEndpoint = urlJoin(gitlabApiUrl, `/projects/${issue.project_id}/issues/${issue.iid}/notes`);
+          debug("Posting issue note to %s", issueNotesEndpoint);
+          const body = successComment
+            ? template(successComment)({ ...context, issue, mergeRequest: false })
+            : getSuccessComment(issue, releaseInfos, nextRelease);
+          return got.post(issueNotesEndpoint, {
+            ...apiOptions,
+            ...proxy,
+            json: { body },
+          });
+        } else {
+          logger.log("Skip commenting on issue #%d.", issue.id);
+        }
       };
 
       const postCommentToMergeRequest = (mergeRequest) => {
-        const mergeRequestNotesEndpoint = urlJoin(
-          gitlabApiUrl,
-          `/projects/${mergeRequest.project_id}/merge_requests/${mergeRequest.iid}/notes`
-        );
-        debug("Posting MR note to %s", mergeRequestNotesEndpoint);
-        const body = successComment
-          ? template(successComment)({ ...context, issue: false, mergeRequest })
-          : getSuccessComment({ isMergeRequest: true, ...mergeRequest }, releaseInfos, nextRelease);
-        return got.post(mergeRequestNotesEndpoint, {
-          ...apiOptions,
-          ...proxy,
-          json: { body },
-        });
+        const canCommentOnMergeRequest = successCommentCondition
+          ? template(successCommentCondition)({ ...context, issue: false, mergeRequest })
+          : true;
+        if (canCommentOnMergeRequest) {
+          const mergeRequestNotesEndpoint = urlJoin(
+            gitlabApiUrl,
+            `/projects/${mergeRequest.project_id}/merge_requests/${mergeRequest.iid}/notes`
+          );
+          debug("Posting MR note to %s", mergeRequestNotesEndpoint);
+          const body = successComment
+            ? template(successComment)({ ...context, issue: false, mergeRequest })
+            : getSuccessComment({ isMergeRequest: true, ...mergeRequest }, releaseInfos, nextRelease);
+          return got.post(mergeRequestNotesEndpoint, {
+            ...apiOptions,
+            ...proxy,
+            json: { body },
+          });
+        } else {
+          logger.log("Skip commenting on merge request #%d.", mergeRequest.iid);
+        }
       };
 
       const getRelatedMergeRequests = async (commitHash) => {


Test patch:
diff --git a/test/fail.test.js b/test/fail.test.js
index dfa7e57..9c6f52d 100644
--- a/test/fail.test.js
+++ b/test/fail.test.js
@@ -243,3 +243,121 @@ test.serial("Does not post comments when failComment is set to false", async (t)
 
   t.true(gitlab.isDone());
 });
+
+test.serial("Does not post comments when failCommentCondition disables it", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { failCommentCondition: "<% return false; %>" };
+  const branch = { name: "main" };
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const errors = [{ message: "An error occured" }];
+  const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+  const encodedFailTitle = encodeURIComponent("The automated release is failing 🚨");
+  const gitlab = authenticate(env)
+    .get(`/projects/${encodedRepoId}/issues?state=opened&&search=${encodedFailTitle}`)
+    .reply(200, [
+      {
+        id: 2,
+        iid: 2,
+        project_id: 1,
+        web_url: "https://gitlab.com/test_user/test_repo/issues/2",
+        title: "API should implemented authentication",
+      },
+    ]);
+
+  await fail(pluginConfig, { env, options, branch, errors, logger: t.context.logger });
+
+  t.true(gitlab.isDone());
+});
+
+test.serial("Does not post comments on existing issues when failCommentCondition disables this", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { failCommentCondition: "<% return !issue; %>" };
+  const branch = { name: "main" };
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const errors = [{ message: "An error occured" }];
+  const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+  const encodedFailTitle = encodeURIComponent("The automated release is failing 🚨");
+  const gitlab = authenticate(env)
+    .get(`/projects/${encodedRepoId}/issues?state=opened&&search=${encodedFailTitle}`)
+    .reply(200, [
+      {
+        id: 1,
+        iid: 1,
+        project_id: 1,
+        web_url: "https://gitlab.com/test_user%2Ftest_repo/issues/1",
+        title: "The automated release is failing 🚨",
+      },
+      {
+        id: 2,
+        iid: 2,
+        project_id: 1,
+        web_url: "https://gitlab.com/test_user%2Ftest_repo/issues/2",
+        title: "API should implemented authentication",
+      },
+    ]);
+
+  await fail(pluginConfig, { env, options, branch, errors, logger: t.context.logger });
+
+  t.true(gitlab.isDone());
+});
+
+test.serial("Post new issue if none exists yet with disabled comment on existing issues", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = {
+    failComment: `Error: Release for branch \${branch.name} failed with error: \${errors.map(error => error.message).join(';')}`,
+    failCommentCondition: "<% return !issue; %>",
+  };
+  const branch = { name: "main" };
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const errors = [{ message: "An error occured" }];
+  const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+  const encodedFailTitle = encodeURIComponent("The automated release is failing 🚨");
+  const gitlab = authenticate(env)
+    .get(`/projects/${encodedRepoId}/issues?state=opened&&search=${encodedFailTitle}`)
+    .reply(200, [
+      {
+        id: 2,
+        iid: 2,
+        project_id: 1,
+        web_url: "https://gitlab.com/test_user/test_repo/issues/2",
+        title: "API should implemented authentication",
+      },
+    ])
+    .post(`/projects/${encodedRepoId}/issues`, {
+      id: "test_user%2Ftest_repo",
+      description: `Error: Release for branch main failed with error: An error occured`,
+      labels: "semantic-release",
+      title: "The automated release is failing 🚨",
+    })
+    .reply(200, { id: 3, web_url: "https://gitlab.com/test_user/test_repo/-/issues/3" });
+
+  await fail(pluginConfig, { env, options, branch, errors, logger: t.context.logger });
+
+  t.true(gitlab.isDone());
+  t.deepEqual(t.context.log.args[0], [
+    "Created issue #%d: %s.",
+    3,
+    "https://gitlab.com/test_user/test_repo/-/issues/3",
+  ]);
+});
+
+test.serial("Does not post comments when failCommentCondition is set to false", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { failCommentCondition: false };
+  const branch = { name: "main" };
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const errors = [{ message: "An error occured" }];
+  const gitlab = authenticate(env);
+
+  await fail(pluginConfig, { env, options, branch, errors, logger: t.context.logger });
+
+  t.true(gitlab.isDone());
+});
diff --git a/test/resolve-config.test.js b/test/resolve-config.test.js
index 475e41d..91efa76 100644
--- a/test/resolve-config.test.js
+++ b/test/resolve-config.test.js
@@ -10,8 +10,10 @@ const defaultOptions = {
   assets: undefined,
   milestones: undefined,
   successComment: undefined,
+  successCommentCondition: undefined,
   failTitle: "The automated release is failing 🚨",
   failComment: undefined,
+  failCommentCondition: undefined,
   labels: "semantic-release",
   assignee: undefined,
   proxy: {},
diff --git a/test/success.test.js b/test/success.test.js
index 47d0c39..9e6272f 100644
--- a/test/success.test.js
+++ b/test/success.test.js
@@ -138,3 +138,170 @@ test.serial("Does not post comments when successComment is set to false", async
 
   t.true(gitlab.isDone());
 });
+
+test.serial("Does not post comments when successCommentCondition disables it", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { successCommentCondition: "<% return false; %>" };
+  const nextRelease = { version: "1.0.0" };
+  const releases = [{ name: RELEASE_NAME, url: "https://gitlab.com/test_user/test_repo/-/releases/v1.0.0" }];
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+  const commits = [{ hash: "abcdef" }, { hash: "fedcba" }];
+  const gitlab = authenticate(env)
+    .get(`/projects/${encodedRepoId}/repository/commits/abcdef/merge_requests`)
+    .reply(200, [
+      { project_id: 100, iid: 1, state: "merged" },
+      { project_id: 200, iid: 2, state: "closed" },
+      { project_id: 300, iid: 3, state: "merged" },
+    ])
+    .get(`/projects/${encodedRepoId}/repository/commits/fedcba/merge_requests`)
+    .reply(200, [{ project_id: 100, iid: 1, state: "merged" }])
+    .get(`/projects/100/merge_requests/1/closes_issues`)
+    .reply(200, [
+      { project_id: 100, iid: 11, state: "closed" },
+      { project_id: 100, iid: 12, state: "open" },
+      { project_id: 100, iid: 13, state: "closed" },
+    ])
+    .get(`/projects/300/merge_requests/3/closes_issues`)
+    .reply(200, []);
+
+  await success(pluginConfig, { env, options, nextRelease, logger: t.context.logger, commits, releases });
+
+  t.true(gitlab.isDone());
+});
+
+test.serial("Does not post comments on issues when successCommentCondition disables issue commenting", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { successCommentCondition: "<% return !issue; %>" };
+  const nextRelease = { version: "1.0.0" };
+  const releases = [{ name: RELEASE_NAME, url: "https://gitlab.com/test_user/test_repo/-/releases/v1.0.0" }];
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+  const commits = [{ hash: "abcdef" }, { hash: "fedcba" }];
+  const gitlab = authenticate(env)
+    .get(`/projects/${encodedRepoId}/repository/commits/abcdef/merge_requests`)
+    .reply(200, [
+      { project_id: 100, iid: 1, state: "merged" },
+      { project_id: 200, iid: 2, state: "closed" },
+      { project_id: 300, iid: 3, state: "merged" },
+    ])
+    .get(`/projects/${encodedRepoId}/repository/commits/fedcba/merge_requests`)
+    .reply(200, [{ project_id: 100, iid: 1, state: "merged" }])
+    .get(`/projects/100/merge_requests/1/closes_issues`)
+    .reply(200, [
+      { project_id: 100, iid: 11, state: "closed" },
+      { project_id: 100, iid: 12, state: "open" },
+      { project_id: 100, iid: 13, state: "closed" },
+    ])
+    .get(`/projects/300/merge_requests/3/closes_issues`)
+    .reply(200, [])
+    .post(`/projects/100/merge_requests/1/notes`, {
+      body: ":tada: This MR is included in version 1.0.0 :tada:\n\nThe release is available on [GitLab release](https://gitlab.com/test_user/test_repo/-/releases/v1.0.0).\n\nYour **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package: :rocket:",
+    })
+    .reply(200)
+    .post(`/projects/300/merge_requests/3/notes`)
+    .reply(200);
+
+  await success(pluginConfig, { env, options, nextRelease, logger: t.context.logger, commits, releases });
+
+  t.true(gitlab.isDone());
+});
+
+test.serial("Only posts comments on issues which are found using the successCommentCondition", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { successCommentCondition: "<% return issue.labels?.includes('semantic-release-relevant'); %>" };
+  const nextRelease = { version: "1.0.0" };
+  const releases = [{ name: RELEASE_NAME, url: "https://gitlab.com/test_user/test_repo/-/releases/v1.0.0" }];
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+  const commits = [{ hash: "abcdef" }, { hash: "fedcba" }];
+  const gitlab = authenticate(env)
+    .get(`/projects/${encodedRepoId}/repository/commits/abcdef/merge_requests`)
+    .reply(200, [
+      { project_id: 100, iid: 1, state: "merged" },
+      { project_id: 200, iid: 2, state: "closed" },
+      { project_id: 300, iid: 3, state: "merged" },
+    ])
+    .get(`/projects/${encodedRepoId}/repository/commits/fedcba/merge_requests`)
+    .reply(200, [{ project_id: 100, iid: 1, state: "merged" }])
+    .get(`/projects/100/merge_requests/1/closes_issues`)
+    .reply(200, [
+      { project_id: 100, iid: 11, labels: "doing,bug", state: "closed" },
+      { project_id: 100, iid: 12, labels: "todo,feature", state: "open" },
+      { project_id: 100, iid: 13, labels: "testing,semantic-release-relevant,critical", state: "closed" },
+    ])
+    .get(`/projects/300/merge_requests/3/closes_issues`)
+    .reply(200, [])
+    .post(`/projects/100/issues/13/notes`, {
+      body: ":tada: This issue has been resolved in version 1.0.0 :tada:\n\nThe release is available on [GitLab release](https://gitlab.com/test_user/test_repo/-/releases/v1.0.0).\n\nYour **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package: :rocket:",
+    })
+    .reply(200);
+
+  await success(pluginConfig, { env, options, nextRelease, logger: t.context.logger, commits, releases });
+
+  t.true(gitlab.isDone());
+});
+
+test.serial(
+  "Does not post comments on merge requets when successCommentCondition disables merge request commenting",
+  async (t) => {
+    const owner = "test_user";
+    const repo = "test_repo";
+    const env = { GITLAB_TOKEN: "gitlab_token" };
+    const pluginConfig = { successCommentCondition: "<% return !mergeRequest; %>" };
+    const nextRelease = { version: "1.0.0" };
+    const releases = [{ name: RELEASE_NAME, url: "https://gitlab.com/test_user/test_repo/-/releases/v1.0.0" }];
+    const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+    const encodedRepoId = encodeURIComponent(`${owner}/${repo}`);
+    const commits = [{ hash: "abcdef" }, { hash: "fedcba" }];
+    const gitlab = authenticate(env)
+      .get(`/projects/${encodedRepoId}/repository/commits/abcdef/merge_requests`)
+      .reply(200, [
+        { project_id: 100, iid: 1, state: "merged" },
+        { project_id: 200, iid: 2, state: "closed" },
+        { project_id: 300, iid: 3, state: "merged" },
+      ])
+      .get(`/projects/${encodedRepoId}/repository/commits/fedcba/merge_requests`)
+      .reply(200, [{ project_id: 100, iid: 1, state: "merged" }])
+      .get(`/projects/100/merge_requests/1/closes_issues`)
+      .reply(200, [
+        { project_id: 100, iid: 11, state: "closed" },
+        { project_id: 100, iid: 12, state: "open" },
+        { project_id: 100, iid: 13, state: "closed" },
+      ])
+      .get(`/projects/300/merge_requests/3/closes_issues`)
+      .reply(200, [])
+      .post(`/projects/100/issues/11/notes`, {
+        body: ":tada: This issue has been resolved in version 1.0.0 :tada:\n\nThe release is available on [GitLab release](https://gitlab.com/test_user/test_repo/-/releases/v1.0.0).\n\nYour **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package: :rocket:",
+      })
+      .reply(200)
+      .post(`/projects/100/issues/13/notes`)
+      .reply(200);
+
+    await success(pluginConfig, { env, options, nextRelease, logger: t.context.logger, commits, releases });
+
+    t.true(gitlab.isDone());
+  }
+);
+
+test.serial("Does not post comments when successCommentCondition is set to false", async (t) => {
+  const owner = "test_user";
+  const repo = "test_repo";
+  const env = { GITLAB_TOKEN: "gitlab_token" };
+  const pluginConfig = { successCommentCondition: false };
+  const nextRelease = { version: "1.0.0" };
+  const releases = [{ name: RELEASE_NAME, url: "https://gitlab.com/test_user/test_repo/-/releases/v1.0.0" }];
+  const options = { repositoryUrl: `https://gitlab.com/${owner}/${repo}.git` };
+  const commits = [{ hash: "abcdef" }, { hash: "fedcba" }];
+  const gitlab = authenticate(env);
+
+  await success(pluginConfig, { env, options, nextRelease, logger: t.context.logger, commits, releases });
+
+  t.true(gitlab.isDone());
+});