mirror of
https://github.com/mshick/add-pr-comment.git
synced 2025-12-31 14:20:32 +11:00
Issue number (#64)
* Major code reorg * Add issue arg * Paginate existing comment list
This commit is contained in:
parent
e8076c64f7
commit
8645f3f0ea
17 changed files with 2187 additions and 1968 deletions
71
src/comments.ts
Normal file
71
src/comments.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { GitHub } from '@actions/github/lib/utils'
|
||||
import { Endpoints } from '@octokit/types'
|
||||
|
||||
export type CreateIssueCommentResponseData =
|
||||
Endpoints['POST /repos/{owner}/{repo}/issues/{issue_number}/comments']['response']['data']
|
||||
|
||||
export async function getExistingCommentId(
|
||||
octokit: InstanceType<typeof GitHub>,
|
||||
owner: string,
|
||||
repo: string,
|
||||
issueNumber: number,
|
||||
messageId: string,
|
||||
): Promise<number | undefined> {
|
||||
const parameters = {
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
|
||||
let found
|
||||
|
||||
for await (const comments of octokit.paginate.iterator(
|
||||
octokit.rest.issues.listComments,
|
||||
parameters,
|
||||
)) {
|
||||
found = comments.data.find(({ body }) => {
|
||||
return (body?.search(messageId) ?? -1) > -1
|
||||
})
|
||||
|
||||
if (found) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return found?.id
|
||||
}
|
||||
|
||||
export async function updateComment(
|
||||
octokit: InstanceType<typeof GitHub>,
|
||||
owner: string,
|
||||
repo: string,
|
||||
existingCommentId: number,
|
||||
body: string,
|
||||
): Promise<CreateIssueCommentResponseData> {
|
||||
const updatedComment = await octokit.rest.issues.updateComment({
|
||||
comment_id: existingCommentId,
|
||||
owner,
|
||||
repo,
|
||||
body,
|
||||
})
|
||||
|
||||
return updatedComment.data
|
||||
}
|
||||
|
||||
export async function createComment(
|
||||
octokit: InstanceType<typeof GitHub>,
|
||||
owner: string,
|
||||
repo: string,
|
||||
issueNumber: number,
|
||||
body: string,
|
||||
): Promise<CreateIssueCommentResponseData> {
|
||||
const createdComment = await octokit.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner,
|
||||
repo,
|
||||
body,
|
||||
})
|
||||
|
||||
return createdComment.data
|
||||
}
|
||||
89
src/config.ts
Normal file
89
src/config.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import fs from 'node:fs/promises'
|
||||
|
||||
interface Inputs {
|
||||
allowRepeats: boolean
|
||||
message?: string
|
||||
messageId: string
|
||||
messagePath?: string
|
||||
messageSuccess?: string
|
||||
messageFailure?: string
|
||||
messageCancelled?: string
|
||||
proxyUrl?: string
|
||||
repoToken: string
|
||||
status?: string
|
||||
issue?: number
|
||||
commitSha: string
|
||||
pullRequestNumber?: number
|
||||
repo: string
|
||||
owner: string
|
||||
}
|
||||
|
||||
export async function getInputs(): Promise<Inputs> {
|
||||
const messageIdInput = core.getInput('message-id', { required: false })
|
||||
const messageId = messageIdInput === '' ? 'add-pr-comment' : `add-pr-comment:${messageIdInput}`
|
||||
const messageInput = core.getInput('message', { required: false })
|
||||
const messagePath = core.getInput('message-path', { required: false })
|
||||
const repoToken = core.getInput('repo-token', { required: true })
|
||||
const status = core.getInput('status', { required: true })
|
||||
const issue = core.getInput('issue', { required: false })
|
||||
const proxyUrl = core.getInput('proxy-url', { required: false }).replace(/\/$/, '')
|
||||
const allowRepeats = core.getInput('allow-repeats', { required: true }) === 'true'
|
||||
|
||||
if (messageInput && messagePath) {
|
||||
throw new Error('must specify only one, message or message-path')
|
||||
}
|
||||
|
||||
let message
|
||||
|
||||
if (messagePath) {
|
||||
message = await fs.readFile(messagePath, { encoding: 'utf8' })
|
||||
} else {
|
||||
message = messageInput
|
||||
}
|
||||
|
||||
const messageSuccess = core.getInput(`message-success`)
|
||||
const messageFailure = core.getInput(`message-failure`)
|
||||
const messageCancelled = core.getInput(`message-cancelled`)
|
||||
|
||||
if (status === 'success' && messageSuccess) {
|
||||
message = messageSuccess
|
||||
}
|
||||
|
||||
if (status === 'failure' && messageFailure) {
|
||||
message = messageFailure
|
||||
}
|
||||
|
||||
if (status === 'cancelled' && messageCancelled) {
|
||||
message = messageCancelled
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
throw new Error('no message, check your message inputs')
|
||||
}
|
||||
|
||||
const { payload } = github.context
|
||||
|
||||
const repoFullName = payload.repository?.full_name
|
||||
|
||||
if (!repoFullName) {
|
||||
throw new Error('unable to determine repository from request type')
|
||||
}
|
||||
|
||||
const [owner, repo] = repoFullName.split('/')
|
||||
|
||||
return {
|
||||
allowRepeats,
|
||||
message,
|
||||
messageId: `<!-- ${messageId} -->`,
|
||||
proxyUrl,
|
||||
repoToken,
|
||||
status,
|
||||
issue: issue ? Number(issue) : payload.issue?.number,
|
||||
pullRequestNumber: payload.pull_request?.number,
|
||||
commitSha: github.context.sha,
|
||||
owner,
|
||||
repo,
|
||||
}
|
||||
}
|
||||
16
src/issues.ts
Normal file
16
src/issues.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { GitHub } from '@actions/github/lib/utils'
|
||||
|
||||
export async function getIssueNumberFromCommitPullsList(
|
||||
octokit: InstanceType<typeof GitHub>,
|
||||
owner: string,
|
||||
repo: string,
|
||||
commitSha: string,
|
||||
): Promise<number | null> {
|
||||
const commitPullsList = await octokit.rest.repos.listPullRequestsAssociatedWithCommit({
|
||||
owner,
|
||||
repo,
|
||||
commit_sha: commitSha,
|
||||
})
|
||||
|
||||
return commitPullsList.data.length ? commitPullsList.data?.[0].number : null
|
||||
}
|
||||
214
src/main.ts
214
src/main.ts
|
|
@ -1,179 +1,46 @@
|
|||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import { HttpClient } from '@actions/http-client'
|
||||
import { Endpoints } from '@octokit/types'
|
||||
import fs from 'node:fs/promises'
|
||||
|
||||
type ListCommitPullsResponseData =
|
||||
Endpoints['GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls']['response']['data']
|
||||
type CreateIssueCommentResponseData =
|
||||
Endpoints['POST /repos/{owner}/{repo}/issues/{issue_number}/comments']['response']['data']
|
||||
type IssuesListCommentsResponseData =
|
||||
Endpoints['GET /repos/{owner}/{repo}/issues/comments']['response']['data']
|
||||
|
||||
const getIssueNumberFromCommitPullsList = (
|
||||
commitPullsList: ListCommitPullsResponseData,
|
||||
): number | null => (commitPullsList.length ? commitPullsList[0].number : null)
|
||||
|
||||
interface CreateCommentProxyParams {
|
||||
repoToken: string
|
||||
commentId?: number
|
||||
body: string
|
||||
owner: string
|
||||
repo: string
|
||||
issueNumber: number
|
||||
proxyUrl: string
|
||||
}
|
||||
|
||||
async function createCommentProxy(
|
||||
params: CreateCommentProxyParams,
|
||||
): Promise<CreateIssueCommentResponseData | null> {
|
||||
const { repoToken, owner, repo, issueNumber, body, commentId, proxyUrl } = params
|
||||
|
||||
const http = new HttpClient('http-client-add-pr-comment')
|
||||
|
||||
const response = await http.postJson<CreateIssueCommentResponseData>(
|
||||
`${proxyUrl}/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
|
||||
{ comment_id: commentId, body },
|
||||
{
|
||||
['temporary-github-token']: repoToken,
|
||||
},
|
||||
)
|
||||
|
||||
return response.result
|
||||
}
|
||||
|
||||
function getExistingCommentId(
|
||||
comments: IssuesListCommentsResponseData,
|
||||
messageId: string,
|
||||
): number | undefined {
|
||||
const found = comments.find(({ body }) => {
|
||||
return (body?.search(messageId) ?? -1) > -1
|
||||
})
|
||||
|
||||
return found?.id
|
||||
}
|
||||
|
||||
interface AddPrCommentInputs {
|
||||
allowRepeats: boolean
|
||||
message?: string
|
||||
messageId: string
|
||||
messagePath?: string
|
||||
messageSuccess?: string
|
||||
messageFailure?: string
|
||||
messageCancelled?: string
|
||||
proxyUrl?: string
|
||||
repoToken: string
|
||||
status?: string
|
||||
}
|
||||
|
||||
async function getInputs(): Promise<AddPrCommentInputs> {
|
||||
const messageId = core.getInput('message-id')
|
||||
const messageInput = core.getInput('message')
|
||||
const messagePath = core.getInput('message-path')
|
||||
const repoToken = core.getInput('repo-token') || process.env['GITHUB_TOKEN']
|
||||
const status = core.getInput('status')
|
||||
|
||||
if (!repoToken) {
|
||||
throw new Error(
|
||||
'no github token provided, set one with the repo-token input or GITHUB_TOKEN env variable',
|
||||
)
|
||||
}
|
||||
|
||||
if (messageInput && messagePath) {
|
||||
throw new Error('must specify only one, message or message-path')
|
||||
}
|
||||
|
||||
let message
|
||||
|
||||
if (messagePath) {
|
||||
message = await fs.readFile(messagePath, { encoding: 'utf8' })
|
||||
} else {
|
||||
message = messageInput
|
||||
}
|
||||
|
||||
const messageSuccess = core.getInput(`message-success`)
|
||||
const messageFailure = core.getInput(`message-failure`)
|
||||
const messageCancelled = core.getInput(`message-cancelled`)
|
||||
|
||||
if ((messageSuccess || messageFailure || messageCancelled) && !status) {
|
||||
throw new Error('to use a status message you must provide a status input')
|
||||
}
|
||||
|
||||
if (status) {
|
||||
if (status === 'success' && messageSuccess) {
|
||||
message = messageSuccess
|
||||
}
|
||||
|
||||
if (status === 'failure' && messageFailure) {
|
||||
message = messageFailure
|
||||
}
|
||||
|
||||
if (status === 'cancelled' && messageCancelled) {
|
||||
message = messageCancelled
|
||||
}
|
||||
}
|
||||
|
||||
if (!message) {
|
||||
throw new Error('no message, check your message inputs')
|
||||
}
|
||||
|
||||
return {
|
||||
allowRepeats: Boolean(core.getInput('allow-repeats') === 'true'),
|
||||
message,
|
||||
messageId: messageId === '' ? 'add-pr-comment' : messageId,
|
||||
proxyUrl: core.getInput('proxy-url').replace(/\/$/, ''),
|
||||
repoToken,
|
||||
status,
|
||||
}
|
||||
}
|
||||
import {
|
||||
createComment,
|
||||
CreateIssueCommentResponseData,
|
||||
getExistingCommentId,
|
||||
updateComment,
|
||||
} from './comments'
|
||||
import { getInputs } from './config'
|
||||
import { getIssueNumberFromCommitPullsList } from './issues'
|
||||
import { createCommentProxy } from './proxy'
|
||||
|
||||
const run = async (): Promise<void> => {
|
||||
try {
|
||||
const { allowRepeats, message, messageId, repoToken, proxyUrl } = await getInputs()
|
||||
const messageIdComment = `<!-- ${messageId} -->`
|
||||
|
||||
const {
|
||||
payload: { pull_request: pullRequest, issue, repository },
|
||||
sha: commitSha,
|
||||
} = github.context
|
||||
allowRepeats,
|
||||
message,
|
||||
messageId,
|
||||
repoToken,
|
||||
proxyUrl,
|
||||
issue,
|
||||
pullRequestNumber,
|
||||
commitSha,
|
||||
repo,
|
||||
owner,
|
||||
} = await getInputs()
|
||||
|
||||
if (!repository) {
|
||||
core.info('unable to determine repository from request type')
|
||||
core.setOutput('comment-created', 'false')
|
||||
return
|
||||
}
|
||||
|
||||
const { full_name: repoFullName } = repository
|
||||
|
||||
if (!repoFullName) {
|
||||
core.info('repository is missing a full_name property... weird')
|
||||
core.setOutput('comment-created', 'false')
|
||||
return
|
||||
}
|
||||
|
||||
const [owner, repo] = repoFullName.split('/')
|
||||
const octokit = github.getOctokit(repoToken)
|
||||
|
||||
let issueNumber
|
||||
|
||||
if (issue && issue.number) {
|
||||
issueNumber = issue.number
|
||||
} else if (pullRequest && pullRequest.number) {
|
||||
issueNumber = pullRequest.number
|
||||
if (issue) {
|
||||
issueNumber = issue
|
||||
} else if (pullRequestNumber) {
|
||||
issueNumber = pullRequestNumber
|
||||
} else {
|
||||
// If this is not a pull request, attempt to find a PR matching the sha
|
||||
const commitPullsList = await octokit.rest.repos.listPullRequestsAssociatedWithCommit({
|
||||
owner,
|
||||
repo,
|
||||
commit_sha: commitSha,
|
||||
})
|
||||
issueNumber = commitPullsList.data && getIssueNumberFromCommitPullsList(commitPullsList.data)
|
||||
issueNumber = await getIssueNumberFromCommitPullsList(octokit, owner, repo, commitSha)
|
||||
}
|
||||
|
||||
if (!issueNumber) {
|
||||
core.info(
|
||||
'this action only works on issues and pull_request events or other commits associated with a pull',
|
||||
'no issue number found, use a pull_request event, a pull event, or provide an issue input',
|
||||
)
|
||||
core.setOutput('comment-created', 'false')
|
||||
return
|
||||
|
|
@ -184,13 +51,7 @@ const run = async (): Promise<void> => {
|
|||
if (!allowRepeats) {
|
||||
core.debug('repeat comments are disallowed, checking for existing')
|
||||
|
||||
const { data: comments } = await octokit.rest.issues.listComments({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: issueNumber,
|
||||
})
|
||||
|
||||
existingCommentId = getExistingCommentId(comments, messageIdComment)
|
||||
existingCommentId = await getExistingCommentId(octokit, owner, repo, issueNumber, messageId)
|
||||
|
||||
if (existingCommentId) {
|
||||
core.debug(`existing comment found with id: ${existingCommentId}`)
|
||||
|
|
@ -198,7 +59,8 @@ const run = async (): Promise<void> => {
|
|||
}
|
||||
|
||||
let comment: CreateIssueCommentResponseData | null | undefined
|
||||
const body = `${messageIdComment}\n\n${message}`
|
||||
|
||||
const body = `${messageId}\n\n${message}`
|
||||
|
||||
if (proxyUrl) {
|
||||
comment = await createCommentProxy({
|
||||
|
|
@ -212,22 +74,10 @@ const run = async (): Promise<void> => {
|
|||
})
|
||||
core.setOutput(existingCommentId ? 'comment-updated' : 'comment-created', 'true')
|
||||
} else if (existingCommentId) {
|
||||
const updatedComment = await octokit.rest.issues.updateComment({
|
||||
comment_id: existingCommentId,
|
||||
owner,
|
||||
repo,
|
||||
body,
|
||||
})
|
||||
comment = updatedComment.data
|
||||
comment = await updateComment(octokit, owner, repo, existingCommentId, body)
|
||||
core.setOutput('comment-updated', 'true')
|
||||
} else {
|
||||
const createdComment = await octokit.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner,
|
||||
repo,
|
||||
body,
|
||||
})
|
||||
comment = createdComment.data
|
||||
comment = await createComment(octokit, owner, repo, issueNumber, body)
|
||||
core.setOutput('comment-created', 'true')
|
||||
}
|
||||
|
||||
|
|
@ -240,8 +90,6 @@ const run = async (): Promise<void> => {
|
|||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
core.setFailed(err.message)
|
||||
} else {
|
||||
core.setFailed('unknown failure')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
33
src/proxy.ts
Normal file
33
src/proxy.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { HttpClient } from '@actions/http-client'
|
||||
import { Endpoints } from '@octokit/types'
|
||||
|
||||
type CreateIssueCommentResponseData =
|
||||
Endpoints['POST /repos/{owner}/{repo}/issues/{issue_number}/comments']['response']['data']
|
||||
|
||||
export interface CreateCommentProxyParams {
|
||||
repoToken: string
|
||||
commentId?: number
|
||||
body: string
|
||||
owner: string
|
||||
repo: string
|
||||
issueNumber: number
|
||||
proxyUrl: string
|
||||
}
|
||||
|
||||
export async function createCommentProxy(
|
||||
params: CreateCommentProxyParams,
|
||||
): Promise<CreateIssueCommentResponseData | null> {
|
||||
const { repoToken, owner, repo, issueNumber, body, commentId, proxyUrl } = params
|
||||
|
||||
const http = new HttpClient('http-client-add-pr-comment')
|
||||
|
||||
const response = await http.postJson<CreateIssueCommentResponseData>(
|
||||
`${proxyUrl}/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
|
||||
{ comment_id: commentId, body },
|
||||
{
|
||||
['temporary-github-token']: repoToken,
|
||||
},
|
||||
)
|
||||
|
||||
return response.result
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue