2014-05-03 17:34:41 +04:00
// # Task automation for Ghost
//
// Run various tasks when developing for and working with Ghost.
//
// **Usage instructions:** can be found in the [Custom Tasks](#custom%20tasks) section or by running `grunt --help`.
//
// **Debug tip:** If you have any problems with any Grunt tasks, try running them with the `--verbose` command
2014-08-02 19:21:38 +04:00
var _ = require ( 'lodash' ) ,
2015-05-19 18:51:53 +03:00
chalk = require ( 'chalk' ) ,
2014-08-02 19:21:38 +04:00
fs = require ( 'fs-extra' ) ,
2016-02-12 23:04:26 +03:00
https = require ( 'https' ) ,
2015-01-06 23:45:46 +03:00
moment = require ( 'moment' ) ,
2014-08-02 19:21:38 +04:00
getTopContribs = require ( 'top-gh-contribs' ) ,
path = require ( 'path' ) ,
Promise = require ( 'bluebird' ) ,
2014-08-07 02:17:14 +04:00
escapeChar = process . platform . match ( /^win/ ) ? '^' : '\\' ,
2014-08-08 19:14:15 +04:00
cwd = process . cwd ( ) . replace ( /( |\(|\))/g , escapeChar + '$1' ) ,
2014-08-07 02:17:14 +04:00
buildDirectory = path . resolve ( cwd , '.build' ) ,
distDirectory = path . resolve ( cwd , '.dist' ) ,
2015-03-19 20:00:13 +03:00
emberPath = path . resolve ( cwd + '/core/client/node_modules/.bin/ember' ) ,
2013-09-13 11:24:28 +04:00
2013-12-06 14:24:25 +04:00
// ## Grunt configuration
2013-06-25 15:43:15 +04:00
configureGrunt = function ( grunt ) {
2014-05-03 17:34:41 +04:00
// #### Load all grunt tasks
//
// Find all of the task which start with `grunt-` and load them, rather than explicitly declaring them all
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 = {
2014-05-03 17:34:41 +04:00
// #### Common paths used by tasks
2013-06-25 15:43:15 +04:00
paths : {
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
} ,
2014-05-03 17:34:41 +04:00
// Standard build type, for when we have nightlies again.
2013-07-11 18:24:33 +04:00
buildType : 'Build' ,
2014-05-03 17:34:41 +04:00
// Load package.json so that we can create correctly versioned releases.
2013-06-25 15:43:15 +04:00
pkg : grunt . file . readJSON ( 'package.json' ) ,
2014-05-03 17:34:41 +04:00
// ### grunt-contrib-watch
// Watch files and livereload in the browser during development.
// See the [grunt dev](#live%20reload) task for how this is used.
2013-08-17 22:39:02 +04:00
watch : {
livereload : {
files : [
2014-08-06 04:26:42 +04:00
'content/themes/casper/assets/css/*.css' ,
'content/themes/casper/assets/js/*.js' ,
2015-06-23 06:08:00 +03:00
'core/built/assets/*.js' ,
'core/client/dist/index.html'
2013-08-17 22:39:02 +04:00
] ,
options : {
livereload : true
}
} ,
express : {
2015-06-05 22:48:04 +03:00
files : [ 'core/ghost-server.js' , 'core/server/**/*.js' ] ,
2013-08-17 22:39:02 +04:00
tasks : [ 'express:dev' ] ,
options : {
2015-06-05 22:48:04 +03:00
spawn : false
2013-08-17 22:39:02 +04:00
}
2015-05-17 15:33:41 +03:00
} ,
csscomb : {
files : [ 'core/client/app/styles/**/*.css' ] ,
tasks : [ 'shell:csscombfix' ] ,
options : {
livereload : true
}
2013-08-17 22:39:02 +04:00
}
} ,
2014-05-03 17:34:41 +04:00
// ### grunt-express-server
// Start a Ghost expess server for use in development and testing
2013-08-17 22:39:02 +04:00
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 : {
2014-05-03 17:34:41 +04:00
options : { }
2013-08-29 14:04:33 +04:00
} ,
test : {
options : {
node _env : 'testing'
}
2013-08-17 22:39:02 +04:00
}
} ,
2014-05-03 17:34:41 +04:00
// ### grunt-contrib-jshint
// Linting rules, run as part of `grunt validate`. See [grunt validate](#validate) and its subtasks for
// more information.
2015-02-19 02:47:55 +03:00
jshint : {
options : {
jshintrc : true
} ,
2014-09-03 19:42:55 +04:00
2015-02-19 02:47:55 +03:00
server : [
'*.js' ,
'!config*.js' , // note: i added this, do we want this linted?
'core/*.js' ,
'core/server/**/*.js' ,
'core/test/**/*.js' ,
2015-04-18 15:20:03 +03:00
'!core/test/coverage/**' ,
2015-02-19 02:47:55 +03:00
'!core/shared/vendor/**/*.js'
]
} ,
jscs : {
options : {
config : true
} ,
server : {
files : {
src : [
'*.js' ,
'!config*.js' , // note: i added this, do we want this linted?
'core/*.js' ,
'core/server/**/*.js' ,
'core/test/**/*.js' ,
2015-04-18 15:20:03 +03:00
'!core/test/coverage/**' ,
2015-02-19 02:47:55 +03:00
'!core/shared/vendor/**/*.js'
]
}
}
} ,
2013-06-25 15:43:15 +04:00
2014-05-03 17:34:41 +04:00
// ### grunt-mocha-cli
// Configuration for the mocha test runner, used to run unit, integration and route tests as part of
// `grunt validate`. See [grunt validate](#validate) and its sub tasks for more information.
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' ,
2014-09-03 23:50:19 +04:00
reporter : grunt . option ( 'reporter' ) || 'spec' ,
timeout : '15000' ,
2016-06-03 11:06:18 +03:00
save : grunt . option ( 'reporter-output' ) ,
require : [ 'core/server/overrides' ]
2013-05-26 21:17:46 +04:00
} ,
2014-05-03 17:34:41 +04:00
// #### All Unit tests
2013-11-24 18:29:36 +04:00
unit : {
2014-07-17 18:33:21 +04:00
src : [
2016-04-11 16:58:41 +03:00
'core/test/unit/**/*_spec.js' ,
'core/server/apps/**/tests/*_spec.js'
2014-07-17 18:33:21 +04:00
]
2013-06-25 15:43:15 +04:00
} ,
2013-05-25 20:48:15 +04:00
2014-05-03 17:34:41 +04:00
// #### All Integration tests
2013-10-30 01:34:47 +04:00
integration : {
2014-02-26 21:51:01 +04:00
src : [
2015-11-20 15:04:49 +03:00
'core/test/integration/**/*_spec.js' ,
2014-05-04 05:30:30 +04:00
'core/test/integration/*_spec.js'
2014-02-26 21:51:01 +04:00
]
2013-11-24 18:29:36 +04:00
} ,
2014-05-03 17:34:41 +04:00
// #### All Route tests
2013-12-31 21:09:49 +04:00
routes : {
2014-07-17 18:33:21 +04:00
src : [
2015-04-18 15:20:03 +03:00
'core/test/functional/routes/**/*_spec.js'
2014-07-17 18:33:21 +04:00
]
2014-09-27 21:26:26 +04:00
} ,
// #### All Module tests
module : {
src : [
2015-04-18 15:20:03 +03:00
'core/test/functional/module/**/*_spec.js'
2014-09-27 21:26:26 +04:00
]
2016-05-25 09:02:23 +03:00
} ,
// #### Run single test (src is set dynamically, see grunt task 'test')
single : { }
2013-06-25 15:43:15 +04:00
} ,
2013-06-15 02:12:04 +04:00
2015-04-18 15:20:03 +03:00
// ### grunt-mocha-istanbul
// Configuration for the mocha test coverage generator
// `grunt coverage`.
mocha _istanbul : {
coverage : {
// they can also have coverage generated for them & the order doesn't matter
2016-03-29 22:14:11 +03:00
src : [
'core/test/unit' ,
'core/server/apps'
] ,
2015-04-18 15:20:03 +03:00
options : {
mask : '**/*_spec.js' ,
2015-07-28 17:02:00 +03:00
coverageFolder : 'core/test/coverage/unit' ,
2016-06-09 14:01:35 +03:00
mochaOptions : [ '--timeout=15000' , '--require' , 'core/server/overrides' ] ,
2016-06-01 14:49:36 +03:00
excludes : [ 'core/client' , 'core/server/built' ]
2015-04-18 15:20:03 +03:00
}
2015-07-28 17:02:00 +03:00
} ,
2016-03-29 22:14:11 +03:00
coverage _all : {
src : [
'core/test/integration' ,
2016-04-13 19:10:40 +03:00
'core/server/apps' ,
'core/test/functional' ,
'core/test/unit'
2016-03-29 22:14:11 +03:00
] ,
2015-07-28 17:02:00 +03:00
options : {
2016-03-29 22:14:11 +03:00
coverageFolder : 'core/test/coverage/all' ,
2015-07-28 17:02:00 +03:00
mask : '**/*_spec.js' ,
2016-06-09 14:01:35 +03:00
mochaOptions : [ '--timeout=15000' , '--require' , 'core/server/overrides' ] ,
2016-06-01 14:49:36 +03:00
excludes : [ 'core/client' , 'core/server/built' ]
2015-07-28 17:02:00 +03:00
}
2015-04-18 15:20:03 +03:00
}
} ,
2015-02-14 21:43:18 +03:00
// ### grunt-bg-shell
// Used to run ember-cli watch in the background
bgShell : {
ember : {
2015-03-19 20:00:13 +03:00
cmd : emberPath + ' build --watch' ,
2015-02-14 21:43:18 +03:00
execOpts : {
cwd : path . resolve ( cwd + '/core/client/' )
} ,
bg : true ,
stdout : function ( out ) {
2015-05-19 18:51:53 +03:00
grunt . log . writeln ( chalk . cyan ( 'Ember-cli::' ) + out ) ;
2015-02-14 21:43:18 +03:00
} ,
stderror : function ( error ) {
2015-05-19 18:51:53 +03:00
grunt . log . error ( chalk . red ( 'Ember-cli::' + error ) ) ;
2015-02-14 21:43:18 +03:00
}
}
} ,
2014-05-03 17:34:41 +04:00
// ### grunt-shell
// Command line tools where it's easier to run a command directly than configure a grunt plugin
2013-06-25 15:43:15 +04:00
shell : {
2015-02-14 21:43:18 +03:00
ember : {
command : function ( mode ) {
switch ( mode ) {
case 'init' :
2016-05-23 18:59:42 +03:00
return 'echo Installing client dependencies... && npm install && bower install' ;
2015-02-14 21:43:18 +03:00
case 'prod' :
2015-03-19 20:00:13 +03:00
return emberPath + ' build --environment=production --silent' ;
2015-02-14 21:43:18 +03:00
case 'dev' :
2015-03-31 10:43:45 +03:00
return emberPath + ' build' ;
2015-02-18 23:02:48 +03:00
case 'test' :
2015-03-19 20:00:13 +03:00
return emberPath + ' test --silent' ;
2015-02-14 21:43:18 +03:00
}
} ,
options : {
execOptions : {
2015-07-06 17:21:16 +03:00
cwd : path . resolve ( process . cwd ( ) + '/core/client/' ) ,
2015-02-14 21:43:18 +03:00
stdout : false
}
}
} ,
2014-08-30 22:52:05 +04:00
2015-02-27 20:15:33 +03:00
shrinkwrap : {
command : 'npm shrinkwrap'
2015-05-16 14:43:12 +03:00
} ,
2016-06-06 15:52:18 +03:00
prune : {
command : 'npm prune'
} ,
2015-09-23 16:21:11 +03:00
dedupe : {
command : 'npm dedupe'
} ,
2015-05-17 15:33:41 +03:00
csscombfix : {
2015-05-16 14:43:12 +03:00
command : path . resolve ( cwd + '/node_modules/.bin/csscomb -c core/client/app/styles/csscomb.json -v core/client/app/styles' )
2013-06-25 15:43:15 +04:00
}
} ,
2013-06-02 03:45:02 +04:00
2014-05-03 17:34:41 +04:00
// ### grunt-docker
// Generate documentation from code
2014-05-03 15:38:59 +04:00
docker : {
2014-05-03 17:34:41 +04:00
docs : {
2014-05-05 19:26:19 +04:00
dest : 'docs' ,
2014-05-03 15:38:59 +04:00
src : [ '.' ] ,
options : {
2014-05-05 19:26:19 +04:00
onlyUpdated : true ,
2015-05-28 18:16:09 +03:00
exclude : 'node_modules,bower_components,content,core/client,*test,*doc*,' +
'*vendor,config.js,*buil*,.dist*,.idea,.git*,.travis.yml,.bower*,.editorconfig,.js*,*.md' ,
2014-05-03 15:38:59 +04:00
extras : [ 'fileSearch' ]
}
}
} ,
2014-05-03 17:34:41 +04:00
// ### grunt-contrib-clean
2013-12-06 14:24:25 +04:00
// Clean up files as part of other tasks
2013-09-18 22:56:39 +04:00
clean : {
2014-03-02 18:30:35 +04:00
built : {
2014-09-03 06:58:20 +04:00
src : [
'core/built/**' ,
2015-02-14 21:43:18 +03:00
'core/client/dist/**' ,
'core/client/public/assets/img/contributors/**' ,
'core/client/app/templates/-contributors.hbs'
2014-09-03 06:58:20 +04:00
]
2014-03-02 18:30:35 +04:00
} ,
2013-11-01 19:33:49 +04:00
release : {
2014-08-06 04:44:23 +04:00
src : [ '<%= paths.releaseBuild %>/**' ]
2013-10-15 07:39:52 +04:00
} ,
test : {
src : [ 'content/data/ghost-test.db' ]
2014-03-10 07:44:08 +04:00
} ,
tmp : {
src : [ '.tmp/**' ]
2015-05-18 00:41:49 +03:00
} ,
dependencies : {
src : [ 'node_modules/**' , 'core/client/bower_components/**' , 'core/client/node_modules/**' ]
2013-09-18 22:56:39 +04:00
}
} ,
2014-05-03 17:34:41 +04:00
// ### grunt-contrib-compress
// Zip up files for 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
} ,
2014-05-27 18:37:44 +04:00
// ### grunt-update-submodules
// Grunt task to update git submodules
2014-09-10 08:06:24 +04:00
update _submodules : {
2014-05-27 18:37:44 +04:00
default : {
options : {
2014-07-01 03:26:08 +04:00
params : '--init'
2014-05-27 18:37:44 +04:00
}
}
2015-12-10 17:46:58 +03:00
} ,
uglify : {
prod : {
options : {
sourceMap : false
} ,
files : {
2016-05-12 19:20:08 +03:00
'core/shared/ghost-url.min.js' : 'core/shared/ghost-url.js'
2015-12-10 17:46:58 +03:00
}
}
2016-05-31 16:40:20 +03:00
} ,
// ### grunt-subgrunt
// Run grunt tasks in submodule Gruntfiles
subgrunt : {
lint : {
'core/client' : 'lint'
}
2013-06-25 15:43:15 +04:00
}
2013-06-10 17:52:04 +04:00
} ;
2013-05-13 23:18:20 +04:00
2014-05-03 17:34:41 +04:00
// Load the configuration
2013-06-25 15:43:15 +04:00
grunt . initConfig ( cfg ) ;
2014-05-03 17:34:41 +04:00
// # Custom Tasks
// Ghost has a number of useful tasks that we use every day in development. Tasks marked as *Utility* are used
// by grunt to perform current actions, but isn't useful to developers.
//
// Skip ahead to the section on:
//
// * [Building assets](#building%20assets):
// `grunt init`, `grunt` & `grunt prod` or live reload with `grunt dev`
// * [Testing](#testing):
2015-04-18 15:20:03 +03:00
// `grunt validate`, the `grunt test-*` sub-tasks or generate a coverage report with `grunt coverage`.
2014-05-03 17:34:41 +04:00
// ### Help
// Run `grunt help` on the commandline to get a print out of the available tasks and details of
// what each one does along with any available options. This is an alias for `grunt --help`
grunt . registerTask ( 'help' ,
'Outputs help information if you type `grunt help` instead of `grunt --help`' ,
function ( ) {
2016-03-29 22:14:11 +03:00
console . log ( 'Type `grunt --help` to get the details of available grunt tasks.' ) ;
2014-05-03 17:34:41 +04:00
} ) ;
2013-08-01 11:12:59 +04:00
2014-05-03 17:34:41 +04:00
// ### Documentation
// Run `grunt docs` to generate annotated source code using the documentation described in the code comments.
2014-05-05 19:26:19 +04:00
grunt . registerTask ( 'docs' , 'Generate Docs' , [ 'docker' ] ) ;
2013-08-01 11:12:59 +04:00
2015-05-28 18:16:09 +03:00
// Runun `grunt watch-docs` to setup livereload & watch whilst you're editing the docs
grunt . registerTask ( 'watch-docs' , function ( ) {
grunt . config . merge ( {
watch : {
docs : {
files : [ 'core/server/**/*' , 'index.js' , 'Gruntfile.js' , 'config.example.js' ] ,
tasks : [ 'docker' ] ,
options : {
livereload : true
}
}
}
} ) ;
grunt . task . run ( 'watch:docs' ) ;
} ) ;
2014-05-03 17:34:41 +04:00
// ## Testing
2013-08-01 11:12:59 +04:00
2014-05-03 17:34:41 +04:00
// Ghost has an extensive set of test suites. The following section documents the various types of tests
// and how to run them.
//
// TLDR; run `grunt validate`
2013-08-01 11:12:59 +04:00
2014-05-03 17:34:41 +04:00
// #### Set Test Env *(Utility Task)*
// Set the NODE_ENV to 'testing' unless the environment is already set to TRAVIS.
// This ensures that the tests get run under the correct environment, using the correct database, and
// that they work as expected. Trying to run tests with no ENV set will throw an error to do with `client`.
grunt . registerTask ( 'setTestEnv' ,
'Use "testing" Ghost config; unless we are running on travis (then show queries for debugging)' ,
function ( ) {
process . env . NODE _ENV = process . env . TRAVIS ? process . env . NODE _ENV : 'testing' ;
cfg . express . test . options . node _env = process . env . NODE _ENV ;
} ) ;
2013-08-01 11:12:59 +04:00
2014-07-17 18:33:21 +04:00
// #### Ensure Config *(Utility Task)*
2014-05-03 17:34:41 +04:00
// Make sure that we have a `config.js` file when running tests
// Ghost requires a `config.js` file to specify the database settings etc. Ghost comes with an example file:
// `config.example.js` which is copied and renamed to `config.js` by the bootstrap process
2014-07-17 18:33:21 +04:00
grunt . registerTask ( 'ensureConfig' , function ( ) {
2014-08-23 20:19:13 +04:00
var config = require ( './core/server/config' ) ,
2014-07-17 18:33:21 +04:00
done = this . async ( ) ;
2015-11-13 14:54:50 +03:00
if ( ! process . env . TEST _SUITE || process . env . TEST _SUITE !== 'client' ) {
config . load ( ) . then ( function ( ) {
done ( ) ;
} ) . catch ( function ( err ) {
grunt . fail . fatal ( err . stack ) ;
} ) ;
} else {
2014-05-03 17:34:41 +04:00
done ( ) ;
2015-11-13 14:54:50 +03:00
}
2013-08-01 11:12:59 +04:00
} ) ;
2014-07-15 23:52:44 +04:00
// #### Reset Database to "New" state *(Utility Task)*
// Drops all database tables and then runs the migration process to put the database
// in a "new" state.
grunt . registerTask ( 'cleanDatabase' , function ( ) {
var done = this . async ( ) ,
2014-08-14 01:58:12 +04:00
models = require ( './core/server/models' ) ,
2014-07-15 23:52:44 +04:00
migration = require ( './core/server/data/migration' ) ;
migration . reset ( ) . then ( function ( ) {
2016-03-02 07:42:01 +03:00
models . init ( ) ;
2014-07-15 23:52:44 +04:00
return migration . init ( ) ;
} ) . then ( function ( ) {
done ( ) ;
} ) . catch ( function ( err ) {
grunt . fail . fatal ( err . stack ) ;
} ) ;
} ) ;
2016-03-29 22:14:11 +03:00
// ### Test
// **Testing utility**
//
// `grunt test:unit/apps_spec.js` will run just the tests inside the apps_spec.js file
//
// It works for any path relative to the core/test folder. It will also run all the tests in a single directory
2016-06-08 18:31:19 +03:00
// You can also run a test with grunt test:core/test/unit/... to get bash autocompletion
2016-03-29 22:14:11 +03:00
//
// `grunt test:integration/api` - runs the api integration tests
// `grunt test:integration` - runs the integration tests in the root folder and excludes all api & model tests
grunt . registerTask ( 'test' , 'Run a particular spec file from the core/test directory e.g. `grunt test:unit/apps_spec.js`' , function ( test ) {
2014-12-24 16:51:16 +03:00
if ( ! test ) {
2015-09-27 20:29:14 +03:00
grunt . fail . fatal ( 'No test provided. `grunt test` expects a filename. e.g.: `grunt test:unit/apps_spec.js`. Did you mean `npm test` or `grunt validate`?' ) ;
2014-12-24 16:51:16 +03:00
}
2016-06-08 18:31:19 +03:00
if ( ! test . match ( /core\/test/ ) ) {
test = 'core/test/' + test ;
}
2016-05-25 09:02:23 +03:00
// CASE: execute folder
if ( ! test . match ( /.js/ ) ) {
test += '/**' ;
} else if ( ! fs . existsSync ( test ) ) {
grunt . fail . fatal ( 'This file does not exist!' ) ;
}
cfg . mochacli . single . src = [ test ] ;
grunt . initConfig ( cfg ) ;
grunt . task . run ( 'test-setup' , 'mochacli:single' ) ;
2014-12-24 16:51:16 +03:00
} ) ;
2014-05-03 17:34:41 +04:00
// ### Validate
// **Main testing task**
//
2014-07-01 16:35:50 +04:00
// `grunt validate` will build, lint and test your local Ghost codebase.
2014-05-03 17:34:41 +04:00
//
// `grunt validate` is one of the most important and useful grunt tasks that we have available to use. It
2014-07-01 16:35:50 +04:00
// manages the build of your environment and then calls `grunt test`
2014-05-03 17:34:41 +04:00
//
2014-07-01 16:35:50 +04:00
// `grunt validate` is called by `npm test` and is used by Travis.
2015-11-13 14:54:50 +03:00
grunt . registerTask ( 'validate' , 'Run tests and lint code' , function ( ) {
if ( process . env . TEST _SUITE === 'server' ) {
2015-12-04 20:45:32 +03:00
grunt . task . run ( [ 'init' , 'test-server' ] ) ;
2015-11-13 14:54:50 +03:00
} else if ( process . env . TEST _SUITE === 'client' ) {
2015-12-04 20:45:32 +03:00
grunt . task . run ( [ 'init' , 'test-client' ] ) ;
2015-11-13 14:54:50 +03:00
} else if ( process . env . TEST _SUITE === 'lint' ) {
2016-05-31 16:40:20 +03:00
grunt . task . run ( [ 'lint' ] ) ;
2015-11-13 14:54:50 +03:00
} else {
grunt . task . run ( [ 'validate-all' ] ) ;
}
} ) ;
grunt . registerTask ( 'validate-all' , 'Lint code and run all tests' ,
2015-04-22 23:39:35 +03:00
[ 'init' , 'lint' , 'test-all' ] ) ;
2014-07-01 16:35:50 +04:00
2015-01-25 23:27:00 +03:00
// ### Test-All
2014-07-01 16:35:50 +04:00
// **Main testing task**
//
2015-01-25 23:27:00 +03:00
// `grunt test-all` will lint and test your pre-built local Ghost codebase.
2014-07-01 16:35:50 +04:00
//
2015-11-13 14:54:50 +03:00
// `grunt test-all` runs all 6 test suites. See the individual sub tasks below for
2014-09-19 18:52:48 +04:00
// details of each of the test suites.
2014-07-01 16:35:50 +04:00
//
2015-11-13 14:54:50 +03:00
grunt . registerTask ( 'test-all' , 'Run tests for both server and client' ,
2015-12-04 20:45:32 +03:00
[ 'test-server' , 'test-client' ] ) ;
2015-11-13 14:54:50 +03:00
grunt . registerTask ( 'test-server' , 'Run server tests' ,
2015-12-04 20:45:32 +03:00
[ 'test-routes' , 'test-module' , 'test-unit' , 'test-integration' ] ) ;
2015-11-13 14:54:50 +03:00
grunt . registerTask ( 'test-client' , 'Run client tests' ,
2015-12-04 20:45:32 +03:00
[ 'test-ember' ] ) ;
2014-05-03 17:34:41 +04:00
2014-09-19 18:52:48 +04:00
// ### Lint
//
// `grunt lint` will run the linter and the code style checker so you can make sure your code is pretty
2016-05-31 16:40:20 +03:00
grunt . registerTask ( 'lint' , 'Run the code style checks and linter for server' ,
[ 'jshint' , 'jscs' ]
) ;
grunt . registerTask ( 'lint-all' , 'Run the code style checks and linter for server and client' ,
[ 'lint' , 'subgrunt:lint' ]
2015-01-25 23:27:00 +03:00
) ;
// ### test-setup *(utility)(
// `grunt test-setup` will run all the setup tasks required for running tests
grunt . registerTask ( 'test-setup' , 'Setup ready to run tests' ,
[ 'clean:test' , 'setTestEnv' , 'ensureConfig' ]
) ;
2014-09-19 18:52:48 +04:00
2014-05-03 17:34:41 +04:00
// ### Unit Tests *(sub task)*
// `grunt test-unit` will run just the unit tests
//
2016-03-29 22:14:11 +03:00
// If you need to run an individual unit test file, you can use the `grunt test:<file_path>` task:
2014-05-03 17:34:41 +04:00
//
2016-03-29 22:14:11 +03:00
// `grunt test:unit/config_spec.js`
2014-05-03 17:34:41 +04:00
//
2016-03-29 22:14:11 +03:00
// This also works for folders (although it isn't recursive), E.g.
2014-05-03 17:34:41 +04:00
//
2016-03-29 22:14:11 +03:00
// `grunt test:unit/server_helpers`
2014-05-03 17:34:41 +04:00
//
2014-11-27 23:50:15 +03:00
// Unit tests are run with [mocha](http://mochajs.org/) using
2014-05-03 17:34:41 +04:00
// [should](https://github.com/visionmedia/should.js) to describe the tests in a highly readable style.
// Unit tests do **not** touch the database.
// A coverage report can be generated for these tests using the `grunt test-coverage` task.
grunt . registerTask ( 'test-unit' , 'Run unit tests (mocha)' ,
2015-01-25 23:27:00 +03:00
[ 'test-setup' , 'mochacli:unit' ]
) ;
2014-05-03 17:34:41 +04:00
// ### Integration tests *(sub task)*
// `grunt test-integration` will run just the integration tests
//
// Provided you already have a `config.js` file, you can run just the model integration tests by running:
//
2016-03-29 22:14:11 +03:00
// `grunt test:integration/model`
2014-05-03 17:34:41 +04:00
//
// Or just the api integration tests by running:
//
2016-03-29 22:14:11 +03:00
// `grunt test:integration/api`
2014-05-03 17:34:41 +04:00
//
2014-11-27 23:50:15 +03:00
// Integration tests are run with [mocha](http://mochajs.org/) using
2014-05-03 17:34:41 +04:00
// [should](https://github.com/visionmedia/should.js) to describe the tests in a highly readable style.
// Integration tests are different to the unit tests because they make requests to the database.
//
2016-03-29 22:14:11 +03:00
// If you need to run an individual integration test file you can use the `grunt test:<file_path>` task:
2014-07-20 18:11:02 +04:00
//
2016-03-29 22:14:11 +03:00
// `grunt test:integration/api/api_tags_spec.js`
2014-07-20 18:11:02 +04:00
//
2014-05-03 17:34:41 +04:00
// Their purpose is to test that both the api and models behave as expected when the database layer is involved.
// These tests are run against sqlite3, mysql and pg on travis and ensure that differences between the databases
// don't cause bugs. At present, pg often fails and is not officially supported.
//
// A coverage report can be generated for these tests using the `grunt test-coverage` task.
grunt . registerTask ( 'test-integration' , 'Run integration tests (mocha + db access)' ,
2015-01-25 23:27:00 +03:00
[ 'test-setup' , 'mochacli:integration' ]
) ;
2014-05-03 17:34:41 +04:00
// ### Route tests *(sub task)*
// `grunt test-routes` will run just the route tests
//
2016-03-29 22:14:11 +03:00
// If you need to run an individual route test file, you can use the `grunt test:<file_path>` task:
2014-05-03 17:34:41 +04:00
//
2016-03-29 22:14:11 +03:00
// `grunt test:functional/routes/admin_spec.js`
2014-05-03 17:34:41 +04:00
//
2014-11-27 23:50:15 +03:00
// Route tests are run with [mocha](http://mochajs.org/) using
2014-05-03 17:34:41 +04:00
// [should](https://github.com/visionmedia/should.js) and [supertest](https://github.com/visionmedia/supertest)
// to describe and create the tests.
//
// Supertest enables us to describe requests that we want to make, and also describe the response we expect to
// receive back. It works directly with express, so we don't have to run a server to run the tests.
//
// The purpose of the route tests is to ensure that all of the routes (pages, and API requests) in Ghost
// are working as expected, including checking the headers and status codes received. It is very easy and
// quick to test many permutations of routes / urls in the system.
grunt . registerTask ( 'test-routes' , 'Run functional route tests (mocha)' ,
2015-01-25 23:27:00 +03:00
[ 'test-setup' , 'mochacli:routes' ]
) ;
2014-05-03 17:34:41 +04:00
2014-09-27 21:26:26 +04:00
// ### Module tests *(sub task)*
// `grunt test-module` will run just the module tests
//
// The purpose of the module tests is to ensure that Ghost can be used as an npm module and exposes all
// required methods to interact with it.
grunt . registerTask ( 'test-module' , 'Run functional module tests (mocha)' ,
2015-01-25 23:27:00 +03:00
[ 'test-setup' , 'mochacli:module' ]
) ;
2014-09-27 21:26:26 +04:00
2015-01-25 23:27:00 +03:00
// ### Ember unit tests *(sub task)*
2015-06-03 09:25:56 +03:00
// `grunt test-ember` will run just the ember unit tests
2015-05-18 00:41:49 +03:00
grunt . registerTask ( 'test-ember' , 'Run the ember unit tests' ,
[ 'test-setup' , 'shell:ember:test' ]
2014-07-15 23:52:44 +04:00
) ;
2014-05-03 17:34:41 +04:00
// ### Coverage
2015-04-18 15:20:03 +03:00
// `grunt coverage` will generate a report for the Unit Tests.
2014-05-03 17:34:41 +04:00
//
// This is not currently done as part of CI or any build, but is a tool we have available to keep an eye on how
// well the unit and integration tests are covering the code base.
// Ghost does not have a minimum coverage level - we're more interested in ensuring important and useful areas
// of the codebase are covered, than that the whole codebase is covered to a particular level.
//
// Key areas for coverage are: helpers and theme elements, apps / GDK, the api and model layers.
2015-04-18 15:20:03 +03:00
grunt . registerTask ( 'coverage' , 'Generate unit and integration (mocha) tests coverage report' ,
[ 'test-setup' , 'mocha_istanbul:coverage' ]
2015-01-25 23:27:00 +03:00
) ;
2014-05-03 17:34:41 +04:00
2016-03-29 22:14:11 +03:00
grunt . registerTask ( 'coverage-all' , 'Generate unit and integration tests coverage report' ,
[ 'test-setup' , 'mocha_istanbul:coverage_all' ]
2015-07-28 17:02:00 +03:00
) ;
2014-05-03 17:34:41 +04:00
// #### Master Warning *(Utility Task)*
// Warns git users not ot use the `master` branch in production.
// `master` is an unstable branch and shouldn't be used in production as you run the risk of ending up with a
// database in an unrecoverable state. Instead there is a branch called `stable` which is the equivalent of the
// release zip for git users.
2014-04-16 13:45:49 +04:00
grunt . registerTask ( 'master-warn' ,
'Outputs a warning to runners of grunt prod, that master shouldn\'t be used for live blogs' ,
function ( ) {
2016-03-29 22:14:11 +03:00
console . log ( chalk . red (
'Use the ' + chalk . bold ( 'stable' ) + ' branch for live blogs. '
+ chalk . bold . underline ( 'Never' ) + ' master!'
) ) ;
console . log ( '>' , 'Always two there are, no more, no less. A master and a ' + chalk . bold ( 'stable' ) + '.' ) ;
2014-04-16 13:45:49 +04:00
} ) ;
2014-08-02 19:21:38 +04:00
// ### Build About Page *(Utility Task)*
// Builds the github contributors partial template used on the Settings/About page,
// and downloads the avatar for each of the users.
2015-02-14 21:43:18 +03:00
// Run by any task that compiles the ember assets or manually via `grunt buildAboutPage`.
2014-09-03 06:58:20 +04:00
// Only builds if the contributors template does not exist.
// To force a build regardless, supply the --force option.
// `grunt buildAboutPage --force`
2014-08-02 19:21:38 +04:00
grunt . registerTask ( 'buildAboutPage' , 'Compile assets for the About Ghost page' , function ( ) {
2014-09-03 06:58:20 +04:00
var done = this . async ( ) ,
2015-02-14 21:43:18 +03:00
templatePath = 'core/client/app/templates/-contributors.hbs' ,
imagePath = 'core/client/public/assets/img/contributors/' ,
2015-07-15 21:45:38 +03:00
timeSpan = moment ( ) . subtract ( 90 , 'days' ) . format ( 'YYYY-MM-DD' ) ,
2015-01-07 23:44:14 +03:00
oauthKey = process . env . GITHUB _OAUTH _KEY ;
2014-09-03 06:58:20 +04:00
if ( fs . existsSync ( templatePath ) && ! grunt . option ( 'force' ) ) {
grunt . log . writeln ( 'Contributors template already exists.' ) ;
2015-05-19 18:51:53 +03:00
grunt . log . writeln ( chalk . bold ( 'Skipped' ) ) ;
2014-09-03 06:58:20 +04:00
return done ( ) ;
}
2014-08-02 19:21:38 +04:00
grunt . verbose . writeln ( 'Downloading release and contributor information from GitHub' ) ;
2014-12-25 03:51:29 +03:00
return Promise . join (
Promise . promisify ( fs . mkdirs ) ( imagePath ) ,
getTopContribs ( {
user : 'tryghost' ,
repo : 'ghost' ,
2015-01-07 23:44:14 +03:00
oauthKey : oauthKey ,
2015-07-15 21:45:38 +03:00
sinceDate : timeSpan ,
2015-05-19 14:42:53 +03:00
count : 18 ,
2015-02-05 21:52:53 +03:00
retry : true
2014-12-25 03:51:29 +03:00
} )
) . then ( function ( results ) {
var contributors = results [ 1 ] ,
2015-05-19 14:42:53 +03:00
contributorTemplate = '<article>\n <a href="<%githubUrl%>" title="<%name%>">\n' +
2016-02-12 23:04:26 +03:00
' <img src="{{gh-path "admin" "/img/contributors"}}/<%name%>" alt="<%name%>" />\n' +
' </a>\n</article>' ,
2014-12-25 03:51:29 +03:00
downloadImagePromise = function ( url , name ) {
return new Promise ( function ( resolve , reject ) {
2016-02-18 16:49:42 +03:00
var file = fs . createWriteStream ( path . join ( _ _dirname , imagePath , name ) ) ;
https . get ( url , function ( response ) {
response . pipe ( file ) ;
file . on ( 'finish' , function ( ) {
file . close ( ) ;
2016-02-12 23:04:26 +03:00
resolve ( ) ;
} ) ;
} )
. on ( 'error' , reject ) ;
2014-12-25 03:51:29 +03:00
} ) ;
} ;
2014-08-02 19:21:38 +04:00
grunt . verbose . writeln ( 'Creating contributors template.' ) ;
2014-09-03 06:58:20 +04:00
grunt . file . write ( templatePath ,
2014-09-10 08:06:24 +04:00
// Map contributors to the template.
2014-08-02 19:21:38 +04:00
_ . map ( contributors , function ( contributor ) {
return contributorTemplate
. replace ( /<%githubUrl%>/g , contributor . githubUrl )
. replace ( /<%name%>/g , contributor . name ) ;
} ) . join ( '\n' )
) ;
2014-12-25 03:51:29 +03:00
2014-08-02 19:21:38 +04:00
grunt . verbose . writeln ( 'Downloading images for top contributors' ) ;
return Promise . all ( _ . map ( contributors , function ( contributor ) {
2014-09-25 13:53:20 +04:00
return downloadImagePromise ( contributor . avatarUrl + '&s=60' , contributor . name ) ;
2014-08-02 19:21:38 +04:00
} ) ) ;
2014-12-25 03:51:29 +03:00
} ) . then ( done ) . catch ( function ( error ) {
2015-01-06 23:45:46 +03:00
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 ( ) ) ;
}
2014-12-25 03:51:29 +03:00
done ( false ) ;
} ) ;
2014-08-02 19:21:38 +04:00
} ) ;
2015-02-14 21:43:18 +03:00
// ## Building assets
//
// Ghost's GitHub repository contains the un-built source code for Ghost. If you're looking for the already
// built release zips, you can get these from the [release page](https://github.com/TryGhost/Ghost/releases) on
// GitHub or from https://ghost.org/download. These zip files are created using the [grunt release](#release)
// task.
//
// If you want to work on Ghost core, or you want to use the source files from GitHub, then you have to build
// the Ghost assets in order to make them work.
//
// There are a number of grunt tasks available to help with this. Firstly after fetching an updated version of
// the Ghost codebase, after running `npm install`, you will need to run [grunt init](#init%20assets).
//
// For production blogs you will need to run [grunt prod](#production%20assets).
//
// For updating assets during development, the tasks [grunt](#default%20asset%20build) and
// [grunt dev](#live%20reload) are available.
2014-05-03 17:34:41 +04:00
// ### Init assets
// `grunt init` - will run an initial asset build for you
//
2016-05-23 18:59:42 +03:00
// Grunt init runs `npm install && bower install` inside `core/client` as well as the standard asset build
// tasks which occur when you run just `grunt`. This fetches the latest client-side dependencies.
2014-05-03 17:34:41 +04:00
//
2016-05-23 18:59:42 +03:00
// This task is very important, and should always be run when fetching down an updated code base just after
2014-05-03 17:34:41 +04:00
// running `npm install`.
//
// `bower` does have some quirks, such as not running as root. If you have problems please try running
// `grunt init --verbose` to see if there are any errors.
grunt . registerTask ( 'init' , 'Prepare the project for development' ,
2016-05-23 18:59:42 +03:00
[ 'update_submodules' , 'shell:ember:init' , 'assets' , 'default' ] ) ;
2014-05-03 17:34:41 +04:00
2015-02-14 21:43:18 +03:00
// ### Basic Asset Building
// Builds and moves necessary client assets. Prod additionally builds the ember app.
grunt . registerTask ( 'assets' , 'Basic asset building & moving' ,
2015-08-18 16:08:52 +03:00
[ 'clean:tmp' , 'buildAboutPage' ] ) ;
2014-05-03 17:34:41 +04:00
// ### Default asset build
// `grunt` - default grunt task
//
2015-02-14 21:43:18 +03:00
// Build assets and dev version of the admin app.
2014-05-03 17:34:41 +04:00
grunt . registerTask ( 'default' , 'Build JS & templates for development' ,
2015-03-08 20:09:57 +03:00
[ 'shell:ember:dev' ] ) ;
2015-02-14 21:43:18 +03:00
// ### Production assets
// `grunt prod` - will build the minified assets used in production.
//
// It is otherwise the same as running `grunt`, but is only used when running Ghost in the `production` env.
grunt . registerTask ( 'prod' , 'Build JS & templates for production' ,
2015-12-10 17:46:58 +03:00
[ 'shell:ember:prod' , 'uglify:prod' , 'master-warn' ] ) ;
2014-05-03 17:34:41 +04:00
// ### Live reload
// `grunt dev` - build assets on the fly whilst developing
//
// If you want Ghost to live reload for you whilst you're developing, you can do this by running `grunt dev`.
// This works hand-in-hand with the [livereload](http://livereload.com/) chrome extension.
//
// `grunt dev` manages starting an express server and restarting the server whenever core files change (which
// require a server restart for the changes to take effect) and also manage reloading the browser whenever
// frontend code changes.
//
// Note that the current implementation of watch only works with casper, not other themes.
grunt . registerTask ( 'dev' , 'Dev Mode; watch files and restart server on changes' ,
2015-03-08 20:09:57 +03:00
[ 'bgShell:ember' , 'express:dev' , 'watch' ] ) ;
2014-05-03 17:34:41 +04:00
// ### Release
// Run `grunt release` to create a Ghost release zip file.
// Uses the files specified by `.npmignore` to know what should and should not be included.
// Runs the asset generation tasks for both development and production so that the release can be used in
// either environment, and packages all the files up into a zip.
grunt . registerTask ( 'release' ,
'Release task - creates a final built zip\n' +
2014-07-01 03:26:08 +04:00
' - Do our standard build steps \n' +
2014-05-03 17:34:41 +04:00
' - Copy files to release-folder/#/#{version} directory\n' +
2014-05-05 19:26:19 +04:00
' - Clean out unnecessary files (travis, .git*, etc)\n' +
2014-05-03 17:34:41 +04:00
' - Zip files in release-folder to dist-folder/#{version} directory' ,
2015-11-24 00:20:59 +03:00
function ( ) {
grunt . config . set ( 'copy.release' , {
expand : true ,
// #### Build File Patterns
// A list of files and patterns to include when creating a release zip.
// This is read 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.
src : fs . readFileSync ( '.npmignore' , 'utf8' ) . split ( '\n' ) . filter ( Boolean ) . map ( function ( pattern ) {
return pattern [ 0 ] === '!' ? pattern . substr ( 1 ) : '!' + pattern ;
} ) ,
dest : '<%= paths.releaseBuild %>/'
} ) ;
2016-06-06 15:52:18 +03:00
grunt . task . run ( [ 'init' , 'prod' , 'clean:release' , 'shell:dedupe' , 'shell:prune' , 'shell:shrinkwrap' , 'copy:release' , 'compress:release' ] ) ;
2015-11-24 00:20:59 +03:00
}
) ;
2013-06-25 15:43:15 +04:00
} ;
2014-06-02 18:55:49 +04:00
module . exports = configureGrunt ;