2013-12-06 14:24:25 +04:00
// # Gruntfile - Task automation for Ghost
// Run various tasks when developing for and working with Ghost
// Run `grunt --help` or visit https://github.com/TryGhost/Ghost/wiki/Grunt-Toolkit/ for usage instructions
2013-09-24 14:46:30 +04:00
var path = require ( 'path' ) ,
when = require ( 'when' ) ,
semver = require ( 'semver' ) ,
fs = require ( 'fs' ) ,
2014-02-05 12:40:30 +04:00
_ = require ( 'lodash' ) ,
2013-10-25 09:58:58 +04:00
spawn = require ( 'child_process' ) . spawn ,
2013-07-02 01:24:59 +04:00
buildDirectory = path . resolve ( process . cwd ( ) , '.build' ) ,
2013-09-24 14:46:30 +04:00
distDirectory = path . resolve ( process . cwd ( ) , '.dist' ) ,
2014-01-05 10:40:53 +04:00
bootstrap = require ( './core/bootstrap' ) ,
2013-09-13 11:24:28 +04:00
2013-12-06 14:24:25 +04:00
// ## Build File Patterns
// a list of files and paterns to process and exclude when running builds & releases
2014-02-22 07:16:07 +04:00
// It is taken from the .npmignore file and all patterns are inverted as the .npmignore
// file defines what to ignore, whereas we want to define what to include.
buildGlob = ( function ( ) {
/*jslint stupid:true */
return fs . readFileSync ( '.npmignore' , { encoding : 'utf8' } ) . split ( '\n' ) . map ( function ( pattern ) {
if ( pattern [ 0 ] === '!' ) {
return pattern . substr ( 1 ) ;
}
return '!' + pattern ;
} ) ;
} ( ) ) ,
2013-09-18 22:45:21 +04:00
2013-12-06 14:24:25 +04:00
// ## Grunt configuration
2013-06-25 15:43:15 +04:00
configureGrunt = function ( grunt ) {
2013-08-17 22:39:02 +04:00
// load all grunt tasks
2014-02-19 07:41:21 +04:00
require ( 'matchdep' ) . filterDev ( [ 'grunt-*' , '!grunt-cli' ] ) . forEach ( grunt . loadNpmTasks ) ;
2013-08-17 22:39:02 +04:00
2013-06-25 15:43:15 +04:00
var cfg = {
// Common paths to be used by tasks
paths : {
2013-07-11 23:02:18 +04:00
adminAssets : './core/client/assets' ,
2013-06-25 15:43:15 +04:00
build : buildDirectory ,
2013-11-01 19:33:49 +04:00
releaseBuild : path . join ( buildDirectory , 'release' ) ,
2013-06-25 15:43:15 +04:00
dist : distDirectory ,
2013-10-11 19:31:47 +04:00
releaseDist : path . join ( distDirectory , 'release' )
2013-06-25 15:43:15 +04:00
} ,
2013-07-11 18:24:33 +04:00
buildType : 'Build' ,
2013-06-25 15:43:15 +04:00
pkg : grunt . file . readJSON ( 'package.json' ) ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-watch
2013-08-17 22:39:02 +04:00
// Watch files and livereload in the browser during development
watch : {
handlebars : {
files : [ 'core/client/tpl/**/*.hbs' ] ,
tasks : [ 'handlebars' ]
} ,
sass : {
files : [ '<%= paths.adminAssets %>/sass/**/*' ] ,
tasks : [ 'sass:admin' ]
} ,
2013-09-14 06:15:30 +04:00
concat : {
files : [
'core/client/*.js' ,
'core/client/helpers/*.js' ,
'core/client/models/*.js' ,
'core/client/tpl/*.js' ,
'core/client/views/*.js'
] ,
tasks : [ 'concat' ]
} ,
2013-08-17 22:39:02 +04:00
livereload : {
files : [
// Theme CSS
'content/themes/casper/css/*.css' ,
// Theme JS
'content/themes/casper/js/*.js' ,
// Admin CSS
'<%= paths.adminAssets %>/css/*.css' ,
// Admin JS
2013-09-14 06:15:30 +04:00
'core/built/scripts/*.js'
2013-08-17 22:39:02 +04:00
] ,
options : {
livereload : true
}
} ,
express : {
// Restart any time client or server js files change
2013-10-24 00:58:28 +04:00
files : [ 'core/server.js' , 'core/server/**/*.js' ] ,
2013-08-17 22:39:02 +04:00
tasks : [ 'express:dev' ] ,
options : {
//Without this option specified express won't be reloaded
nospawn : true
}
}
} ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-express-server
2013-08-17 22:39:02 +04:00
// Start our server in development
express : {
options : {
2013-12-05 00:20:16 +04:00
script : 'index.js' ,
output : 'Ghost is running'
2013-08-17 22:39:02 +04:00
} ,
dev : {
options : {
2013-10-25 09:58:58 +04:00
//output: 'Express server listening on address:.*$'
2013-08-17 22:39:02 +04:00
}
2013-08-29 14:04:33 +04:00
} ,
test : {
options : {
node _env : 'testing'
}
2013-08-17 22:39:02 +04:00
}
} ,
2014-02-27 06:44:09 +04:00
// ### Config for grunt-contrib-jshint
// JSHint all the things!
jshint : {
2013-07-11 23:02:18 +04:00
server : {
2014-02-27 06:44:09 +04:00
options : {
2013-06-25 15:43:15 +04:00
// node environment
node : true ,
// browser environment
browser : false ,
// allow dangling underscores in var names
2014-02-27 06:44:09 +04:00
nomen : false ,
2013-06-25 15:43:15 +04:00
// don't require use strict pragma
2014-02-27 06:44:09 +04:00
strict : false ,
sub : true ,
eqeqeq : true ,
laxbreak : true ,
bitwise : true ,
curly : true ,
forin : true ,
immed : true ,
latedef : true ,
newcap : true ,
noarg : true ,
noempty : true ,
nonew : true ,
plusplus : true ,
regexp : true ,
undef : true ,
unused : true ,
trailing : true ,
indent : 4 ,
onevar : true ,
white : true
2013-06-25 15:43:15 +04:00
} ,
files : {
src : [
2013-09-24 14:46:30 +04:00
'*.js' ,
'core/*.js' ,
'core/server/**/*.js'
2013-06-25 16:38:41 +04:00
]
2013-07-11 23:02:18 +04:00
}
2013-06-25 15:43:15 +04:00
} ,
2013-07-11 23:02:18 +04:00
client : {
2014-02-27 06:44:09 +04:00
options : {
"predef" : {
"document" : true ,
"window" : true ,
"location" : true ,
"setTimeout" : true ,
"Ember" : true ,
"Em" : true ,
"DS" : true ,
"$" : true
} ,
2013-06-25 15:43:15 +04:00
// node environment
node : false ,
// browser environment
browser : true ,
// allow dangling underscores in var names
2014-02-27 06:44:09 +04:00
nomen : false ,
bitwise : true ,
curly : true ,
eqeqeq : true ,
forin : true ,
immed : true ,
latedef : true ,
newcap : true ,
noarg : true ,
noempty : true ,
nonew : true ,
plusplus : true ,
regexp : true ,
undef : true ,
unused : true ,
trailing : true ,
indent : 4 ,
esnext : true ,
onevar : true ,
white : true
2013-06-25 15:43:15 +04:00
} ,
files : {
2014-02-27 06:44:09 +04:00
src : [
'core/client/**/*.js' ,
// Ignore files
'!core/client/assets/vendor/**/*.js' ,
'!core/client/tpl/**/*.js'
]
}
2013-07-11 23:02:18 +04:00
} ,
shared : {
2014-02-27 06:44:09 +04:00
options : {
2013-07-11 23:02:18 +04:00
// node environment
node : true ,
// browser environment
browser : false ,
// don't require use strict pragma
2014-02-27 06:44:09 +04:00
strict : false ,
// allow dangling underscores in var names
nomen : false ,
bitwise : true ,
curly : true ,
eqeqeq : true ,
forin : true ,
immed : true ,
latedef : true ,
newcap : true ,
noarg : true ,
noempty : true ,
nonew : true ,
plusplus : true ,
regexp : true ,
undef : true ,
unused : true ,
trailing : true ,
indent : 4 ,
onevar : true ,
white : true
2013-07-11 23:02:18 +04:00
} ,
files : {
src : [
2014-02-27 06:44:09 +04:00
'core/shared/**/*.js' ,
// Ignore files
2014-03-04 01:41:34 +04:00
'!core/shared/vendor/**/*.js' ,
'!core/shared/lib/**/*.js'
2013-07-11 23:02:18 +04:00
]
2014-02-27 06:44:09 +04:00
}
2013-06-25 15:43:15 +04:00
}
} ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-mocha-cli
// Run mocha unit tests
2013-07-10 02:54:57 +04:00
mochacli : {
2013-06-25 15:43:15 +04:00
options : {
2013-09-24 14:46:30 +04:00
ui : 'bdd' ,
2013-10-17 02:57:52 +04:00
reporter : 'spec' ,
timeout : '15000'
2013-05-26 21:17:46 +04:00
} ,
2013-11-24 18:29:36 +04:00
unit : {
2013-07-31 11:33:28 +04:00
src : [ 'core/test/unit/**/*_spec.js' ]
2013-06-25 15:43:15 +04:00
} ,
2013-05-25 20:48:15 +04:00
2013-11-07 17:26:47 +04:00
model : {
src : [ 'core/test/integration/**/model*_spec.js' ]
2013-06-25 15:43:15 +04:00
} ,
2013-05-25 20:55:23 +04:00
2013-08-20 01:52:50 +04:00
client : {
src : [ 'core/test/unit/**/client*_spec.js' ]
} ,
server : {
src : [ 'core/test/unit/**/server*_spec.js' ]
} ,
shared : {
src : [ 'core/test/unit/**/shared*_spec.js' ]
2013-08-25 00:51:58 +04:00
} ,
2013-06-25 15:43:15 +04:00
perm : {
2013-07-31 11:33:28 +04:00
src : [ 'core/test/unit/**/permissions_spec.js' ]
2013-08-03 19:11:16 +04:00
} ,
migrate : {
src : [
'core/test/unit/**/export_spec.js' ,
'core/test/unit/**/import_spec.js'
]
2013-10-30 01:34:47 +04:00
} ,
2013-12-04 06:47:39 +04:00
storage : {
src : [ 'core/test/unit/**/storage*_spec.js' ]
} ,
2013-10-30 01:34:47 +04:00
integration : {
2014-02-26 21:51:01 +04:00
src : [
'core/test/integration/**/model*_spec.js' ,
'core/test/integration/**/api*_spec.js'
]
2013-11-24 18:29:36 +04:00
} ,
api : {
src : [ 'core/test/functional/api/*_test.js' ]
2013-12-31 21:09:49 +04:00
} ,
routes : {
src : [ 'core/test/functional/routes/*_test.js' ]
2013-06-25 15:43:15 +04:00
}
} ,
2013-06-15 02:12:04 +04:00
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-sass
2013-06-25 15:43:15 +04:00
// Compile all the SASS!
sass : {
admin : {
files : {
'<%= paths.adminAssets %>/css/screen.css' : '<%= paths.adminAssets %>/sass/screen.scss'
2013-06-10 17:52:04 +04:00
}
2013-09-27 19:02:35 +04:00
} ,
compress : {
options : {
style : 'compressed'
} ,
files : {
'<%= paths.adminAssets %>/css/screen.css' : '<%= paths.adminAssets %>/sass/screen.scss'
}
2013-06-25 15:43:15 +04:00
}
} ,
2013-12-06 14:24:25 +04:00
// ### config for grunt-shell
// command line tools
2013-06-25 15:43:15 +04:00
shell : {
2014-02-19 07:41:21 +04:00
// run bundle
bundle : {
command : 'bundle install'
} ,
2013-12-06 14:24:25 +04:00
// install bourbon
2013-06-25 15:43:15 +04:00
bourbon : {
command : 'bourbon install --path <%= paths.adminAssets %>/sass/modules/'
2013-11-03 03:40:43 +04:00
} ,
2013-12-06 14:24:25 +04:00
// generate coverage report
2013-11-03 03:40:43 +04:00
coverage : {
2013-11-12 00:54:48 +04:00
command : function ( ) {
// will work on windows only if mocha is globally installed
var cmd = ! ! process . platform . match ( /^win/ ) ? 'mocha' : './node_modules/mocha/bin/mocha' ;
return cmd + ' --timeout 15000 --reporter html-cov > coverage.html ./core/test/blanket_coverage.js' ;
} ,
execOptions : {
env : 'NODE_ENV=' + process . env . NODE _ENV
}
2013-06-25 15:43:15 +04:00
}
} ,
2013-06-02 03:45:02 +04:00
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-handlebars
// Compile templates for admin client
2013-06-25 15:43:15 +04:00
handlebars : {
core : {
options : {
2013-09-24 14:46:30 +04:00
namespace : 'JST' ,
2013-06-25 15:43:15 +04:00
processName : function ( filename ) {
2013-07-12 00:00:41 +04:00
filename = filename . replace ( 'core/client/tpl/' , '' ) ;
2013-06-25 15:43:15 +04:00
return filename . replace ( '.hbs' , '' ) ;
}
2013-06-02 03:45:02 +04:00
} ,
2013-06-25 15:43:15 +04:00
files : {
2013-09-24 14:46:30 +04:00
'core/client/tpl/hbs-tpl.js' : 'core/client/tpl/**/*.hbs'
2013-06-02 03:45:02 +04:00
}
2013-06-25 15:43:15 +04:00
}
} ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-groc
// Generate documentation from code
2013-06-25 15:43:15 +04:00
groc : {
2013-06-25 22:10:33 +04:00
docs : {
options : {
2013-09-24 14:46:30 +04:00
'out' : './docs/' ,
'glob' : [
'README.md' ,
'config.example.js' ,
'index.js' ,
'core/*.js' ,
'core/server/**/*.js' ,
'core/shared/**/*.js' ,
'core/client/**/*.js'
2013-07-11 23:02:18 +04:00
] ,
2013-09-24 14:46:30 +04:00
'except' : [
'!core/**/vendor/**/*.js' ,
'!core/client/tpl/**/*.js'
2013-06-25 22:10:33 +04:00
]
}
2013-06-25 15:43:15 +04:00
}
} ,
2013-06-02 03:45:02 +04:00
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-clean
// Clean up files as part of other tasks
2013-09-18 22:56:39 +04:00
clean : {
2013-11-01 19:33:49 +04:00
release : {
src : [ '<%= paths.releaseBuild %>/**' ]
2013-10-15 07:39:52 +04:00
} ,
test : {
src : [ 'content/data/ghost-test.db' ]
2013-09-18 22:56:39 +04:00
}
} ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-copy
// Prepare files for builds / releases
2013-06-25 15:43:15 +04:00
copy : {
2014-03-04 01:41:34 +04:00
dev : {
files : [ {
cwd : 'bower_components/jquery/dist/' ,
src : 'jquery.js' ,
dest : 'core/built/public/' ,
expand : true
} ]
} ,
prod : {
files : [ {
cwd : 'bower_components/jquery/dist/' ,
src : 'jquery.js' ,
dest : 'core/built/public/' ,
expand : true
} ]
} ,
2013-11-01 19:33:49 +04:00
release : {
2013-06-25 15:43:15 +04:00
files : [ {
expand : true ,
2013-09-18 22:45:21 +04:00
src : buildGlob ,
2013-11-01 19:33:49 +04:00
dest : '<%= paths.releaseBuild %>/'
2014-03-04 01:41:34 +04:00
} , {
cwd : 'bower_components/jquery/dist/' ,
src : 'jquery.js' ,
dest : 'core/built/public/' ,
expand : true
2013-06-25 15:43:15 +04:00
} ]
}
} ,
2013-05-20 08:22:00 +04:00
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-compress
// Zip up builds / releases
2013-06-25 15:43:15 +04:00
compress : {
2013-10-11 19:31:47 +04:00
release : {
options : {
archive : '<%= paths.releaseDist %>/Ghost-<%= pkg.version %>.zip'
} ,
expand : true ,
2013-11-01 19:33:49 +04:00
cwd : '<%= paths.releaseBuild %>/' ,
2013-10-11 19:31:47 +04:00
src : [ '**' ]
2013-06-10 17:52:04 +04:00
}
2013-07-02 00:58:47 +04:00
} ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-concat
// concatenate multiple JS files into a single file ready for use
2013-09-14 06:15:30 +04:00
concat : {
dev : {
files : {
2013-09-24 14:46:30 +04:00
'core/built/scripts/vendor.js' : [
2014-03-04 01:41:34 +04:00
'bower_components/jquery/dist/jquery.js' ,
'bower_components/jquery-ui/ui/jquery-ui.js' ,
2013-09-24 14:46:30 +04:00
'core/client/assets/lib/jquery-utils.js' ,
'core/client/assets/lib/uploader.js' ,
2014-03-04 01:41:34 +04:00
'bower_components/lodash/dist/lodash.underscore.js' ,
'bower_components/backbone/backbone.js' ,
'bower_components/handlebars.js/dist/handlebars.runtime.js' ,
'bower_components/moment/moment.js' ,
'bower_components/jquery-file-upload/js/jquery.fileupload.js' ,
'bower_components/codemirror/lib/codemirror.js' ,
'bower_components/codemirror/lib/addon/mode/overlay.js' ,
'bower_components/codemirror/lib/mode/markdown/markdown.js' ,
'bower_components/codemirror/lib/mode/gfm/gfm.js' ,
'bower_components/showdown/src/showdown.js' ,
'bower_components/validator-js/validator.js' ,
'core/client/assets/lib/showdown/extensions/ghostdown.js' ,
'core/shared/lib/showdown/extensions/typography.js' ,
'core/shared/lib/showdown/extensions/github.js' ,
// ToDo: Remove or replace
2013-09-24 14:46:30 +04:00
'core/client/assets/vendor/shortcuts.js' ,
'core/client/assets/vendor/to-title-case.js' ,
2014-03-04 01:41:34 +04:00
'bower_components/Countable/Countable.js' ,
'bower_components/fastclick/lib/fastclick.js' ,
'bower_components/nprogress/nprogress.js'
2013-09-14 06:15:30 +04:00
] ,
2013-09-24 14:46:30 +04:00
'core/built/scripts/helpers.js' : [
'core/client/init.js' ,
2013-09-14 06:15:30 +04:00
2013-09-24 14:46:30 +04:00
'core/client/mobile-interactions.js' ,
'core/client/toggle.js' ,
'core/client/markdown-actions.js' ,
'core/client/helpers/index.js'
2013-09-14 06:15:30 +04:00
] ,
2013-09-24 14:46:30 +04:00
'core/built/scripts/templates.js' : [
'core/client/tpl/hbs-tpl.js'
2013-09-14 06:15:30 +04:00
] ,
2013-09-24 14:46:30 +04:00
'core/built/scripts/models.js' : [
'core/client/models/**/*.js'
2013-09-14 06:15:30 +04:00
] ,
2013-09-24 14:46:30 +04:00
'core/built/scripts/views.js' : [
'core/client/views/**/*.js' ,
'core/client/router.js'
2013-09-14 06:15:30 +04:00
]
}
} ,
prod : {
files : {
2013-09-24 14:46:30 +04:00
'core/built/scripts/ghost.js' : [
2014-03-04 01:41:34 +04:00
'bower_components/jquery/dist/jquery.js' ,
'bower_components/jquery-ui/ui/jquery-ui.js' ,
2013-09-24 14:46:30 +04:00
'core/client/assets/lib/jquery-utils.js' ,
'core/client/assets/lib/uploader.js' ,
2014-03-04 01:41:34 +04:00
'bower_components/lodash/dist/lodash.underscore.js' ,
'bower_components/backbone/backbone.js' ,
'bower_components/handlebars.js/dist/handlebars.runtime.js' ,
'bower_components/moment/moment.js' ,
'bower_components/jquery-file-upload/js/jquery.fileupload.js' ,
'bower_components/codemirror/lib/codemirror.js' ,
'bower_components/codemirror/lib/addon/mode/overlay.js' ,
'bower_components/codemirror/lib/mode/markdown/markdown.js' ,
'bower_components/codemirror/lib/mode/gfm/gfm.js' ,
'bower_components/showdown/src/showdown.js' ,
'bower_components/validator-js/validator.js' ,
'core/client/assets/lib/showdown/extensions/ghostdown.js' ,
'core/shared/lib/showdown/extensions/typography.js' ,
'core/shared/lib/showdown/extensions/github.js' ,
// ToDo: Remove or replace
2013-09-24 14:46:30 +04:00
'core/client/assets/vendor/shortcuts.js' ,
'core/client/assets/vendor/to-title-case.js' ,
2014-03-04 01:41:34 +04:00
'bower_components/Countable/Countable.js' ,
'bower_components/fastclick/lib/fastclick.js' ,
'bower_components/nprogress/nprogress.js' ,
2013-09-24 14:46:30 +04:00
'core/client/init.js' ,
'core/client/mobile-interactions.js' ,
'core/client/toggle.js' ,
'core/client/markdown-actions.js' ,
'core/client/helpers/index.js' ,
'core/client/tpl/hbs-tpl.js' ,
'core/client/models/**/*.js' ,
'core/client/views/**/*.js' ,
'core/client/router.js'
2013-09-14 06:15:30 +04:00
]
}
}
} ,
2013-12-06 14:24:25 +04:00
// ### Config for grunt-contrib-uglify
// minify javascript file for production
2013-09-14 06:15:30 +04:00
uglify : {
prod : {
files : {
2013-09-24 14:46:30 +04:00
'core/built/scripts/ghost.min.js' : 'core/built/scripts/ghost.js'
2013-09-14 06:15:30 +04:00
}
}
2013-06-25 15:43:15 +04:00
}
2013-06-10 17:52:04 +04:00
} ;
2013-05-13 23:18:20 +04:00
2013-06-25 15:43:15 +04:00
grunt . initConfig ( cfg ) ;
2013-12-06 14:24:25 +04:00
// ## Custom Tasks
2013-10-25 09:58:58 +04:00
grunt . registerTask ( 'setTestEnv' , 'Use "testing" Ghost config; unless we are running on travis (then show queries for debugging)' , function ( ) {
2013-10-30 00:54:37 +04:00
process . env . NODE _ENV = process . env . TRAVIS ? 'travis-' + process . env . DB : 'testing' ;
2013-11-03 18:12:09 +04:00
cfg . express . test . options . node _env = process . env . NODE _ENV ;
2013-09-13 18:12:38 +04:00
} ) ;
2013-09-13 11:24:28 +04:00
grunt . registerTask ( 'loadConfig' , function ( ) {
var done = this . async ( ) ;
2014-01-05 10:40:53 +04:00
bootstrap ( ) . then ( function ( ) {
2013-09-13 11:24:28 +04:00
done ( ) ;
} ) ;
} ) ;
2013-08-27 01:28:41 +04:00
grunt . registerTask ( 'spawn-casperjs' , function ( ) {
var done = this . async ( ) ,
options = [ 'host' , 'noPort' , 'port' , 'email' , 'password' ] ,
2013-10-19 04:13:07 +04:00
args = [ 'test' ]
. concat ( grunt . option ( 'target' ) || [ 'admin/' , 'frontend/' ] )
. concat ( [ '--includes=base.js' , '--verbose' , '--log-level=debug' , '--port=2369' ] ) ;
2013-08-27 01:28:41 +04:00
// Forward parameters from grunt to casperjs
_ . each ( options , function processOption ( option ) {
if ( grunt . option ( option ) ) {
args . push ( '--' + option + '=' + grunt . option ( option ) ) ;
}
} ) ;
grunt . util . spawn ( {
cmd : 'casperjs' ,
args : args ,
opts : {
cwd : path . resolve ( 'core/test/functional' ) ,
stdio : 'inherit'
}
} , function ( error , result , code ) {
2014-02-27 06:44:09 +04:00
/*jshint unused:false*/
2013-08-27 01:28:41 +04:00
if ( error ) {
grunt . fail . fatal ( result . stdout ) ;
}
grunt . log . writeln ( result . stdout ) ;
done ( ) ;
} ) ;
} ) ;
2013-08-01 11:12:59 +04:00
/ * G e n e r a t e C h a n g e l o g
* - Pulls changelog from git , excluding merges .
* - Uses first line of commit message . Includes committer name .
* /
2013-10-25 09:58:58 +04:00
grunt . registerTask ( 'changelog' , 'Generate changelog from Git' , function ( ) {
2013-08-01 11:12:59 +04:00
// TODO: Break the contents of this task out into a separate module,
// put on npm. (@cgiffard)
var done = this . async ( ) ;
function git ( args , callback , depth ) {
depth = depth || 0 ;
if ( ! depth ) {
2013-09-24 14:46:30 +04:00
grunt . log . writeln ( 'git ' + args . join ( ' ' ) ) ;
2013-08-01 11:12:59 +04:00
}
var buffer = [ ] ;
2013-09-24 14:46:30 +04:00
spawn ( 'git' , args , {
2013-08-01 11:12:59 +04:00
// We can reasonably assume the gruntfile will be in the root of the repo.
cwd : _ _dirname ,
2013-09-24 14:46:30 +04:00
stdio : [ 'ignore' , 'pipe' , process . stderr ]
2013-08-01 11:12:59 +04:00
2013-09-24 14:46:30 +04:00
} ) . on ( 'exit' , function ( code ) {
2013-08-01 11:12:59 +04:00
// Process exited correctly but we got no output.
// Spawn again, but make sure we don't spiral out of control.
// Hack to work around an apparent node bug.
//
// Frustratingly, it's impossible to distinguish this
// bug from a genuine empty log.
if ( ! buffer . length && code === 0 && depth < 20 ) {
return setImmediate ( function ( ) {
git ( args , callback , depth ? depth + 1 : 1 ) ;
} ) ;
}
if ( code === 0 ) {
2013-09-24 14:46:30 +04:00
return callback ( buffer . join ( '' ) ) ;
2013-08-01 11:12:59 +04:00
}
// We failed. Git returned a non-standard exit code.
grunt . log . error ( 'Git returned a non-zero exit code.' ) ;
done ( false ) ;
// Push returned data into the buffer
2013-09-24 14:46:30 +04:00
} ) . stdout . on ( 'data' , buffer . push . bind ( buffer ) ) ;
2013-08-01 11:12:59 +04:00
}
// Crazy function for getting around inconsistencies in tagging
function sortTags ( a , b ) {
a = a . tag ;
b = b . tag ;
// NOTE: Accounting for different tagging method for
// 0.2.1 and up.
// If we didn't have this issue I'd just pass rcompare
// into sort directly. Could be something to think about
// in future.
2013-09-24 14:46:30 +04:00
if ( semver . rcompare ( a , '0.2.0' ) < 0 ||
semver . rcompare ( b , '0.2.0' ) < 0 ) {
2013-08-01 11:12:59 +04:00
return semver . rcompare ( a , b ) ;
}
2013-09-24 14:46:30 +04:00
a = a . split ( '-' ) ;
b = b . split ( '-' ) ;
2013-08-01 11:12:59 +04:00
if ( semver . rcompare ( a [ 0 ] , b [ 0 ] ) !== 0 ) {
return semver . rcompare ( a [ 0 ] , b [ 0 ] ) ;
}
// Using this janky looking integerising-method
// because it's faster and doesn't result in NaN, which
// breaks sorting
2013-09-13 00:03:01 +04:00
/*jslint bitwise: true */
2013-08-01 11:12:59 +04:00
return ( + b [ 1 ] | 0 ) - ( + a [ 1 ] | 0 ) ;
}
// Gets tags in master branch, sorts them with semver,
function getTags ( callback ) {
2013-09-24 14:46:30 +04:00
git ( [ 'show-ref' , '--tags' ] , function ( results ) {
2013-08-01 11:12:59 +04:00
results = results
. split ( /\n+/ )
. filter ( function ( tag ) {
return tag . length && tag . match ( /\/\d+\.\d+/ ) ;
} )
. map ( function ( tag ) {
return {
2013-09-24 14:46:30 +04:00
'tag' : tag . split ( /tags\// ) . pop ( ) . trim ( ) ,
'ref' : tag . split ( /\s+/ ) . shift ( ) . trim ( )
2013-08-01 11:12:59 +04:00
} ;
} )
. sort ( sortTags ) ;
callback ( results ) ;
} ) ;
}
// Parses log to extract commit data.
function parseLog ( data ) {
var commits = [ ] ,
commitRegex =
new RegExp (
2013-09-24 14:46:30 +04:00
'\\n*[|\\*\\s]*commit\\s+([a-f0-9]+)' +
'\\n[|\\*\\s]*Author:\\s+([^<\\n]+)<([^>\\n]+)>' +
'\\n[|\\*\\s]*Date:\\s+([^\\n]+)' +
'\\n+[|\\*\\s]*[ ]{4}([^\\n]+)' ,
'ig'
2013-08-01 11:12:59 +04:00
) ;
// Using String.prototype.replace as a kind of poor-man's substitute
// for a streaming parser.
data . replace (
commitRegex ,
function ( wholeCommit , hash , author , email , date , message ) {
2014-02-27 06:44:09 +04:00
/*jshint unused:false*/
2013-08-01 11:12:59 +04:00
// The author name and commit message may have trailing space.
author = author . trim ( ) ;
message = message . trim ( ) ;
// Reformat date to make it parse-able by JS
date =
date . replace (
/^(\w+)\s(\w+)\s(\d+)\s([\d\:]+)\s(\d+)\s([\+\-\d]+)$/ ,
2013-09-24 14:46:30 +04:00
'$1, $2 $3 $5 $4 $6'
2013-08-01 11:12:59 +04:00
) ;
commits . push ( {
2013-09-24 14:46:30 +04:00
'hash' : hash ,
'author' : author ,
'email' : email ,
'date' : date ,
'parsedDate' : new Date ( Date . parse ( date ) ) ,
'message' : message
2013-08-01 11:12:59 +04:00
} ) ;
return null ;
}
) ;
return commits ;
}
// Gets git log for specified range.
function getLog ( to , from , callback ) {
2013-09-24 14:46:30 +04:00
var range = from && to ? from + '..' + to : '' ,
args = [ 'log' , 'master' , '--no-color' , '--no-merges' , '--graph' ] ;
2013-08-01 11:12:59 +04:00
if ( range ) {
args . push ( range ) ;
}
git ( args , function ( data ) {
callback ( parseLog ( data ) ) ;
} ) ;
}
// Run the job
getTags ( function ( tags ) {
2013-09-24 14:46:30 +04:00
var logPath = path . join ( _ _dirname , 'CHANGELOG.md' ) ,
2013-08-01 11:12:59 +04:00
log = fs . createWriteStream ( logPath ) ,
commitCache = { } ;
function processTag ( tag , callback ) {
2013-09-24 14:46:30 +04:00
var buffer = '' ,
2013-08-01 11:12:59 +04:00
peek = tag [ 1 ] ;
tag = tag [ 0 ] ;
getLog ( tag . tag , peek . tag , function ( commits ) {
// Use the comparison with HEAD to remove commits which
// haven't been included in a build/release yet.
2013-10-25 09:58:58 +04:00
if ( tag . tag === 'HEAD' ) {
2013-08-01 11:12:59 +04:00
commits . forEach ( function ( commit ) {
commitCache [ commit . hash ] = true ;
} ) ;
2013-10-25 09:58:58 +04:00
return callback ( '' ) ;
2013-08-01 11:12:59 +04:00
}
2013-09-24 14:46:30 +04:00
buffer += '## Release ' + tag . tag + '\n' ;
2013-08-01 11:12:59 +04:00
commits = commits
. filter ( function ( commit ) {
// Get rid of jenkins' release tagging commits
// Remove commits we've already spat out
return (
2013-09-24 14:46:30 +04:00
commit . author !== 'TryGhost-Jenkins' &&
2013-08-01 11:12:59 +04:00
! commitCache [ commit . hash ]
) ;
} )
. map ( function ( commit ) {
2013-09-24 14:46:30 +04:00
buffer += '\n* ' + commit . message + ' (_' + commit . author + '_)' ;
2013-08-01 11:12:59 +04:00
commitCache [ commit . hash ] = true ;
} ) ;
if ( ! commits . length ) {
2013-10-25 09:58:58 +04:00
buffer += '\nNo changes were made in this build.\n' ;
2013-08-01 11:12:59 +04:00
}
2013-09-24 14:46:30 +04:00
callback ( buffer + '\n' ) ;
2013-08-01 11:12:59 +04:00
} ) ;
}
// Get two weeks' worth of tags
2013-09-24 14:46:30 +04:00
tags . unshift ( { 'tag' : 'HEAD' } ) ;
2013-08-01 11:12:59 +04:00
tags =
tags
. slice ( 0 , 14 )
. map ( function ( tag , index ) {
return [
tag ,
tags [ index + 1 ] || tags [ index ]
] ;
} ) ;
2013-09-24 14:46:30 +04:00
log . write ( '# Ghost Changelog\n\n' ) ;
log . write ( '_Showing ' + tags . length + ' releases._\n' ) ;
2013-08-01 11:12:59 +04:00
when . reduce ( tags ,
function ( prev , tag , idx ) {
2014-02-27 06:44:09 +04:00
/*jshint unused:false*/
2013-08-01 11:12:59 +04:00
return when . promise ( function ( resolve ) {
processTag ( tag , function ( releaseData ) {
2013-09-24 14:46:30 +04:00
resolve ( prev + '\n' + releaseData ) ;
2013-08-01 11:12:59 +04:00
} ) ;
} ) ;
2013-09-24 14:46:30 +04:00
} , '' )
2013-08-01 11:12:59 +04:00
. then ( function ( reducedChangelog ) {
log . write ( reducedChangelog ) ;
log . close ( ) ;
done ( true ) ;
} ) ;
} ) ;
} ) ;
2013-11-01 19:33:49 +04:00
grunt . registerTask ( 'release' ,
'Release task - creates a final built zip\n' +
' - Do our standard build steps (sass, handlebars, etc)\n' +
' - Generate changelog for the past 14 releases\n' +
' - Copy files to release-folder/#/#{version} directory\n' +
' - Clean out unnecessary files (travis, .git*, .af*, .groc*)\n' +
' - Zip files in release-folder to dist-folder/#{version} directory' ,
[
'shell:bourbon' ,
'sass:compress' ,
'handlebars' ,
'concat' ,
'uglify' ,
'clean:release' ,
'copy:release' ,
'compress:release'
] ) ;
2013-10-11 19:31:47 +04:00
2013-10-25 09:58:58 +04:00
grunt . registerTask ( 'dev' ,
'Dev Mode; watch files and restart server on changes' ,
[
'sass:admin' ,
'handlebars' ,
'concat' ,
2014-03-04 01:41:34 +04:00
'copy:dev' ,
2013-10-25 09:58:58 +04:00
'express:dev' ,
'watch'
] ) ;
2013-08-17 22:39:02 +04:00
2013-12-06 14:24:25 +04:00
// ### Find out more about grunt task usage
grunt . registerTask ( 'help' ,
'Outputs help information if you type `grunt help` instead of `grunt --help`' ,
function ( ) {
console . log ( 'Type `grunt --help` to get the details of available grunt tasks, or alternatively visit https://github.com/TryGhost/Ghost/wiki/Grunt-Toolkit' ) ;
} ) ;
2013-11-01 19:33:49 +04:00
2013-12-06 14:24:25 +04:00
// ### Running the test suites
2013-09-13 18:12:38 +04:00
2013-12-06 14:24:25 +04:00
grunt . registerTask ( 'test-unit' , 'Run unit tests (mocha)' , [ 'clean:test' , 'setTestEnv' , 'loadConfig' , 'mochacli:unit' ] ) ;
2013-09-13 18:12:38 +04:00
2013-12-06 14:24:25 +04:00
grunt . registerTask ( 'test-integration' , 'Run integration tests (mocha + db access)' , [ 'clean:test' , 'setTestEnv' , 'loadConfig' , 'mochacli:integration' ] ) ;
2013-10-30 01:34:47 +04:00
2014-03-04 01:41:34 +04:00
grunt . registerTask ( 'test-functional' , 'Run functional interface tests (CasperJS)' , [ 'clean:test' , 'setTestEnv' , 'loadConfig' , 'copy:dev' , 'express:test' , 'spawn-casperjs' , 'express:test:stop' ] ) ;
2013-09-13 18:12:38 +04:00
2013-12-06 14:24:25 +04:00
grunt . registerTask ( 'test-api' , 'Run functional api tests (mocha)' , [ 'clean:test' , 'setTestEnv' , 'loadConfig' , 'express:test' , 'mochacli:api' , 'express:test:stop' ] ) ;
2013-11-24 18:29:36 +04:00
2013-12-31 21:09:49 +04:00
grunt . registerTask ( 'test-routes' , 'Run functional route tests (mocha)' , [ 'clean:test' , 'setTestEnv' , 'loadConfig' , 'express:test' , 'mochacli:routes' , 'express:test:stop' ] ) ;
2014-02-27 06:44:09 +04:00
grunt . registerTask ( 'validate' , 'Run tests and lint code' , [ 'jshint' , 'test-routes' , 'test-unit' , 'test-api' , 'test-integration' , 'test-functional' ] ) ;
2013-09-13 18:12:38 +04:00
2013-11-01 19:33:49 +04:00
2013-12-06 14:24:25 +04:00
// ### Coverage report for Unit and Integration Tests
2013-11-03 03:40:43 +04:00
2014-01-04 18:09:59 +04:00
grunt . registerTask ( 'test-coverage' , 'Generate unit and integration (mocha) tests coverage report' , [ 'clean:test' , 'setTestEnv' , 'loadConfig' , 'shell:coverage' ] ) ;
2013-11-03 03:40:43 +04:00
2013-12-06 14:24:25 +04:00
// ### Documentation
2013-11-01 19:33:49 +04:00
2013-10-25 09:58:58 +04:00
grunt . registerTask ( 'docs' , 'Generate Docs' , [ 'groc' ] ) ;
2013-09-13 18:12:38 +04:00
2013-11-01 19:33:49 +04:00
2013-12-06 14:24:25 +04:00
// ### Tools for building assets
2013-11-01 19:33:49 +04:00
2014-02-19 07:41:21 +04:00
grunt . registerTask ( 'init' , 'Prepare the project for development' , [ 'shell:bundle' , 'shell:bourbon' , 'default' ] ) ;
2013-11-01 19:33:49 +04:00
// Before running in production mode
2014-03-04 01:41:34 +04:00
grunt . registerTask ( 'prod' , 'Build CSS, JS & templates for production' , [ 'sass:compress' , 'handlebars' , 'concat' , 'uglify' , 'copy:prod' ] ) ;
2013-09-14 06:15:30 +04:00
2013-10-25 09:58:58 +04:00
// When you just say 'grunt'
2014-03-04 01:41:34 +04:00
grunt . registerTask ( 'default' , 'Build CSS, JS & templates for development' , [ 'update_submodules' , 'sass:compress' , 'handlebars' , 'concat' , 'copy:dev' ] ) ;
2013-06-25 15:43:15 +04:00
} ;
2013-11-03 18:12:09 +04:00
module . exports = configureGrunt ;