Skip to content

Commit

Permalink
Merge branch 'master' into add/warn
Browse files Browse the repository at this point in the history
  • Loading branch information
babblebey authored Sep 20, 2024
2 parents 775480e + 7fb46a3 commit 311eecf
Show file tree
Hide file tree
Showing 8 changed files with 998 additions and 303 deletions.
2 changes: 2 additions & 0 deletions lib/definitions/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const ISSUE_ID = "<!-- semantic-release:github -->";

export const RELEASE_NAME = "GitHub release";

export const RELEASE_FAIL_LABEL = "semantic-release";
13 changes: 10 additions & 3 deletions lib/fail.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { template } from "lodash-es";
import debugFactory from "debug";

import parseGithubUrl from "./parse-github-url.js";
import { ISSUE_ID } from "./definitions/constants.js";
import { ISSUE_ID, RELEASE_FAIL_LABEL } from "./definitions/constants.js";
import resolveConfig from "./resolve-config.js";
import { toOctokitOptions } from "./octokit.js";
import findSRIssues from "./find-sr-issues.js";
Expand Down Expand Up @@ -56,7 +56,14 @@ export default async function fail(pluginConfig, context, { Octokit }) {
const body = failComment
? template(failComment)({ branch, errors })
: getFailComment(branch, errors);
const [srIssue] = await findSRIssues(octokit, failTitle, owner, repo);
const [srIssue] = await findSRIssues(
octokit,
logger,
failTitle,
labels,
owner,
repo,
);

const canCommentOnOrCreateIssue = failCommentCondition
? template(failCommentCondition)({ ...context, issue: srIssue })
Expand Down Expand Up @@ -84,7 +91,7 @@ export default async function fail(pluginConfig, context, { Octokit }) {
repo,
title: failTitle,
body: `${body}\n\n${ISSUE_ID}`,
labels: labels || [],
labels: (labels || []).concat([RELEASE_FAIL_LABEL]),
assignees,
};
debug("create issue: %O", newIssue);
Expand Down
64 changes: 58 additions & 6 deletions lib/find-sr-issues.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,63 @@
import { ISSUE_ID } from "./definitions/constants.js";
import { uniqBy } from "lodash-es";
import { ISSUE_ID, RELEASE_FAIL_LABEL } from "./definitions/constants.js";

export default async (octokit, logger, title, labels, owner, repo) => {
let issues = [];

export default async (octokit, title, owner, repo) => {
const {
data: { items: issues },
} = await octokit.request("GET /search/issues", {
q: `in:title+repo:${owner}/${repo}+type:issue+state:open+${title}`,
repository: {
issues: { nodes: issueNodes },
},
} = await octokit.graphql(loadGetSRIssuesQuery, {
owner,
repo,
filter: {
labels: (labels || []).concat([RELEASE_FAIL_LABEL]),
},
});

return issues.filter((issue) => issue.body && issue.body.includes(ISSUE_ID));
issues.push(...issueNodes);

/**
* BACKWARD COMPATIBILITY: Fallback to the search API if the issue was not found in the GraphQL response.
* This fallback will be removed in a future release
*/
if (issueNodes.length === 0) {
try {
const {
data: { items: backwardIssues },
} = await octokit.request("GET /search/issues", {
q: `in:title+repo:${owner}/${repo}+type:issue+state:open+${title}`,
});
issues.push(...backwardIssues);
} catch (error) {
logger.log(
"An error occured fetching issue via fallback (with GH SearchAPI)",
);
}
}

const uniqueSRIssues = uniqBy(
issues.filter((issue) => issue.body && issue.body.includes(ISSUE_ID)),
"number",
);

return uniqueSRIssues;
};

/**
* GraphQL Query to et the semantic-release issues for a repository.
*/
const loadGetSRIssuesQuery = `#graphql
query getSRIssues($owner: String!, $repo: String!, $filter: IssueFilters) {
repository(owner: $owner, name: $repo) {
issues(first: 100, states: OPEN, filterBy: $filter) {
nodes {
number
title
body
}
}
}
}
`;
10 changes: 9 additions & 1 deletion lib/success.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default async function success(pluginConfig, context, { Octokit }) {
githubApiPathPrefix,
githubApiUrl,
proxy,
labels,
successComment,
successCommentCondition,
failTitle,
Expand Down Expand Up @@ -265,7 +266,14 @@ export default async function success(pluginConfig, context, { Octokit }) {
if (failComment === false || failTitle === false) {
logger.log("Skip closing issue.");
} else {
const srIssues = await findSRIssues(octokit, failTitle, owner, repo);
const srIssues = await findSRIssues(
octokit,
logger,
failTitle,
labels,
owner,
repo,
);

debug("found semantic-release issues: %O", srIssues);

Expand Down
83 changes: 63 additions & 20 deletions test/fail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import sinon from "sinon";
import test from "ava";
import fetchMock from "fetch-mock";

import { ISSUE_ID } from "../lib/definitions/constants.js";
import { ISSUE_ID, RELEASE_FAIL_LABEL } from "../lib/definitions/constants.js";
import { TestOctokit } from "./helpers/test-octokit.js";

/* eslint camelcase: ["error", {properties: "never"}] */
Expand Down Expand Up @@ -41,6 +41,13 @@ test("Open a new issue with the list of errors", async (t) => {
.getOnce("https://api.github.local/repos/test_user/test_repo", {
full_name: `${redirectedOwner}/${redirectedRepo}`,
})
.postOnce("https://api.github.local/graphql", {
data: {
repository: {
issues: { nodes: [] },
},
},
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand All @@ -64,7 +71,7 @@ test("Open a new issue with the list of errors", async (t) => {
data.body,
/---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/,
);
t.deepEqual(data.labels, ["semantic-release"]);
t.deepEqual(data.labels, ["semantic-release", RELEASE_FAIL_LABEL]);
return true;
},
{
Expand Down Expand Up @@ -122,6 +129,13 @@ test("Open a new issue with the list of errors and custom title and comment", as
full_name: `${owner}/${repo}`,
clone_url: `https://api.github.local/${owner}/${repo}.git`,
})
.postOnce("https://api.github.local/graphql", {
data: {
repository: {
issues: { nodes: [] },
},
},
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand All @@ -137,7 +151,7 @@ test("Open a new issue with the list of errors and custom title and comment", as
body: {
title: failTitle,
body: `branch master Error message 1 Error message 2 Error message 3\n\n${ISSUE_ID}`,
labels: ["semantic-release"],
labels: ["semantic-release", RELEASE_FAIL_LABEL],
},
},
);
Expand Down Expand Up @@ -190,6 +204,13 @@ test("Open a new issue with assignees and the list of errors", async (t) => {
full_name: `${owner}/${repo}`,
clone_url: `https://api.github.local/${owner}/${repo}.git`,
})
.postOnce("https://api.github.local/graphql", {
data: {
repository: {
issues: { nodes: [] },
},
},
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand All @@ -208,7 +229,7 @@ test("Open a new issue with assignees and the list of errors", async (t) => {
data.body,
/---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---/,
);
t.deepEqual(data.labels, ["semantic-release"]);
t.deepEqual(data.labels, ["semantic-release", RELEASE_FAIL_LABEL]);
t.deepEqual(data.assignees, ["user1", "user2"]);
return true;
},
Expand Down Expand Up @@ -263,6 +284,13 @@ test("Open a new issue without labels and the list of errors", async (t) => {
full_name: `${owner}/${repo}`,
clone_url: `https://api.github.local/${owner}/${repo}.git`,
})
.postOnce("https://api.github.local/graphql", {
data: {
repository: {
issues: { nodes: [] },
},
},
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand All @@ -281,7 +309,7 @@ test("Open a new issue without labels and the list of errors", async (t) => {
data.body,
/---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---/,
);
t.deepEqual(data.labels, []);
t.deepEqual(data.labels, [RELEASE_FAIL_LABEL]);
return true;
},
{ html_url: "https://github.com/issues/1", number: 1 },
Expand Down Expand Up @@ -340,14 +368,13 @@ test("Update the first existing issue with the list of errors", async (t) => {
full_name: `${owner}/${repo}`,
clone_url: `https://api.github.local/${owner}/${repo}.git`,
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
)}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent(
"type:issue",
)}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`,
{ items: issues },
)
.postOnce("https://api.github.local/graphql", {
data: {
repository: {
issues: { nodes: issues },
},
},
})
.postOnce(
(url, { body }) => {
t.is(
Expand Down Expand Up @@ -506,13 +533,17 @@ test('Does not post comments on existing issues when "failCommentCondition" is "
.getOnce(`https://api.github.local/repos/${owner}/${repo}`, {
full_name: `${owner}/${repo}`,
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
)}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent(
"type:issue",
)}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`,
{ items: issues },
.postOnce(
(url, { body }) =>
url === "https://api.github.local/graphql" &&
JSON.parse(body).query.includes("query getSRIssues("),
{
data: {
repository: {
issues: { nodes: issues },
},
},
},
);

await fail(
Expand Down Expand Up @@ -556,6 +587,18 @@ test(`Post new issue if none exists yet, but don't comment on existing issues wh
.getOnce(`https://api.github.local/repos/${owner}/${repo}`, {
full_name: `${owner}/${repo}`,
})
.postOnce(
(url, { body }) =>
url === "https://api.github.local/graphql" &&
JSON.parse(body).query.includes("query getSRIssues("),
{
data: {
repository: {
issues: { nodes: [] },
},
},
},
)
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand Down
Loading

0 comments on commit 311eecf

Please sign in to comment.