Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/approve-preset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,13 @@ jobs:

- name: Comment result
if: always() && hashFiles('report.md') != ''
env:
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: gh issue comment "$ISSUE_NUMBER" --body-file report.md
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ secrets.GH_BOT_TOKEN }}
script: |
const fs = require('node:fs')
const {upsertIssueComment} = require('./src/issue-comment.js')
await upsertIssueComment({github, context, body: fs.readFileSync('report.md', 'utf8')})

- name: Close issue
if: steps.publish.outcome == 'success'
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/check-preset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,13 @@ jobs:

- name: Post result
if: always() && hashFiles('report.md') != ''
env:
GH_TOKEN: ${{ github.token }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: gh issue comment "$ISSUE_NUMBER" --body-file report.md
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ secrets.GH_BOT_TOKEN }}
script: |
const fs = require('node:fs')
const {upsertIssueComment} = require('./src/issue-comment.js')
await upsertIssueComment({github, context, body: fs.readFileSync('report.md', 'utf8')})

- name: Update issue title
if: steps.validate.outcome == 'success'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"scripts": {
"test": "node --test tests/*.test.js",
"lint": "node --check src/presets.js && node --check src/record.js && node --check src/migrate-database.js && node --check src/database.js && node --check src/issue.js && node --check src/build-site.js && node --check src/statistics.js && node --check src/approval-queue.js && node --check src/comment-command.js && node --check src/verify-approval.js && node --check src/workflow-queue.js && node --check gh-pages-template/assets/js/app.js",
"lint": "node --check src/presets.js && node --check src/record.js && node --check src/migrate-database.js && node --check src/database.js && node --check src/issue.js && node --check src/issue-comment.js && node --check src/build-site.js && node --check src/statistics.js && node --check src/approval-queue.js && node --check src/comment-command.js && node --check src/verify-approval.js && node --check src/workflow-queue.js && node --check gh-pages-template/assets/js/app.js",
"test:ci": "node --test --experimental-test-coverage --test-reporter=junit --test-reporter=lcov --test-reporter-destination=junit.xml --test-reporter-destination=lcov.info tests/*.test.js"
}
}
25 changes: 25 additions & 0 deletions src/issue-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';

const MARKER = '<!-- PresetDB status -->';
const LEGACY_REPORT = /^(?:Preset (?:request|replacement) validated for |Preset validation failed: |Approval stopped: )/;

async function upsertIssueComment({ github, context, body }) {
const issue = { ...context.repo, issue_number: context.issue.number };
const { data: actor } = await github.rest.users.getAuthenticated();
const comments = await github.paginate(github.rest.issues.listComments, { ...issue, per_page: 100 });
const existing = comments.findLast(comment => comment.user?.id === actor.id &&
(comment.body?.includes(MARKER) || LEGACY_REPORT.test(comment.body || '')));
const markedBody = body.trimEnd() + '\n\n' + MARKER;

if (existing) {
if (existing.body !== markedBody) {
await github.rest.issues.updateComment({ ...context.repo, comment_id: existing.id, body: markedBody });
}
return existing.id;
}

const { data: created } = await github.rest.issues.createComment({ ...issue, body: markedBody });
return created.id;
}

module.exports = { MARKER, upsertIssueComment };
69 changes: 69 additions & 0 deletions tests/issue-comment.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict';

const test = require('node:test');
const assert = require('node:assert/strict');
const { MARKER, upsertIssueComment } = require('../src/issue-comment');

function fixture(initialComments = []) {
const comments = initialComments.map(comment => ({ ...comment }));
const actions = [];
const github = {
paginate: async (_endpoint, params) => {
assert.equal(params.issue_number, 6);
return comments;
},
rest: {
users: { getAuthenticated: async () => ({ data: { id: 42 } }) },
issues: {
listComments() {},
createComment: async ({ body }) => {
actions.push('create');
const created = { id: 100, user: { id: 42 }, body };
comments.push(created);
return { data: created };
},
updateComment: async ({ comment_id, body }) => {
actions.push('update');
const comment = comments.find(item => item.id === comment_id);
comment.body = body;
return { data: comment };
}
}
}
};
const context = { repo: { owner: 'LizardByte', repo: 'PresetDB' }, issue: { number: 6 } };
return { github, context, comments, actions };
}

test('approval replaces the bot validation comment instead of creating a second one', async () => {
const state = fixture([{ id: 1, user: { id: 99 }, body: MARKER + '\nOther user comment' }]);
await upsertIssueComment({ ...state, body: 'Status: awaiting maintainer review\n' });
await upsertIssueComment({ ...state, body: 'Status: approved and saved\n' });

assert.deepEqual(state.actions, ['create', 'update']);
assert.equal(state.comments.length, 2);
assert.equal(state.comments[0].body, MARKER + '\nOther user comment');
assert.match(state.comments[1].body, /Status: approved and saved/);
assert.ok(state.comments[1].body.endsWith(MARKER));
});

test('an older bot report is reused and an unchanged result is not posted again', async () => {
const state = fixture([{ id: 9, user: { id: 42 }, body: 'Preset request validated for GameDB game 1164.' }]);
const body = 'Preset request validated for GameDB game 1164.\n\n- Status: approved and saved\n';
await upsertIssueComment({ ...state, body });
await upsertIssueComment({ ...state, body });

assert.deepEqual(state.actions, ['update']);
assert.equal(state.comments.length, 1);
assert.ok(state.comments[0].body.endsWith(MARKER));
});
test('the next check after an issue edit updates the original status comment', async () => {
const state = fixture();
await upsertIssueComment({ ...state, body: 'Preset validation failed: Invalid command\n' });
await upsertIssueComment({ ...state, body: 'Preset request validated for GameDB game 1164.\n' });

assert.deepEqual(state.actions, ['create', 'update']);
assert.equal(state.comments.length, 1);
assert.match(state.comments[0].body, /Preset request validated/);
assert.doesNotMatch(state.comments[0].body, /validation failed/);
});
Loading