Skip to content

Commit

Permalink
Merge branch 'development' of https://github.com/ubiquity/devpool-dir…
Browse files Browse the repository at this point in the history
…ectory into development
  • Loading branch information
0x4007 committed Oct 18, 2024
2 parents 6b447ae + 1ea661d commit b570de6
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 260 deletions.
1 change: 0 additions & 1 deletion devpool-issues.json

This file was deleted.

1 change: 0 additions & 1 deletion devpool-statistics.json

This file was deleted.

2 changes: 2 additions & 0 deletions helpers/directory/directory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { RestEndpointMethodTypes } from "@octokit/plugin-rest-endpoint-methods";
import { Octokit } from "@octokit/rest";
import dotenv from "dotenv";
import _projects from "../../projects.json";
dotenv.config();

export const octokit = new Octokit({ auth: process.env.DEVPOOL_GITHUB_API_TOKEN });

Expand Down
7 changes: 0 additions & 7 deletions helpers/directory/get-all-issues.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { GitHubIssue, octokit } from "./directory";

/**
* Returns all issues in a repo
* @param ownerName owner name
* @param repoName repo name
* @returns array of issues
*/

export async function getAllIssues(ownerName: string, repoName: string) {
// get all project issues (opened and closed)
let issues: GitHubIssue[] = await octokit.paginate({
Expand Down
54 changes: 15 additions & 39 deletions helpers/directory/get-directory-issue-labels.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,26 @@
import { GitHubIssue, GitHubLabel, LABELS, PRICING_NOT_SET, projects } from "./directory";
import { getIssuePriceLabel } from "./get-issue-price-label";
import { getRepoCredentials } from "./get-repo-credentials";

/**
* Returns array of labels for a devpool issue
* @param issue issue object
* @param projectUrl url of the project
*/
export function getDirectoryIssueLabelsFromPartnerIssue(partnerIssue: GitHubIssue) {
const buffer: string[] = [`id: ${partnerIssue.node_id}`];
const pricing = getIssuePriceLabel(partnerIssue);

export function getDirectoryIssueLabels(issue: GitHubIssue, projectUrl: string) {
// get owner and repo name from issue's URL because the repo name could be updated
const [ownerName, repoName] = getRepoCredentials(issue.html_url);

const pricing = getIssuePriceLabel(issue);

let devpoolIssueLabels: string[];

// default labels
if (pricing != PRICING_NOT_SET) {
devpoolIssueLabels = [
pricing,
`Partner: ${ownerName}/${repoName}`,
`id: ${issue.node_id}`, // id
];
} else {
devpoolIssueLabels = [
`Partner: ${ownerName}/${repoName}`,
`id: ${issue.node_id}`, // id
];
buffer.push(pricing);
}

// if project is already assigned then add the "Unavailable" label
if (issue.assignee?.login) devpoolIssueLabels.push(LABELS.UNAVAILABLE);

const labels = issue.labels as GitHubLabel[];

// add all missing labels that exist in a project's issue and don't exist in devpool issue
for (const projectIssueLabel of labels) {
// skip the "Price" label in order to not accidentally generate a permit
if (projectIssueLabel.name.includes("Price")) continue;
// if project issue label does not exist in devpool issue then add it
if (!devpoolIssueLabels.includes(projectIssueLabel.name)) devpoolIssueLabels.push(projectIssueLabel.name);
if (partnerIssue.assignees && partnerIssue.assignees.length > 0) {
buffer.push(LABELS.UNAVAILABLE);
}

// if project category for the project is defined, add its category label
if (projects.category && projectUrl in projects.category) devpoolIssueLabels.push(projects.category[projectUrl]);

return devpoolIssueLabels;
const partnerIssueLabels = partnerIssue.labels as GitHubLabel[];
// add all missing labels that exist in a project's issue and don't exist in Directory issue
for (const label of partnerIssueLabels) {
// add all missing labels that exist in a project's issue and don't exist in Directory issue
if (label.name.includes("Price")) continue; // skip the "Price" label in order to not accidentally generate a permit
if (!buffer.includes(label.name)) buffer.push(label.name); // if project issue label does not exist in Directory issue then add it
}
if (projects.category && partnerIssue.html_url in projects.category) buffer.push(projects.category[partnerIssue.html_url]); // if project category for the project is defined, add its category label
return buffer;
}
46 changes: 46 additions & 0 deletions helpers/directory/label-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { DEVPOOL_OWNER_NAME, DEVPOOL_REPO_NAME, GitHubLabel, octokit } from "./directory";

// Function to check if a label exists
export async function ensureLabelExists(labelName: string, labelColor: string, labelDescription: string): Promise<void> {
try {
// Fetch all labels in the repository
let labels = [] as GitHubLabel[];
let page = 1;
let hasNextPage = true;

while (hasNextPage) {
const response = await octokit.rest.issues.listLabelsForRepo({
owner: DEVPOOL_OWNER_NAME,
repo: DEVPOOL_REPO_NAME,
per_page: 100,
page: page,
});

labels = labels.concat(response.data);

if (response.data.length < 100) {
hasNextPage = false;
} else {
page++;
}
}

// Check if the label already exists
const isLabelPresent = labels.some((label) => label.name === labelName);

// If the label does not exist, create it
if (!isLabelPresent) {
await octokit.rest.issues.createLabel({
owner: DEVPOOL_OWNER_NAME,
repo: DEVPOOL_REPO_NAME,
name: labelName,
color: "ededed",
description: labelDescription,
});
console.log(`Created label "${labelName}"`);
}
} catch (error) {
console.error(`Error ensuring label "${labelName}" exists:`, error);
throw error; // Rethrow the error after logging
}
}
32 changes: 20 additions & 12 deletions helpers/directory/new-directory-issue.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import { commitTwitterMap } from "../git";
import { TwitterMap } from "../initialize-twitter-map";
import twitter from "../twitter";
import { checkIfForked } from "./check-if-forked";
import { DEVPOOL_OWNER_NAME, DEVPOOL_REPO_NAME, GitHubIssue, GitHubLabel, LABELS, octokit } from "./directory";
import { getDirectoryIssueLabels } from "./get-directory-issue-labels";
import { getDirectoryIssueLabelsFromPartnerIssue } from "./get-directory-issue-labels";
import { getSocialMediaText } from "./get-social-media-text";

export async function newDirectoryIssue(projectIssue: GitHubIssue, projectUrl: string, body: string, twitterMap: TwitterMap) {
// if issue is "closed" then skip it, no need to copy/paste already "closed" issues
if (projectIssue.state === "closed") return;
export async function newDirectoryIssue(partnerIssue: GitHubIssue, projectUrl: string, twitterMap: TwitterMap) {
if (partnerIssue.state === "closed") return; // if issue is "closed" then skip it, no need to copy/paste already "closed" issues

// if the project issue is assigned to someone, then skip it
if (projectIssue.assignee) return;
const hasPriceLabel = (partnerIssue.labels as GitHubLabel[]).some((label) => label.name.includes(LABELS.PRICE)); // check if the issue is the same type as it should be

// check if the issue is the same type as it should be
const hasPriceLabel = (projectIssue.labels as GitHubLabel[]).some((label) => label.name.includes(LABELS.PRICE));
let body;
if (await checkIfForked()) {
body = partnerIssue.html_url.replace("https://github.com", "https://www.github.com");
} else {
body = partnerIssue.html_url;
}

// create a new issue
try {
const createdIssue = await octokit.rest.issues.create({
owner: DEVPOOL_OWNER_NAME,
repo: DEVPOOL_REPO_NAME,
title: projectIssue.title,
title: partnerIssue.title,
body,
labels: getDirectoryIssueLabels(projectIssue, projectUrl),
labels: getDirectoryIssueLabelsFromPartnerIssue(partnerIssue),
});
console.log(`Created: ${createdIssue.data.html_url} (${projectIssue.html_url})`);
console.log(`Created: ${createdIssue.data.html_url} (${partnerIssue.html_url})`);

if (!createdIssue) {
console.log("No new issue to tweet about");
Expand All @@ -46,7 +49,12 @@ export async function newDirectoryIssue(projectIssue: GitHubIssue, projectUrl: s
}
}
} catch (err) {
console.error("Failed to create new issue: ", err);
console.error("Failed to create new issue:", {
partnerIssueTitle: partnerIssue.title,
partnerIssueUrl: partnerIssue.html_url,
projectUrl,
error: err.message,
});
return;
}
}
38 changes: 17 additions & 21 deletions helpers/directory/set-meta-changes.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
import { DEVPOOL_OWNER_NAME, DEVPOOL_REPO_NAME, GitHubIssue, octokit } from "./directory";
import { checkIfForked } from "./check-if-forked";
import { DEVPOOL_OWNER_NAME, DEVPOOL_REPO_NAME, octokit } from "./directory";
import { MetadataInterface } from "./sync-issue-meta-data";

export async function setMetaChanges({
metaChanges,
remoteFullIssue,
directoryIssue,
labelRemoved,
originalLabels,
}: {
metaChanges: { title: boolean; body: boolean; labels: boolean };
remoteFullIssue: GitHubIssue;
directoryIssue: GitHubIssue;
labelRemoved: string[];
originalLabels: string[];
}) {
export async function setMetaChanges({ metaChanges, partnerIssue, directoryIssue, labelRemoved, originalLabels }: MetadataInterface) {
const shouldUpdate = metaChanges.title || metaChanges.body || metaChanges.labels;

if (shouldUpdate) {
let newBody = remoteFullIssue.body;

newBody = directoryIssue.html_url;
let directoryIssueBody = partnerIssue.html_url;
const isFork = await checkIfForked();
if (isFork) {
directoryIssueBody = partnerIssue.html_url.replace("https://github.com", "https://www.github.com");
}

try {
await octokit.rest.issues.update({
owner: DEVPOOL_OWNER_NAME,
repo: DEVPOOL_REPO_NAME,
issue_number: remoteFullIssue.number,
title: metaChanges.title ? directoryIssue.title : remoteFullIssue.title,
body: newBody,
issue_number: directoryIssue.number,
title: metaChanges.title ? directoryIssue.title : directoryIssue.title,
body: directoryIssueBody,
labels: metaChanges.labels ? labelRemoved : originalLabels,
});
} catch (err) {
console.error(err);
}

console.log(`Updated metadata: ${remoteFullIssue.html_url} - (${directoryIssue.html_url})`, metaChanges);
console.log(`Updated metadata for issue:`, {
partnerIssueUrl: partnerIssue.html_url,
directoryIssueUrl: directoryIssue.html_url,
changes: metaChanges,
});
}
}
22 changes: 11 additions & 11 deletions helpers/directory/set-state-changes.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
import { DEVPOOL_OWNER_NAME, DEVPOOL_REPO_NAME, GitHubIssue, octokit, StateChanges } from "./directory";
import { getIssueLabelValue } from "./get-issue-label-value";

export async function setStateChanges(directoryIssues: GitHubIssue[], directoryIssue: GitHubIssue, remoteIssue: GitHubIssue) {
export async function setStateChanges(directoryIssues: GitHubIssue[], directoryIssue: GitHubIssue, partnerIssue: GitHubIssue) {
const stateChanges: StateChanges = {
// missing in the partners
forceMissing_Close: {
cause: !directoryIssues.some((projectIssue) => projectIssue.node_id === getIssueLabelValue(remoteIssue, "id:")),
cause: !directoryIssues.some((projectIssue) => projectIssue.node_id === getIssueLabelValue(partnerIssue, "id:")),
effect: "closed",
comment: "Closed (missing in partners)",
},
// it's closed, been merged and still open in the Directory
issueComplete_Close: {
cause: directoryIssue.state === "closed" && remoteIssue.state === "open" && !!directoryIssue.pull_request?.merged_at,
cause: directoryIssue.state === "closed" && partnerIssue.state === "open" && !!directoryIssue.pull_request?.merged_at,
effect: "closed",
comment: "Closed (merged)",
},
// it's closed, assigned and still open in the Directory
issueAssignedClosed_Close: {
cause: directoryIssue.state === "closed" && remoteIssue.state === "open" && !!directoryIssue.assignee?.login,
cause: directoryIssue.state === "closed" && partnerIssue.state === "open" && !!directoryIssue.assignee?.login,
effect: "closed",
comment: "Closed (assigned-closed)",
},
// it's closed, not merged and still open in the Directory
issueClosed_Close: {
cause: directoryIssue.state === "closed" && remoteIssue.state === "open",
cause: directoryIssue.state === "closed" && partnerIssue.state === "open",
effect: "closed",
comment: "Closed (not merged)",
},
// it's open, assigned and still open in the Directory
issueAssignedOpen_Close: {
cause: directoryIssue.state === "open" && remoteIssue.state === "open" && !!directoryIssue.assignee?.login,
cause: directoryIssue.state === "open" && partnerIssue.state === "open" && !!directoryIssue.assignee?.login,
effect: "closed",
comment: "Closed (assigned-open)",
},
// it's open, merged, unassigned and is closed in the Directory
issueReopenedMerged_Open: {
cause: directoryIssue.state === "open" && remoteIssue.state === "closed" && !!directoryIssue.pull_request?.merged_at && !directoryIssue.assignee?.login,
cause: directoryIssue.state === "open" && partnerIssue.state === "closed" && !!directoryIssue.pull_request?.merged_at && !directoryIssue.assignee?.login,
effect: "open",
comment: "Reopened (merged)",
},
// it's open, unassigned and is closed in the Directory
issueUnassigned_Open: {
cause: directoryIssue.state === "open" && remoteIssue.state === "closed" && !directoryIssue.assignee?.login,
cause: directoryIssue.state === "open" && partnerIssue.state === "closed" && !directoryIssue.assignee?.login,
effect: "open",
comment: "Reopened (unassigned)",
},
Expand All @@ -51,7 +51,7 @@ export async function setStateChanges(directoryIssues: GitHubIssue[], directoryI

for (const value of Object.values(stateChanges)) {
// if the cause is true and the effect is different from the current state
if (value.cause && remoteIssue.state != value.effect) {
if (value.cause && partnerIssue.state != value.effect) {
// if the new state is already set, then skip it
if (newState && newState === value.effect) {
continue;
Expand All @@ -61,10 +61,10 @@ export async function setStateChanges(directoryIssues: GitHubIssue[], directoryI
await octokit.rest.issues.update({
owner: DEVPOOL_OWNER_NAME,
repo: DEVPOOL_REPO_NAME,
issue_number: remoteIssue.number,
issue_number: partnerIssue.number,
state: value.effect,
});
console.log(`Updated state: (${value.comment})\n${remoteIssue.html_url} - (${directoryIssue.html_url})`);
console.log(`Updated state: (${value.comment})\n${partnerIssue.html_url} - (${directoryIssue.html_url})`);
newState = value.effect;
} catch (err) {
console.log(err);
Expand Down
Loading

0 comments on commit b570de6

Please sign in to comment.