Skip to content

Instantly share code, notes, and snippets.

@Anubisss
Created September 18, 2015 10:26
Show Gist options
  • Save Anubisss/54cd885b06f0053ce429 to your computer and use it in GitHub Desktop.
Save Anubisss/54cd885b06f0053ce429 to your computer and use it in GitHub Desktop.
Gruntfile which deploys to AWS S3 and Azure Storage. It converts the LESS code to CSS, minifies it, change values (config) in the JavaScript Angular app and in HTML files also, concatenate and minifies the JavaScript files, minifies the HTML files, prepends the CDN URL into the HTML files.
'use strict';
module.exports = function(grunt) {
var appConfig = {
// The app directory: location of the client-side code.
app: 'app',
// The style directory: location of CSS and LESS files, the code of the style.
style: 'style',
// The build directory: location of the production ready (deployable) app.
build: 'build',
// The js directory: location of JavaScript files which made by us or
// third party libs which not on cdnjs.
js: 'scripts',
// The images directory: location of image files.
images: 'images'
};
// Location of the production ready CSS styles.
appConfig.buildStyle = appConfig.build + '/' + appConfig.style;
// Location of the production ready and minified and concatenated/merged JavaScript files.
appConfig.buildJs = appConfig.build + '/' + appConfig.js;
// Location of the production ready image files.
appConfig.buildImages = appConfig.build + '/' + appConfig.images;
// Configs which can be modified via environment variables.
var PORT = process.env.PORT || 9000;
var LIVERELOAD_PORT = process.env.LIVERELOAD_PORT || 35729;
var CDN_URL = process.env.CDN_URL || 'http://defaultcdn.com/';
var CDN_DISABLED = process.env.CDN_DISABLED || '0';
var API_URL = process.env.API_URL;
var GOOGLE_ANALYTICS_ID = process.env.GOOGLE_ANALYTICS_ID;
var MIXPANEL_ID = process.env.MIXPANEL_ID;
var DEPLOY_AWS_S3_ACCESS_KEY_ID = process.env.DEPLOY_AWS_S3_ACCESS_KEY_ID;
var DEPLOY_AWS_S3_SECRET_ACCESS_KEY = process.env.DEPLOY_AWS_S3_SECRET_ACCESS_KEY;
var DEPLOY_AWS_S3_REGION = process.env.DEPLOY_AWS_S3_REGION || 'eu-central-1';
var DEPLOY_AWS_S3_BUCKET_NAME = process.env.DEPLOY_AWS_S3_BUCKET_NAME;
var AZURE_STORAGE_ACCOUNT = process.env.AZURE_STORAGE_ACCOUNT;
var AZURE_STORAGE_ACCESS_KEY = process.env.AZURE_STORAGE_ACCESS_KEY;
grunt.initConfig({
project: appConfig,
// Watches files for changes and runs tasks based on the changed files.
watch: {
// JavaScript files.
js: {
files: ['<%= project.app %>/{,*/}*.js'],
tasks: ['newer:jshint:angularApp'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
// HTML files.
html: {
files: ['<%= project.app %>/{,*/}*.html'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
// CSS files.
css: {
files: ['<%= project.style %>/*.css'],
tasks: ['newer:csslint:style'],
options: {
livereload: '<%= connect.options.livereload %>'
}
},
// LESS files.
less: {
files: ['<%= project.style %>/src/less/{,*/}*.less'],
tasks: ['less:style', 'newer:csslint:style', 'postcss'],
options: {
spawn: false,
livereload: '<%= connect.options.livereload %>'
}
}
},
// A web server which serves the client-side code:
// HTML, CSS and JavaScript files.
connect: {
options: {
port: PORT,
hostname: 'localhost',
livereload: LIVERELOAD_PORT
},
// Development version.
livereload: {
options: {
open: true,
middleware: function(connect) {
return [
// Maps the style (CSS) files.
connect().use('/' + appConfig.style, connect.static(appConfig.style)),
// Maps the third-party JavaScript files and the other ones which made by us.
connect().use('/' + appConfig.js, connect.static(appConfig.js)),
// The app directory, root of the web server.
connect.static(appConfig.app)
];
}
}
},
// Build/production/deployable version.
build: {
options: {
base: '<%= project.build %>',
open: true,
}
}
},
// JSHint is a tool to detect errors and potential problems in the code.
jshint: {
// JSHint for the Angular app
angularApp: {
src: [
'<%= project.app %>/{,*/}*.js'
],
options: {
jshintrc: '.jshintrc', // config for the Angular app
reporter: require('jshint-stylish') // nice reporter for JSHint
}
}
},
// Automated linting for CSS files.
csslint: {
// CSSLint for the style file.
style: {
options: {
csslintrc: '.csslintrc'
},
src: ['<%= project.style %>/*.css']
},
},
// Compiles a LESS file to a CSS file.
less: {
// App's style files.
style: {
// Files to compile.
files: {
// destination source
'<%= project.style %>/style.css': "<%= project.style %>/src/less/main.less"
}
}
},
// CSS transforming tool, a postprocessor.
postcss: {
options: {
processors: [
// Plugin which adds vendor prefixes to CSS rules.
require('autoprefixer-core')(),
]
},
dist: {
src: '<%= project.style %>/*.css'
}
},
// Minifies (compress) CSS files.
cssmin: {
// The app's style file.
buildStyle: {
files: [{
expand: true,
cwd: '<%= project.buildStyle %>',
src: ['*.css', '!*.min.css'],
dest: '<%= project.buildStyle %>',
ext: '.min.css'
}]
}
},
// JavaScript minifier.
uglify: {
// Minifies and concatenates the app's JavaScript files.
buildAppJs: {
files: {
'<%= project.buildJs %>/app.min.js': ['<%= project.build %>/*.js',
'<%= project.build %>/controllers/*.js',
'<%= project.build %>/filters/*.js',
'<%= project.build %>/services/*.js']
}
},
// Minifies our JavaScript codes which are not part of the Angular app.
buildJs: {
files: [{
expand: true,
cwd: '<%= project.buildJs %>',
src: ['*.js', '!*.min.js'],
dest: '<%= project.buildJs %>',
ext: '.min.js'
}]
}
},
// Minifies and removes comments from HTML files.
htmlmin: {
// Minifies the app's index file.
buildHtmlIndex: {
options: {
removeComments: true,
collapseWhitespace: true
},
files: [{
expand: true,
cwd: '<%= project.build %>',
src: ['*.html'],
dest: '<%= project.build %>'
}]
},
// Minifies the app's view files.
buildHtmlViews: {
options: {
removeComments: true,
collapseWhitespace: true
},
files: [{
expand: true,
cwd: '<%= project.build %>/views',
src: ['*.html'],
dest: '<%= project.build %>/views'
}]
}
},
// Process HTML files at build time to modify them
// depending on the release environment.
processhtml: {
// Angular app's index.html file.
buildHtmlIndex: {
options: {
// Sets variables for templates.
data: {
// Sets google analytics id.
googleAnalyticsId: GOOGLE_ANALYTICS_ID,
// Sets mixpanel id.
mixpanelId: MIXPANEL_ID
}
},
files: {
'<%= project.build %>/index.html': ['<%= project.build %>/index.html']
}
},
},
// Prepends CDN URL to the JavaScripts and CSS files.
cdn: {
options: {
// URL of the CDN, foobar.css -> CDN_URL/foobar.css
cdn: CDN_URL,
// Is CDN preprending is enabled?
flatten: CDN_DISABLED === '0' ? true : false
},
// The app's index.html file.
buildHtmlIndex: {
files: [{
cwd: '<%= project.build %>',
dest: '<%= project.build %>',
src: ['index.html']
}]
}
},
// Replaces strings on files by using string or regex patterns.
'string-replace': {
// Modifies the Angular app's config file.
buildConfig: {
files: {
'<%= project.build %>/config.js': '<%= project.build %>/config.js'
},
options: {
replacements: [
// API_URL
{
pattern: /(API_URL: )'([a-z0-9.:/]+)'/,
replacement: function (match, p1, p2) {
var apiUrl = API_URL || p2;
return p1 + '\'' + apiUrl + '\'';
}
}
]
}
},
// Modifies the app's index.html file.
buildIndexHtml: {
files: {
'<%= project.build %>/index.html': '<%= project.build %>/index.html'
},
options: {
replacements: [
// Prepends the CDN URL in og:image meta tag.
{
pattern: /(<meta property="og:image" content=")([a-zA-Z0-9\/\.-_]+">)/,
replacement: function (match, matchGroup1, matchGroup2) {
var cdnUrl = '';
// CDN is enabled
if (CDN_URL && CDN_DISABLED === '0') {
cdnUrl = CDN_URL;
}
return matchGroup1 + cdnUrl + matchGroup2;
}
},
// Prepends the CDN URL in twitter:image meta tag.
{
pattern: /(<meta name="twitter:image" content=")([a-zA-Z0-9\/\.-_]+">)/,
replacement: function (match, matchGroup1, matchGroup2) {
var cdnUrl = '';
// CDN is enabled
if (CDN_URL && CDN_DISABLED === '0') {
cdnUrl = CDN_URL;
}
return matchGroup1 + cdnUrl + matchGroup2;
}
}
]
}
}
},
// Deletes a directory.
clean: {
// Deletes/cleans the app's build directory.
buildFull: {
src: ['<%= project.build %>']
},
// Deletes the app's style files except the minified versions (.min).
buildCSS: {
src: ['<%= project.buildStyle %>/*.css', '!<%= project.buildStyle %>/*.min.css']
},
// Deletes the not minified JavaScript files and folders.
buildJs: {
src: [
'<%= project.build %>/controllers',
'<%= project.build %>/filters',
'<%= project.build %>/services',
'<%= project.build %>/*.js',
'<%= project.buildJs %>/profile_popover.js'
]
}
},
// Copies files.
copy: {
// Copies files from the Angular app to the app's build directory.
app: {
cwd: '<%= project.app %>',
src: ['**'],
dest: '<%= project.build %>',
expand: true
},
// Copies the CSS style file to the app's build directory.
css: {
cwd: '<%= project.style %>',
src: ['*.css'],
dest: '<%= project.buildStyle %>',
expand: true
},
// Copies the JavaScript files to the build directory.
js: {
cwd: '<%= project.js %>',
src: ['*.js'],
dest: '<%= project.buildJs %>',
expand: true
},
images: {
cwd: '<%= project.images %>',
src: ['**'],
dest: '<%= project.buildImages %>',
expand: true
}
},
// Interacts with AWS S3.
aws_s3: {
// Uploads some files from the build directory to S3.
production: {
options: {
accessKeyId: DEPLOY_AWS_S3_ACCESS_KEY_ID,
secretAccessKey: DEPLOY_AWS_S3_SECRET_ACCESS_KEY,
region: DEPLOY_AWS_S3_REGION,
bucket: DEPLOY_AWS_S3_BUCKET_NAME,
uploadConcurrency: 5,
access: 'public-read',
overwrite: true
},
files: [
{
action: 'upload',
expand: true,
cwd: '<%= project.build %>',
src: ['index.html', 'favicon.ico', 'views/*.html'],
dest: '/',
params: { StorageClass: 'REDUCED_REDUNDANCY' }
}
]
}
},
// Uploads files to Azure BLOB Storage.
'azure-blob': {
options: {
containerDelete: false,
maxNumberOfConcurrentUploads: 5,
containerOptions: { publicAccessLevel: 'blob' }
},
// CSS files.
style: {
options: {
containerName: '<%= project.style %>',
// 1 day
metadata: { cacheControl: 'public, max-age=86400' }
},
files: [{
expand: true,
cwd: '<%= project.buildStyle %>',
src: ['*.css']
}]
},
// Third party JavaScript files.
jsThirdParty: {
options: {
containerName: '<%= project.js %>',
// 1 week
metadata: { cacheControl: 'public, max-age=604800' }
},
files: [{
expand: true,
cwd: '<%= project.buildJs %>',
src: ['*.js', '!app.min.js']
}]
},
// The angular app's JavaScript file.
jsApp: {
options: {
containerName: '<%= project.js %>',
// 1 hour
metadata: { cacheControl: 'public, max-age=3600' }
},
files: [{
expand: true,
cwd: '<%= project.buildJs %>',
src: ['app.min.js']
}]
},
// Images.
images: {
options: {
containerName: '<%= project.images %>',
// 1 week
metadata: { cacheControl: 'public, max-age=1209600' }
},
files: [{
expand: true,
cwd: '<%= project.buildImages %>',
src: ['**']
}]
},
}
});
// Load Grunt plugins.
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-newer');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.loadNpmTasks('grunt-processhtml');
grunt.loadNpmTasks('grunt-cdn');
grunt.loadNpmTasks('grunt-string-replace');
grunt.loadNpmTasks('grunt-aws-s3');
grunt.loadNpmTasks('grunt-azure-blob');
// The default task which does just some checks (JSHint report).
grunt.registerTask('default', [
'newer:jshint',
'newer:csslint'
]);
// The serve task, starts a developer/test web server
// which serves the client-side (angular) code.
grunt.registerTask('serve', 'Starts a connect web server.', [
'less',
'postcss',
'connect:livereload',
'watch'
]);
// Same as the serve task but for the
//build (production ready, deploayable) version of the code.
grunt.registerTask('serve-build', 'Starts a connect web server', [
'connect:build',
'watch'
]);
// The build task, makes a production version (deployable) from the app.
// Copies the Angular app (HTML, JavaScript files).
// Generates CSS files from LESS files.
// Adds vendor prefixes to CSS rules.
// Minifies the CSS file.
// Minifies and merges JavaScript files.
// Modifies HTML files to get production ready.
// Minifies HTML files.
grunt.registerTask('build', 'Makes the app deployable.', [
'clean:buildFull', // build directory, full clean
'less', // LESS -> CSS
'postcss', // CSS -> add vendor prefixes
'copy', // copy files to build directory
'cssmin', // build directory, CSS -> minification
'clean:buildCSS', // build directory, clean the not minified CSS files
'string-replace', // build directory, JavaScript & HTML -> change values based on configs
'uglify', // build directory, JavaScript -> minification and concatenation
'clean:buildJs', // build directory, clean the not minified JavaScript files
'processhtml', // build directory, HTML -> modifies the code
'cdn', // build directory, HTML -> prepends CDN URL
'htmlmin' // build directory, HTML -> minification
]);
// The deploy task.
// Uploads the content of the build directory to Amazon S3 and Azure Storage.
grunt.registerTask('deploy', 'Deploys the app.', [
'aws_s3',
'azure-blob'
]);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment