Slideshow



Slideshow

0 1


slides-js-build-automation-2014

Presentation on build automation in early 2014

On Github homburg / slides-js-build-automation-2014

Build automation / task runners

- Build automation in general - make - ninja - phing - grunt - gulp js - ant - maven - gradle - features - incremental fields - dependency graphs - plugins - package management - tasks - concatenation - glob-to-glob - cache busting - gulp js - vinyl fs - createReadStream - Buffers vs Streams - Assets! - Rails - Laravel - Flask - sources -

GNU Make

DSL for compiling files

-- Makefile
hello: hello.c
$ make hello
cc -o hello hello.c

$ make hello
make: 'hello' er tidssvarende

GNU Make

Use file dependencies and modification time

# Makefile
hello: hello.c
# Catch system calls from process
$ strace -e stat "make hello"
...
stat("hello", {st_mode=S_IFREG|0775, st_size=8559, ...}) = 0
stat("hello.c", {st_mode=S_IFREG|0664, st_size=61, ...}) = 0
...
make: 'hello' er tidssvarende.

GNU Make

Rules

# Makefile
fisk.js: fisk.coffee

%.js: %.coffee
  coffee < $< > $@

GNU Make

JS_FINAL = js/project-name-all.js
JS_TARGETS = $(shell find js -name "*.js")
JS_MINIFIED = $(JS_TARGETS:.js=.min.js)

all: $(JS_FINAL)

# Concat
$(JS_FINAL): $(JS_MINIFIED)
  cat $^ >$@
  rm -f $^
  
%.min.js: %.js
  uglifyjs -o $@ $<
  echo >> $@
  
clean:
  rm -f $(JS_FINAL)

Score

  • + ubiquitous
  • + dependency flow/declarative = brain candy
  • + file dependencies
  • +/- DSL
  • - windows
  • - frills: log, watch, etc.
  • - intermediate/temporary files (more names!)

Actually build system, but great project "menu" and tool abstraction layer

[Why use make]
# Makefile
.PHONY: migrate js

migrate:
  php artisan migrate --env=development
  
js:
  # grunt build
  gulp
$ make migrate

$ make js

Could also use npm/composer "scripts"

$ composer run-script migrate

Many build systems

At least one pr language

  • Rake
  • Cake
  • Shake
  • pmake
  • nmake
  • cmake
  • scons

phing, what?

<!--?xml version="1.0" encoding="UTF-8"?-->
<project name="FooBar" default="dist">
    <target name="prepare">
        <echo msg="Making directory ./build">
        <mkdir dir="./build">
    </mkdir></echo></target>
    <target name="build" depends="prepare">
        <echo msg="Copying files to build directory...">
        <echo msg="Copying ./about.php to ./build directory...">
        <copy file="./about.php" tofile="./build/about.php">
        <echo msg="Copying ./browsers.php to ./build directory...">
        <copy file="./browsers.php" tofile="./build/browsers.php">
        <echo msg="Copying ./contact.php to ./build directory...">
        <copy file="./contact.php" tofile="./build/contact.php">
    </copy></echo></copy></echo></copy></echo></echo></target>
    <target name="dist" depends="build">
        <echo msg="Creating archive...">
        <tar destfile="./build/build.tar.gz" compression="gzip">
            <fileset dir="./build">
                <include name="*">
            </include></fileset>
        </tar>
        <echo msg="Files copied and compressed in build directory OK!">
    </echo></echo></target>
</project>

New

redo, fabricate.py, Tup, ninja, shake

Aim for make, with virtual fs, etc.

Grunt, so 2013

The JavaScript task runner

Tasks only, no dependencies

Lots of plugins, though

coffee, less, stylus, jade...

// JavaScript
module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    concat: {
      options: {
        separator: ';'
      },
      dist: {
        src: ['src/**/*.js'],
        dest: 'dist/<%= pkg.name %>.js'
      }
    },
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
      },
      dist: {
        files: {
          'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
        }
      }
    },
-> Educas.dk

Score

  • + General purpose language
  • + Logging
  • + Watch*
  • - Not DSL
  • - No knowledge of dependencies
  • - Manual incremental builds (everything manual)

Gulp js

The streaming build system

Streams er en del af vinyl-fs, men det er et centralt og interessant koncept

Hello gulp

var gulp = require("gulp"),
  jade = require("gulp-jade");
  
gulp.task("default", function () {
  gulp.src("app/**/*.jade")
    .pipe(jade())
    .pipe(gulp.dest("public/"));
});

Parts

  • orchestrator
  • node streams
  • gaze
  • vinyl-fs

orchestrator

Concurrent tasks

// takes in a callback so the engine knows when it'll be done
orchestrator.add('one', function (cb) {
    // do stuff -- async or otherwise
    cb(err);
});

// identifies a dependent task must be complete before this one begins
orchestrator.add('two', ['one'], function () {
    // task 'one' is done now
});

orchestrator.start('one', 'two');

node streams

# .on('something', ...) "EventEmitter"
readable.on('data', function(chunk) {
  console.log('got %d bytes of data', chunk.length);
})
readable.on('end', function() {
  console.log('there will be no more data.');
});

# Readable stream -> writeable stream

Gaze

File watching (wraps fs.watch)

gulp.watch("app/**/*.coffee", function () {...});

vinyl-fs

Virtual files on streams

new File({
  cwd: "/home/tbh/educas-dk/",
  base: "/app/",
  path: "/app/file.coffee"
  contents: new Buffer("test = 123")
});

Score

  • + No temporary files
  • + pipeline-oriented
  • + logging
  • + watch
  • - incremental builds* (gulp-watch?)

Bonus!

Assets

Rails-assets.org

# Gemfile
source 'https://rails-assets.org'
gem 'rails'

gem 'rails-assets'
gem 'rails-assets-angular'
gem 'rails-assets-bootstrap'
// application.js
//= require angular

angular.module(...)
$ bundle install
$ rails server