From dfeeb31e7e0e8f1694b55b121c7eadbd669006a0 Mon Sep 17 00:00:00 2001 From: Fabio Bonelli Date: Thu, 11 Jun 2026 16:30:46 +0200 Subject: [PATCH 1/2] refactor: centralize vote deadline parsing --- dist/index.js | 32 +++++++++++++++++--------------- src/commands/voteEnd.ts | 24 ++++++++++-------------- src/commands/voteSync.test.ts | 13 ++++++++++++- src/commands/voteSync.ts | 9 +++++++-- 4 files changed, 46 insertions(+), 32 deletions(-) diff --git a/dist/index.js b/dist/index.js index 7a3a573..970e81d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -9401,18 +9401,15 @@ function run(context) { console.error('Can\'t find the bot comment where the voting is taking place'); return; } - const deadlineMatch = (_a = voteComment.body) === null || _a === void 0 ? void 0 : _a.match(//); - if (deadlineMatch) { - const deadline = new Date(deadlineMatch[1]); - if (deadline > new Date()) { - yield octokit_1.default.issues.createComment({ - owner, - repo, - issue_number: number, - body: `The vote is still open until ${deadline.toUTCString()}.`, - }); - return; - } + const deadline = (0, voteSync_1.resolveDeadline)((_a = voteComment.body) !== null && _a !== void 0 ? _a : ''); + if (deadline && deadline > new Date()) { + yield octokit_1.default.issues.createComment({ + owner, + repo, + issue_number: number, + body: `The vote is still open until ${deadline.toUTCString()}.`, + }); + return; } yield (0, voteSync_1.syncIssueVoteLog)(owner, repo, number); const reactions = yield octokit_1.default.reactions.listForIssueComment({ @@ -9558,10 +9555,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.syncIssueVoteLog = exports.buildUpdatedComment = exports.diffVotes = exports.parseStateMarker = void 0; +exports.syncIssueVoteLog = exports.buildUpdatedComment = exports.diffVotes = exports.parseStateMarker = exports.resolveDeadline = void 0; const config_1 = __nccwpck_require__(6373); const octokit_1 = __importDefault(__nccwpck_require__(6161)); const STATE_MARKER_RE = //; +const DEADLINE_MARKER_RE = //; +function resolveDeadline(body) { + const match = body.match(DEADLINE_MARKER_RE); + return match ? new Date(match[1]) : null; +} +exports.resolveDeadline = resolveDeadline; function parseStateMarker(body) { const match = body.match(STATE_MARKER_RE); if (!match || !match[1]) @@ -9646,8 +9649,7 @@ function syncIssueVoteLog(owner, repo, issueNumber, members) { })); const body = (_a = voteComment.body) !== null && _a !== void 0 ? _a : ''; const state = parseStateMarker(body); - const deadlineMatch = body.match(//); - const deadline = deadlineMatch ? new Date(deadlineMatch[1]) : null; + const deadline = resolveDeadline(body); const now = new Date(); const newLines = diffVotes(state, liveReactions, deadline, now); if (newLines.length === 0) diff --git a/src/commands/voteEnd.ts b/src/commands/voteEnd.ts index 0d161d0..f8272f5 100644 --- a/src/commands/voteEnd.ts +++ b/src/commands/voteEnd.ts @@ -5,7 +5,7 @@ import { BOT_USERNAME, MAINTAINERS_TEAM } from '../config'; import { reactToComment, commentToIssue, addLabels, removeLabel, hasLabel, } from '../bot'; -import { syncIssueVoteLog } from './voteSync'; +import { syncIssueVoteLog, resolveDeadline } from './voteSync'; import { LabelName } from '../labels'; import octokit from '../octokit'; @@ -116,19 +116,15 @@ export default async function run(context: Context) { return; } - const deadlineMatch = voteComment.body?.match(//); - if (deadlineMatch) { - const deadline = new Date(deadlineMatch[1]!); - if (deadline > new Date()) { - await octokit.issues.createComment({ - owner, - repo, - issue_number: number, - body: `The vote is still open until ${deadline.toUTCString()}.`, - }); - - return; - } + const deadline = resolveDeadline(voteComment.body ?? ''); + if (deadline && deadline > new Date()) { + await octokit.issues.createComment({ + owner, + repo, + issue_number: number, + body: `The vote is still open until ${deadline.toUTCString()}.`, + }); + return; } await syncIssueVoteLog(owner, repo, number); diff --git a/src/commands/voteSync.test.ts b/src/commands/voteSync.test.ts index cb1e50b..c0d678c 100644 --- a/src/commands/voteSync.test.ts +++ b/src/commands/voteSync.test.ts @@ -1,4 +1,15 @@ -import { parseStateMarker, diffVotes, buildUpdatedComment } from './voteSync'; +import { + parseStateMarker, diffVotes, buildUpdatedComment, resolveDeadline, +} from './voteSync'; + +test('resolveDeadline reads the marker when present', () => { + const body = 'body\n\n'; + expect(resolveDeadline(body)).toEqual(new Date('2026-06-25T07:00:00.000Z')); +}); + +test('resolveDeadline returns null when the marker is absent', () => { + expect(resolveDeadline('no marker here')).toBeNull(); +}); test('parseStateMarker returns empty object when marker is absent', () => { expect(parseStateMarker('\nsome body')).toEqual({}); diff --git a/src/commands/voteSync.ts b/src/commands/voteSync.ts index 12efbc5..7d13a96 100644 --- a/src/commands/voteSync.ts +++ b/src/commands/voteSync.ts @@ -15,6 +15,12 @@ export interface LiveReaction { } const STATE_MARKER_RE = //; +const DEADLINE_MARKER_RE = //; + +export function resolveDeadline(body: string): Date | null { + const match = body.match(DEADLINE_MARKER_RE); + return match ? new Date(match[1]!) : null; +} export function parseStateMarker(body: string): VoteState { const match = body.match(STATE_MARKER_RE); @@ -128,8 +134,7 @@ export async function syncIssueVoteLog( const body = voteComment.body ?? ''; const state = parseStateMarker(body); - const deadlineMatch = body.match(//); - const deadline = deadlineMatch ? new Date(deadlineMatch[1]!) : null; + const deadline = resolveDeadline(body); const now = new Date(); const newLines = diffVotes(state, liveReactions, deadline, now); From 89a42a9b183cca8dcc7b110684af4a1a2744dd47 Mon Sep 17 00:00:00 2001 From: Fabio Bonelli Date: Thu, 11 Jun 2026 16:32:01 +0200 Subject: [PATCH 2/2] feat: reconstruct vote deadline for legacy vote-starts Votes opened before the deadline marker feature have no marker, so the after the deadline log note and the early vote-end refusal were silently skipped for them. Rebuild the deadline from the vote comment's creation time plus the voting period, same as when the vote opens. --- dist/index.js | 14 +++++++++----- src/commands/voteEnd.ts | 4 ++-- src/commands/voteSync.test.ts | 8 +++++--- src/commands/voteSync.ts | 12 ++++++++---- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/dist/index.js b/dist/index.js index 970e81d..1bab836 100644 --- a/dist/index.js +++ b/dist/index.js @@ -9401,8 +9401,8 @@ function run(context) { console.error('Can\'t find the bot comment where the voting is taking place'); return; } - const deadline = (0, voteSync_1.resolveDeadline)((_a = voteComment.body) !== null && _a !== void 0 ? _a : ''); - if (deadline && deadline > new Date()) { + const deadline = (0, voteSync_1.resolveDeadline)((_a = voteComment.body) !== null && _a !== void 0 ? _a : '', voteComment.created_at); + if (deadline > new Date()) { yield octokit_1.default.issues.createComment({ owner, repo, @@ -9560,9 +9560,13 @@ const config_1 = __nccwpck_require__(6373); const octokit_1 = __importDefault(__nccwpck_require__(6161)); const STATE_MARKER_RE = //; const DEADLINE_MARKER_RE = //; -function resolveDeadline(body) { +function resolveDeadline(body, commentCreatedAt) { const match = body.match(DEADLINE_MARKER_RE); - return match ? new Date(match[1]) : null; + if (match) + return new Date(match[1]); + const deadline = new Date(commentCreatedAt); + deadline.setDate(deadline.getDate() + config_1.VOTE_PERIOD_DAYS); + return deadline; } exports.resolveDeadline = resolveDeadline; function parseStateMarker(body) { @@ -9649,7 +9653,7 @@ function syncIssueVoteLog(owner, repo, issueNumber, members) { })); const body = (_a = voteComment.body) !== null && _a !== void 0 ? _a : ''; const state = parseStateMarker(body); - const deadline = resolveDeadline(body); + const deadline = resolveDeadline(body, voteComment.created_at); const now = new Date(); const newLines = diffVotes(state, liveReactions, deadline, now); if (newLines.length === 0) diff --git a/src/commands/voteEnd.ts b/src/commands/voteEnd.ts index f8272f5..b87b45f 100644 --- a/src/commands/voteEnd.ts +++ b/src/commands/voteEnd.ts @@ -116,8 +116,8 @@ export default async function run(context: Context) { return; } - const deadline = resolveDeadline(voteComment.body ?? ''); - if (deadline && deadline > new Date()) { + const deadline = resolveDeadline(voteComment.body ?? '', voteComment.created_at); + if (deadline > new Date()) { await octokit.issues.createComment({ owner, repo, diff --git a/src/commands/voteSync.test.ts b/src/commands/voteSync.test.ts index c0d678c..0c18de8 100644 --- a/src/commands/voteSync.test.ts +++ b/src/commands/voteSync.test.ts @@ -4,11 +4,13 @@ import { test('resolveDeadline reads the marker when present', () => { const body = 'body\n\n'; - expect(resolveDeadline(body)).toEqual(new Date('2026-06-25T07:00:00.000Z')); + expect(resolveDeadline(body, '2026-06-11T07:00:00Z')) + .toEqual(new Date('2026-06-25T07:00:00.000Z')); }); -test('resolveDeadline returns null when the marker is absent', () => { - expect(resolveDeadline('no marker here')).toBeNull(); +test('resolveDeadline falls back to comment date plus the vote period', () => { + expect(resolveDeadline('no marker here', '2026-06-11T07:00:00Z')) + .toEqual(new Date('2026-06-25T07:00:00Z')); }); test('parseStateMarker returns empty object when marker is absent', () => { diff --git a/src/commands/voteSync.ts b/src/commands/voteSync.ts index 7d13a96..50e98e7 100644 --- a/src/commands/voteSync.ts +++ b/src/commands/voteSync.ts @@ -1,5 +1,5 @@ import { GetResponseDataTypeFromEndpointMethod } from '@octokit/types'; -import { BOT_USERNAME } from '../config'; +import { BOT_USERNAME, VOTE_PERIOD_DAYS } from '../config'; import octokit from '../octokit'; type Comment = GetResponseDataTypeFromEndpointMethod< @@ -17,9 +17,13 @@ export interface LiveReaction { const STATE_MARKER_RE = //; const DEADLINE_MARKER_RE = //; -export function resolveDeadline(body: string): Date | null { +export function resolveDeadline(body: string, commentCreatedAt: string): Date { const match = body.match(DEADLINE_MARKER_RE); - return match ? new Date(match[1]!) : null; + if (match) return new Date(match[1]!); + + const deadline = new Date(commentCreatedAt); + deadline.setDate(deadline.getDate() + VOTE_PERIOD_DAYS); + return deadline; } export function parseStateMarker(body: string): VoteState { @@ -134,7 +138,7 @@ export async function syncIssueVoteLog( const body = voteComment.body ?? ''; const state = parseStateMarker(body); - const deadline = resolveDeadline(body); + const deadline = resolveDeadline(body, voteComment.created_at); const now = new Date(); const newLines = diffVotes(state, liveReactions, deadline, now);