diff --git a/.travis.yml b/.travis.yml index c4c3a6ac8..b9f1d7f35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,15 @@ language: node_js node_js: - "0.10" +env: + global: + - GITHUB_OAUTH_KEY=003a44d58f12089d0c0261338298af3813330949 + matrix: include: - node_js: "0.10" env: TEST_SUITE=lint - + branches: exclude: - /^greenkeeper-.+$/ diff --git a/Gruntfile.js b/Gruntfile.js index 24fde096b..1a01edbbe 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,14 @@ -/* global module, require, process */ +/* global -moment */ +/* jshint node: true */ /* jscs:disable */ -var path = require('path'), +var _ = require('lodash'), + fs = require('fs-extra'), + path = require('path'), + https = require('https'), + getTopContribs = require('top-gh-contribs'), + moment = require('moment'), + chalk = require('chalk'), + Promise = require('bluebird'), escapeChar = process.platform.match(/^win/) ? '^' : '\\', cwd = process.cwd().replace(/( |\(|\))/g, escapeChar + '$1'); @@ -78,10 +86,122 @@ module.exports = function(grunt) { }); grunt.registerTask('init', 'Install the client dependencies', - ['shell:npm-install', 'shell:bower-install'] + ['shell:npm-install', 'shell:bower-install', 'buildAboutPage'] ); grunt.registerTask('lint', 'Run the code style checks and linter', ['jshint', 'jscs', 'shell:csscomblint'] ); + + // ### Build About Page *(Utility Task)* + // Builds the github contributors partial template used on the about page, + // and downloads the avatar for each of the users. + // Run by any task that compiles ember assets or manually via `grunt buildAboutPage`. + // Only builds if the template does not exist. + // To force a build regardless, supply the --force option. + // `grunt buildAboutPage --force` + grunt.registerTask('buildAboutPage', 'Compile assets for the About Ghost page', function () { + var done = this.async(), + templatePath = 'app/templates/-contributors.hbs', + imagePath = 'public/assets/img/contributors', + timeSpan = moment().subtract(90, 'days').format('YYYY-MM-DD'), + oauthKey = process.env.GITHUB_OAUTH_KEY, + contribNumber = 18; + + if (fs.existsSync(templatePath) && !grunt.option('force')) { + grunt.log.writeln('Contributors template already exists.'); + grunt.log.writeln(chalk.bold('Skipped')); + return done(); + } + + grunt.verbose.writeln('Downloading release and contributor information from Github'); + + function mergeContribs(first, second) { + _.each(second, function (contributor) { + var contributorInFirst = _.find(first, ['name', contributor.name]); + + if (contributorInFirst) { + contributorInFirst.commitCount += contributor.commitCount; + } else { + first.push(contributor); + } + }); + + return _(first) + .filter(function (contributor) { + // remove greenkeeper from contributor list + return contributor.name !== 'greenkeeperio-bot'; + }) + .sortBy('commitCount') + .reverse() + .take(contribNumber) + .value(); + } + + return Promise.join( + Promise.promisify(fs.mkdirs)(imagePath), + getTopContribs({ + user: 'tryghost', + repo: 'ghost', + oauthKey: oauthKey, + sinceDate: timeSpan, + count: contribNumber, + retry: true + }), + getTopContribs({ + user: 'tryghost', + repo: 'ghost-admin', + oauthKey: oauthKey, + sinceDate: timeSpan, + count: contribNumber, + retry: true + }) + ).then(function (results) { + var contributors = mergeContribs(results[1], results[2]), + contributorTemplate = '
\n \n' + + ' " alt="<%= name %>" />\n' + + ' \n
', + + downloadImagePromise = function (url, name) { + return new Promise(function (resolve, reject) { + var file = fs.createWriteStream(path.join(__dirname, imagePath, name)); + + https.get(url, function (response) { + response.pipe(file); + file.on('finish', function () { + file.close(); + resolve(); + }); + }).on('error', reject); + }); + }; + + grunt.verbose.writeln('Creating contributors template'); + grunt.file.write( + templatePath, + _.map(contributors, function (contributor) { + var compiled = _.template(contributorTemplate); + + return compiled(contributor); + }).join('\n') + ); + + grunt.verbose.writeln('Downloading images for top contributors'); + return Promise.all(_.map(contributors, function (contributor) { + return downloadImagePromise(contributor.avatarUrl + '&s=60', contributor.name); + })); + }).then(done).catch(function (error) { + grunt.log.error(error); + + if (error.http_status) { + grunt.log.writeln('GitHub API request returned status: ' + error.http_status); + } + + if (error.ratelimit_limit) { + grunt.log.writeln('Rate limit data: limit: %d, remaining: %d, reset: %s', error.ratelimit_limit, error.ratelimit_remaining, moment.unix(error.ratelimit_reset).fromNow()); + } + + done(false); + }); + }); }; diff --git a/package.json b/package.json index 2a11873bd..615a592d5 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,10 @@ "node": ">= 0.10.0" }, "devDependencies": { + "bluebird": "3.4.1", "bower": "1.7.9", "broccoli-asset-rev": "2.4.3", + "chalk": "1.1.3", "csscomb": "3.1.8", "ember-ajax": "2.4.1", "ember-cli": "2.6.2", @@ -72,7 +74,10 @@ "liquid-fire": "0.23.1", "liquid-tether": "1.1.1", "loader.js": "4.0.10", + "lodash": "4.13.1", "matchdep": "1.0.1", + "moment": "2.13.0", + "top-gh-contribs": "2.0.4", "walk-sync": "^0.2.6" }, "ember-addon": {