From 23ac7eee6b65b160001065586b3a323257732893 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 1 Mar 2026 05:08:43 +0000
Subject: [PATCH 1/8] Initial plan
From fefbf9cae212c88916c7344df79407d355386486 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 1 Mar 2026 05:34:22 +0000
Subject: [PATCH 2/8] Add set-issue-type safe output type
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.../setup/js/safe_output_handler_manager.cjs | 1 +
actions/setup/js/set_issue_type.cjs | 215 ++++++++++++
actions/setup/js/set_issue_type.test.cjs | 311 ++++++++++++++++++
.../setup/js/types/safe-outputs-config.d.ts | 10 +
actions/setup/js/types/safe-outputs.d.ts | 13 +
pkg/workflow/compiler_safe_outputs_config.go | 21 ++
pkg/workflow/compiler_safe_outputs_job.go | 1 +
pkg/workflow/compiler_types.go | 1 +
pkg/workflow/imports.go | 5 +
pkg/workflow/js/safe_outputs_tools.json | 24 ++
pkg/workflow/safe_output_validation_config.go | 8 +
pkg/workflow/safe_outputs_config.go | 6 +
pkg/workflow/safe_outputs_permissions.go | 4 +
pkg/workflow/safe_outputs_tools_generation.go | 11 +-
pkg/workflow/safe_outputs_tools_test.go | 1 +
pkg/workflow/set_issue_type.go | 37 +++
16 files changed, 668 insertions(+), 1 deletion(-)
create mode 100644 actions/setup/js/set_issue_type.cjs
create mode 100644 actions/setup/js/set_issue_type.test.cjs
create mode 100644 pkg/workflow/set_issue_type.go
diff --git a/actions/setup/js/safe_output_handler_manager.cjs b/actions/setup/js/safe_output_handler_manager.cjs
index 5f7a2845f7f..3a20d3a6f83 100644
--- a/actions/setup/js/safe_output_handler_manager.cjs
+++ b/actions/setup/js/safe_output_handler_manager.cjs
@@ -47,6 +47,7 @@ const HANDLER_MAP = {
close_pull_request: "./close_pull_request.cjs",
mark_pull_request_as_ready_for_review: "./mark_pull_request_as_ready_for_review.cjs",
hide_comment: "./hide_comment.cjs",
+ set_issue_type: "./set_issue_type.cjs",
add_reviewer: "./add_reviewer.cjs",
assign_milestone: "./assign_milestone.cjs",
assign_to_user: "./assign_to_user.cjs",
diff --git a/actions/setup/js/set_issue_type.cjs b/actions/setup/js/set_issue_type.cjs
new file mode 100644
index 00000000000..d424eb3b637
--- /dev/null
+++ b/actions/setup/js/set_issue_type.cjs
@@ -0,0 +1,215 @@
+// @ts-check
+///
+
+/**
+ * @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction
+ */
+
+const { getErrorMessage } = require("./error_helpers.cjs");
+const { logStagedPreviewInfo } = require("./staged_preview.cjs");
+const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs");
+
+/** @type {string} Safe output type handled by this module */
+const HANDLER_TYPE = "set_issue_type";
+
+/**
+ * Fetches the node ID of an issue for use in GraphQL mutations.
+ * @param {Object} authClient - Authenticated GitHub client
+ * @param {string} owner - Repository owner
+ * @param {string} repo - Repository name
+ * @param {number} issueNumber - Issue number
+ * @returns {Promise} Issue node ID
+ */
+async function getIssueNodeId(authClient, owner, repo, issueNumber) {
+ const { data } = await authClient.rest.issues.get({
+ owner,
+ repo,
+ issue_number: issueNumber,
+ });
+ return data.node_id;
+}
+
+/**
+ * Fetches the available issue types for the given repository via GraphQL.
+ * Returns an array of { id, name } objects, or an empty array if not supported.
+ * @param {Object} authClient - Authenticated GitHub client
+ * @param {string} owner - Repository owner
+ * @param {string} repo - Repository name
+ * @returns {Promise>} Available issue types
+ */
+async function fetchIssueTypes(authClient, owner, repo) {
+ try {
+ const result = await authClient.graphql(
+ `query($owner: String!, $repo: String!) {
+ repository(owner: $owner, name: $repo) {
+ issueTypes(first: 100) {
+ nodes {
+ id
+ name
+ }
+ }
+ }
+ }`,
+ { owner, repo }
+ );
+ return result?.repository?.issueTypes?.nodes ?? [];
+ } catch (error) {
+ // Issue types may not be enabled for this repository/organization
+ // Log at debug level to aid debugging without being noisy
+ if (typeof core !== "undefined") {
+ core.debug(`Could not fetch issue types (may not be enabled): ${error instanceof Error ? error.message : String(error)}`);
+ }
+ return [];
+ }
+}
+
+/**
+ * Sets the issue type via GraphQL mutation.
+ * Passing null for typeId clears the type.
+ * @param {Object} authClient - Authenticated GitHub client
+ * @param {string} issueNodeId - GraphQL node ID of the issue
+ * @param {string|null} typeId - GraphQL node ID of the issue type, or null to clear
+ * @returns {Promise}
+ */
+async function setIssueTypeById(authClient, issueNodeId, typeId) {
+ await authClient.graphql(
+ `mutation($issueId: ID!, $typeId: ID) {
+ updateIssue(input: { id: $issueId, issueTypeId: $typeId }) {
+ issue {
+ id
+ }
+ }
+ }`,
+ { issueId: issueNodeId, typeId }
+ );
+}
+
+/**
+ * Main handler factory for set_issue_type
+ * Returns a message handler function that processes individual set_issue_type messages
+ * @type {HandlerFactoryFunction}
+ */
+async function main(config = {}) {
+ // Extract configuration
+ const allowedTypes = config.allowed || [];
+ const maxCount = config.max || 5;
+ const authClient = await createAuthenticatedGitHubClient(config);
+
+ // Check if we're in staged mode
+ const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true";
+
+ core.info(`Set issue type configuration: max=${maxCount}`);
+ if (allowedTypes.length > 0) {
+ core.info(`Allowed issue types: ${allowedTypes.join(", ")}`);
+ }
+
+ // Track how many items we've processed for max limit
+ let processedCount = 0;
+
+ /**
+ * Message handler function that processes a single set_issue_type message
+ * @param {Object} message - The set_issue_type message to process
+ * @param {Object} resolvedTemporaryIds - Map of temporary IDs to {repo, number}
+ * @returns {Promise