From 38f6eab9d26f2d50d85287fe584f41ed7c81b0d4 Mon Sep 17 00:00:00 2001 From: Jeffrey Sica Date: Wed, 7 Oct 2026 19:54:38 -0500 Subject: [PATCH] fix: print the payload, not [object Object], when a command has no issue or pull number 18 handlers interpolated `${context.payload}` into their "github context payload missing issue/pull/pr number" error, which renders as `[object Object]`. They now use `JSON.stringify(context.payload)`, the convention the other 9 "missing ..." errors already follow. The message prefix and the thrown error types are unchanged. A parametrized test drives each of the 18 sites with a payload lacking the number and asserts the exact message and the absence of `[object Object]`. Signed-off-by: Jeffrey Sica --- __tests__/missingNumberMessage.test.ts | 88 ++++++++++++++++++++++++++ dist/index.js | 36 +++++------ src/issueComment/approve.ts | 2 +- src/issueComment/assign.ts | 2 +- src/issueComment/cc.ts | 2 +- src/issueComment/close.ts | 2 +- src/issueComment/lock.ts | 2 +- src/issueComment/meow.ts | 2 +- src/issueComment/milestone.ts | 2 +- src/issueComment/reopen.ts | 2 +- src/issueComment/retitle.ts | 2 +- src/issueComment/trigger.ts | 2 +- src/issueComment/unassign.ts | 2 +- src/issueComment/uncc.ts | 2 +- src/labels/fixed.ts | 2 +- src/labels/hold.ts | 2 +- src/labels/lgtm.ts | 2 +- src/labels/prefixed.ts | 2 +- src/labels/remove.ts | 2 +- src/pullReq/onPrLgtm.ts | 2 +- 20 files changed, 124 insertions(+), 36 deletions(-) create mode 100644 __tests__/missingNumberMessage.test.ts diff --git a/__tests__/missingNumberMessage.test.ts b/__tests__/missingNumberMessage.test.ts new file mode 100644 index 00000000..7bd88209 --- /dev/null +++ b/__tests__/missingNumberMessage.test.ts @@ -0,0 +1,88 @@ +import type { Context } from '@actions/github/lib/context' +import { setupServer } from 'msw/node' +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest' + +import { approve } from '../src/issueComment/approve' +import { assign } from '../src/issueComment/assign' +import { cc } from '../src/issueComment/cc' +import { close } from '../src/issueComment/close' +import { lock } from '../src/issueComment/lock' +import { meow } from '../src/issueComment/meow' +import { milestone } from '../src/issueComment/milestone' +import { reopen } from '../src/issueComment/reopen' +import { retitle } from '../src/issueComment/retitle' +import { retest } from '../src/issueComment/trigger' +import { unassign } from '../src/issueComment/unassign' +import { uncc } from '../src/issueComment/uncc' +import { addFixedLabels, fixedLabelCommands } from '../src/labels/fixed' +import { hold } from '../src/labels/hold' +import { lgtm } from '../src/labels/lgtm' +import { addPrefixedLabels, prefixedLabelCommands } from '../src/labels/prefixed' +import { remove } from '../src/labels/remove' +import { onPrLgtm } from '../src/pullReq/onPrLgtm' + +import issueCommentEvent from './fixtures/issues/issueCommentEvent.json' +import pullReqOpenedEvent from './fixtures/pullReq/pullReqOpenedEvent.json' +import * as utils from './testUtils' + +// every request is unexpected: the handlers must refuse before calling the API +const server = setupServer() +beforeAll(() => + server.listen(utils.failOnUnhandledRequest), +) +beforeEach(() => { + utils.setupActionsEnv() +}) +afterEach(() => server.resetHandlers()) +afterAll(() => server.close()) + +const helpCommand = fixedLabelCommands.find(cmd => cmd.command === '/help')! +const lifecycleCommand = prefixedLabelCommands.find(cmd => cmd.command === '/lifecycle')! + +function commentWithoutIssue(body: string): Context { + const payload = structuredClone(issueCommentEvent) as Record + payload.comment.body = body + delete payload.issue + return new utils.MockContext(payload) +} + +function pullRequestEventWithoutPullRequest(): Context { + const payload = structuredClone(pullReqOpenedEvent) as Record + delete payload.pull_request + return new utils.MockContext(payload) +} + +describe('missing issue or pull number errors', () => { + it.each<[string, string, () => Context, (context: Context) => Promise]>([ + ['issueComment/approve', 'issue', () => commentWithoutIssue('/approve'), approve], + ['issueComment/assign', 'issue', () => commentWithoutIssue('/assign'), assign], + ['issueComment/cc', 'pull', () => commentWithoutIssue('/cc'), cc], + ['issueComment/close', 'issue', () => commentWithoutIssue('/close'), close], + ['issueComment/lock', 'issue', () => commentWithoutIssue('/lock'), lock], + ['issueComment/meow', 'issue', () => commentWithoutIssue('/meow'), meow], + ['issueComment/milestone', 'issue', () => commentWithoutIssue('/milestone v1.0'), milestone], + ['issueComment/reopen', 'issue', () => commentWithoutIssue('/reopen'), reopen], + ['issueComment/retitle', 'issue', () => commentWithoutIssue('/retitle a new title'), retitle], + ['issueComment/trigger', 'issue', () => commentWithoutIssue('/retest'), retest], + ['issueComment/unassign', 'issue', () => commentWithoutIssue('/unassign'), unassign], + ['issueComment/uncc', 'pull', () => commentWithoutIssue('/uncc'), uncc], + ['labels/fixed', 'issue', () => commentWithoutIssue('/help'), context => addFixedLabels(context, helpCommand)], + ['labels/hold', 'issue', () => commentWithoutIssue('/hold'), hold], + ['labels/lgtm', 'issue', () => commentWithoutIssue('/lgtm'), lgtm], + ['labels/prefixed', 'issue', () => commentWithoutIssue('/lifecycle stale'), context => addPrefixedLabels(context, lifecycleCommand)], + ['labels/remove', 'issue', () => commentWithoutIssue('/remove bug'), remove], + ['pullReq/onPrLgtm', 'pr', pullRequestEventWithoutPullRequest, onPrLgtm], + ])('%s prints the payload, not [object Object]', async (_name, kind, contextFor, handler) => { + const context = contextFor() + + const error: Error = await handler(context).then( + () => { throw new Error('expected the handler to reject') }, + (e: Error) => e, + ) + + expect(error.message).toBe( + `github context payload missing ${kind} number: ${JSON.stringify(context.payload)}`, + ) + expect(error.message).not.toContain('[object Object]') + }) +}) diff --git a/dist/index.js b/dist/index.js index 574f8c00..db216f4c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -42855,7 +42855,7 @@ async function prepare(context, command, options = {}) { const issueNumber = context.payload.issue?.number; const commenter = context.payload.comment?.user?.login; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } if (context.payload.issue?.pull_request === undefined) { await createComment(octokit, context, issueNumber, `\`${command}\` only applies to pull requests.`); @@ -43074,7 +43074,7 @@ function commenter(context) { function requireIssueNumber(context) { const issueNumber = context.payload.issue?.number; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } return issueNumber; } @@ -43180,7 +43180,7 @@ async function hold(context = github_context) { const issueNumber = context.payload.issue?.number; const commentBody = context.payload.comment?.body; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const config = await loadProwConfig(octokit, context); const holdLabel = resolveHoldLabel(config.hold); @@ -45509,7 +45509,7 @@ async function assertLabelsPolicy(octokit, context, command) { function fixed_requireIssueNumber(context) { const issueNumber = context.payload.issue?.number; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } return issueNumber; } @@ -45546,7 +45546,7 @@ async function lgtm(context = github_context) { const isAuthor = commenterId === context.payload.issue?.user?.login; const isPullRequest = context.payload.issue?.pull_request !== undefined; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const cancel = hasCommand('/remove-lgtm', commentBody) || (hasCommand('/lgtm', commentBody) && hasKeyword(getCommandArgs('/lgtm', commentBody), 'cancel')); @@ -45637,7 +45637,7 @@ async function remove(context = github_context) { const commentBody = context.payload.comment?.body; const commenterId = context.payload.comment?.user?.login; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } // Only users who: // - are collaborators @@ -45703,7 +45703,7 @@ async function approve(context = github_context) { const commentBody = context.payload.comment?.body; const commenterLogin = context.payload.comment?.user.login; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const isCancel = hasCommand('/remove-approve', commentBody) || (hasCommand('/approve', commentBody) && hasKeyword(getCommandArgs('/approve', commentBody), 'cancel')); @@ -45828,7 +45828,7 @@ async function assign_assign(context = github_context) { const commenterId = context.payload.comment?.user?.login; const commentBody = context.payload.comment?.body; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const commentArgs = getCommandArgs('/assign', commentBody); // no arguments after command provided @@ -45907,7 +45907,7 @@ async function cc(context = github_context) { const commenterId = context.payload.comment?.user?.login; const commentBody = context.payload.comment?.body; if (pullNumber === undefined) { - throw new Error(`github context payload missing pull number: ${context.payload}`); + throw new Error(`github context payload missing pull number: ${JSON.stringify(context.payload)}`); } const commentArgs = getCommandArgs('/cc', commentBody); // no arguments after command provided @@ -45986,7 +45986,7 @@ async function close_close(context = github_context) { const commentBody = context.payload.comment?.body; const commenterId = context.payload.comment?.user?.login; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } // Only users who: // - are the issue / PR author @@ -46055,7 +46055,7 @@ async function lock(context = github_context) { const commenterId = context.payload.comment?.user?.login; const commentBody = context.payload.comment?.body; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const commentArgs = getCommandArgs('/lock', commentBody); // Only users who: @@ -46119,7 +46119,7 @@ async function meow(context = github_context) { const octokit = newOctokit(token); const issueNumber = context.payload.issue?.number; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } // a provider outage degrades to a note; only the github write can fail the action let body; @@ -46244,7 +46244,7 @@ async function milestone(context = github_context) { const commentBody = context.payload.comment?.body; const commenterId = context.payload.comment?.user?.login; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } // Only users who: // - are collaborators @@ -46302,7 +46302,7 @@ async function reopen(context = github_context) { const issueNumber = context.payload.issue?.number; const commenterId = context.payload.comment?.user?.login; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } // Only users who: // - are the issue / PR author @@ -46354,7 +46354,7 @@ async function retitle(context = github_context) { const commenterId = context.payload.comment?.user?.login; const commentBody = context.payload.comment?.body; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const title = getLineArgs('/retitle', commentBody); // no arguments after command provided. Can't retitle! @@ -46402,7 +46402,7 @@ async function unassign(context = github_context) { const commenterId = context.payload.comment?.user?.login; const commentBody = context.payload.comment?.body; if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`); + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`); } const commentArgs = getCommandArgs('/unassign', commentBody); // no arguments after command provided @@ -46458,7 +46458,7 @@ async function uncc(context = github_context) { const commenterId = context.payload.comment?.user?.login; const commentBody = context.payload.comment?.body; if (pullNumber === undefined) { - throw new Error(`github context payload missing pull number: ${context.payload}`); + throw new Error(`github context payload missing pull number: ${JSON.stringify(context.payload)}`); } const commentArgs = getCommandArgs('/uncc', commentBody); // no arguments after command provided @@ -46785,7 +46785,7 @@ async function onPrLgtm(context) { const octokit = newOctokit(token); const prNumber = context.payload.pull_request?.number; if (prNumber === undefined) { - throw new Error(`github context payload missing pr number: ${context.payload}`); + throw new Error(`github context payload missing pr number: ${JSON.stringify(context.payload)}`); } let currentLabels = []; try { diff --git a/src/issueComment/approve.ts b/src/issueComment/approve.ts index 20eb1ef1..5f9c4daf 100644 --- a/src/issueComment/approve.ts +++ b/src/issueComment/approve.ts @@ -42,7 +42,7 @@ export async function approve( if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/assign.ts b/src/issueComment/assign.ts index c7a9f9ce..9ee0f17c 100644 --- a/src/issueComment/assign.ts +++ b/src/issueComment/assign.ts @@ -27,7 +27,7 @@ export async function assign(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/cc.ts b/src/issueComment/cc.ts index 64847a08..e03b1d3b 100644 --- a/src/issueComment/cc.ts +++ b/src/issueComment/cc.ts @@ -24,7 +24,7 @@ export async function cc(context: Context = github.context): Promise { if (pullNumber === undefined) { throw new Error( - `github context payload missing pull number: ${context.payload}`, + `github context payload missing pull number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/close.ts b/src/issueComment/close.ts index 3ebe428c..cb0553a5 100644 --- a/src/issueComment/close.ts +++ b/src/issueComment/close.ts @@ -22,7 +22,7 @@ export async function close(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/lock.ts b/src/issueComment/lock.ts index c3e7f337..76f853f6 100644 --- a/src/issueComment/lock.ts +++ b/src/issueComment/lock.ts @@ -31,7 +31,7 @@ export async function lock(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/meow.ts b/src/issueComment/meow.ts index b1f6542b..c375ff66 100644 --- a/src/issueComment/meow.ts +++ b/src/issueComment/meow.ts @@ -33,7 +33,7 @@ export async function meow(context: Context = github.context): Promise { const issueNumber: number | undefined = context.payload.issue?.number if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/milestone.ts b/src/issueComment/milestone.ts index 7dc0ff9c..13dcfdce 100644 --- a/src/issueComment/milestone.ts +++ b/src/issueComment/milestone.ts @@ -26,7 +26,7 @@ export async function milestone( if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/reopen.ts b/src/issueComment/reopen.ts index 421256f6..d22fa6e8 100644 --- a/src/issueComment/reopen.ts +++ b/src/issueComment/reopen.ts @@ -19,7 +19,7 @@ export async function reopen(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/retitle.ts b/src/issueComment/retitle.ts index d7d9fccb..9e1203ec 100644 --- a/src/issueComment/retitle.ts +++ b/src/issueComment/retitle.ts @@ -24,7 +24,7 @@ export async function retitle( if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/trigger.ts b/src/issueComment/trigger.ts index 2f7a9859..2451f201 100644 --- a/src/issueComment/trigger.ts +++ b/src/issueComment/trigger.ts @@ -204,7 +204,7 @@ async function prepare(context: Context, command: string, options: { refuseAutho const commenter: string = context.payload.comment?.user?.login if (issueNumber === undefined) { - throw new Error(`github context payload missing issue number: ${context.payload}`) + throw new Error(`github context payload missing issue number: ${JSON.stringify(context.payload)}`) } if (context.payload.issue?.pull_request === undefined) { diff --git a/src/issueComment/unassign.ts b/src/issueComment/unassign.ts index a8257724..e6ffa08f 100644 --- a/src/issueComment/unassign.ts +++ b/src/issueComment/unassign.ts @@ -23,7 +23,7 @@ export async function unassign( if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/issueComment/uncc.ts b/src/issueComment/uncc.ts index 45b583c9..6af0b121 100644 --- a/src/issueComment/uncc.ts +++ b/src/issueComment/uncc.ts @@ -23,7 +23,7 @@ export async function uncc(context: Context = github.context): Promise { if (pullNumber === undefined) { throw new Error( - `github context payload missing pull number: ${context.payload}`, + `github context payload missing pull number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/labels/fixed.ts b/src/labels/fixed.ts index 6c5e44f2..f82ada30 100644 --- a/src/labels/fixed.ts +++ b/src/labels/fixed.ts @@ -81,7 +81,7 @@ function requireIssueNumber(context: Context): number { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/labels/hold.ts b/src/labels/hold.ts index 642e8abb..10d6fdf6 100644 --- a/src/labels/hold.ts +++ b/src/labels/hold.ts @@ -31,7 +31,7 @@ export async function hold(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/labels/lgtm.ts b/src/labels/lgtm.ts index 15115c5d..a9d122fe 100644 --- a/src/labels/lgtm.ts +++ b/src/labels/lgtm.ts @@ -37,7 +37,7 @@ export async function lgtm(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/labels/prefixed.ts b/src/labels/prefixed.ts index a6f934f8..f00166aa 100644 --- a/src/labels/prefixed.ts +++ b/src/labels/prefixed.ts @@ -157,7 +157,7 @@ function requireIssueNumber(context: Context): number { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/labels/remove.ts b/src/labels/remove.ts index c3a4e705..91effa72 100644 --- a/src/labels/remove.ts +++ b/src/labels/remove.ts @@ -23,7 +23,7 @@ export async function remove(context: Context = github.context): Promise { if (issueNumber === undefined) { throw new Error( - `github context payload missing issue number: ${context.payload}`, + `github context payload missing issue number: ${JSON.stringify(context.payload)}`, ) } diff --git a/src/pullReq/onPrLgtm.ts b/src/pullReq/onPrLgtm.ts index 652d836c..c925c7a3 100644 --- a/src/pullReq/onPrLgtm.ts +++ b/src/pullReq/onPrLgtm.ts @@ -17,7 +17,7 @@ export async function onPrLgtm(context: Context): Promise { if (prNumber === undefined) { throw new Error( - `github context payload missing pr number: ${context.payload}`, + `github context payload missing pr number: ${JSON.stringify(context.payload)}`, ) }