Skip to content

Instantly share code, notes, and snippets.

@nadnoslen
Last active February 7, 2016 00:53
Show Gist options
  • Save nadnoslen/ab3b638088df3ee82d75 to your computer and use it in GitHub Desktop.
Save nadnoslen/ab3b638088df3ee82d75 to your computer and use it in GitHub Desktop.
Ember Hosted On AWS S3
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
# dependencies
/node_modules
/bower_components
# misc
/.sass-cache
/connect.lock
/coverage/*
/libpeerconnection.log
npm-debug.log
testem.log
# Jetbrains
/.idea
# Grunt
/.grunt
# AWS
/.aws
/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function (defaults) {
var app = new EmberApp(defaults, {
fingerprint: {
extensions: ['js', 'css', 'png', 'jpg', 'gif', 'svg', 'pdf'],
prepend: "//cdn.example.com/foo-folder/"
},
outputPaths: {
app: {
css: {
'app': '/assets/foo.css'
}
}
}
});
// Use `app.import` to add additional libraries to the generated
// output files.
//
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
// object's keys should be the environment name and the values
// should be the asset to use in that environment.
//
// If the library that you are including contains AMD or ES6
// modules that you would like to import into your application
// please specify an object with the list of modules as keys
// along with the exports of each module as its value.
app.import(app.bowerDirectory + "/bootstrap/fonts/glyphicons-halflings-regular.eot", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/bootstrap/fonts/glyphicons-halflings-regular.svg", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/bootstrap/fonts/glyphicons-halflings-regular.ttf", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/bootstrap/fonts/glyphicons-halflings-regular.woff", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/bootstrap/fonts/glyphicons-halflings-regular.woff2", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + '/bootstrap/dist/js/bootstrap.js');
app.import(app.bowerDirectory + "/font-awesome/fonts/FontAwesome.otf", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/font-awesome/fonts/fontawesome-webfont.eot", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/font-awesome/fonts/fontawesome-webfont.svg", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/font-awesome/fonts/fontawesome-webfont.ttf", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/font-awesome/fonts/fontawesome-webfont.woff", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + "/font-awesome/fonts/fontawesome-webfont.woff2", {destDir: "assets/fonts"});
app.import(app.bowerDirectory + '/keyevent/src/keyevent.js');
app.import(app.bowerDirectory + '/moment/moment.js');
app.import(app.bowerDirectory + '/numbro/dist/numbro.min.js');
return app.toTree();
};
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"
}
},
// -------------------------------------------------------------------------------------------------------------
// 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: "foo-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: "foo-folder"
}]
},l
upload_staging_index: {
options: {
bucket: "staging.example.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.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: "example.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: "foo-s3"
},
files: [{
action: "delete",
dest: "foo-folder"
}]
}
}
});
// dynamically load the grunt tasks
require("matchdep").filterDev("grunt-*").forEach(grunt.loadNpmTasks);
// 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 S3. 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"
]));
});
// -------------------------------------------------------------------------------------------------------------
// 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));
}
};
{
"name": "foo",
"version": "0.0.0",
"description": "Small description for foo goes here",
"private": true,
"directories": {
"doc": "doc",
"test": "tests"
},
"scripts": {
"build": "ember build",
"start": "ember server",
"test": "ember test"
},
"repository": "",
"engines": {
"node": ">= 0.10.0"
},
"author": "",
"license": "MIT",
"devDependencies": {
"broccoli-asset-rev": "^2.2.0",
"ember-cli": "1.13.13",
"ember-cli-app-version": "^1.0.0",
"ember-cli-babel": "^5.1.5",
"ember-cli-content-security-policy": "0.4.0",
"ember-cli-dependency-checker": "^1.1.0",
"ember-cli-htmlbars": "^1.0.1",
"ember-cli-htmlbars-inline-precompile": "^0.3.1",
"ember-cli-ic-ajax": "0.2.4",
"ember-cli-inject-live-reload": "^1.3.1",
"ember-cli-less": "^1.5.3",
"ember-cli-qunit": "^1.0.4",
"ember-cli-release": "0.2.8",
"ember-cli-sri": "^1.2.0",
"ember-cli-uglify": "^1.2.0",
"ember-data": "2.2.0",
"ember-disable-proxy-controllers": "^1.0.1",
"ember-export-application-global": "^1.0.4",
"grunt": "^0.4.5",
"grunt-aws-s3": "^0.14.4",
"grunt-contrib-imagemin": "^1.0.0",
"grunt-exec": "^0.4.6",
"grunt-image-resize": "^1.0.0",
"matchdep": "^1.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment