diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index abe3acbf2a..ddd7291d33 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -5261,6 +5261,7 @@ jobs: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const fs = require("fs"); + const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT || ""; @@ -5357,23 +5358,28 @@ jobs: } core.info("Found push-to-pull-request-branch item"); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - summaryContent += `**Target:** ${target}\n\n`; - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } if (target !== "*" && target !== "triggering") { diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 39ea272e3b..eccf146555 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -4177,6 +4177,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -4215,23 +4232,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index ca339e2f09..a0745360fa 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -4197,6 +4197,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -4235,23 +4252,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index a24858058b..eb4126e24d 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -5441,6 +5441,7 @@ jobs: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const fs = require("fs"); + const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT || ""; @@ -5537,23 +5538,28 @@ jobs: } core.info("Found push-to-pull-request-branch item"); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - summaryContent += `**Target:** ${target}\n\n`; - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } if (target !== "*" && target !== "triggering") { diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 815891a33a..c06128216e 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -4344,6 +4344,7 @@ jobs: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const fs = require("fs"); + const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT || ""; @@ -4440,23 +4441,28 @@ jobs: } core.info("Found push-to-pull-request-branch item"); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - summaryContent += `**Target:** ${target}\n\n`; - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } if (target !== "*" && target !== "triggering") { diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index b2a9caccf3..01aacb158a 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -3170,6 +3170,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -3208,23 +3225,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 8c5ed06a90..953f8de981 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -3407,6 +3407,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -3445,23 +3462,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 392dafe6af..6130da071e 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -806,6 +806,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } async function main() { const result = loadAgentOutput(); if (!result.success) { @@ -818,18 +835,23 @@ jobs: } core.info(`Found add-labels item with ${labelsItem.labels.length} labels`); if (process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true") { - let summaryContent = "## 🎭 Staged Mode: Add Labels Preview\n\n"; - summaryContent += "The following labels would be added if staged mode was disabled:\n\n"; - if (labelsItem.item_number) { - summaryContent += `**Target Issue:** #${labelsItem.item_number}\n\n`; - } else { - summaryContent += `**Target:** Current issue/PR\n\n`; - } - if (labelsItem.labels && labelsItem.labels.length > 0) { - summaryContent += `**Labels to add:** ${labelsItem.labels.join(", ")}\n\n`; - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Label addition preview written to step summary"); + await generateStagedPreview({ + title: "Add Labels", + description: "The following labels would be added if staged mode was disabled:", + items: [labelsItem], + renderItem: item => { + let content = ""; + if (item.item_number) { + content += `**Target Issue:** #${item.item_number}\n\n`; + } else { + content += `**Target:** Current issue/PR\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels to add:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const allowedLabelsEnv = process.env.GH_AW_LABELS_ALLOWED?.trim(); diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 19e1a99481..bd99c268e7 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -4876,6 +4876,7 @@ jobs: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const fs = require("fs"); + const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT || ""; @@ -4972,23 +4973,28 @@ jobs: } core.info("Found push-to-pull-request-branch item"); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - summaryContent += `**Target:** ${target}\n\n`; - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } if (target !== "*" && target !== "triggering") { diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index cbbb82baf8..10c1471c0e 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -4268,6 +4268,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -4306,23 +4323,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index cf1f8be0f5..1f5cb9f521 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1256,6 +1256,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } async function main() { const result = loadAgentOutput(); if (!result.success) { @@ -1268,18 +1285,23 @@ jobs: } core.info(`Found add-labels item with ${labelsItem.labels.length} labels`); if (process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true") { - let summaryContent = "## 🎭 Staged Mode: Add Labels Preview\n\n"; - summaryContent += "The following labels would be added if staged mode was disabled:\n\n"; - if (labelsItem.item_number) { - summaryContent += `**Target Issue:** #${labelsItem.item_number}\n\n`; - } else { - summaryContent += `**Target:** Current issue/PR\n\n`; - } - if (labelsItem.labels && labelsItem.labels.length > 0) { - summaryContent += `**Labels to add:** ${labelsItem.labels.join(", ")}\n\n`; - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Label addition preview written to step summary"); + await generateStagedPreview({ + title: "Add Labels", + description: "The following labels would be added if staged mode was disabled:", + items: [labelsItem], + renderItem: item => { + let content = ""; + if (item.item_number) { + content += `**Target Issue:** #${item.item_number}\n\n`; + } else { + content += `**Target:** Current issue/PR\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels to add:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const allowedLabelsEnv = process.env.GH_AW_LABELS_ALLOWED?.trim(); @@ -5162,6 +5184,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -5200,23 +5239,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; @@ -5475,6 +5513,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -5523,29 +5578,29 @@ jobs: } core.info(`Found ${reviewCommentItems.length} create-pull-request-review-comment item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create PR Review Comments Preview\n\n"; - summaryContent += "The following review comments would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < reviewCommentItems.length; i++) { - const item = reviewCommentItems[i]; - summaryContent += `### Review Comment ${i + 1}\n`; - if (item.pull_request_number) { - const repoUrl = getRepositoryUrl(); - const pullUrl = `${repoUrl}/pull/${item.pull_request_number}`; - summaryContent += `**Target PR:** [#${item.pull_request_number}](${pullUrl})\n\n`; - } else { - summaryContent += `**Target:** Current PR\n\n`; - } - summaryContent += `**File:** ${item.path || "No path provided"}\n\n`; - summaryContent += `**Line:** ${item.line || "No line provided"}\n\n`; - if (item.start_line) { - summaryContent += `**Start Line:** ${item.start_line}\n\n`; - } - summaryContent += `**Side:** ${item.side || "RIGHT"}\n\n`; - summaryContent += `**Body:**\n${item.body || "No content provided"}\n\n`; - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 PR review comment creation preview written to step summary"); + await generateStagedPreview({ + title: "Create PR Review Comments", + description: "The following review comments would be created if staged mode was disabled:", + items: reviewCommentItems, + renderItem: (item, index) => { + let content = `### Review Comment ${index + 1}\n`; + if (item.pull_request_number) { + const repoUrl = getRepositoryUrl(); + const pullUrl = `${repoUrl}/pull/${item.pull_request_number}`; + content += `**Target PR:** [#${item.pull_request_number}](${pullUrl})\n\n`; + } else { + content += `**Target:** Current PR\n\n`; + } + content += `**File:** ${item.path || "No path provided"}\n\n`; + content += `**Line:** ${item.line || "No line provided"}\n\n`; + if (item.start_line) { + content += `**Start Line:** ${item.start_line}\n\n`; + } + content += `**Side:** ${item.side || "RIGHT"}\n\n`; + content += `**Body:**\n${item.body || "No content provided"}\n\n`; + return content; + }, + }); return; } const defaultSide = process.env.GH_AW_PR_REVIEW_COMMENT_SIDE || "RIGHT"; @@ -6801,6 +6856,7 @@ jobs: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const fs = require("fs"); + const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT || ""; @@ -6897,23 +6953,28 @@ jobs: } core.info("Found push-to-pull-request-branch item"); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - summaryContent += `**Target:** ${target}\n\n`; - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } if (target !== "*" && target !== "triggering") { @@ -7178,6 +7239,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const result = loadAgentOutput(); @@ -7191,29 +7269,29 @@ jobs: } core.info(`Found ${updateItems.length} update-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Update Issues Preview\n\n"; - summaryContent += "The following issue updates would be applied if staged mode was disabled:\n\n"; - for (let i = 0; i < updateItems.length; i++) { - const item = updateItems[i]; - summaryContent += `### Issue Update ${i + 1}\n`; - if (item.issue_number) { - summaryContent += `**Target Issue:** #${item.issue_number}\n\n`; - } else { - summaryContent += `**Target:** Current issue\n\n`; - } - if (item.title !== undefined) { - summaryContent += `**New Title:** ${item.title}\n\n`; - } - if (item.body !== undefined) { - summaryContent += `**New Body:**\n${item.body}\n\n`; - } - if (item.status !== undefined) { - summaryContent += `**New Status:** ${item.status}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Issue update preview written to step summary"); + await generateStagedPreview({ + title: "Update Issues", + description: "The following issue updates would be applied if staged mode was disabled:", + items: updateItems, + renderItem: (item, index) => { + let content = `### Issue Update ${index + 1}\n`; + if (item.issue_number) { + content += `**Target Issue:** #${item.issue_number}\n\n`; + } else { + content += `**Target:** Current issue\n\n`; + } + if (item.title !== undefined) { + content += `**New Title:** ${item.title}\n\n`; + } + if (item.body !== undefined) { + content += `**New Body:**\n${item.body}\n\n`; + } + if (item.status !== undefined) { + content += `**New Status:** ${item.status}\n\n`; + } + return content; + }, + }); return; } const updateTarget = process.env.GH_AW_UPDATE_TARGET || "triggering"; diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 4d1ccbf1b8..623f28730d 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -3811,6 +3811,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -3849,23 +3866,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index b845fce813..d11feed74d 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -3257,6 +3257,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -3295,23 +3312,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index dab018436e..a23985b834 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -2910,6 +2910,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -2948,23 +2965,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 5664b8af4a..e9022f2c5d 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -3962,6 +3962,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -4000,23 +4017,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml index 3c9fd4e62d..3428dd7678 100644 --- a/.github/workflows/smoke-detector.lock.yml +++ b/.github/workflows/smoke-detector.lock.yml @@ -4430,6 +4430,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -4468,23 +4485,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/test-ollama-threat-detection.lock.yml b/.github/workflows/test-ollama-threat-detection.lock.yml index d5dfeef9e4..d7b7aaaae4 100644 --- a/.github/workflows/test-ollama-threat-detection.lock.yml +++ b/.github/workflows/test-ollama-threat-detection.lock.yml @@ -3538,6 +3538,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -3576,23 +3593,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index e498159852..8c33254ce8 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -5194,6 +5194,7 @@ jobs: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | const fs = require("fs"); + const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true"; const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT || ""; @@ -5290,23 +5291,28 @@ jobs: } core.info("Found push-to-pull-request-branch item"); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - summaryContent += `**Target:** ${target}\n\n`; - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } if (target !== "*" && target !== "triggering") { diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index a37d1cecc4..424f38583f 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -3829,6 +3829,23 @@ jobs: } return { success: true, items: validatedOutput.items }; } + async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } + } function generateFooter( workflowName, runUrl, @@ -3867,23 +3884,22 @@ jobs: } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/pkg/workflow/js.go b/pkg/workflow/js.go index a680762dc1..ca436c1d3c 100644 --- a/pkg/workflow/js.go +++ b/pkg/workflow/js.go @@ -80,6 +80,9 @@ var sanitizeWorkflowNameScript string //go:embed js/load_agent_output.cjs var loadAgentOutputScript string +//go:embed js/staged_preview.cjs +var stagedPreviewScript string + // Source scripts that may contain local requires // //go:embed js/collect_ndjson_output.cjs @@ -360,6 +363,7 @@ func GetJavaScriptSources() map[string]string { "sanitize_label_content.cjs": sanitizeLabelContentScript, "sanitize_workflow_name.cjs": sanitizeWorkflowNameScript, "load_agent_output.cjs": loadAgentOutputScript, + "staged_preview.cjs": stagedPreviewScript, } } diff --git a/pkg/workflow/js/add_labels.cjs b/pkg/workflow/js/add_labels.cjs index 0eb5f91b52..96b6e53317 100644 --- a/pkg/workflow/js/add_labels.cjs +++ b/pkg/workflow/js/add_labels.cjs @@ -3,6 +3,7 @@ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { const result = loadAgentOutput(); @@ -17,18 +18,23 @@ async function main() { } core.info(`Found add-labels item with ${labelsItem.labels.length} labels`); if (process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true") { - let summaryContent = "## 🎭 Staged Mode: Add Labels Preview\n\n"; - summaryContent += "The following labels would be added if staged mode was disabled:\n\n"; - if (labelsItem.item_number) { - summaryContent += `**Target Issue:** #${labelsItem.item_number}\n\n`; - } else { - summaryContent += `**Target:** Current issue/PR\n\n`; - } - if (labelsItem.labels && labelsItem.labels.length > 0) { - summaryContent += `**Labels to add:** ${labelsItem.labels.join(", ")}\n\n`; - } - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Label addition preview written to step summary"); + await generateStagedPreview({ + title: "Add Labels", + description: "The following labels would be added if staged mode was disabled:", + items: [labelsItem], + renderItem: item => { + let content = ""; + if (item.item_number) { + content += `**Target Issue:** #${item.item_number}\n\n`; + } else { + content += `**Target:** Current issue/PR\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels to add:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const allowedLabelsEnv = process.env.GH_AW_LABELS_ALLOWED?.trim(); diff --git a/pkg/workflow/js/create_issue.cjs b/pkg/workflow/js/create_issue.cjs index c5c8b060f2..b087d0d2a2 100644 --- a/pkg/workflow/js/create_issue.cjs +++ b/pkg/workflow/js/create_issue.cjs @@ -3,6 +3,7 @@ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { generateStagedPreview } = require("./staged_preview.cjs"); /** * Generate footer with AI attribution and workflow installation instructions @@ -62,23 +63,22 @@ async function main() { } core.info(`Found ${createIssueItems.length} create-issue item(s)`); if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create Issues Preview\n\n"; - summaryContent += "The following issues would be created if staged mode was disabled:\n\n"; - for (let i = 0; i < createIssueItems.length; i++) { - const item = createIssueItems[i]; - summaryContent += `### Issue ${i + 1}\n`; - summaryContent += `**Title:** ${item.title || "No title provided"}\n\n`; - if (item.body) { - summaryContent += `**Body:**\n${item.body}\n\n`; - } - if (item.labels && item.labels.length > 0) { - summaryContent += `**Labels:** ${item.labels.join(", ")}\n\n`; - } - summaryContent += "---\n\n"; - } - await core.summary.addRaw(summaryContent).write(); - core.info(summaryContent); - core.info("📝 Issue creation preview written to step summary"); + await generateStagedPreview({ + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: createIssueItems, + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }); return; } const parentIssueNumber = context.payload?.issue?.number; diff --git a/pkg/workflow/js/create_issue.test.cjs b/pkg/workflow/js/create_issue.test.cjs index 581c049110..761f751262 100644 --- a/pkg/workflow/js/create_issue.test.cjs +++ b/pkg/workflow/js/create_issue.test.cjs @@ -765,7 +765,7 @@ describe("create_issue.cjs", () => { expect(infoCall[0]).toContain("**Labels:** bug, enhancement"); // Should also log the completion message - expect(mockCore.info).toHaveBeenCalledWith("📝 Issue creation preview written to step summary"); + expect(mockCore.info).toHaveBeenCalledWith("📝 Create Issues preview written to step summary"); // Clean up delete process.env.GH_AW_SAFE_OUTPUTS_STAGED; diff --git a/pkg/workflow/js/create_pr_review_comment.cjs b/pkg/workflow/js/create_pr_review_comment.cjs index d3719650de..54854677ba 100644 --- a/pkg/workflow/js/create_pr_review_comment.cjs +++ b/pkg/workflow/js/create_pr_review_comment.cjs @@ -2,6 +2,7 @@ /// const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { generateStagedPreview } = require("./staged_preview.cjs"); /** * Generate footer with AI attribution and workflow installation instructions @@ -81,32 +82,29 @@ async function main() { // If in staged mode, emit step summary instead of creating review comments if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Create PR Review Comments Preview\n\n"; - summaryContent += "The following review comments would be created if staged mode was disabled:\n\n"; - - for (let i = 0; i < reviewCommentItems.length; i++) { - const item = reviewCommentItems[i]; - summaryContent += `### Review Comment ${i + 1}\n`; - if (item.pull_request_number) { - const repoUrl = getRepositoryUrl(); - const pullUrl = `${repoUrl}/pull/${item.pull_request_number}`; - summaryContent += `**Target PR:** [#${item.pull_request_number}](${pullUrl})\n\n`; - } else { - summaryContent += `**Target:** Current PR\n\n`; - } - summaryContent += `**File:** ${item.path || "No path provided"}\n\n`; - summaryContent += `**Line:** ${item.line || "No line provided"}\n\n`; - if (item.start_line) { - summaryContent += `**Start Line:** ${item.start_line}\n\n`; - } - summaryContent += `**Side:** ${item.side || "RIGHT"}\n\n`; - summaryContent += `**Body:**\n${item.body || "No content provided"}\n\n`; - summaryContent += "---\n\n"; - } - - // Write to step summary - await core.summary.addRaw(summaryContent).write(); - core.info("📝 PR review comment creation preview written to step summary"); + await generateStagedPreview({ + title: "Create PR Review Comments", + description: "The following review comments would be created if staged mode was disabled:", + items: reviewCommentItems, + renderItem: (item, index) => { + let content = `### Review Comment ${index + 1}\n`; + if (item.pull_request_number) { + const repoUrl = getRepositoryUrl(); + const pullUrl = `${repoUrl}/pull/${item.pull_request_number}`; + content += `**Target PR:** [#${item.pull_request_number}](${pullUrl})\n\n`; + } else { + content += `**Target:** Current PR\n\n`; + } + content += `**File:** ${item.path || "No path provided"}\n\n`; + content += `**Line:** ${item.line || "No line provided"}\n\n`; + if (item.start_line) { + content += `**Start Line:** ${item.start_line}\n\n`; + } + content += `**Side:** ${item.side || "RIGHT"}\n\n`; + content += `**Body:**\n${item.body || "No content provided"}\n\n`; + return content; + }, + }); return; } diff --git a/pkg/workflow/js/push_to_pull_request_branch.cjs b/pkg/workflow/js/push_to_pull_request_branch.cjs index ce29382daa..660377e614 100644 --- a/pkg/workflow/js/push_to_pull_request_branch.cjs +++ b/pkg/workflow/js/push_to_pull_request_branch.cjs @@ -3,6 +3,7 @@ /** @type {typeof import("fs")} */ const fs = require("fs"); +const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { // Check if we're in staged mode @@ -138,28 +139,30 @@ async function main() { // If in staged mode, emit step summary instead of pushing changes if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Push to PR Branch Preview\n\n"; - summaryContent += "The following changes would be pushed if staged mode was disabled:\n\n"; - - summaryContent += `**Target:** ${target}\n\n`; - - if (pushItem.commit_message) { - summaryContent += `**Commit Message:** ${pushItem.commit_message}\n\n`; - } - - if (fs.existsSync("/tmp/gh-aw/aw.patch")) { - const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); - if (patchStats.trim()) { - summaryContent += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; - summaryContent += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; - } else { - summaryContent += `**Changes:** No changes (empty patch)\n\n`; - } - } - - // Write to step summary - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Push to PR branch preview written to step summary"); + await generateStagedPreview({ + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [{ target, commit_message: pushItem.commit_message }], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + + if (fs.existsSync("/tmp/gh-aw/aw.patch")) { + const patchStats = fs.readFileSync("/tmp/gh-aw/aw.patch", "utf8"); + if (patchStats.trim()) { + content += `**Changes:** Patch file exists with ${patchStats.split("\n").length} lines\n\n`; + content += `
Show patch preview\n\n\`\`\`diff\n${patchStats.slice(0, 2000)}${patchStats.length > 2000 ? "\n... (truncated)" : ""}\n\`\`\`\n\n
\n\n`; + } else { + content += `**Changes:** No changes (empty patch)\n\n`; + } + } + return content; + }, + }); return; } diff --git a/pkg/workflow/js/staged_preview.cjs b/pkg/workflow/js/staged_preview.cjs new file mode 100644 index 0000000000..8097800ac3 --- /dev/null +++ b/pkg/workflow/js/staged_preview.cjs @@ -0,0 +1,35 @@ +// @ts-check +/// + +/** + * Generate a staged mode preview summary and write it to the step summary. + * + * @param {Object} options - Configuration options for the preview + * @param {string} options.title - The main title for the preview (e.g., "Create Issues") + * @param {string} options.description - Description of what would happen if staged mode was disabled + * @param {Array} options.items - Array of items to preview + * @param {(item: any, index: number) => string} options.renderItem - Function to render each item as markdown + * @returns {Promise} + */ +async function generateStagedPreview(options) { + const { title, description, items, renderItem } = options; + + let summaryContent = `## 🎭 Staged Mode: ${title} Preview\n\n`; + summaryContent += `${description}\n\n`; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + summaryContent += renderItem(item, i); + summaryContent += "---\n\n"; + } + + try { + await core.summary.addRaw(summaryContent).write(); + core.info(summaryContent); + core.info(`📝 ${title} preview written to step summary`); + } catch (error) { + core.setFailed(error instanceof Error ? error : String(error)); + } +} + +module.exports = { generateStagedPreview }; diff --git a/pkg/workflow/js/staged_preview.test.cjs b/pkg/workflow/js/staged_preview.test.cjs new file mode 100644 index 0000000000..3e8ce4d836 --- /dev/null +++ b/pkg/workflow/js/staged_preview.test.cjs @@ -0,0 +1,306 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; + +// Mock the global objects that GitHub Actions provides +const mockCore = { + info: vi.fn(), + summary: { + addRaw: vi.fn().mockReturnThis(), + write: vi.fn().mockResolvedValue(), + }, +}; + +// Set up global mocks before importing the module +globalThis.core = mockCore; + +const { generateStagedPreview } = await import("./staged_preview.cjs"); + +describe("staged_preview.cjs", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("generateStagedPreview", () => { + it("should generate preview with single item", async () => { + const options = { + title: "Create Issues", + description: "The following issues would be created if staged mode was disabled:", + items: [ + { + title: "Test Issue", + body: "Test body", + labels: ["bug", "enhancement"], + }, + ], + renderItem: (item, index) => { + let content = `### Issue ${index + 1}\n`; + content += `**Title:** ${item.title || "No title provided"}\n\n`; + if (item.body) { + content += `**Body:**\n${item.body}\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }; + + await generateStagedPreview(options); + + expect(mockCore.summary.addRaw).toHaveBeenCalledTimes(1); + expect(mockCore.summary.write).toHaveBeenCalledTimes(1); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("## 🎭 Staged Mode: Create Issues Preview"); + expect(summaryContent).toContain("The following issues would be created if staged mode was disabled:"); + expect(summaryContent).toContain("### Issue 1"); + expect(summaryContent).toContain("**Title:** Test Issue"); + expect(summaryContent).toContain("**Body:**\nTest body"); + expect(summaryContent).toContain("**Labels:** bug, enhancement"); + expect(summaryContent).toContain("---"); + + // Verify that summary content is logged to core.info + expect(mockCore.info).toHaveBeenCalledWith(summaryContent); + expect(mockCore.info).toHaveBeenCalledWith("📝 Create Issues preview written to step summary"); + }); + + it("should generate preview with multiple items", async () => { + const options = { + title: "Update Issues", + description: "The following issue updates would be applied if staged mode was disabled:", + items: [ + { issue_number: 1, title: "New Title 1", status: "open" }, + { issue_number: 2, title: "New Title 2", status: "closed" }, + { issue_number: 3, body: "New Body 3" }, + ], + renderItem: (item, index) => { + let content = `### Issue Update ${index + 1}\n`; + if (item.issue_number) { + content += `**Target Issue:** #${item.issue_number}\n\n`; + } + if (item.title !== undefined) { + content += `**New Title:** ${item.title}\n\n`; + } + if (item.body !== undefined) { + content += `**New Body:**\n${item.body}\n\n`; + } + if (item.status !== undefined) { + content += `**New Status:** ${item.status}\n\n`; + } + return content; + }, + }; + + await generateStagedPreview(options); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("## 🎭 Staged Mode: Update Issues Preview"); + expect(summaryContent).toContain("### Issue Update 1"); + expect(summaryContent).toContain("**Target Issue:** #1"); + expect(summaryContent).toContain("**New Title:** New Title 1"); + expect(summaryContent).toContain("**New Status:** open"); + expect(summaryContent).toContain("### Issue Update 2"); + expect(summaryContent).toContain("**Target Issue:** #2"); + expect(summaryContent).toContain("**New Status:** closed"); + expect(summaryContent).toContain("### Issue Update 3"); + expect(summaryContent).toContain("**New Body:**\nNew Body 3"); + + // Check that all items are separated by dividers + const dividerCount = (summaryContent.match(/---/g) || []).length; + expect(dividerCount).toBe(3); + }); + + it("should handle add labels preview", async () => { + const options = { + title: "Add Labels", + description: "The following labels would be added if staged mode was disabled:", + items: [ + { + item_number: 42, + labels: ["bug", "enhancement", "good first issue"], + }, + ], + renderItem: item => { + let content = ""; + if (item.item_number) { + content += `**Target Issue:** #${item.item_number}\n\n`; + } else { + content += `**Target:** Current issue/PR\n\n`; + } + if (item.labels && item.labels.length > 0) { + content += `**Labels to add:** ${item.labels.join(", ")}\n\n`; + } + return content; + }, + }; + + await generateStagedPreview(options); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("## 🎭 Staged Mode: Add Labels Preview"); + expect(summaryContent).toContain("**Target Issue:** #42"); + expect(summaryContent).toContain("**Labels to add:** bug, enhancement, good first issue"); + }); + + it("should handle PR review comments preview", async () => { + const options = { + title: "Create PR Review Comments", + description: "The following review comments would be created if staged mode was disabled:", + items: [ + { + pull_request_number: 123, + path: "src/main.js", + line: 42, + start_line: 40, + side: "RIGHT", + body: "This needs improvement", + }, + { + path: "src/utils.js", + line: 10, + side: "LEFT", + body: "Consider refactoring", + }, + ], + renderItem: (item, index) => { + const getRepositoryUrl = () => "https://github.com/test/repo"; + let content = `### Review Comment ${index + 1}\n`; + if (item.pull_request_number) { + const repoUrl = getRepositoryUrl(); + const pullUrl = `${repoUrl}/pull/${item.pull_request_number}`; + content += `**Target PR:** [#${item.pull_request_number}](${pullUrl})\n\n`; + } else { + content += `**Target:** Current PR\n\n`; + } + content += `**File:** ${item.path || "No path provided"}\n\n`; + content += `**Line:** ${item.line || "No line provided"}\n\n`; + if (item.start_line) { + content += `**Start Line:** ${item.start_line}\n\n`; + } + content += `**Side:** ${item.side || "RIGHT"}\n\n`; + content += `**Body:**\n${item.body || "No content provided"}\n\n`; + return content; + }, + }; + + await generateStagedPreview(options); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("## 🎭 Staged Mode: Create PR Review Comments Preview"); + expect(summaryContent).toContain("### Review Comment 1"); + expect(summaryContent).toContain("**Target PR:** [#123](https://github.com/test/repo/pull/123)"); + expect(summaryContent).toContain("**File:** src/main.js"); + expect(summaryContent).toContain("**Line:** 42"); + expect(summaryContent).toContain("**Start Line:** 40"); + expect(summaryContent).toContain("**Side:** RIGHT"); + expect(summaryContent).toContain("**Body:**\nThis needs improvement"); + expect(summaryContent).toContain("### Review Comment 2"); + expect(summaryContent).toContain("**Target:** Current PR"); + expect(summaryContent).toContain("**File:** src/utils.js"); + expect(summaryContent).toContain("**Side:** LEFT"); + }); + + it("should handle push to PR branch preview with complex data", async () => { + const options = { + title: "Push to PR Branch", + description: "The following changes would be pushed if staged mode was disabled:", + items: [ + { + target: "feature-branch", + commit_message: "Update implementation", + has_patch: true, + patch_size: 150, + }, + ], + renderItem: item => { + let content = ""; + content += `**Target:** ${item.target}\n\n`; + if (item.commit_message) { + content += `**Commit Message:** ${item.commit_message}\n\n`; + } + if (item.has_patch) { + content += `**Changes:** Patch file exists with ${item.patch_size} lines\n\n`; + } + return content; + }, + }; + + await generateStagedPreview(options); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("## 🎭 Staged Mode: Push to PR Branch Preview"); + expect(summaryContent).toContain("**Target:** feature-branch"); + expect(summaryContent).toContain("**Commit Message:** Update implementation"); + expect(summaryContent).toContain("**Changes:** Patch file exists with 150 lines"); + }); + + it("should handle empty items array", async () => { + const options = { + title: "Test Preview", + description: "Nothing to preview:", + items: [], + renderItem: () => "", + }; + + await generateStagedPreview(options); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("## 🎭 Staged Mode: Test Preview Preview"); + expect(summaryContent).toContain("Nothing to preview:"); + // Should not have any dividers since there are no items + expect(summaryContent).not.toContain("---"); + }); + + it("should handle custom renderItem function with no divider", async () => { + const options = { + title: "Custom Preview", + description: "Custom items:", + items: [{ name: "item1" }, { name: "item2" }], + renderItem: item => { + return `- ${item.name}\n`; + }, + }; + + await generateStagedPreview(options); + + const summaryContent = mockCore.summary.addRaw.mock.calls[0][0]; + expect(summaryContent).toContain("- item1"); + expect(summaryContent).toContain("- item2"); + // Dividers should still be added after each item by the function + const dividerCount = (summaryContent.match(/---/g) || []).length; + expect(dividerCount).toBe(2); + }); + + it("should properly chain summary methods", async () => { + const options = { + title: "Test", + description: "Test description", + items: [{ test: "data" }], + renderItem: () => "Test content\n", + }; + + await generateStagedPreview(options); + + expect(mockCore.summary.addRaw).toHaveBeenCalledTimes(1); + expect(mockCore.summary.addRaw).toHaveReturnedWith(mockCore.summary); + expect(mockCore.summary.write).toHaveBeenCalledTimes(1); + }); + + it("should include index in renderItem callback", async () => { + const renderItemSpy = vi.fn((item, index) => `Item ${index + 1}\n`); + + const options = { + title: "Index Test", + description: "Testing index parameter", + items: [{ id: 1 }, { id: 2 }, { id: 3 }], + renderItem: renderItemSpy, + }; + + await generateStagedPreview(options); + + expect(renderItemSpy).toHaveBeenCalledTimes(3); + expect(renderItemSpy).toHaveBeenNthCalledWith(1, { id: 1 }, 0); + expect(renderItemSpy).toHaveBeenNthCalledWith(2, { id: 2 }, 1); + expect(renderItemSpy).toHaveBeenNthCalledWith(3, { id: 3 }, 2); + }); + }); +}); diff --git a/pkg/workflow/js/update_issue.cjs b/pkg/workflow/js/update_issue.cjs index 439627e266..eeb5083640 100644 --- a/pkg/workflow/js/update_issue.cjs +++ b/pkg/workflow/js/update_issue.cjs @@ -2,6 +2,7 @@ /// const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { generateStagedPreview } = require("./staged_preview.cjs"); async function main() { // Check if we're in staged mode @@ -23,33 +24,30 @@ async function main() { // If in staged mode, emit step summary instead of updating issues if (isStaged) { - let summaryContent = "## 🎭 Staged Mode: Update Issues Preview\n\n"; - summaryContent += "The following issue updates would be applied if staged mode was disabled:\n\n"; - - for (let i = 0; i < updateItems.length; i++) { - const item = updateItems[i]; - summaryContent += `### Issue Update ${i + 1}\n`; - if (item.issue_number) { - summaryContent += `**Target Issue:** #${item.issue_number}\n\n`; - } else { - summaryContent += `**Target:** Current issue\n\n`; - } - - if (item.title !== undefined) { - summaryContent += `**New Title:** ${item.title}\n\n`; - } - if (item.body !== undefined) { - summaryContent += `**New Body:**\n${item.body}\n\n`; - } - if (item.status !== undefined) { - summaryContent += `**New Status:** ${item.status}\n\n`; - } - summaryContent += "---\n\n"; - } + await generateStagedPreview({ + title: "Update Issues", + description: "The following issue updates would be applied if staged mode was disabled:", + items: updateItems, + renderItem: (item, index) => { + let content = `### Issue Update ${index + 1}\n`; + if (item.issue_number) { + content += `**Target Issue:** #${item.issue_number}\n\n`; + } else { + content += `**Target:** Current issue\n\n`; + } - // Write to step summary - await core.summary.addRaw(summaryContent).write(); - core.info("📝 Issue update preview written to step summary"); + if (item.title !== undefined) { + content += `**New Title:** ${item.title}\n\n`; + } + if (item.body !== undefined) { + content += `**New Body:**\n${item.body}\n\n`; + } + if (item.status !== undefined) { + content += `**New Status:** ${item.status}\n\n`; + } + return content; + }, + }); return; }