#tasks #automation #makefile #command-line

bin+lib devrc

devrc is an easy to use task runner tool on steroids for developers

5 releases (breaking)

0.4.0 Sep 14, 2022
0.3.0 Mar 30, 2021
0.2.0 Mar 3, 2021
0.1.0 Feb 23, 2021
0.0.1 Nov 17, 2020

#363 in Development tools

21 downloads per month

Custom license

3.5K SLoC

Task automation tool on steroids for developers

Crates.io Crates.io CI Security audit Minimum supported Rust version Lines Of Code LICENSE


devrc is an easy to use task runner on steroids. It's a small and fast utility written in Rust 🦀.

It's userful for project or common routine automation such as minification, compilation, unit testing, linting and many more.

It's just single binary and you don't need to install python, ruby or something else.

It allows you to run tasks that are stored in YAML file named Devrcfile. Just type command, e.g. devrc task_name_1 task_name_2 task_name_3.

Quick introduction

There are several ways to define task. Here are general variants:

  1. Simple command definition without documentation string.

    task_name: echo "Hello world"
  2. More complex definition with documentation string.

      desc: "Task description"
      exec: echo "Hello world"

To run task just type command devrc TASK_NAME, e.g.:

devrc task_name

For more details look into usage section or examples.


Lets start with an overview of features that exist in devrc:

  • All tasks can be listed from the command line with documentation
  • Template engine and variables interpolation are supported
  • Environment variables customization
  • Command line completion scripts
  • devrc supports dotenv files
  • Writing task commands in different languages
  • Task parameters and user input
  • Set global variables and environment variables from tasks
  • Remote command execution
  • Read Devrcfile contents from stdin
  • Global and local defined variables and environment variables
  • Embedded deno runtime

Why YAML is used?

There are many formats (e.g. TOML, Makefile, YAML, custom formats) that have been examining after many years of using Makefiles for project routine automation.

What are the benefits of using YAML for this purpose and why it's choosen:

  1. YAML is designed to be human-friendly and easy to read;
  2. Syntax highlight works out of the box;
  3. YAML is used industry-wide for declarative configuration. For example, it's used by gitlab-ci, GitHub actions and ansible;
  4. Many text editors and platforms have plugins or built-in tools to check YAML configuration syntax for you.
  5. Good parsers for YAML already exist and I don't need to waste time for implementing and testing a self-written parser.

Table of contents


To install devrc, you can download a pre-compiled binary, or you can compile it from source. You may be able to install devrc using your OS’s package manager, depending on your platform.

Install from crates.io

devrc is written in Rust. You will need rustc version 1.48.0 or higher.

The recommended way to install Rust for development is from the official download page, using rustup.

If you have the Rust toolchain already installed on your local system, you can use the cargo install command:

rustup update stable
cargo install devrc

Cargo will build the devrc binary and place it in $HOME/.cargo.

Compile from sources

GitHub GitHub

Clone the repository and change it to your working directory.

git clone https://github.com/devrc-hub/devrc.git
cd devrc

rustup update stable
cargo install

Binary Releases

Binary releases are available in the github releases page for macOS, and Linux targets.
The following binaries are available for each release:

  • x86_64-unknown-linux-musl
  • x86_64-unknown-linux-gnu
  • x86_64-apple-darwin


Tasks are stored as mappings (hashes/dictionaries/key-value pairs) in YAML file named Devrcfile by default. Or you can specify environment variable DEVRC_FILE with alternative file name. Also you can store global tasks in ~/.devrc in your home directory or overwrite shared project tasks or varibles by local Devrcfile.local file.

If command line option -f is used:

  1. Loading ~/.devrc in home directory if it exists and if command line flag -g or option devrc_config.global are enabled;
  2. Loading files which are specified by command line option -f;

If command line option -f isn't used:

  1. Loading ~/.devrc in home directory if it exists and if command line flag -g or option devrc_config.global in Devrcfile are enabled;
  2. Loading Devrcfile or file name which is defined by environment variable DEVRC_FILE in the current directory;
  3. Loading Devrcfile.local.

The names of the files are a case sensitive.

Task defition is like to definition of job in .gitlab-ci files. Key is used as task name and value is used to create task logic. Some key names are reserved and described below.

Task definition

