├── .gitignore ├── .travis.yml ├── Readme.md ├── buildkite.png ├── cli.js ├── graph.js ├── index.css ├── index.html ├── index.js ├── package.json └── web.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '4' 4 | - '5' 5 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | bkboard 2 | ======= 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![build status][travis-image]][travis-url] 6 | [![Downloads][downloads-image]][downloads-url] 7 | [![js-standard-style][standard-image]][standard-url] 8 | 9 | A Buildkite build report dashboard in terminal and web, or anywhere. 10 | 11 | bkboard 12 | 13 | ### Installation 14 | 15 | ```sh 16 | $ npm install bkboard --save 17 | ``` 18 | 19 | ### Usage in browser 20 | 21 | ```JavaScript 22 | var bkboard = require('bkboard') 23 | 24 | /** 25 | * Get the builds data from a pipeline 26 | * @param {String} The token string 27 | * @param {Object} The opts object contains 'from', 'to', 'org', 'pipeline' 28 | * @param {Function} Callback function with `err, builds` 29 | */ 30 | bkboard(token, opts, function (err, builds) { 31 | if (err) { 32 | console.log('err', err) 33 | return 34 | } 35 | 36 | drawBoard(builds) 37 | }) 38 | ``` 39 | 40 | Example `opts` object: 41 | 42 | ```JavaScript 43 | { 44 | from: '2016-03-13T00:00:00Z', 45 | to: '2016-03-18T00:00:00Z', 46 | org: 'xxx', 47 | pipeline: 'xxx' 48 | } 49 | ``` 50 | 51 | ### Usage from terminal 52 | 53 | ```sh 54 | $ npm install bkboard -g 55 | ``` 56 | 57 | You can get Buildkite api_token from https://buildkite.com/user/api-access-tokens and set it with `git config` or pass it to `process.env.BUILDKITE_API_KEY`. 58 | 59 | **Note:** Please make sure you have the `Read Builds (read_builds)` permission enable to be able to read builds from Buildkite API. 60 | 61 | > Permission to list and retrieve details of builds 62 | 63 | ### Exampe 64 | 65 | ```sh 66 | $ bkboard --from='2016-03-13T00:00:00Z' --to='2016-03-16T00:00:00Z' --org='$ORG' --pipeline='$PIPELINE' 67 | ``` 68 | 69 | ### License 70 | 71 | MIT 72 | 73 | [npm-image]: https://img.shields.io/npm/v/bkboard.svg?style=flat-square 74 | [npm-url]: https://npmjs.org/package/bkboard 75 | [travis-image]: https://img.shields.io/travis/fraserxu/bkboard/master.svg?style=flat-square 76 | [travis-url]: https://travis-ci.org/fraserxu/bkboard 77 | [downloads-image]: http://img.shields.io/npm/dm/bkboard.svg?style=flat-square 78 | [downloads-url]: https://npmjs.org/package/bkboard 79 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square 80 | [standard-url]: https://github.com/feross/standard 81 | -------------------------------------------------------------------------------- /buildkite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fraserxu/bkboard/40625055aad5518460bc7aa8916c0ea85b36560e/buildkite.png -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var _ = require('lodash') 4 | var ora = require('ora') 5 | var argv = require('minimist')(process.argv.slice(2)) 6 | var gitConfig = require('git-config') 7 | 8 | var drawBoard = require('./graph.js') 9 | var getBuilds = require('./index.js') 10 | 11 | var COLORS = { 12 | passed: 'green', 13 | failed: 'red' 14 | } 15 | 16 | var config = gitConfig.sync() 17 | var BUILDKITE_API_KEY = config.buildkite.apikey || process.env.BUILDKITE_API_KEY 18 | var spinner = ora('Loading builds from Buildkite...') 19 | 20 | if (!BUILDKITE_API_KEY) { 21 | throw new Error('BUILDKITE_API_KEY not found.') 22 | } 23 | 24 | // list builds for a pipeline 25 | var from = argv._[0] || argv.f || argv.from || '2016-03-13T00:00:00Z' 26 | var to = argv._[1] || argv.t || argv.to || '2016-03-16T00:00:00Z' 27 | var org = argv._[2] || argv.o || argv.org 28 | var pipeline = argv._[3] || argv.p || argv.pipeline 29 | 30 | function groupBuildsByState (builds) { 31 | var buildsByState = _.groupBy(builds, function (build) { 32 | return build.state 33 | }) 34 | 35 | var total = builds.length 36 | var data = Object.keys(buildsByState).map(function (state) { 37 | return { 38 | percent: ((buildsByState[state].length / total) * 100).toFixed(0), 39 | label: state, 40 | color: COLORS[state] || 'cyan' 41 | } 42 | }) 43 | 44 | return data 45 | } 46 | 47 | function groupBuildsByCreator (builds) { 48 | var buildsByCreator = _.groupBy(builds, function (build) { 49 | if (build.creator && build.creator.name) { 50 | return build.creator.name 51 | } else { 52 | return 'Unknown' 53 | } 54 | }) 55 | 56 | var titles = Object.keys(buildsByCreator).map(function (name) { 57 | return name.split(' ')[0] 58 | }) 59 | var data = Object.keys(buildsByCreator).map(function (key) { 60 | return buildsByCreator[key].length 61 | }) 62 | return { 63 | titles: titles, 64 | data: data 65 | } 66 | } 67 | 68 | getBuilds(BUILDKITE_API_KEY, { 69 | org: org, 70 | pipeline: pipeline, 71 | from: from, 72 | to: to 73 | }, function (err, builds) { 74 | if (err) { 75 | console.log('err', err) 76 | spinner.stop() 77 | process.exit(1) 78 | } 79 | 80 | builds = builds.map(function (build) { 81 | return { 82 | state: build.state, 83 | creator: build.creator 84 | } 85 | }) 86 | 87 | var buildsByState = groupBuildsByState(builds) 88 | var buildsByCreator = groupBuildsByCreator(builds) 89 | 90 | drawBoard(buildsByCreator, buildsByState) 91 | }) 92 | 93 | spinner.start() 94 | -------------------------------------------------------------------------------- /graph.js: -------------------------------------------------------------------------------- 1 | var blessed = require('blessed') 2 | var contrib = require('blessed-contrib') 3 | 4 | module.exports = function drawBoard (barData, donutData) { 5 | var screen = blessed.screen() 6 | var Grid = contrib.grid 7 | var grid = new Grid({ 8 | rows: 2, 9 | cols: 1, 10 | screen: screen 11 | }) 12 | 13 | var bar = grid.set(0, 0, 1, 1, contrib.bar, { 14 | label: 'Buildkite Weekly builds by creator chart', 15 | barWidth: 5, 16 | barSpacing: 1, 17 | xOffset: 0, 18 | maxHeight: 5 19 | }) 20 | var donut = grid.set(1, 0, 1, 1, contrib.donut, { 21 | label: 'Buildkite Weekly builds by state chart', 22 | radius: 8, 23 | arcWidth: 3, 24 | remainColor: 'black', 25 | yPadding: 2 26 | }) 27 | 28 | bar.setData(barData) 29 | donut.setData(donutData) 30 | 31 | screen.key(['escape', 'q', 'C-c'], function (ch, key) { 32 | return process.exit(0) 33 | }) 34 | 35 | screen.render() 36 | } 37 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fraserxu/bkboard/40625055aad5518460bc7aa8916c0ea85b36560e/index.css -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var qs = require('querystring') 2 | var linkParser = require('parse-link-header') 3 | var axios = require('axios') 4 | var objectAssign = require('object-assign') 5 | 6 | var BUILDKITE_API_ENDPOINT = 'https://api.buildkite.com/v2/' 7 | 8 | module.exports = function getBuilds (token, opts, cb) { 9 | if (!token) throw new Error('Token is required.') 10 | 11 | function request (endpoint, opts) { 12 | var params = objectAssign({ 13 | access_token: token 14 | }, opts) 15 | return axios(BUILDKITE_API_ENDPOINT + endpoint + '?' + qs.stringify(params)) 16 | } 17 | 18 | var MARKET_BUILDS_URL = 'organizations/' + opts.org + '/pipelines/' + opts.pipeline + '/builds' 19 | request(MARKET_BUILDS_URL, { 20 | 'created_from': opts.from, 21 | 'created_to': opts.to, 22 | 'per_page': 50 23 | }).then(function (res) { 24 | var builds = res.data 25 | function requestNext (res) { 26 | if (res.headers.link && linkParser(res.headers.link).next) { 27 | var nextLink = linkParser(res.headers.link).next.url 28 | axios(nextLink).then(function (res) { 29 | builds = builds.concat(res.data) 30 | requestNext(res) 31 | }) 32 | } else { 33 | cb(null, builds) 34 | } 35 | } 36 | 37 | requestNext(res) 38 | }).catch(function (err) { 39 | cb(err) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bkboard", 3 | "version": "0.0.3", 4 | "description": "A Buildkite build report dashboard in terminal.", 5 | "main": "index.js", 6 | "bin": "./cli.js", 7 | "scripts": { 8 | "test": "standard", 9 | "start": "wzrd web.js:bundle.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/fraserxu/bkboard.git" 14 | }, 15 | "keywords": [ 16 | "buildkite", 17 | "terminal", 18 | "api" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/fraserxu/bkboard/issues" 22 | }, 23 | "homepage": "https://github.com/fraserxu/bkboard", 24 | "author": "Fraser Xu", 25 | "license": "MIT", 26 | "dependencies": { 27 | "axios": "^0.9.1", 28 | "blessed": "^0.1.81", 29 | "blessed-contrib": "^3.5.4", 30 | "d3-bar": "github:tj/d3-bar", 31 | "git-config": "0.0.7", 32 | "lodash": "^4.6.1", 33 | "minimist": "^1.2.0", 34 | "object-assign": "^4.0.1", 35 | "ora": "^0.2.0", 36 | "parse-link-header": "^0.4.1", 37 | "yo-yo": "^1.1.1" 38 | }, 39 | "devDependencies": { 40 | "standard": "^6.0.8", 41 | "wzrd": "^1.3.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /web.js: -------------------------------------------------------------------------------- 1 | var getBuilds = require('./index.js') 2 | 3 | var token = 'xxxx' 4 | 5 | getBuilds(token, { 6 | from: '2016-03-13T00:00:00Z', 7 | to: '2016-03-18T00:00:00Z', 8 | org: 'xxx', 9 | pipeline: 'xxx' 10 | }, function (err, builds) { 11 | console.log('err', err) 12 | console.log('builds', builds) 13 | }) 14 | --------------------------------------------------------------------------------