From 111a767299ebd5d0612053df08730f2e57b2e738 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 07:06:28 +0000 Subject: [PATCH 1/4] Initial plan From b3b918654e39912c185675644a46639ceb3eac61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 07:18:14 +0000 Subject: [PATCH 2/4] Implement subissue functionality in create_issue.js script Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/test-claude.lock.yml | 32 ++++++- pkg/workflow/create_issue_subissue_test.go | 102 +++++++++++++++++++++ pkg/workflow/js/create_issue.js | 32 ++++++- 3 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 pkg/workflow/create_issue_subissue_test.go diff --git a/.github/workflows/test-claude.lock.yml b/.github/workflows/test-claude.lock.yml index 241b1cb83ee..440c1cdc8a2 100644 --- a/.github/workflows/test-claude.lock.yml +++ b/.github/workflows/test-claude.lock.yml @@ -539,17 +539,47 @@ jobs: console.log('Labels:', labels); console.log('Body length:', body.length); + // Check if we're in an issue context (triggered by an issue event) + const parentIssueNumber = context.payload?.issue?.number; + let finalBody = body; + + if (parentIssueNumber) { + console.log('Detected issue context, parent issue #' + parentIssueNumber); + + // Add reference to parent issue in the child issue body + if (finalBody.trim()) { + finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; + } else { + finalBody = `Related to #${parentIssueNumber}`; + } + } + // Create the issue using GitHub API const { data: issue } = await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: title, - body: body, + body: finalBody, labels: labels }); console.log('Created issue #' + issue.number + ': ' + issue.html_url); + // If we have a parent issue, add a comment to it referencing the new child issue + if (parentIssueNumber) { + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parentIssueNumber, + body: `Created related issue: #${issue.number}` + }); + console.log('Added comment to parent issue #' + parentIssueNumber); + } catch (error) { + console.log('Warning: Could not add comment to parent issue:', error.message); + } + } + // Set output for other jobs to use core.setOutput('issue_number', issue.number); core.setOutput('issue_url', issue.html_url); diff --git a/pkg/workflow/create_issue_subissue_test.go b/pkg/workflow/create_issue_subissue_test.go new file mode 100644 index 00000000000..59091b075a5 --- /dev/null +++ b/pkg/workflow/create_issue_subissue_test.go @@ -0,0 +1,102 @@ +package workflow + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +// TestCreateIssueSubissueFeature tests that the create_issue.js script includes subissue functionality +func TestCreateIssueSubissueFeature(t *testing.T) { + // Test that the script contains the subissue detection logic + if !strings.Contains(createIssueScript, "context.payload?.issue?.number") { + t.Error("Expected create_issue.js to check for parent issue context") + } + + // Test that the script modifies the body when in issue context + if !strings.Contains(createIssueScript, "Related to #${parentIssueNumber}") { + t.Error("Expected create_issue.js to add parent issue reference to body") + } + + // Test that the script creates a comment on the parent issue + if !strings.Contains(createIssueScript, "github.rest.issues.createComment") { + t.Error("Expected create_issue.js to create comment on parent issue") + } + + // Test that the script has proper error handling for comment creation + if !strings.Contains(createIssueScript, "Warning: Could not add comment to parent issue") { + t.Error("Expected create_issue.js to have error handling for parent issue comment") + } + + // Test console logging for debugging + if !strings.Contains(createIssueScript, "Detected issue context, parent issue") { + t.Error("Expected create_issue.js to log when issue context is detected") + } +} + +// TestCreateIssueWorkflowCompilation tests that workflows with output.issue still compile correctly +func TestCreateIssueWorkflowCompilationWithSubissue(t *testing.T) { + // Create temporary directory for test files + tmpDir, err := os.MkdirTemp("", "subissue-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + testContent := `--- +name: Test Subissue Feature +on: + issues: + types: [opened] +permissions: + contents: read +engine: claude +output: + issue: + title-prefix: "[test] " + labels: [automation, test] +--- + +# Test Workflow + +This is a test workflow that should create an issue with subissue functionality. +Write output to ${{ env.GITHUB_AW_OUTPUT }}.` + + testFile := filepath.Join(tmpDir, "test-subissue.md") + if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil { + t.Fatal(err) + } + + compiler := NewCompiler(false, "", "test") + err = compiler.CompileWorkflow(testFile) + if err != nil { + t.Fatalf("Failed to compile workflow with output.issue: %v", err) + } + + // Read the generated lock file to verify content + lockFile := filepath.Join(tmpDir, "test-subissue.lock.yml") + lockContentBytes, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + lockContent := string(lockContentBytes) + + // Verify the compiled workflow includes the subissue functionality + if !strings.Contains(lockContent, "context.payload?.issue?.number") { + t.Error("Expected compiled workflow to include subissue detection") + } + + if !strings.Contains(lockContent, "Created related issue: #${issue.number}") { + t.Error("Expected compiled workflow to include parent issue comment") + } + + // Verify it still has the standard create_output_issue job structure + if !strings.Contains(lockContent, "create_output_issue:") { + t.Error("Expected create_output_issue job to be present") + } + + if !strings.Contains(lockContent, "permissions:\n contents: read\n issues: write") { + t.Error("Expected correct permissions in create_output_issue job") + } +} diff --git a/pkg/workflow/js/create_issue.js b/pkg/workflow/js/create_issue.js index df2567c89d0..acb59ab0ecd 100644 --- a/pkg/workflow/js/create_issue.js +++ b/pkg/workflow/js/create_issue.js @@ -62,17 +62,47 @@ console.log('Creating issue with title:', title); console.log('Labels:', labels); console.log('Body length:', body.length); +// Check if we're in an issue context (triggered by an issue event) +const parentIssueNumber = context.payload?.issue?.number; +let finalBody = body; + +if (parentIssueNumber) { + console.log('Detected issue context, parent issue #' + parentIssueNumber); + + // Add reference to parent issue in the child issue body + if (finalBody.trim()) { + finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; + } else { + finalBody = `Related to #${parentIssueNumber}`; + } +} + // Create the issue using GitHub API const { data: issue } = await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: title, - body: body, + body: finalBody, labels: labels }); console.log('Created issue #' + issue.number + ': ' + issue.html_url); +// If we have a parent issue, add a comment to it referencing the new child issue +if (parentIssueNumber) { + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parentIssueNumber, + body: `Created related issue: #${issue.number}` + }); + console.log('Added comment to parent issue #' + parentIssueNumber); + } catch (error) { + console.log('Warning: Could not add comment to parent issue:', error.message); + } +} + // Set output for other jobs to use core.setOutput('issue_number', issue.number); core.setOutput('issue_url', issue.html_url); \ No newline at end of file From 108a42a1fdebb917ba87efe823bbe402ffd7e640 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:24:57 +0000 Subject: [PATCH 3/4] Use GraphQL to create proper sub-issue relationships instead of text references Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/test-claude.lock.yml | 170 ++++++++++++++++---- pkg/workflow/create_issue_subissue_test.go | 36 +++-- pkg/workflow/js/create_issue.js | 172 +++++++++++++++++---- 3 files changed, 314 insertions(+), 64 deletions(-) diff --git a/.github/workflows/test-claude.lock.yml b/.github/workflows/test-claude.lock.yml index 440c1cdc8a2..7499ab15ff1 100644 --- a/.github/workflows/test-claude.lock.yml +++ b/.github/workflows/test-claude.lock.yml @@ -541,43 +541,161 @@ jobs: // Check if we're in an issue context (triggered by an issue event) const parentIssueNumber = context.payload?.issue?.number; - let finalBody = body; + let issue; if (parentIssueNumber) { console.log('Detected issue context, parent issue #' + parentIssueNumber); - // Add reference to parent issue in the child issue body - if (finalBody.trim()) { - finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; - } else { - finalBody = `Related to #${parentIssueNumber}`; - } - } + try { + // Get the parent issue's GraphQL node ID + const parentIssueQuery = ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + issue(number: $number) { + id + } + } + } + `; - // Create the issue using GitHub API - const { data: issue } = await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: finalBody, - labels: labels - }); + const parentIssueResult = await github.graphql(parentIssueQuery, { + owner: context.repo.owner, + repo: context.repo.repo, + number: parentIssueNumber + }); - console.log('Created issue #' + issue.number + ': ' + issue.html_url); + const parentIssueId = parentIssueResult.repository.issue.id; + console.log('Found parent issue GraphQL ID:', parentIssueId); - // If we have a parent issue, add a comment to it referencing the new child issue - if (parentIssueNumber) { - try { - await github.rest.issues.createComment({ + // Get the repository's GraphQL node ID + const repoQuery = ` + query($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + id + } + } + `; + + const repoResult = await github.graphql(repoQuery, { owner: context.repo.owner, - repo: context.repo.repo, - issue_number: parentIssueNumber, - body: `Created related issue: #${issue.number}` + repo: context.repo.repo }); - console.log('Added comment to parent issue #' + parentIssueNumber); + + const repositoryId = repoResult.repository.id; + console.log('Found repository GraphQL ID:', repositoryId); + + // Create the issue as a sub-issue using GraphQL mutation + const createIssueMutation = ` + mutation($repositoryId: ID!, $title: String!, $body: String, $labelIds: [ID!], $parentIssueId: ID!) { + createIssue(input: { + repositoryId: $repositoryId, + title: $title, + body: $body, + labelIds: $labelIds, + parentIssueId: $parentIssueId + }) { + issue { + id + number + url + } + } + } + `; + + // Get label IDs if labels are specified + let labelIds = []; + if (labels && labels.length > 0) { + const labelsQuery = ` + query($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + labels(first: 100) { + nodes { + id + name + } + } + } + } + `; + + const labelsResult = await github.graphql(labelsQuery, { + owner: context.repo.owner, + repo: context.repo.repo + }); + + const availableLabels = labelsResult.repository.labels.nodes; + labelIds = labels + .map(label => availableLabels.find(l => l.name.toLowerCase() === label.toLowerCase())?.id) + .filter(id => id); + + console.log('Found label IDs:', labelIds); + } + + const createIssueResult = await github.graphql(createIssueMutation, { + repositoryId: repositoryId, + title: title, + body: body || '', + labelIds: labelIds, + parentIssueId: parentIssueId + }); + + issue = { + number: createIssueResult.createIssue.issue.number, + html_url: createIssueResult.createIssue.issue.url, + id: createIssueResult.createIssue.issue.id + }; + + console.log('Created sub-issue #' + issue.number + ': ' + issue.html_url); + console.log('Successfully linked to parent issue #' + parentIssueNumber); + } catch (error) { - console.log('Warning: Could not add comment to parent issue:', error.message); + console.log('Error creating sub-issue with GraphQL, falling back to regular issue creation:', error.message); + + // Fallback to regular issue creation with text reference + let finalBody = body; + if (finalBody.trim()) { + finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; + } else { + finalBody = `Related to #${parentIssueNumber}`; + } + + const issueResult = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: finalBody, + labels: labels + }); + + issue = issueResult.data; + console.log('Created regular issue #' + issue.number + ': ' + issue.html_url); + + // Add a comment to the parent issue + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parentIssueNumber, + body: `Created related issue: #${issue.number}` + }); + console.log('Added comment to parent issue #' + parentIssueNumber); + } catch (commentError) { + console.log('Warning: Could not add comment to parent issue:', commentError.message); + } } + } else { + // No parent issue context, create a regular issue + const issueResult = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: body, + labels: labels + }); + + issue = issueResult.data; + console.log('Created issue #' + issue.number + ': ' + issue.html_url); } // Set output for other jobs to use diff --git a/pkg/workflow/create_issue_subissue_test.go b/pkg/workflow/create_issue_subissue_test.go index 59091b075a5..096254beedc 100644 --- a/pkg/workflow/create_issue_subissue_test.go +++ b/pkg/workflow/create_issue_subissue_test.go @@ -14,25 +14,35 @@ func TestCreateIssueSubissueFeature(t *testing.T) { t.Error("Expected create_issue.js to check for parent issue context") } - // Test that the script modifies the body when in issue context - if !strings.Contains(createIssueScript, "Related to #${parentIssueNumber}") { - t.Error("Expected create_issue.js to add parent issue reference to body") + // Test that the script uses GraphQL to get parent issue ID + if !strings.Contains(createIssueScript, "repository(owner: $owner, name: $repo) {") { + t.Error("Expected create_issue.js to use GraphQL to get parent issue") + } + + // Test that the script uses GraphQL createIssue mutation with parentIssueId + if !strings.Contains(createIssueScript, "parentIssueId: $parentIssueId") { + t.Error("Expected create_issue.js to use parentIssueId in createIssue mutation") } - // Test that the script creates a comment on the parent issue - if !strings.Contains(createIssueScript, "github.rest.issues.createComment") { - t.Error("Expected create_issue.js to create comment on parent issue") + // Test that the script has fallback to REST API with text reference + if !strings.Contains(createIssueScript, "Error creating sub-issue with GraphQL, falling back") { + t.Error("Expected create_issue.js to have fallback error handling") } - // Test that the script has proper error handling for comment creation - if !strings.Contains(createIssueScript, "Warning: Could not add comment to parent issue") { - t.Error("Expected create_issue.js to have error handling for parent issue comment") + // Test that the fallback still adds text reference for compatibility + if !strings.Contains(createIssueScript, "Related to #${parentIssueNumber}") { + t.Error("Expected create_issue.js fallback to add parent issue reference to body") } // Test console logging for debugging if !strings.Contains(createIssueScript, "Detected issue context, parent issue") { t.Error("Expected create_issue.js to log when issue context is detected") } + + // Test that it logs successful sub-issue creation + if !strings.Contains(createIssueScript, "Successfully linked to parent issue") { + t.Error("Expected create_issue.js to log successful sub-issue creation") + } } // TestCreateIssueWorkflowCompilation tests that workflows with output.issue still compile correctly @@ -87,8 +97,12 @@ Write output to ${{ env.GITHUB_AW_OUTPUT }}.` t.Error("Expected compiled workflow to include subissue detection") } - if !strings.Contains(lockContent, "Created related issue: #${issue.number}") { - t.Error("Expected compiled workflow to include parent issue comment") + if !strings.Contains(lockContent, "parentIssueId: $parentIssueId") { + t.Error("Expected compiled workflow to include GraphQL parentIssueId parameter") + } + + if !strings.Contains(lockContent, "Successfully linked to parent issue") { + t.Error("Expected compiled workflow to include success logging for sub-issue creation") } // Verify it still has the standard create_output_issue job structure diff --git a/pkg/workflow/js/create_issue.js b/pkg/workflow/js/create_issue.js index acb59ab0ecd..dbccb7b7aef 100644 --- a/pkg/workflow/js/create_issue.js +++ b/pkg/workflow/js/create_issue.js @@ -64,43 +64,161 @@ console.log('Body length:', body.length); // Check if we're in an issue context (triggered by an issue event) const parentIssueNumber = context.payload?.issue?.number; -let finalBody = body; +let issue; if (parentIssueNumber) { console.log('Detected issue context, parent issue #' + parentIssueNumber); - // Add reference to parent issue in the child issue body - if (finalBody.trim()) { - finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; - } else { - finalBody = `Related to #${parentIssueNumber}`; - } -} - -// Create the issue using GitHub API -const { data: issue } = await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: finalBody, - labels: labels -}); - -console.log('Created issue #' + issue.number + ': ' + issue.html_url); - -// If we have a parent issue, add a comment to it referencing the new child issue -if (parentIssueNumber) { try { - await github.rest.issues.createComment({ + // Get the parent issue's GraphQL node ID + const parentIssueQuery = ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + issue(number: $number) { + id + } + } + } + `; + + const parentIssueResult = await github.graphql(parentIssueQuery, { owner: context.repo.owner, repo: context.repo.repo, - issue_number: parentIssueNumber, - body: `Created related issue: #${issue.number}` + number: parentIssueNumber }); - console.log('Added comment to parent issue #' + parentIssueNumber); + + const parentIssueId = parentIssueResult.repository.issue.id; + console.log('Found parent issue GraphQL ID:', parentIssueId); + + // Get the repository's GraphQL node ID + const repoQuery = ` + query($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + id + } + } + `; + + const repoResult = await github.graphql(repoQuery, { + owner: context.repo.owner, + repo: context.repo.repo + }); + + const repositoryId = repoResult.repository.id; + console.log('Found repository GraphQL ID:', repositoryId); + + // Create the issue as a sub-issue using GraphQL mutation + const createIssueMutation = ` + mutation($repositoryId: ID!, $title: String!, $body: String, $labelIds: [ID!], $parentIssueId: ID!) { + createIssue(input: { + repositoryId: $repositoryId, + title: $title, + body: $body, + labelIds: $labelIds, + parentIssueId: $parentIssueId + }) { + issue { + id + number + url + } + } + } + `; + + // Get label IDs if labels are specified + let labelIds = []; + if (labels && labels.length > 0) { + const labelsQuery = ` + query($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + labels(first: 100) { + nodes { + id + name + } + } + } + } + `; + + const labelsResult = await github.graphql(labelsQuery, { + owner: context.repo.owner, + repo: context.repo.repo + }); + + const availableLabels = labelsResult.repository.labels.nodes; + labelIds = labels + .map(label => availableLabels.find(l => l.name.toLowerCase() === label.toLowerCase())?.id) + .filter(id => id); + + console.log('Found label IDs:', labelIds); + } + + const createIssueResult = await github.graphql(createIssueMutation, { + repositoryId: repositoryId, + title: title, + body: body || '', + labelIds: labelIds, + parentIssueId: parentIssueId + }); + + issue = { + number: createIssueResult.createIssue.issue.number, + html_url: createIssueResult.createIssue.issue.url, + id: createIssueResult.createIssue.issue.id + }; + + console.log('Created sub-issue #' + issue.number + ': ' + issue.html_url); + console.log('Successfully linked to parent issue #' + parentIssueNumber); + } catch (error) { - console.log('Warning: Could not add comment to parent issue:', error.message); + console.log('Error creating sub-issue with GraphQL, falling back to regular issue creation:', error.message); + + // Fallback to regular issue creation with text reference + let finalBody = body; + if (finalBody.trim()) { + finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; + } else { + finalBody = `Related to #${parentIssueNumber}`; + } + + const issueResult = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: finalBody, + labels: labels + }); + + issue = issueResult.data; + console.log('Created regular issue #' + issue.number + ': ' + issue.html_url); + + // Add a comment to the parent issue + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parentIssueNumber, + body: `Created related issue: #${issue.number}` + }); + console.log('Added comment to parent issue #' + parentIssueNumber); + } catch (commentError) { + console.log('Warning: Could not add comment to parent issue:', commentError.message); + } } +} else { + // No parent issue context, create a regular issue + const issueResult = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: body, + labels: labels + }); + + issue = issueResult.data; + console.log('Created issue #' + issue.number + ': ' + issue.html_url); } // Set output for other jobs to use From 2a84325f62137f623a627c3767d051bc71b58966 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:50:56 +0000 Subject: [PATCH 4/4] Revert "Use GraphQL to create proper sub-issue relationships instead of text references" This reverts commit 108a42a1fdebb917ba87efe823bbe402ffd7e640, returning to the simpler REST API approach for issue creation with text-based references instead of GraphQL sub-issue relationships. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/test-claude.lock.yml | 170 ++++---------------- pkg/workflow/create_issue_subissue_test.go | 36 ++--- pkg/workflow/js/create_issue.js | 172 ++++----------------- 3 files changed, 64 insertions(+), 314 deletions(-) diff --git a/.github/workflows/test-claude.lock.yml b/.github/workflows/test-claude.lock.yml index 7499ab15ff1..440c1cdc8a2 100644 --- a/.github/workflows/test-claude.lock.yml +++ b/.github/workflows/test-claude.lock.yml @@ -541,161 +541,43 @@ jobs: // Check if we're in an issue context (triggered by an issue event) const parentIssueNumber = context.payload?.issue?.number; - let issue; + let finalBody = body; if (parentIssueNumber) { console.log('Detected issue context, parent issue #' + parentIssueNumber); - try { - // Get the parent issue's GraphQL node ID - const parentIssueQuery = ` - query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - issue(number: $number) { - id - } - } - } - `; - - const parentIssueResult = await github.graphql(parentIssueQuery, { - owner: context.repo.owner, - repo: context.repo.repo, - number: parentIssueNumber - }); - - const parentIssueId = parentIssueResult.repository.issue.id; - console.log('Found parent issue GraphQL ID:', parentIssueId); - - // Get the repository's GraphQL node ID - const repoQuery = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - id - } - } - `; - - const repoResult = await github.graphql(repoQuery, { - owner: context.repo.owner, - repo: context.repo.repo - }); - - const repositoryId = repoResult.repository.id; - console.log('Found repository GraphQL ID:', repositoryId); - - // Create the issue as a sub-issue using GraphQL mutation - const createIssueMutation = ` - mutation($repositoryId: ID!, $title: String!, $body: String, $labelIds: [ID!], $parentIssueId: ID!) { - createIssue(input: { - repositoryId: $repositoryId, - title: $title, - body: $body, - labelIds: $labelIds, - parentIssueId: $parentIssueId - }) { - issue { - id - number - url - } - } - } - `; - - // Get label IDs if labels are specified - let labelIds = []; - if (labels && labels.length > 0) { - const labelsQuery = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - labels(first: 100) { - nodes { - id - name - } - } - } - } - `; - - const labelsResult = await github.graphql(labelsQuery, { - owner: context.repo.owner, - repo: context.repo.repo - }); - - const availableLabels = labelsResult.repository.labels.nodes; - labelIds = labels - .map(label => availableLabels.find(l => l.name.toLowerCase() === label.toLowerCase())?.id) - .filter(id => id); - - console.log('Found label IDs:', labelIds); - } - - const createIssueResult = await github.graphql(createIssueMutation, { - repositoryId: repositoryId, - title: title, - body: body || '', - labelIds: labelIds, - parentIssueId: parentIssueId - }); - - issue = { - number: createIssueResult.createIssue.issue.number, - html_url: createIssueResult.createIssue.issue.url, - id: createIssueResult.createIssue.issue.id - }; - - console.log('Created sub-issue #' + issue.number + ': ' + issue.html_url); - console.log('Successfully linked to parent issue #' + parentIssueNumber); + // Add reference to parent issue in the child issue body + if (finalBody.trim()) { + finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; + } else { + finalBody = `Related to #${parentIssueNumber}`; + } + } - } catch (error) { - console.log('Error creating sub-issue with GraphQL, falling back to regular issue creation:', error.message); + // Create the issue using GitHub API + const { data: issue } = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: finalBody, + labels: labels + }); - // Fallback to regular issue creation with text reference - let finalBody = body; - if (finalBody.trim()) { - finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; - } else { - finalBody = `Related to #${parentIssueNumber}`; - } + console.log('Created issue #' + issue.number + ': ' + issue.html_url); - const issueResult = await github.rest.issues.create({ + // If we have a parent issue, add a comment to it referencing the new child issue + if (parentIssueNumber) { + try { + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - title: title, - body: finalBody, - labels: labels + issue_number: parentIssueNumber, + body: `Created related issue: #${issue.number}` }); - - issue = issueResult.data; - console.log('Created regular issue #' + issue.number + ': ' + issue.html_url); - - // Add a comment to the parent issue - try { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: parentIssueNumber, - body: `Created related issue: #${issue.number}` - }); - console.log('Added comment to parent issue #' + parentIssueNumber); - } catch (commentError) { - console.log('Warning: Could not add comment to parent issue:', commentError.message); - } + console.log('Added comment to parent issue #' + parentIssueNumber); + } catch (error) { + console.log('Warning: Could not add comment to parent issue:', error.message); } - } else { - // No parent issue context, create a regular issue - const issueResult = await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: body, - labels: labels - }); - - issue = issueResult.data; - console.log('Created issue #' + issue.number + ': ' + issue.html_url); } // Set output for other jobs to use diff --git a/pkg/workflow/create_issue_subissue_test.go b/pkg/workflow/create_issue_subissue_test.go index 096254beedc..59091b075a5 100644 --- a/pkg/workflow/create_issue_subissue_test.go +++ b/pkg/workflow/create_issue_subissue_test.go @@ -14,35 +14,25 @@ func TestCreateIssueSubissueFeature(t *testing.T) { t.Error("Expected create_issue.js to check for parent issue context") } - // Test that the script uses GraphQL to get parent issue ID - if !strings.Contains(createIssueScript, "repository(owner: $owner, name: $repo) {") { - t.Error("Expected create_issue.js to use GraphQL to get parent issue") - } - - // Test that the script uses GraphQL createIssue mutation with parentIssueId - if !strings.Contains(createIssueScript, "parentIssueId: $parentIssueId") { - t.Error("Expected create_issue.js to use parentIssueId in createIssue mutation") + // Test that the script modifies the body when in issue context + if !strings.Contains(createIssueScript, "Related to #${parentIssueNumber}") { + t.Error("Expected create_issue.js to add parent issue reference to body") } - // Test that the script has fallback to REST API with text reference - if !strings.Contains(createIssueScript, "Error creating sub-issue with GraphQL, falling back") { - t.Error("Expected create_issue.js to have fallback error handling") + // Test that the script creates a comment on the parent issue + if !strings.Contains(createIssueScript, "github.rest.issues.createComment") { + t.Error("Expected create_issue.js to create comment on parent issue") } - // Test that the fallback still adds text reference for compatibility - if !strings.Contains(createIssueScript, "Related to #${parentIssueNumber}") { - t.Error("Expected create_issue.js fallback to add parent issue reference to body") + // Test that the script has proper error handling for comment creation + if !strings.Contains(createIssueScript, "Warning: Could not add comment to parent issue") { + t.Error("Expected create_issue.js to have error handling for parent issue comment") } // Test console logging for debugging if !strings.Contains(createIssueScript, "Detected issue context, parent issue") { t.Error("Expected create_issue.js to log when issue context is detected") } - - // Test that it logs successful sub-issue creation - if !strings.Contains(createIssueScript, "Successfully linked to parent issue") { - t.Error("Expected create_issue.js to log successful sub-issue creation") - } } // TestCreateIssueWorkflowCompilation tests that workflows with output.issue still compile correctly @@ -97,12 +87,8 @@ Write output to ${{ env.GITHUB_AW_OUTPUT }}.` t.Error("Expected compiled workflow to include subissue detection") } - if !strings.Contains(lockContent, "parentIssueId: $parentIssueId") { - t.Error("Expected compiled workflow to include GraphQL parentIssueId parameter") - } - - if !strings.Contains(lockContent, "Successfully linked to parent issue") { - t.Error("Expected compiled workflow to include success logging for sub-issue creation") + if !strings.Contains(lockContent, "Created related issue: #${issue.number}") { + t.Error("Expected compiled workflow to include parent issue comment") } // Verify it still has the standard create_output_issue job structure diff --git a/pkg/workflow/js/create_issue.js b/pkg/workflow/js/create_issue.js index dbccb7b7aef..acb59ab0ecd 100644 --- a/pkg/workflow/js/create_issue.js +++ b/pkg/workflow/js/create_issue.js @@ -64,161 +64,43 @@ console.log('Body length:', body.length); // Check if we're in an issue context (triggered by an issue event) const parentIssueNumber = context.payload?.issue?.number; -let issue; +let finalBody = body; if (parentIssueNumber) { console.log('Detected issue context, parent issue #' + parentIssueNumber); + // Add reference to parent issue in the child issue body + if (finalBody.trim()) { + finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; + } else { + finalBody = `Related to #${parentIssueNumber}`; + } +} + +// Create the issue using GitHub API +const { data: issue } = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: title, + body: finalBody, + labels: labels +}); + +console.log('Created issue #' + issue.number + ': ' + issue.html_url); + +// If we have a parent issue, add a comment to it referencing the new child issue +if (parentIssueNumber) { try { - // Get the parent issue's GraphQL node ID - const parentIssueQuery = ` - query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - issue(number: $number) { - id - } - } - } - `; - - const parentIssueResult = await github.graphql(parentIssueQuery, { + await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - number: parentIssueNumber + issue_number: parentIssueNumber, + body: `Created related issue: #${issue.number}` }); - - const parentIssueId = parentIssueResult.repository.issue.id; - console.log('Found parent issue GraphQL ID:', parentIssueId); - - // Get the repository's GraphQL node ID - const repoQuery = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - id - } - } - `; - - const repoResult = await github.graphql(repoQuery, { - owner: context.repo.owner, - repo: context.repo.repo - }); - - const repositoryId = repoResult.repository.id; - console.log('Found repository GraphQL ID:', repositoryId); - - // Create the issue as a sub-issue using GraphQL mutation - const createIssueMutation = ` - mutation($repositoryId: ID!, $title: String!, $body: String, $labelIds: [ID!], $parentIssueId: ID!) { - createIssue(input: { - repositoryId: $repositoryId, - title: $title, - body: $body, - labelIds: $labelIds, - parentIssueId: $parentIssueId - }) { - issue { - id - number - url - } - } - } - `; - - // Get label IDs if labels are specified - let labelIds = []; - if (labels && labels.length > 0) { - const labelsQuery = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - labels(first: 100) { - nodes { - id - name - } - } - } - } - `; - - const labelsResult = await github.graphql(labelsQuery, { - owner: context.repo.owner, - repo: context.repo.repo - }); - - const availableLabels = labelsResult.repository.labels.nodes; - labelIds = labels - .map(label => availableLabels.find(l => l.name.toLowerCase() === label.toLowerCase())?.id) - .filter(id => id); - - console.log('Found label IDs:', labelIds); - } - - const createIssueResult = await github.graphql(createIssueMutation, { - repositoryId: repositoryId, - title: title, - body: body || '', - labelIds: labelIds, - parentIssueId: parentIssueId - }); - - issue = { - number: createIssueResult.createIssue.issue.number, - html_url: createIssueResult.createIssue.issue.url, - id: createIssueResult.createIssue.issue.id - }; - - console.log('Created sub-issue #' + issue.number + ': ' + issue.html_url); - console.log('Successfully linked to parent issue #' + parentIssueNumber); - + console.log('Added comment to parent issue #' + parentIssueNumber); } catch (error) { - console.log('Error creating sub-issue with GraphQL, falling back to regular issue creation:', error.message); - - // Fallback to regular issue creation with text reference - let finalBody = body; - if (finalBody.trim()) { - finalBody = `Related to #${parentIssueNumber}\n\n${finalBody}`; - } else { - finalBody = `Related to #${parentIssueNumber}`; - } - - const issueResult = await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: finalBody, - labels: labels - }); - - issue = issueResult.data; - console.log('Created regular issue #' + issue.number + ': ' + issue.html_url); - - // Add a comment to the parent issue - try { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: parentIssueNumber, - body: `Created related issue: #${issue.number}` - }); - console.log('Added comment to parent issue #' + parentIssueNumber); - } catch (commentError) { - console.log('Warning: Could not add comment to parent issue:', commentError.message); - } + console.log('Warning: Could not add comment to parent issue:', error.message); } -} else { - // No parent issue context, create a regular issue - const issueResult = await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: body, - labels: labels - }); - - issue = issueResult.data; - console.log('Created issue #' + issue.number + ': ' + issue.html_url); } // Set output for other jobs to use