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

fix: replace searchAPI usage with GraphQL in findSRIssue util #907

Merged
merged 38 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
d8fb827
feat: add graphql query loader `loadGetSRIssuesQuery` to fetch SRIssues
babblebey Aug 28, 2024
8945a63
chore: add new `RELEASE_FAIL_LABEL` constant
babblebey Aug 28, 2024
70ca54f
feat: integrate issues fetch with graphql
babblebey Aug 28, 2024
7f9875a
feat: add fallback to searchAPI for backward compatibility
babblebey Aug 28, 2024
f2c9b84
feat: integrated config label in SRIssues search
babblebey Aug 28, 2024
f8160f3
fix: error `getSRIssue` graphql query label param type
babblebey Aug 28, 2024
63c1dc5
fix: undefined `data` property destructed from graphql reponse
babblebey Aug 28, 2024
9189ccf
refactor: modified wrong `body` property in query
babblebey Aug 28, 2024
a478a2b
refactor: remove conditions from searchAPI fallback logic
babblebey Aug 28, 2024
4bf11b0
refactor: replace `getSRIssues` graphql query `label` param with `fil…
babblebey Aug 28, 2024
7be5548
feat: implement unique issue sorting to address fallback `backwardIss…
babblebey Aug 28, 2024
c95a52f
feat: modify `findSRIssue` integration in `success` script; add `logg…
babblebey Aug 28, 2024
5c18493
feat: integrate opinionated `RELEASE_FAIL_LABEL` into `fail` script
babblebey Aug 28, 2024
cf6f182
chore: Questions and lint fixes
babblebey Aug 28, 2024
f4aec76
Merge branch 'master' into feat/gql-for-search-api
babblebey Aug 29, 2024
9373a70
refactor: modified `findSRIssues` integration in `fail` script
babblebey Aug 29, 2024
66437fd
Merge branch 'feat/gql-for-search-api' of https://github.com/semantic…
babblebey Aug 29, 2024
cbe47c7
test: fixed `findSRIssue` units test
babblebey Aug 29, 2024
2efba0e
test: fixed `fail` unit tests
babblebey Aug 29, 2024
91dec1c
test: fix integrations test
babblebey Aug 29, 2024
ba214de
test: fixed `success` unit tests
babblebey Aug 29, 2024
50c3f9c
test: `Verify, release and notify success` fix attempt
babblebey Aug 29, 2024
2f2b154
test: addressed `"Verify, release and notify success"` case in `integ…
babblebey Aug 30, 2024
b03fcd8
Merge branch 'master' into feat/gql-for-search-api
babblebey Sep 2, 2024
ebfbaf0
Merge branch 'master' of https://github.com/semantic-release/github i…
babblebey Sep 2, 2024
a0ac832
test: fix `success` units
babblebey Sep 2, 2024
f3a6bd6
test: fix `fail` units
babblebey Sep 2, 2024
5a6d6dd
refactor: remove error object from searchAPI fallback error handle
babblebey Sep 2, 2024
0e3c2dc
test: add new case `"Handle error in searchAPI fallback"`
babblebey Sep 2, 2024
fc19b40
Merge branch 'master' into feat/gql-for-search-api
babblebey Sep 3, 2024
65adac0
Merge branch 'master' into feat/gql-for-search-api
babblebey Sep 10, 2024
21dea8c
Merge branch 'master' into feat/gql-for-search-api
babblebey Sep 11, 2024
1065e08
Revert "refactor: remove conditions from searchAPI fallback logic"
babblebey Sep 11, 2024
2ce3efe
modified `RELEASE_FAIL_LABEL` value to `semantic-release`
babblebey Sep 11, 2024
b595743
test: fix cases for conditional `searchAPI` fallback consumption
babblebey Sep 11, 2024
e40128f
Merge branch 'master' into feat/gql-for-search-api
babblebey Sep 16, 2024
9329cbc
Update lib/resolve-config.js
babblebey Sep 20, 2024
15ba2ad
Merge branch 'master' into feat/gql-for-search-api
babblebey Sep 20, 2024
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
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 = "release-failing";
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 @@ -57,7 +57,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 @@ -85,7 +92,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
62 changes: 56 additions & 6 deletions lib/find-sr-issues.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,61 @@
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
*/
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
}
}
}
}
`;
1 change: 1 addition & 0 deletions lib/resolve-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default function resolveConfig(
: failTitle,
failComment,
failCommentCondition,
// ATTN??? We opinionated :(???
babblebey marked this conversation as resolved.
Show resolved Hide resolved
labels: isNil(labels)
? ["semantic-release"]
: labels === false
babblebey marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
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 @@ -266,7 +267,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
69 changes: 64 additions & 5 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 @@ -36,6 +36,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 @@ -59,7 +66,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 @@ -117,6 +124,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 @@ -132,7 +146,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 @@ -185,6 +199,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 @@ -203,7 +224,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 @@ -258,6 +279,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 @@ -276,7 +304,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 @@ -335,6 +363,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`,
})
.postOnce("https://api.github.local/graphql", {
data: {
repository: {
issues: { nodes: issues },
},
},
})
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand Down Expand Up @@ -501,6 +536,18 @@ test('Does not post comments on existing issues when "failCommentCondition" is "
.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: issues },
},
},
},
)
.getOnce(
`https://api.github.local/search/issues?q=${encodeURIComponent(
"in:title",
Expand Down Expand Up @@ -551,6 +598,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