diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 811828be..78e8ad04 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,8 +8,11 @@ jobs: - name: Checkout uses: actions/checkout@v1 - - name: Comment + - name: Comment PR uses: ./ with: - message: 'Current branch is `${{ github.head_ref }}`.' + message: | + Current branch is `${{ github.head_ref }}`. + _(execution **${{ github.run_id }}** / attempt **${{ github.run_attempt }}**)_ + comment_includes: Current branch GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 98dbafeb..b880da6e 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,41 @@ That is particularly useful for manual workflow for instance (`workflow_run`). GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ``` + +### Upsert a comment + +Editing an existing comment is also possible thanks to the `comment_includes` property. +It will search through all the comments of the PR and get the first one that has the `comment_includes` text in it. +If the comment body is not found, it will create a new comment. +That is particularly interesting while committing multiple times in a PR and that you just want to have the last execution report to avoid flooding the PR. + +``` +... +- name: Comment PR + uses: thollander/actions-comment-pull-request@v1 + with: + message: 'Loading ...' + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +... +- name: Edit PR comment + uses: thollander/actions-comment-pull-request@v1 + with: + message: 'Content loaded ! (edited)' + comment_includes: 'Loading' + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +## Inputs + +### Action inputs + +| Name | Description | Default | +| --- | --- | --- | +| `GITHUB_TOKEN` | Token that is used to create comments | | +| `pr_number` | The number of the pull request where to create the comment | | +| `message` | The comment body | | +| `comment_includes` | The text that should be used in case of comment replacement. | | + ## Contributing ### Build diff --git a/action.yml b/action.yml index 5d5f36fb..016eae73 100644 --- a/action.yml +++ b/action.yml @@ -17,3 +17,5 @@ runs: - ${{ inputs.message }} - ${{ inputs.GITHUB_TOKEN }} - ${{ inputs.pr_number }} + - ${{ inputs.comment_includes }} + diff --git a/lib/main.js b/lib/main.js index f3c9225d..84c0cd4d 100644 --- a/lib/main.js +++ b/lib/main.js @@ -31,23 +31,57 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; Object.defineProperty(exports, "__esModule", { value: true }); const github = __importStar(require("@actions/github")); const core = __importStar(require("@actions/core")); function run() { - var _a; + var e_1, _a; + var _b; return __awaiter(this, void 0, void 0, function* () { try { const message = core.getInput('message'); const github_token = core.getInput('GITHUB_TOKEN'); const pr_number = core.getInput('pr_number'); + const comment_includes = core.getInput('comment_includes'); const context = github.context; - const pull_number = parseInt(pr_number) || ((_a = context.payload.pull_request) === null || _a === void 0 ? void 0 : _a.number); + const pull_number = parseInt(pr_number) || ((_b = context.payload.pull_request) === null || _b === void 0 ? void 0 : _b.number); const octokit = github.getOctokit(github_token); if (!pull_number) { core.setFailed('No pull request in input neither in current context.'); return; } + if (comment_includes) { + let comment; + try { + for (var _c = __asyncValues(octokit.paginate.iterator(octokit.rest.issues.listComments, Object.assign(Object.assign({}, context.repo), { issue_number: pull_number }))), _d; _d = yield _c.next(), !_d.done;) { + const { data: comments } = _d.value; + comment = comments.find((comment) => { var _a; return (_a = comment === null || comment === void 0 ? void 0 : comment.body) === null || _a === void 0 ? void 0 : _a.includes(comment_includes); }); + if (comment) + break; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_d && !_d.done && (_a = _c.return)) yield _a.call(_c); + } + finally { if (e_1) throw e_1.error; } + } + if (comment) { + yield octokit.rest.issues.updateComment(Object.assign(Object.assign({}, context.repo), { comment_id: comment.id, body: message })); + return; + } + else { + core.info('No comment has been found with asked pattern. Creating a new comment.'); + } + } yield octokit.rest.issues.createComment(Object.assign(Object.assign({}, context.repo), { issue_number: pull_number, body: message })); } catch (error) { diff --git a/src/main.ts b/src/main.ts index ad623cca..65145e4e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,11 +1,13 @@ import * as github from '@actions/github'; import * as core from '@actions/core'; +import { GetResponseDataTypeFromEndpointMethod } from '@octokit/types'; async function run() { try { const message: string = core.getInput('message'); const github_token: string = core.getInput('GITHUB_TOKEN'); const pr_number: string = core.getInput('pr_number'); + const comment_includes: string = core.getInput('comment_includes'); const context = github.context; const pull_number = parseInt(pr_number) || context.payload.pull_request?.number; @@ -16,6 +18,29 @@ async function run() { core.setFailed('No pull request in input neither in current context.'); return; } + + if (comment_includes) { + type ListCommentsResponseDataType = GetResponseDataTypeFromEndpointMethod; + let comment: ListCommentsResponseDataType[0] | undefined; + for await (const { data: comments } of octokit.paginate.iterator(octokit.rest.issues.listComments, { + ...context.repo, + issue_number: pull_number, + })) { + comment = comments.find((comment) => comment?.body?.includes(comment_includes)); + if (comment) break; + } + + if (comment) { + await octokit.rest.issues.updateComment({ + ...context.repo, + comment_id: comment.id, + body: message, + }); + return; + } else { + core.info('No comment has been found with asked pattern. Creating a new comment.'); + } + } await octokit.rest.issues.createComment({ ...context.repo,