@@ -20,6 +20,69 @@ const AVAILABLE_TYPES_PATTERNS = [/one of:\s*(.+)$/i, /available(?: types?)?:\s*
2020const NO_ISSUE_TYPES_PATTERNS = [ / n o i s s u e t y p e s ? (?: a r e ) ? a v a i l a b l e / i, / i s s u e t y p e s ? (?: i s | a r e ) n o t (?: e n a b l e d | c o n f i g u r e d ) / i] ;
2121const NO_ISSUE_TYPES_AVAILABLE_ERROR = "No issue types are available for this repository. Issue types must be configured in the repository or organization settings." ;
2222
23+ /**
24+ * Fetches the node ID of an issue for use in GraphQL mutations.
25+ * @param {Object } githubClient - Authenticated GitHub client
26+ * @param {string } owner - Repository owner
27+ * @param {string } repo - Repository name
28+ * @param {number } issueNumber - Issue number
29+ * @returns {Promise<string> } Issue node ID
30+ */
31+ async function getIssueNodeId ( githubClient , owner , repo , issueNumber ) {
32+ const { data } = await githubClient . rest . issues . get ( { owner, repo, issue_number : issueNumber } ) ;
33+ return data . node_id ;
34+ }
35+
36+ /**
37+ * Fetches the available issue types for an organization.
38+ * For personal-account owners the query returns null and the call site receives an empty array.
39+ * @param {Object } githubClient - Authenticated GitHub client
40+ * @param {string } owner - Organization login
41+ * @returns {Promise<Array<{id: string, name: string}>> } Issue type nodes
42+ */
43+ async function fetchIssueTypesForOrg ( githubClient , owner ) {
44+ const result = await githubClient . graphql (
45+ `query($owner: String!) {
46+ organization(login: $owner) {
47+ issueTypes(first: 100) {
48+ nodes {
49+ id
50+ name
51+ }
52+ }
53+ }
54+ }` ,
55+ { owner }
56+ ) ;
57+ return result ?. organization ?. issueTypes ?. nodes ?? [ ] ;
58+ }
59+
60+ /**
61+ * Sets the issue type via GraphQL mutation using `IssueTypeUpdateInput`.
62+ * @param {Object } githubClient - Authenticated GitHub client
63+ * @param {string } issueNodeId - GraphQL node ID of the issue
64+ * @param {string } issueTypeId - GraphQL node ID of the issue type
65+ * @param {{ rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean } } intentMetadata - Intent metadata in GraphQL format
66+ * @returns {Promise<void> }
67+ */
68+ async function setIssueTypeById ( githubClient , issueNodeId , issueTypeId , intentMetadata ) {
69+ const issueType = { id : issueTypeId , ...intentMetadata } ;
70+ await githubClient . graphql (
71+ `mutation($issueId: ID!, $issueType: IssueTypeUpdateInput!) {
72+ updateIssue(input: { id: $issueId, issueType: $issueType }) {
73+ issue {
74+ id
75+ }
76+ }
77+ }` ,
78+ {
79+ issueId : issueNodeId ,
80+ issueType,
81+ headers : { "GraphQL-Features" : "update_issue_suggestions" } ,
82+ }
83+ ) ;
84+ }
85+
2386/**
2487 * @param {{ rationale?: string, confidence?: "LOW"|"MEDIUM"|"HIGH", suggest?: boolean } } intentMetadata Intent metadata in GraphQL format.
2588 * @returns {{ rationale?: string, confidence?: "low"|"medium"|"high", suggest?: boolean } } Intent metadata formatted for REST.
@@ -271,13 +334,35 @@ async function main(config = {}) {
271334 try {
272335 const { owner, repo } = repoParts ;
273336 const intentMetadata = normalizeIssueIntentMetadata ( item ) ;
274- const typeValue = buildIssueTypeValue ( isClear , resolvedIssueTypeName , intentMetadata ) ;
275- await githubClient . rest . issues . update ( {
276- owner,
277- repo,
278- issue_number : issueNumber ,
279- type : typeValue ,
280- } ) ;
337+
338+ if ( hasIssueIntentsRuntimeFeature ( ) && ! isClear ) {
339+ // GraphQL intent path: resolve the type's node ID from org issue types, then
340+ // call setIssueTypeById with IssueTypeUpdateInput + the GraphQL-Features header.
341+ core . info ( `Using GraphQL intent path (issue_intents runtime feature enabled)` ) ;
342+ core . info ( `Fetching issue node ID for issue #${ issueNumber } ` ) ;
343+ const issueNodeId = await getIssueNodeId ( githubClient , owner , repo , issueNumber ) ;
344+ core . info ( `Fetching issue types for org ${ owner } ` ) ;
345+ const issueTypes = await fetchIssueTypesForOrg ( githubClient , owner ) ;
346+ core . info ( `Found ${ issueTypes . length } issue type(s) for org ${ owner } ` ) ;
347+ const typeNode = issueTypes . find ( t => t . name . toLowerCase ( ) === resolvedIssueTypeName . toLowerCase ( ) ) ;
348+ if ( ! typeNode ) {
349+ const availableNames = issueTypes . map ( t => t . name ) . join ( ", " ) ;
350+ const error = availableNames ? `Issue type ${ JSON . stringify ( resolvedIssueTypeName ) } not found. Available types: ${ availableNames } ` : NO_ISSUE_TYPES_AVAILABLE_ERROR ;
351+ core . error ( `Failed to set issue type on issue #${ issueNumber } : ${ error } ` ) ;
352+ return { success : false , error } ;
353+ }
354+ core . info ( `Resolved issue type ${ JSON . stringify ( resolvedIssueTypeName ) } to node ID ${ typeNode . id } ` ) ;
355+ await setIssueTypeById ( githubClient , issueNodeId , typeNode . id , intentMetadata ) ;
356+ } else {
357+ // REST path: used for the clear case and when the issue_intents feature is off.
358+ const typeValue = buildIssueTypeValue ( isClear , resolvedIssueTypeName , intentMetadata ) ;
359+ await githubClient . rest . issues . update ( {
360+ owner,
361+ repo,
362+ issue_number : issueNumber ,
363+ type : typeValue ,
364+ } ) ;
365+ }
281366
282367 const successMsg = isClear ? `Successfully cleared issue type on issue #${ issueNumber } ` : `Successfully set issue type to ${ JSON . stringify ( resolvedIssueTypeName ) } on issue #${ issueNumber } ` ;
283368 core . info ( successMsg ) ;
0 commit comments