Skip to content

Commit

Permalink
Merge pull request #39 from ishowvel/development
Browse files Browse the repository at this point in the history
feat: add a config prop 'aggressiveFollowUps' to follow up based on p…
  • Loading branch information
gentlementlegen authored Nov 2, 2024
2 parents 91783e0 + 06f1200 commit 7815ea7
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 23 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ yarn test
with:
disqualification: "7 days"
warning: "3.5 days"
prioritySpeed: true
watch:
optOut:
- "repoName"
Expand Down
4 changes: 4 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
}
}
},
"prioritySpeed": {
"default": true,
"type": "boolean"
},
"disqualification": {
"default": "7 days",
"type": "string"
Expand Down
42 changes: 28 additions & 14 deletions src/helpers/task-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import { DateTime } from "luxon";
import ms from "ms";
import { ListForOrg, ListIssueForRepo } from "../types/github-types";
import { ContextPlugin } from "../types/plugin-input";
import { RestEndpointMethodTypes } from "@octokit/rest";

type IssueLabel = Partial<Omit<RestEndpointMethodTypes["issues"]["listLabelsForRepo"]["response"]["data"][0], "color">> & {
color?: string | null;
};

/**
* Retrieves assignment events from the timeline of an issue and calculates the deadline based on the time label.
Expand Down Expand Up @@ -67,20 +72,7 @@ export async function getTaskAssignmentDetails(
return metadata;
}

function parseTimeLabel(
labels: (
| string
| {
id?: number;
node_id?: string;
url?: string;
name?: string;
description?: string | null;
color?: string | null;
default?: boolean;
}
)[]
): number {
function parseTimeLabel(labels: (IssueLabel | string)[]): number {
let taskTimeEstimate = 0;

for (const label of labels) {
Expand Down Expand Up @@ -108,3 +100,25 @@ function parseTimeLabel(

return taskTimeEstimate;
}

export function parsePriorityLabel(labels: (IssueLabel | string)[]): number {
for (const label of labels) {
let priorityLabel = "";
if (typeof label === "string") {
priorityLabel = label;
} else {
priorityLabel = label.name || "";
}

if (priorityLabel.startsWith("Priority:")) {
const matched = priorityLabel.match(/Priority: (\d+)/i);
if (!matched) {
return 1;
}

return Number(matched[1]);
}
}

return 1;
}
12 changes: 7 additions & 5 deletions src/helpers/task-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getAssigneesActivityForIssue } from "./get-assignee-activity";
import { parseIssueUrl } from "./github-url";
import { remindAssigneesForIssue, unassignUserFromIssue } from "./remind-and-remove";
import { getCommentsFromMetadata } from "./structured-metadata";
import { getTaskAssignmentDetails } from "./task-metadata";
import { getTaskAssignmentDetails, parsePriorityLabel } from "./task-metadata";

const getMostRecentActivityDate = (assignedEventDate: DateTime, activityEventDate?: DateTime): DateTime => {
return activityEventDate && activityEventDate > assignedEventDate ? activityEventDate : assignedEventDate;
Expand All @@ -18,7 +18,7 @@ export async function updateTaskReminder(context: ContextPlugin, repo: ListForOr
const {
octokit,
logger,
config: { eventWhitelist, warning, disqualification },
config: { eventWhitelist, warning, disqualification, prioritySpeed },
} = context;
const handledMetadata = await getTaskAssignmentDetails(context, repo, issue);
const now = DateTime.local();
Expand Down Expand Up @@ -46,6 +46,8 @@ export async function updateTaskReminder(context: ContextPlugin, repo: ListForOr
.shift();

const assignedDate = DateTime.fromISO(assignedEvent.created_at);
const priorityValue = parsePriorityLabel(issue.labels);
const priorityLevel = Math.max(1, priorityValue);
const activityDate = activityEvent?.created_at ? DateTime.fromISO(activityEvent.created_at) : undefined;
let mostRecentActivityDate = getMostRecentActivityDate(assignedDate, activityDate);

Expand Down Expand Up @@ -75,16 +77,16 @@ export async function updateTaskReminder(context: ContextPlugin, repo: ListForOr
if (lastReminderComment) {
const lastReminderTime = DateTime.fromISO(lastReminderComment.created_at);
mostRecentActivityDate = lastReminderTime > mostRecentActivityDate ? lastReminderTime : mostRecentActivityDate;
if (mostRecentActivityDate.plus({ milliseconds: disqualificationTimeDifference }) <= now) {
if (mostRecentActivityDate.plus({ milliseconds: prioritySpeed ? disqualificationTimeDifference / priorityLevel : disqualificationTimeDifference }) <= now) {
await unassignUserFromIssue(context, issue);
} else {
logger.info(`Reminder was sent for ${issue.html_url} already, not beyond disqualification deadline yet.`);
}
} else {
if (mostRecentActivityDate.plus({ milliseconds: warning }) <= now) {
if (mostRecentActivityDate.plus({ milliseconds: prioritySpeed ? warning / priorityLevel : warning }) <= now) {
await remindAssigneesForIssue(context, issue);
} else {
logger.info(`Nothing to do for ${issue.html_url}, still within due-time.`);
logger.info(`Nothing to do for ${issue.html_url} still within due-time.`);
}
}
}
4 changes: 4 additions & 0 deletions src/types/plugin-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export const pluginSettingsSchema = T.Object(
},
{ default: {} }
),
/*
* Whether to rush the follow ups by the priority level
*/
prioritySpeed: T.Boolean({ default: true }),
/**
* Delay to unassign users. 0 means disabled. Any other value is counted in days, e.g. 7 days
*/
Expand Down
1 change: 1 addition & 0 deletions tests/__mocks__/results/valid-configuration.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"warning": "3.5 days",
"disqualification": "7 days",
"prioritySpeed": true,
"watch": {
"optOut": ["private-repo"]
},
Expand Down
11 changes: 7 additions & 4 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ describe("User start/stop", () => {
expect(pluginSettings).toEqual({
pullRequestRequired: true,
warning: 302400000,
prioritySpeed: true,
disqualification: 604800000,
watch: { optOut: [STRINGS.PRIVATE_REPO_NAME] },
eventWhitelist: ["review_requested", "ready_for_review", "commented", "committed"],
Expand Down Expand Up @@ -103,6 +104,7 @@ describe("User start/stop", () => {
pullRequestRequired: true,
warning: ms("3.5 days"),
disqualification: ms("7 days"),
prioritySpeed: true,
watch: { optOut: [STRINGS.PRIVATE_REPO_NAME] },
eventWhitelist: ["review_requested", "ready_for_review", "commented", "committed"],
});
Expand All @@ -121,7 +123,7 @@ describe("User start/stop", () => {
await expect(run(context)).resolves.toEqual({ message: "OK" });

expect(errorSpy).toHaveBeenCalledWith(`Failed to update activity for ${getIssueHtmlUrl(1)}, there is no assigned event.`);
expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)}, still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)} still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Passed the reminder threshold on ${getIssueHtmlUrl(3)}, sending a reminder.`);
expect(infoSpy).toHaveBeenCalledWith(`@user2, this task has been idle for a while. Please provide an update.\n\n`, {
taskAssignees: [2],
Expand All @@ -137,7 +139,7 @@ describe("User start/stop", () => {

await expect(run(context)).resolves.toEqual({ message: "OK" });

expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)}, still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)} still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Passed the reminder threshold on ${getIssueHtmlUrl(3)}, sending a reminder.`);
expect(infoSpy).toHaveBeenCalledWith(`@user2, this task has been idle for a while. Please provide an update.\n\n`, {
taskAssignees: [2],
Expand All @@ -156,7 +158,7 @@ describe("User start/stop", () => {

await run(context);

expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)}, still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)} still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Passed the reminder threshold on ${getIssueHtmlUrl(3)}, sending a reminder.`);
expect(infoSpy).toHaveBeenCalledWith(`@user2, this task has been idle for a while. Please provide an update.\n\n`, {
taskAssignees: [2],
Expand Down Expand Up @@ -193,7 +195,7 @@ describe("User start/stop", () => {

await run(context);

expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)}, still within due-time.`);
expect(infoSpy).toHaveBeenCalledWith(`Nothing to do for ${getIssueHtmlUrl(2)} still within due-time.`);

const updatedIssue = db.issue.findFirst({ where: { id: { equals: 1 } } });
expect(updatedIssue?.assignees).toEqual([{ login: STRINGS.UBIQUITY, id: 1 }]);
Expand Down Expand Up @@ -279,6 +281,7 @@ function createContext(issueId: number, senderId: number, optOut = [STRINGS.PRIV
config: {
disqualification: ONE_DAY * 7,
warning: ONE_DAY * 3.5,
prioritySpeed: true,
watch: { optOut },
eventWhitelist: ["review_requested", "ready_for_review", "commented", "committed"],
pullRequestRequired: false,
Expand Down

0 comments on commit 7815ea7

Please sign in to comment.