There are different types of tasks.

  1. Executable task
  2. Configuration task (it hasn't been implemented yet).

There are three styles of task definition.

Simple command definition without documentation string:

  name: "Alex"

task_name: echo "Hello {{ name }}"

or several commands in one task

  first_name: "Alex"
  second_name: "Alice"

  ENV_NAME: "{{ second_name }}"

  - echo "Hello {{ first_name }}!"
  - echo "Hello $ENV_NAME!"

This tasks can be rewritten to more complex definition form with documentation string and variables.

  name: "Alex"
desc: "Task description"
  exec: echo "Hello {{ name }}"


  first_name: "Alex"
  second_name: "Alice"

  ENV_NAME: "{{ second_name }}"

  desc: "Task description"
    - echo "Hello {{ first_name }}!"
    - echo "Hello $ENV_NAME!"

If we write code to Devrcfile and type command devrc task_name in console it will output to console:

Hello Alex!
Hello Alice!

Pay attention that {{ first_name }} are replaced by Alex by template engine and $ENV_NAME! are replaced by Alice by bash.

More complex examples can be found in examples directory.

Reserved keywords

There are few reserved keywords that can't be used as task name:

  • devrc_config - global options such as shell, log_level, current_directory;
  • variables - global set of variables that are used by template engine;
  • environment - global set of environment variables that are passed to children process's environment;
  • before_script - is a task that are executed before first task;
  • after_script - is a task that are executed after last task;
  • before_task - is a task that are executed before each task;
  • after_task - is a task that are executed after each task;
  • env_file - is used for dotenv files;


  global: true
  interpreter: /bin/bash -c
  default: [task_1, task_2]


Variables are used by template engine to compute commands, another variables (global or local) or environment variables. If there exists global and local variables with the same name, then local will overwrite it's value.

Variable modifiers

There exists special keywords in variable binding definition:

  • +global - if this keywords specified, then variable saved to global scope.

Environment variables

Environment variables that are passed to children process's environment and they must be accessed using $VARIABLE_NAME in commands. Environment variables can be defined globally or locally in task. If there exists global and local environment variables with the same name, then local will overwrite it's value. The shell will expand or substitute the value of a variable into a command line if you put a Dollar Sign $ in front of the variable name.

    name: "Alex"

  exec: Hello $name!

Dotenv files support

devrc can load environment variables from env (dotenv) files. These variables are environment variables, not template variables. By default if something goes wrong in dotenv loading, devrc will break and exit. You can change default behaviour by using option ignore_errors and if something goes wrong, devrc will continue.

If env file contains:


you can load environment varibles from files using one of variants:

  - ./.env
  - file: ./.env_3
    ignore_errors: true
  - file: ./.env_2

task_name: echo "Hello $ENV_NAME"

File path can be absolute or relative. Part ./ substitute to current directory.

Execution and computation rules

devrc views commands, global or local task defined variables, global or local task defines environment variables as templates. It applies template engine for commands before executing them or before variable assignment.

devrc consistently reads variables from files and applies a template engine based on Jinja2/Django syntax.

In the example text variable_1 variable_2 is assigned to a variable var_2 and text env_var variable_1 variable_2 is assignet to an environment variable ENV_VAR:

    var_1: "variable 1"
    var_2: "variable_2 {{ var_1}}"

    ENV_VAR: "env_var {{ var_2 }}"

Task dependencies

Task may have dependencies from another tasks. Dependencies of a task always run before a task execution and before before_task hook. This is useful to make some job before given task, like clean cache, remove atrifacts, etc. Dependencies run in series.

task_1: echo "Hello world!"

  exec: echo "Hello $USER!"
  deps: [task_1]

Template engine

Task parameters and user input

Tasks may have parameters. Task arguments are passed after task name when devrc is called. Parameters can be required or have default value. Also parameter value is a template string and previously defined variables or parameters can be used:

devrc task_name arg1 arg2 "argument with spaces and {{ param1 }}" task_name2

There are 2 forms of parameters definitions.

Here is a simple form where param1 and param2 are required and param2 is optional with default value:

task_name param1 param2 param3="argument with spaces and {{ param1 }}": |
  echo "Hello {{ param1 }} and {{ param2 }}";
  echo "{{ param3 }}"

Default value must be in double quotes. If you need to use quotes inside default value you can escape it by \ (backslash) symbol.

Here is a more complex form:

Here task has a required parameter name, an optional parameter other with default value of Alice and host parameter host which is assigned after user input.

    - echo "Hello {{ name }} and {{ other}}"

    name: # this is required parameter
    other: "Alice"

Here usage example:

$ devrc task_name name="Alex"
Hello Alex and Alice


$ devrc task_name "Alex"
Hello Alex and Alice

Remote command execution

It's also possible to execute task on remote hosts.

Notice: This feature has't been implemented yet.

  exec: echo "Hello {{ name }} from $(hostname)"
    name: "Alex"
    username: root

    - "{{ username }}@hostname1:22"
    - root@hostname2:22
  exec: echo "Hello {{ name }} from $(hostname)"
    name: "Alex"
    username: root

      - "{{ username }}@hostname1:22"
      - hostname2

Writing task commands in different languages

  desc: "Execute python script"
  exec: |
    #!/usr/bin/env python

    print("Hello from python")

  desc: "Execute javascript by node"
  exec: |
    #!/usr/bin/env node

    console.log("Hello from node")

Command devrc hello_1 hello_2 output:

Hello from python
Hello from node

Read Devrcfile from stdin

Instead of reading files devrc can read tasks file from stdin. To enable this behaviour pass --stdin flag:

cat ./Devrcfile | devrc --stdin task_name


devrc --stdin task_name < ./Devrcfile

Embedded deno runtime

Devrc has embedded deno runtime. Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8. This runtime can be enabled on a global level or task level. By default all permissions disabled. No file, network, or environment access, unless explicitly enabled.

    runtime: deno-runtime
      - allow-env
      - allow-net: [google.com, httpbin.org]

      # - disable-all
      # - allow-all
      # - allow-env
      # - allow-hrtime
      # - allow-net: [google.com, httpbin.org]
      # - allow-plugin
      # - allow-read: ["/tmp"]
      # - allow-run
      # - allow-write-all
      # - allow-write: ["/tmp"]

colors: |
  import { bgBlue, bold, italic, red } from "https://deno.land/std/fmt/colors.ts";

  const name = prompt("What is your name?");

  confirm(`Are you sure ${name} is your name?`);

  if (import.meta.main) {
     console.log(bgBlue(italic(red(bold(`Hello ${name} !`)))));

More examples can be found here.


  • Bash or makefile :-)
  • just - is a handy way to save and run project-specific commands. Commands are stored in a file called justfile with syntax inspired by make.
  • robo - Simple YAML-based task runner written in Go. It looks abandoned.
  • go-task - simpler Make alternative written in Go. It uses Go's template engine which syntax makes me cry.


Any suggestion, feedback or contributing is highly appreciated.

I'm especially very thankful for your grammar correction contributions, because English isn't my native language.

Thank you for your support!


~1.5M SLoC