diff --git a/actions/setup/js/create_missing_data_issue.cjs b/actions/setup/js/create_missing_data_issue.cjs index b04fa7bad63..1276178e316 100644 --- a/actions/setup/js/create_missing_data_issue.cjs +++ b/actions/setup/js/create_missing_data_issue.cjs @@ -18,6 +18,7 @@ const HANDLER_TYPE = "create_missing_data_issue"; const main = buildMissingIssueHandler({ handlerType: HANDLER_TYPE, defaultTitlePrefix: "[missing data]", + defaultLabels: ["agentic-workflows"], itemsField: "missing_data", templatePath: "/opt/gh-aw/prompts/missing_data_issue.md", templateListKey: "missing_data_list", diff --git a/actions/setup/js/create_missing_tool_issue.cjs b/actions/setup/js/create_missing_tool_issue.cjs index 462dab32e01..2ca315be88b 100644 --- a/actions/setup/js/create_missing_tool_issue.cjs +++ b/actions/setup/js/create_missing_tool_issue.cjs @@ -18,6 +18,7 @@ const HANDLER_TYPE = "create_missing_tool_issue"; const main = buildMissingIssueHandler({ handlerType: HANDLER_TYPE, defaultTitlePrefix: "[missing tool]", + defaultLabels: ["agentic-workflows"], itemsField: "missing_tools", templatePath: "/opt/gh-aw/prompts/missing_tool_issue.md", templateListKey: "missing_tools_list", diff --git a/actions/setup/js/missing_issue_helpers.cjs b/actions/setup/js/missing_issue_helpers.cjs index 81c548cedc4..00b0c52929a 100644 --- a/actions/setup/js/missing_issue_helpers.cjs +++ b/actions/setup/js/missing_issue_helpers.cjs @@ -25,15 +25,17 @@ const { sanitizeContent } = require("./sanitize_content.cjs"); * @param {function(string): string[]} options.buildCommentHeader - Returns header lines for the comment body given runUrl * @param {function(Object, number): string[]} options.renderCommentItem - Renders a single item for an existing-issue comment * @param {function(Object, number): string[]} options.renderIssueItem - Renders a single item for a new-issue body + * @param {string[]} [options.defaultLabels] - Labels always applied to created issues (merged with config.labels) * @returns {HandlerFactoryFunction} */ function buildMissingIssueHandler(options) { - const { handlerType, defaultTitlePrefix, itemsField, templatePath, templateListKey, buildCommentHeader, renderCommentItem, renderIssueItem } = options; + const { handlerType, defaultTitlePrefix, itemsField, templatePath, templateListKey, buildCommentHeader, renderCommentItem, renderIssueItem, defaultLabels = [] } = options; return async function main(config = {}) { // Extract configuration const titlePrefix = config.title_prefix || defaultTitlePrefix; - const envLabels = config.labels ? (Array.isArray(config.labels) ? config.labels : config.labels.split(",")).map(label => String(label).trim()).filter(label => label) : []; + const userLabels = config.labels ? (Array.isArray(config.labels) ? config.labels : config.labels.split(",")).map(label => String(label).trim()).filter(label => label) : []; + const envLabels = [...new Set([...defaultLabels, ...userLabels])]; const maxCount = config.max || 1; // Default to 1 to create only one issue per workflow run core.info(`Title prefix: ${titlePrefix}`); diff --git a/actions/setup/js/missing_issue_helpers.test.cjs b/actions/setup/js/missing_issue_helpers.test.cjs index a6ae5280d30..ce0b01de2ee 100644 --- a/actions/setup/js/missing_issue_helpers.test.cjs +++ b/actions/setup/js/missing_issue_helpers.test.cjs @@ -270,6 +270,48 @@ describe("missing_issue_helpers.cjs - buildMissingIssueHandler", () => { expect(mockGithub.rest.issues.create).toHaveBeenCalledWith(expect.objectContaining({ labels: ["bug", "needs-triage"] })); }); + + it("should always apply defaultLabels from options even without config.labels", async () => { + mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ + data: { total_count: 0, items: [] }, + }); + mockGithub.rest.issues.create.mockResolvedValue({ + data: { number: 77, html_url: "https://github.com/owner/repo/issues/77" }, + }); + + const handler = await buildMissingIssueHandler(makeOptions({ defaultLabels: ["agentic-workflows"] }))({}); + await handler(defaultMessage); + + expect(mockGithub.rest.issues.create).toHaveBeenCalledWith(expect.objectContaining({ labels: ["agentic-workflows"] })); + }); + + it("should merge defaultLabels with config.labels", async () => { + mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ + data: { total_count: 0, items: [] }, + }); + mockGithub.rest.issues.create.mockResolvedValue({ + data: { number: 77, html_url: "https://github.com/owner/repo/issues/77" }, + }); + + const handler = await buildMissingIssueHandler(makeOptions({ defaultLabels: ["agentic-workflows"] }))({ labels: ["bug"] }); + await handler(defaultMessage); + + expect(mockGithub.rest.issues.create).toHaveBeenCalledWith(expect.objectContaining({ labels: ["agentic-workflows", "bug"] })); + }); + + it("should deduplicate labels when defaultLabels and config.labels overlap", async () => { + mockGithub.rest.search.issuesAndPullRequests.mockResolvedValue({ + data: { total_count: 0, items: [] }, + }); + mockGithub.rest.issues.create.mockResolvedValue({ + data: { number: 77, html_url: "https://github.com/owner/repo/issues/77" }, + }); + + const handler = await buildMissingIssueHandler(makeOptions({ defaultLabels: ["agentic-workflows"] }))({ labels: ["agentic-workflows", "bug"] }); + await handler(defaultMessage); + + expect(mockGithub.rest.issues.create).toHaveBeenCalledWith(expect.objectContaining({ labels: ["agentic-workflows", "bug"] })); + }); }); describe("error handling", () => {