diff --git a/actions/setup/js/close_entity_helpers.cjs b/actions/setup/js/close_entity_helpers.cjs
index 5d7dec6aac7..254dabed235 100644
--- a/actions/setup/js/close_entity_helpers.cjs
+++ b/actions/setup/js/close_entity_helpers.cjs
@@ -2,7 +2,7 @@
///
const { loadAgentOutput } = require("./load_agent_output.cjs");
-const { generateFooter } = require("./generate_footer.cjs");
+const { generateFooterWithMessages } = require("./messages_footer.cjs");
const { getTrackerID } = require("./get_tracker_id.cjs");
const { getRepositoryUrl } = require("./get_repository_url.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");
@@ -57,7 +57,7 @@ function buildCommentBody(body, triggeringIssueNumber, triggeringPRNumber) {
const workflowSourceURL = process.env.GH_AW_WORKFLOW_SOURCE_URL || "";
const runUrl = buildRunUrl();
- return body.trim() + getTrackerID("markdown") + generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, undefined);
+ return body.trim() + getTrackerID("markdown") + generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, undefined);
}
/**
diff --git a/actions/setup/js/close_pull_request.cjs b/actions/setup/js/close_pull_request.cjs
index 79e3350ef71..4eb67f86927 100644
--- a/actions/setup/js/close_pull_request.cjs
+++ b/actions/setup/js/close_pull_request.cjs
@@ -3,7 +3,7 @@
const { getErrorMessage } = require("./error_helpers.cjs");
const { getTrackerID } = require("./get_tracker_id.cjs");
-const { generateFooter } = require("./generate_footer.cjs");
+const { generateFooterWithMessages } = require("./messages_footer.cjs");
/**
* @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction
@@ -321,7 +321,7 @@ function buildCommentBody(body, triggeringIssueNumber, triggeringPRNumber) {
const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com";
const runUrl = context.payload.repository ? `${context.payload.repository.html_url}/actions/runs/${runId}` : `${githubServer}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
- return body.trim() + getTrackerID("markdown") + generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, undefined);
+ return body.trim() + getTrackerID("markdown") + generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, undefined);
}
module.exports = { main };
diff --git a/actions/setup/js/create_issue.cjs b/actions/setup/js/create_issue.cjs
index 9661badaf57..ffff2f65fac 100644
--- a/actions/setup/js/create_issue.cjs
+++ b/actions/setup/js/create_issue.cjs
@@ -27,7 +27,8 @@ function resetIssuesToAssignCopilot() {
const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
const { sanitizeTitle, applyTitlePrefix } = require("./sanitize_title.cjs");
-const { generateFooter, generateWorkflowIdMarker } = require("./generate_footer.cjs");
+const { generateFooterWithMessages } = require("./messages_footer.cjs");
+const { generateWorkflowIdMarker } = require("./generate_footer.cjs");
const { getTrackerID } = require("./get_tracker_id.cjs");
const { generateTemporaryId, isTemporaryId, normalizeTemporaryId, replaceTemporaryIdReferences } = require("./temporary_id.cjs");
const { parseAllowedRepos, getDefaultTargetRepo, validateRepo, parseRepoSlug } = require("./repo_helpers.cjs");
@@ -444,7 +445,7 @@ async function main(config = {}) {
// Generate footer and add expiration using helper
// When footer is disabled, only add XML markers (no visible footer content)
if (includeFooter) {
- const footer = addExpirationToFooter(generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber).trimEnd(), expiresHours, "Issue");
+ const footer = addExpirationToFooter(generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber).trimEnd(), expiresHours, "Issue");
bodyLines.push(``, ``, footer);
}
diff --git a/actions/setup/js/generate_footer.cjs b/actions/setup/js/generate_footer.cjs
index d7b3dc15fce..febb4f4ba7e 100644
--- a/actions/setup/js/generate_footer.cjs
+++ b/actions/setup/js/generate_footer.cjs
@@ -1,10 +1,6 @@
// @ts-check
///
-const fs = require("fs");
-const { getMissingInfoSections } = require("./missing_messages_helper.cjs");
-const { getBlockedDomains, generateBlockedDomainsSection } = require("./firewall_blocked_domains.cjs");
-
/**
* Generates a standalone workflow-id XML comment marker for searchability.
* This marker enables finding all items (issues, discussions, PRs, comments)
@@ -81,58 +77,6 @@ function generateXMLMarker(workflowName, runUrl) {
return ``;
}
-/**
- * Generate footer with AI attribution and workflow installation instructions
- * @param {string} workflowName - Name of the workflow
- * @param {string} runUrl - URL of the workflow run
- * @param {string} workflowSource - Source of the workflow (owner/repo/path@ref)
- * @param {string} workflowSourceURL - GitHub URL for the workflow source
- * @param {number|undefined} triggeringIssueNumber - Issue number that triggered this workflow
- * @param {number|undefined} triggeringPRNumber - Pull request number that triggered this workflow
- * @param {number|undefined} triggeringDiscussionNumber - Discussion number that triggered this workflow
- * @returns {string} Footer text
- */
-function generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber) {
- let footer = `\n\n> AI generated by [${workflowName}](${runUrl})`;
-
- // Add reference to triggering issue/PR/discussion if available
- if (triggeringIssueNumber) {
- footer += ` for #${triggeringIssueNumber}`;
- } else if (triggeringPRNumber) {
- footer += ` for #${triggeringPRNumber}`;
- } else if (triggeringDiscussionNumber) {
- footer += ` for discussion #${triggeringDiscussionNumber}`;
- }
-
- if (workflowSource && workflowSourceURL) {
- // Load workflow install note template
- const promptsDir = process.env.GH_AW_PROMPTS_DIR || "/opt/gh-aw/prompts";
- const installNoteTemplatePath = `${promptsDir}/workflow_install_note.md`;
- const installNoteTemplate = fs.readFileSync(installNoteTemplatePath, "utf8");
- const installNote = installNoteTemplate.replace("{workflow_source}", workflowSource);
- footer += `\n>\n> ${installNote}`;
- }
-
- // Add missing tools and data sections if available
- const missingInfoSections = getMissingInfoSections();
- if (missingInfoSections) {
- footer += missingInfoSections;
- }
-
- // Add firewall blocked domains section if any domains were blocked
- const blockedDomains = getBlockedDomains();
- const blockedDomainsSection = generateBlockedDomainsSection(blockedDomains);
- if (blockedDomainsSection) {
- footer += blockedDomainsSection;
- }
-
- // Add XML comment marker for traceability
- footer += "\n\n" + generateXMLMarker(workflowName, runUrl);
-
- footer += "\n";
- return footer;
-}
-
/**
* Generate footer for expired entity closing comments
* @param {string} workflowName - Name of the workflow
@@ -154,7 +98,6 @@ function generateExpiredEntityFooter(workflowName, runUrl, workflowId) {
}
module.exports = {
- generateFooter,
generateXMLMarker,
generateWorkflowIdMarker,
getWorkflowIdMarkerContent,
diff --git a/actions/setup/js/generate_footer.test.cjs b/actions/setup/js/generate_footer.test.cjs
index 9476703ddfe..5d4212d6a80 100644
--- a/actions/setup/js/generate_footer.test.cjs
+++ b/actions/setup/js/generate_footer.test.cjs
@@ -1,7 +1,4 @@
-import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
-import fs from "fs";
-import path from "path";
-import os from "os";
+import { describe, it, expect, beforeEach, vi } from "vitest";
// Mock the global objects that GitHub Actions provides
const mockCore = {
@@ -35,31 +32,11 @@ global.core = mockCore;
global.context = mockContext;
describe("generate_footer.cjs", () => {
- let generateFooter;
let generateXMLMarker;
let generateWorkflowIdMarker;
let getWorkflowIdMarkerContent;
- let testPromptsDir;
- let originalEnv;
beforeEach(async () => {
- // Save original environment
- originalEnv = process.env.GH_AW_PROMPTS_DIR;
-
- // Set up test prompts directory
- testPromptsDir = path.join(os.tmpdir(), "gh-aw-test-footer", "prompts");
- if (!fs.existsSync(testPromptsDir)) {
- fs.mkdirSync(testPromptsDir, { recursive: true });
- }
-
- // Create the workflow install note template
- const templatePath = path.join(testPromptsDir, "workflow_install_note.md");
- const templateContent = "To add this workflow in your repository, run `gh aw add {workflow_source}`. See [usage guide](https://github.github.com/gh-aw/guides/packaging-imports/).";
- fs.writeFileSync(templatePath, templateContent, "utf8");
-
- // Set environment to use test directory
- process.env.GH_AW_PROMPTS_DIR = testPromptsDir;
-
// Reset mocks
vi.clearAllMocks();
// Clear env vars
@@ -71,134 +48,11 @@ describe("generate_footer.cjs", () => {
// Dynamic import to get fresh module state
const module = await import("./generate_footer.cjs");
- generateFooter = module.generateFooter;
generateXMLMarker = module.generateXMLMarker;
generateWorkflowIdMarker = module.generateWorkflowIdMarker;
getWorkflowIdMarkerContent = module.getWorkflowIdMarkerContent;
});
- afterEach(() => {
- // Restore original environment
- if (originalEnv !== undefined) {
- process.env.GH_AW_PROMPTS_DIR = originalEnv;
- } else {
- delete process.env.GH_AW_PROMPTS_DIR;
- }
-
- // Clean up test directory
- if (testPromptsDir && fs.existsSync(testPromptsDir)) {
- fs.rmSync(testPromptsDir, { recursive: true, force: true });
- }
- });
-
- describe("generateFooter", () => {
- it("should generate basic footer with workflow name and run URL", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).toContain("> AI generated by [Test Workflow](https://github.com/test/repo/actions/runs/123)");
- expect(result).not.toContain("for #");
- expect(result).not.toContain("gh aw add");
- });
-
- it("should include issue number reference when provided", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", 42, undefined, undefined);
-
- expect(result).toContain("> AI generated by [Test Workflow](https://github.com/test/repo/actions/runs/123) for #42");
- });
-
- it("should include PR number reference when provided", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, 99, undefined);
-
- expect(result).toContain("> AI generated by [Test Workflow](https://github.com/test/repo/actions/runs/123) for #99");
- });
-
- it("should include discussion number reference when provided", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, 7);
-
- expect(result).toContain("> AI generated by [Test Workflow](https://github.com/test/repo/actions/runs/123) for discussion #7");
- });
-
- it("should prioritize issue number over PR number", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", 42, 99, undefined);
-
- expect(result).toContain("for #42");
- expect(result).not.toContain("for #99");
- });
-
- it("should prioritize PR number over discussion number", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, 99, 7);
-
- expect(result).toContain("for #99");
- expect(result).not.toContain("for discussion #7");
- });
-
- it("should include workflow installation instructions when source is provided", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "owner/repo/workflow.md@main", "https://github.com/owner/repo/blob/main/workflow.md", undefined, undefined, undefined);
-
- expect(result).toContain("gh aw add owner/repo/workflow.md@main");
- expect(result).toContain("See [usage guide](https://github.github.com/gh-aw/guides/packaging-imports/)");
- });
-
- it("should not include installation instructions when source is empty", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).not.toContain("gh aw add");
- });
-
- it("should include both issue reference and installation instructions", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "owner/repo/workflow.md@main", "https://github.com/owner/repo/blob/main/workflow.md", 42, undefined, undefined);
-
- expect(result).toContain("for #42");
- expect(result).toContain("gh aw add owner/repo/workflow.md@main");
- });
-
- it("should start and end with newlines", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).toMatch(/^\n\n/);
- expect(result).toMatch(/\n$/);
- });
-
- it("should handle special characters in workflow name", () => {
- const result = generateFooter("Test Workflow (v2) [beta]", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).toContain("> AI generated by [Test Workflow (v2) [beta]](https://github.com/test/repo/actions/runs/123)");
- });
-
- it("should include XML comment marker for traceability", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).toContain("");
- });
-
- it("should include engine metadata in XML marker when env vars are set", async () => {
- // Set env vars before re-importing
- process.env.GH_AW_ENGINE_ID = "copilot";
- process.env.GH_AW_ENGINE_VERSION = "1.0.0";
- process.env.GH_AW_ENGINE_MODEL = "gpt-5";
-
- // Re-import to pick up new env vars
- vi.resetModules();
- const freshModule = await import("./generate_footer.cjs");
-
- const result = freshModule.generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).toContain("");
- });
-
- it("should not include blocked domains section when no firewall logs exist", () => {
- const result = generateFooter("Test Workflow", "https://github.com/test/repo/actions/runs/123", "", "", undefined, undefined, undefined);
-
- expect(result).not.toContain("⚠️ Firewall blocked");
- expect(result).not.toContain("");
- });
- });
-
describe("generateXMLMarker", () => {
it("should generate basic XML marker with workflow name and run URL", () => {
const result = generateXMLMarker("Test Workflow", "https://github.com/test/repo/actions/runs/123");
diff --git a/actions/setup/js/mark_pull_request_as_ready_for_review.cjs b/actions/setup/js/mark_pull_request_as_ready_for_review.cjs
index d19bb6fd9bb..27f28475ca9 100644
--- a/actions/setup/js/mark_pull_request_as_ready_for_review.cjs
+++ b/actions/setup/js/mark_pull_request_as_ready_for_review.cjs
@@ -5,7 +5,7 @@
* @typedef {import('./types/handler-factory').HandlerFactoryFunction} HandlerFactoryFunction
*/
-const { generateFooter } = require("./generate_footer.cjs");
+const { generateFooterWithMessages } = require("./messages_footer.cjs");
const { sanitizeContent } = require("./sanitize_content.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");
@@ -171,7 +171,7 @@ async function main(config = {}) {
const triggeringDiscussionNumber = context.payload?.discussion?.number;
const sanitizedReason = sanitizeContent(item.reason);
- const footer = generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber);
+ const footer = generateFooterWithMessages(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber);
const commentBody = `${sanitizedReason}\n\n${footer}`;
await addPullRequestComment(github, context.repo.owner, context.repo.repo, prNumber, commentBody);
diff --git a/actions/setup/js/pr_review_buffer.cjs b/actions/setup/js/pr_review_buffer.cjs
index 8469012f463..cc442e02b1e 100644
--- a/actions/setup/js/pr_review_buffer.cjs
+++ b/actions/setup/js/pr_review_buffer.cjs
@@ -15,7 +15,7 @@
* await buffer.submitReview();
*/
-const { generateFooter } = require("./generate_footer.cjs");
+const { generateFooterWithMessages } = require("./messages_footer.cjs");
const { getErrorMessage } = require("./error_helpers.cjs");
/**
@@ -185,7 +185,7 @@ function createReviewBuffer() {
// Add footer to review body if enabled and we have footer context.
// Footer is always added (even for body-less reviews) to track which workflow submitted the review.
if (includeFooter && footerContext) {
- body += generateFooter(
+ body += generateFooterWithMessages(
footerContext.workflowName,
footerContext.runUrl,
footerContext.workflowSource,
diff --git a/actions/setup/js/pr_review_buffer.test.cjs b/actions/setup/js/pr_review_buffer.test.cjs
index cdc3f164d82..de1f5669769 100644
--- a/actions/setup/js/pr_review_buffer.test.cjs
+++ b/actions/setup/js/pr_review_buffer.test.cjs
@@ -1,7 +1,4 @@
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
-import fs from "fs";
-import os from "os";
-import path from "path";
const mockCore = {
debug: vi.fn(),
@@ -27,19 +24,14 @@ const { createReviewBuffer } = require("./pr_review_buffer.cjs");
describe("pr_review_buffer (factory pattern)", () => {
let buffer;
- let testPromptsDir;
- let originalEnv;
+ let originalMessages;
beforeEach(() => {
vi.clearAllMocks();
- // Save and set up test prompts directory for generateFooter
- originalEnv = process.env.GH_AW_PROMPTS_DIR;
- testPromptsDir = path.join(os.tmpdir(), "gh-aw-test-pr-review-buffer", "prompts");
- fs.mkdirSync(testPromptsDir, { recursive: true });
- const templateContent = "Install this workflow: `gh aw add {workflow_source}` ([usage guide](https://github.com/github/gh-aw))";
- fs.writeFileSync(path.join(testPromptsDir, "workflow_install_note.md"), templateContent, "utf8");
- process.env.GH_AW_PROMPTS_DIR = testPromptsDir;
+ // Save and clear messages env var (generateFooterWithMessages reads this)
+ originalMessages = process.env.GH_AW_SAFE_OUTPUT_MESSAGES;
+ delete process.env.GH_AW_SAFE_OUTPUT_MESSAGES;
// Create a fresh buffer instance for each test (no shared global state)
buffer = createReviewBuffer();
@@ -47,17 +39,10 @@ describe("pr_review_buffer (factory pattern)", () => {
afterEach(() => {
// Restore original environment
- if (originalEnv !== undefined) {
- process.env.GH_AW_PROMPTS_DIR = originalEnv;
+ if (originalMessages !== undefined) {
+ process.env.GH_AW_SAFE_OUTPUT_MESSAGES = originalMessages;
} else {
- delete process.env.GH_AW_PROMPTS_DIR;
- }
-
- // Clean up test directory
- try {
- fs.rmSync(path.dirname(testPromptsDir), { recursive: true, force: true });
- } catch {
- // Ignore cleanup errors
+ delete process.env.GH_AW_SAFE_OUTPUT_MESSAGES;
}
});
@@ -405,7 +390,7 @@ describe("pr_review_buffer (factory pattern)", () => {
const callArgs = mockGithub.rest.pulls.createReview.mock.calls[0][0];
expect(callArgs.body).toContain("Review body");
- // Footer generated by generate_footer.cjs
+ // Footer generated by messages_footer.cjs
expect(callArgs.body).toContain("test-workflow");
});