Fire (火)

Homepage / Report Issue / Source Code / IRC Channel

Fire is a rules-based build tool and continuous integration system. The creative spark that created Fire is "logic programming meets build tool". With it the Ruby developer defines project states and rules to follow when those state are met. In this manner, a project can all but manage itself!

Instruction

Rule Files

Rule files by default are looked up from a project's root directory matching task/file.rb or task/*.fire.rb. All matching files will be loaded. If you prefer to use a different location, you can create a .fire/script.rb file and only that file will be used. In it you can import any other paths you like.

Rule files are Ruby scripts, containing primarily a collection of state and rule definitions. States define conditions and rules define procedures to take based on such states.

States

States are conditions upon which rule depend to decide when a rules procedure should run or not. States are defined using the state method with a code block to express the condition. General rules are given a specific name.

  state :happy_hour? do
    t = Time.now; t.hour >= 2 && t.hour <= 3
  end

Named states define a method internally, which is called when defining rules (see below). But states can also be anonymous. A common type of anonymous state is the file state, which automatically creates a condition to check for files on disk that have changed since the previous "firing".

    file('lib/**/*.rb')

Another such state is the env state, which is used to match system environment variables. The variable's value can be matched against a specific string or a regular expression.

    env('PATH'=>/foo/)

Rules

Rules take a state as an argument and attaches it to an action procedure. When fired, if the state condition evaluates as true, the rule procedure will be called.

    # Mast handles manifest updates.

    state :update_manifest? do
      ! system "mast --recent"
    end

    rule update_manifest? do
      system "mast -u"
    end

To create a Rule for a file state, we can use the file state method mentioned previously.

    rule file('demo/*.md') do |files|
      system `qed #{files.join(' ')}`
    end

But using file isn't necessary when it is the only condition b/c rules that use a string or a regular expression for the state are automatically interpreted to be a file state.

    rule 'man/*.ronn' do |paths|
      system 'ronn ' + paths.join(' ')
    end

State Logic

Rules sometimes require more nuanced conditions based on multiple states. Fire has a state logic system based on set logic that can be used to build complex states using logical operators & (And) and | (Or).

    rule happy_hour? & file('*.happy') do |files|
      puts "These are you happy files:"
      puts files.join("\n")
    end

Tasks

Rules work admirably for most usecases, but sometimes it is necessary just to trigger a single action and not have all ones rules come to bare. For this Fire provides tasks.

    task :test do
      sh 'rubytest'
    end

These rules are then triggered via the command line, or as prerequisite actions of other tasks or rules (see below). To make a tasks accessible via the command line, a description must be given using the desc method before the task definition.

    desc "run all unit tests"
    task :test do
      sh "rubytest"
    end

Btw, rules can be given descriptions too, as can states. This information isn't important to the Fire's functionality but can useful to a user who can request help information about a system.

Prerequisites

Sometimes rules have prerequisite actions. And often different rules may share the same prerequisite actions. But a prerequisite is only ever run once for any given run regardless.

    desc "Run all unit tests"
    task :test => [:setup] do
      sh "rubytest"
    end

    desc "Run unit tests when a test file changes."
    rule 'test/**/test_*.rb' => [:setup] do |files|
      sh "rubytest #{files.join(' ')}"
    end

    task :setup do
      mkdir_p 'tmp'
    end

Notice how both the test rule and the test task relay on the same prequisite task.

Running

To run your rules simply use the fire command.

    fire

There are few was to manually trigger builds. For file rules, the -n option will cause the digest to be "null and void", which will cause all files to appear out-of-date and thus all be triggered.

Triggers are specified as a command argument.

    fire test

Continuous Integration

Fire can be run continuously by running autofire. To set the interval provide then number of seconds to wait between firings.

    autofire -w 60

This will run fire every 60 seconds. By default the periodicity is 300 seconds, or every 5 minutes. To stop autofiring run autofire again.

Building Useful Rules

Fire doesn't dictate how rule procedures are coded. It's just Ruby. While it does provide easy access to FileUtils methods, beyond that the how of things is completely up to the developer.

We do offer one recommendation that will likely make endeavors in this regard much easier. Have a look at the Detroit Toolchain. It has numerous tools largely preconfigured and with built-in smarts to make them quick and easy to put into action.

Fire is copyrighted open-source software.

Copyright (c) 2011 Rubyworks. All rights reserved.

It is modifiable and redistributable under the terms of the BSD-2-Clause license.

See the enclosed LICENSE.txt file for details.