diff --git a/public/images/builds/canceled.svg b/public/images/builds/canceled.svg new file mode 100644 index 0000000..3e0de41 --- /dev/null +++ b/public/images/builds/canceled.svg @@ -0,0 +1 @@ +buildbuildcanceledcanceled \ No newline at end of file diff --git a/public/images/builds/errored.svg b/public/images/builds/errored.svg new file mode 100644 index 0000000..df1b7d0 --- /dev/null +++ b/public/images/builds/errored.svg @@ -0,0 +1 @@ +buildbuilderrorederrored \ No newline at end of file diff --git a/public/images/builds/failed.svg b/public/images/builds/failed.svg new file mode 100644 index 0000000..bba2e8f --- /dev/null +++ b/public/images/builds/failed.svg @@ -0,0 +1 @@ +buildbuildfailedfailed \ No newline at end of file diff --git a/public/images/builds/invalid.svg b/public/images/builds/invalid.svg new file mode 100644 index 0000000..ce1e483 --- /dev/null +++ b/public/images/builds/invalid.svg @@ -0,0 +1 @@ +buildbuildinvalidinvalid \ No newline at end of file diff --git a/public/images/builds/no_tests.svg b/public/images/builds/no_tests.svg new file mode 100644 index 0000000..b44858d --- /dev/null +++ b/public/images/builds/no_tests.svg @@ -0,0 +1 @@ +buildbuildno testsno tests \ No newline at end of file diff --git a/public/images/builds/none.svg b/public/images/builds/none.svg new file mode 100644 index 0000000..5031356 --- /dev/null +++ b/public/images/builds/none.svg @@ -0,0 +1 @@ +buildbuildnonenone \ No newline at end of file diff --git a/public/images/builds/passed.svg b/public/images/builds/passed.svg new file mode 100644 index 0000000..5398cb7 --- /dev/null +++ b/public/images/builds/passed.svg @@ -0,0 +1 @@ +buildbuildpassedpassed \ No newline at end of file diff --git a/public/images/builds/success.svg b/public/images/builds/success.svg new file mode 100644 index 0000000..5398cb7 --- /dev/null +++ b/public/images/builds/success.svg @@ -0,0 +1 @@ +buildbuildpassedpassed \ No newline at end of file diff --git a/public/images/builds/timedout.svg b/public/images/builds/timedout.svg new file mode 100644 index 0000000..52df931 --- /dev/null +++ b/public/images/builds/timedout.svg @@ -0,0 +1 @@ +buildbuildtimed outtimed out \ No newline at end of file diff --git a/public/images/builds/unknown.svg b/public/images/builds/unknown.svg new file mode 100644 index 0000000..c72a2f5 --- /dev/null +++ b/public/images/builds/unknown.svg @@ -0,0 +1 @@ +buildbuildunknownunknown \ No newline at end of file diff --git a/public/images/circle-ci-no-builds.svg b/public/images/circle-ci-no-builds.svg deleted file mode 100644 index 686290c..0000000 --- a/public/images/circle-ci-no-builds.svg +++ /dev/null @@ -1,4 +0,0 @@ - - -NO BUILDS - diff --git a/public/styles/app.css b/public/styles/app.css index 98758c5..8171d02 100644 --- a/public/styles/app.css +++ b/public/styles/app.css @@ -21,3 +21,7 @@ td, th { td.left, th.left { text-align: left; } + +.hide { + display: none; +} diff --git a/src/RepoMatrix.coffee b/src/RepoMatrix.coffee index 1517262..9340a66 100644 --- a/src/RepoMatrix.coffee +++ b/src/RepoMatrix.coffee @@ -1,8 +1,8 @@ Promise = require 'bluebird' Octokat = require 'octokat' request = require 'request-promise' -{log} = require 'lightsaber' -{flatten, merge, round, sample, size, sortBy} = require 'lodash' +{log, pjson} = require 'lightsaber' +{get, flatten, keys, merge, round, sample, size, sortBy} = require 'lodash' Wave = require 'loading-wave' $ = require 'jquery' require('datatables.net')() @@ -69,6 +69,31 @@ class RepoMatrix CONTRIBUTE = 'CONTRIBUTE.md' ] + CI = + travis: + addProject: (repoFullName) -> "https://travis-ci.org/#{repoFullName}" + urlTemplate: (repoFullName) -> "https://travis-ci.org/#{repoFullName}" + apiTemplate: (repoFullName) -> "https://api.travis-ci.org/repos/#{repoFullName}/branches/master" + apiStatePath: "branch.state" + circle: + addProject: -> "https://circleci.com/add-projects" + urlTemplate: (repoFullName) -> "https://circleci.com/gh/#{repoFullName}" + apiTemplate: (repoFullName) -> "https://circleci.com/api/v1.1/project/github/#{repoFullName}/tree/master" + apiStatePath: "[0].outcome" + + # roughly in order of best -> worst states + BUILD_STATES = + passed: 10 + success: 10 + canceled: 20 + unknown: 25 + none: 30 + no_tests: 35 + invalid: 40 + timedout: 45 + errored: 50 + failed: 60 + github = new Octokat @start: -> @@ -121,10 +146,14 @@ class RepoMatrix @showMatrix: (repos) -> $('#matrix').append @matrix repos - $('table').DataTable - paging: false - searching: false - fixedHeader: true + @loadCiBadges(repos) + .catch (error) => + console.error error + .then => + $('table').DataTable + paging: false + searching: false + fixedHeader: true @getFiles: (repos) -> repos = sortBy repos, 'fullName' @@ -168,8 +197,8 @@ class RepoMatrix for repo in repos tr => td class: 'left', => a href: "https://github.com/#{repo.fullName}", => repo.fullName # Name - td class: 'left', => @travis repo.fullName # Builds - td class: 'left', => @circle repo.fullName # Builds + td class: 'left', id: "#{@slug(repo.fullName)}-travis" # Builds + td class: 'left', id: "#{@slug(repo.fullName)}-circle" # Builds td class: 'no-padding', => @check repo.files[README] # README.md td class: 'no-padding', => @check(repo.files[README]?.length > 500) # README.md td class: 'no-padding', => @check repo.files[LICENSE] # Files @@ -184,20 +213,47 @@ class RepoMatrix td => repo.stargazersCount.toString() td => repo.openIssuesCount.toString() + @loadCiBadges: (repos) => + promises = for ciBrand, ciData of CI + do (ciBrand, ciData) => + {addProject, apiTemplate, apiStatePath, urlTemplate} = ciData + Promise.map repos, (repo) => + new Promise (resolve) => + apiUrl = apiTemplate(repo.fullName) + $.getJSON apiUrl + .fail (err) => + if err.status is 404 + @addCiBadge repo.fullName, ciBrand, 'none', addProject + else + console.error err + resolve() + .done (data) => + state = get(data, apiStatePath) + if state in keys(BUILD_STATES) + @addCiBadge repo.fullName, ciBrand, state, urlTemplate + else + @addCiBadge repo.fullName, ciBrand, 'unknown', urlTemplate + console.error "Unknown build state `#{state}` -- please add to + BUILD_STATES and add a badge to images/builds/#{state}.svg" + # " -- selector: #{apiStatePath} -- full data:\n#{pjson data}" + resolve() + Promise.all promises + + @addCiBadge: (repoFullName, ciBrand, state, urlTemplate) => + tableCell = $("##{@slug(repoFullName)}-#{ciBrand}") + stateHtml = render -> span class: 'hide', -> BUILD_STATES[state].toString() + badgeHtml = render -> + a href: urlTemplate(repoFullName), _target: '_repos', -> + img src: "images/builds/#{state}.svg" + tableCell.append(stateHtml) + tableCell.append(badgeHtml) + @check: renderable (success) -> if success div class: 'success', -> '✓' else div class: 'failure', -> '✗' - @travis: renderable (repoFullName) -> - a href: "https://travis-ci.org/#{repoFullName}", -> - img src: "https://travis-ci.org/#{repoFullName}.svg?branch=master" - - @circle: renderable (repoFullName) -> - a href: "https://circleci.com/gh/#{repoFullName}", -> - img src: "https://circleci.com/gh/#{repoFullName}.svg?style=svg", onError: "this.parentElement.href = 'https://circleci.com/add-projects'; this.src = 'images/circle-ci-no-builds.svg'" - @loadStats: -> github.rateLimit.fetch() .then (info) => $('#stats').append @stats info @@ -209,4 +265,7 @@ class RepoMatrix minutesUntilReset = (reset - now) / 60 # minutes "Github API calls: #{remaining} remaining of #{limit} limit per hour; clean slate in: #{round minutesUntilReset, 1} minutes" + @slug: (string) -> + string.replace(/\W+/, '-') + module.exports = RepoMatrix