From 8ddda1b4d779f0d4ffa569d687ea3a9e0359c436 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 30 Apr 2026 04:55:10 +0000
Subject: [PATCH 1/2] jsweep: clean remove_trigger_label.cjs and add tests
- Move require('./error_helpers.cjs') from inside main() to top-level imports
- Add comprehensive test suite (14 tests) covering all code paths:
- Missing/invalid GH_AW_LABEL_NAMES config
- workflow_dispatch skip
- No label in payload
- Label not in configured list
- issues, pull_request, discussion label removal
- 404 non-fatal error handling
- Non-404 API error warnings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
actions/setup/js/remove_trigger_label.cjs | 3 +-
.../setup/js/remove_trigger_label.test.cjs | 220 ++++++++++++++++++
2 files changed, 221 insertions(+), 2 deletions(-)
create mode 100644 actions/setup/js/remove_trigger_label.test.cjs
diff --git a/actions/setup/js/remove_trigger_label.cjs b/actions/setup/js/remove_trigger_label.cjs
index 102342778a8..b9dd6df6de5 100644
--- a/actions/setup/js/remove_trigger_label.cjs
+++ b/actions/setup/js/remove_trigger_label.cjs
@@ -2,6 +2,7 @@
///
const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
+const { getErrorMessage } = require("./error_helpers.cjs");
/**
* Remove the label that triggered this workflow from the issue, pull request, or discussion.
@@ -13,8 +14,6 @@ const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
async function main() {
const labelNamesJSON = process.env.GH_AW_LABEL_NAMES;
- const { getErrorMessage } = require("./error_helpers.cjs");
-
if (!labelNamesJSON) {
core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_LABEL_NAMES not specified.`);
return;
diff --git a/actions/setup/js/remove_trigger_label.test.cjs b/actions/setup/js/remove_trigger_label.test.cjs
new file mode 100644
index 00000000000..be675a8d6f8
--- /dev/null
+++ b/actions/setup/js/remove_trigger_label.test.cjs
@@ -0,0 +1,220 @@
+// @ts-check
+import { describe, it, expect, beforeEach, vi } from "vitest";
+const { ERR_CONFIG, ERR_API } = require("./error_codes.cjs");
+
+const mockCore = {
+ info: vi.fn(),
+ warning: vi.fn(),
+ error: vi.fn(),
+ setFailed: vi.fn(),
+ setOutput: vi.fn(),
+};
+
+const mockGithub = {
+ rest: {
+ issues: {
+ removeLabel: vi.fn(),
+ },
+ },
+ graphql: vi.fn(),
+};
+
+/** @type {any} */
+let mockContext = {
+ eventName: "issues",
+ repo: { owner: "testowner", repo: "testrepo" },
+ payload: {
+ label: { name: "ai-label", node_id: "LA_label1" },
+ issue: { number: 42 },
+ },
+};
+
+global.core = mockCore;
+global.github = mockGithub;
+global.context = mockContext;
+
+/** @returns {Promise} */
+async function runScript() {
+ const { main } = await import("./remove_trigger_label.cjs?" + Date.now());
+ await main();
+}
+
+describe("remove_trigger_label", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ vi.resetModules();
+
+ process.env.GH_AW_LABEL_NAMES = JSON.stringify(["ai-label", "bot-run"]);
+
+ mockContext = {
+ eventName: "issues",
+ repo: { owner: "testowner", repo: "testrepo" },
+ payload: {
+ label: { name: "ai-label", node_id: "LA_label1" },
+ issue: { number: 42 },
+ },
+ };
+ global.context = mockContext;
+
+ mockGithub.rest.issues.removeLabel.mockResolvedValue({});
+ mockGithub.graphql.mockResolvedValue({});
+ });
+
+ afterEach(() => {
+ delete process.env.GH_AW_LABEL_NAMES;
+ });
+
+ describe("missing configuration", () => {
+ it("should fail when GH_AW_LABEL_NAMES is not set", async () => {
+ delete process.env.GH_AW_LABEL_NAMES;
+ await runScript();
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(ERR_CONFIG));
+ });
+
+ it("should fail when GH_AW_LABEL_NAMES is invalid JSON", async () => {
+ process.env.GH_AW_LABEL_NAMES = "not-valid-json";
+ await runScript();
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(ERR_CONFIG));
+ });
+
+ it("should fail when GH_AW_LABEL_NAMES is not an array", async () => {
+ process.env.GH_AW_LABEL_NAMES = JSON.stringify({ label: "ai-label" });
+ await runScript();
+ expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(ERR_CONFIG));
+ });
+ });
+
+ describe("workflow_dispatch event", () => {
+ it("should skip label removal for workflow_dispatch", async () => {
+ global.context = { ...mockContext, eventName: "workflow_dispatch", payload: {} };
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "");
+ });
+ });
+
+ describe("no trigger label in payload", () => {
+ it("should skip removal when payload has no label", async () => {
+ global.context = { ...mockContext, payload: { issue: { number: 42 } } };
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "");
+ });
+ });
+
+ describe("label not in configured list", () => {
+ it("should skip removal when label is not configured", async () => {
+ global.context = {
+ ...mockContext,
+ payload: { label: { name: "random-label" }, issue: { number: 42 } },
+ };
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "random-label");
+ });
+ });
+
+ describe("issues event", () => {
+ it("should remove label from issue", async () => {
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ issue_number: 42,
+ name: "ai-label",
+ });
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
+ });
+
+ it("should skip when issue number is missing", async () => {
+ global.context = {
+ ...mockContext,
+ payload: { label: { name: "ai-label" } },
+ };
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
+ });
+ });
+
+ describe("pull_request event", () => {
+ it("should remove label from pull request", async () => {
+ global.context = {
+ ...mockContext,
+ eventName: "pull_request",
+ payload: {
+ label: { name: "ai-label" },
+ pull_request: { number: 99 },
+ },
+ };
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).toHaveBeenCalledWith({
+ owner: "testowner",
+ repo: "testrepo",
+ issue_number: 99,
+ name: "ai-label",
+ });
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
+ });
+
+ it("should skip when PR number is missing", async () => {
+ global.context = {
+ ...mockContext,
+ eventName: "pull_request",
+ payload: { label: { name: "ai-label" } },
+ };
+ await runScript();
+ expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("discussion event", () => {
+ it("should remove label from discussion via graphql", async () => {
+ global.context = {
+ ...mockContext,
+ eventName: "discussion",
+ payload: {
+ label: { name: "ai-label", node_id: "LA_label1" },
+ discussion: { node_id: "D_disc1" },
+ },
+ };
+ await runScript();
+ expect(mockGithub.graphql).toHaveBeenCalledWith(
+ expect.stringContaining("removeLabelsFromLabelable"),
+ expect.objectContaining({
+ labelableId: "D_disc1",
+ labelIds: ["LA_label1"],
+ })
+ );
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
+ });
+
+ it("should skip when discussion node_id is missing", async () => {
+ global.context = {
+ ...mockContext,
+ eventName: "discussion",
+ payload: { label: { name: "ai-label" } },
+ };
+ await runScript();
+ expect(mockGithub.graphql).not.toHaveBeenCalled();
+ });
+ });
+
+ describe("error handling", () => {
+ it("should treat 404 error as already-removed (non-fatal)", async () => {
+ const err = Object.assign(new Error("Not Found"), { status: 404 });
+ mockGithub.rest.issues.removeLabel.mockRejectedValue(err);
+ await runScript();
+ expect(mockCore.setFailed).not.toHaveBeenCalled();
+ expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
+ });
+
+ it("should warn on non-404 API errors", async () => {
+ const err = Object.assign(new Error("Server error"), { status: 500 });
+ mockGithub.rest.issues.removeLabel.mockRejectedValue(err);
+ await runScript();
+ expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining(ERR_API));
+ expect(mockCore.setFailed).not.toHaveBeenCalled();
+ });
+ });
+});
From 11cff5bc9bcfd8a624df2a707a54d794dc92ec50 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Apr 2026 05:13:18 +0000
Subject: [PATCH 2/2] fix: add afterEach to vitest imports in
remove_trigger_label.test.cjs
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b453b824-12cd-44a5-a0de-5a6946b25d66
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/remove_trigger_label.test.cjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/actions/setup/js/remove_trigger_label.test.cjs b/actions/setup/js/remove_trigger_label.test.cjs
index be675a8d6f8..59d8ed413f5 100644
--- a/actions/setup/js/remove_trigger_label.test.cjs
+++ b/actions/setup/js/remove_trigger_label.test.cjs
@@ -1,5 +1,5 @@
// @ts-check
-import { describe, it, expect, beforeEach, vi } from "vitest";
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
const { ERR_CONFIG, ERR_API } = require("./error_codes.cjs");
const mockCore = {