Skip to content

Instantly share code, notes, and snippets.

@ClementParis016
Last active May 5, 2022 09:34
Show Gist options
  • Save ClementParis016/c911cf1f1df7af09ff8e0e5a85cc4d0a to your computer and use it in GitHub Desktop.
Save ClementParis016/c911cf1f1df7af09ff8e0e5a85cc4d0a to your computer and use it in GitHub Desktop.
Extract commits from GitHub org to CSV
const { Octokit } = require("@octokit/rest");
const fastCsv = require("fast-csv");
const fs = require("fs");
const consola = require("consola");
process.on("unhandledRejection", (error, promise) => {
consola.fatal(`Unhandled rejection! ${error.name}: ${error.message}`, {
error,
promise,
});
process.exit(1);
});
process.on("uncaughtException", (error) => {
consola.fatal(`Uncaught exception! ${error.name}: ${error.message}`, {
error,
});
process.exit(1);
});
(async () => {
consola.info("Preparing...");
fs.unlinkSync("out.csv"); // <===== FAILS IF THE FILE DOESN'T EXIST, SO CREATE IT FIRST OR REMOVE THIS LINE, NOT SURE IT'S NEEDED
const fileStream = fs.createWriteStream("out.csv");
const csvStream = fastCsv.format({ headers: true });
csvStream.pipe(fileStream).on("end", () => process.exit());
const octokit = new Octokit({
auth: process.env.GH_TOKEN, // <===== SHOULD BE A PERSONAL ACCESS TOKEN WITH FULL REPO PERMISSIONS
timeZone: "Europe/Paris", // <===== NOT SURE THIS MATTERS
});
consola.info("Ready!");
consola.info("Fetching repos...");
const repos = await octokit.paginate("GET /orgs/{org}/repos", {
org: "<org-name>", // <===== REPLACE WITH TARGET GITHUB ORG
});
consola.debug(`Fetched ${repos.length} repos...`);
for (const repo of repos) {
consola.info(`Processing ${repo.name} repo...`);
consola.info(`Fetching commits for repo ${repo.name}...`);
let commits;
try {
commits = await octokit.paginate(octokit.rest.repos.listCommits, {
owner: "<org-name>", // <===== REPLACE WITH TARGET GITHUB ORG
repo: repo.name,
since: "2021-02-04T00:00:00Z",
});
} catch (error) {
if (error.message.includes("Repository is empty")) {
consola.warn(`Repo ${repo.name} is empty, skipping...`);
continue;
} else {
throw error;
}
}
consola.debug(`Fetched ${commits.length} commits for ${repo.name} repo...`);
for (const commit of commits) {
let entry;
try {
entry = {
repo: repo.name,
url: commit.html_url,
message: commit.commit.message,
author_login: commit.author?.login ?? "UNKNOWN LOGIN",
author_name: commit.commit.author.name,
author_email: commit.commit.author.email,
authored_at: commit.commit.author.date,
author_url: commit.author?.html_url ?? "N/A",
committer_login: commit.committer?.login ?? "UNKNOWN LOGIN",
committer_name: commit.commit.committer.name,
committer_email: commit.commit.committer.email,
committer_url: commit.committer?.html_url ?? "N/A",
committed_at: commit.commit.committer.date,
};
} catch (error) {
consola.error(`Error with commit ${commit.html_url}`, { commit });
throw error;
}
csvStream.write(entry);
}
consola.success(`Wrote ${commits.length} commits for ${repo.name} repo...`);
}
consola.success("Done!");
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment