module.exports = function (grunt) { grunt.initConfig({ // ------------------------------------------------------------------------------------------------------------- // Read the package.json file into a variable // ------------------------------------------------------------------------------------------------------------- pkg: function () { grunt.log.verbose.writeln("Loading `package.json` into the pkg variable."); return grunt.file.readJSON("package.json"); }(), bld: function () { grunt.log.verbose.writeln("Loading the `.grunt/build.json` into the bld variable."); return getBuildJSON(); }(), aws: function () { grunt.log.verbose.writeln("Loading the `.aws/config.json` into the aws variable."); return getAWSConfigJSON(); }(), // ------------------------------------------------------------------------------------------------------------- // Ember Execution // ------------------------------------------------------------------------------------------------------------- exec: { ember_test: { cmd: "ember test" }, ember_build: { cmd: "ember build -prod" } }, // ------------------------------------------------------------------------------------------------------------- // Image Processing // ------------------------------------------------------------------------------------------------------------- imagemin: { options: { optimizationLevel: 3 }, optimize: { options: { optimizationLevel: 3 }, files: [{ expand: true, cwd: "public/assets/images", src: "**/*.{png,jpg,gif}", dest: "public/assets/images" }] }, thumbnails: { files: [{ expand: true, cwd: "public/assets/images", src: "**/thumbnails/*.{png,jpg,gif}", dest: "public/assets/images" }] } }, image_resize: { thumbnails: { options: { crop: true, height: 75, width: 75, overwrite: true, concurrency: 4 }, files: [] } }, // ------------------------------------------------------------------------------------------------------------- // CDN Related (AWS S3 with CloudFront) // ------------------------------------------------------------------------------------------------------------- aws_s3: { options: { accessKeyId: getAWSConfigJSON().AccessKeyId, secretAccessKey: getAWSConfigJSON().SecretAccessKey, region: "us-west-2", uploadConcurrency: 5, // 5 simultaneous uploads downloadConcurrency: 5, // 5 simultaneous downloads params: { CacheControl: "max-age=315360000", // 10 years...we version the files anyway Expires: new Date(2038, 0, 1, 0, 0, 0, 0) // 20+ years from today but before than http://en.wikipedia.org/wiki/Year_2038_problem#Solutions } }, upload_assets: { options: { bucket: "luxus-s3", access: "bucket-owner-full-control", // uploaded files are restricted to the bucket owner only; this fixes issues where assets are available from s3, but they should ALWAYS be sourced from CloudFront differential: true // Only uploads the files that have changed }, files: [{ action: "upload", expand: true, cwd: "dist", src: ["assets/**"], dest: "tdc" }] }, upload_staging_index: { options: { bucket: "staging.poderepanico.com", access: "public-read", // uploaded files are restricted to the bucket owner only; this fixes issues where assets are available from s3, but they should ALWAYS be sourced from CloudFront differential: true, params: { CacheControl: "max-age=no-cache", // don't want any caching Expires: new Date(2015, 0, 1, 0, 0, 0, 0) // always expired } }, files: [{ action: "upload", src: "dist/index.html", //dest: "index." + new Date().toISOString() + ".html" dest: "index.html" }, { action: "upload", expand: true, cwd: "dist", // DO NOT upload robots.txt because we need to manage that for each of the staging environments separately src: ["*.{xml,txt}", "!robots.txt"] }] }, upload_production_index: { options: { bucket: "poderepanico.com", access: "public-read", // uploaded files are restricted to the bucket owner only; this fixes issues where assets are available from s3, but they should ALWAYS be sourced from CloudFront differential: false, params: { CacheControl: "max-age=no-cache", // don't want any caching Expires: new Date(2015, 0, 1, 0, 0, 0, 0) // always expired } }, files: [{ action: "upload", src: "dist/index.html", dest: "index." + new Date().toISOString() + ".html" }, { action: "upload", expand: true, cwd: "dist", // DO NOT upload robots.txt because we need to manage that for each of the staging environments separately src: ["*.{xml,txt}", "!robots.txt"] }] }, // USE WITH CAUTION // example: grunt aws_s3:delete_assets --deleteDest=1.0.0 delete_assets: { options: { bucket: "luxus-s3" }, files: [{ action: "delete", dest: "tdc" }] } // TODO: could create a delete index } }); // dynamically load the grunt tasks require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks); // has the optimize flag been passed, this is good for running deploy and forcing an optimize var isOptimize = grunt.option("optimize"); // grab the environment, either --staging or --production or --prod var isProduction = grunt.option("prod") || grunt.option("production"); grunt.log.ok(isProduction ? "Working in the PRODUCTION environment." : "Working in the staging environment."); // ------------------------------------------------------------------------------------------------------------- // These tasks marked with an * should instead be specified from within this Gruntfile wherever possible. // ------------------------------------------------------------------------------------------------------------- grunt.registerTask("setBuildParameter", "Sets the specified build parameter in the .grunt/build.json file (e.g. grunt setBuildParameter:someName:someValue). *", function (name, value) { if (arguments.length != 2) { grunt.fail.fatal("`setBuildParameter` expects two arguments: e.g. `grunt setBuildParameter:enableCompression:true`."); } var build = grunt.config("bld"); var was = build[name]; build[name] = value; writeBuildJSON(build); grunt.log.ok(name + " was '" + was + "' and has now been set to '" + build[name] + "'."); }); // ------------------------------------------------------------------------------------------------------------- // These tasks deploy the website // ------------------------------------------------------------------------------------------------------------- grunt.registerTask("default", ["deploy"]); grunt.registerTask("deploy", "Builds the app and deploys it to AWS S3.", function () { if (!isAWSConfigPresent()) { grunt.fail.fatal("AWS credentials are required to upload the assets to the Luxus CDN. Please run `grunt registerAWSCredentials:yourAccessKeyIdValue:yourSecretAccesssKeyValue` to create the .aws/config.json file."); } var tasks = isOptimize ? ["optimize"] : []; grunt.task.run( tasks.concat([ "exec:ember_test", "exec:ember_build", "aws_s3:upload_assets", isProduction ? "aws_s3:upload_production_index" : "aws_s3:upload_staging_index" ])); }); grunt.registerTask("optimize", "Optimizes (in-place) all the images found in the public folder.", function () { grunt.task.run([ "imagemin:optimize" ]); }); grunt.registerTask("thumbnails", "Re-sizes and minifies prescribed directories of images down to thumbnail images.", function () { grunt.task.run([ "image_resize:thumbnails", "imagemin:thumbnails" ]); }); // ------------------------------------------------------------------------------------------------------------- // Registered Tasks for persisting build configuration changes. // ------------------------------------------------------------------------------------------------------------- grunt.registerTask("registerAWSCredentials", "Sets your AWS credentials in the local .aws/config.json file.", function (accessKeyId, secretAccessKey) { if (arguments.length != 2) { grunt.fail.fatal("You must provide two arguments to task " + this.name + ". e.g. grunt " + this.name + ":yourAccessKeyIdValue:yourSecretAccesssKeyValue") } var config = getAWSConfigJSON(); config.AccessKeyId = accessKeyId; config.SecretAccessKey = secretAccessKey; writeAWSConfigJSON(config); grunt.log.ok("Your AWS credentials for access key " + config.AccessKeyId + " have been stored in .aws/config.json."); }); grunt.registerTask("pkgSettings", "Print the `package.json` settings to the grunt.log.", function () { grunt.log.writeln("package.json=" + JSON.stringify(grunt.config("pkg"))); }); grunt.registerTask("bldSettings", "Print the `.grunt/build.json` settings to the grunt.log.", function () { grunt.log.writeln(".grunt/build.json=" + JSON.stringify(grunt.config("bld"))); }); // ------------------------------------------------------------------------------------------------------------- // .grunt/build.json helpers // ------------------------------------------------------------------------------------------------------------- function getBuildJSON() { try { return grunt.file.readJSON(".grunt/build.json"); } catch (e) { grunt.verbose.writeln("The .grunt/build.json does not exist. Creating the default and returning it."); var bld = {}; writeBuildJSON(bld); return bld; } } function writeBuildJSON(json) { grunt.file.write(".grunt/build.json", JSON.stringify(json)); } // ------------------------------------------------------------------------------------------------------------- // .aws/config.json helpers // ------------------------------------------------------------------------------------------------------------- function isAWSConfigPresent() { return grunt.file.exists(".aws/config.json"); } function getAWSConfigJSON() { try { return grunt.file.readJSON(".aws/config.json"); } catch (e) { grunt.verbose.writeln("The .aws/config.json does not exist. Returning the default config object."); return { AccessKeyId: null, SecretAccessKey: null }; } } function writeAWSConfigJSON(json) { grunt.file.write(".aws/config.json", JSON.stringify(json)); } };