Skip to content

Commit

Permalink
Merge pull request #730 from JonasSchubert/feat/allow-to-skip-success…
Browse files Browse the repository at this point in the history
…-comment-#480-#636

feat: allow to skip success and fail comments based on provided condition
  • Loading branch information
fgreinacher authored Jul 8, 2024
2 parents 6b12097 + 4c651c5 commit 3132c19
Show file tree
Hide file tree
Showing 7 changed files with 440 additions and 63 deletions.
68 changes: 57 additions & 11 deletions README.md

Large diffs are not rendered by default.

63 changes: 36 additions & 27 deletions lib/fail.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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.");
}
}
};
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
69 changes: 45 additions & 24 deletions lib/success.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
118 changes: 118 additions & 0 deletions test/fail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
});
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

0 comments on commit 3132c19

Please sign in to comment.