Last active
April 10, 2023 16:27
-
-
Save shekohex/c10468fbb76a9b8393667d6c2f45fd27 to your computer and use it in GitHub Desktop.
Generates KPIs for github repo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To run the above script use the following command:
Optionally, for better performance and not hitting API rate limit, use a github token
Or if you have Github CLI installed, and logged in: