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
.
There are several ways to define task. Here are general variants:
Simple command definition without documentation string.
yaml
task_name: echo "Hello world"
More complex definition with documentation string.
yaml
task_name:
desc: "Task description"
exec: echo "Hello world"
To run task just type command devrc TASK_NAME
, e.g.:
bash
devrc task_name
For more details look into usage section or examples.
Lets start with an overview of features that exist in devrc:
Devrcfile
contents from stdinThere 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:
Devrcfile
from sdtinTo 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.
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:
bash
rustup update stable
cargo install devrc
Cargo will build the devrc
binary and place it in $HOME/.cargo
.
Clone the repository and change it to your working directory.
```bash git clone https://github.com/devrc-hub/devrc.git cd devrc
rustup update stable cargo install ```
Binary releases are available in the github releases page for macOS, and Linux targets.
The following binaries are available for each release:
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.
There are different types of tasks.
There are three styles of task definition.
Simple command definition without documentation string:
```yaml variables: name: "Alex"
task_name: echo "Hello {{ name }}" ```
or several commands in one task ```yaml
variables: firstname: "Alex" secondname: "Alice"
environment: ENVNAME: "{{ secondname }}"
taskname: - echo "Hello {{ firstname }}!" - echo "Hello $ENV_NAME!" ```
This tasks can be rewritten to more complex definition form with documentation string and variables.
yaml
variables:
name: "Alex"
task_name:
desc: "Task description"
exec: echo "Hello {{ name }}"
or
```yaml variables: firstname: "Alex" secondname: "Alice"
environment: ENVNAME: "{{ secondname }}"
taskname: desc: "Task description" exec: - echo "Hello {{ firstname }}!" - echo "Hello $ENV_NAME!" ```
If we write code to Devrcfile
and type command devrc task_name
in console it will output to console:
text
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.
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;```yaml
devrcconfig: global: true interpreter: /bin/bash -c default: [task1, 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.
There exists special keywords in variable binding definition:
+global
- if this keywords specified, then variable saved to global scope.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.
```yaml
tast_name: environment: name: "Alex"
exec: Hello $name!
```
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:
text
ENV_NAME=Alex
you can load environment varibles from files using one of variants:
```yaml envfile: - ./.env - file: ./.env3 ignoreerrors: true - file: ./.env2
taskname: echo "Hello $ENVNAME"
```
File path can be absolute or relative. Part ./
substitute to current directory.
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
:
```yaml variables: var1: "variable 1" var2: "variable2 {{ var1}}"
environment: ENVVAR: "envvar {{ var_2 }}" ```
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.
```yaml
task_1: echo "Hello world!"
task2: exec: echo "Hello $USER!" deps: [task1]
```
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:
bash
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:
```yaml 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.
```yaml
task_name: exec: - echo "Hello {{ name }} and {{ other}}"
params: name: # this is required parameter other: "Alice"
```
Here usage example:
bash
$ devrc task_name name="Alex"
Hello Alex and Alice
or
bash
$ devrc task_name "Alex"
Hello Alex and Alice
It's also possible to execute task on remote hosts.
Notice: This feature has't been implemented yet.
```yaml task_name: exec: echo "Hello {{ name }} from $(hostname)" variables: name: "Alex" username: root
remote: - "{{ username }}@hostname1:22" - root@hostname2:22 ```
```yaml task_name: exec: echo "Hello {{ name }} from $(hostname)" variables: name: "Alex" username: root
remote: hosts: - "{{ username }}@hostname1:22" - hostname2 ```
```yaml
hello_1: desc: "Execute python script" exec: | #!/usr/bin/env python
print("Hello from python")
hello_2: desc: "Execute javascript by node" exec: | #!/usr/bin/env node
console.log("Hello from node")
```
Command devrc hello_1 hello_2
output:
text
Hello from python
Hello from node
Instead of reading files devrc
can read tasks file from stdin. To enable this behaviour pass --stdin
flag:
bash
cat ./Devrcfile | devrc --stdin task_name
or
bash
devrc --stdin task_name < ./Devrcfile
Tasks can be executed by plugin that is loaded through the devrc_config.plugins
option.
```yaml
devrc_config:
plugins: local-plugin-alias: ../path/to/dynamic/library.dylib
interpreter:
runtime: local-plugin-alias
options:
plugin-option-1: plugin-option-1-value
```
More examples can be found here. An example plugin can be found in the repository.
Devrc has embedded deno runtime via plugin. 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.
```yaml
devrc_config: interpreter: runtime: deno-runtime options: permissions: - 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"]
plugins: deno-runtime: ../path/to/dynamic/library.dylib
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.
devrc allows you to load tasks, variables, and environment variables using the import mechanism. Files can be included by specifying an absolute path, URL address, or relative path. In turn, imported devrc files can include other files using relative paths. There are several strategies for resolving relative paths. By default, the strategy is to use the directory of the loaded file as the base path. The connection strategy for a specific file can be specified using the path_resolve
option.
Let's assume that we are in the directory /projects/awesome_project
and we execute the command devrc -f devrcfiles/local_1.yml --list
. In the file /project/awesome_project/devrcfiles/local_1.yml
, we include other files as follows:
```yaml include: - url: "https://raw.githubusercontent.com/devrc-hub/devrc/master/examples/remote/entry.yml" checksum: 1234
# /projects/awesomeproject/devrcfiles/local2.yml - file: ./local2.yml pathresolve: relative
# /projects/awesomeproject/local3.yml - file: ./local3.yml pathresolve: pwd
```
In the file /project/awesome_project/devrcfiles/local_1.yml
, we load tasks from the remotely located file https://raw.githubusercontent.com/devrc-hub/devrc/master/examples/remote/entry.yml
. In this file, other files are included as follows:
```yaml include: # https://raw.githubusercontent.com/devrc-hub/devrc/master/examples/remote/remote1.yml - file: ./remote1.yml
# /projects/awesomeproject/devrcfile/local4.yml - file: ./devrcfiles/local4.yml pathresolve: pwd "
```
/projects/awesome_project/devrcfiles/local_1.yml
will be loaded;entry.yml
file will be loaded from the remote source https://raw.githubusercontent.com/devrc-hub/devrc/master/examples/remote/entry.yml
;remote_1.yml
file will be loaded from the remote source https://raw.githubusercontent.com/devrc-hub/devrc/master/examples/remote/remote_1.yml
;local_4.yml
file will be loaded from the local source /projects/awesome_project/devrcfile/local_4.yml
;local_2.yml
file will be loaded from the local source /projects/awesome_project/devrcfiles/local_2.yml
;local_3.yml
file will be loaded from the local source /projects/awesome_project/local_3.yml
.make
.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!