Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge development into main #40

Merged
merged 126 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
2cce223
Merge pull request #2 from Meniole/development
gentlementlegen Jul 7, 2024
48203fe
chore: removed yarn 4
gentlementlegen Jul 7, 2024
ba529e1
chore: renamed wrangler.toml
gentlementlegen Jul 7, 2024
d3226ca
chore: default enabled true
gentlementlegen Jul 7, 2024
e0f9047
chore: added manifest.json
gentlementlegen Jul 10, 2024
dad7b2b
chore: added manifest.json
gentlementlegen Jul 10, 2024
0c40f37
chore: added manifest.json
gentlementlegen Jul 10, 2024
e4fee10
chore: manifest.json
gentlementlegen Jul 11, 2024
acb228c
chore: manifest.json
gentlementlegen Jul 12, 2024
565fd1c
feat: max task assignment for collaborators
Jul 14, 2024
a74d31b
feat: make task limits configurable
Jul 15, 2024
b30416e
Merge pull request #3 from Meniole/development
gentlementlegen Jul 17, 2024
8017e89
Merge branch 'refs/heads/development' into meniole-main
gentlementlegen Jul 17, 2024
cddec1e
chore: manifest.json
gentlementlegen Jul 17, 2024
c36730a
Merge remote-tracking branch 'meniole/main' into meniole-main
gentlementlegen Jul 17, 2024
a1508fc
fix: get contributors from config
Jul 22, 2024
f5199d8
fix: remove union type
Jul 22, 2024
df7770e
fix: return lowest task limit if user role doesnt match any of those …
Jul 23, 2024
661fb85
chore: code cleanups
Jul 23, 2024
b2ec2f6
test: add test to cover functionality
Jul 23, 2024
14f7c33
chore: fix merge conflicts
Jul 23, 2024
05f43ed
fix: pass correct inputs to getUserRole
Jul 23, 2024
71e1c1a
test: add test
Jul 29, 2024
7d17f77
feat: check unassigns
Jul 15, 2024
efce69b
chore: fix error message
Jul 22, 2024
e201278
chore:eslint
Keyrxng Jul 28, 2024
832af78
feat: previous assignment filter
Keyrxng Jul 28, 2024
534b0ec
chore: make bot name match configurable
Keyrxng Jul 28, 2024
2d8b175
chore: tests and configurable botUsernames
Keyrxng Jul 28, 2024
930562f
chore: remove log
Keyrxng Jul 28, 2024
b76b3ef
chore: simplify function
Keyrxng Jul 28, 2024
f6634ab
chore: remove botUsernames item
Keyrxng Jul 29, 2024
a721cfb
chore: fix test
Keyrxng Jul 29, 2024
06294ca
test: cleanups
Jul 29, 2024
ec2e5bb
test: made requested changes
Jul 30, 2024
38be206
test: cleanpup test
Jul 30, 2024
468cd49
fix: set smallest task on error when getting user role
Jul 30, 2024
91984c7
chore: fix merge conflicts
Jul 30, 2024
175dcd3
chore: minor fixes
Keyrxng Jul 30, 2024
5d487fb
chore: refactor tests
Keyrxng Jul 30, 2024
352a56d
chore: filter by ubiquibot id
Keyrxng Jul 30, 2024
86accef
fix: refactor test to use the correct format
Jul 31, 2024
1927aed
fix: add db issues to cover all test
Jul 31, 2024
a06ab85
fix: assignee issue and open pr fetching
Keyrxng Jul 31, 2024
e343a6d
chore: fix filter and use math.abs
Keyrxng Jul 31, 2024
09c1091
chore: tests
Keyrxng Jul 31, 2024
c341006
Merge branch 'max-assignments' into fix/max-and-assigned
Keyrxng Jul 31, 2024
ca1f0c5
chore: ci
Keyrxng Jul 31, 2024
944883d
Merge pull request #21 from ubq-testing/fix/max-and-assigned
jordan-ae Jul 31, 2024
83f03f2
chore: pull APP_ID from env
Keyrxng Jul 31, 2024
9b8928a
chore: taskDeadline, taskAssignees metadata
Keyrxng Jul 31, 2024
dcddd3e
chore: rename file to match return
jordan-ae Aug 5, 2024
00f4334
chore: use typebox for app_id
Keyrxng Aug 5, 2024
c02fbbe
chore: fetching fixes
Keyrxng Aug 5, 2024
f7e5244
chore: fix tests and eslint
Keyrxng Aug 5, 2024
8221f51
fix: use logger to display message
jordan-ae Aug 6, 2024
30ca585
chore: paginate and knip fix
Keyrxng Aug 6, 2024
0d55cb1
Update src/handlers/shared/start.ts
jordan-ae Aug 12, 2024
101c1f8
chore: rename function to match return
jordan-ae Aug 12, 2024
0084bcb
chore: address code reviews
jordan-ae Aug 12, 2024
92ce9d4
fix: clean up queries
jordan-ae Aug 14, 2024
665fd5c
Merge branch 'fork/ubq-test-jordan/max-assignments' into meniole-main
gentlementlegen Aug 14, 2024
c8a018d
feat: user self assign message display
gentlementlegen Aug 20, 2024
cffb784
Merge branch 'feat/notify-self-assign' into meniole-main
gentlementlegen Aug 20, 2024
c3fc7ca
Merge remote-tracking branch 'meniole/main' into meniole-main
gentlementlegen Aug 20, 2024
d0a00d8
chore: fixed event name
gentlementlegen Aug 20, 2024
6fc8f09
chore: simplified deadline calculation
gentlementlegen Aug 20, 2024
f46ffd0
Merge remote-tracking branch 'upstream/development' into feat/disable…
Keyrxng Aug 24, 2024
9e3dbe0
chore: better assignment checks
Keyrxng Aug 24, 2024
a53e425
chore: correct throwing of errors
Keyrxng Aug 24, 2024
1c891b3
chore: previous unassignment check for each teammate
Keyrxng Aug 24, 2024
99f61aa
chore: refactor tests
Keyrxng Aug 24, 2024
7fa0ead
feat: add a new config param
whck6 Aug 27, 2024
2bf7363
feat: check unassigns
Jul 15, 2024
1f82df8
feat: previous assignment filter
Keyrxng Jul 28, 2024
7310c50
chore: make bot name match configurable
Keyrxng Jul 28, 2024
b69f4c3
chore: tests and configurable botUsernames
Keyrxng Jul 28, 2024
9089fe3
chore: remove botUsernames item
Keyrxng Jul 29, 2024
316ca9e
chore: fix test
Keyrxng Jul 29, 2024
f3b1e52
chore: minor fixes
Keyrxng Jul 30, 2024
671be3d
chore: refactor tests
Keyrxng Jul 30, 2024
bedb60b
feat: max task assignment for collaborators
Jul 14, 2024
71324b8
feat: make task limits configurable
Jul 15, 2024
212f9bf
fix: get contributors from config
Jul 22, 2024
391bdde
fix: return lowest task limit if user role doesnt match any of those …
Jul 23, 2024
bf6e7da
chore: code cleanups
Jul 23, 2024
da1b51d
test: add test to cover functionality
Jul 23, 2024
83a6941
test: cleanups
Jul 29, 2024
9005f01
test: made requested changes
Jul 30, 2024
10b985a
fix: set smallest task on error when getting user role
Jul 30, 2024
73119b5
chore: fix filter and use math.abs
Keyrxng Jul 31, 2024
0753f36
chore: rename file to match return
jordan-ae Aug 5, 2024
42cdf6e
chore: rename function to match return
jordan-ae Aug 12, 2024
535ec00
chore: address code reviews
jordan-ae Aug 12, 2024
df7ff28
fix: clean up queries
jordan-ae Aug 14, 2024
c148fba
chore: merging
gentlementlegen Aug 27, 2024
4251e60
chore: transform app_id via schema
Keyrxng Aug 27, 2024
ad911c1
chore: util for splitting html_url
Keyrxng Aug 27, 2024
5158cbe
chore: throw if toAssignId is null
Keyrxng Aug 27, 2024
f5bc1d5
fix: add env typebox validator to worker, fix tests
Keyrxng Aug 27, 2024
40def46
fix: set a default value which is type string
whck6 Aug 28, 2024
b11fc4f
fix: show message by a config of the emptyWalletText
whck6 Aug 28, 2024
d2313c0
chore: update README.md
whck6 Aug 29, 2024
733ab4a
chore: decode env
Keyrxng Aug 29, 2024
a094a8c
Merge pull request #19 from ubq-testing/feat/disabled-multi-self-assign
Keyrxng Aug 30, 2024
8241c00
Merge branch 'development' into feature/new-text-config-param-for-emp…
whck6 Aug 31, 2024
78b19d2
Merge pull request #25 from whck6/feature/new-text-config-param-for-e…
Keyrxng Aug 31, 2024
9a7bc37
Update worker-deploy.yml
gentlementlegen Sep 1, 2024
4fe61f3
chore: merge changes
gentlementlegen Sep 1, 2024
dd7eb09
chore: merge changes
gentlementlegen Sep 1, 2024
eb37a3d
Update src/handlers/shared/start.ts
gentlementlegen Sep 1, 2024
1977116
chore: merge changes
gentlementlegen Sep 1, 2024
d7ca019
chore: merge changes
gentlementlegen Sep 1, 2024
615d16a
chore: merge changes
gentlementlegen Sep 1, 2024
68890ac
chore: merge changes
gentlementlegen Sep 1, 2024
22b64c4
chore: fixed test
gentlementlegen Sep 1, 2024
5437e76
chore: fixed test
gentlementlegen Sep 1, 2024
43244a9
fix: fixed /stop formatting
obeys Sep 1, 2024
10650d8
chore: fixed test
gentlementlegen Sep 2, 2024
b8cd7af
chore: changed status code
gentlementlegen Sep 2, 2024
3764eea
chore: fixed tests
gentlementlegen Sep 2, 2024
ddd787b
Merge pull request #33 from cohow/format
Keyrxng Sep 2, 2024
0dc082b
Merge branch 'development' into feat/notify-self-assign
gentlementlegen Sep 3, 2024
ebf98c4
fix: skipping deadline self assign post if no deadline
gentlementlegen Sep 3, 2024
c79b63f
fix: the messages are displayed on catch errors
gentlementlegen Sep 4, 2024
2be6491
Merge pull request #24 from gentlementlegen/feat/notify-self-assign
gentlementlegen Sep 7, 2024
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
4 changes: 3 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"ubiquibot",
"signoff",
"sonarjs",
"mswjs"
"mswjs",
"unassignment",
"unassignments"
],
"dictionaries": ["typescript", "node", "software-terms"],
"import": ["@cspell/dict-typescript/cspell-ext.json", "@cspell/dict-node/cspell-ext.json", "@cspell/dict-software-terms"],
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/worker-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ jobs:
secrets: |
SUPABASE_URL
SUPABASE_KEY
APP_ID
env:
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_KEY: ${{ secrets.SUPABASE_KEY }}
APP_ID: ${{ secrets.APP_ID }}

