I found a couple node packages that will sync to S3 and invalidate items in cloudfront, I added this to a gulp file that I use to handle all my images, JS and SASS (makes jekyll faster).
Works pretty good so far, using gulp 4.
I have cloudfront gzipping files automatically so it is not needed here.
The base gulp
command will run jekyll build and launch browsersync and build all the sass and other assets.
gulp deploy
will minify the html and then sync with s3 and invalidate CF.
The AWS ID and secret have been removed, they should really be stored somewhere else but it works fine having them in this file.
This is probably an improvement over what I was doing with S3_website, my pingdom score actually went up a couple points. I have also used this with GitLab CI and am able to get it to publish automatically when I make a commit.
So far it works pretty well but I am sure I could improve it some more, not really a gulp expert.
Things this does that are better than jekyll serve
- css sourcemaps
- rebuilds/refreshes browser when changes to config file are made
- auto prefixer for css
- probably faster as gulp can move image files and process sass faster than jekyll but I have not actually tested that with jekyll 4. It was true for jekyll 3.
The script needs all sass, js and images to be in a folder called _assets
, that folder is ignored by jekyll since it starts with an underscore. I then tell jekyll to keep_files: [assets]
so it does not delete the assets folder that gulp creates. Kind of a lot going on, I’ll try to make a video on it.
var gulp = require('gulp');
var awspublish = require("gulp-awspublish");
var cloudfront = require('gulp-cloudfront-invalidate-aws-publish');
var htmlmin = require('gulp-htmlmin');
var browsersync = require('browser-sync').create();
var cp = require('child_process');
var sass = require('gulp-sass');
var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('autoprefixer');
//var watch = require('gulp-watch'); //not needed?
var terser = require('gulp-terser');
var cssnano = require('cssnano');
var imagemin = require('gulp-imagemin');
var htmlhint = require("gulp-htmlhint");
var parallelize = require("concurrent-transform");
var messages = {
jekyllBuild: '<span style="color: grey">Running:</span> $ jekyll build'
};
//if sass files change just rebuild them with gulp-sass and what not
function sassRebuild() {
var plugins = [
autoprefixer(),
cssnano()
];
return gulp.src('_assets/sass/**/*.scss')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(sourcemaps.init())
.pipe(postcss(plugins))
.pipe(sourcemaps.write('.'))
.pipe( gulp.dest('_site/assets/css/') )
.pipe(browsersync.stream()); //streams changes to browser without refresh
}
function jsRebuild() {
return gulp.src('_assets/js/**/*.js')
.pipe(terser()) //uglify with es6
.pipe( gulp.dest('_site/assets/js/') )
}
function imagesRebuild() {
return gulp.src('_assets/img/**/*.*')
.pipe( gulp.dest('_site/assets/img/') )
}
//build jekyll
function jekyllBuild(){
browsersync.notify(messages.jekyllBuild);
return cp.spawn('jekyll.bat', ['build'], {stdio: 'inherit'})
}
// BrowserSync
function browserSync(done) {
browsersync.init({
server: "_site/", logLevel: "silent"
});
done();
}
// BrowserSync Reload
function browserSyncReload(done) {
browsersync.reload();
done();
}
function watchFiles() {
gulp.watch("_assets/sass/**/*.scss", sassRebuild);
gulp.watch("_assets/js/**/*", gulp.series(jsRebuild,browserSyncReload));
gulp.watch("_assets/images/**/*", gulp.series(imagesRebuild,browserSyncReload));
gulp.watch(['**/*.*', '!_site/**/*','!_assets/**/*','!node_modules/**/*','!.sass-cache/**/*' ],
gulp.series(jekyllBuild,browserSyncReload)
);
}
// create a new publisher using S3 options
// http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#constructor-property
var publisher = awspublish.create({
region: "us-east-1",
params: {
Bucket: "prolabprints.com"
},accessKeyId: 'AK...', // Optional AWS Access Key ID
secretAccessKey: 'MZI..v'
});
// define custom headers
var headers = {
"Cache-Control": "max-age=31536000, no-transform, public"
// ...
};
var cfSettings = {
distribution: 'E19RZ...', // Cloudfront distribution ID
accessKeyId: 'AKI...', // Optional AWS Access Key ID
secretAccessKey: 'M...v',
wait: false, // Whether to wait until invalidation is completed (default: false)
originPath: '', // Configure OriginPath to be removed of file path to invalidation
indexRootPath: true // Invalidate index.html root paths (`foo/index.html` and `foo/`) (default: false)
}
//sync to s3 and invalidate cloudfront, can't have spaces in filenames
function push() {
console.log('Pushing to S3 and invalidating Cloudfront files');
return (
gulp
.src("./_site/**/*")
.pipe(parallelize(publisher.publish(headers),10))
.pipe(publisher.sync())
.pipe(publisher.cache())
.pipe(cloudfront(cfSettings))
.pipe(awspublish.reporter({states: ["create", "update", "delete"]})));
}
//minify html
function minifyHTML() {
console.log('Minimizing HTML files');
return gulp.src('_site/**/*.html')
.pipe(htmlmin({ collapseWhitespace: true, removeComments:true, minifyCSS: true, minifyJS: true }))
.pipe(gulp.dest(function (file) {
return file.base;
}));
}
function validate() {
return gulp.src('_site/**/*.html')
.pipe(htmlhint())
.pipe(htmlhint.reporter());
}
const watch2 = gulp.parallel(watchFiles,browserSync);
const watch = gulp.parallel(jsRebuild,imagesRebuild,jekyllBuild,sassRebuild,watch2);
const deploy = gulp.series(minifyHTML,push);
const deployGitLab = gulp.series(jsRebuild,imagesRebuild,sassRebuild,minifyHTML,push);
exports.deployGitLab = deployGitLab;
exports.validate = validate;
exports.jekyllBuild = jekyllBuild;
exports.push = push;
exports.minifyHTML = minifyHTML;
exports.watch = watch;
exports.deploy = deploy;
exports.browserSync = browserSync;
exports.sassRebuild = sassRebuild;
gulp.task('default', watch);