diff --git a/actions/setup/js/add_comment.cjs b/actions/setup/js/add_comment.cjs index 80cc9399e6..40b8e25dec 100644 --- a/actions/setup/js/add_comment.cjs +++ b/actions/setup/js/add_comment.cjs @@ -6,6 +6,7 @@ const { generateFooterWithMessages } = require("./messages_footer.cjs"); const { getRepositoryUrl } = require("./get_repository_url.cjs"); const { replaceTemporaryIdReferences, loadTemporaryIdMap } = require("./temporary_id.cjs"); const { getTrackerID } = require("./get_tracker_id.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Hide/minimize a comment using the GraphQL API @@ -314,7 +315,7 @@ async function main() { core.info(`Allowed reasons for hiding: [${parsed.join(", ")}]`); return parsed; } catch (error) { - core.warning(`Failed to parse GH_AW_ALLOWED_REASONS: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to parse GH_AW_ALLOWED_REASONS: ${getErrorMessage(error)}`); return null; } })() diff --git a/actions/setup/js/add_copilot_reviewer.cjs b/actions/setup/js/add_copilot_reviewer.cjs index b82baf2e34..b564663928 100644 --- a/actions/setup/js/add_copilot_reviewer.cjs +++ b/actions/setup/js/add_copilot_reviewer.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Add Copilot as a reviewer to a pull request. * @@ -52,7 +54,7 @@ Successfully added Copilot as a reviewer to PR #${prNumber}. ) .write(); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to add Copilot as reviewer: ${errorMessage}`); core.setFailed(`Failed to add Copilot as reviewer to PR #${prNumber}: ${errorMessage}`); } diff --git a/actions/setup/js/add_labels.cjs b/actions/setup/js/add_labels.cjs index cd76c83a5a..3b1d5a851c 100644 --- a/actions/setup/js/add_labels.cjs +++ b/actions/setup/js/add_labels.cjs @@ -3,6 +3,7 @@ const { processSafeOutput } = require("./safe_output_processor.cjs"); const { validateLabels } = require("./safe_output_validator.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Use shared processor for common steps @@ -117,7 +118,7 @@ ${labelsListMarkdown} ) .write(); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to add labels: ${errorMessage}`); core.setFailed(`Failed to add labels: ${errorMessage}`); } diff --git a/actions/setup/js/add_reaction_and_edit_comment.cjs b/actions/setup/js/add_reaction_and_edit_comment.cjs index a787b02789..d890d81858 100644 --- a/actions/setup/js/add_reaction_and_edit_comment.cjs +++ b/actions/setup/js/add_reaction_and_edit_comment.cjs @@ -2,6 +2,7 @@ /// const { getRunStartedMessage } = require("./messages_run_status.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Read inputs from environment variables @@ -151,7 +152,7 @@ async function main() { core.info(`Skipping comment for event type: ${eventName}`); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to process reaction and comment creation: ${errorMessage}`); core.setFailed(`Failed to process reaction and comment creation: ${errorMessage}`); } @@ -457,7 +458,7 @@ async function addCommentWithWorkflowLink(endpoint, runUrl, eventName) { core.setOutput("comment-repo", `${context.repo.owner}/${context.repo.repo}`); } catch (error) { // Don't fail the entire job if comment creation fails - just log it - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.warning("Failed to create comment with workflow link (This is not critical - the reaction was still added successfully): " + errorMessage); } } diff --git a/actions/setup/js/add_reviewer.cjs b/actions/setup/js/add_reviewer.cjs index 29f59a4021..63a22bfda4 100644 --- a/actions/setup/js/add_reviewer.cjs +++ b/actions/setup/js/add_reviewer.cjs @@ -2,6 +2,7 @@ /// const { processSafeOutput, processItems } = require("./safe_output_processor.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); // GitHub Copilot reviewer bot username const COPILOT_REVIEWER_BOT = "copilot-pull-request-reviewer[bot]"; @@ -125,7 +126,7 @@ ${reviewersListMarkdown} ) .write(); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to add reviewers: ${errorMessage}`); core.setFailed(`Failed to add reviewers: ${errorMessage}`); } diff --git a/actions/setup/js/assign_agent_helpers.cjs b/actions/setup/js/assign_agent_helpers.cjs index f8c39d6f47..c939a52cc2 100644 --- a/actions/setup/js/assign_agent_helpers.cjs +++ b/actions/setup/js/assign_agent_helpers.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Shared helper functions for assigning coding agents (like Copilot) to issues * These functions use GraphQL to properly assign bot actors that cannot be assigned via gh CLI @@ -27,6 +29,8 @@ function getAgentName(assignee) { // Normalize: remove @ prefix if present const normalized = assignee.startsWith("@") ? assignee.slice(1) : assignee; + const { getErrorMessage } = require("./error_helpers.cjs"); + // Check if it's a known agent if (AGENT_LOGIN_NAMES[normalized]) { return normalized; @@ -118,7 +122,7 @@ async function findAgent(owner, repo, agentName) { } return null; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to find ${agentName} agent: ${errorMessage}`); return null; } @@ -163,7 +167,7 @@ async function getIssueDetails(owner, repo, issueNumber) { currentAssignees, }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to get issue details: ${errorMessage}`); // Re-throw the error to preserve the original error message for permission error detection throw error; @@ -208,7 +212,7 @@ async function assignAgentToIssue(issueId, agentId, currentAssignees, agentName) core.error("Unexpected response from GitHub API"); return false; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); // Debug: surface the raw GraphQL error structure for troubleshooting fine-grained permission issues try { @@ -400,7 +404,7 @@ async function assignAgentToIssueByName(owner, repo, issueNumber, agentName) { core.info(`Successfully assigned ${agentName} coding agent to issue #${issueNumber}`); return { success: true }; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); return { success: false, error: errorMessage }; } } diff --git a/actions/setup/js/assign_copilot_to_created_issues.cjs b/actions/setup/js/assign_copilot_to_created_issues.cjs index e3b4ba604f..0c97669928 100644 --- a/actions/setup/js/assign_copilot_to_created_issues.cjs +++ b/actions/setup/js/assign_copilot_to_created_issues.cjs @@ -2,6 +2,7 @@ /// const { AGENT_LOGIN_NAMES, findAgent, getIssueDetails, assignAgentToIssue, generatePermissionErrorSummary } = require("./assign_agent_helpers.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Assign copilot to issues created by create_issue job. @@ -110,7 +111,7 @@ async function main() { success: true, }); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to assign ${agentName} to issue #${issueNumber} in ${repoSlug}: ${errorMessage}`); results.push({ repo: repoSlug, diff --git a/actions/setup/js/assign_issue.cjs b/actions/setup/js/assign_issue.cjs index 44b0150aae..f83e475051 100644 --- a/actions/setup/js/assign_issue.cjs +++ b/actions/setup/js/assign_issue.cjs @@ -2,6 +2,7 @@ /// const { getAgentName, getIssueDetails, findAgent, assignAgentToIssue } = require("./assign_agent_helpers.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Assign an issue to a user or bot (including copilot) @@ -95,7 +96,7 @@ Successfully assigned issue #${trimmedIssueNumber} to \`${trimmedAssignee}\`. ) .write(); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to assign issue: ${errorMessage}`); core.setFailed(`Failed to assign issue #${trimmedIssueNumber} to ${trimmedAssignee}: ${errorMessage}`); } diff --git a/actions/setup/js/assign_milestone.cjs b/actions/setup/js/assign_milestone.cjs index 12929fa9d1..0793927a28 100644 --- a/actions/setup/js/assign_milestone.cjs +++ b/actions/setup/js/assign_milestone.cjs @@ -2,6 +2,7 @@ /// const { processSafeOutput } = require("./safe_output_processor.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Use shared processor for common steps @@ -62,7 +63,7 @@ async function main() { allMilestones = milestonesResponse.data; core.info(`Fetched ${allMilestones.length} milestones from repository`); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to fetch milestones: ${errorMessage}`); core.setFailed(`Failed to fetch milestones for validation: ${errorMessage}`); return; @@ -119,7 +120,7 @@ async function main() { success: true, }); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to assign milestone #${milestoneNumber} to issue #${issueNumber}: ${errorMessage}`); results.push({ issue_number: issueNumber, diff --git a/actions/setup/js/assign_to_agent.cjs b/actions/setup/js/assign_to_agent.cjs index 1e411abc60..b29a7f5f09 100644 --- a/actions/setup/js/assign_to_agent.cjs +++ b/actions/setup/js/assign_to_agent.cjs @@ -4,6 +4,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); const { AGENT_LOGIN_NAMES, getAvailableAgentLogins, findAgent, getIssueDetails, assignAgentToIssue, generatePermissionErrorSummary } = require("./assign_agent_helpers.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { const result = loadAgentOutput(); @@ -148,7 +149,7 @@ async function main() { success: true, }); } catch (error) { - let errorMessage = error instanceof Error ? error.message : String(error); + let errorMessage = getErrorMessage(error); if (errorMessage.includes("coding agent is not available for this repository")) { // Enrich with available agent logins to aid troubleshooting - uses built-in github object try { diff --git a/actions/setup/js/assign_to_user.cjs b/actions/setup/js/assign_to_user.cjs index 66241dc016..c53f5fc4f0 100644 --- a/actions/setup/js/assign_to_user.cjs +++ b/actions/setup/js/assign_to_user.cjs @@ -2,6 +2,7 @@ /// const { processSafeOutput, processItems } = require("./safe_output_processor.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Use shared processor for common steps @@ -122,7 +123,7 @@ ${assigneesListMarkdown} ) .write(); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to assign users: ${errorMessage}`); core.setFailed(`Failed to assign users: ${errorMessage}`); } diff --git a/actions/setup/js/check_command_position.cjs b/actions/setup/js/check_command_position.cjs index d0df669e00..a7048dd659 100644 --- a/actions/setup/js/check_command_position.cjs +++ b/actions/setup/js/check_command_position.cjs @@ -8,6 +8,8 @@ async function main() { const command = process.env.GH_AW_COMMAND; + const { getErrorMessage } = require("./error_helpers.cjs"); + if (!command) { core.setFailed("Configuration error: GH_AW_COMMAND not specified."); return; @@ -62,7 +64,7 @@ async function main() { core.setOutput("command_position_ok", "false"); } } catch (error) { - core.setFailed(error instanceof Error ? error.message : String(error)); + core.setFailed(getErrorMessage(error)); } } diff --git a/actions/setup/js/check_permissions_utils.cjs b/actions/setup/js/check_permissions_utils.cjs index 754aa74691..874661d2c7 100644 --- a/actions/setup/js/check_permissions_utils.cjs +++ b/actions/setup/js/check_permissions_utils.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Shared utility for repository permission validation * Used by both check_permissions.cjs and check_membership.cjs @@ -59,14 +61,12 @@ async function checkBotStatus(actor, owner, repo) { return { isBot: true, isActive: false }; } // For other errors, we'll treat as inactive to be safe - // @ts-expect-error - Error handling with optional chaining - const errorMessage = botError?.message ?? String(botError); + const errorMessage = getErrorMessage(botError); core.warning(`Failed to check bot status: ${errorMessage}`); return { isBot: true, isActive: false, error: errorMessage }; } } catch (error) { - // @ts-expect-error - Error handling with optional chaining - const errorMessage = error?.message ?? String(error); + const errorMessage = getErrorMessage(error); core.warning(`Error checking bot status: ${errorMessage}`); return { isBot: false, isActive: false, error: errorMessage }; } @@ -105,8 +105,7 @@ async function checkRepositoryPermission(actor, owner, repo, requiredPermissions core.warning(`User permission '${permission}' does not meet requirements: ${requiredPermissions.join(", ")}`); return { authorized: false, permission }; } catch (repoError) { - // @ts-expect-error - Error handling with optional chaining - const errorMessage = repoError?.message ?? String(repoError); + const errorMessage = getErrorMessage(repoError); core.warning(`Repository permission check failed: ${errorMessage}`); return { authorized: false, error: errorMessage }; } diff --git a/actions/setup/js/check_skip_if_match.cjs b/actions/setup/js/check_skip_if_match.cjs index fa8525b928..f54b56875c 100644 --- a/actions/setup/js/check_skip_if_match.cjs +++ b/actions/setup/js/check_skip_if_match.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { const skipQuery = process.env.GH_AW_SKIP_QUERY; const workflowName = process.env.GH_AW_WORKFLOW_NAME; @@ -53,7 +55,7 @@ async function main() { core.info(`✓ Found ${totalCount} matches (below threshold of ${maxMatches}), workflow can proceed`); core.setOutput("skip_check_ok", "true"); } catch (error) { - core.setFailed(`Failed to execute search query: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to execute search query: ${getErrorMessage(error)}`); return; } } diff --git a/actions/setup/js/check_workflow_timestamp_api.cjs b/actions/setup/js/check_workflow_timestamp_api.cjs index db00c08a39..277c475fba 100644 --- a/actions/setup/js/check_workflow_timestamp_api.cjs +++ b/actions/setup/js/check_workflow_timestamp_api.cjs @@ -7,6 +7,8 @@ * with the compiled .lock.yml file and warns if recompilation is needed */ +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { const workflowFile = process.env.GH_AW_WORKFLOW_FILE; @@ -52,7 +54,7 @@ async function main() { } return null; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.info(`Could not fetch commit for ${path}: ${errorMessage}`); return null; } diff --git a/actions/setup/js/checkout_pr_branch.cjs b/actions/setup/js/checkout_pr_branch.cjs index 86f65dda23..dadf69ad85 100644 --- a/actions/setup/js/checkout_pr_branch.cjs +++ b/actions/setup/js/checkout_pr_branch.cjs @@ -6,6 +6,8 @@ * This script handles both pull_request events and comment events on PRs */ +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { const eventName = context.eventName; const pullRequest = context.payload.pull_request; @@ -38,7 +40,7 @@ async function main() { core.info(`✅ Successfully checked out PR #${prNumber}`); } } catch (error) { - core.setFailed(`Failed to checkout PR branch: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to checkout PR branch: ${getErrorMessage(error)}`); } } diff --git a/actions/setup/js/checkout_pr_branch.test.cjs b/actions/setup/js/checkout_pr_branch.test.cjs index b033fd1875..af9958e4fc 100644 --- a/actions/setup/js/checkout_pr_branch.test.cjs +++ b/actions/setup/js/checkout_pr_branch.test.cjs @@ -53,12 +53,20 @@ describe("checkout_pr_branch.cjs", () => { const scriptPath = path.join(import.meta.dirname, "checkout_pr_branch.cjs"); const scriptContent = fs.readFileSync(scriptPath, "utf8"); + // Mock require for the script + const mockRequire = module => { + if (module === "./error_helpers.cjs") { + return { getErrorMessage: error => (error instanceof Error ? error.message : String(error)) }; + } + throw new Error(`Module ${module} not mocked in test`); + }; + // Execute the script in a new context with our mocks const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor; - const wrappedScript = new AsyncFunction("core", "exec", "context", scriptContent.replace(/module\.exports = \{ main \};?\s*$/s, "await main();")); + const wrappedScript = new AsyncFunction("core", "exec", "context", "require", scriptContent.replace(/module\.exports = \{ main \};?\s*$/s, "await main();")); try { - await wrappedScript(mockCore, mockExec, mockContext); + await wrappedScript(mockCore, mockExec, mockContext, mockRequire); } catch (error) { // Errors are handled by the script itself via core.setFailed } diff --git a/actions/setup/js/close_discussion.cjs b/actions/setup/js/close_discussion.cjs index 5db69bea76..fad827c3b0 100644 --- a/actions/setup/js/close_discussion.cjs +++ b/actions/setup/js/close_discussion.cjs @@ -5,6 +5,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateFooter } = require("./generate_footer.cjs"); const { getTrackerID } = require("./get_tracker_id.cjs"); const { getRepositoryUrl } = require("./get_repository_url.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Get discussion details using GraphQL @@ -294,7 +295,7 @@ async function main() { core.setOutput("comment_url", comment.url); } } catch (error) { - core.error(`✗ Failed to close discussion #${discussionNumber}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to close discussion #${discussionNumber}: ${getErrorMessage(error)}`); throw error; } } diff --git a/actions/setup/js/close_entity_helpers.cjs b/actions/setup/js/close_entity_helpers.cjs index 08797458c5..fc3d2d5691 100644 --- a/actions/setup/js/close_entity_helpers.cjs +++ b/actions/setup/js/close_entity_helpers.cjs @@ -5,6 +5,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateFooter } = require("./generate_footer.cjs"); const { getTrackerID } = require("./get_tracker_id.cjs"); const { getRepositoryUrl } = require("./get_repository_url.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * @typedef {'issue' | 'pull_request'} EntityType @@ -324,7 +325,7 @@ async function processCloseEntityItems(config, callbacks) { core.setOutput("comment_url", comment.html_url); } } catch (error) { - core.error(`✗ Failed to close ${config.displayName} #${entityNumber}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to close ${config.displayName} #${entityNumber}: ${getErrorMessage(error)}`); throw error; } } diff --git a/actions/setup/js/close_expired_discussions.cjs b/actions/setup/js/close_expired_discussions.cjs index 0cd3db2dec..c9e4035d29 100644 --- a/actions/setup/js/close_expired_discussions.cjs +++ b/actions/setup/js/close_expired_discussions.cjs @@ -1,6 +1,8 @@ // @ts-check // +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Maximum number of discussions to update per run */ @@ -32,6 +34,8 @@ async function searchDiscussionsWithExpiration(github, owner, repo) { let hasNextPage = true; let cursor = null; + const { getErrorMessage } = require("./error_helpers.cjs"); + while (hasNextPage) { const query = ` query($owner: String!, $repo: String!, $cursor: String) { @@ -256,7 +260,7 @@ async function main() { closedCount++; core.info(`✓ Closed discussion #${discussion.number}: ${discussion.url}`); } catch (error) { - core.error(`✗ Failed to close discussion #${discussion.number}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to close discussion #${discussion.number}: ${getErrorMessage(error)}`); // Continue with other discussions even if one fails } diff --git a/actions/setup/js/close_expired_issues.cjs b/actions/setup/js/close_expired_issues.cjs index 63436f3668..d36c85ea47 100644 --- a/actions/setup/js/close_expired_issues.cjs +++ b/actions/setup/js/close_expired_issues.cjs @@ -1,6 +1,8 @@ // @ts-check // +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Maximum number of issues to update per run */ @@ -32,6 +34,8 @@ async function searchIssuesWithExpiration(github, owner, repo) { let hasNextPage = true; let cursor = null; + const { getErrorMessage } = require("./error_helpers.cjs"); + while (hasNextPage) { const query = ` query($owner: String!, $repo: String!, $cursor: String) { @@ -249,7 +253,7 @@ async function main() { closedCount++; core.info(`✓ Closed issue #${issue.number}: ${issue.url}`); } catch (error) { - core.error(`✗ Failed to close issue #${issue.number}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to close issue #${issue.number}: ${getErrorMessage(error)}`); // Continue with other issues even if one fails } diff --git a/actions/setup/js/close_older_discussions.cjs b/actions/setup/js/close_older_discussions.cjs index b5aeda6a73..c2ab8a97d4 100644 --- a/actions/setup/js/close_older_discussions.cjs +++ b/actions/setup/js/close_older_discussions.cjs @@ -2,6 +2,7 @@ /// const { getCloseOlderDiscussionMessage } = require("./messages_close_discussion.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Maximum number of older discussions to close @@ -242,7 +243,7 @@ async function closeOlderDiscussions(github, owner, repo, titlePrefix, labels, c core.info(`✓ Closed discussion #${discussion.number}: ${discussion.url}`); } catch (error) { - core.error(`✗ Failed to close discussion #${discussion.number}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to close discussion #${discussion.number}: ${getErrorMessage(error)}`); // Continue with other discussions even if one fails } diff --git a/actions/setup/js/collect_ndjson_output.cjs b/actions/setup/js/collect_ndjson_output.cjs index d53305392f..60756ca8c7 100644 --- a/actions/setup/js/collect_ndjson_output.cjs +++ b/actions/setup/js/collect_ndjson_output.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { const fs = require("fs"); const { sanitizeContent } = require("./sanitize_content.cjs"); @@ -19,7 +21,7 @@ async function main() { core.info(`Loaded validation config from ${validationConfigPath}`); } } catch (error) { - core.warning(`Failed to read validation config from ${validationConfigPath}: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to read validation config from ${validationConfigPath}: ${getErrorMessage(error)}`); } // Extract mentions configuration from validation config @@ -187,7 +189,7 @@ async function main() { core.info(`[INGESTION] Config file does not exist at: ${configPath}`); } } catch (error) { - core.warning(`Failed to read config file from ${configPath}: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to read config file from ${configPath}: ${getErrorMessage(error)}`); } core.info(`[INGESTION] Output file path: ${outputFile}`); @@ -217,7 +219,7 @@ async function main() { core.info(`[INGESTION] Expected output types after normalization: ${JSON.stringify(Object.keys(expectedOutputTypes))}`); core.info(`[INGESTION] Expected output types full config: ${JSON.stringify(expectedOutputTypes)}`); } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); + const errorMsg = getErrorMessage(error); core.info(`Warning: Could not parse safe-outputs config: ${errorMsg}`); } } @@ -292,7 +294,7 @@ async function main() { core.info(`Line ${i + 1}: Valid ${itemType} item`); parsedItems.push(item); } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); + const errorMsg = getErrorMessage(error); errors.push(`Line ${i + 1}: Invalid JSON - ${errorMsg}`); } } @@ -322,7 +324,7 @@ async function main() { core.info(`Stored validated output to: ${agentOutputFile}`); core.exportVariable("GH_AW_AGENT_OUTPUT", agentOutputFile); } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); + const errorMsg = getErrorMessage(error); core.error(`Failed to write agent output file: ${errorMsg}`); } core.setOutput("output", JSON.stringify(validatedOutput)); diff --git a/actions/setup/js/compute_text.cjs b/actions/setup/js/compute_text.cjs index d4f77620d6..19560baaf1 100644 --- a/actions/setup/js/compute_text.cjs +++ b/actions/setup/js/compute_text.cjs @@ -7,6 +7,7 @@ * @returns {string} The sanitized content */ const { sanitizeIncomingText, writeRedactedDomainsLog } = require("./sanitize_incoming_text.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { let text = ""; @@ -125,7 +126,7 @@ async function main() { const body = release.body || ""; text = `${name}\n\n${body}`; } catch (error) { - core.warning(`Failed to fetch release from URL: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to fetch release from URL: ${getErrorMessage(error)}`); } } } else if (releaseId) { @@ -140,7 +141,7 @@ async function main() { const body = release.body || ""; text = `${name}\n\n${body}`; } catch (error) { - core.warning(`Failed to fetch release by ID: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to fetch release by ID: ${getErrorMessage(error)}`); } } } diff --git a/actions/setup/js/create_agent_task.cjs b/actions/setup/js/create_agent_task.cjs index f6f50f08c8..a8641e0bcd 100644 --- a/actions/setup/js/create_agent_task.cjs +++ b/actions/setup/js/create_agent_task.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); const path = require("path"); @@ -21,7 +23,7 @@ async function main() { try { outputContent = fs.readFileSync(agentOutputFile, "utf8"); } catch (error) { - core.setFailed(`Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error reading agent output file: ${getErrorMessage(error)}`); return; } @@ -35,7 +37,7 @@ async function main() { try { validatedOutput = JSON.parse(outputContent); } catch (error) { - core.setFailed(`Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error parsing agent output JSON: ${getErrorMessage(error)}`); return; } @@ -155,7 +157,7 @@ async function main() { createdTasks.push({ number: "", url: output }); } } catch (error) { - core.error(`Task ${index + 1}: Error creating agent task: ${error instanceof Error ? error.message : String(error)}`); + core.error(`Task ${index + 1}: Error creating agent task: ${getErrorMessage(error)}`); } } diff --git a/actions/setup/js/create_code_scanning_alert.cjs b/actions/setup/js/create_code_scanning_alert.cjs index bd70bc892e..8f2cf287f6 100644 --- a/actions/setup/js/create_code_scanning_alert.cjs +++ b/actions/setup/js/create_code_scanning_alert.cjs @@ -2,6 +2,7 @@ /// const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { const result = loadAgentOutput(); @@ -230,7 +231,7 @@ async function main() { await core.summary.addRaw(summaryContent).write(); } catch (error) { - core.error(`✗ Failed to create SARIF file: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to create SARIF file: ${getErrorMessage(error)}`); throw error; } diff --git a/actions/setup/js/create_discussion.cjs b/actions/setup/js/create_discussion.cjs index 066bd6aa04..65c35ff07e 100644 --- a/actions/setup/js/create_discussion.cjs +++ b/actions/setup/js/create_discussion.cjs @@ -8,6 +8,7 @@ const { replaceTemporaryIdReferences, loadTemporaryIdMap } = require("./temporar const { parseAllowedRepos, getDefaultTargetRepo, validateRepo, parseRepoSlug } = require("./repo_helpers.cjs"); const { addExpirationComment } = require("./expiration_helpers.cjs"); const { removeDuplicateTitleFromDescription } = require("./remove_duplicate_title.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Fetch repository ID and discussion categories for a repository @@ -196,7 +197,7 @@ async function main() { repoInfoCache.set(itemRepo, repoInfo); core.info(`Fetched discussion categories for ${itemRepo}: ${JSON.stringify(repoInfo.discussionCategories.map(cat => ({ name: cat.name, id: cat.id })))}`); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); if (errorMessage.includes("Not Found") || errorMessage.includes("not found") || errorMessage.includes("Could not resolve to a Repository")) { core.warning(`Skipping discussion: Discussions are not enabled for repository '${itemRepo}'`); continue; @@ -319,7 +320,7 @@ async function main() { core.warning("close-older-discussions is enabled but no title-prefix or labels are set - skipping close older discussions"); } } catch (error) { - core.error(`✗ Failed to create discussion "${title}" in ${itemRepo}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to create discussion "${title}" in ${itemRepo}: ${getErrorMessage(error)}`); throw error; } } diff --git a/actions/setup/js/create_issue.cjs b/actions/setup/js/create_issue.cjs index eaf53c1a03..e4e0ca0aa4 100644 --- a/actions/setup/js/create_issue.cjs +++ b/actions/setup/js/create_issue.cjs @@ -10,6 +10,7 @@ const { generateTemporaryId, isTemporaryId, normalizeTemporaryId, replaceTempora const { parseAllowedRepos, getDefaultTargetRepo, validateRepo, parseRepoSlug } = require("./repo_helpers.cjs"); const { addExpirationComment } = require("./expiration_helpers.cjs"); const { removeDuplicateTitleFromDescription } = require("./remove_duplicate_title.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Initialize outputs to empty strings to ensure they're always set @@ -287,7 +288,7 @@ async function main() { core.info("✓ Successfully linked issue #" + issue.number + " as sub-issue of #" + effectiveParentIssueNumber); } catch (error) { - core.info(`Warning: Could not link sub-issue to parent: ${error instanceof Error ? error.message : String(error)}`); + core.info(`Warning: Could not link sub-issue to parent: ${getErrorMessage(error)}`); core.info(`Error details: ${error instanceof Error ? error.stack : String(error)}`); // Fallback: add a comment if sub-issue linking fails try { @@ -313,7 +314,7 @@ async function main() { core.setOutput("issue_url", issue.html_url); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); if (errorMessage.includes("Issues has been disabled in this repository")) { core.info(`⚠ Cannot create issue "${title}" in ${itemRepo}: Issues are disabled for this repository`); core.info("Consider enabling issues in repository settings if you want to create issues automatically"); diff --git a/actions/setup/js/create_pr_review_comment.cjs b/actions/setup/js/create_pr_review_comment.cjs index 65f6d6cb48..3e0abdb6d4 100644 --- a/actions/setup/js/create_pr_review_comment.cjs +++ b/actions/setup/js/create_pr_review_comment.cjs @@ -5,6 +5,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); const { generateFooter } = require("./generate_footer.cjs"); const { getRepositoryUrl } = require("./get_repository_url.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Check if we're in staged mode @@ -155,7 +156,7 @@ async function main() { pullRequest = fullPR; core.info(`Fetched full pull request details for PR #${pullRequestNumber}`); } catch (error) { - core.info(`Failed to fetch pull request details for PR #${pullRequestNumber}: ${error instanceof Error ? error.message : String(error)}`); + core.info(`Failed to fetch pull request details for PR #${pullRequestNumber}: ${getErrorMessage(error)}`); continue; } } @@ -238,7 +239,7 @@ async function main() { core.setOutput("review_comment_url", comment.html_url); } } catch (error) { - core.error(`✗ Failed to create review comment: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to create review comment: ${getErrorMessage(error)}`); throw error; } } diff --git a/actions/setup/js/create_pull_request.cjs b/actions/setup/js/create_pull_request.cjs index 7436d2ce43..01e3e4f561 100644 --- a/actions/setup/js/create_pull_request.cjs +++ b/actions/setup/js/create_pull_request.cjs @@ -9,6 +9,7 @@ const { updateActivationComment } = require("./update_activation_comment.cjs"); const { getTrackerID } = require("./get_tracker_id.cjs"); const { addExpirationComment } = require("./expiration_helpers.cjs"); const { removeDuplicateTitleFromDescription } = require("./remove_duplicate_title.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Generate a patch preview with max 500 lines and 2000 chars for issue body @@ -71,7 +72,7 @@ async function main() { try { outputContent = fs.readFileSync(agentOutputFile, "utf8"); } catch (error) { - core.setFailed(`Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error reading agent output file: ${getErrorMessage(error)}`); return; } } @@ -224,7 +225,7 @@ async function main() { try { validatedOutput = JSON.parse(outputContent); } catch (error) { - core.setFailed(`Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error parsing agent output JSON: ${getErrorMessage(error)}`); return; } diff --git a/actions/setup/js/create_pull_request.test.cjs b/actions/setup/js/create_pull_request.test.cjs index 1fae264f1b..9e81eb56b8 100644 --- a/actions/setup/js/create_pull_request.test.cjs +++ b/actions/setup/js/create_pull_request.test.cjs @@ -13,8 +13,9 @@ const createTestableFunction = scriptContent => { (scriptBody = scriptBody.replace(/const \{ getTrackerID \} = require\("\.\/get_tracker_id\.cjs"\);?\s*/g, "")), (scriptBody = scriptBody.replace(/const \{ addExpirationComment \} = require\("\.\/expiration_helpers\.cjs"\);?\s*/g, "")), (scriptBody = scriptBody.replace(/const \{ removeDuplicateTitleFromDescription \} = require\("\.\/remove_duplicate_title\.cjs"\);?\s*/g, "")), + (scriptBody = scriptBody.replace(/const \{ getErrorMessage \} = require\("\.\/error_helpers\.cjs"\);?\s*/g, "")), new Function( - `\n const { fs, crypto, github, core, context, process, console, updateActivationComment, getTrackerID, addExpirationComment, removeDuplicateTitleFromDescription } = arguments[0];\n \n ${scriptBody}\n \n return main;\n ` + `\n const { fs, crypto, github, core, context, process, console, updateActivationComment, getTrackerID, addExpirationComment, removeDuplicateTitleFromDescription, getErrorMessage } = arguments[0];\n \n ${scriptBody}\n \n return main;\n ` ) ); }; @@ -71,6 +72,7 @@ describe("create_pull_request.cjs", () => { getTrackerID: vi.fn(format => ""), addExpirationComment: vi.fn(), removeDuplicateTitleFromDescription: vi.fn((title, description) => description), + getErrorMessage: vi.fn(error => (error instanceof Error ? error.message : String(error))), })); }), afterEach(() => { diff --git a/actions/setup/js/generate_git_patch.cjs b/actions/setup/js/generate_git_patch.cjs index af9654f28f..552e59311f 100644 --- a/actions/setup/js/generate_git_patch.cjs +++ b/actions/setup/js/generate_git_patch.cjs @@ -6,6 +6,7 @@ const path = require("path"); const { execSync } = require("child_process"); const { getBaseBranch } = require("./get_base_branch.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Generates a git patch file for the current changes @@ -100,7 +101,7 @@ function generateGitPatch(branchName) { } } } catch (error) { - errorMessage = `Failed to generate patch: ${error instanceof Error ? error.message : String(error)}`; + errorMessage = `Failed to generate patch: ${getErrorMessage(error)}`; } // Check if patch was generated and has content diff --git a/actions/setup/js/hide_comment.cjs b/actions/setup/js/hide_comment.cjs index c63dbf1291..4e863422af 100644 --- a/actions/setup/js/hide_comment.cjs +++ b/actions/setup/js/hide_comment.cjs @@ -2,6 +2,7 @@ /// const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Hide a comment using the GraphQL API. @@ -40,7 +41,7 @@ async function main() { allowedReasons = JSON.parse(process.env.GH_AW_HIDE_COMMENT_ALLOWED_REASONS); core.info(`Allowed reasons for hiding: [${allowedReasons.join(", ")}]`); } catch (error) { - core.warning(`Failed to parse GH_AW_HIDE_COMMENT_ALLOWED_REASONS: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to parse GH_AW_HIDE_COMMENT_ALLOWED_REASONS: ${getErrorMessage(error)}`); } } @@ -110,7 +111,7 @@ async function main() { throw new Error(`Failed to hide comment: ${commentId}`); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to hide comment: ${errorMessage}`); core.setFailed(`Failed to hide comment: ${errorMessage}`); return; diff --git a/actions/setup/js/interpolate_prompt.cjs b/actions/setup/js/interpolate_prompt.cjs index aa34145996..6a8fa84eeb 100644 --- a/actions/setup/js/interpolate_prompt.cjs +++ b/actions/setup/js/interpolate_prompt.cjs @@ -8,6 +8,7 @@ const fs = require("fs"); const { isTruthy } = require("./is_truthy.cjs"); const { processRuntimeImports } = require("./runtime_import.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Interpolates variables in the prompt content @@ -119,7 +120,7 @@ async function main() { // Write back to the same file fs.writeFileSync(promptPath, content, "utf8"); } catch (error) { - core.setFailed(error instanceof Error ? error.message : String(error)); + core.setFailed(getErrorMessage(error)); } } diff --git a/actions/setup/js/link_sub_issue.cjs b/actions/setup/js/link_sub_issue.cjs index dc90c83955..7fadeeb367 100644 --- a/actions/setup/js/link_sub_issue.cjs +++ b/actions/setup/js/link_sub_issue.cjs @@ -4,6 +4,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); const { loadTemporaryIdMap, resolveIssueNumber } = require("./temporary_id.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { const result = loadAgentOutput(); @@ -164,7 +165,7 @@ async function main() { }); parentIssue = parentResponse.data; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.warning(`Failed to fetch parent issue #${parentIssueNumber}: ${errorMessage}`); results.push({ parent_issue_number: parentIssueNumber, @@ -212,7 +213,7 @@ async function main() { }); subIssue = subResponse.data; } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to fetch sub-issue #${subIssueNumber}: ${errorMessage}`); results.push({ parent_issue_number: parentIssueNumber, @@ -256,7 +257,7 @@ async function main() { } } catch (error) { // If the GraphQL query fails (e.g., parent field not available), log warning but continue - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.warning(`Could not check if sub-issue #${subIssueNumber} has a parent: ${errorMessage}. Proceeding with link attempt.`); } @@ -322,7 +323,7 @@ async function main() { success: true, }); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.warning(`Failed to link issue #${subIssueNumber} as sub-issue of #${parentIssueNumber}: ${errorMessage}`); results.push({ parent_issue_number: parentIssueNumber, diff --git a/actions/setup/js/load_agent_output.cjs b/actions/setup/js/load_agent_output.cjs index caaa944e6c..0863647df3 100644 --- a/actions/setup/js/load_agent_output.cjs +++ b/actions/setup/js/load_agent_output.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); /** @@ -53,7 +55,7 @@ function loadAgentOutput() { try { outputContent = fs.readFileSync(agentOutputFile, "utf8"); } catch (error) { - const errorMessage = `Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`; + const errorMessage = `Error reading agent output file: ${getErrorMessage(error)}`; core.error(errorMessage); return { success: false, error: errorMessage }; } @@ -71,7 +73,7 @@ function loadAgentOutput() { try { validatedOutput = JSON.parse(outputContent); } catch (error) { - const errorMessage = `Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`; + const errorMessage = `Error parsing agent output JSON: ${getErrorMessage(error)}`; core.error(errorMessage); core.info(`Failed to parse content:\n${truncateForLogging(outputContent)}`); return { success: false, error: errorMessage }; diff --git a/actions/setup/js/lock-issue.cjs b/actions/setup/js/lock-issue.cjs index 0e26b67b8e..dae96e3395 100644 --- a/actions/setup/js/lock-issue.cjs +++ b/actions/setup/js/lock-issue.cjs @@ -7,6 +7,8 @@ * to prevent concurrent modifications during agent workflow execution */ +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { // Log actor and event information for debugging core.info(`Lock-issue debug: actor=${context.actor}, eventName=${context.eventName}`); @@ -59,7 +61,7 @@ async function main() { // Set output to indicate the issue was locked and needs to be unlocked core.setOutput("locked", "true"); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to lock issue: ${errorMessage}`); core.setFailed(`Failed to lock issue #${issueNumber}: ${errorMessage}`); core.setOutput("locked", "false"); diff --git a/actions/setup/js/log_parser_bootstrap.cjs b/actions/setup/js/log_parser_bootstrap.cjs index 19e63632e7..e936152ca1 100644 --- a/actions/setup/js/log_parser_bootstrap.cjs +++ b/actions/setup/js/log_parser_bootstrap.cjs @@ -2,6 +2,7 @@ /// const { generatePlainTextSummary, generateCopilotCliStyleSummary, formatSafeOutputsPreview } = require("./log_parser_shared.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Bootstrap helper for log parser entry points. @@ -95,7 +96,7 @@ function runLogParser(options) { try { safeOutputsContent = fs.readFileSync(safeOutputsPath, "utf8"); } catch (error) { - core.warning(`Failed to read safe outputs file: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to read safe outputs file: ${getErrorMessage(error)}`); } } diff --git a/actions/setup/js/log_parser_shared.cjs b/actions/setup/js/log_parser_shared.cjs index 7e14ca20e7..5ce12983c7 100644 --- a/actions/setup/js/log_parser_shared.cjs +++ b/actions/setup/js/log_parser_shared.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Shared utility functions for log parsers * Used by parse_claude_log.cjs, parse_copilot_log.cjs, and parse_codex_log.cjs @@ -1495,7 +1497,7 @@ function wrapLogParser(parseFunction, parserName, logContent) { try { return parseFunction(logContent); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); return { markdown: `## Agent Log Summary\n\nError parsing ${parserName} log (tried both JSON array and JSONL formats): ${errorMessage}\n`, mcpFailures: [], diff --git a/actions/setup/js/mcp_logger.cjs b/actions/setup/js/mcp_logger.cjs index c4e764160a..aa152e01c6 100644 --- a/actions/setup/js/mcp_logger.cjs +++ b/actions/setup/js/mcp_logger.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * MCP Logger Utility * @@ -37,7 +39,7 @@ function createLogger(serverName) { * @param {Error|string|any} error - Error object or message */ debugError: (prefix, error) => { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); logger.debug(`${prefix}${errorMessage}`); if (error instanceof Error && error.stack) { logger.debug(`${prefix}Stack trace: ${error.stack}`); diff --git a/actions/setup/js/mcp_server_core.cjs b/actions/setup/js/mcp_server_core.cjs index a751085bc7..9402c0d577 100644 --- a/actions/setup/js/mcp_server_core.cjs +++ b/actions/setup/js/mcp_server_core.cjs @@ -25,6 +25,7 @@ const path = require("path"); const { ReadBuffer } = require("./read_buffer.cjs"); const { validateRequiredFields } = require("./safe_inputs_validation.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); const { generateEnhancedErrorMessage } = require("./mcp_enhanced_errors.cjs"); const encoder = new TextEncoder(); @@ -115,7 +116,7 @@ function createDebugFunction(server) { */ function createDebugErrorFunction(server) { return (prefix, error) => { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); server.debug(`${prefix}${errorMessage}`); if (error instanceof Error && error.stack) { server.debug(`${prefix}Stack trace: ${error.stack}`); @@ -704,7 +705,7 @@ async function processReadBuffer(server, defaultHandler) { } catch (error) { // For parse errors, we can't know the request id, so we shouldn't send a response // according to JSON-RPC spec. Just log the error. - server.debug(`Parse error: ${error instanceof Error ? error.message : String(error)}`); + server.debug(`Parse error: ${getErrorMessage(error)}`); } } } diff --git a/actions/setup/js/messages_core.cjs b/actions/setup/js/messages_core.cjs index ce38d3afed..fc66063ce5 100644 --- a/actions/setup/js/messages_core.cjs +++ b/actions/setup/js/messages_core.cjs @@ -43,11 +43,13 @@ function getMessages() { return null; } + const { getErrorMessage } = require("./error_helpers.cjs"); + try { // Parse JSON with camelCase keys from Go struct (using json struct tags) return JSON.parse(messagesEnv); } catch (error) { - core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_MESSAGES: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_MESSAGES: ${getErrorMessage(error)}`); return null; } } diff --git a/actions/setup/js/missing_tool.cjs b/actions/setup/js/missing_tool.cjs index a566dccfbc..68c65c5398 100644 --- a/actions/setup/js/missing_tool.cjs +++ b/actions/setup/js/missing_tool.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { const fs = require("fs"); @@ -29,7 +31,7 @@ async function main() { try { agentOutput = fs.readFileSync(agentOutputFile, "utf8"); } catch (error) { - core.info(`Agent output file not found or unreadable: ${error instanceof Error ? error.message : String(error)}`); + core.info(`Agent output file not found or unreadable: ${getErrorMessage(error)}`); core.setOutput("tools_reported", JSON.stringify(missingTools)); core.setOutput("total_count", missingTools.length.toString()); return; @@ -49,7 +51,7 @@ async function main() { try { validatedOutput = JSON.parse(agentOutput); } catch (error) { - core.setFailed(`Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error parsing agent output JSON: ${getErrorMessage(error)}`); return; } diff --git a/actions/setup/js/missing_tool.test.cjs b/actions/setup/js/missing_tool.test.cjs index 655ea17d34..86f1bfda3a 100644 --- a/actions/setup/js/missing_tool.test.cjs +++ b/actions/setup/js/missing_tool.test.cjs @@ -43,6 +43,7 @@ describe("missing_tool.cjs", () => { (global.require = vi.fn().mockImplementation(module => { if ("fs" === module) return fs; if ("@actions/core" === module) return mockCore; + if ("./error_helpers.cjs" === module) return { getErrorMessage: error => (error instanceof Error ? error.message : String(error)) }; throw new Error(`Module not found: ${module}`); }))); const scriptPath = path.join(__dirname, "missing_tool.cjs"); diff --git a/actions/setup/js/notify_comment_error.cjs b/actions/setup/js/notify_comment_error.cjs index 93a371a3d8..0659326c32 100644 --- a/actions/setup/js/notify_comment_error.cjs +++ b/actions/setup/js/notify_comment_error.cjs @@ -7,6 +7,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { getRunSuccessMessage, getRunFailureMessage, getDetectionFailureMessage } = require("./messages_run_status.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Collect generated asset URLs from safe output jobs @@ -25,7 +26,7 @@ function collectGeneratedAssets() { try { jobOutputMapping = JSON.parse(safeOutputJobsEnv); } catch (error) { - core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_JOBS: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to parse GH_AW_SAFE_OUTPUT_JOBS: ${getErrorMessage(error)}`); return assets; } @@ -203,7 +204,7 @@ async function main() { } } catch (error) { // Don't fail the workflow if we can't update the comment - core.warning(`Failed to update comment: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to update comment: ${getErrorMessage(error)}`); } } diff --git a/actions/setup/js/parse_safe_inputs_logs.cjs b/actions/setup/js/parse_safe_inputs_logs.cjs index 43bea2773c..6c9691c3a0 100644 --- a/actions/setup/js/parse_safe_inputs_logs.cjs +++ b/actions/setup/js/parse_safe_inputs_logs.cjs @@ -13,6 +13,8 @@ async function main() { const fs = require("fs"); const path = require("path"); + const { getErrorMessage } = require("./error_helpers.cjs"); + try { // Get the safe-inputs logs directory path const safeInputsLogsDir = `/tmp/gh-aw/safe-inputs/logs/`; @@ -63,7 +65,7 @@ async function main() { const summary = generateSafeInputsSummary(allLogEntries); core.summary.addRaw(summary).write(); } catch (error) { - core.setFailed(error instanceof Error ? error.message : String(error)); + core.setFailed(getErrorMessage(error)); } } diff --git a/actions/setup/js/push_repo_memory.cjs b/actions/setup/js/push_repo_memory.cjs index 23318e27fd..c9e026c48f 100644 --- a/actions/setup/js/push_repo_memory.cjs +++ b/actions/setup/js/push_repo_memory.cjs @@ -4,6 +4,7 @@ const fs = require("fs"); const path = require("path"); const { execSync } = require("child_process"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Push repo-memory changes to git branch @@ -201,7 +202,7 @@ async function main() { core.info(`Created orphan branch: ${branchName}`); } } catch (error) { - core.setFailed(`Failed to checkout branch: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to checkout branch: ${getErrorMessage(error)}`); return; } @@ -290,7 +291,7 @@ async function main() { try { scanDirectory(sourceMemoryPath); } catch (error) { - core.setFailed(`Failed to scan artifact directory: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to scan artifact directory: ${getErrorMessage(error)}`); return; } @@ -344,7 +345,7 @@ async function main() { fs.copyFileSync(file.source, destFilePath); core.info(`Copied: ${file.relativePath} (${file.size} bytes)`); } catch (error) { - core.setFailed(`Failed to copy file ${file.relativePath}: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to copy file ${file.relativePath}: ${getErrorMessage(error)}`); return; } } @@ -355,7 +356,7 @@ async function main() { const status = execSync("git status --porcelain", { encoding: "utf8" }); hasChanges = status.trim().length > 0; } catch (error) { - core.setFailed(`Failed to check git status: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to check git status: ${getErrorMessage(error)}`); return; } @@ -370,7 +371,7 @@ async function main() { try { execSync("git add .", { stdio: "inherit" }); } catch (error) { - core.setFailed(`Failed to stage changes: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to stage changes: ${getErrorMessage(error)}`); return; } @@ -378,7 +379,7 @@ async function main() { try { execSync(`git commit -m "Update repo memory from workflow run ${githubRunId}"`, { stdio: "inherit" }); } catch (error) { - core.setFailed(`Failed to commit changes: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to commit changes: ${getErrorMessage(error)}`); return; } @@ -389,7 +390,7 @@ async function main() { execSync(`git pull --no-rebase -X ours "${repoUrl}" "${branchName}"`, { stdio: "inherit" }); } catch (error) { // Pull might fail if branch doesn't exist yet or on conflicts - this is acceptable - core.warning(`Pull failed (this may be expected): ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Pull failed (this may be expected): ${getErrorMessage(error)}`); } // Push changes @@ -399,7 +400,7 @@ async function main() { execSync(`git push "${repoUrl}" HEAD:"${branchName}"`, { stdio: "inherit" }); core.info(`Successfully pushed changes to ${branchName} branch`); } catch (error) { - core.setFailed(`Failed to push changes: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to push changes: ${getErrorMessage(error)}`); return; } } diff --git a/actions/setup/js/push_to_pull_request_branch.cjs b/actions/setup/js/push_to_pull_request_branch.cjs index 25ffcc183a..16f50bb11e 100644 --- a/actions/setup/js/push_to_pull_request_branch.cjs +++ b/actions/setup/js/push_to_pull_request_branch.cjs @@ -5,6 +5,7 @@ const fs = require("fs"); const { generateStagedPreview } = require("./staged_preview.cjs"); const { updateActivationCommentWithCommit } = require("./update_activation_comment.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Check if we're in staged mode @@ -22,7 +23,7 @@ async function main() { try { outputContent = fs.readFileSync(agentOutputFile, "utf8"); } catch (error) { - core.setFailed(`Error reading agent output file: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error reading agent output file: ${getErrorMessage(error)}`); return; } @@ -120,7 +121,7 @@ async function main() { try { validatedOutput = JSON.parse(outputContent); } catch (error) { - core.setFailed(`Error parsing agent output JSON: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Error parsing agent output JSON: ${getErrorMessage(error)}`); return; } @@ -217,7 +218,7 @@ async function main() { prTitle = pullRequest.title || ""; prLabels = pullRequest.labels.map(label => label.name); } catch (error) { - core.info(`Warning: Could not fetch PR ${pullNumber} details: ${error instanceof Error ? error.message : String(error)}`); + core.info(`Warning: Could not fetch PR ${pullNumber} details: ${getErrorMessage(error)}`); // Exit with failure if we cannot determine the branch name core.setFailed(`Failed to determine branch name for PR ${pullNumber}`); return; @@ -324,7 +325,7 @@ async function main() { await exec.exec(`git push origin ${branchName}`); core.info(`Changes committed and pushed to branch: ${branchName}`); } catch (error) { - core.error(`Failed to apply patch: ${error instanceof Error ? error.message : String(error)}`); + core.error(`Failed to apply patch: ${getErrorMessage(error)}`); // Investigate why the patch failed by logging git status and the failed patch try { diff --git a/actions/setup/js/read_buffer.cjs b/actions/setup/js/read_buffer.cjs index 4221c67735..1c70a28582 100644 --- a/actions/setup/js/read_buffer.cjs +++ b/actions/setup/js/read_buffer.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * ReadBuffer Module * @@ -57,7 +59,7 @@ class ReadBuffer { try { return JSON.parse(line); } catch (error) { - throw new Error(`Parse error: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Parse error: ${getErrorMessage(error)}`); } } } diff --git a/actions/setup/js/redact_secrets.cjs b/actions/setup/js/redact_secrets.cjs index 103c3fb134..a42c8c31cc 100644 --- a/actions/setup/js/redact_secrets.cjs +++ b/actions/setup/js/redact_secrets.cjs @@ -35,7 +35,7 @@ function findFiles(dir, extensions) { } } } catch (error) { - core.warning(`Failed to scan directory ${dir}: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to scan directory ${dir}: ${getErrorMessage(error)}`); } return results; } @@ -90,7 +90,7 @@ function processFile(filePath, secretValues) { } return redactionCount; } catch (error) { - core.warning(`Failed to process file ${filePath}: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to process file ${filePath}: ${getErrorMessage(error)}`); return 0; } } @@ -145,8 +145,10 @@ async function main() { core.info("Secret redaction complete: no secrets found"); } } catch (error) { - core.setFailed(`Secret redaction failed: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Secret redaction failed: ${getErrorMessage(error)}`); } } +const { getErrorMessage } = require("./error_helpers.cjs"); + module.exports = { main }; diff --git a/actions/setup/js/render_template.cjs b/actions/setup/js/render_template.cjs index 8cb35a9e21..b7a80c94dd 100644 --- a/actions/setup/js/render_template.cjs +++ b/actions/setup/js/render_template.cjs @@ -5,6 +5,8 @@ // Single-function Markdown → Markdown postprocessor for GitHub Actions. // Processes only {{#if }} ... {{/if}} blocks after ${{ }} evaluation. +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); /** @@ -79,7 +81,7 @@ function main() { core.info("Template rendered successfully"); // core.summary.addHeading("Template Rendering", 3).addRaw("\n").addRaw("Processed conditional blocks in prompt\n").write(); } catch (error) { - core.setFailed(error instanceof Error ? error.message : String(error)); + core.setFailed(getErrorMessage(error)); } } diff --git a/actions/setup/js/resolve_mentions.cjs b/actions/setup/js/resolve_mentions.cjs index caad8107f7..aabf44702b 100644 --- a/actions/setup/js/resolve_mentions.cjs +++ b/actions/setup/js/resolve_mentions.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * @typedef {Object} MentionResolutionResult * @property {string[]} allowedMentions - List of allowed mention usernames @@ -19,6 +21,8 @@ function extractMentions(text) { return []; } + const { getErrorMessage } = require("./error_helpers.cjs"); + const mentionRegex = /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g; const mentions = []; const seen = new Set(); @@ -73,7 +77,7 @@ async function getRecentCollaborators(owner, repo, github, core) { return allowedMap; } catch (error) { - core.warning(`Failed to fetch recent collaborators: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to fetch recent collaborators: ${getErrorMessage(error)}`); return new Map(); } } diff --git a/actions/setup/js/resolve_mentions_from_payload.cjs b/actions/setup/js/resolve_mentions_from_payload.cjs index b8f7086e7f..63b4a13a81 100644 --- a/actions/setup/js/resolve_mentions_from_payload.cjs +++ b/actions/setup/js/resolve_mentions_from_payload.cjs @@ -6,6 +6,7 @@ */ const { resolveMentionsLazily, isPayloadUserBot } = require("./resolve_mentions.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Resolve allowed mentions from the current GitHub event context @@ -187,7 +188,7 @@ async function resolveAllowedMentionsFromPayload(context, github, core, mentions return allowedMentions; } catch (error) { - core.warning(`Failed to resolve mentions for output collector: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to resolve mentions for output collector: ${getErrorMessage(error)}`); // Return empty array on error return []; } diff --git a/actions/setup/js/runtime_import.cjs b/actions/setup/js/runtime_import.cjs index 690aee138c..453b72d057 100644 --- a/actions/setup/js/runtime_import.cjs +++ b/actions/setup/js/runtime_import.cjs @@ -5,6 +5,8 @@ // Processes {{#runtime-import filepath}} and {{#runtime-import? filepath}} macros // at runtime to import markdown file contents dynamically. +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); const path = require("path"); @@ -138,7 +140,7 @@ function processRuntimeImports(content, workspaceDir) { // Replace the macro with the imported content processedContent = processedContent.replace(fullMatch, importedContent); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); throw new Error(`Failed to process runtime import for ${filepath}: ${errorMessage}`); } } diff --git a/actions/setup/js/safe_inputs_mcp_server.cjs b/actions/setup/js/safe_inputs_mcp_server.cjs index 208fda0847..a6c9f97801 100644 --- a/actions/setup/js/safe_inputs_mcp_server.cjs +++ b/actions/setup/js/safe_inputs_mcp_server.cjs @@ -22,6 +22,7 @@ const { createServer, registerTool, start } = require("./mcp_server_core.cjs"); const { loadConfig } = require("./safe_inputs_config_loader.cjs"); const { createToolConfig } = require("./safe_inputs_tool_factory.cjs"); const { bootstrapSafeInputsServer, cleanupConfigFile } = require("./safe_inputs_bootstrap.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * @typedef {Object} SafeInputsToolConfig @@ -100,7 +101,7 @@ if (require.main === module) { try { startSafeInputsServer(configPath, options); } catch (error) { - console.error(`Error starting safe-inputs server: ${error instanceof Error ? error.message : String(error)}`); + console.error(`Error starting safe-inputs server: ${getErrorMessage(error)}`); process.exit(1); } } diff --git a/actions/setup/js/safe_inputs_mcp_server_http.cjs b/actions/setup/js/safe_inputs_mcp_server_http.cjs index 50a6b0fb78..36aacbe35e 100644 --- a/actions/setup/js/safe_inputs_mcp_server_http.cjs +++ b/actions/setup/js/safe_inputs_mcp_server_http.cjs @@ -25,6 +25,7 @@ const { validateRequiredFields } = require("./safe_inputs_validation.cjs"); const { generateEnhancedErrorMessage } = require("./mcp_enhanced_errors.cjs"); const { createLogger } = require("./mcp_logger.cjs"); const { bootstrapSafeInputsServer, cleanupConfigFile } = require("./safe_inputs_bootstrap.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Create and configure the MCP server with tools @@ -341,7 +342,7 @@ if (require.main === module) { } startHttpServer(configPath, options).catch(error => { - console.error(`Error starting HTTP server: ${error instanceof Error ? error.message : String(error)}`); + console.error(`Error starting HTTP server: ${getErrorMessage(error)}`); process.exit(1); }); } diff --git a/actions/setup/js/safe_output_type_validator.cjs b/actions/setup/js/safe_output_type_validator.cjs index 71d8ac7b96..550b4c8939 100644 --- a/actions/setup/js/safe_output_type_validator.cjs +++ b/actions/setup/js/safe_output_type_validator.cjs @@ -11,6 +11,7 @@ const { sanitizeContent } = require("./sanitize_content.cjs"); const { isTemporaryId } = require("./temporary_id.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Default max body length for GitHub content @@ -73,7 +74,7 @@ function loadValidationConfig() { cachedValidationConfig = parsed || {}; return cachedValidationConfig; } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); + const errorMsg = getErrorMessage(error); // Log as error since missing validation config is critical if (typeof core !== "undefined") { core.error(`CRITICAL: Failed to parse validation config: ${errorMsg}. Validation will be skipped.`); diff --git a/actions/setup/js/safe_output_validator.cjs b/actions/setup/js/safe_output_validator.cjs index 9ada5a5334..b279b5b0b2 100644 --- a/actions/setup/js/safe_output_validator.cjs +++ b/actions/setup/js/safe_output_validator.cjs @@ -3,6 +3,7 @@ const fs = require("fs"); const { sanitizeLabelContent } = require("./sanitize_label_content.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Load and parse the safe outputs configuration from config.json @@ -18,7 +19,7 @@ function loadSafeOutputsConfig() { const configContent = fs.readFileSync(configPath, "utf8"); return JSON.parse(configContent); } catch (error) { - core.warning(`Failed to load config: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to load config: ${getErrorMessage(error)}`); return {}; } } diff --git a/actions/setup/js/safe_outputs_append.cjs b/actions/setup/js/safe_outputs_append.cjs index 7561ac2995..88bb82dcd9 100644 --- a/actions/setup/js/safe_outputs_append.cjs +++ b/actions/setup/js/safe_outputs_append.cjs @@ -1,5 +1,7 @@ // @ts-check +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); /** @@ -27,7 +29,7 @@ function createAppendFunction(outputFile) { try { fs.appendFileSync(outputFile, jsonLine); } catch (error) { - throw new Error(`Failed to write to output file: ${error instanceof Error ? error.message : String(error)}`); + throw new Error(`Failed to write to output file: ${getErrorMessage(error)}`); } }; } diff --git a/actions/setup/js/safe_outputs_config.cjs b/actions/setup/js/safe_outputs_config.cjs index debc341042..4f00984713 100644 --- a/actions/setup/js/safe_outputs_config.cjs +++ b/actions/setup/js/safe_outputs_config.cjs @@ -1,5 +1,7 @@ // @ts-check +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); const path = require("path"); @@ -30,7 +32,7 @@ function loadConfig(server) { safeOutputsConfigRaw = {}; } } catch (error) { - server.debug(`Error reading config file: ${error instanceof Error ? error.message : String(error)}`); + server.debug(`Error reading config file: ${getErrorMessage(error)}`); server.debug(`Falling back to empty configuration`); safeOutputsConfigRaw = {}; } diff --git a/actions/setup/js/safe_outputs_mcp_server.cjs b/actions/setup/js/safe_outputs_mcp_server.cjs index 0abd29c73a..d2900d2722 100644 --- a/actions/setup/js/safe_outputs_mcp_server.cjs +++ b/actions/setup/js/safe_outputs_mcp_server.cjs @@ -17,6 +17,7 @@ const { createAppendFunction } = require("./safe_outputs_append.cjs"); const { createHandlers } = require("./safe_outputs_handlers.cjs"); const { attachHandlers, registerPredefinedTools, registerDynamicTools } = require("./safe_outputs_tools_loader.cjs"); const { bootstrapSafeOutputsServer, cleanupConfigFile } = require("./safe_outputs_bootstrap.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Start the safe-outputs MCP server @@ -70,7 +71,7 @@ if (require.main === module) { try { startSafeOutputsServer(); } catch (error) { - console.error(`Error starting safe-outputs server: ${error instanceof Error ? error.message : String(error)}`); + console.error(`Error starting safe-outputs server: ${getErrorMessage(error)}`); process.exit(1); } } diff --git a/actions/setup/js/safe_outputs_tools_loader.cjs b/actions/setup/js/safe_outputs_tools_loader.cjs index 2e9edb80f2..aa12a1a5e4 100644 --- a/actions/setup/js/safe_outputs_tools_loader.cjs +++ b/actions/setup/js/safe_outputs_tools_loader.cjs @@ -1,5 +1,7 @@ // @ts-check +const { getErrorMessage } = require("./error_helpers.cjs"); + const fs = require("fs"); /** @@ -27,7 +29,7 @@ function loadTools(server) { server.debug(`Successfully parsed ${tools.length} tools from file`); return tools; } catch (error) { - server.debug(`Error reading tools file: ${error instanceof Error ? error.message : String(error)}`); + server.debug(`Error reading tools file: ${getErrorMessage(error)}`); server.debug(`Falling back to empty tools array`); return []; } diff --git a/actions/setup/js/substitute_placeholders.cjs b/actions/setup/js/substitute_placeholders.cjs index 751b3a01ee..32c854ef70 100644 --- a/actions/setup/js/substitute_placeholders.cjs +++ b/actions/setup/js/substitute_placeholders.cjs @@ -1,24 +1,26 @@ -const fs = require("fs"), - substitutePlaceholders = async ({ file, substitutions }) => { - if (!file) throw new Error("file parameter is required"); - if (!substitutions || "object" != typeof substitutions) throw new Error("substitutions parameter must be an object"); - let content; - try { - content = fs.readFileSync(file, "utf8"); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - throw new Error(`Failed to read file ${file}: ${errorMessage}`); - } - for (const [key, value] of Object.entries(substitutions)) { - const placeholder = `__${key}__`; - content = content.split(placeholder).join(value); - } - try { - fs.writeFileSync(file, content, "utf8"); - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - throw new Error(`Failed to write file ${file}: ${errorMessage}`); - } - return `Successfully substituted ${Object.keys(substitutions).length} placeholder(s) in ${file}`; - }; +const fs = require("fs"); +const { getErrorMessage } = require("./error_helpers.cjs"); + +const substitutePlaceholders = async ({ file, substitutions }) => { + if (!file) throw new Error("file parameter is required"); + if (!substitutions || "object" != typeof substitutions) throw new Error("substitutions parameter must be an object"); + let content; + try { + content = fs.readFileSync(file, "utf8"); + } catch (error) { + const errorMessage = getErrorMessage(error); + throw new Error(`Failed to read file ${file}: ${errorMessage}`); + } + for (const [key, value] of Object.entries(substitutions)) { + const placeholder = `__${key}__`; + content = content.split(placeholder).join(value); + } + try { + fs.writeFileSync(file, content, "utf8"); + } catch (error) { + const errorMessage = getErrorMessage(error); + throw new Error(`Failed to write file ${file}: ${errorMessage}`); + } + return `Successfully substituted ${Object.keys(substitutions).length} placeholder(s) in ${file}`; +}; module.exports = substitutePlaceholders; diff --git a/actions/setup/js/temporary_id.cjs b/actions/setup/js/temporary_id.cjs index 8e1e26fab1..9661d3f7ce 100644 --- a/actions/setup/js/temporary_id.cjs +++ b/actions/setup/js/temporary_id.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + const crypto = require("crypto"); /** @@ -116,7 +118,7 @@ function loadTemporaryIdMap() { return result; } catch (error) { if (typeof core !== "undefined") { - core.warning(`Failed to parse temporary ID map: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to parse temporary ID map: ${getErrorMessage(error)}`); } return new Map(); } diff --git a/actions/setup/js/unlock-issue.cjs b/actions/setup/js/unlock-issue.cjs index bfc2ee8cba..44924b48ea 100644 --- a/actions/setup/js/unlock-issue.cjs +++ b/actions/setup/js/unlock-issue.cjs @@ -7,6 +7,8 @@ * after agent workflow execution completes or fails */ +const { getErrorMessage } = require("./error_helpers.cjs"); + async function main() { // Log actor and event information for debugging core.info(`Unlock-issue debug: actor=${context.actor}, eventName=${context.eventName}`); @@ -55,7 +57,7 @@ async function main() { core.info(`✅ Successfully unlocked issue #${issueNumber}`); } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); core.error(`Failed to unlock issue: ${errorMessage}`); core.setFailed(`Failed to unlock issue #${issueNumber}: ${errorMessage}`); } diff --git a/actions/setup/js/update_activation_comment.cjs b/actions/setup/js/update_activation_comment.cjs index b563bfb1c1..c73694e6a2 100644 --- a/actions/setup/js/update_activation_comment.cjs +++ b/actions/setup/js/update_activation_comment.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + /** * Update the activation comment with a link to the created pull request or issue * @param {any} github - GitHub REST API instance @@ -145,7 +147,7 @@ async function updateActivationCommentWithMessage(github, context, core, message } } catch (error) { // Don't fail the workflow if we can't update the comment - just log a warning - core.warning(`Failed to update activation comment: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to update activation comment: ${getErrorMessage(error)}`); } } diff --git a/actions/setup/js/update_activation_comment.test.cjs b/actions/setup/js/update_activation_comment.test.cjs index 0f5aa508d8..5eda58e8e3 100644 --- a/actions/setup/js/update_activation_comment.test.cjs +++ b/actions/setup/js/update_activation_comment.test.cjs @@ -5,7 +5,14 @@ const createTestableFunction = scriptContent => { const beforeMainCall = scriptContent.match(/^([\s\S]*?)\s*module\.exports\s*=\s*{[\s\S]*?};?\s*$/); if (!beforeMainCall) throw new Error("Could not extract script content before module.exports"); let scriptBody = beforeMainCall[1]; - return new Function(`\n const { github, core, context, process } = arguments[0];\n \n ${scriptBody}\n \n return { updateActivationComment };\n `); + // Mock the error_helpers module + const mockRequire = module => { + if (module === "./error_helpers.cjs") { + return { getErrorMessage: error => (error instanceof Error ? error.message : String(error)) }; + } + throw new Error(`Module ${module} not mocked in test`); + }; + return new Function(`\n const { github, core, context, process } = arguments[0];\n const require = ${mockRequire.toString()};\n \n ${scriptBody}\n \n return { updateActivationComment };\n `); }; describe("update_activation_comment.cjs", () => { let createFunctionFromScript, mockDependencies; diff --git a/actions/setup/js/update_release.cjs b/actions/setup/js/update_release.cjs index fbf939107d..a8a7623873 100644 --- a/actions/setup/js/update_release.cjs +++ b/actions/setup/js/update_release.cjs @@ -3,6 +3,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); async function main() { // Check if we're in staged mode @@ -142,7 +143,7 @@ async function main() { core.setOutput("release_tag", updatedRelease.tag_name); } } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); + const errorMessage = getErrorMessage(error); const tagInfo = updateItem.tag || "inferred from context"; core.error(`Failed to update release with tag ${tagInfo}: ${errorMessage}`); diff --git a/actions/setup/js/update_runner.cjs b/actions/setup/js/update_runner.cjs index d283e5a979..109f20d53f 100644 --- a/actions/setup/js/update_runner.cjs +++ b/actions/setup/js/update_runner.cjs @@ -15,6 +15,7 @@ const { loadAgentOutput } = require("./load_agent_output.cjs"); const { generateStagedPreview } = require("./staged_preview.cjs"); const { removeDuplicateTitleFromDescription } = require("./remove_duplicate_title.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * @typedef {Object} UpdateRunnerConfig @@ -276,7 +277,7 @@ async function runUpdateWorkflow(config) { core.setOutput(outputUrlKey, updatedItem.html_url); } } catch (error) { - core.error(`✗ Failed to update ${displayName} #${targetNumber}: ${error instanceof Error ? error.message : String(error)}`); + core.error(`✗ Failed to update ${displayName} #${targetNumber}: ${getErrorMessage(error)}`); throw error; } } diff --git a/actions/setup/js/upload_assets.cjs b/actions/setup/js/upload_assets.cjs index 9eb258b935..cf9fc82a70 100644 --- a/actions/setup/js/upload_assets.cjs +++ b/actions/setup/js/upload_assets.cjs @@ -5,6 +5,7 @@ const fs = require("fs"); const path = require("path"); const crypto = require("crypto"); const { loadAgentOutput } = require("./load_agent_output.cjs"); +const { getErrorMessage } = require("./error_helpers.cjs"); /** * Normalizes a branch name to be a valid git branch name. @@ -158,7 +159,7 @@ async function main() { core.info(`Added asset: ${targetFileName} (${size} bytes)`); } catch (error) { - core.warning(`Failed to process asset ${asset.fileName}: ${error instanceof Error ? error.message : String(error)}`); + core.warning(`Failed to process asset ${asset.fileName}: ${getErrorMessage(error)}`); } } @@ -184,7 +185,7 @@ async function main() { core.info("No new assets to upload"); } } catch (error) { - core.setFailed(`Failed to upload assets: ${error instanceof Error ? error.message : String(error)}`); + core.setFailed(`Failed to upload assets: ${getErrorMessage(error)}`); return; } diff --git a/actions/setup/js/validate_errors.cjs b/actions/setup/js/validate_errors.cjs index 3d2e6fba01..d8abf90fe3 100644 --- a/actions/setup/js/validate_errors.cjs +++ b/actions/setup/js/validate_errors.cjs @@ -1,6 +1,8 @@ // @ts-check /// +const { getErrorMessage } = require("./error_helpers.cjs"); + function main() { const fs = require("fs"); const path = require("path"); @@ -82,7 +84,7 @@ function main() { } } catch (error) { console.debug(error); - core.error(`Error validating log: ${error instanceof Error ? error.message : String(error)}`); + core.error(`Error validating log: ${getErrorMessage(error)}`); } } diff --git a/actions/setup/setup.sh b/actions/setup/setup.sh index 0718605ca2..4edd7104bb 100755 --- a/actions/setup/setup.sh +++ b/actions/setup/setup.sh @@ -111,6 +111,8 @@ SAFE_INPUTS_FILES=( "read_buffer.cjs" "generate_safe_inputs_config.cjs" "setup_globals.cjs" + "error_helpers.cjs" + "mcp_enhanced_errors.cjs" ) SAFE_INPUTS_COUNT=0 @@ -158,6 +160,8 @@ SAFE_OUTPUTS_FILES=( "write_large_content_to_file.cjs" "generate_compact_schema.cjs" "setup_globals.cjs" + "error_helpers.cjs" + "mcp_enhanced_errors.cjs" ) SAFE_OUTPUTS_COUNT=0