// Github Pull requests // v2.0 // Noam Knispel // noamknispel // Get list of pull requests from Github for multiple repositories // node.js request co bluebird import nodeFetch, { FetchError } from 'node-fetch'; const GITHUB_API = 'https://api.github.com'; // make fetch sane const fetch = async (...args) => { const res = await nodeFetch(...args); if (res.ok) return res; throw new FetchError(`Unsuccessful Request (${args[0]}) [${res.status}]`); }; const api = ({ username, token }) => { const getPull = async (repo, id) => fetch(`${GITHUB_API}/repos/${repo}/pulls/${id}`, { method: 'GET', headers: { Authorization: `Basic ${Buffer.from(`${username}:${token}`).toString('base64')}`, }, }).then(res => res.json()); return { getPulls: async repo => { const issues = await fetch( `${GITHUB_API}/repos/${repo}/issues?state=open&sort=updated&creator=w33ble`, { method: 'GET', headers: { Authorization: `Basic ${Buffer.from(`${username}:${token}`).toString('base64')}`, }, } ).then(res => res.json()); // remove non-pull issues const pulls = issues.filter(issue => !!issue.pull_request); // return actual pull info return Promise.all(pulls.map(pull => getPull(repo, pull.number))); }, getStatus: async (repo, sha) => fetch(`${GITHUB_API}/repos/${repo}/commits/${sha}/status`, { method: 'GET', headers: { Authorization: `Basic ${Buffer.from(`${username}:${token}`).toString('base64')}`, }, }).then(res => res.json()), }; }; const getStatusColor = status => { if (status === 'failure') return ' color=#aa0000'; if (status === 'success') return ' color=#00aa00'; if (status === 'pending') return ' color=#aaaa00'; return ''; }; export default async ({ username, token, repos = [] }) => { const { getPulls, getStatus } = api({ username, token }); let failed = false; const tasks = await (() => { try { return Promise.all( repos.map(async repo => { // fetch pulls for repo const pulls = await getPulls(repo); // fetch status for each pull return Promise.all( pulls.map(async pull => { const status = await getStatus(repo, pull.head.sha); return { repo, pull, status }; }) ); }, []) ); } catch (err) { console.log(`Request failed: ${err.message}`); failed = true; return false; } })(); if (failed) return false; const { totalCount, failCount, strings } = tasks.reduce( (acc, info) => { // skip repo if there are no matches if (info.length <= 0) return acc; // build bitbar output info.forEach(({ repo, pull, status }, i) => { // append repo name if (i === 0) acc.strings.push(`${repo} | color=#0000ff`); // append information about each PR acc.strings.push( `#${pull.number} ${pull.title} | href=${pull.html_url} ${getStatusColor(status.state)}` ); // add count to tally acc.totalCount += 1; if (status.state === 'failure') acc.failCount += 1; }); return acc; }, { totalCount: 0, failCount: 0, strings: ['---'] } ); strings.unshift(`${totalCount}/${failCount} PRs`); strings.forEach(s => console.log(s)); return true; };