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
5 changes: 5 additions & 0 deletions .changeset/patch-disable-activation-comments.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions actions/setup/js/add_workflow_run_comment.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const { getErrorMessage } = require("./error_helpers.cjs");
const { generateWorkflowIdMarker } = require("./generate_footer.cjs");
const { sanitizeContent } = require("./sanitize_content.cjs");
const { ERR_NOT_FOUND, ERR_VALIDATION } = require("./error_codes.cjs");
const { getMessages } = require("./messages_core.cjs");
const { parseBoolTemplatable } = require("./templatable.cjs");

/**
* Event type descriptions for comment messages
Expand Down Expand Up @@ -60,6 +62,13 @@ function setCommentOutputs(commentId, commentUrl) {
* Use add_reaction.cjs in the pre-activation job to add reactions first for immediate feedback.
*/
async function main() {
// Check if activation comments are disabled
const messagesConfig = getMessages();
if (!parseBoolTemplatable(messagesConfig?.activationComments, true)) {
core.info("activation-comments is disabled: skipping activation comment creation");
Copy link
Contributor

Choose a reason for hiding this comment

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

Good guard: checking activationComments before proceeding keeps the early-exit clean. Consider logging the config value at debug level to aid troubleshooting when activation-comments is unexpectedly disabled.

return;
}
Comment on lines +65 to +70
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

Missing test coverage for the new activation-comments guard functionality. The guard at lines 65-70 skips comment creation when activationComments is false, but there are no tests in the corresponding test file verifying this behavior. Consider adding tests that verify:

  1. Comment creation is skipped when activationComments is set to false
  2. Comment creation proceeds normally when activationComments is true or undefined
  3. The parseBoolTemplatable function correctly handles string expressions like "${{ inputs.activation-comments }}"

Copilot uses AI. Check for mistakes.

const runId = context.runId;
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}`;
Expand Down
12 changes: 12 additions & 0 deletions actions/setup/js/generate_footer.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ function generateXMLMarker(workflowName, runUrl) {
const engineVersion = process.env.GH_AW_ENGINE_VERSION || "";
const engineModel = process.env.GH_AW_ENGINE_MODEL || "";
const trackerId = process.env.GH_AW_TRACKER_ID || "";
const runId = process.env.GITHUB_RUN_ID || "";
const workflowId = process.env.GH_AW_WORKFLOW_ID || "";

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

// Add numeric run ID if available
if (runId) {
parts.push(`id: ${runId}`);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding id and workflow_id to the XML marker improves traceability. This pairs nicely with any log-scraping or run-correlation tooling that parses the footer.


// Add workflow identifier if available
if (workflowId) {
parts.push(`workflow_id: ${workflowId}`);
}

// Always include run URL
parts.push(`run: ${runUrl}`);

Expand Down
37 changes: 37 additions & 0 deletions actions/setup/js/generate_footer.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe("generate_footer.cjs", () => {
delete process.env.GH_AW_ENGINE_MODEL;
delete process.env.GH_AW_TRACKER_ID;
delete process.env.GH_AW_WORKFLOW_ID;
delete process.env.GITHUB_RUN_ID;

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

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 -->");
});

it("should include run id when GITHUB_RUN_ID env var is set", async () => {
process.env.GITHUB_RUN_ID = "9876543210";

vi.resetModules();
const freshModule = await import("./generate_footer.cjs");

const result = freshModule.generateXMLMarker("Test Workflow", "https://github.com/test/repo/actions/runs/9876543210");

expect(result).toBe("<!-- gh-aw-agentic-workflow: Test Workflow, id: 9876543210, run: https://github.com/test/repo/actions/runs/9876543210 -->");
});

it("should include workflow_id when GH_AW_WORKFLOW_ID env var is set", async () => {
process.env.GH_AW_WORKFLOW_ID = "smoke-copilot";

vi.resetModules();
const freshModule = await import("./generate_footer.cjs");

const result = freshModule.generateXMLMarker("Test Workflow", "https://github.com/test/repo/actions/runs/123");

expect(result).toBe("<!-- gh-aw-agentic-workflow: Test Workflow, workflow_id: smoke-copilot, run: https://github.com/test/repo/actions/runs/123 -->");
});

it("should include all identifiers when all standard env vars are set", async () => {
process.env.GH_AW_ENGINE_ID = "copilot";
process.env.GH_AW_TRACKER_ID = "tracker-abc";
process.env.GITHUB_RUN_ID = "12345";
process.env.GH_AW_WORKFLOW_ID = "my-workflow";

vi.resetModules();
const freshModule = await import("./generate_footer.cjs");

const result = freshModule.generateXMLMarker("My Workflow", "https://github.com/test/repo/actions/runs/12345");

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 -->");
});
});

describe("generateWorkflowIdMarker", () => {
Expand Down
4 changes: 4 additions & 0 deletions actions/setup/js/messages_core.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@
* @property {string} [runSuccess] - Custom workflow success message template
* @property {string} [runFailure] - Custom workflow failure message template
* @property {string} [detectionFailure] - Custom detection job failure message template
* @property {string} [pullRequestCreated] - Custom template for pull request creation link. Placeholders: {item_number}, {item_url}
* @property {string} [issueCreated] - Custom template for issue creation link. Placeholders: {item_number}, {item_url}
* @property {string} [commitPushed] - Custom template for commit push link. Placeholders: {commit_sha}, {short_sha}, {commit_url}
* @property {string} [agentFailureIssue] - Custom footer template for agent failure tracking issues
* @property {string} [agentFailureComment] - Custom footer template for comments on agent failure tracking issues
* @property {string} [closeOlderDiscussion] - Custom message for closing older discussions as outdated
* @property {boolean} [appendOnlyComments] - If true, create new comments instead of updating the activation comment
* @property {string|boolean} [activationComments] - If false or "false", disable all activation/fallback comments entirely. Supports templatable boolean values (default: true)
*/

/**
Expand Down
12 changes: 12 additions & 0 deletions actions/setup/js/messages_footer.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ function generateXMLMarker(workflowName, runUrl) {
const engineVersion = process.env.GH_AW_ENGINE_VERSION || "";
const engineModel = process.env.GH_AW_ENGINE_MODEL || "";
const trackerId = process.env.GH_AW_TRACKER_ID || "";
const runId = process.env.GITHUB_RUN_ID || "";
const workflowId = process.env.GH_AW_WORKFLOW_ID || "";

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

// Add numeric run ID if available
if (runId) {
parts.push(`id: ${runId}`);
}

// Add workflow identifier if available
if (workflowId) {
parts.push(`workflow_id: ${workflowId}`);
}

// Always include run URL
parts.push(`run: ${runUrl}`);

Expand Down
58 changes: 58 additions & 0 deletions actions/setup/js/messages_run_status.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,67 @@ function getDetectionFailureMessage(ctx) {
return messages?.detectionFailure ? renderTemplate(messages.detectionFailure, templateContext) : renderTemplate(defaultMessage, templateContext);
}

/**
* @typedef {Object} PullRequestCreatedContext
* @property {number} itemNumber - PR number
* @property {string} itemUrl - URL of the pull request
*/

/**
* Get the pull-request-created message, using custom template if configured.
* @param {PullRequestCreatedContext} ctx - Context for message generation
* @returns {string} Pull-request-created message
*/
function getPullRequestCreatedMessage(ctx) {
const messages = getMessages();
const templateContext = toSnakeCase(ctx);
const defaultMessage = "Pull request created: [#{item_number}]({item_url})";
return messages?.pullRequestCreated ? renderTemplate(messages.pullRequestCreated, templateContext) : renderTemplate(defaultMessage, templateContext);
}

/**
* @typedef {Object} IssueCreatedContext
* @property {number} itemNumber - Issue number
* @property {string} itemUrl - URL of the issue
*/

/**
* Get the issue-created message, using custom template if configured.
* @param {IssueCreatedContext} ctx - Context for message generation
* @returns {string} Issue-created message
*/
function getIssueCreatedMessage(ctx) {
const messages = getMessages();
const templateContext = toSnakeCase(ctx);
const defaultMessage = "Issue created: [#{item_number}]({item_url})";
return messages?.issueCreated ? renderTemplate(messages.issueCreated, templateContext) : renderTemplate(defaultMessage, templateContext);
}

/**
* @typedef {Object} CommitPushedContext
* @property {string} commitSha - Full commit SHA
* @property {string} shortSha - Short (7-char) commit SHA
* @property {string} commitUrl - URL of the commit
*/

/**
* Get the commit-pushed message, using custom template if configured.
* @param {CommitPushedContext} ctx - Context for message generation
* @returns {string} Commit-pushed message
*/
function getCommitPushedMessage(ctx) {
const messages = getMessages();
const templateContext = toSnakeCase(ctx);
const defaultMessage = "Commit pushed: [`{short_sha}`]({commit_url})";
return messages?.commitPushed ? renderTemplate(messages.commitPushed, templateContext) : renderTemplate(defaultMessage, templateContext);
}

module.exports = {
getRunStartedMessage,
getRunSuccessMessage,
getRunFailureMessage,
getDetectionFailureMessage,
getPullRequestCreatedMessage,
getIssueCreatedMessage,
getCommitPushedMessage,
};
7 changes: 7 additions & 0 deletions actions/setup/js/notify_comment_error.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { getMessages } = require("./messages_core.cjs");
const { getErrorMessage, isLockedError } = require("./error_helpers.cjs");
const { sanitizeContent } = require("./sanitize_content.cjs");
const { ERR_VALIDATION } = require("./error_codes.cjs");
const { parseBoolTemplatable } = require("./templatable.cjs");

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

// If activation comments are disabled entirely, skip all comment updates
if (!parseBoolTemplatable(messagesConfig?.activationComments, true)) {
core.info("activation-comments is disabled: skipping completion comment update");
return;
}
Comment on lines +64 to +68
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

Missing test coverage for the activation-comments guard. The guard at lines 64-68 skips completion comment updates when activationComments is false, but there are no tests in notify_comment_error.test.cjs verifying this behavior. Consider adding tests that verify:

  1. Completion comment updates are skipped when activationComments is set to false
  2. Completion comment updates proceed normally when activationComments is true or undefined
  3. The early return at line 67 prevents unnecessary processing when activation comments are disabled

Copilot uses AI. Check for mistakes.

core.info(`Comment ID: ${commentId}`);
core.info(`Comment Repo: ${commentRepo}`);
core.info(`Run URL: ${runUrl}`);
Expand Down
29 changes: 27 additions & 2 deletions actions/setup/js/update_activation_comment.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@
const { getErrorMessage } = require("./error_helpers.cjs");
const { getMessages } = require("./messages_core.cjs");
const { sanitizeContent } = require("./sanitize_content.cjs");
const { getPullRequestCreatedMessage, getIssueCreatedMessage, getCommitPushedMessage } = require("./messages_run_status.cjs");
const { parseBoolTemplatable } = require("./templatable.cjs");
const { generateXMLMarker } = require("./generate_footer.cjs");

/**
* Build the workflow run URL from context and environment.
* @param {any} context - GitHub Actions context
* @returns {string} The workflow run URL
*/
function getRunUrl(context) {
const runId = context.runId || process.env.GITHUB_RUN_ID || "";
const githubServer = process.env.GITHUB_SERVER_URL || "https://github.com";
return context.payload?.repository?.html_url ? `${context.payload.repository.html_url}/actions/runs/${runId}` : `${githubServer}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`;
}

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

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

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

// Check if activation comments are disabled entirely
if (!parseBoolTemplatable(messagesConfig?.activationComments, true)) {
core.info("activation-comments is disabled: skipping activation comment update");
return;
}
Comment on lines +76 to +80
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

Missing test coverage for the activation-comments guard. The guard at lines 76-80 skips activation comment updates when activationComments is false, but the existing tests in update_activation_comment.test.cjs do not verify this behavior. Consider adding tests that verify:

  1. Comment updates are skipped when activationComments is set to false
  2. Comment updates proceed normally when activationComments is true or undefined
  3. The guard works correctly with both updateActivationComment and updateActivationCommentWithCommit functions

Copilot uses AI. Check for mistakes.

// Parse comment repo (format: "owner/repo") with validation
let repoOwner = context.repo.owner;
let repoName = context.repo.repo;
Expand Down
Loading