Compare commits

..

No commits in common. "main" and "v1.2.0" have entirely different histories.
main ... v1.2.0

30 changed files with 16748 additions and 39600 deletions

View File

@ -1,3 +0,0 @@
dist/
lib/
node_modules/

View File

@ -1,18 +1,17 @@
{
"env": { "node": true, "jest": true },
"parser": "@typescript-eslint/parser",
"parserOptions": { "ecmaVersion": 9, "sourceType": "module" },
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:prettier/recommended"
],
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/camelcase": "off"
}
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
}
}

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
github: peter-evans

View File

@ -1 +0,0 @@
This is still the second line.

View File

@ -1,2 +0,0 @@
This is a multi-line test comment read from a file.
This is the second line.

View File

@ -1,2 +0,0 @@
This is a test comment template
Render template variables such as {{ .foo }} and {{ .bar }}.

View File

@ -1,20 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
day: "wednesday"
labels:
- "dependencies"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: "wednesday"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
labels:
- "dependencies"

View File

@ -1,13 +0,0 @@
name: Auto-merge Dependabot
on: pull_request
jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
pull-request-number: ${{ github.event.pull_request.number }}
merge-method: squash

View File

@ -1,142 +0,0 @@
name: CI
on:
push:
branches: [main]
paths-ignore:
- 'README.md'
- 'docs/**'
pull_request:
branches: [main]
paths-ignore:
- 'README.md'
- 'docs/**'
permissions:
issues: write
pull-requests: write
contents: write
jobs:
build:
runs-on: ubuntu-latest
outputs:
issue-number: ${{ steps.vars.outputs.issue-number }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
cache: npm
- run: npm ci
- run: npm run build
- run: npm run format-check
- run: npm run lint
- run: npm run test
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist
- uses: actions/upload-artifact@v4
with:
name: action.yml
path: action.yml
- id: vars
run: |
if [[ "${{ github.event_name }}" == "pull_request" ]]; then \
echo "issue-number=${{ github.event.number }}" >> $GITHUB_OUTPUT; \
else \
echo "issue-number=1" >> $GITHUB_OUTPUT; \
fi
test:
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
needs: [build]
runs-on: ubuntu-latest
strategy:
matrix:
target: [built, committed]
steps:
- uses: actions/checkout@v4
- if: matrix.target == 'built' || github.event_name == 'pull_request'
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- if: matrix.target == 'built' || github.event_name == 'pull_request'
uses: actions/download-artifact@v4
with:
name: action.yml
path: .
- name: Test create comment
uses: ./
id: couc
with:
issue-number: ${{ needs.build.outputs.issue-number }}
body: |
This is a multi-line test comment
- With GitHub **Markdown** :sparkles:
- Created by [create-or-update-comment][1]
[1]: https://github.com/peter-evans/create-or-update-comment
reactions: '+1'
- name: Test update comment
uses: ./
with:
comment-id: ${{ steps.couc.outputs.comment-id }}
body: |
**Edit:** Some additional info
reactions: eyes
reactions-edit-mode: replace
- name: Test add reactions
uses: ./
with:
comment-id: ${{ steps.couc.outputs.comment-id }}
reactions: |
heart
hooray
laugh
- name: Test create comment from file
uses: ./
id: couc2
with:
issue-number: ${{ needs.build.outputs.issue-number }}
body-path: .github/comment-body.md
reactions: |
+1
- name: Test update comment from file
uses: ./
with:
comment-id: ${{ steps.couc2.outputs.comment-id }}
body-path: .github/comment-body-addition.md
append-separator: space
reactions: eyes, rocket
reactions-edit-mode: replace
package:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
commit-message: Update distribution
title: Update distribution
body: |
- Updates the distribution for changes on `main`
Auto-generated by [create-pull-request][1]
[1]: https://github.com/peter-evans/create-pull-request
branch: update-distribution

28
.github/workflows/create-comment.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Create Comment
on:
push:
branches:
- master
jobs:
createComment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# For debugging purposes. Remove this in production.
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Create comment
uses: ./
with:
issue-number: 1
body: |
This is a multi-line test comment
- With GitHub **Markdown**
- Created by [create-or-update-comment][1]
[1]: https://github.com/peter-evans/create-or-update-comment
reaction-type: '+1'

View File

@ -1,26 +0,0 @@
name: Slash Command Dispatch
on:
issue_comment:
types: [created]
jobs:
slashCommandDispatch:
runs-on: ubuntu-latest
steps:
- name: Slash Command Dispatch
uses: peter-evans/slash-command-dispatch@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
config: >
[
{
"command": "rebase",
"permission": "admin",
"repository": "peter-evans/slash-command-dispatch-processor",
"issue_type": "pull-request"
},
{
"command": "test",
"permission": "admin",
"named_args": true
}
]

View File

@ -1,85 +0,0 @@
name: Test Command
on:
repository_dispatch:
types: [test-command]
jobs:
testCreateOrUpdateComment:
runs-on: ubuntu-latest
steps:
# Get the target repository and branch
- name: Get the target repository and branch
id: vars
run: |
repository=${{ github.event.client_payload.slash_command.repository }}
if [[ -z "$repository" ]]; then repository=${{ github.repository }}; fi
echo "repository=$repository" >> $GITHUB_OUTPUT
branch=${{ github.event.client_payload.slash_command.branch }}
if [[ -z "$branch" ]]; then branch="main"; fi
echo "branch=$branch" >> $GITHUB_OUTPUT
# Checkout the branch to test
- uses: actions/checkout@v4
with:
repository: ${{ steps.vars.outputs.repository }}
ref: ${{ steps.vars.outputs.branch }}
# Test create
- name: Create comment
uses: ./
id: couc
with:
issue-number: 1
body: |
This is a multi-line test comment
- With GitHub **Markdown** :sparkles:
- Created by [create-or-update-comment][1]
[1]: https://github.com/peter-evans/create-or-update-comment
reactions: '+1'
# Test update
- name: Update comment
uses: ./
with:
comment-id: ${{ steps.couc.outputs.comment-id }}
body: |
**Edit:** Some additional info
reactions: eyes
reactions-edit-mode: replace
# Test add reactions
- name: Add reactions
uses: ./
with:
comment-id: ${{ steps.couc.outputs.comment-id }}
reactions: heart, hooray, laugh
# Test create with body from file
- name: Create comment
uses: ./
with:
issue-number: 1
body-path: .github/comment-body.md
# Test create from template
- name: Render template
id: template
uses: chuhlomin/render-template@v1.10
with:
template: .github/comment-template.md
vars: |
foo: this
bar: that
- name: Create comment
uses: ./
with:
issue-number: 1
body: ${{ steps.template.outputs.result }}
- name: Add reaction
uses: peter-evans/create-or-update-comment@v4
with:
repository: ${{ github.event.client_payload.github.payload.repository.full_name }}
comment-id: ${{ github.event.client_payload.github.payload.comment.id }}
reactions: hooray

View File

@ -1,66 +0,0 @@
name: Test v3
on: workflow_dispatch
jobs:
testCreateOrUpdateComment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Test create
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
id: couc
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
issue-number: 1
body: |
This is a multi-line test comment
- With GitHub **Markdown** :sparkles:
- Created by [create-or-update-comment][1]
[1]: https://github.com/peter-evans/create-or-update-comment
reactions: '+1'
# Test update
- name: Update comment
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
comment-id: ${{ steps.couc.outputs.comment-id }}
body: |
**Edit:** Some additional info
reactions: eyes
reactions-edit-mode: replace
# Test add reactions
- name: Add reactions
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
comment-id: ${{ steps.couc.outputs.comment-id }}
reactions: heart, hooray, laugh
# Test create with body from file
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
issue-number: 1
body-file: .github/comment-body.md
# Test create from template
- name: Render template
id: template
uses: chuhlomin/render-template@v1.10
with:
template: .github/comment-template.md
vars: |
foo: this
bar: that
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
issue-number: 1
body: ${{ steps.template.outputs.result }}

31
.github/workflows/update-comment.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: Update Comment
on:
issue_comment:
types: [created]
jobs:
updateComment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# For debugging purposes. Remove this in production.
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Update comment
if: github.event.issue.number == 1
uses: ./
with:
comment-id: ${{ github.event.comment.id }}
body: |
**Edit:** Some additional info
reaction-type: eyes
- name: Add reaction
if: github.event.issue.number == 1
uses: ./
with:
comment-id: ${{ github.event.comment.id }}
reaction-type: heart

View File

@ -1,32 +0,0 @@
name: Update Major Version
run-name: Update ${{ github.event.inputs.main_version }} to ${{ github.event.inputs.target }}
on:
workflow_dispatch:
inputs:
target:
description: The target tag or reference
required: true
main_version:
type: choice
description: The major version tag to update
options:
- v3
- v4
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.ACTIONS_BOT_TOKEN }}
fetch-depth: 0
- name: Git config
run: |
git config user.name actions-bot
git config user.email actions-bot@users.noreply.github.com
- name: Tag new target
run: git tag -f ${{ github.event.inputs.main_version }} ${{ github.event.inputs.target }}
- name: Push new tag
run: git push origin ${{ github.event.inputs.main_version }} --force

