From 8d7024224de5e6338521a705834ae024556a5b9f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 5 Jul 2026 06:33:46 +0000
Subject: [PATCH 1/4] Initial plan
From aa786259cfdd18ff93070b0a9a0c3a7179c53d02 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 5 Jul 2026 06:45:22 +0000
Subject: [PATCH 2/4] refactor: deduplicate skip query gate logic
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/check_skip_if_helpers.cjs | 72 ++++++++++++++++++-
.../setup/js/check_skip_if_helpers.test.cjs | 62 ++++++++++++++++
actions/setup/js/check_skip_if_match.cjs | 63 +++++-----------
actions/setup/js/check_skip_if_no_match.cjs | 63 +++++-----------
4 files changed, 165 insertions(+), 95 deletions(-)
diff --git a/actions/setup/js/check_skip_if_helpers.cjs b/actions/setup/js/check_skip_if_helpers.cjs
index 1ede805da65..9165de8eee6 100644
--- a/actions/setup/js/check_skip_if_helpers.cjs
+++ b/actions/setup/js/check_skip_if_helpers.cjs
@@ -1,6 +1,10 @@
// @ts-check
///
+const { getErrorMessage } = require("./error_helpers.cjs");
+const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
+const { writeDenialSummary } = require("./pre_activation_summary.cjs");
+
/**
* Builds the GitHub search query, optionally scoping it to the current repository.
* @param {string} skipQuery - The base query string
@@ -18,4 +22,70 @@ function buildSearchQuery(skipQuery, skipScope) {
return searchQuery;
}
-module.exports = { buildSearchQuery };
+/**
+ * Shared runner for skip-if query gates.
+ * @param {{
+ * skipQuery: string | undefined;
+ * workflowName: string | undefined;
+ * thresholdStr: string | undefined;
+ * thresholdEnvVar: string;
+ * thresholdLabel: string;
+ * checkLabel: string;
+ * outputName: string;
+ * skipScope: string | undefined;
+ * shouldSkip: (totalCount: number, threshold: number) => boolean;
+ * warningMessage: (totalCount: number, threshold: number) => string;
+ * successMessage: (totalCount: number, threshold: number) => string;
+ * denialSummaryMessage: (totalCount: number, threshold: number) => string;
+ * denialSummaryNextStep: string;
+ * }} options
+ */
+async function runSkipQueryGate(options) {
+ const { skipQuery, workflowName, thresholdStr = "1", thresholdEnvVar, thresholdLabel, checkLabel, outputName, skipScope, shouldSkip, warningMessage, successMessage, denialSummaryMessage, denialSummaryNextStep } = options;
+
+ if (!skipQuery) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_QUERY not specified.`);
+ return;
+ }
+
+ if (!workflowName) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_WORKFLOW_NAME not specified.`);
+ return;
+ }
+
+ const threshold = parseInt(thresholdStr, 10);
+ if (isNaN(threshold) || threshold < 1) {
+ core.setFailed(`${ERR_CONFIG}: Configuration error: ${thresholdEnvVar} must be a positive integer, got "${thresholdStr}".`);
+ return;
+ }
+
+ core.info(`Checking ${checkLabel} query: ${skipQuery}`);
+ core.info(`${thresholdLabel}: ${threshold}`);
+
+ const searchQuery = buildSearchQuery(skipQuery, skipScope);
+
+ try {
+ const {
+ data: { total_count: totalCount },
+ } = await github.rest.search.issuesAndPullRequests({
+ q: searchQuery,
+ per_page: 1,
+ });
+
+ core.info(`Search found ${totalCount} matching items`);
+
+ if (shouldSkip(totalCount, threshold)) {
+ core.warning(warningMessage(totalCount, threshold));
+ core.setOutput(outputName, "false");
+ await writeDenialSummary(denialSummaryMessage(totalCount, threshold), denialSummaryNextStep);
+ return;
+ }
+
+ core.info(successMessage(totalCount, threshold));
+ core.setOutput(outputName, "true");
+ } catch (error) {
+ core.setFailed(`${ERR_API}: Failed to execute search query: ${getErrorMessage(error)}`);
+ }
+}
+
+module.exports = { buildSearchQuery, runSkipQueryGate };
diff --git a/actions/setup/js/check_skip_if_helpers.test.cjs b/actions/setup/js/check_skip_if_helpers.test.cjs
index 54ac24a41b1..05ce37c286e 100644
--- a/actions/setup/js/check_skip_if_helpers.test.cjs
+++ b/actions/setup/js/check_skip_if_helpers.test.cjs
@@ -69,6 +69,68 @@ describe("check_skip_if_helpers.cjs - buildSearchQuery", () => {
});
});
+ describe("check_skip_if_helpers.cjs - runSkipQueryGate", () => {
+ /** @returns {Promise<{runSkipQueryGate: (options: any) => Promise}>} */
+ const loadModule = () => import("./check_skip_if_helpers.cjs");
+
+ beforeEach(() => {
+ global.github = {
+ rest: {
+ search: {
+ issuesAndPullRequests: vi.fn(),
+ },
+ },
+ };
+ });
+
+ it("uses thresholdEnvVar in validation errors", async () => {
+ const { runSkipQueryGate } = await loadModule();
+
+ await runSkipQueryGate({
+ skipQuery: "is:issue is:open",
+ workflowName: "test-workflow",
+ thresholdStr: "invalid",
+ thresholdEnvVar: "GH_AW_SKIP_MAX_MATCHES",
+ thresholdLabel: "Maximum matches threshold",
+ checkLabel: "skip-if-match",
+ outputName: "skip_check_ok",
+ skipScope: undefined,
+ shouldSkip: () => false,
+ warningMessage: () => "",
+ successMessage: () => "",
+ denialSummaryMessage: () => "",
+ denialSummaryNextStep: "",
+ });
+
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("GH_AW_SKIP_MAX_MATCHES must be a positive integer"));
+ expect(global.github.rest.search.issuesAndPullRequests).not.toHaveBeenCalled();
+ });
+
+ it("applies policy decision and output name when skip condition matches", async () => {
+ global.github.rest.search.issuesAndPullRequests.mockResolvedValue({ data: { total_count: 2 } });
+ const { runSkipQueryGate } = await loadModule();
+
+ await runSkipQueryGate({
+ skipQuery: "is:issue is:open",
+ workflowName: "test-workflow",
+ thresholdStr: "1",
+ thresholdEnvVar: "GH_AW_SKIP_MAX_MATCHES",
+ thresholdLabel: "Maximum matches threshold",
+ checkLabel: "skip-if-match",
+ outputName: "skip_check_ok",
+ skipScope: undefined,
+ shouldSkip: (totalCount, threshold) => totalCount >= threshold,
+ warningMessage: () => "skip warning",
+ successMessage: () => "success message",
+ denialSummaryMessage: () => "summary message",
+ denialSummaryNextStep: "summary next step",
+ });
+
+ expect(mockCore.warning).toHaveBeenCalledWith("skip warning");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("skip_check_ok", "false");
+ });
+ });
+
describe("scope: default (repo-scoped)", () => {
it("appends repo:owner/repo when skipScope is undefined", async () => {
const { buildSearchQuery } = await loadModule();
diff --git a/actions/setup/js/check_skip_if_match.cjs b/actions/setup/js/check_skip_if_match.cjs
index fc379c91e7a..a4dc5c23810 100644
--- a/actions/setup/js/check_skip_if_match.cjs
+++ b/actions/setup/js/check_skip_if_match.cjs
@@ -1,57 +1,26 @@
// @ts-check
///
-const { getErrorMessage } = require("./error_helpers.cjs");
-const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
-const { buildSearchQuery } = require("./check_skip_if_helpers.cjs");
-const { writeDenialSummary } = require("./pre_activation_summary.cjs");
+const { runSkipQueryGate } = require("./check_skip_if_helpers.cjs");
async function main() {
const { GH_AW_SKIP_QUERY: skipQuery, GH_AW_WORKFLOW_NAME: workflowName, GH_AW_SKIP_MAX_MATCHES: maxMatchesStr = "1", GH_AW_SKIP_SCOPE: skipScope } = process.env;
- if (!skipQuery) {
- core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_QUERY not specified.`);
- return;
- }
-
- if (!workflowName) {
- core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_WORKFLOW_NAME not specified.`);
- return;
- }
-
- const maxMatches = parseInt(maxMatchesStr, 10);
- if (isNaN(maxMatches) || maxMatches < 1) {
- core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_MAX_MATCHES must be a positive integer, got "${maxMatchesStr}".`);
- return;
- }
-
- core.info(`Checking skip-if-match query: ${skipQuery}`);
- core.info(`Maximum matches threshold: ${maxMatches}`);
-
- const searchQuery = buildSearchQuery(skipQuery, skipScope);
-
- try {
- const {
- data: { total_count: totalCount },
- } = await github.rest.search.issuesAndPullRequests({
- q: searchQuery,
- per_page: 1,
- });
-
- core.info(`Search found ${totalCount} matching items`);
-
- if (totalCount >= maxMatches) {
- core.warning(`🔍 Skip condition matched (${totalCount} items found, threshold: ${maxMatches}). Workflow execution will be prevented by activation job.`);
- core.setOutput("skip_check_ok", "false");
- await writeDenialSummary(`Skip-if-match query matched: ${totalCount} item(s) found (threshold: ${maxMatches}).`, "Update `on.skip-if-match:` in the workflow frontmatter if this skip was unexpected.");
- return;
- }
-
- core.info(`✓ Found ${totalCount} matches (below threshold of ${maxMatches}), workflow can proceed`);
- core.setOutput("skip_check_ok", "true");
- } catch (error) {
- core.setFailed(`${ERR_API}: Failed to execute search query: ${getErrorMessage(error)}`);
- }
+ await runSkipQueryGate({
+ skipQuery,
+ workflowName,
+ thresholdStr: maxMatchesStr,
+ thresholdEnvVar: "GH_AW_SKIP_MAX_MATCHES",
+ thresholdLabel: "Maximum matches threshold",
+ checkLabel: "skip-if-match",
+ outputName: "skip_check_ok",
+ skipScope,
+ shouldSkip: (totalCount, threshold) => totalCount >= threshold,
+ warningMessage: (totalCount, threshold) => `🔍 Skip condition matched (${totalCount} items found, threshold: ${threshold}). Workflow execution will be prevented by activation job.`,
+ successMessage: (totalCount, threshold) => `✓ Found ${totalCount} matches (below threshold of ${threshold}), workflow can proceed`,
+ denialSummaryMessage: (totalCount, threshold) => `Skip-if-match query matched: ${totalCount} item(s) found (threshold: ${threshold}).`,
+ denialSummaryNextStep: "Update `on.skip-if-match:` in the workflow frontmatter if this skip was unexpected.",
+ });
}
module.exports = { main };
diff --git a/actions/setup/js/check_skip_if_no_match.cjs b/actions/setup/js/check_skip_if_no_match.cjs
index b3edfef7803..6d67cf8f032 100644
--- a/actions/setup/js/check_skip_if_no_match.cjs
+++ b/actions/setup/js/check_skip_if_no_match.cjs
@@ -1,57 +1,26 @@
// @ts-check
///
-const { getErrorMessage } = require("./error_helpers.cjs");
-const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
-const { buildSearchQuery } = require("./check_skip_if_helpers.cjs");
-const { writeDenialSummary } = require("./pre_activation_summary.cjs");
+const { runSkipQueryGate } = require("./check_skip_if_helpers.cjs");
async function main() {
const { GH_AW_SKIP_QUERY: skipQuery, GH_AW_WORKFLOW_NAME: workflowName, GH_AW_SKIP_MIN_MATCHES: minMatchesStr = "1", GH_AW_SKIP_SCOPE: skipScope } = process.env;
- if (!skipQuery) {
- core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_QUERY not specified.`);
- return;
- }
-
- if (!workflowName) {
- core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_WORKFLOW_NAME not specified.`);
- return;
- }
-
- const minMatches = parseInt(minMatchesStr, 10);
- if (isNaN(minMatches) || minMatches < 1) {
- core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_MIN_MATCHES must be a positive integer, got "${minMatchesStr}".`);
- return;
- }
-
- core.info(`Checking skip-if-no-match query: ${skipQuery}`);
- core.info(`Minimum matches threshold: ${minMatches}`);
-
- const searchQuery = buildSearchQuery(skipQuery, skipScope);
-
- try {
- const {
- data: { total_count: totalCount },
- } = await github.rest.search.issuesAndPullRequests({
- q: searchQuery,
- per_page: 1,
- });
-
- core.info(`Search found ${totalCount} matching items`);
-
- if (totalCount < minMatches) {
- core.warning(`🔍 Skip condition matched (${totalCount} items found, minimum required: ${minMatches}). Workflow execution will be prevented by activation job.`);
- core.setOutput("skip_no_match_check_ok", "false");
- await writeDenialSummary(`Skip-if-no-match query returned too few results: ${totalCount} item(s) found (minimum required: ${minMatches}).`, "Update `on.skip-if-no-match:` in the workflow frontmatter if this skip was unexpected.");
- return;
- }
-
- core.info(`✓ Found ${totalCount} matches (meets or exceeds minimum of ${minMatches}), workflow can proceed`);
- core.setOutput("skip_no_match_check_ok", "true");
- } catch (error) {
- core.setFailed(`${ERR_API}: Failed to execute search query: ${getErrorMessage(error)}`);
- }
+ await runSkipQueryGate({
+ skipQuery,
+ workflowName,
+ thresholdStr: minMatchesStr,
+ thresholdEnvVar: "GH_AW_SKIP_MIN_MATCHES",
+ thresholdLabel: "Minimum matches threshold",
+ checkLabel: "skip-if-no-match",
+ outputName: "skip_no_match_check_ok",
+ skipScope,
+ shouldSkip: (totalCount, threshold) => totalCount < threshold,
+ warningMessage: (totalCount, threshold) => `🔍 Skip condition matched (${totalCount} items found, minimum required: ${threshold}). Workflow execution will be prevented by activation job.`,
+ successMessage: (totalCount, threshold) => `✓ Found ${totalCount} matches (meets or exceeds minimum of ${threshold}), workflow can proceed`,
+ denialSummaryMessage: (totalCount, threshold) => `Skip-if-no-match query returned too few results: ${totalCount} item(s) found (minimum required: ${threshold}).`,
+ denialSummaryNextStep: "Update `on.skip-if-no-match:` in the workflow frontmatter if this skip was unexpected.",
+ });
}
module.exports = { main };
From 08ad2b900d827fab84b4a6191f506a565fc031ca Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 5 Jul 2026 07:57:33 +0000
Subject: [PATCH 3/4] refactor: address review feedback on runSkipQueryGate
helper
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Break single-line destructure to multi-line (Prettier at printWidth:240 normalizes it; semantically correct)
- Use workflowName in log line — eliminates dead validation
- Remove thresholdStr default from helper (callers own their defaults)
- Move runSkipQueryGate describe to top-level sibling of buildSearchQuery
- Remove duplicate loadModule inside runSkipQueryGate describe
- Add missing test cases: skipQuery/workflowName early-exit guards,
success path (skip condition not met), API error path
- Assert writeDenialSummary side effects in skip-path test
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/check_skip_if_helpers.cjs | 4 +-
.../setup/js/check_skip_if_helpers.test.cjs | 149 ++++++++++--------
2 files changed, 90 insertions(+), 63 deletions(-)
diff --git a/actions/setup/js/check_skip_if_helpers.cjs b/actions/setup/js/check_skip_if_helpers.cjs
index 9165de8eee6..7c688232d5f 100644
--- a/actions/setup/js/check_skip_if_helpers.cjs
+++ b/actions/setup/js/check_skip_if_helpers.cjs
@@ -41,7 +41,7 @@ function buildSearchQuery(skipQuery, skipScope) {
* }} options
*/
async function runSkipQueryGate(options) {
- const { skipQuery, workflowName, thresholdStr = "1", thresholdEnvVar, thresholdLabel, checkLabel, outputName, skipScope, shouldSkip, warningMessage, successMessage, denialSummaryMessage, denialSummaryNextStep } = options;
+ const { skipQuery, workflowName, thresholdStr, thresholdEnvVar, thresholdLabel, checkLabel, outputName, skipScope, shouldSkip, warningMessage, successMessage, denialSummaryMessage, denialSummaryNextStep } = options;
if (!skipQuery) {
core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_QUERY not specified.`);
@@ -53,6 +53,8 @@ async function runSkipQueryGate(options) {
return;
}
+ core.info(`Running ${checkLabel} gate for workflow: ${workflowName}`);
+
const threshold = parseInt(thresholdStr, 10);
if (isNaN(threshold) || threshold < 1) {
core.setFailed(`${ERR_CONFIG}: Configuration error: ${thresholdEnvVar} must be a positive integer, got "${thresholdStr}".`);
diff --git a/actions/setup/js/check_skip_if_helpers.test.cjs b/actions/setup/js/check_skip_if_helpers.test.cjs
index 05ce37c286e..4e9970658d6 100644
--- a/actions/setup/js/check_skip_if_helpers.test.cjs
+++ b/actions/setup/js/check_skip_if_helpers.test.cjs
@@ -69,68 +69,6 @@ describe("check_skip_if_helpers.cjs - buildSearchQuery", () => {
});
});
- describe("check_skip_if_helpers.cjs - runSkipQueryGate", () => {
- /** @returns {Promise<{runSkipQueryGate: (options: any) => Promise}>} */
- const loadModule = () => import("./check_skip_if_helpers.cjs");
-
- beforeEach(() => {
- global.github = {
- rest: {
- search: {
- issuesAndPullRequests: vi.fn(),
- },
- },
- };
- });
-
- it("uses thresholdEnvVar in validation errors", async () => {
- const { runSkipQueryGate } = await loadModule();
-
- await runSkipQueryGate({
- skipQuery: "is:issue is:open",
- workflowName: "test-workflow",
- thresholdStr: "invalid",
- thresholdEnvVar: "GH_AW_SKIP_MAX_MATCHES",
- thresholdLabel: "Maximum matches threshold",
- checkLabel: "skip-if-match",
- outputName: "skip_check_ok",
- skipScope: undefined,
- shouldSkip: () => false,
- warningMessage: () => "",
- successMessage: () => "",
- denialSummaryMessage: () => "",
- denialSummaryNextStep: "",
- });
-
- expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("GH_AW_SKIP_MAX_MATCHES must be a positive integer"));
- expect(global.github.rest.search.issuesAndPullRequests).not.toHaveBeenCalled();
- });
-
- it("applies policy decision and output name when skip condition matches", async () => {
- global.github.rest.search.issuesAndPullRequests.mockResolvedValue({ data: { total_count: 2 } });
- const { runSkipQueryGate } = await loadModule();
-
- await runSkipQueryGate({
- skipQuery: "is:issue is:open",
- workflowName: "test-workflow",
- thresholdStr: "1",
- thresholdEnvVar: "GH_AW_SKIP_MAX_MATCHES",
- thresholdLabel: "Maximum matches threshold",
- checkLabel: "skip-if-match",
- outputName: "skip_check_ok",
- skipScope: undefined,
- shouldSkip: (totalCount, threshold) => totalCount >= threshold,
- warningMessage: () => "skip warning",
- successMessage: () => "success message",
- denialSummaryMessage: () => "summary message",
- denialSummaryNextStep: "summary next step",
- });
-
- expect(mockCore.warning).toHaveBeenCalledWith("skip warning");
- expect(mockCore.setOutput).toHaveBeenCalledWith("skip_check_ok", "false");
- });
- });
-
describe("scope: default (repo-scoped)", () => {
it("appends repo:owner/repo when skipScope is undefined", async () => {
const { buildSearchQuery } = await loadModule();
@@ -171,3 +109,90 @@ describe("check_skip_if_helpers.cjs - buildSearchQuery", () => {
});
});
});
+
+describe("check_skip_if_helpers.cjs - runSkipQueryGate", () => {
+ /** @returns {Promise<{runSkipQueryGate: (options: any) => Promise}>} */
+ const loadModule = () => import("./check_skip_if_helpers.cjs");
+
+ /** @type {Record} */
+ const baseOptions = {
+ skipQuery: "is:issue is:open",
+ workflowName: "test-workflow",
+ thresholdStr: "1",
+ thresholdEnvVar: "GH_AW_SKIP_MAX_MATCHES",
+ thresholdLabel: "Maximum matches threshold",
+ checkLabel: "skip-if-match",
+ outputName: "skip_check_ok",
+ skipScope: undefined,
+ shouldSkip: (/** @type {number} */ totalCount, /** @type {number} */ threshold) => totalCount >= threshold,
+ warningMessage: () => "skip warning",
+ successMessage: () => "success message",
+ denialSummaryMessage: () => "summary message",
+ denialSummaryNextStep: "summary next step",
+ };
+
+ beforeEach(() => {
+ global.github = {
+ rest: {
+ search: {
+ issuesAndPullRequests: vi.fn(),
+ },
+ },
+ };
+ });
+
+ it("fails with config error when skipQuery is undefined", async () => {
+ const { runSkipQueryGate } = await loadModule();
+ await runSkipQueryGate({ ...baseOptions, skipQuery: undefined });
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("GH_AW_SKIP_QUERY not specified"));
+ expect(global.github.rest.search.issuesAndPullRequests).not.toHaveBeenCalled();
+ });
+
+ it("fails with config error when workflowName is undefined", async () => {
+ const { runSkipQueryGate } = await loadModule();
+ await runSkipQueryGate({ ...baseOptions, workflowName: undefined });
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("GH_AW_WORKFLOW_NAME not specified"));
+ expect(global.github.rest.search.issuesAndPullRequests).not.toHaveBeenCalled();
+ });
+
+ it("uses thresholdEnvVar in validation errors", async () => {
+ const { runSkipQueryGate } = await loadModule();
+ await runSkipQueryGate({ ...baseOptions, thresholdStr: "invalid" });
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("GH_AW_SKIP_MAX_MATCHES must be a positive integer"));
+ expect(global.github.rest.search.issuesAndPullRequests).not.toHaveBeenCalled();
+ });
+
+ it("applies policy decision and output name when skip condition matches", async () => {
+ global.github.rest.search.issuesAndPullRequests.mockResolvedValue({ data: { total_count: 2 } });
+ const { runSkipQueryGate } = await loadModule();
+
+ await runSkipQueryGate({ ...baseOptions });
+
+ expect(mockCore.warning).toHaveBeenCalledWith("skip warning");
+ expect(mockCore.setOutput).toHaveBeenCalledWith("skip_check_ok", "false");
+ expect(mockCore.summary.addRaw).toHaveBeenCalled();
+ expect(mockCore.summary.write).toHaveBeenCalled();
+ });
+
+ it("sets output to true and logs success when skip condition is not met", async () => {
+ global.github.rest.search.issuesAndPullRequests.mockResolvedValue({ data: { total_count: 0 } });
+ const { runSkipQueryGate } = await loadModule();
+ const successMessage = vi.fn(() => "all clear");
+
+ await runSkipQueryGate({ ...baseOptions, successMessage });
+
+ expect(mockCore.setOutput).toHaveBeenCalledWith("skip_check_ok", "true");
+ expect(successMessage).toHaveBeenCalledWith(0, 1);
+ expect(mockCore.warning).not.toHaveBeenCalled();
+ });
+
+ it("calls setFailed with ERR_API prefix when search throws", async () => {
+ global.github.rest.search.issuesAndPullRequests.mockRejectedValue(new Error("rate limited"));
+ const { runSkipQueryGate } = await loadModule();
+
+ await runSkipQueryGate({ ...baseOptions });
+
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("ERR_API"));
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining("rate limited"));
+ });
+});
From 21dac8f23ddcec482e0d60ecabda5e77c77c3f13 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 5 Jul 2026 09:41:47 +0000
Subject: [PATCH 4/4] fix: resolve TS2345 type error and address remaining
review threads
- Fix TS2345: parseInt(thresholdStr ?? "", 10) so undefined thresholdStr
falls through to the isNaN guard instead of failing type-check
- Add prettier-ignore + multi-line destructure for readability
- Add ambient globals comment (core, github, context) for
discoverability by static analysis and IDE tooling
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
---
actions/setup/js/check_skip_if_helpers.cjs | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/actions/setup/js/check_skip_if_helpers.cjs b/actions/setup/js/check_skip_if_helpers.cjs
index 7c688232d5f..ed8813d5168 100644
--- a/actions/setup/js/check_skip_if_helpers.cjs
+++ b/actions/setup/js/check_skip_if_helpers.cjs
@@ -40,8 +40,15 @@ function buildSearchQuery(skipQuery, skipScope) {
* denialSummaryNextStep: string;
* }} options
*/
+// Ambient globals provided by @actions/github-script: core, github, context
async function runSkipQueryGate(options) {
- const { skipQuery, workflowName, thresholdStr, thresholdEnvVar, thresholdLabel, checkLabel, outputName, skipScope, shouldSkip, warningMessage, successMessage, denialSummaryMessage, denialSummaryNextStep } = options;
+ // prettier-ignore
+ const {
+ skipQuery, workflowName, thresholdStr,
+ thresholdEnvVar, thresholdLabel, checkLabel, outputName, skipScope,
+ shouldSkip, warningMessage, successMessage,
+ denialSummaryMessage, denialSummaryNextStep,
+ } = options;
if (!skipQuery) {
core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_SKIP_QUERY not specified.`);
@@ -55,7 +62,7 @@ async function runSkipQueryGate(options) {
core.info(`Running ${checkLabel} gate for workflow: ${workflowName}`);
- const threshold = parseInt(thresholdStr, 10);
+ const threshold = parseInt(thresholdStr ?? "", 10);
if (isNaN(threshold) || threshold < 1) {
core.setFailed(`${ERR_CONFIG}: Configuration error: ${thresholdEnvVar} must be a positive integer, got "${thresholdStr}".`);
return;