2222// Attachments (images and files) will not copy over - they need manual handling.
2323//
2424// TODO: Polls don't copy options
25- // TODO: Mark as answers
2625
2726const { Octokit } = require ( "octokit" ) ;
2827
@@ -158,6 +157,9 @@ const FETCH_DISCUSSIONS_QUERY = `
158157 description
159158 }
160159 }
160+ answer {
161+ id
162+ }
161163 author {
162164 login
163165 }
@@ -300,6 +302,18 @@ const CLOSE_DISCUSSION_MUTATION = `
300302 }
301303` ;
302304
305+ const MARK_DISCUSSION_COMMENT_AS_ANSWER_MUTATION = `
306+ mutation($commentId: ID!) {
307+ markDiscussionCommentAsAnswer(input: {
308+ id: $commentId
309+ }) {
310+ discussion {
311+ id
312+ }
313+ }
314+ }
315+ ` ;
316+
303317// Main functions
304318async function checkDiscussionsEnabled ( octokit , owner , repo ) {
305319 log ( `Checking if discussions are enabled in ${ owner } /${ repo } ...` ) ;
@@ -581,15 +595,36 @@ async function closeDiscussion(octokit, discussionId) {
581595 }
582596}
583597
584- async function copyDiscussionComments ( octokit , discussionId , comments ) {
598+ async function markCommentAsAnswer ( octokit , commentId ) {
599+ log ( "Marking comment as answer..." ) ;
600+
601+ await rateLimitSleep ( 2 ) ;
602+
603+ try {
604+ await octokit . graphql ( MARK_DISCUSSION_COMMENT_AS_ANSWER_MUTATION , {
605+ commentId
606+ } ) ;
607+
608+ log ( "✓ Comment marked as answer" ) ;
609+ return true ;
610+ } catch ( err ) {
611+ error ( `Failed to mark comment as answer: ${ err . message } ` ) ;
612+ return false ;
613+ }
614+ }
615+
616+ async function copyDiscussionComments ( octokit , discussionId , comments , answerCommentId = null ) {
585617 if ( ! comments || comments . length === 0 ) {
586618 log ( "No comments to copy for this discussion" ) ;
587- return ;
619+ return null ;
588620 }
589621
590622 log ( `Copying ${ comments . length } comments...` ) ;
591623 totalComments += comments . length ;
592624
625+ // Map to track source comment ID to target comment ID
626+ const commentIdMap = new Map ( ) ;
627+
593628 for ( const comment of comments ) {
594629 if ( ! comment . body ) continue ;
595630
@@ -609,6 +644,9 @@ async function copyDiscussionComments(octokit, discussionId, comments) {
609644 if ( newCommentId ) {
610645 copiedComments ++ ;
611646
647+ // Track the mapping
648+ commentIdMap . set ( comment . id , newCommentId ) ;
649+
612650 // Copy replies if any
613651 const replies = comment . replies ?. nodes || [ ] ;
614652 if ( replies . length > 0 ) {
@@ -638,6 +676,13 @@ async function copyDiscussionComments(octokit, discussionId, comments) {
638676 }
639677
640678 log ( "✓ Finished copying comments" ) ;
679+
680+ // Return the new comment ID if this was the answer comment
681+ if ( answerCommentId && commentIdMap . has ( answerCommentId ) ) {
682+ return commentIdMap . get ( answerCommentId ) ;
683+ }
684+
685+ return null ;
641686}
642687
643688async function processDiscussionsPage ( sourceOctokit , targetOctokit , owner , repo , targetRepoId , targetCategories , targetLabels , cursor = null ) {
@@ -720,7 +765,19 @@ async function processDiscussionsPage(sourceOctokit, targetOctokit, owner, repo,
720765 // Copy comments
721766 log ( "Processing comments for discussion..." ) ;
722767 const comments = await fetchDiscussionComments ( sourceOctokit , discussion . id ) ;
723- await copyDiscussionComments ( targetOctokit , newDiscussion . id , comments ) ;
768+ const answerCommentId = discussion . answer ?. id || null ;
769+ const newAnswerCommentId = await copyDiscussionComments (
770+ targetOctokit ,
771+ newDiscussion . id ,
772+ comments ,
773+ answerCommentId
774+ ) ;
775+
776+ // Mark answer if applicable
777+ if ( newAnswerCommentId ) {
778+ log ( "Source discussion has an answer comment, marking it in target..." ) ;
779+ await markCommentAsAnswer ( targetOctokit , newAnswerCommentId ) ;
780+ }
724781
725782 // Close discussion if it was closed in source
726783 if ( discussion . closed ) {
0 commit comments