Skip to content

Commit 2f34639

Browse files
authored
feat: add activation-comments to disable activation/fallback bot comments (#17834)
1 parent 4094506 commit 2f34639

17 files changed

Lines changed: 332 additions & 10 deletions

.changeset/patch-disable-activation-comments.md

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

actions/setup/js/add_workflow_run_comment.cjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const { getErrorMessage } = require("./error_helpers.cjs");
66
const { generateWorkflowIdMarker } = require("./generate_footer.cjs");
77
const { sanitizeContent } = require("./sanitize_content.cjs");
88
const { ERR_NOT_FOUND, ERR_VALIDATION } = require("./error_codes.cjs");
9+
const { getMessages } = require("./messages_core.cjs");
10+
const { parseBoolTemplatable } = require("./templatable.cjs");
911

1012
/**
1113
* Event type descriptions for comment messages
@@ -60,6 +62,13 @@ function setCommentOutputs(commentId, commentUrl) {
6062
* Use add_reaction.cjs in the pre-activation job to add reactions first for immediate feedback.
6163
*/
6264
async function main() {
65+
// Check if activation comments are disabled
66+
const messagesConfig = getMessages();
67+
if (!parseBoolTemplatable(messagesConfig?.activationComments, true)) {
68+
core.info("activation-comments is disabled: skipping activation comment creation");
69+
return;
70+
}
71+
6372
const runId = context.runId;
6473
const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com";
6574
const runUrl = context.payload.repository ? `${context.payload.repository.html_url}/actions/runs/${runId}` : `${githubServer}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;

actions/setup/js/generate_footer.cjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ function generateXMLMarker(workflowName, runUrl) {
4343
const engineVersion = process.env.GH_AW_ENGINE_VERSION || "";
4444
const engineModel = process.env.GH_AW_ENGINE_MODEL || "";
4545
const trackerId = process.env.GH_AW_TRACKER_ID || "";
46+
const runId = process.env.GITHUB_RUN_ID || "";
47+
const workflowId = process.env.GH_AW_WORKFLOW_ID || "";
4648

4749
// Build the key-value pairs for the marker
4850
const parts = [];
@@ -70,6 +72,16 @@ function generateXMLMarker(workflowName, runUrl) {
7072
parts.push(`model: ${engineModel}`);
7173
}
7274

75+
// Add numeric run ID if available
76+
if (runId) {
77+
parts.push(`id: ${runId}`);
78+
}
79+
80+
// Add workflow identifier if available
81+
if (workflowId) {
82+
parts.push(`workflow_id: ${workflowId}`);
83+
}
84+
7385
// Always include run URL
7486
parts.push(`run: ${runUrl}`);
7587

actions/setup/js/generate_footer.test.cjs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ describe("generate_footer.cjs", () => {
4545
delete process.env.GH_AW_ENGINE_MODEL;
4646
delete process.env.GH_AW_TRACKER_ID;
4747
delete process.env.GH_AW_WORKFLOW_ID;
48+
delete process.env.GITHUB_RUN_ID;
4849

4950
// Dynamic import to get fresh module state
5051
const module = await import("./generate_footer.cjs");
@@ -126,6 +127,42 @@ describe("generate_footer.cjs", () => {
126127

127128
expect(result).toBe("<!-- gh-aw-agentic-workflow: Test Workflow, gh-aw-tracker-id: workflow-2024-q1, engine: copilot, version: 1.0.0, model: gpt-5, run: https://github.com/test/repo/actions/runs/123 -->");
128129
});
130+
131+
it("should include run id when GITHUB_RUN_ID env var is set", async () => {
132+
process.env.GITHUB_RUN_ID = "9876543210";
133+
134+
vi.resetModules();
135+
const freshModule = await import("./generate_footer.cjs");
136+
137+
const result = freshModule.generateXMLMarker("Test Workflow", "https://github.com/test/repo/actions/runs/9876543210");
138+
139+
expect(result).toBe("<!-- gh-aw-agentic-workflow: Test Workflow, id: 9876543210, run: https://github.com/test/repo/actions/runs/9876543210 -->");
140+
});
141+
142+
it("should include workflow_id when GH_AW_WORKFLOW_ID env var is set", async () => {
143+
process.env.GH_AW_WORKFLOW_ID = "smoke-copilot";
144+
145+
vi.resetModules();
146+
const freshModule = await import("./generate_footer.cjs");
147+
148+
const result = freshModule.generateXMLMarker("Test Workflow", "https://github.com/test/repo/actions/runs/123");
149+
150+
expect(result).toBe("<!-- gh-aw-agentic-workflow: Test Workflow, workflow_id: smoke-copilot, run: https://github.com/test/repo/actions/runs/123 -->");
151+
});
152+
153+
it("should include all identifiers when all standard env vars are set", async () => {
154+
process.env.GH_AW_ENGINE_ID = "copilot";
155+
process.env.GH_AW_TRACKER_ID = "tracker-abc";
156+
process.env.GITHUB_RUN_ID = "12345";
157+
process.env.GH_AW_WORKFLOW_ID = "my-workflow";
158+
159+
vi.resetModules();
160+
const freshModule = await import("./generate_footer.cjs");
161+
162+
const result = freshModule.generateXMLMarker("My Workflow", "https://github.com/test/repo/actions/runs/12345");
163+
164+
expect(result).toBe("<!-- gh-aw-agentic-workflow: My Workflow, gh-aw-tracker-id: tracker-abc, engine: copilot, id: 12345, workflow_id: my-workflow, run: https://github.com/test/repo/actions/runs/12345 -->");
165+
});
129166
});
130167

131168
describe("generateWorkflowIdMarker", () => {

actions/setup/js/messages_core.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@
3333
* @property {string} [runSuccess] - Custom workflow success message template
3434
* @property {string} [runFailure] - Custom workflow failure message template
3535
* @property {string} [detectionFailure] - Custom detection job failure message template
36+
* @property {string} [pullRequestCreated] - Custom template for pull request creation link. Placeholders: {item_number}, {item_url}
37+
* @property {string} [issueCreated] - Custom template for issue creation link. Placeholders: {item_number}, {item_url}
38+
* @property {string} [commitPushed] - Custom template for commit push link. Placeholders: {commit_sha}, {short_sha}, {commit_url}
3639
* @property {string} [agentFailureIssue] - Custom footer template for agent failure tracking issues
3740
* @property {string} [agentFailureComment] - Custom footer template for comments on agent failure tracking issues
3841
* @property {string} [closeOlderDiscussion] - Custom message for closing older discussions as outdated
3942
* @property {boolean} [appendOnlyComments] - If true, create new comments instead of updating the activation comment
43+
* @property {string|boolean} [activationComments] - If false or "false", disable all activation/fallback comments entirely. Supports templatable boolean values (default: true)
4044
*/
4145

4246
/**

actions/setup/js/messages_footer.cjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ function generateXMLMarker(workflowName, runUrl) {
171171
const engineVersion = process.env.GH_AW_ENGINE_VERSION || "";
172172
const engineModel = process.env.GH_AW_ENGINE_MODEL || "";
173173
const trackerId = process.env.GH_AW_TRACKER_ID || "";
174+
const runId = process.env.GITHUB_RUN_ID || "";
175+
const workflowId = process.env.GH_AW_WORKFLOW_ID || "";
174176

175177
// Build the key-value pairs for the marker
176178
const parts = [];
@@ -198,6 +200,16 @@ function generateXMLMarker(workflowName, runUrl) {
198200
parts.push(`model: ${engineModel}`);
199201
}
200202

203+
// Add numeric run ID if available
204+
if (runId) {
205+
parts.push(`id: ${runId}`);
206+
}
207+
208+
// Add workflow identifier if available
209+
if (workflowId) {
210+
parts.push(`workflow_id: ${workflowId}`);
211+
}
212+
201213
// Always include run URL
202214
parts.push(`run: ${runUrl}`);
203215

actions/setup/js/messages_run_status.cjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,67 @@ function getDetectionFailureMessage(ctx) {
108108
return messages?.detectionFailure ? renderTemplate(messages.detectionFailure, templateContext) : renderTemplate(defaultMessage, templateContext);
109109
}
110110

111+
/**
112+
* @typedef {Object} PullRequestCreatedContext
113+
* @property {number} itemNumber - PR number
114+
* @property {string} itemUrl - URL of the pull request
115+
*/
116+
117+
/**
118+
* Get the pull-request-created message, using custom template if configured.
119+
* @param {PullRequestCreatedContext} ctx - Context for message generation
120+
* @returns {string} Pull-request-created message
121+
*/
122+
function getPullRequestCreatedMessage(ctx) {
123+
const messages = getMessages();
124+
const templateContext = toSnakeCase(ctx);
125+
const defaultMessage = "Pull request created: [#{item_number}]({item_url})";
126+
return messages?.pullRequestCreated ? renderTemplate(messages.pullRequestCreated, templateContext) : renderTemplate(defaultMessage, templateContext);
127+
}
128+
129+
/**
130+
* @typedef {Object} IssueCreatedContext
131+
* @property {number} itemNumber - Issue number
132+
* @property {string} itemUrl - URL of the issue
133+
*/
134+
135+
/**
136+
* Get the issue-created message, using custom template if configured.
137+
* @param {IssueCreatedContext} ctx - Context for message generation
138+
* @returns {string} Issue-created message
139+
*/
140+
function getIssueCreatedMessage(ctx) {
141+
const messages = getMessages();
142+
const templateContext = toSnakeCase(ctx);
143+
const defaultMessage = "Issue created: [#{item_number}]({item_url})";
144+
return messages?.issueCreated ? renderTemplate(messages.issueCreated, templateContext) : renderTemplate(defaultMessage, templateContext);
145+
}
146+
147+
/**
148+
* @typedef {Object} CommitPushedContext
149+
* @property {string} commitSha - Full commit SHA
150+
* @property {string} shortSha - Short (7-char) commit SHA
151+
* @property {string} commitUrl - URL of the commit
152+
*/
153+
154+
/**
155+
* Get the commit-pushed message, using custom template if configured.
156+
* @param {CommitPushedContext} ctx - Context for message generation
157+
* @returns {string} Commit-pushed message
158+
*/
159+
function getCommitPushedMessage(ctx) {
160+
const messages = getMessages();
161+
const templateContext = toSnakeCase(ctx);
162+
const defaultMessage = "Commit pushed: [`{short_sha}`]({commit_url})";
163+
return messages?.commitPushed ? renderTemplate(messages.commitPushed, templateContext) : renderTemplate(defaultMessage, templateContext);
164+
}
165+
111166
module.exports = {
112167
getRunStartedMessage,
113168
getRunSuccessMessage,
114169
getRunFailureMessage,
115170
getDetectionFailureMessage,
171+
getPullRequestCreatedMessage,
172+
getIssueCreatedMessage,
173+
getCommitPushedMessage,
116174
};

actions/setup/js/notify_comment_error.cjs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const { getMessages } = require("./messages_core.cjs");
1111
const { getErrorMessage, isLockedError } = require("./error_helpers.cjs");
1212
const { sanitizeContent } = require("./sanitize_content.cjs");
1313
const { ERR_VALIDATION } = require("./error_codes.cjs");
14+
const { parseBoolTemplatable } = require("./templatable.cjs");
1415

1516
/**
1617
* Collect generated asset URLs from safe output jobs
@@ -60,6 +61,12 @@ async function main() {
6061
const messagesConfig = getMessages();
6162
const appendOnlyComments = messagesConfig?.appendOnlyComments === true;
6263

64+
// If activation comments are disabled entirely, skip all comment updates
65+
if (!parseBoolTemplatable(messagesConfig?.activationComments, true)) {
66+
core.info("activation-comments is disabled: skipping completion comment update");
67+
return;
68+
}
69+
6370
core.info(`Comment ID: ${commentId}`);
6471
core.info(`Comment Repo: ${commentRepo}`);
6572
core.info(`Run URL: ${runUrl}`);

actions/setup/js/update_activation_comment.cjs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44
const { getErrorMessage } = require("./error_helpers.cjs");
55
const { getMessages } = require("./messages_core.cjs");
66
const { sanitizeContent } = require("./sanitize_content.cjs");
7+
const { getPullRequestCreatedMessage, getIssueCreatedMessage, getCommitPushedMessage } = require("./messages_run_status.cjs");
8+
const { parseBoolTemplatable } = require("./templatable.cjs");
9+
const { generateXMLMarker } = require("./generate_footer.cjs");
10+
11+
/**
12+
* Build the workflow run URL from context and environment.
13+
* @param {any} context - GitHub Actions context
14+
* @returns {string} The workflow run URL
15+
*/
16+
function getRunUrl(context) {
17+
const runId = context.runId || process.env.GITHUB_RUN_ID || "";
18+
const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com";
19+
return context.payload?.repository?.html_url ? `${context.payload.repository.html_url}/actions/runs/${runId}` : `${githubServer}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
20+
}
721

822
/**
923
* Update the activation comment with a link to the created pull request or issue
@@ -16,7 +30,10 @@ const { sanitizeContent } = require("./sanitize_content.cjs");
1630
*/
1731
async function updateActivationComment(github, context, core, itemUrl, itemNumber, itemType = "pull_request") {
1832
const itemLabel = itemType === "issue" ? "issue" : "pull request";
19-
const linkMessage = itemType === "issue" ? `\n\n✅ Issue created: [#${itemNumber}](${itemUrl})` : `\n\n✅ Pull request created: [#${itemNumber}](${itemUrl})`;
33+
const workflowName = process.env.GH_AW_WORKFLOW_NAME || "Workflow";
34+
const runUrl = getRunUrl(context);
35+
const body = itemType === "issue" ? getIssueCreatedMessage({ itemNumber, itemUrl }) : getPullRequestCreatedMessage({ itemNumber, itemUrl });
36+
const linkMessage = `\n\n${body}\n\n${generateXMLMarker(workflowName, runUrl)}`;
2037
await updateActivationCommentWithMessage(github, context, core, linkMessage, itemLabel, { targetIssueNumber: itemNumber });
2138
}
2239

@@ -32,7 +49,9 @@ async function updateActivationComment(github, context, core, itemUrl, itemNumbe
3249
*/
3350
async function updateActivationCommentWithCommit(github, context, core, commitSha, commitUrl, options = {}) {
3451
const shortSha = commitSha.substring(0, 7);
35-
const message = `\n\n✅ Commit pushed: [\`${shortSha}\`](${commitUrl})`;
52+
const workflowName = process.env.GH_AW_WORKFLOW_NAME || "Workflow";
53+
const runUrl = getRunUrl(context);
54+
const message = `\n\n${getCommitPushedMessage({ commitSha, shortSha, commitUrl })}\n\n${generateXMLMarker(workflowName, runUrl)}`;
3655
await updateActivationCommentWithMessage(github, context, core, message, "commit", options);
3756
}
3857

@@ -54,6 +73,12 @@ async function updateActivationCommentWithMessage(github, context, core, message
5473
const messagesConfig = getMessages();
5574
const appendOnlyComments = messagesConfig?.appendOnlyComments === true;
5675

76+
// Check if activation comments are disabled entirely
77+
if (!parseBoolTemplatable(messagesConfig?.activationComments, true)) {
78+
core.info("activation-comments is disabled: skipping activation comment update");
79+
return;
80+
}
81+
5782
// Parse comment repo (format: "owner/repo") with validation
5883
let repoOwner = context.repo.owner;
5984
let repoName = context.repo.repo;

0 commit comments

Comments
 (0)