/** Gruntfile - control build and deploy tasks */ var _ = require('underscore'); module.exports = function(grunt) { // TASKS // default task grunt.registerTask('default', ['compile']); // development grunt.registerTask('compile', "Lint all sources and compile Coffee and LESS sources.", ['path-check:svn' ,'svn-ignore:compile' ,'jshint:lint-sources' ,'htmlhint:lint' ,'coffeelint:lint' ,'jsonlint:lint' //,'tv4:lint' ,'less:compile' ,'usebanner:generated-css' ,'coffee:compile' ,'usebanner:generated-js' ,'jshint:lint-generated' //,'amd-check' // too smart about plugins, r.js can't find them ]); grunt.registerTask('run', "Compile and start the server locally with foreman.", ['path-check:foreman' ,'compile' ,'foreman:run-src']); grunt.registerTask('build', "Compile and make a Dojo build of the client (compress sources).", ['compile' ,'clean:build' ,'copy:build' ,'dojo:build' ]); grunt.registerTask('run-build', "Make a build and start server from build locally with foreman.", ['path-check:foreman' ,'build' ,'foreman:run-build']); grunt.registerTask('deploy', "Make a build and deploy it to Heroku.", ['path-check:svn' ,'path-check:git' ,'svninfo' ,'build' ,'git_deploy:deploy-build']); grunt.registerTask('deploy-src', "Deploy sources to Heroku (FOR DEBUGGING ONLY).", ['path-check:svn' ,'path-check:git' ,'svninfo' ,'compile' ,'git_deploy:deploy-src']); // database management grunt.registerTask('db-backup-cloud-to-dated-local', "Backup the Heroku database to a timestamped local database.", ['path-check:heroku' ,'heroku-config' ,'http:db-backup-cloud-to-dated-local']); grunt.registerTask('db-pull-cloud-to-local', "Synchronize local database with the Heroku database (pull changes).", ['path-check:heroku' ,'heroku-config' ,'http:db-pull-cloud-to-local']); grunt.registerTask('db-push-local-to-cloud', "Synchronize the Heroku database with the local database (push changes).", ['path-check:heroku' ,'heroku-config' ,'http:db-push-local-to-cloud']); grunt.registerTask('#', "---\nTASKS BELOW ARE INTERNAL AND SHOULD NOT USUALLY BE CALLED FROM THE COMMAND-LINE\n---", []); // FILES AND FOLDERS var srcDir = 'src/'; var buildDir = 'build/'; var coffeeMap = grunt.file.expandMapping( [ 'client/qed-client/**/*.coffee', 'server/**/*.coffee' ], srcDir, { cwd: srcDir, ext: '.js' }); var lessMap = grunt.file.expandMapping( [ 'client/qed-client/css/qed.less' ], srcDir, { cwd: srcDir, ext: '.css' }); var mode = process.env.QED_ENV || 'dev'; var dbNames = { dev: 'qed-dev', production: 'qed' }; if ( !( mode in dbNames ) ) { throw new Error("Unknown mode "+mode+" specified."); } // TASK CONFIG grunt.initConfig({ localDbURL: "http://localhost:5984", dbName: dbNames[mode], timestamp: "<%= grunt.template.today(\"UTC:yyyymmdd't'HHMMss'z'\") %>", 'amd-check': { files: [ srcDir+'client/qed-client/**/*.js' ] }, clean: { build: { src: [buildDir] } }, coffee: { options: { bare: true }, compile: { files: coffeeMap } }, coffeelint: { lint: { options: require('./'+srcDir+'.coffeelint.json'), files: coffeeMap } }, copy: { build: { files: [{ expand: true, cwd: srcDir, src: ['**', '!client/*/**' ], dest: buildDir }]} }, dojo: { build: { options: { dojo: srcDir+'client/dojo/dojo.js', profile: srcDir+'client/client.profile.js', releaseDir: '../../'+buildDir }} }, foreman: { 'run-src': { options: { cwd: srcDir } }, 'run-build': { options: { cwd: buildDir } } }, git_deploy: { 'deploy-build': { options: { url: 'git@heroku.com:quod-erat.git', branch: 'master', message: "Build deployment of revision <%= svninfo.rev %> on <%= grunt.template.today() %>." }, src: buildDir }, 'deploy-src': { options: { url: 'git@heroku.com:quod-erat.git', branch: 'master', message: "Source deployment of revision <%= svninfo.rev %> on <%= grunt.template.today() %>." }, src: srcDir } }, 'heroku-config': { options: { app: 'quod-erat', keys: ['CLOUDANT_URL'] } }, htmlhint: { options: { htmlhintrc: srcDir+".htmlhintrc" }, lint: { files: [{ expand: true, cwd: srcDir, src: ['client/*.html', 'client/qed-client/**/*.html'] }]} }, http: { 'db-backup-cloud-to-dated-local': { options: { url: "<%= localDbURL %>/_replicate", method: 'POST', body: { source: "<%= herokuConfig.CLOUDANT_URL %>/<%= dbName %>", target: "<%= dbName %>-<%= timestamp %>", create_target: true }, json: true } }, 'db-pull-cloud-to-local': { options: { url: "<%= localDbURL %>/_replicate", method: 'POST', body: { source: "<%= herokuConfig.CLOUDANT_URL %>/<%= dbName %>", target: "<%= dbName %>", create_target: true }, json: true } }, 'db-push-local-to-cloud': { options: { url: "<%= localDbURL %>/_replicate", method: 'POST', body: { source: "<%= dbName %>", target: "<%= herokuConfig.CLOUDANT_URL %>/<%= dbName %>", create_target: true }, json: true } } }, jshint: { 'lint-sources': { options: { jshintrc: srcDir+".jshintrc" }, files: { src: [ srcDir+'client/qed-client/**/*.js', srcDir+'server/**/*.js' ].concat(exclude(dest(coffeeMap))) } }, 'lint-generated': { options: { jshintrc: srcDir+".jshintrc-generated" }, files: { src: dest(coffeeMap) } } }, jsonlint: { 'lint': { files: { src: [ srcDir+'client/qed-client/**/*.json', srcDir+'server/**/*.json' ] } } }, less: { options: { strictImports: false, dumpLineNumbers: "all" }, compile: { files: lessMap } }, 'path-check': { 'heroku': { src: ['heroku'] }, 'foreman': { src: ['foreman'] }, 'svn': { src: ['svn'] }, 'git': { src: ['git'] } }, requirejs: { basePath: srcDir+'client/', packages: [ { name: "dojo", location: "dojo" }, { name: "dijit", location: "dijit" }, { name: "dojox", location: "dojox" }, { name: "qed-client", location: "qed-client" } ] }, 'svn-ignore': { compile: { files: { src: dest(coffeeMap).concat(dest(lessMap)) } } }, 'svn-ignore-clean': { clean: { files: [{ expand: true, cwd: srcDir, src: ['**', '!node_modules/**', '!client/dojo/**', '!client/dijit/**', '!client/dojox/**', '!client/util/**'], filter: 'isDirectory' }] } }, tv4: { lint: { options: { root: grunt.file.readJSON('json-schema-draft-04.json'), multi: true }, src: [srcDir+'server/config/couchdb-schema.json'] } }, usebanner: { 'generated-css': { options: { position: 'top', banner: '/* This CSS file is generated. All edits will be lost on recompile. */'}, files: { src: dest(lessMap) }}, 'generated-js': { options: { position: 'top', banner: '/* This JS file is generated, All edits will be lost on recompile. */'}, files: { src: dest(coffeeMap) }} } }); // LOAD TASKS grunt.loadNpmTasks('grunt-amd-check'); grunt.loadNpmTasks('grunt-banner'); grunt.loadNpmTasks('grunt-coffeelint'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-coffee'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-dojo'); grunt.loadNpmTasks('grunt-git-deploy'); grunt.loadNpmTasks('grunt-htmlhint'); grunt.loadNpmTasks('grunt-http'); grunt.loadNpmTasks('grunt-jsonlint'); grunt.loadNpmTasks('grunt-path-check'); grunt.loadNpmTasks('grunt-svninfo'); grunt.loadNpmTasks('grunt-tv4'); grunt.loadTasks('./grunt-tasks'); // UTIL FUNCTIONS function dest(files) { return _.chain(files) .map(function(item){ return item.dest; }) .flatten() .value(); } function exclude(dest) { return _.map(dest, function(dest){ return '!'+dest; }); } };