Runfile Documentation

Table of Contents

Introduction

What is Runfile and how to use it.

Runfile Command Reference

An extensive description explaining how to construct your Runfile.
Jump directly to:

Runfile Location and Filename

All about the runfile search path, local and global (named) runfiles.

Creating Reusable Tasks

Create tasks you can reuse in more than one project.

Multiple Project Runfiles

For large projects with many Runfile tasks, you can easily separate your Runfile to smaller files by using a .runfile settings file.

Related Gems

Premade Tasks: runfile-tasks

Easily add common tasks for code testing, gem publishing and more.

Introduction to Runfile

Runfile lets you create command line applications in a way similar to Rake, but with the full power of Docopt command line options.

You create a Runfile, and execute commands with run command arguments -and --flags.

If you are not familiar with Rake or Docopt...

You don't have to be.

These are just the tools that inspired Runfile (and docopt is used behind the scenes).

Think about Runfile as a way to easily define a command line application using Ruby code.

The fastest way to understand how Runfile works, is to create one or play with one of the examples

To create your first Runfile:

  1. Install Runfile - execute gem install runfile
  2. Make sure it is installed - execute run
  3. Create a template Runfile - execute run new
  4. Execute your shiny new program - execute run

If everything went well, you should now see:

Usage:
  run command <arg> [--flag]
  run (-h|--help|--version)

Take a look at the examples, or the Runfile Command Reference

If you are familiar with Rake...

If you know rake, then Runfile is like Rakefile. You use a special, lightweight language (DSL) to define tasks in ruby.

One of the key differences between a Runfile and a Rakefile, is that in a Runfile you can define a fairly expressive set of commands, with required/optional parameters and long/short option flags.

For example, defining a Runfile that responds to

run watch style.css --verbose --all

is done like this:

usage  "watch <file> [--verbose --all]"
action :watch do |args|
  say "Watching #{args['<file>']}"
  say "Verbosity: High" if args['--verbose']
  # your code here
end

If you are familiar with Docopt...

If you have ever used Docopt, you know it is one of the simplest ways to create expressive command line applications. Runfile is sort of a DSL around docopt.

Each command you use in the Runfile, adds a little something to the final "docopt" that will be generated.

For example, if you have this in your Runfile:

usage  "greet <name> [--long --color]"
help   "Say hello to <name>"
option "--long", "Show a longer greeting"
option "--color", "Use colored output"

The generated docopt will look like this:

$ run -h
Runfile 0.0.0

Usage:
  run greet <name> [--long --color]
  run (-h|--help|--version)

Commands:
  greet <name> [--long --color]
      Say hello to <name>

Options:
  -h --help
      Show this screen

  --version
      Show version

  --long
      Show a longer greeting

  --color
      Use colored output

Familiar right? Good. Familiar is good.

See the full Runfile Command Reference (it is short and not at all scary) or learn more about Runfile Locations and Filenames.

Runfile Command Reference

Runfile is ruby. You can write any ruby code in it as usual.

Tip: To create an initial Runfile, go to a folder that does not contain one already, and execute run new.

Runfile provides several commands to help you define your program and a handful of utility functions to make writing beautiful command line applications a breeze.

A simple Runfile looks like this ( more examples ):

usage  "greet <name>"
help   "Say hello to <name>"
action :greet do |args|
  say "Hello #{args['<name>']}" 
end

Defining Program Details

Commands to specify information about the program, such as its name and version.

Defining Program Actions

Commands to define program actions

Output Commands

Commands that can be executed in any action to print colorful, indented messages to the console.

Execution Commands

Commands that can be executed in any action to run external programs in the foreground or background easily.

Misc Commands

Additional commands you can use in your action blocks.

Defining Program Details

Cheatsheet

title   'My Utilities'
summary 'Utilities for my Rails'
version '0.1.0'

Reference

title string

Define the title of the application.
This command is optional.

Example: title "RoadRunner"

summary string

Define the one line application help message.
This command is optional.

Example: summary "My new application"

version string

Specify a version string for your program.
This command is optional.

Example: version "0.1.0"

Defining Program Actions

Cheatsheet

# --- Action Definition

usage   "server PORT [--background]"
help    "Start the server, optionally in the background"
option  "-b --background", "Start in the background"
param   "PORT", "Server port to listen on"
example "server 3000 -b"
action  :server do |args|
  # ... action code here
end

# --- Command Prefix

command 'prefix'

# all actions here will be prefixed by 'prefix'

action :anything do
  # ...
end

endcommand

Reference

usage string

Specify the usage pattern of the following action. This string is expected to be in any format recognized by docopt.

In some cases ( compact usage example ) you may want to force-disable the usage text for the following action. Simply use usage false.

This command is optional if the action does not have any arguments.

Examples:

usage "command"              # The next action (:command) has no 
                             # arguments (optional in this case)

usage "greet <name>"         # The next action (:greet) has one
                             # require argument, called 'name'

usage "run [--fast --slow]"  # The next action (:run) has two 
                             # optional flags.

usage false                  # Disable usage pattern for the next 
                             # action.

help string

Specify the help message for the following action. This is the message that will be displayed when you execute run --help

This command is optional.

Example: help "Greet the user with a colorful 'Hello!'"

option string, description [, label]

Specify the help information for any of the option flags. This is the place where you can specify that a certain flag also has a short version.

Normally, the help message for all options appear under the Options: caption. If you provide the third <label> argument, it will appear under a different caption.

See Also: Options Label Example

This command is optional unless the usage pattern may be ambiguous.

Examples:

option "-c --color", "Show message with a color"
option "--force", "Force delete", "Delete Options"

param string, description [, label]

Specify the help information for any of the positional parameters. This is purely decorative and has no impact on the operation of the Runfile.

Normally, the help message for all parameters appear under the Parameters: caption. If you provide the third <label> argument, it will appear under a different caption.

See Also: Parameters Example

This command is optional.

Examples:

param "PORT", "Server port to listen to"
param "SOURCE", "File to copy", "Copy Options"

example string

Add an example command to the general help information. When adding an example, the command run --help will show an Examples: section at the end of the help text, with all added examples.

You do not need to include the run or run runfile_name at the beginning of your example text, it is done automatically.

See Also: Example Command Example

This command is optional.

Examples:

example "copy source.txt dest.txt --force"
example "copy source.txt --here"

action name [, alias] { block }

Define the actual action block that will be called.
This command is required

Examples:

# If you do not need to process any arguments:
action :command do 
  # your code here
end

# if you want to handle arguments defined in `usage`:
action :command do |args|
  # your code here
  # access any argument with arg['--flagname'] or arg['<arg>']
end

# If you wish to use commands with hyphen, simply use single quotes:
action :'drink-beer' do 
  # your code here
end

# To define an alias (shortcut) to a command, use:
action :command, :c do 
  # your code here
end

See Also: Alias Example

command string

The command command lets you define a namespace. Once you use this command, any subsequent action will be associated with this namespace.

This is a way to create sub commands.

Call without parameter to revert back to the global namespace.

This command is optional.

Example: command "html"

See Also: Namespace Example

endcommand

Alias of command. Intended as a syntactic sugar so that you can end a command 'namespace' with endcommand instead of an empty command.

This command is optional.

Output Commands

Cheatsheet

say 'anything'
say '!txtred!anything in red'
say 'only one !txtred!word!txtrst! in red'
resay 'go back to the beginning of the line'
say_status :download, 'Download in progress'
say_status :download, 'Download in progress', :txtblu
word_wrap '  Indent a potentially wrapping line by two spaces'
say word_wrap '  Indent a potentially wrapping line by two spaces and say it'

Reference

All these commands can be used inside your :action blocks.

These commands are provided by the Colsole gem which is already bundled with Runfile.

say string

Print a message to screen, with support for color markers and partial strings.

End the string with a space to force the cursor to stay at the same line (otherwise, a newline is added).

Examples:

say "Hello world"
say "!txtgrn!I am green"
say "Legen... wait for it... "
say "dary"

resay string

Erase the current output line and print a new message. Should be called only after a space terminated call to say to ensure the cursor is at the same line.

Examples:

say "Downloading... "
resay "Downloaded"

say_status status, message [, color]

Print a message to screen, with a colored status tag.

Examples:

say_status :success, "Task completed"
say_status :error, "Task failed", :txtred

word_wrap string [, length]

If you wish to print a string and make it wrap automatically based on the width of the terminal, use word_wrap.

The function is also sensitive to any leading spaces. These will be preserved in any wrapped line, so it is easy to print indented long strings.

Examples:

say word_wrap("one two three four", 10)
say word_wrap("   one two three four", 10)
say word_wrap "Some long line ... that ends here"

Execution Commands

Cheatsheet


# --- Running commands and services

run 'pwd'
run! 'pwd' # and exit
run_bg 'rails server'
run_bg 'rails server', log: 'my.log', pid: 'myserver'
stop_bg 'myserver'

# --- Before / After run hooks

before_run do |command|
  command.gsub /^(rails|rake|cucumber)/, "bin/\\1"
end

after_run do |command|
  puts "Finished #{command}"
end

# --- Configuration

Runfile.setup do |config|
  config.pid_dir = 'tmp/pid'
  config.quiet   = true
end

# or...

Runfile.pid_dir = 'tmp'

Reference

run string

Print and run a command. Wait until it is done and continue. This is executed using system. If Runfile.quiet is true, it will not echo the command to the console.

Example:

run "rails server -b* -p3000"

run! string

Print and run a command, then exit. This is executed using exec. If Runfile.quiet is true, it will not echo the command to the console.

Examples:

run! "rails server -b* -p3000"
puts "this message will never be shown"

run_bg string [, options]

Run a command in the background, which you can later stop with stop_bg.

Available options are:

  • pid - provide a string that will be used to name the PID file. This is the string you will later use in stop_bg. All PID files will be stored in the working directory, or in Runfile.pid_dir.
  • log - provide a filename that will be used to log the output.

Examples:

run_bg 'some/long-running/process'
run_bg 'some/long-running/process', log: 'my.log', pid: 'daemon'

stop_bg string

Stop a command started with 'run_bg'. Provide the name of he pid file you used in 'run_bg'

Example:

stop_bg 'server'

before_run { block }

Intercept each call before executed. Can be used to modify the command, run something before it, or cancel it altogether.

Your block receives the command as argument, and should return a command to run or false to stop execution.

Examples:

# Prefix the command with "bin/" if it is rails, rake or cucumber
before_run do |command|
  cmd.gsub /^(rails|rake|cucumber)/, "bin/\\1"
end

# Abort the command execution based on the value of an environment 
# variable
before_run do |command|
  ENV['PREVENT_EXECUTION'] ? false : command
end

after_run { block }

Intercept each call before it exits. Note this is only useful with run and run_bg but not with run! which exits immediately after execution.

Examples:

after_run do |command|
  puts "Finished #{command}"
end

Configuration

You can configure several aspects of how these commands behave.

Configuration can be done in one of two ways. Either use the direct syntax: Runfile.option = value, or provide a block:

Runfile.setup do |config|
  config.option = value
end

Add the configuration anywhere in your Runfile (either inside an action or outside, at the beginning of the file).

pid_dir - configure folder for PID files

PID files are stored in the working directory by default.

Runfile.pid_dir = './tmp/pids'

quiet - run without echoing the command

By default, calls to run will show the command before running it. Set quiet to true to change that.

Example:

Runfile.quiet = true

Example

See this example file sample usage or this feature file to get an idea of what is possible with this extension.

Misc Commands

Cheatsheet

execute 'other_action'

Reference

execute string

Call another action. This command accepts a single string argument which should include the command as if you type it in the command prompt (only without the run prefix).

Example: execute "theme watch --all"

See Also: Cross Call Example

Runfile Location and Filename

Runfile is designed to help you create both project specific command line tools, and system wide command line applications.

Project Runfiles are simply named Runfile and can only be accessed in the same directory they live in.

Named Runfiles (*.runfile) can exist in several places:

  • *.runfile in the current folder
  • *.runfile in ~/runfile and its sub directories
  • *.runfile in /etc/runfile and its sub directories
  • *.runfile in any custom folder

When you execute run, this is what happens:

  • If there is a file called Runfile in the current directory, we will use it.
  • If there is a file called .runfile in the current folder, we will use it as a configuration file to tell us where the runfiles are. (See Custom Location below).
  • If not, search for *.runfiles in the runfile search directories and sub-directories.
  • If one or more were found, show a list of all of them.

Using a project Runfile

$ cd /your/project

$ run new
Runfile created.

$ run
Usage:
  run command <arg> [--flag]
  run (-h|--help|--version)

$ run command hello
Command running...

$ cat Runfile
summary "Application description"
version "0.1.0"

usage  "command <arg> [--flag]"
help   "Help line for command"
option "-f --flag", "Help text for option"
action :command do |args|
  say "Command running..."
end

Using Named Runfiles

$ cd /your/project

$ run new greet
greet.runfile created.

$ run
Runfile engine v0.7.0

Tip: Type 'run new' or 'run new name' to create a runfile.
For global access, place named.runfiles in ~/runfile/ or in /etc/runfile/ or 
anywhere in the PATH.

  run greet ........................ /path/to/file

$ run greet
Usage:
  run greet command <arg> [--flag]
  run greet (-h|--help|--version)

$ cd ~/runfile    # or /etc/runfile

$ run new hotdog
hotdog.runfile created.

$ run
Runfile engine v0.4.0

Tip: Type 'run new' or 'run new name' to create a runfile.
For global access, place named.runfiles in ~/runfile/ or in /etc/runfile/ or 
anywhere in the PATH.

  run greet ........................ /path/to/file
  run hotdog ....................... /path/to/file

Custom Location for Named Runfiles

For more advanced uses, you can define multiple Runfiles per project.

This can be handy if you have a large set of commands and wish to separate them to multiple files.

Simply create a .runfile settings file in your project, and use it to specify the location of the folder containing your runfiles.

# in .runfile
---
folder: lib/commands

This settings file supports several more options, like auto-loading a helper file, and creating command shortcuts.

Read more in the Multiple Project Runfiles page, or see the Settings Example

Ignoring the local Runfile

In case you are using both local Runfiles and global named runfiles, you may find yourself in a situation where you are trying to run a named runfile from a folder that contains a local Runfile.

The local Runfile will take precedence and not allow access to your global runfiles.

To overcome this issue, use run! instead of run.

The run! command will ignore the local Runfile and only look for named runfiles.

Note for RVM users

If you are using RVM, it is recommended you add this to your .bashrc:

export NOEXEC_EXCLUDE="run!"

This will prevent RVM from silently prepending it with bundle exec.

More information is available in the RVM documentation and in the RVM GitHub Repository.

Creating Reusable Tasks

You can easily build a set of tasks that can be reused in any Runfile.

Since this is pure ruby, there are many ways to do so. The below is an example.

Step 1: Create your common tasks file

Let's save this file as 'tasks.rb'

# The reusable tasks module

module MyCommonTasks
  def self.tasks
    # We can use the standard Runfile syntax here

    command "make"

    usage  "cake [--chocolate|--cheese]"
    help   "Make some cake"
    action :cake do |args|
      say "Making a cake..."
      say "... a chocolate cake" if args['--chocolate']
      say "... a cheese cake" if args['--cheese']
    end

    usage  "faces"
    help   "Make faces"
    action :faces do |args|
      say "Making faces"
    end

    endcommand
  end
end

Step 2: Embed the tasks in your Runfile

Create a Runfile file (you can simply run run make) and paste this content in it.

# include our common tasks file
require_relative "tasks"

title   "My Runfile"
summary "A sample Runfile"
version "0.1.0"

# include the external tasks
MyCommonTasks.tasks

# continue with regular tasks
usage  "hello [<name> --color]"
help   "Say hello"
option "-c --color", "Greet with color"
action :hello do |args|
  if args['--color']
    say "!txtgrn!Hello #{args['<name>']}"
  else
    say "Hello #{args['<name>']}"
  end
end

Step 3: Test the Runfile

Running run will now show both the embedded and local tasks:

$ run
Usage:
  run make cake [--chocolate|--cheese]
  run make faces
  run hello [<name> --color]
  run (-h|--help|--version)

Packing reusable tasks as a gem

You can pack your reusable tasks as a gem for all to enjoy.

An example of such a gem, is the runfile-tasks gem which provides a collection of tasks like:

  • Tasks for running tests (minitest, rspec, cucumber)
  • Tasks for packing and publishing gems

And more.

Multiple Project Runfiles

Runfile provides another mechanism for more advanced uses.

This mechanism lets you have multiple named runfiles per project. This is useful when your Runfile becomes too large, or when you wish to separate it to logical units.

To use this mechanism, you need to:

  • Delete the Runfile file from your project's folder.
  • Create a .runfile settings file (YAML) in your project's folder.

The .runfile Configuration File

# .runfile
# Runfile settings YAML 

# Define the folder where *.runfile files reside.
folder: lib/commands

# Optional: File to load before any runfile.
# Consider this your "helper" file.
helper: helper.rb

# Optional: Message to show before the list of files.
# This string supports color markers.
intro: "!txtgrn!My Command Line"

# Optional: An array of shortcut commands
# Each of the keys here will be expanded to their values before Runfile
# tries to move on to the execution stage. In other words, if you have
# a shortcut saying "s: server --daemon", and you run "run s" it is
# exactly like you run "run server --daemon".
shortcuts:
  s: server start
  sd: server start --daemon
  stat: server status

To see how it works, check out the Settings Example.

Now, whenever you execute run, we will look for lib/commands/*.runfile files and you can execute any of them like any other named runfile.

Running run without any argument will show both the runfiles in the folder and the available shortcuts.