Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow to skip success and fail comments based on provided condition #730

68 changes: 57 additions & 11 deletions README.md

Large diffs are not rendered by default.

61 changes: 34 additions & 27 deletions lib/fail.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ 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(`Disabling failure reporting should be done using 'failCommentCondition'.
Using 'false' for 'failComment' or 'failTitle' is deprecated and will be removed in a future major version!`);
JonasSchubert marked this conversation as resolved.
Show resolved Hide resolved
} else {
const encodedFailTitle = encodeURIComponent(failTitle);
const description = failComment ? template(failComment)({ branch, errors }) : getFailComment(branch, errors);
Expand All @@ -34,32 +34,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
fgreinacher marked this conversation as resolved.
Show resolved Hide resolved
? 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.");
}
}
};
16 changes: 15 additions & 1 deletion lib/resolve-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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,
};
Expand Down
67 changes: 43 additions & 24 deletions lib/success.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,62 @@ 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(`Disabling commenting on issues and pull requests should be done using 'successCommentCondition'.
Using 'false' for 'successComment' is deprecated and will be removed in a future major version!`);
JonasSchubert marked this conversation as resolved.
Show resolved Hide resolved
} 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
JonasSchubert marked this conversation as resolved.
Show resolved Hide resolved
? 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
JonasSchubert marked this conversation as resolved.
Show resolved Hide resolved
? 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) => {
Expand Down
127 changes: 127 additions & 0 deletions test/fail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,130 @@ 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 = { 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: `## :rotating_light: The automated release from the \`main\` branch failed. :rotating_light:
JonasSchubert marked this conversation as resolved.
Show resolved Hide resolved

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 \`main\` 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](https://github.com/semantic-release/semantic-release/blob/master/docs/usage/README.md)
- [Frequently Asked Questions](https://github.com/semantic-release/semantic-release/blob/master/docs/support/FAQ.md)
- [Support channels](https://github.com/semantic-release/semantic-release#get-help)

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](https://github.com/semantic-release/semantic-release/issues/new)**.

---

### An error occured

Unfortunately this error doesn't have any additional information.

---

Good luck with your project ✨

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package: :rocket:`,
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",
]);
});
2 changes: 2 additions & 0 deletions test/resolve-config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand Down
Loading