Skip to content

Commit 2124e0f

Browse files
committed
feat(duplicates): run more often, skip duplicate issues
1 parent 0951fea commit 2124e0f

File tree

5 files changed

+48
-18
lines changed

5 files changed

+48
-18
lines changed

.github/workflows/create-meeting-artifacts-scheduled.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Create Meeting Artifacts (Scheduled)
33
on:
44
workflow_dispatch:
55
schedule:
6-
- cron: '0 10 * * 1'
6+
- cron: '0 0 * * *'
77

88
jobs:
99
create-artifacts:

create-node-meeting-artifacts.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const program = new Command();
2121
program
2222
.argument('<group>', 'Meeting group')
2323
.option('--dry-run', 'Show output without creating/updating anything', false)
24+
.option('--force', 'Create a new issue even if one already exists', false)
2425
.option('--verbose', 'Show debug output')
2526
.parse(process.argv);
2627

@@ -66,13 +67,32 @@ if (config.dryRun) {
6667
// Step 6: Find next meeting event in calendar
6768
const meetingDate = await calendar.findNextMeetingDate(meetingConfig);
6869

70+
if (!meetingDate) {
71+
process.exit(0);
72+
}
73+
6974
// Step 8: Get Meeting Title
7075
const meetingTitle = meetings.generateMeetingTitle(
7176
config,
7277
meetingConfig,
7378
meetingDate
7479
);
7580

81+
// Look for existing issues
82+
if (!config.force) {
83+
const existingIssue = await github.findIssueByTitle(
84+
githubClient,
85+
meetingTitle,
86+
meetingConfig
87+
);
88+
89+
if (existingIssue) {
90+
console.log(`${existingIssue.html_url} already exists. Exiting.`);
91+
process.exit(0);
92+
}
93+
}
94+
95+
process.exit(0);
7696
// Step 9: Get agenda information using native implementation
7797
const gitHubAgendaIssues = await github.getAgendaIssues(
7898
githubClient,

src/calendar.mjs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,12 @@ const getEventsFromCalendar = async url => {
1717
* @returns {Promise<Date>} The date of the next meeting
1818
*/
1919
export const findNextMeetingDate = async ({ properties }) => {
20-
const now = new Date();
20+
const start = new Date();
21+
start.setUTCHours(0, 0, 0, 0);
2122

22-
// Calculate the start of the current week (Saturday 00:00:00 UTC)
23-
// This handles the scenario where we want a full week from Saturday to Friday
24-
const daysSinceStartOfWeek = (now.getUTCDay() + 1) % 7; // Saturday = 0, Sunday = 1, ..., Friday = 6
25-
const weekStart = new Date(now);
26-
27-
weekStart.setUTCDate(now.getUTCDate() - daysSinceStartOfWeek);
28-
weekStart.setUTCHours(0, 0, 0, 0);
29-
30-
// Calculate the end of the week (Friday 23:59:59 UTC)
31-
const weekEnd = new Date(weekStart);
32-
33-
weekEnd.setUTCDate(weekStart.getUTCDate() + 6);
34-
weekEnd.setUTCHours(23, 59, 59, 999);
23+
// One week from today
24+
const end = new Date(start);
25+
end.setUTCDate(start.getUTCDate() + 7);
3526

3627
const allEvents = await getEventsFromCalendar(properties.ICAL_URL);
3728

@@ -46,16 +37,16 @@ export const findNextMeetingDate = async ({ properties }) => {
4637
for (const event of filteredEvents) {
4738
// Get all recurrences in our timeframe
4839
event.rrule.options.tzid = event.tzid;
49-
const duringOurTimeframe = event.rrule.between(weekStart, weekEnd);
40+
const duringOurTimeframe = event.rrule.between(start, end);
5041

5142
if (duringOurTimeframe.length > 0) {
5243
return duringOurTimeframe[0];
5344
}
5445
}
5546

56-
throw new Error(
47+
console.error(
5748
`No meeting found for ${properties.GROUP_NAME || 'this group'} ` +
58-
`in the current week (${weekStart.toISOString().split('T')[0]} to ${weekEnd.toISOString().split('T')[0]}). ` +
49+
`in the next week (${start.toISOString().split('T')[0]} to ${end.toISOString().split('T')[0]}). ` +
5950
`This is expected for bi-weekly meetings or meetings that don't occur every week.`
6051
);
6152
};

src/github.mjs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,24 @@ export const sortIssuesByRepo = issues =>
5555
return obj;
5656
}, {});
5757

58+
/**
59+
* Fetches GitHub issues from all repositories in an organization with a specific label
60+
* @param {import('@octokit/rest').Octokit} githubClient - Authenticated GitHub API client
61+
* @param {string} title - The title to find
62+
* @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration
63+
*/
64+
export const findIssueByTitle = async (githubClient, title, { properties }) => {
65+
const githubOrg = properties.USER ?? DEFAULT_CONFIG.githubOrg;
66+
67+
const issues = await githubClient.request('GET /search/issues', {
68+
q: `is:open in:title repo:"${githubOrg}/${properties.REPO}" "${title}"`,
69+
advanced_search: true,
70+
per_page: 1,
71+
});
72+
73+
return issues.data.items[0];
74+
};
75+
5876
/**
5977
* Fetches GitHub issues from all repositories in an organization with a specific label
6078
* @param {import('@octokit/rest').Octokit} githubClient - Authenticated GitHub API client

src/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface EnvironmentConfig {
1515
*/
1616
export interface CLIConfig {
1717
verbose: boolean;
18+
force: boolean;
1819
dryRun: boolean;
1920
meetingGroup: string;
2021
}

0 commit comments

Comments
 (0)