- name: Write Deployment URL to Summary
run: |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ To configure your Ubiquibot to run this plugin, add the following to the `.ubiqu
taskStaleTimeoutDuration: "30 Days"
maxConcurrentTasks: 3
startRequiresWallet: true # default is true
emptyWalletText: "Please set your wallet address with the /wallet command first and try again."
```

# Testing
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Start | Stop",
"description": "Assign or un-assign yourself from an issue.",
"ubiquity:listeners": ["issue_comment.created"],
"ubiquity:listeners": ["issue_comment.created", "issues.assigned"],
"commands": {
"start": {
"ubiquity:example": "/start",
Expand Down
4 changes: 2 additions & 2 deletions src/adapters/supabase/helpers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export class User extends Super {
if ((error && !data) || !data.wallets?.address) {
this.context.logger.error("No wallet address found", { userId, issueNumber });
if (this.context.config.startRequiresWallet) {
await addCommentToIssue(this.context, "```diff\n! Please set your wallet address with the /wallet command first and try again.\n```");
await addCommentToIssue(this.context, this.context.config.emptyWalletText);
throw new Error("No wallet address found");
} else {
await addCommentToIssue(this.context, "```diff\n# Please set your wallet address with the /wallet command in order to be eligible for rewards.\n```");
await addCommentToIssue(this.context, this.context.config.emptyWalletText);
}
} else {
this.context.logger.info("Successfully fetched wallet", { userId, address: data.wallets?.address });
Expand Down
10 changes: 10 additions & 0 deletions src/handlers/result-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export enum HttpStatusCode {
OK = 200,
NOT_MODIFIED = 304,
}

export interface Result {
status: HttpStatusCode;
content?: string;
reason?: string;
}
92 changes: 92 additions & 0 deletions src/handlers/shared/check-assignments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Context } from "../../types";
import { getOwnerRepoFromHtmlUrl } from "../../utils/issue";

async function getUserStopComments(context: Context, username: string): Promise<number> {
const { payload, octokit, logger } = context;
const { number, html_url } = payload.issue;
const { owner, repo } = getOwnerRepoFromHtmlUrl(html_url);

try {
const comments = await octokit.paginate(octokit.issues.listComments, {
owner,
repo,
issue_number: number,
});

return comments.filter((comment) => comment.body?.includes("/stop") && comment.user?.login.toLowerCase() === username.toLowerCase()).length;
} catch (error) {
throw new Error(logger.error("Error while getting user stop comments", { error: error as Error }).logMessage.raw);
}
}

export async function hasUserBeenUnassigned(context: Context, username: string): Promise<boolean> {
const {
env: { APP_ID },
} = context;
const events = await getAssignmentEvents(context);
const userAssignments = events.filter((event) => event.assignee === username);

if (userAssignments.length === 0) {
return false;
}

const unassignedEvents = userAssignments.filter((event) => event.event === "unassigned");
// all bot unassignments (/stop, disqualification, etc)
// TODO: task-xp-guard: will also prevent future assignments so we need to add a comment tracker we can use here
const botUnassigned = unassignedEvents.filter((event) => event.actorId === APP_ID);
// UI assignment
const adminUnassigned = unassignedEvents.filter((event) => event.actor !== username && event.actorId !== APP_ID);
// UI assignment
const userUnassigned = unassignedEvents.filter((event) => event.actor === username);
const userStopComments = await getUserStopComments(context, username);
/**
* Basically the bot will be the actor in most cases but if we
* remove the /stop usage which does not trigger future disqualification
* then any other bot unassignment will be considered valid
*/

const botMinusUserStopCommands = Math.max(0, botUnassigned.length - userStopComments);
const userUiMinusUserStopCommands = Math.max(0, userUnassigned.length - userStopComments);

return botMinusUserStopCommands > 0 || userUiMinusUserStopCommands > 0 || adminUnassigned.length > 0;
}

async function getAssignmentEvents(context: Context) {
const { repository, issue } = context.payload;
try {
const data = await context.octokit.paginate(context.octokit.issues.listEventsForTimeline, {
owner: repository.owner.login,
repo: repository.name,
issue_number: issue.number,
});

const events = data
.filter((event) => event.event === "assigned" || event.event === "unassigned")
.map((event) => {
let actor, assignee, createdAt, actorId;

if ((event.event === "unassigned" || event.event === "assigned") && "actor" in event && event.actor && "assignee" in event && event.assignee) {
actor = event.actor.login;
assignee = event.assignee.login;
createdAt = event.created_at;
actorId = event.actor.id;
}

return {
event: event.event,
actor,
actorId,
assignee,
createdAt,
};
});

return events
.filter((event) => event !== undefined)
.sort((a, b) => {
return new Date(a.createdAt || "").getTime() - new Date(b.createdAt || "").getTime();
});
} catch (error) {
throw new Error(context.logger.error("Error while getting assignment events", { error: error as Error }).logMessage.raw);
}
}
24 changes: 16 additions & 8 deletions src/handlers/shared/generate-assignment-comment.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Context } from "../../types/context";
import { Context } from "../../types";
import { calculateDurations } from "../../utils/shared";

const options: Intl.DateTimeFormatOptions = {
export const options: Intl.DateTimeFormatOptions = {
weekday: "short",
month: "short",
day: "numeric",
Expand All @@ -10,16 +11,23 @@ const options: Intl.DateTimeFormatOptions = {
timeZoneName: "short",
};

export async function generateAssignmentComment(context: Context, issueCreatedAt: string, issueNumber: number, senderId: number, duration: number) {
export function getDeadline(issue: Context["payload"]["issue"]): string | null {
if (!issue?.labels) {
throw new Error("No labels are set.");
}
const startTime = new Date().getTime();
const duration: number = calculateDurations(issue.labels).shift() ?? 0;
if (!duration) return null;
const endTime = new Date(startTime + duration * 1000);
return endTime.toLocaleString("en-US", options);
}

export async function generateAssignmentComment(context: Context, issueCreatedAt: string, issueNumber: number, senderId: number, deadline: string | null) {
const startTime = new Date().getTime();
let endTime: null | Date = null;
let deadline: null | string = null;
endTime = new Date(startTime + duration * 1000);
deadline = endTime.toLocaleString("en-US", options);

return {
daysElapsedSinceTaskCreation: Math.floor((startTime - new Date(issueCreatedAt).getTime()) / 1000 / 60 / 60 / 24),
deadline: duration > 0 ? deadline : null,
deadline: deadline ?? null,
registeredWallet:
(await context.adapters.supabase.user.getWalletByUserId(senderId, issueNumber)) ||
"Register your wallet address using the following slash command: `/wallet 0x0000...0000`",
Expand Down
92 changes: 67 additions & 25 deletions src/handlers/shared/start.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Context, ISSUE_TYPE, Label } from "../../types";
import { isParentIssue, getAvailableOpenedPullRequests, getAssignedIssues, addAssignees, addCommentToIssue, getTimeValue } from "../../utils/issue";
import { calculateDurations } from "../../utils/shared";
import { addAssignees, addCommentToIssue, getAssignedIssues, getAvailableOpenedPullRequests, getTimeValue, isParentIssue } from "../../utils/issue";
import { HttpStatusCode, Result } from "../result-types";
import { hasUserBeenUnassigned } from "./check-assignments";
import { checkTaskStale } from "./check-task-stale";
import { generateAssignmentComment } from "./generate-assignment-comment";
import { generateAssignmentComment, getDeadline } from "./generate-assignment-comment";
import structuredMetadata from "./structured-metadata";
import { assignTableComment } from "./table";

export async function start(context: Context, issue: Context["payload"]["issue"], sender: Context["payload"]["sender"], teammates: string[]) {
export async function start(context: Context, issue: Context["payload"]["issue"], sender: Context["payload"]["sender"], teammates: string[]): Promise<Result> {
const { logger, config } = context;
const { maxConcurrentTasks, taskStaleTimeoutDuration } = config;

Expand All @@ -16,8 +17,7 @@ export async function start(context: Context, issue: Context["payload"]["issue"]
context,
"```diff\n# Please select a child issue from the specification checklist to work on. The '/start' command is disabled on parent issues.\n```"
);
logger.error(`Skipping '/start' since the issue is a parent issue`);
return { output: "Parent issue detected" };
throw new Error(logger.error(`Skipping '/start' since the issue is a parent issue`).logMessage.raw);
}

let commitHash: string | null = null;
Expand All @@ -36,51 +36,66 @@ export async function start(context: Context, issue: Context["payload"]["issue"]
// is it assignable?

if (issue.state === ISSUE_TYPE.CLOSED) {
throw logger.error("This issue is closed, please choose another.", { issueNumber: issue.number });
throw new Error(logger.error("This issue is closed, please choose another.", { issueNumber: issue.number }).logMessage.raw);
}

const assignees = issue?.assignees ?? [];

// find out if the issue is already assigned
if (assignees.length !== 0) {
const isCurrentUserAssigned = !!assignees.find((assignee) => assignee?.login === sender.login);
throw logger.error(
isCurrentUserAssigned ? "You are already assigned to this task." : "This issue is already assigned. Please choose another unassigned task.",
{ issueNumber: issue.number }
throw new Error(
logger.error(
isCurrentUserAssigned ? "You are already assigned to this task." : "This issue is already assigned. Please choose another unassigned task.",
{ issueNumber: issue.number }
).logMessage.raw
);
}

teammates.push(sender.login);

const toAssign = [];
// check max assigned issues
for (const user of teammates) {
await handleTaskLimitChecks(user, context, maxConcurrentTasks, logger, sender.login);
if (await handleTaskLimitChecks(user, context, maxConcurrentTasks, logger, sender.login)) {
toAssign.push(user);
}
}

let error: string | null = null;

if (toAssign.length === 0 && teammates.length > 1) {
error = "All teammates have reached their max task limit. Please close out some tasks before assigning new ones.";
} else if (toAssign.length === 0) {
error = "You have reached your max task limit. Please close out some tasks before assigning new ones.";
}

if (error) {
throw new Error(logger.error(error, { issueNumber: issue.number }).logMessage.raw);
}

// get labels
const labels = issue.labels;
const labels = issue.labels ?? [];
const priceLabel = labels.find((label: Label) => label.name.startsWith("Price: "));

if (!priceLabel) {
throw logger.error("No price label is set to calculate the duration", { issueNumber: issue.number });
throw new Error(logger.error("No price label is set to calculate the duration", { issueNumber: issue.number }).logMessage.raw);
}

const duration: number = calculateDurations(labels).shift() ?? 0;
const deadline = getDeadline(issue);
const toAssignIds = await fetchUserIds(context, toAssign);

const { id } = sender;
const assignmentComment = await generateAssignmentComment(context, issue.created_at, issue.number, sender.id, deadline);
const logMessage = logger.info("Task assigned successfully", {
duration,
taskDeadline: assignmentComment.deadline,
taskAssignees: toAssignIds,
priceLabel,
revision: commitHash?.substring(0, 7),
assignees: teammates,
issue: issue.number,
});

const assignmentComment = await generateAssignmentComment(context, issue.created_at, issue.number, id, duration);
const metadata = structuredMetadata.create("Assignment", logMessage);

// assign the issue
await addAssignees(context, issue.number, teammates);
// add assignee
await addAssignees(context, issue.number, toAssign);

const isTaskStale = checkTaskStale(getTimeValue(taskStaleTimeoutDuration), issue.created_at);

Expand All @@ -98,19 +113,46 @@ export async function start(context: Context, issue: Context["payload"]["issue"]
].join("\n") as string
);

return { output: "Task assigned successfully" };
return { content: "Task assigned successfully", status: HttpStatusCode.OK };
}

async function fetchUserIds(context: Context, username: string[]) {
const ids = [];

for (const user of username) {
const { data } = await context.octokit.rest.users.getByUsername({
username: user,
});

ids.push(data.id);
}

if (ids.filter((id) => !id).length > 0) {
throw new Error("Error while fetching user ids");
}

return ids;
}

async function handleTaskLimitChecks(username: string, context: Context, maxConcurrentTasks: number, logger: Context["logger"], sender: string) {
const openedPullRequests = await getAvailableOpenedPullRequests(context, username);
const assignedIssues = await getAssignedIssues(context, username);

// check for max and enforce max
if (assignedIssues.length - openedPullRequests.length >= maxConcurrentTasks) {
throw logger.error(username === sender ? "You have reached your max task limit" : `${username} has reached their max task limit`, {

if (Math.abs(assignedIssues.length - openedPullRequests.length) >= maxConcurrentTasks) {
const log = logger.error(username === sender ? "You have reached your max task limit" : `${username} has reached their max task limit`, {
assignedIssues: assignedIssues.length,
openedPullRequests: openedPullRequests.length,
maxConcurrentTasks,
});
await addCommentToIssue(context, log?.logMessage.diff as string);
return false;
}

if (await hasUserBeenUnassigned(context, username)) {
throw new Error(logger.error(`${username} you were previously unassigned from this task. You cannot be reassigned.`, { username }).logMessage.raw);
}

return true;
}
Loading
Loading