From 022bf20c8e1d389e7fc08b83413487207852fb76 Mon Sep 17 00:00:00 2001 From: kitsuyui Date: Thu, 21 May 2026 07:33:54 +0900 Subject: [PATCH] Use raw body for managed comment comparison Compare existing managed comments with the raw markdown comment body when the API returns it. This keeps the hidden marker available for equality checks while retaining body_text as a fallback. --- dist/github.mjs | 2 +- dist/github.mjs.map | 2 +- src/github.spec.ts | 6 +++--- src/github.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dist/github.mjs b/dist/github.mjs index acaca58..737acef 100644 --- a/dist/github.mjs +++ b/dist/github.mjs @@ -1,4 +1,4 @@ -import{t as e}from"./github-D91hw1gk.mjs";const t=``;function n(e){return`${t}\n${e}`}function r(e,t){return e?e.body===t?{type:`noop`}:{type:`update`,commentId:e.id,body:t}:{type:`create`,body:t}}function i(e,t){return t.lucky?r(e,n(t.body)):e?{type:`delete`,commentId:e.id}:{type:`noop`}}async function a(t,n,r,a){let s=e,c=i(await o(t,n,r),a);switch(c.type){case`update`:await t.issues.updateComment({...s.repo,comment_id:c.commentId,body:c.body});return;case`create`:await t.issues.createComment({...s.repo,issue_number:n,body:c.body});return;case`delete`:await t.issues.deleteComment({...s.repo,comment_id:c.commentId});return;case`noop`:return}}async function o(t,n,r){let i=e,a=(await t.issues.listComments({...i.repo,issue_number:n})).data.find(e=>s(e,r));return a?{id:a.id,body:a.body_text||``}:null}function s(e,n){return e.user?.login===n&&(e.body||e.body_text||``).includes(t)}async function c(t){let n=e;return(await t.pulls.listCommits({...n.repo,pull_number:n.issue.number})).data.map(e=>e.sha)}async function l(t,n){let r=e,i=(await t.graphql(` +import{t as e}from"./github-D91hw1gk.mjs";const t=``;function n(e){return`${t}\n${e}`}function r(e,t){return e?e.body===t?{type:`noop`}:{type:`update`,commentId:e.id,body:t}:{type:`create`,body:t}}function i(e,t){return t.lucky?r(e,n(t.body)):e?{type:`delete`,commentId:e.id}:{type:`noop`}}async function a(t,n,r,a){let s=e,c=i(await o(t,n,r),a);switch(c.type){case`update`:await t.issues.updateComment({...s.repo,comment_id:c.commentId,body:c.body});return;case`create`:await t.issues.createComment({...s.repo,issue_number:n,body:c.body});return;case`delete`:await t.issues.deleteComment({...s.repo,comment_id:c.commentId});return;case`noop`:return}}async function o(t,n,r){let i=e,a=(await t.issues.listComments({...i.repo,issue_number:n})).data.find(e=>s(e,r));return a?{id:a.id,body:a.body||a.body_text||``}:null}function s(e,n){return e.user?.login===n&&(e.body||e.body_text||``).includes(t)}async function c(t){let n=e;return(await t.pulls.listCommits({...n.repo,pull_number:n.issue.number})).data.map(e=>e.sha)}async function l(t,n){let r=e,i=(await t.graphql(` query ($owner: String!, $repo: String!, $expression: String!) { repository(owner: $owner, name: $repo) { object(expression: $expression) { diff --git a/dist/github.mjs.map b/dist/github.mjs.map index f6d6617..48c7077 100644 --- a/dist/github.mjs.map +++ b/dist/github.mjs.map @@ -1 +1 @@ -{"version":3,"file":"github.mjs","names":["context","github.context"],"sources":["../src/github.ts"],"sourcesContent":["import * as github from '@actions/github'\nimport type { Octokit } from '@octokit/action'\n\nimport type { Comment, MessageContext } from './interfaces'\n\nconst COMMENT_MARKER = ''\n\ntype CommentAction =\n | { type: 'create'; body: string }\n | { type: 'update'; commentId: number; body: string }\n | { type: 'delete'; commentId: number }\n | { type: 'noop' }\n\nfunction buildManagedBody(body: string): string {\n return `${COMMENT_MARKER}\\n${body}`\n}\n\nfunction createLuckyCommentAction(\n pastComment: Comment | null,\n managedBody: string\n): CommentAction {\n if (!pastComment) {\n return { type: 'create', body: managedBody }\n }\n if (pastComment.body === managedBody) {\n return { type: 'noop' }\n }\n return {\n type: 'update',\n commentId: pastComment.id,\n body: managedBody,\n }\n}\n\nexport function decideCommentAction(\n pastComment: Comment | null,\n message: MessageContext\n): CommentAction {\n if (!message.lucky) {\n return pastComment\n ? { type: 'delete', commentId: pastComment.id }\n : { type: 'noop' }\n }\n\n return createLuckyCommentAction(pastComment, buildManagedBody(message.body))\n}\n\n/**\n * Update the comment of the current PR\n * if lucky and past comment does not exist, create it.\n * if lucky and past comment exists, update it.\n * if not lucky, delete the comment.\n *\n * @param octokit {Octokit} the octokit instance\n * @param prNum {number} the PR number\n * @param userLogin {string} the user login name\n * @param message {MessageContext} the message context\n */\nexport async function updateMessage(\n octokit: Octokit,\n prNum: number,\n userLogin: string,\n message: MessageContext\n): Promise {\n const context = github.context\n const pastComment = await getManagedComment(octokit, prNum, userLogin)\n const action = decideCommentAction(pastComment, message)\n\n switch (action.type) {\n case 'update': {\n await octokit.issues.updateComment({\n ...context.repo,\n comment_id: action.commentId,\n body: action.body,\n })\n return\n }\n case 'create': {\n await octokit.issues.createComment({\n ...context.repo,\n issue_number: prNum,\n body: action.body,\n })\n return\n }\n case 'delete': {\n await octokit.issues.deleteComment({\n ...context.repo,\n comment_id: action.commentId,\n })\n return\n }\n case 'noop':\n return\n }\n}\n\n/**\n * Get the managed comment of the current PR by the current user\n * @param octokit {Octokit} the octokit instance\n * @param prNum {number} the PR number\n * @param userLogin {string} the user login name\n * @returns comment id {LastComment}\n */\nasync function getManagedComment(\n octokit: Octokit,\n prNum: number,\n userLogin: string\n): Promise {\n const context = github.context\n // get comments on the PR\n const comments = await octokit.issues.listComments({\n ...context.repo,\n issue_number: prNum,\n })\n const comment = comments.data.find((candidate) =>\n isManagedCommentByUser(candidate, userLogin)\n )\n return comment\n ? {\n id: comment.id,\n body: comment.body_text || '',\n }\n : null\n}\n\nfunction isManagedCommentByUser(\n comment: {\n user?: { login?: string | null } | null\n body?: string | null\n body_text?: string | null\n },\n userLogin: string\n): boolean {\n return (\n comment.user?.login === userLogin &&\n (comment.body || comment.body_text || '').includes(COMMENT_MARKER)\n )\n}\n\n/**\n * Get commit ids of the current PR\n * @param octokit {Octokit} the octokit instance\n * @returns commit ids {string[]}\n */\nexport async function getCommitIds(octokit: Octokit): Promise {\n const context = github.context\n const commits = await octokit.pulls.listCommits({\n ...context.repo,\n pull_number: context.issue.number,\n })\n return commits.data.map((commit: { sha: string }) => commit.sha)\n}\n\nexport async function getRepositoryCommitCount(\n octokit: Octokit,\n defaultBranch: string\n): Promise {\n interface Result {\n repository: {\n object: {\n history: {\n totalCount: number\n }\n } | null\n } | null\n }\n\n const context = github.context\n const resp: Result = await octokit.graphql(\n `\nquery ($owner: String!, $repo: String!, $expression: String!) {\n repository(owner: $owner, name: $repo) {\n object(expression: $expression) {\n ... on Commit {\n history(first: 1) {\n totalCount\n }\n }\n }\n }\n}\n`,\n {\n owner: context.repo.owner,\n repo: context.repo.repo,\n expression: `refs/heads/${defaultBranch}`,\n }\n )\n\n const totalCount = resp.repository?.object?.history.totalCount\n if (typeof totalCount !== 'number') {\n throw new Error(`Could not resolve commit count for ${defaultBranch}`)\n }\n\n return totalCount\n}\n\n/**\n * Get login name of the current user\n * By default, this returns `github-actions[bot]`\n * @param octokit {Octokit} the octokit instance\n * @returns user login {string}\n */\nexport async function getUserLogin(octokit: Octokit) {\n interface Result {\n viewer: {\n login: string\n }\n }\n const resp: Result = await octokit.graphql(`\nquery {\n viewer {\n login\n }\n}`)\n return resp.viewer.login\n}\n"],"mappings":"0CAKA,MAAM,EAAiB,wBAQvB,SAAS,EAAiB,EAAsB,CAC9C,MAAO,GAAG,EAAe,IAAI,IAG/B,SAAS,EACP,EACA,EACe,CAOf,OANK,EAGD,EAAY,OAAS,EAChB,CAAE,KAAM,OAAQ,CAElB,CACL,KAAM,SACN,UAAW,EAAY,GACvB,KAAM,EACP,CATQ,CAAE,KAAM,SAAU,KAAM,EAAa,CAYhD,SAAgB,EACd,EACA,EACe,CAOf,OANK,EAAQ,MAMN,EAAyB,EAAa,EAAiB,EAAQ,KAAK,CAAC,CALnE,EACH,CAAE,KAAM,SAAU,UAAW,EAAY,GAAI,CAC7C,CAAE,KAAM,OAAQ,CAiBxB,eAAsB,EACpB,EACA,EACA,EACA,EACe,CACf,IAAMA,EAAUC,EAEV,EAAS,EADK,MAAM,EAAkB,EAAS,EAAO,EAAU,CACtB,EAAQ,CAExD,OAAQ,EAAO,KAAf,CACE,IAAK,SACH,MAAM,EAAQ,OAAO,cAAc,CACjC,GAAGD,EAAQ,KACX,WAAY,EAAO,UACnB,KAAM,EAAO,KACd,CAAC,CACF,OAEF,IAAK,SACH,MAAM,EAAQ,OAAO,cAAc,CACjC,GAAGA,EAAQ,KACX,aAAc,EACd,KAAM,EAAO,KACd,CAAC,CACF,OAEF,IAAK,SACH,MAAM,EAAQ,OAAO,cAAc,CACjC,GAAGA,EAAQ,KACX,WAAY,EAAO,UACpB,CAAC,CACF,OAEF,IAAK,OACH,QAWN,eAAe,EACb,EACA,EACA,EACyB,CACzB,IAAMA,EAAUC,EAMV,GAJW,MAAM,EAAQ,OAAO,aAAa,CACjD,GAAGD,EAAQ,KACX,aAAc,EACf,CAAC,EACuB,KAAK,KAAM,GAClC,EAAuB,EAAW,EAAU,CAC7C,CACD,OAAO,EACH,CACE,GAAI,EAAQ,GACZ,KAAM,EAAQ,WAAa,GAC5B,CACD,KAGN,SAAS,EACP,EAKA,EACS,CACT,OACE,EAAQ,MAAM,QAAU,IACvB,EAAQ,MAAQ,EAAQ,WAAa,IAAI,SAAS,EAAe,CAStE,eAAsB,EAAa,EAAqC,CACtE,IAAMA,EAAUC,EAKhB,OAJgB,MAAM,EAAQ,MAAM,YAAY,CAC9C,GAAGD,EAAQ,KACX,YAAaA,EAAQ,MAAM,OAC5B,CAAC,EACa,KAAK,IAAK,GAA4B,EAAO,IAAI,CAGlE,eAAsB,EACpB,EACA,EACiB,CAWjB,IAAMA,EAAUC,EAsBV,GArBe,MAAM,EAAQ,QACjC;;;;;;;;;;;;EAaA,CACE,MAAOD,EAAQ,KAAK,MACpB,KAAMA,EAAQ,KAAK,KACnB,WAAY,cAAc,IAC3B,CACF,EAEuB,YAAY,QAAQ,QAAQ,WACpD,GAAI,OAAO,GAAe,SACxB,MAAU,MAAM,sCAAsC,IAAgB,CAGxE,OAAO,EAST,eAAsB,EAAa,EAAkB,CAYnD,OANqB,MAAM,EAAQ,QAAQ;;;;;GAK1C,EACW,OAAO"} \ No newline at end of file +{"version":3,"file":"github.mjs","names":["context","github.context"],"sources":["../src/github.ts"],"sourcesContent":["import * as github from '@actions/github'\nimport type { Octokit } from '@octokit/action'\n\nimport type { Comment, MessageContext } from './interfaces'\n\nconst COMMENT_MARKER = ''\n\ntype CommentAction =\n | { type: 'create'; body: string }\n | { type: 'update'; commentId: number; body: string }\n | { type: 'delete'; commentId: number }\n | { type: 'noop' }\n\nfunction buildManagedBody(body: string): string {\n return `${COMMENT_MARKER}\\n${body}`\n}\n\nfunction createLuckyCommentAction(\n pastComment: Comment | null,\n managedBody: string\n): CommentAction {\n if (!pastComment) {\n return { type: 'create', body: managedBody }\n }\n if (pastComment.body === managedBody) {\n return { type: 'noop' }\n }\n return {\n type: 'update',\n commentId: pastComment.id,\n body: managedBody,\n }\n}\n\nexport function decideCommentAction(\n pastComment: Comment | null,\n message: MessageContext\n): CommentAction {\n if (!message.lucky) {\n return pastComment\n ? { type: 'delete', commentId: pastComment.id }\n : { type: 'noop' }\n }\n\n return createLuckyCommentAction(pastComment, buildManagedBody(message.body))\n}\n\n/**\n * Update the comment of the current PR\n * if lucky and past comment does not exist, create it.\n * if lucky and past comment exists, update it.\n * if not lucky, delete the comment.\n *\n * @param octokit {Octokit} the octokit instance\n * @param prNum {number} the PR number\n * @param userLogin {string} the user login name\n * @param message {MessageContext} the message context\n */\nexport async function updateMessage(\n octokit: Octokit,\n prNum: number,\n userLogin: string,\n message: MessageContext\n): Promise {\n const context = github.context\n const pastComment = await getManagedComment(octokit, prNum, userLogin)\n const action = decideCommentAction(pastComment, message)\n\n switch (action.type) {\n case 'update': {\n await octokit.issues.updateComment({\n ...context.repo,\n comment_id: action.commentId,\n body: action.body,\n })\n return\n }\n case 'create': {\n await octokit.issues.createComment({\n ...context.repo,\n issue_number: prNum,\n body: action.body,\n })\n return\n }\n case 'delete': {\n await octokit.issues.deleteComment({\n ...context.repo,\n comment_id: action.commentId,\n })\n return\n }\n case 'noop':\n return\n }\n}\n\n/**\n * Get the managed comment of the current PR by the current user\n * @param octokit {Octokit} the octokit instance\n * @param prNum {number} the PR number\n * @param userLogin {string} the user login name\n * @returns comment id {LastComment}\n */\nasync function getManagedComment(\n octokit: Octokit,\n prNum: number,\n userLogin: string\n): Promise {\n const context = github.context\n // get comments on the PR\n const comments = await octokit.issues.listComments({\n ...context.repo,\n issue_number: prNum,\n })\n const comment = comments.data.find((candidate) =>\n isManagedCommentByUser(candidate, userLogin)\n )\n return comment\n ? {\n id: comment.id,\n body: comment.body || comment.body_text || '',\n }\n : null\n}\n\nfunction isManagedCommentByUser(\n comment: {\n user?: { login?: string | null } | null\n body?: string | null\n body_text?: string | null\n },\n userLogin: string\n): boolean {\n return (\n comment.user?.login === userLogin &&\n (comment.body || comment.body_text || '').includes(COMMENT_MARKER)\n )\n}\n\n/**\n * Get commit ids of the current PR\n * @param octokit {Octokit} the octokit instance\n * @returns commit ids {string[]}\n */\nexport async function getCommitIds(octokit: Octokit): Promise {\n const context = github.context\n const commits = await octokit.pulls.listCommits({\n ...context.repo,\n pull_number: context.issue.number,\n })\n return commits.data.map((commit: { sha: string }) => commit.sha)\n}\n\nexport async function getRepositoryCommitCount(\n octokit: Octokit,\n defaultBranch: string\n): Promise {\n interface Result {\n repository: {\n object: {\n history: {\n totalCount: number\n }\n } | null\n } | null\n }\n\n const context = github.context\n const resp: Result = await octokit.graphql(\n `\nquery ($owner: String!, $repo: String!, $expression: String!) {\n repository(owner: $owner, name: $repo) {\n object(expression: $expression) {\n ... on Commit {\n history(first: 1) {\n totalCount\n }\n }\n }\n }\n}\n`,\n {\n owner: context.repo.owner,\n repo: context.repo.repo,\n expression: `refs/heads/${defaultBranch}`,\n }\n )\n\n const totalCount = resp.repository?.object?.history.totalCount\n if (typeof totalCount !== 'number') {\n throw new Error(`Could not resolve commit count for ${defaultBranch}`)\n }\n\n return totalCount\n}\n\n/**\n * Get login name of the current user\n * By default, this returns `github-actions[bot]`\n * @param octokit {Octokit} the octokit instance\n * @returns user login {string}\n */\nexport async function getUserLogin(octokit: Octokit) {\n interface Result {\n viewer: {\n login: string\n }\n }\n const resp: Result = await octokit.graphql(`\nquery {\n viewer {\n login\n }\n}`)\n return resp.viewer.login\n}\n"],"mappings":"0CAKA,MAAM,EAAiB,wBAQvB,SAAS,EAAiB,EAAsB,CAC9C,MAAO,GAAG,EAAe,IAAI,IAG/B,SAAS,EACP,EACA,EACe,CAOf,OANK,EAGD,EAAY,OAAS,EAChB,CAAE,KAAM,OAAQ,CAElB,CACL,KAAM,SACN,UAAW,EAAY,GACvB,KAAM,EACP,CATQ,CAAE,KAAM,SAAU,KAAM,EAAa,CAYhD,SAAgB,EACd,EACA,EACe,CAOf,OANK,EAAQ,MAMN,EAAyB,EAAa,EAAiB,EAAQ,KAAK,CAAC,CALnE,EACH,CAAE,KAAM,SAAU,UAAW,EAAY,GAAI,CAC7C,CAAE,KAAM,OAAQ,CAiBxB,eAAsB,EACpB,EACA,EACA,EACA,EACe,CACf,IAAMA,EAAUC,EAEV,EAAS,EADK,MAAM,EAAkB,EAAS,EAAO,EAAU,CACtB,EAAQ,CAExD,OAAQ,EAAO,KAAf,CACE,IAAK,SACH,MAAM,EAAQ,OAAO,cAAc,CACjC,GAAGD,EAAQ,KACX,WAAY,EAAO,UACnB,KAAM,EAAO,KACd,CAAC,CACF,OAEF,IAAK,SACH,MAAM,EAAQ,OAAO,cAAc,CACjC,GAAGA,EAAQ,KACX,aAAc,EACd,KAAM,EAAO,KACd,CAAC,CACF,OAEF,IAAK,SACH,MAAM,EAAQ,OAAO,cAAc,CACjC,GAAGA,EAAQ,KACX,WAAY,EAAO,UACpB,CAAC,CACF,OAEF,IAAK,OACH,QAWN,eAAe,EACb,EACA,EACA,EACyB,CACzB,IAAMA,EAAUC,EAMV,GAJW,MAAM,EAAQ,OAAO,aAAa,CACjD,GAAGD,EAAQ,KACX,aAAc,EACf,CAAC,EACuB,KAAK,KAAM,GAClC,EAAuB,EAAW,EAAU,CAC7C,CACD,OAAO,EACH,CACE,GAAI,EAAQ,GACZ,KAAM,EAAQ,MAAQ,EAAQ,WAAa,GAC5C,CACD,KAGN,SAAS,EACP,EAKA,EACS,CACT,OACE,EAAQ,MAAM,QAAU,IACvB,EAAQ,MAAQ,EAAQ,WAAa,IAAI,SAAS,EAAe,CAStE,eAAsB,EAAa,EAAqC,CACtE,IAAMA,EAAUC,EAKhB,OAJgB,MAAM,EAAQ,MAAM,YAAY,CAC9C,GAAGD,EAAQ,KACX,YAAaA,EAAQ,MAAM,OAC5B,CAAC,EACa,KAAK,IAAK,GAA4B,EAAO,IAAI,CAGlE,eAAsB,EACpB,EACA,EACiB,CAWjB,IAAMA,EAAUC,EAsBV,GArBe,MAAM,EAAQ,QACjC;;;;;;;;;;;;EAaA,CACE,MAAOD,EAAQ,KAAK,MACpB,KAAMA,EAAQ,KAAK,KACnB,WAAY,cAAc,IAC3B,CACF,EAEuB,YAAY,QAAQ,QAAQ,WACpD,GAAI,OAAO,GAAe,SACxB,MAAU,MAAM,sCAAsC,IAAgB,CAGxE,OAAO,EAST,eAAsB,EAAa,EAAkB,CAYnD,OANqB,MAAM,EAAQ,QAAQ;;;;;GAK1C,EACW,OAAO"} \ No newline at end of file diff --git a/src/github.spec.ts b/src/github.spec.ts index 3533f66..3014b57 100644 --- a/src/github.spec.ts +++ b/src/github.spec.ts @@ -153,7 +153,7 @@ describe('updateMessage', () => { data: [ { id: 55, - body_text: '\nold body', + body_text: 'old body', body: '\nold body', user: { login: 'github-actions[bot]' }, }, @@ -181,7 +181,7 @@ describe('updateMessage', () => { data: [ { id: 55, - body_text: '\nsame body', + body_text: 'same body', body: '\nsame body', user: { login: 'github-actions[bot]' }, }, @@ -204,7 +204,7 @@ describe('updateMessage', () => { data: [ { id: 55, - body_text: '\nsame body', + body_text: 'same body', body: '\nsame body', user: { login: 'github-actions[bot]' }, }, diff --git a/src/github.ts b/src/github.ts index 44ad138..d63911c 100644 --- a/src/github.ts +++ b/src/github.ts @@ -119,7 +119,7 @@ async function getManagedComment( return comment ? { id: comment.id, - body: comment.body_text || '', + body: comment.body || comment.body_text || '', } : null }