Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/development' into e2e-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
whilefoo committed Sep 26, 2023
2 parents 56e4427 + 960e991 commit 31be5c6
Show file tree
Hide file tree
Showing 24 changed files with 951 additions and 55 deletions.
57 changes: 57 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"useGitignore": true,
"version": "0.2",
"language": "en",
"words": [
"autodetection",
"autopay",
"AUTOPAY",
"bucketid",
"bucketname",
"demilestoned",
"devpool",
"ensname",
"fkey",
"gelato",
"Gelato",
"gollum",
"keccak",
"libsodium",
"logdna",
"LOGDNA",
"mdast",
"Mdast",
"mergeable",
"micromark",
"milestoned",
"Numberish",
"orgname",
"pavlovcik",
"permisson",
"prereleased",
"probot",
"Probot",
"ratelimit",
"rebaseable",
"rerequested",
"scalarmult",
"signoff",
"sortcolumn",
"sortorder",
"supabase",
"Supabase",
"SUPABASE",
"svgs",
"timelabel",
"TURL",
"typebox",
"Ubiqui",
"ubiquibot",
"unarchived",
"Unassigning",
"Upserting",
"URLSAFE",
"vitalik",
"WXDAI"
]
}
2 changes: 1 addition & 1 deletion .github/workflows/conventional-commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ubiquity/action-conventional-commits@v1.1.2
- uses: ubiquity/action-conventional-commits@master
2 changes: 1 addition & 1 deletion app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ default_events:
# - gollum
- issue_comment
- issues
# - label
- label
# - milestone
# - member
# - membership
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"start:watch": "nodemon --exec 'yarn start'",
"start": "probot run ./lib/src/index.js",
"prepare": "husky install",
"test:e2e": "jest"
"test:e2e": "jest",
"utils:cspell": "cspell --config .cspell.json 'src/**/*.{js,ts,json,md,yml}'"
},
"dependencies": {
"@actions/core": "^1.10.0",
Expand All @@ -44,6 +45,7 @@
"ajv-formats": "^2.1.1",
"axios": "^1.3.2",
"copyfiles": "^2.4.1",
"cspell": "^7.0.0",
"decimal.js": "^10.4.3",
"ethers": "^5.7.2",
"exponential-backoff": "^3.1.1",
Expand Down Expand Up @@ -90,6 +92,9 @@
"lint-staged": {
"*.{ts,json}": [
"prettier --write"
],
"src/**.{ts,json}": [
"cspell"
]
},
"nodemonConfig": {
Expand Down
54 changes: 53 additions & 1 deletion src/adapters/supabase/helpers/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ export const upsertAccessControl = async (username: string, repository: string,
const { data: _data, error: _error } = await supabase.from("access").insert({
created_at: new Date().toUTCString(),
price_access: false,
time_access: false,
time_access: true,
multiplier_access: false,
priority_access: false,
...properties,
Expand Down Expand Up @@ -522,3 +522,55 @@ export const savePermit = async (permit: InsertPermit): Promise<Permit> => {
}
return getPermitFromDbData(data[0]);
};

export const saveLabelChange = async (username: string, repository: string, label_from: string, label_to: string, hasAccess: boolean) => {
const { supabase } = getAdapters();
const { data, error } = await supabase
.from("label_changes")
.insert({
username,
repository,
label_from,
label_to,
authorized: hasAccess || false,
created: new Date().toISOString(),
updated: new Date().toISOString(),
})
.select();
if (error) {
throw new Error(error.message);
}
if (!data || data.length === 0) {
throw new Error("No data returned");
}
return data[0];
};

export const getLabelChanges = async (repository: string, labels: string[]) => {
const { supabase } = getAdapters();
const logger = getLogger();

const { data, error } = await supabase.from("label_changes").select("*").in("label_to", labels).eq("repository", repository).eq("authorized", false);

logger.debug(`Getting label changes done, { data: ${JSON.stringify(data)}, error: ${JSON.stringify(error)} }`);

if (error) {
throw new Error(`Error getting label changes: ${error.message}`);
}

if (data.length === 0) {
return null;
}
return data[0];
};

export const _approveLabelChange = async (changeId: number) => {
const { supabase } = getAdapters();
const { error } = await supabase.from("label_changes").update({ authorized: true }).eq("id", changeId);

if (error) {
throw new Error(error.message);
}

return;
};
1 change: 1 addition & 0 deletions src/assets/fonts/proxima-nova-regular-b64.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/configs/shared.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// cspell:disable
export const COLORS = {
default: "ededed",
price: "1f883d",
};
// cspell:enable
export const DEFAULT_BOT_DELAY = 100; // 100ms
export const DEFAULT_TIME_RANGE_FOR_MAX_ISSUE = 24;
export const DEFAULT_TIME_RANGE_FOR_MAX_ISSUE_ENABLED = true;
Expand Down
12 changes: 6 additions & 6 deletions src/handlers/assign/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ export const commentWithAssignMessage = async (): Promise<void> => {
return;
}

const curDate = new Date();
const curDateInMillisecs = curDate.getTime();
const endDate = new Date(curDateInMillisecs + duration * 1000);
const commit_msg = `${flattened_assignees} ${deadLinePrefix} ${endDate.toUTCString().replace("GMT", "UTC")}`;
logger.debug(`Creating an issue comment, commit_msg: ${commit_msg}`);
const currentDate = new Date();
const currentDateInMilliseconds = currentDate.getTime();
const endDate = new Date(currentDateInMilliseconds + duration * 1000);
const commitMessage = `${flattened_assignees} ${deadLinePrefix} ${endDate.toUTCString().replace("GMT", "UTC")}`;
logger.debug(`Creating an issue comment, commit_msg: ${commitMessage}`);

await addCommentToIssue(commit_msg, payload.issue?.number);
await addCommentToIssue(commitMessage, payload.issue?.number);
};

export const closePullRequestForAnIssue = async (): Promise<void> => {
Expand Down
1 change: 1 addition & 0 deletions src/handlers/comment/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export enum IssueCommentCommands {

ALLOW = "/allow",
AUTOPAY = "/autopay",
AUTHORIZE = "/authorize",
}
2 changes: 1 addition & 1 deletion src/handlers/comment/handlers/assign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const assign = async (body: string) => {
commit: `@${payload.sender.login} ${deadLinePrefix} ${endTime.toUTCString()}`,
tips: `<h6>Tips:</h6>
<ul>
<li>Use <code>/wallet 0x0000...0000</code> if you want to update your registered payment wallet address @user.</li>
<li>Use <code>/wallet 0x0000...0000</code> if you want to update your registered payment wallet address @${payload.sender.login}.</li>
<li>Be sure to open a draft pull request as soon as possible to communicate updates on your progress.</li>
<li>Be sure to provide timely updates to us when requested, or you will be automatically unassigned from the bounty.</li>
<ul>`,
Expand Down
45 changes: 45 additions & 0 deletions src/handlers/comment/handlers/authorize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { _approveLabelChange, getLabelChanges } from "../../../adapters/supabase";
import { getBotContext, getLogger } from "../../../bindings";
import { getUserPermission } from "../../../helpers";
import { Payload } from "../../../types";
import { ErrorDiff } from "../../../utils/helpers";
import { bountyInfo } from "../../wildcard";

export const approveLabelChange = async () => {
const context = getBotContext();
const logger = getLogger();
const payload = context.payload as Payload;
const sender = payload.sender.login;

logger.info(`Received '/authorize' command from user: ${sender}`);

const { issue, repository } = payload;
if (!issue) {
logger.info(`Skipping '/authorize' because of no issue instance`);
return;
}

// check if sender is admin
// passing in context so we don't have to make another request to get the user
const permissionLevel = await getUserPermission(sender, context);

// if sender is not admin, return
if (permissionLevel !== "admin" && permissionLevel !== "billing_manager") {
logger.info(`User ${sender} is not an admin/billing_manager`);
return ErrorDiff(`You are not an admin/billing_manager and do not have the required permissions to access this function.`);
}

const issueDetailed = bountyInfo(issue);

if (!issueDetailed.priceLabel || !issueDetailed.priorityLabel || !issueDetailed.timelabel) {
logger.info(`Skipping... its not a bounty`);
return ErrorDiff(`No valid bounty label on this issue`);
}

// check for label altering here
const labelChanges = await getLabelChanges(repository.full_name, [issueDetailed.priceLabel, issueDetailed.priorityLabel, issueDetailed.timelabel]);

await _approveLabelChange(labelChanges.id);

return `Label change has been approved, permit can now be generated`;
};
8 changes: 8 additions & 0 deletions src/handlers/comment/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { listAvailableCommands } from "./help";
// import { payout } from "./payout";
import { unassign } from "./unassign";
import { registerWallet } from "./wallet";
import { approveLabelChange } from "./authorize";
import { setAccess } from "./allow";
import { ask } from "./ask";
import { multiplier } from "./multiplier";
Expand Down Expand Up @@ -46,6 +47,7 @@ export * from "./help";
export * from "./multiplier";
export * from "./query";
export * from "./ask";
export * from "./authorize";

export interface RewardsResponse {
error: string | null;
Expand Down Expand Up @@ -304,6 +306,12 @@ export const userCommands = (): UserCommands[] => {
handler: setAccess,
callback: commandCallback,
},
{
id: IssueCommentCommands.AUTHORIZE,
description: `Approve a label change. Superuser only.`,
handler: approveLabelChange,
callback: commandCallback,
},
{
id: IssueCommentCommands.WALLET,
description: config.wallet.registerWalletWithVerification
Expand Down
21 changes: 14 additions & 7 deletions src/handlers/comment/handlers/query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getAllAccessLevels, getWalletInfo } from "../../../adapters/supabase";
import { getAllAccessLevels, getWalletInfo, upsertAccessControl } from "../../../adapters/supabase";
import { getBotContext, getLogger } from "../../../bindings";
import { Payload } from "../../../types";
import { ErrorDiff } from "../../../utils/helpers";

export const query = async (body: string) => {
const context = getBotContext();
Expand Down Expand Up @@ -28,12 +29,18 @@ export const query = async (body: string) => {
let data = await getAllAccessLevels(user, repo.full_name);
if (!data) {
logger.info(`Access info does not exist for @${user}`);
data = {
multiplier: false,
priority: false,
time: true,
price: false,
};
try {
await upsertAccessControl(user, repo.full_name, "time_access", true);
data = {
multiplier: false,
priority: false,
time: true,
price: false,
};
} catch (e) {
ErrorDiff(e);
return `Error upserting access info for @${user}`;
}
}
const walletInfo = await getWalletInfo(user, id?.toString());
if (!walletInfo?.address) {
Expand Down
30 changes: 30 additions & 0 deletions src/handlers/label/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { saveLabelChange } from "../../adapters/supabase";
import { getBotContext, getLogger } from "../../bindings";
import { hasLabelEditPermission } from "../../helpers";
import { Payload } from "../../types";

export const watchLabelChange = async () => {
const logger = getLogger();
const context = getBotContext();

const payload = context.payload as Payload;

const { repository, label, changes, sender } = payload;

const { full_name } = repository;

const previousLabel = changes?.name.from;
const currentLabel = label?.name;
const triggerUser = sender.login;

if (!previousLabel || !currentLabel) {
logger.debug("watchLabelChange: No label name change.. skipping");
return;
}

// check if user is authorized to make the change
const hasAccess = await hasLabelEditPermission(currentLabel, triggerUser, repository.full_name);

await saveLabelChange(triggerUser, full_name, previousLabel, currentLabel, hasAccess);
logger.debug("watchLabelChange: label name change saved to db");
};
13 changes: 12 additions & 1 deletion src/handlers/payout/action.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BigNumber, ethers } from "ethers";
import { getPenalty, getWalletAddress, getWalletMultiplier, removePenalty } from "../../adapters/supabase";
import { getLabelChanges, getPenalty, getWalletAddress, getWalletMultiplier, removePenalty } from "../../adapters/supabase";
import { getBotConfig, getBotContext, getLogger } from "../../bindings";
import {
addLabelToIssue,
Expand Down Expand Up @@ -194,6 +194,17 @@ export const incentivesCalculation = async (): Promise<IncentivesCalculationResu
throw new Error(`Permit generation disabled because this issue didn't qualify as bounty.`);
}

// check for label altering here
const labelChanges = await getLabelChanges(repository.full_name, [issueDetailed.priceLabel, issueDetailed.priorityLabel, issueDetailed.timelabel]);

if (labelChanges) {
// if approved is still false, it means user was certainly not authorized for that edit
if (!labelChanges.approved) {
logger.info(`Skipping... label was changed by unauthorized user`);
throw new Error(`Permit generation disabled because label: "${labelChanges.label_to}" was modified by an unauthorized user`);
}
}

const assignees = issue?.assignees ?? [];
const assignee = assignees.length > 0 ? assignees[0] : undefined;
if (!assignee) {
Expand Down
6 changes: 6 additions & 0 deletions src/handlers/processors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { checkPullRequests } from "./assign/auto";
import { createDevPoolPR } from "./pull-request";
import { runOnPush, validateConfigChange } from "./push";
import { findDuplicateOne } from "./issue";
import { watchLabelChange } from "./label";

export const processors: Record<string, Handler> = {
[GithubEvent.ISSUES_OPENED]: {
Expand Down Expand Up @@ -70,6 +71,11 @@ export const processors: Record<string, Handler> = {
action: [validateConfigChange, runOnPush],
post: [nullHandler],
},
[GithubEvent.LABEL_EDITED]: {
pre: [nullHandler],
action: [watchLabelChange],
post: [nullHandler],
},
};

/**
Expand Down
Loading

0 comments on commit 31be5c6

Please sign in to comment.