Skip to content

Instantly share code, notes, and snippets.

@shekohex
Last active April 10, 2023 16:27
Show Gist options
  • Save shekohex/c10468fbb76a9b8393667d6c2f45fd27 to your computer and use it in GitHub Desktop.
Save shekohex/c10468fbb76a9b8393667d6c2f45fd27 to your computer and use it in GitHub Desktop.
Generates KPIs for github repo
#!/usr/bin/env -S deno run -A
/**
* This script is used to generate the KPIs for a repository, by fetching all
* the issues/bugs that got resolved and the PRs that got merged in the specified time period.
* It then outputs the KPIs in a Markdown format.
*/
// Import the required modules, we need the following:
// * cli - for parsing the command line arguments
// * github - for fetching the issues/PRs from GitHub
// * date - for parsing the date arguments
import * as flags from "https://deno.land/[email protected]/flags/mod.ts";
import * as datetime from "https://deno.land/[email protected]/datetime/mod.ts";
import { Octokit } from "npm:octokit@^2";
// Define the command line arguments that we need
// * org - the GitHub organization
// * repo - the GitHub repository
// * month - the month for which we want to generate the KPIs
// * year - the year for which we want to generate the KPIs
const args = flags.parse(Deno.args, {
string: ["org", "repo", "month", "year"],
alias: {
org: "o",
repo: "r",
month: "m",
year: "y",
},
default: {
org: "webb-tools",
repo: "relayer",
month: "jan",
year: datetime.format(new Date(), "yyyy"),
},
stopEarly: true,
});
type Options = {
org: string;
repo: string;
startDate: Date;
endDate: Date;
};
const monthNameToNumber = (month: string) =>
[
["jan", "january"],
["feb", "february"],
["mar", "march"],
["apr", "april"],
["may", "may"],
["jun", "june"],
["jul", "july"],
["aug", "august"],
["sep", "september"],
["oct", "october"],
["nov", "november"],
["dec", "december"],
].findIndex((names) => names.includes(month.toLowerCase()));
function getOptions(v: typeof args): Options {
// parse the month name to a number
const monthIndex = monthNameToNumber(v.month);
if (monthIndex === -1) {
throw new Error(`Invalid month name: ${v.month}`);
}
const year = datetime.parse(v.year, "yyyy").getFullYear();
const startDate = new Date(year, monthIndex, 1);
const endDate = new Date(year, monthIndex + 1, 0);
return {
org: args.org,
repo: args.repo,
startDate,
endDate,
};
}
const options = getOptions(args);
// Create the Octokit client
const octokit: Octokit = new Octokit({
auth: Deno.env.get("GITHUB_TOKEN"),
});
// Fetch all the issues that got closed in the specified time period
const issues = octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
owner: options.org,
repo: options.repo,
since: options.startDate.toISOString(),
state: "all",
per_page: 50,
});
// Fetch all the PRs that got merged in the specified time period
// Note: We need to fetch the PRs separately, because the issues API does not
// return the PRs that got merged.
const prs = octokit.paginate.iterator(octokit.rest.pulls.list, {
owner: options.org,
repo: options.repo,
since: options.startDate.toISOString(),
state: "all",
per_page: 50,
});
let totalIssuesCount = 0;
let totalClosedIssuesCount = 0;
let totalPRsCount = 0;
let totalMergedPRsCount = 0;
// Print the KPIs in a Markdown format
// * List of issues closed
// * List of PRs merged
// * A table with the total issues, closed issues, PRs and merged PRs
const startDateString = datetime.format(options.startDate, "yyyy-MM-dd");
const endDateString = datetime.format(options.endDate, "yyyy-MM-dd");
console.log(
`# KPIs for ${options.repo} in ${startDateString} - ${endDateString}`
);
console.log("## Issues");
for await (const { data: _issues } of issues) {
for (const issue of _issues) {
// filter out any issues that are not created in the specified time period
const createdAt = new Date(issue.created_at);
if (createdAt > options.endDate) {
continue;
}
if (issue.pull_request) {
continue;
}
if (issue.state === "closed") {
console.log(`- ${issue.title} [(#${issue.number})](${issue.html_url})`);
totalClosedIssuesCount++;
}
totalIssuesCount++;
}
}
console.log("## PRs");
for await (const { data: _prs } of prs) {
for (const pr of _prs) {
const createdAt = new Date(pr.created_at);
// filter out any PRs that are not created in the specified time period
if (createdAt < options.startDate) {
continue;
}
if (pr.merged_at) {
console.log(`- ${pr.title} [(#${pr.number})](${pr.html_url})`);
totalMergedPRsCount++;
}
totalPRsCount++;
}
}
console.log(`## Total`);
console.log(`| Issues | Closed Issues | PRs | Merged PRs |`);
console.log(`| --- | --- | --- | --- |`);
console.log(
`| ${totalIssuesCount} | ${totalClosedIssuesCount} | ${totalPRsCount} | ${totalMergedPRsCount} |`
);
console.log();
Deno.exit(0);
@shekohex
Copy link
Author

To run the above script use the following command:

  • Without Github Token
deno run -A https://gist.github.com/shekohex/c10468fbb76a9b8393667d6c2f45fd27/raw/e7f4158b64e396f501a25afe359ff71b79b1ae94/generate-kpi.ts -o <YOUR_ORG> -r <YOUR_REPO> -m jan -y 2023

Optionally, for better performance and not hitting API rate limit, use a github token

GITHUB_TOKEN=<YOUR_TOKEN> deno run -A https://gist.github.com/shekohex/c10468fbb76a9b8393667d6c2f45fd27/raw/e7f4158b64e396f501a25afe359ff71b79b1ae94/generate-kpi.ts -o <YOUR_ORG> -r <YOUR_REPO> -m jan -y 2023

Or if you have Github CLI installed, and logged in:

GITHUB_TOKEN=$(gh auth token) deno run -A https://gist.github.com/shekohex/c10468fbb76a9b8393667d6c2f45fd27/raw/e7f4158b64e396f501a25afe359ff71b79b1ae94/generate-kpi.ts -o <YOUR_ORG> -r <YOUR_REPO> -m jan -y 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment