Cake Recipes: Build Automation

Must automate, or script, the tedium of the obscure contortions my CoffeeScript undergoes, repeatedly, over the development life cycle. And Stylus, et cetera. Inhuman mess, so have the machine do it!

Ad nauseum

Workflows

  1. Build (selectively, configurable)
  2. Run (localhost development)
  3. Watch (ie, re-build on demand)
  4. Reload (auto, when modified)
  5. Deploy (per environment)

NB

  1. Modularization: convoluted logic (and sometimes CoffeeScript's terseness) are taxing enough, so I don't want to see (any?) markup templates and cached JSON constants in my code, and I'd prefer short and simple (modularized?) source files.
  2. And all these need to be combined and ugified and linted and tested and… in intricate ways. Repeatedly.
  3. Environments vary significantly, and are probably automated (daemonized, reloaded…) using additional tools.
  4. Watch/reload seem trendy — developer happiness sells! — but really are mere fluff; re-build efficiency is the real bottleneck.
  5. Gitflow! Tangents? Eg, auto bump version/tag when rebuilding?

Alternatives: syntax, vagaries

(As of late 2012; pro'ly stale!)

  1. Grunt: what a monster! Configuration oriented, like, verbose JSON. Yuck! (Nu, code vs data, Make vs Ant…?)
  2. Gulp?
  3. Cake: a Cakefile is just a CoffeeScript, uh, script, where you define tasks (as callbacks) and options, and then run them with "cake" in a shell. Minimalistic, neat. Modules (CJS) provide lots(!) of extensions.
    1. CoffeeScript = require "coffee-script" — so can, eg:
      code+=CoffeeScript.compile file_contents
    2. {spawn, exec} = require "child_process" — for, eg:
      exec "coffee -b -c -o #{out_dir} #{src_dir}",(err,stdout,stderr)->...
      What a mess?!
  4. Cherry: alternative to Cake. Weird, functional programming (read: LISP) syntax. Ugh.
  5. Icing on the Cake — but is incompatible with it, and, meh. KISS!
  6. Cakery, flour, muffin, brownie, frosting… what, no kitchen sink?!
  7. Bashing!
  8. Scaffolding and versioning (and publishing)

Recipes

  1. CS→JS
    1. Must "shell out", because API is broken. Brownie should've taken care of it, except it doesn't work. Muffin? Flour!
    2. coffee does globbing already, so glob not required if spawning a shell. But, detect modified files?
    3. Simplest:
      build_coffee=(next)->shell "coffee --compile --output dist/client/ client/*.coffee",next
    4. Dev vs production:
      build_coffee=(next)->switch env.NODE_ENV
      	when 'development' then shell "mkdir -p dist/client/; ls --format=commas client/*.coffee; cat client/*.coffee | coffee --compile --map --stdio > dist/client/app.js",next
      	else shell "mkdir dist/client/; cat client/*.coffee | coffee --compile --stdio > dist/client/app.js",next
      
      NB: Coffee won't isolate (wrap in an IIFE) these concatenated "modules" from globals/each other, so ought to manually: simply "do ->". But, HTTP/2 upturns combining! So I'm still looking for a clean client-side "require" (module loader)…
  2. Stylus (→CSS)
    1. build_styl=(next)->switch env.NODE_ENV
      	when 'development' then shell "stylus --use autoprefixer-stylus --sourcemap --out dist/client/ client/*.styl",next
      	else shell "stylus --use autoprefixer-stylus --compress --out dist/client/ client/*.styl",next
      
      NB: Autoprefixer should be configured.
  3. Double Macchiato (CS→HTML)
    1. Naïve (unconditional on modification):
      build_templates=(next)->
      	# Repeat per language (i18n): eg spa.-.html.coffee -> spa.en.html, etc.
      	# ("./" for Node's require to include CWD. Bash: either '$f' or \'$f\' works; similarly for \{\}.)
      	shell """for fp in templates/*.-.html.coffee
      	do
      		for lang in #{config.translations}
      		do
      			f=${fp#templates/}
      			coffee -e "console.log (require './$fp')('$lang')" > dist/${f%.-.html.coffee}.$lang.html
      		done
      	done""",next
      
      Where, eg, translations='en es fr'.
  4. Run (locally)
    1. serve=(next)->launch 'coffee',['dist/server.coffee'],next
      
      (Add watch/reload…)
  5. Deploy
  6. Lint and hint, beautify and uglify…
  7. Dependency resolution?
  8. To combine, or not to combine… (HTTP/2 is the question)
  9. Bookmarklets
  10. Testing testing…


--
The real world is a special case