Skip to content

Instantly share code, notes, and snippets.

@zstix
Created May 5, 2021 00:09
Show Gist options
  • Save zstix/bfee01a6f13d0c9c6afd6b5880bcdcd3 to your computer and use it in GitHub Desktop.
Save zstix/bfee01a6f13d0c9c6afd6b5880bcdcd3 to your computer and use it in GitHub Desktop.
// Generates a simplified kanban board view for a Github project.
// Author: Zack Stickles
//
// Requires a GH_TOKEN is set in the environment (with org and repo read permissions).
/*
*/
const { Octokit } = require("@octokit/rest");
const ORG_NAME = "newrelic";
const PROJECT_NAME = "Developer Enablement Team";
const COLUMN_NAMES = ["To do", "In progress", "In review", "Blocked", "Done"];
const MILESTONE_NAME = "[DevEn] Sprint 3";
const STORY_POINT_PREFIX = "sp:"; // labels with the prefix are used for points (e.g. `sp:3`)
const withUnderline = (str) => `\x1b[4m${str}\x1b[0m`;
const getPoints = (labels) => {
const label = labels.find(
({ name }) =>
name && name.substr(0, STORY_POINT_PREFIX.length) === STORY_POINT_PREFIX
);
return label ? parseInt(label.name.replace(STORY_POINT_PREFIX, ""), 10) : 0;
};
const getIssueData = async (github, url) => {
const { data } = await github.request({ url });
return {
title: data.title || "Unknown",
milestone: data.milestone ? data.milestone.title : "",
repo: data.repository_url.replace(
`https://api.github.com/repos/${ORG_NAME}/`,
``
),
number: data.number,
points: data.labels ? getPoints(data.labels) : 0,
};
};
(async () => {
const github = new Octokit({
auth: process.env.GH_TOKEN,
});
// Get the project
const { data: projects } = await github.projects.listForOrg({
org: ORG_NAME,
});
const project = projects.find(({ name }) => name === PROJECT_NAME);
// Get the columns we care about
const { data: allColumns } = await github.projects.listColumns({
project_id: project.id,
});
const columns = COLUMN_NAMES.map((name) =>
allColumns.find((column) => column.name === name)
);
// Get the issues & points for each column
const columnsWithIssues = await Promise.all(
columns.map(async ({ name, id }) => {
// get cards
const { data: cards } = await github.projects.listCards({
column_id: id,
archived_state: "not_archived",
per_page: 100,
});
// get issues, with the details we care about
const allIssues = await Promise.all(
cards.map(async (card) => getIssueData(github, card.content_url))
);
const issues = allIssues.filter(
({ milestone, points }) => milestone === MILESTONE_NAME && points > 0
);
// return { name, issues: allIssues };
return { name, issues };
})
);
// Display the data
for (let { name, issues } of columnsWithIssues) {
const totalPoints = issues.reduce((sum, { points }) => sum + points, 0);
console.log(`[${totalPoints}]`, withUnderline(name));
for (let { title, number, points, repo } of issues) {
console.log(
`\t[${points}] #${number} ${title.substr(0, 30)}... (${repo})`
);
}
}
})();
@zstix
Copy link
Author

zstix commented Jul 21, 2021

This is a bit messy, but it gets the job done.

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