Markdown-based task runner - 'saku'
Tags: javascript, taskrunner, makefile, diy
Published: 2018-05-13 07:10:23

---
title: Markdown-based task runner - 'saku'
published: true
description: I made a task-runner called saku, based on markdown syntax.
tags: 
  - JavaScript
  - task-runner
  - Makefile
  - DIY
---

# tl;dr

- It's painful to use `make` command as a task runner. It's especially intolerable when writing `.PHONY` directives for evey task.
- I found some simple altenatives (like [go-task][], [robo][]), but I didn't like them.
- I came up with the idea that markdown seems a better syntax for task definition.
- I made a markdown-based task runner, named [saku][]!

# Motivation

Because I've been using JavaScript both at work and hobby always, I use [npm run-script][] as my task runner. `run-script` has some flaws such that it's unable to be documented well in a decent way, or that it's a little hard to read because it's written in JSON file. But in my opinion, it's sufficient tool for defining the necessary tasks with the support of the tools like [npm-run-all][], [cross-env][] etc.

One day I started using [terraform][] for our team's infrastructure management and I was setting up the git repository for it.

I was searching the web for how to set up the terraform project and many people seem using Makefile for organizing the commands in it.

I thought it was a good idea to use task runner to invoke terraform because `terraform` is too long to type, but I also felt uncomfortable about using Makefile in 2018. It's depressing to write enigmatic `.PHONY` directive again and again. In the first place `make` isn't a tool for running tasks, but a tool for compiling source code of the language like C, C++ etc. It is a *hack* to use `make` as a task runner and I thought there must be some other tools which are more appropriate for just running tasks in 2018.

# go-task

I found several tools. Among them, [go-task][] seemed the closest to what I was looking for. [go-task][] is a simple tool. It uses the yaml format for task configuration.

However I felt something was wrong about it. That was the key names in the yaml DSL. In [go-task][], you write commands and descripions as values of keys `cmds` and `desc`.

```
tasks:
  build:
    cmds:
      - go build -v -i main.go

  assets:
    cmds:
      - minify -o public/style.css src/css
```

To my eyes, these key names look a little redundant and seem noisy. In Makefile, targets are expressed as their names and colons and the commands are expressed by tab indents.

```
build:
	go build -v -i main.go

assets:
	minify -o public/style.css src/css
 ```

That was more elegant. Because I'm looking for more elegant replacement of `make`, it's so unfortunate if the notations get less elegant.

# Think syntax

I considered whether it's possible or not to build more simpler version of configuration in yaml syntax, but I gave up because it seemed impossible because of the characteristics of yaml. In yaml, if you need to define something, you always need `key: value` pair for anything and I felt it's impossible to get a simpler version than [go-task][]'s design.

Then I thought about several languages which I was familiar with and finally I found markdown is very much suitable for my needs because it allows bare strings in many places in different contexts.

# Markdown-based task runner

I couldn't find markdown-based task runner and therefore I created one. I named it [saku][]!

I used `remark` as markdown parser and used `minimist` as CLI option parser. I reused several toolchains for building CLI base structure which I had prepared for writing some other tools like [kocha][] or [bulbo][]. I stole some techniques from yarn to show emojis only in TTY environment.

I modeled the domain of saku according to what [DDD book][] teaches me. I like this style of progarmming these days quite a lot. Modeling makes the software development quite straightforward and easy for reasoning and testing. In the course of writing suku, I created the only one model class `Task` and the entire implementation seemed very organized ☺️.

# 🐕 Dogfooding

I started dogfooding it at [12th commit](https://github.com/kt3k/saku/commit/270c72d08ee4612a9143774bae5eb713977a4ca5) by writing down the tasks in saku.md which are necessary to build saku itself.

```md
# test
> Runs unit tests

    npx kocha --require src/__tests__/helper src{/,/**/}__tests__/*.js

# lint
> Runs lint checks

    npx standard

# fix
> Runs auto fixer of lint tool

    npx standard --fix

# cov
> Makes coverage reports

    npx nyc --reporter=text-summary --reporter=lcov saku test

# codecov
> Posts reports to codecov.io

    saku cov
    npx codecov
```

As I expected, I felt it was easy to write, and unexpectedly I found that the rendering of the saku.md in github UI was [so beautiful](https://github.com/kt3k/node-saku/blob/master/saku.md)).

# Wrap up

- It's so fun to create things from scratch! 😄
- Though markdown doesn't have a natural logical structure like yaml, personally I think it has a great potential as DSL because it has various places where bare strings can be written without quotes.

# Update: 2018-05-12

I've completely rewritten saku in [go language][go-saku]. Please check [this version][go-saku] as well 😉

[saku]: https://github.com/kt3k/node-saku
[go-saku]: https://github.com/kt3k/saku
[go-task]: https://github.com/go-task/task
[robo]: https://github.com/tj/robo
[npm run-script]: https://docs.npmjs.com/cli/run-script
[terraform]: https://www.terraform.io
[remark]: https://github.com/remarkjs/remark
[bulbo]: https://github.com/kt3k/bulbo
[kocha]: https://github.com/kt3k/kocha
[npm-run-all]: https://github.com/mysticatea/npm-run-all
[cross-env]: https://github.com/kentcdodds/cross-env
[DDD book]: http://dddcommunity.org/book/evans_2003/