Skip to content

Instantly share code, notes, and snippets.

@osuka
Created May 30, 2013 12:55
Show Gist options
  • Save osuka/5677620 to your computer and use it in GitHub Desktop.
Save osuka/5677620 to your computer and use it in GitHub Desktop.
Sample gruntfile for Firefox OS projects, including testing, code coverage. Requires npm, bower and ruby (for compass)
/*
Needs a configuration file with paths
for Gaia and standalone simulator
File contents for grunt.local.json (config):
{
"b2g_bin_path": "/Applications/B2G.app/Contents/MacOS",
"gaia_homepath": "/Users/OAmat/Documents/code/gaia"
}
File contents for packages.json (node):
{
"name": "webapp",
"version": "0.0.0",
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-compass": "~0.2.0",
"grunt-contrib-jshint": "~0.4.3",
"grunt-contrib-connect": "~0.3.0",
"grunt-contrib-clean": "~0.4.1",
"grunt-contrib-imagemin": "~0.1.4",
"grunt-regarde": "~0.1.1",
"grunt-requirejs": "~0.3.5",
"grunt-mocha": "~0.3.1",
"grunt-open": "~0.2.0",
"grunt-shell": "~0.2.2",
"grunt-coverjs": "git://github.com/hull/grunt-coverjs.git",
"matchdep": "~0.1.2",
"grunt-contrib-handlebars": "~0.5.8"
},
"engines": {
"node": ">=0.8.0"
}
}
File contents for component.json: (bower)
{
"name": "webapp",
"version": "0.0.0",
"dependencies": {
"requirejs": "~2.1.5",
"underscore": "~1.4.4",
"zepto": "~1.0.0",
"backbone": "~1.0.0",
"handlebars.js": "1.0.0-rc.3",
"PhoneNumber.js": "~0.5.0"
},
"devDependencies": {}
}
First time run: REQUIRES node >= 0.10.1, ruby >= 1.9.2
npm install
gem install bundler && bundle install
bower install
*/
/*jshint es5: true */
'use strict';
var mountFolder = function (connect, dir) {
return connect.static(require('path').resolve(dir));
};
module.exports = function (grunt) {
// load all grunt tasks
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
grunt.initConfig({
appPaths: {
app: 'app',
dist: 'dist'
},
/* Local Project configuration (per developer) */
properties: grunt.file.isFile('grunt.local.json') ?
grunt.file.readJSON('grunt.local.json') : {},
simulator: {
b2gbin: '<%= properties.b2g_bin_path %>/b2g-bin'
},
gaia: {
home: '<%= properties.gaia_homepath %>'
},
/* Project tasks */
watch: {
compass: {
files: ['<%= appPaths.app %>/styles/{,*/}*.{scss,sass}'],
tasks: ['compass']
},
handlebars: {
files: [
'<%= appPaths.app %>/scripts/templates/*.hbs'
],
tasks: 'handlebars:compile'
},
coverage: {
files: [
'<%= appPaths.app %>/scripts/{,*/}*.js',
'<%= .tmp/scripts/*.js',
],
tasks: 'cover:compile'
}
},
connect: {
options: {
port: 9000,
// change this to '0.0.0.0' to access the server from outside
hostname: 'localhost'
},
server: {
options: {
middleware: function (connect) {
return [
mountFolder(connect, '.tmp'),
mountFolder(connect, 'app')
];
}
}
},
test: {
options: {
port: 9002,
middleware: function (connect) {
return [
mountFolder(connect, '.tmp'),
mountFolder(connect, 'test')
];
}
}
},
dist: {
options: {
middleware: function (connect) {
return [
mountFolder(connect, 'dist')
];
}
}
}
},
open: {
server: {
path: 'http://localhost:<%= connect.options.port %>'
},
test: {
path: 'http://localhost:<%= connect.test.options.port %>'
}
},
clean: {
dist: ['.tmp', '<%= appPaths.dist %>/*'],
server: '.tmp'
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'Gruntfile.js',
'<%= appPaths.app %>/scripts/{,*/}*.js',
'!<%= appPaths.app %>/scripts/vendor/*',
'test/spec/{,*/}*.js'
]
},
mocha: {
all: {
options: {
reporter: 'Spec',
run: false,
urls: ['http://localhost:<%= connect.test.options.port %>/index.html']
}
}
},
compass: {
options: {
sassDir: '<%= appPaths.app %>/styles',
cssDir: '.tmp/styles',
imagesDir: '<%= appPaths.app %>/images',
javascriptsDir: '<%= appPaths.app %>/scripts',
fontsDir: '<%= appPaths.app %>/styles/fonts',
importPath: 'app/components',
relativeAssets: true
},
dist: {},
server: {
options: {
debugInfo: true
}
}
},
imagemin: {
dist: {
files: [{
expand: true,
cwd: '<%= appPaths.app %>/images',
src: '**/{,*/}*.{png,jpg,jpeg}',
dest: '<%= appPaths.dist %>/images'
}]
}
},
copy: {
build: {
files: [{
expand: true,
dot: true,
cwd: '<%= appPaths.app %>',
dest: '<%= appPaths.dist %>',
src: [
/** COMPONENTS - Extracted from main.js config **/
'components/requirejs/require.js',
'components/zepto/zepto.min.js',
'components/underscore/underscore-min.js',
'components/backbone/backbone-min.js',
'components/handlebars.js/dist/handlebars.runtime.js',
/** END OF COMPONENTS **/
'icons/**/*.{png,jpg}',
'scripts/**/*.js',
'*.{ico,txt}',
'*.html',
'manifest.webapp'
]
}, {
expand: true,
cwd: '.tmp',
dest: '<%= appPaths.dist %>',
src: [
'styles/**/*.css',
'scripts/**/*.js',
]
},
{
expand: true,
cwd: '<%= appPaths.app %>/images',
src: '**/*.{png,jpg,jpeg}',
dest: '<%= appPaths.dist %>/images'
}
]
}
},
bower: {
all: {
rjsConfig: '<%= appPaths.app %>/scripts/main.js'
}
},
handlebars: {
compile: {
files: {
'.tmp/scripts/templates.js': [
'<%= appPaths.app %>/scripts/templates/*.hbs'
]
},
options: {
namespace: 'Handlebars.templates',
amd: true,
processName: function (filename) {
// funky name processing here
return filename
.replace(/^app\/scripts\/templates\//, '')
.replace(/\.hbs$/, '');
}
}
}
},
cover: {
compile: {
files: {
'test/cov/*.js': ['app/scripts/**/*.js'],
'test/cov/templates.js': ['.tmp/scripts/templates.js']
}
}
},
shell: {
ensureLinkedInGaia: {
command: 'if [ ! -d <%= gaia.home %>/apps/myappname ]; then' +
' ln -s "$(pwd)/dist" "<%= gaia.home %>/apps/myappname";' +
'fi',
options: {
stdout: true
}
},
launchB2G: {
command: '<%= simulator.b2gbin %> -profile ' +
'"<%= gaia.home %>/profile" ' +
'--screen=320x480@72 -jsconsole'
},
killB2G: {
command: 'killall b2g-bin'
},
makeGaiaDebug: {
command: 'DEBUG=1 NOFTU=1 make',
options: {
stdout: true,
execOptions: {
cwd: '<%= gaia.home %>'
}
}
},
makeGaia: {
command: 'NOFTU=1 make',
options: {
stdout: true,
execOptions: {
cwd: '<%= gaia.home %>'
}
}
},
ensureDistributionFolderExists: {
command: 'if [ ! -d "<%= gaia.home %>/distribution" ]; then ' +
'mkdir "<%= gaia.home %>/distribution";' +
'fi',
options: {
stdout: true
}
},
generateContacts: {
command: 'if [ -f "$(pwd)/contacts.json" ]; then ' +
'cp "$(pwd)/contacts.json" ' +
'"<%= gaia.home %>/distribution/contacts.json";' +
'fi',
options: {
stdout: true
}
},
cleanContacts: {
command: 'rm "<%= gaia.home %>/distribution/contacts.json";',
options: {
stdout: true
}
},
pushAllToDevice: {
command: 'NOFTU=1 make install-gaia && adb reboot',
options: {
stdout: true,
execOptions: {
cwd: '<%= gaia.home %>'
}
}
},
pushToDevice: {
command: 'NOFTU=1 BUILD_APP_NAME=myappname make install-gaia',
options: {
stdout: true,
execOptions: {
cwd: '<%= gaia.home %>'
}
}
},
resetDevice: {
command: 'NOFTU=1 make reset-gaia',
options: {
stdout: true,
execOptions: {
cwd: '<%= gaia.home %>'
}
}
},
removeGaiaProfile: {
command: 'rm -r profile',
options: {
stdout: true,
failOnError: false,
execOptions: {
cwd: '<%= gaia.home %>'
}
}
},
enableRemoteDebugging: {
command: './enable-remote-debugging.sh "<%= gaia.home %>"',
options: {
stdout: true,
failOnError: true,
}
}
},
});
grunt.renameTask('regarde', 'watch');
grunt.registerTask('checkProperties', function () {
return grunt.config.requires('properties.gaia_homepath');
});
grunt.registerTask('server', function (target) {
if (target === 'dist') {
grunt.task.run([
'build',
'open:server',
'connect:dist:keepalive'
]);
} else if (target === 'test') {
grunt.task.run([
'clean:server',
'handlebars:compile',
'cover:compile',
'compass:server',
'connect:test',
'open:test',
'watch'
]);
} else {
grunt.task.run([
'jshint',
'clean:server',
'handlebars:compile',
'compass:server',
'connect:server',
//'open:server',
'watch'
]);
}
});
grunt.registerTask('test', [
'jshint',
'clean:server',
'handlebars:compile',
'compass',
'connect:test',
'mocha'
]);
grunt.registerTask('build', [
'clean:dist',
'handlebars:compile',
'compass:dist',
// 'imagemin',
'copy:build',
]);
grunt.registerTask('dist', [
'clean:dist',
'handlebars:compile',
'compass:dist',
// 'imagemin',
'copy:build',
]);
grunt.registerTask('simulate', 'Launch with B2G simulator', function () {
grunt.log.write('Using ' + grunt.config.get('simulator.b2gbin'));
grunt.task.run([
'checkProperties',
'shell:removeGaiaProfile',
'build',
'shell:ensureLinkedInGaia',
'shell:killB2G',
'shell:ensureDistributionFolderExists',
'shell:generateContacts',
'shell:makeGaia',
'shell:enableRemoteDebugging',
'shell:launchB2G'
]);
});
grunt.registerTask('clean-contacts', 'Clean generated contacts', function () {
if (grunt.file.isFile('contacts.json')) {
grunt.file.copy('contacts.json', 'contacts.json.sample');
grunt.file.delete('contacts.json');
}
grunt.task.run(['shell:cleanContacts']);
});
grunt.registerTask('push-clean', 'Install in device ' +
'(rebooting and cleaning it)', function () {
grunt.task.run([
'checkProperties',
'shell:removeGaiaProfile',
'build',
'shell:ensureLinkedInGaia',
'shell:ensureDistributionFolderExists',
'shell:generateContacts',
'shell:enableRemoteDebugging',
'shell:pushAllToDevice'
]);
});
grunt.registerTask('push-hard', 'Install and reset the device ' +
' (use when you are getting desperate)', function () {
grunt.task.run([
'checkProperties',
'shell:removeGaiaProfile',
'build',
'shell:ensureLinkedInGaia',
'shell:ensureDistributionFolderExists',
'shell:generateContacts',
'shell:enableRemoteDebugging',
'shell:resetDevice',
]);
});
grunt.registerTask('push', 'Install in device ' +
'(only the this application)', function () {
grunt.task.run([
'checkProperties',
'build',
'shell:ensureLinkedInGaia',
'shell:ensureDistributionFolderExists',
'shell:generateContacts',
'shell:enableRemoteDebugging',
'shell:pushToDevice'
]);
});
grunt.registerTask('default', [
'jshint',
'test',
'build'
]);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment