Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions actions/setup/js/create_discussion.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ async function main(config = {}) {
const expiresHours = config.expires ? parseInt(String(config.expires), 10) : 0;
const fallbackToIssue = config.fallback_to_issue !== false; // Default to true
const closeOlderDiscussions = config.close_older_discussions === true || config.close_older_discussions === "true";
const includeFooter = config.footer !== false; // Default to true (include footer)

// Parse labels from config
const labelsConfig = config.labels || [];
Expand Down Expand Up @@ -366,15 +367,18 @@ async function main(config = {}) {
const runUrl = context.payload.repository ? `${context.payload.repository.html_url}/actions/runs/${runId}` : `${githubServer}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;

// Generate footer with expiration using helper
const footer = generateFooterWithExpiration({
footerText: `> AI generated by [${workflowName}](${runUrl})`,
expiresHours,
entityType: "Discussion",
});

bodyLines.push(``, ``, footer);
// When footer is disabled, only add XML markers (no visible footer content)
if (includeFooter) {
const footer = generateFooterWithExpiration({
footerText: `> AI generated by [${workflowName}](${runUrl})`,
expiresHours,
entityType: "Discussion",
});
bodyLines.push(``, ``, footer);
}

// Add standalone workflow-id marker for searchability (consistent with comments)
// Always add XML markers even when footer is disabled
if (workflowId) {
bodyLines.push(``, generateWorkflowIdMarker(workflowId));
}
Expand Down
10 changes: 7 additions & 3 deletions actions/setup/js/create_issue.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ async function main(config = {}) {
const defaultTargetRepo = getDefaultTargetRepo(config);
const groupEnabled = config.group === true || config.group === "true";
const closeOlderIssuesEnabled = config.close_older_issues === true || config.close_older_issues === "true";
const includeFooter = config.footer !== false; // Default to true (include footer)

// Check if copilot assignment is enabled
const assignCopilot = process.env.GH_AW_ASSIGN_COPILOT === "true";
Expand Down Expand Up @@ -417,11 +418,14 @@ async function main(config = {}) {
}

// Generate footer and add expiration using helper
const footer = addExpirationToFooter(generateFooter(workflowName, runUrl, workflowSource, workflowSourceURL, triggeringIssueNumber, triggeringPRNumber, triggeringDiscussionNumber).trimEnd(), expiresHours, "Issue");

bodyLines.push(``, ``, footer);
// 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");
bodyLines.push(``, ``, footer);
}

// Add standalone workflow-id marker for searchability (consistent with comments)
// Always add XML markers even when footer is disabled
if (workflowId) {
bodyLines.push(``, generateWorkflowIdMarker(workflowId));
}
Expand Down
20 changes: 12 additions & 8 deletions actions/setup/js/create_pull_request.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ async function main(config = {}) {
const baseBranch = config.base_branch || "";
const maxSizeKb = config.max_patch_size ? parseInt(String(config.max_patch_size), 10) : 1024;
const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config);
const includeFooter = config.footer !== false; // Default to true (include footer)

// Environment validation - fail early if required variables are missing
const workflowId = process.env.GH_AW_WORKFLOW_ID;
Expand Down Expand Up @@ -371,16 +372,19 @@ async function main(config = {}) {
}

// Generate footer with expiration using helper
const footer = generateFooterWithExpiration({
footerText: `> AI generated by [${workflowName}](${runUrl})`,
expiresHours,
entityType: "Pull Request",
suffix: expiresHours > 0 ? "\n\n<!-- gh-aw-expires-type: pull-request -->" : undefined,
});

bodyLines.push(``, ``, footer);
// When footer is disabled, only add XML markers (no visible footer content)
if (includeFooter) {
const footer = generateFooterWithExpiration({
footerText: `> AI generated by [${workflowName}](${runUrl})`,
expiresHours,
entityType: "Pull Request",
suffix: expiresHours > 0 ? "\n\n<!-- gh-aw-expires-type: pull-request -->" : undefined,
});
bodyLines.push(``, ``, footer);
}

// Add standalone workflow-id marker for searchability (consistent with comments)
// Always add XML markers even when footer is disabled
if (workflowId) {
bodyLines.push(``, generateWorkflowIdMarker(workflowId));
}
Expand Down
6 changes: 6 additions & 0 deletions actions/setup/js/types/safe-outputs-config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface CreateIssueConfig extends SafeOutputConfig {
labels?: string[];
"target-repo"?: string;
"allowed-repos"?: string[];
footer?: boolean;
}

/**
Expand All @@ -26,6 +27,7 @@ interface CreateDiscussionConfig extends SafeOutputConfig {
"category-id"?: string;
"target-repo"?: string;
"allowed-repos"?: string[];
footer?: boolean;
}

/**
Expand Down Expand Up @@ -81,6 +83,7 @@ interface CreatePullRequestConfig extends SafeOutputConfig {
labels?: string[];
draft?: boolean;
"if-no-changes"?: string;
footer?: boolean;
}

/**
Expand Down Expand Up @@ -128,6 +131,7 @@ interface UpdateIssueConfig extends SafeOutputConfig {
target?: string;
title?: boolean;
body?: boolean;
footer?: boolean;
}

/**
Expand All @@ -137,6 +141,7 @@ interface UpdateDiscussionConfig extends SafeOutputConfig {
target?: string;
title?: boolean;
body?: boolean;
footer?: boolean;
}

/**
Expand Down Expand Up @@ -190,6 +195,7 @@ interface AssignToAgentConfig extends SafeOutputConfig {
*/
interface UpdateReleaseConfig extends SafeOutputConfig {
target?: string;
footer?: boolean;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions actions/setup/js/update_discussion.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ function buildDiscussionUpdateData(item, config) {
updateData.body = item.body;
}

// Pass footer config to executeUpdate (default to true)
updateData._includeFooter = config.footer !== false;

Comment on lines +137 to +139
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildDiscussionUpdateData sets updateData._includeFooter, but executeDiscussionUpdate never reads it and the handler never uses updateBody/footer generation. As-is, safe-outputs.update-discussion.footer has no effect. Either implement footer handling for discussion body updates (and strip internal fields before sending to GraphQL), or remove _includeFooter until it’s supported.

Suggested change
// Pass footer config to executeUpdate (default to true)
updateData._includeFooter = config.footer !== false;

Copilot uses AI. Check for mistakes.
return { success: true, data: updateData };
}

Expand Down
7 changes: 6 additions & 1 deletion actions/setup/js/update_issue.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ async function executeIssueUpdate(github, context, issueNumber, updateData) {
// Default to "append" to add footer with AI attribution
const operation = updateData._operation || "append";
let rawBody = updateData._rawBody;
const includeFooter = updateData._includeFooter !== false; // Default to true

// Remove internal fields
const { _operation, _rawBody, ...apiData } = updateData;
const { _operation, _rawBody, _includeFooter, ...apiData } = updateData;

// If we have a body, process it with the appropriate operation
if (rawBody !== undefined) {
Expand Down Expand Up @@ -61,6 +62,7 @@ async function executeIssueUpdate(github, context, issueNumber, updateData) {
workflowName,
runUrl,
runId: context.runId,
includeFooter, // Pass footer flag to helper
});

core.info(`Will update body (length: ${apiData.body.length})`);
Expand Down Expand Up @@ -136,6 +138,9 @@ function buildIssueUpdateData(item, config) {
updateData.milestone = item.milestone;
}

// Pass footer config to executeUpdate (default to true)
updateData._includeFooter = config.footer !== false;

return { success: true, data: updateData };
}

Expand Down
11 changes: 6 additions & 5 deletions actions/setup/js/update_pr_description_helpers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,16 @@ function findIsland(body, runId) {
* @param {string} params.workflowName - Name of the workflow
* @param {string} params.runUrl - URL of the workflow run
* @param {number} params.runId - Workflow run ID
* @param {boolean} [params.includeFooter=true] - Whether to include AI-generated footer (default: true)
* @returns {string} Updated body content
*/
function updateBody(params) {
const { currentBody, newContent, operation, workflowName, runUrl, runId } = params;
const aiFooter = buildAIFooter(workflowName, runUrl);
const { currentBody, newContent, operation, workflowName, runUrl, runId, includeFooter = true } = params;
const aiFooter = includeFooter ? buildAIFooter(workflowName, runUrl) : "";

if (operation === "replace") {
// Replace: use new content with AI footer
core.info("Operation: replace (full body replacement with footer)");
// Replace: use new content with optional AI footer
core.info("Operation: replace (full body replacement)");
return newContent + aiFooter;
}
Comment on lines +78 to 85
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When includeFooter is false, updateBody currently appends/prepends/replaces content without adding any traceability XML markers. The PR description/schema claim markers must still be injected when the visible footer is omitted, so this helper should always add the required XML markers (and only conditionally add the visible attribution/footer text).

Copilot uses AI. Check for mistakes.

Expand Down Expand Up @@ -109,7 +110,7 @@ function updateBody(params) {
}

if (operation === "prepend") {
// Prepend: add content, AI footer, and horizontal line at the start
// Prepend: add content, AI footer (if enabled), and horizontal line at the start
core.info("Operation: prepend (add to start with separator)");
const prependSection = `${newContent}${aiFooter}\n\n---\n\n`;
return prependSection + currentBody;
Expand Down
96 changes: 96 additions & 0 deletions actions/setup/js/update_pr_description_helpers.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,100 @@ describe("update_pr_description_helpers.cjs", () => {
expect(mockCore.info).toHaveBeenCalledWith(expect.stringContaining("append"));
});
});

describe("Footer parameter", () => {
it("should omit footer when includeFooter is false", () => {
const result = updateBody({
currentBody: "Original",
newContent: "New content",
operation: "append",
workflowName: "Test",
runUrl: "https://github.com/test/actions/runs/123",
runId: 123,
includeFooter: false,
});
expect(result).toContain("Original");
expect(result).toContain("New content");
expect(result).not.toContain("Generated by");
expect(result).not.toContain("AI generated");
});

it("should include footer by default when includeFooter is not specified", () => {
const result = updateBody({
currentBody: "Original",
newContent: "New content",
operation: "append",
workflowName: "Test",
runUrl: "https://github.com/test/actions/runs/123",
runId: 123,
// includeFooter not specified, should default to true
});
expect(result).toContain("Original");
expect(result).toContain("New content");
expect(result).toContain("Generated by");
});

it("should include footer when includeFooter is explicitly true", () => {
const result = updateBody({
currentBody: "Original",
newContent: "New content",
operation: "append",
workflowName: "Test",
runUrl: "https://github.com/test/actions/runs/123",
runId: 123,
includeFooter: true,
});
expect(result).toContain("Original");
expect(result).toContain("New content");
expect(result).toContain("Generated by");
});

it("should omit footer in replace operation when includeFooter is false", () => {
const result = updateBody({
currentBody: "Original",
newContent: "Replacement",
operation: "replace",
workflowName: "Test",
runUrl: "https://github.com/test/actions/runs/123",
runId: 123,
includeFooter: false,
});
expect(result).toBe("Replacement");
expect(result).not.toContain("Generated by");
});

it("should omit footer in prepend operation when includeFooter is false", () => {
const result = updateBody({
currentBody: "Original",
newContent: "Prepended",
operation: "prepend",
workflowName: "Test",
runUrl: "https://github.com/test/actions/runs/123",
runId: 123,
includeFooter: false,
});
expect(result).toContain("Prepended");
expect(result).toContain("Original");
expect(result).not.toContain("Generated by");
});

it("should omit footer in replace-island operation when includeFooter is false", () => {
const currentBody = "Before\n<!-- gh-aw-island-start:123 -->\nOld island\n<!-- gh-aw-island-end:123 -->\nAfter";
const result = updateBody({
currentBody,
newContent: "New island",
operation: "replace-island",
workflowName: "Test",
runUrl: "https://github.com/test/actions/runs/123",
runId: 123,
includeFooter: false,
});
expect(result).toContain("Before");
expect(result).toContain("New island");
expect(result).toContain("After");
expect(result).not.toContain("Generated by");
expect(result).toContain("<!-- gh-aw-island-start:123 -->");
expect(result).toContain("<!-- gh-aw-island-end:123 -->");
});
});
});
3 changes: 3 additions & 0 deletions actions/setup/js/update_release.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ const { updateBody } = require("./update_pr_description_helpers.cjs");
*
* @param {Object} config - Handler configuration
* @param {number} [config.max] - Maximum number of releases to update
* @param {boolean} [config.footer] - Controls whether AI-generated footer is added (default: true)
* @returns {Promise<Function>} Handler function that processes a single message
*/
async function main(config = {}) {
// Check if we're in staged mode
const isStaged = process.env.GH_AW_SAFE_OUTPUTS_STAGED === "true";
const workflowName = process.env.GH_AW_WORKFLOW_NAME || "GitHub Agentic Workflow";
const includeFooter = config.footer !== false; // Default to true (include footer)

/**
* Process a single update-release message
Expand Down Expand Up @@ -90,6 +92,7 @@ async function main(config = {}) {
workflowName,
runUrl,
runId: context.runId,
includeFooter, // Pass footer flag to helper
});

// Update the release
Expand Down
Loading
Loading