3
.gitignore vendored
View File

@ -1,2 +1 @@
lib/
node_modules/
node_modules

View File

@ -1,3 +0,0 @@
dist/
lib/
node_modules/

View File

@ -1,11 +0,0 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid",
"parser": "typescript"
}

153
README.md
View File

@ -1,88 +1,61 @@
# Create or Update Comment
[![CI](https://github.com/peter-evans/create-or-update-comment/workflows/CI/badge.svg)](https://github.com/peter-evans/create-or-update-comment/actions?query=workflow%3ACI)
[![GitHub Marketplace](https://img.shields.io/badge/Marketplace-Create%20or%20Update%20Comment-blue.svg?colorA=24292e&colorB=0366d6&style=flat&longCache=true&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAM6wAADOsB5dZE0gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAERSURBVCiRhZG/SsMxFEZPfsVJ61jbxaF0cRQRcRJ9hlYn30IHN/+9iquDCOIsblIrOjqKgy5aKoJQj4O3EEtbPwhJbr6Te28CmdSKeqzeqr0YbfVIrTBKakvtOl5dtTkK+v4HfA9PEyBFCY9AGVgCBLaBp1jPAyfAJ/AAdIEG0dNAiyP7+K1qIfMdonZic6+WJoBJvQlvuwDqcXadUuqPA1NKAlexbRTAIMvMOCjTbMwl1LtI/6KWJ5Q6rT6Ht1MA58AX8Apcqqt5r2qhrgAXQC3CZ6i1+KMd9TRu3MvA3aH/fFPnBodb6oe6HM8+lYHrGdRXW8M9bMZtPXUji69lmf5Cmamq7quNLFZXD9Rq7v0Bpc1o/tp0fisAAAAASUVORK5CYII=)](https://github.com/marketplace/actions/create-or-update-comment)
A GitHub action to create or update an issue or pull request comment.
This action was created to help facilitate a GitHub Actions "ChatOps" solution in conjunction with [slash-command-dispatch](https://github.com/peter-evans/slash-command-dispatch) action.
## Usage
### Add a comment to an issue or pull request
```yml
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
uses: peter-evans/create-or-update-comment@v1
with:
issue-number: 1
body: |
This is a multi-line test comment
- With GitHub **Markdown** :sparkles:
- With GitHub **Markdown**
- Created by [create-or-update-comment][1]
[1]: https://github.com/peter-evans/create-or-update-comment
reactions: '+1'
reaction-type: '+1'
```
### Update a comment
```yml
- name: Update comment
uses: peter-evans/create-or-update-comment@v4
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: 557858210
body: |
**Edit:** Some additional info
reactions: eyes
reaction-type: eyes
```
### Add comment reactions
### Add a comment reaction
```yml
- name: Add reactions
uses: peter-evans/create-or-update-comment@v4
- name: Add reaction
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: 557858210
reactions: |
heart
hooray
laugh
reaction-type: heart
```
### Action inputs
| Name | Description | Default |
| --- | --- | --- |
| `token` | `GITHUB_TOKEN` (`issues: write`, `pull-requests: write`) or a `repo` scoped [PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). | `GITHUB_TOKEN` |
| `token` | `GITHUB_TOKEN` or a `repo` scoped [PAT](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line). | `GITHUB_TOKEN` |
| `repository` | The full name of the repository in which to create or update a comment. | Current repository |
| `issue-number` | The number of the issue or pull request in which to create a comment. | |
| `comment-id` | The id of the comment to update. | |
| `body` | The comment body. Cannot be used in conjunction with `body-path`. | |
| `body-path` | The path to a file containing the comment body. Cannot be used in conjunction with `body`. | |
| `body` | The comment body. | |
| `edit-mode` | The mode when updating a comment, `replace` or `append`. | `append` |
| `append-separator` | The separator to use when appending to an existing comment. (`newline`, `space`, `none`) | `newline` |
| `reactions` | A comma or newline separated list of reactions to add to the comment. (`+1`, `-1`, `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`) | |
| `reactions-edit-mode` | The mode when updating comment reactions, `replace` or `append`. | `append` |
Note: In *public* repositories this action does not work in `pull_request` workflows when triggered by forks.
Any attempt will be met with the error, `Resource not accessible by integration`.
This is due to token restrictions put in place by GitHub Actions. Private repositories can be configured to [enable workflows](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#enabling-workflows-for-forks-of-private-repositories) from forks to run without restriction. See [here](https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#restrictions-on-repository-forks) for further explanation. Alternatively, use the [`pull_request_target`](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target) event to comment on pull requests.
#### Outputs
The ID of the created comment will be output for use in later steps.
Note that in order to read the step output the action step must have an id.
```yml
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
id: couc
with:
issue-number: 1
body: |
My comment
- name: Check outputs
run: |
echo "Comment ID - ${{ steps.couc.outputs.comment-id }}"
```
| `reaction-type` | The reaction to add to the comment. (`+1`, `-1`, `laugh`, `confused`, `heart`, `hooray`, `rocket`, `eyes`) | |
### Where to find the id of a comment
@ -98,107 +71,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Add reaction
uses: peter-evans/create-or-update-comment@v4
uses: peter-evans/create-or-update-comment@v1
with:
comment-id: ${{ github.event.comment.id }}
reactions: eyes
```
Some use cases might find the [find-comment](https://github.com/peter-evans/find-comment) action useful.
This will search an issue or pull request for the first comment containing a specified string, and/or by a specified author.
See the repository for detailed usage.
In the following example, find-comment is used to determine if a comment has already been created on a pull request.
If the find-comment action output `comment-id` returns an empty string, a new comment will be created.
If it returns a value, the comment already exists and the content is replaced.
```yml
- name: Find Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Build output
- name: Create or update comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
Build output
${{ steps.build.outputs.build-log }}
edit-mode: replace
```
If required, the create and update steps can be separated for greater control.
```yml
- name: Find Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: This comment was written by a bot!
- name: Create comment
if: steps.fc.outputs.comment-id == ''
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
This comment was written by a bot!
reactions: rocket
- name: Update comment
if: steps.fc.outputs.comment-id != ''
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
body: |
This comment has been updated!
reactions: hooray
```
### Setting the comment body from a file
```yml
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: 1
body-path: 'comment-body.md'
```
### Using a markdown template
In this example, a markdown template file is added to the repository at `.github/comment-template.md` with the following content.
```
This is a test comment template
Render template variables such as {{ .foo }} and {{ .bar }}.
```
The template is rendered using the [render-template](https://github.com/chuhlomin/render-template) action and the result is used to create the comment.
```yml
- name: Render template
id: template
uses: chuhlomin/render-template@v1.4
with:
template: .github/comment-template.md
vars: |
foo: this
bar: that
- name: Create comment
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: 1
body: ${{ steps.template.outputs.result }}
reaction-type: eyes
```
### Accessing issues and comments in other repositories
You can create and update comments in another repository by using a [PAT](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) instead of `GITHUB_TOKEN`.
You can create and update comments in another repository by using a [PAT](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) instead of `GITHUB_TOKEN`.
The user associated with the PAT must have write access to the repository.
## License

View File

@ -6,33 +6,18 @@ inputs:
default: ${{ github.token }}
repository:
description: 'The full name of the repository in which to create or update a comment.'
default: ${{ github.repository }}
issue-number:
description: 'The number of the issue or pull request in which to create a comment.'
comment-id:
description: 'The id of the comment to update.'
body:
description: 'The comment body. Cannot be used in conjunction with `body-path`.'
body-path:
description: 'The path to a file containing the comment body. Cannot be used in conjunction with `body`.'
body-file:
description: 'Deprecated in favour of `body-path`.'
description: 'The comment body.'
edit-mode:
description: 'The mode when updating a comment, "replace" or "append".'
default: 'append'
append-separator:
description: 'The separator to use when appending to an existing comment. (`newline`, `space`, `none`)'
default: 'newline'
reactions:
description: 'A comma or newline separated list of reactions to add to the comment.'
reactions-edit-mode:
description: 'The mode when updating comment reactions, "replace" or "append".'
default: 'append'
outputs:
comment-id:
description: 'The id of the created comment'
reaction-type:
description: 'The reaction to add to the comment.'
runs:
using: 'node20'
using: 'node12'
main: 'dist/index.js'
branding:
icon: 'message-square'

42752
dist/index.js vendored

File diff suppressed because one or more lines are too long

121
index.js Normal file
View File

@ -0,0 +1,121 @@
const { inspect } = require("util");
const core = require("@actions/core");
const github = require("@actions/github");
const REACTION_TYPES = [
"+1",
"-1",
"laugh",
"confused",
"heart",
"hooray",
"rocket",
"eyes"
];
async function addReaction(octokit, repo, comment_id, reactionType) {
if (REACTION_TYPES.includes(reactionType)) {
await octokit.reactions.createForIssueComment({
owner: repo[0],
repo: repo[1],
comment_id: comment_id,
content: reactionType
});
core.info(`Set '${reactionType}' reaction on comment.`);
} else {
core.setFailed("Invalid 'reaction-type'.");
return;
}
}
async function run() {
try {
const inputs = {
token: core.getInput("token"),
repository: core.getInput("repository"),
issueNumber: core.getInput("issue-number"),
commentId: core.getInput("comment-id"),
body: core.getInput("body"),
editMode: core.getInput("edit-mode"),
reactionType: core.getInput("reaction-type")
};
core.debug(`Inputs: ${inspect(inputs)}`);
const repository = inputs.repository
? inputs.repository
: process.env.GITHUB_REPOSITORY;
const repo = repository.split("/");
core.debug(`repository: ${repository}`);
const editMode = inputs.editMode ? inputs.editMode : "append";
core.debug(`editMode: ${editMode}`);
if (!["append", "replace"].includes(editMode)) {
core.setFailed(`Invalid edit-mode '${editMode}'.`);
return;
}
const octokit = new github.GitHub(inputs.token);
if (inputs.commentId) {
// Edit a comment
if (!inputs.body && !inputs.reactionType) {
core.setFailed("Missing either comment 'body' or 'reaction-type'.");
return;
}
if (inputs.body) {
var commentBody = "";
if (editMode == "append") {
// Get the comment body
const { data: comment } = await octokit.issues.getComment({
owner: repo[0],
repo: repo[1],
comment_id: inputs.commentId
});
commentBody = comment.body + "\n";
}
commentBody = commentBody + inputs.body;
core.debug(`Comment body: ${commentBody}`);
await octokit.issues.updateComment({
owner: repo[0],
repo: repo[1],
comment_id: inputs.commentId,
body: commentBody
});
core.info(`Updated comment id '${inputs.commentId}'.`);
}
// Set a comment reaction
if (inputs.reactionType) {
await addReaction(octokit, repo, inputs.commentId, inputs.reactionType);
}
} else if (inputs.issueNumber) {
// Create a comment
if (!inputs.body) {
core.setFailed("Missing comment 'body'.");
return;
}
const { data: comment } = await octokit.issues.createComment({
owner: repo[0],
repo: repo[1],
issue_number: inputs.issueNumber,
body: inputs.body
});
core.info(`Created comment on issue '${inputs.issueNumber}'.`);
// Set a comment reaction
if (inputs.reactionType) {
await addReaction(octokit, repo, comment.id, inputs.reactionType);
}
} else {
core.setFailed("Missing either 'issue-number' or 'comment-id'.");
return;
}
} catch (error) {
core.debug(inspect(error));
core.setFailed(error.message);
}
}
run();

View File

@ -1,11 +0,0 @@
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
testRunner: 'jest-circus/runner',
transform: {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}

12338
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,18 @@
{
"name": "create-or-update-comment",
"version": "4.0.0",
"version": "1.0.0",
"description": "Create or update an issue or pull request comment",
"main": "lib/main.js",
"main": "index.js",
"scripts": {
"build": "tsc && ncc build",
"format": "prettier --write '**/*.ts'",
"format-check": "prettier --check '**/*.ts'",
"lint": "eslint src/**/*.ts",
"test": "jest --passWithNoTests"
"lint": "eslint index.js",
"package": "ncc build index.js -o dist",
"test": "eslint index.js && jest --passWithNoTests"
},
"repository": {
"type": "git",
"url": "git+https://github.com/peter-evans/create-or-update-comment.git"
},
"keywords": [
"actions",
"create",
"update",
"comment"
],
"keywords": [],
"author": "Peter Evans",
"license": "MIT",
"bugs": {
@ -27,24 +20,12 @@
},
"homepage": "https://github.com/peter-evans/create-or-update-comment#readme",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/github": "^6.0.0"
"@actions/core": "^1.1.1",
"@actions/github": "^2.0.0"
},
"devDependencies": {
"@types/jest": "^27.0.3",
"@types/node": "^18.19.86",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"@vercel/ncc": "^0.38.3",
"eslint": "^8.57.1",
"eslint-plugin-github": "^4.10.2",
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-prettier": "^5.2.6",
"jest": "^27.5.1",
"jest-circus": "^27.5.1",
"js-yaml": "^4.1.0",
"prettier": "^3.5.3",
"ts-jest": "^27.1.5",
"typescript": "^4.9.5"
"@zeit/ncc": "0.22.0",
"eslint": "6.8.0",
"jest": "25.2.7"
}
}

5
renovate.json Normal file
View File

@ -0,0 +1,5 @@
{
"extends": [
"config:base"
]
}

View File

@ -1,282 +0,0 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import * as utils from './utils'
import {inspect} from 'util'
export interface Inputs {
token: string
repository: string
issueNumber: number
commentId: number
body: string
bodyPath: string
editMode: string
appendSeparator: string
reactions: string[]
reactionsEditMode: string
}
const REACTION_TYPES = [
'+1',
'-1',
'laugh',
'confused',
'heart',
'hooray',
'rocket',
'eyes'
]
function getReactionsSet(reactions: string[]): string[] {
const reactionsSet = [
...new Set(
reactions.filter(item => {
if (!REACTION_TYPES.includes(item)) {
core.warning(`Skipping invalid reaction '${item}'.`)
return false
}
return true
})
)
]
if (!reactionsSet) {
throw new Error(`No valid reactions are contained in '${reactions}'.`)
}
return reactionsSet
}
async function addReactions(
octokit,
owner: string,
repo: string,
commentId: number,
reactions: string[]
) {
const results = await Promise.allSettled(
reactions.map(async reaction => {
await octokit.rest.reactions.createForIssueComment({
owner: owner,
repo: repo,
comment_id: commentId,
content: reaction
})
core.info(`Setting '${reaction}' reaction on comment.`)
})
)
for (let i = 0, l = results.length; i < l; i++) {
if (results[i].status === 'fulfilled') {
core.info(
`Added reaction '${reactions[i]}' to comment id '${commentId}'.`
)
} else if (results[i].status === 'rejected') {
core.warning(
`Adding reaction '${reactions[i]}' to comment id '${commentId}' failed.`
)
}
}
}
async function removeReactions(
octokit,
owner: string,
repo: string,
commentId: number,
reactions: Reaction[]
) {
const results = await Promise.allSettled(
reactions.map(async reaction => {
await octokit.rest.reactions.deleteForIssueComment({
owner: owner,
repo: repo,
comment_id: commentId,
reaction_id: reaction.id
})
core.info(`Removing '${reaction.content}' reaction from comment.`)
})
)
for (let i = 0, l = results.length; i < l; i++) {
if (results[i].status === 'fulfilled') {
core.info(
`Removed reaction '${reactions[i].content}' from comment id '${commentId}'.`
)
} else if (results[i].status === 'rejected') {
core.warning(
`Removing reaction '${reactions[i].content}' from comment id '${commentId}' failed.`
)
}
}
}
function appendSeparatorTo(body: string, separator: string): string {
switch (separator) {
case 'newline':
return body + '\n'
case 'space':
return body + ' '
default: // none
return body
}
}
function truncateBody(body: string) {
// 65536 characters is the maximum allowed for issue comments.
const truncateWarning = '...*[Comment body truncated]*'
if (body.length > 65536) {
core.warning(`Comment body is too long. Truncating to 65536 characters.`)
return body.substring(0, 65536 - truncateWarning.length) + truncateWarning
}
return body
}
async function createComment(
octokit,
owner: string,
repo: string,
issueNumber: number,
body: string
): Promise<number> {
body = truncateBody(body)
const {data: comment} = await octokit.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: issueNumber,
body
})
core.info(`Created comment id '${comment.id}' on issue '${issueNumber}'.`)
return comment.id
}
async function updateComment(
octokit,
owner: string,
repo: string,
commentId: number,
body: string,
editMode: string,
appendSeparator: string
): Promise<number> {
if (body) {
let commentBody = ''
if (editMode == 'append') {
// Get the comment body
const {data: comment} = await octokit.rest.issues.getComment({
owner: owner,
repo: repo,
comment_id: commentId
})
commentBody = appendSeparatorTo(
comment.body ? comment.body : '',
appendSeparator
)
}
commentBody = truncateBody(commentBody + body)
core.debug(`Comment body: ${commentBody}`)
await octokit.rest.issues.updateComment({
owner: owner,
repo: repo,
comment_id: commentId,
body: commentBody
})
core.info(`Updated comment id '${commentId}'.`)
}
return commentId
}
async function getAuthenticatedUser(octokit): Promise<string> {
try {
const {data: user} = await octokit.rest.users.getAuthenticated()
return user.login
} catch (error) {
if (
utils
.getErrorMessage(error)
.includes('Resource not accessible by integration')
) {
// In this case we can assume the token is the default GITHUB_TOKEN and
// therefore the user is 'github-actions[bot]'.
return 'github-actions[bot]'
} else {
throw error
}
}
}
type Reaction = {
id: number
content: string
}
async function getCommentReactionsForUser(
octokit,
owner: string,
repo: string,
commentId: number,
user: string
): Promise<Reaction[]> {
const userReactions: Reaction[] = []
for await (const {data: reactions} of octokit.paginate.iterator(
octokit.rest.reactions.listForIssueComment,
{
owner,
repo,
comment_id: commentId,
per_page: 100
}
)) {
const filteredReactions: Reaction[] = reactions
.filter(reaction => reaction.user.login === user)
.map(reaction => {
return {id: reaction.id, content: reaction.content}
})
userReactions.push(...filteredReactions)
}
return userReactions
}
export async function createOrUpdateComment(
inputs: Inputs,
body: string
): Promise<void> {
const [owner, repo] = inputs.repository.split('/')
const octokit = github.getOctokit(inputs.token)
const commentId = inputs.commentId
? await updateComment(
octokit,
owner,
repo,
inputs.commentId,
body,
inputs.editMode,
inputs.appendSeparator
)
: await createComment(octokit, owner, repo, inputs.issueNumber, body)
core.setOutput('comment-id', commentId)
if (inputs.reactions) {
const reactionsSet = getReactionsSet(inputs.reactions)
// Remove reactions if reactionsEditMode is 'replace'
if (inputs.commentId && inputs.reactionsEditMode === 'replace') {
const authenticatedUser = await getAuthenticatedUser(octokit)
const userReactions = await getCommentReactionsForUser(
octokit,
owner,
repo,
commentId,
authenticatedUser
)
core.debug(inspect(userReactions))
const reactionsToRemove = userReactions.filter(
reaction => !reactionsSet.includes(reaction.content)
)
await removeReactions(octokit, owner, repo, commentId, reactionsToRemove)
}
await addReactions(octokit, owner, repo, commentId, reactionsSet)
}
}

View File

@ -1,82 +0,0 @@
import * as core from '@actions/core'
import {Inputs, createOrUpdateComment} from './create-or-update-comment'
import {existsSync, readFileSync} from 'fs'
import {inspect} from 'util'
import * as utils from './utils'
function getBody(inputs: Inputs) {
if (inputs.body) {
return inputs.body
} else if (inputs.bodyPath) {
return readFileSync(inputs.bodyPath, 'utf-8')
} else {
return ''
}
}
async function run(): Promise<void> {
try {
const inputs: Inputs = {
token: core.getInput('token'),
repository: core.getInput('repository'),
issueNumber: Number(core.getInput('issue-number')),
commentId: Number(core.getInput('comment-id')),
body: core.getInput('body'),
bodyPath: core.getInput('body-path') || core.getInput('body-file'),
editMode: core.getInput('edit-mode'),
appendSeparator: core.getInput('append-separator'),
reactions: utils.getInputAsArray('reactions'),
reactionsEditMode: core.getInput('reactions-edit-mode')
}
core.debug(`Inputs: ${inspect(inputs)}`)
if (!['append', 'replace'].includes(inputs.editMode)) {
throw new Error(`Invalid edit-mode '${inputs.editMode}'.`)
}
if (!['append', 'replace'].includes(inputs.reactionsEditMode)) {
throw new Error(
`Invalid reactions edit-mode '${inputs.reactionsEditMode}'.`
)
}
if (!['newline', 'space', 'none'].includes(inputs.appendSeparator)) {
throw new Error(`Invalid append-separator '${inputs.appendSeparator}'.`)
}
if (inputs.bodyPath && inputs.body) {
throw new Error("Only one of 'body' or 'body-path' can be set.")
}
if (inputs.bodyPath) {
if (!existsSync(inputs.bodyPath)) {
throw new Error(`File '${inputs.bodyPath}' does not exist.`)
}
}
const body = getBody(inputs)
if (inputs.commentId) {
if (!body && !inputs.reactions) {
throw new Error("Missing comment 'body', 'body-path', or 'reactions'.")
}
} else if (inputs.issueNumber) {
if (!body) {
throw new Error("Missing comment 'body' or 'body-path'.")
}
} else {
throw new Error("Missing either 'issue-number' or 'comment-id'.")
}
createOrUpdateComment(inputs, body)
} catch (error) {
core.debug(inspect(error))
const errMsg = utils.getErrorMessage(error)
core.setFailed(errMsg)
if (errMsg == 'Resource not accessible by integration') {
core.error(`See this action's readme for details about this error`)
}
}
}
run()

View File

@ -1,20 +0,0 @@
import * as core from '@actions/core'
export function getInputAsArray(
name: string,
options?: core.InputOptions
): string[] {
return getStringAsArray(core.getInput(name, options))
}
export function getStringAsArray(str: string): string[] {
return str
.split(/[\n,]+/)
.map(s => s.trim())
.filter(x => x !== '')
}
export function getErrorMessage(error: unknown) {
if (error instanceof Error) return error.message
return String(error)
}

View File

@ -1,16 +0,0 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": [
"es6"
],
"outDir": "./lib",
"rootDir": "./src",
"declaration": true,
"strict": true,
"noImplicitAny": false,
"esModuleInterop": true
},
"exclude": ["__test__", "lib", "node_modules"]
}