Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/TekWizely/run
Task runner that helps you easily manage and invoke small scripts and wrappers
https://github.com/TekWizely/run
golang-application make makefile run runfile scripting task-runner wrappers
Last synced: 10 days ago
JSON representation
Task runner that helps you easily manage and invoke small scripts and wrappers
- Host: GitHub
- URL: https://github.com/TekWizely/run
- Owner: TekWizely
- License: mit
- Created: 2019-07-03T17:16:13.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-09-25T21:38:00.000Z (about 1 year ago)
- Last Synced: 2024-05-01T16:42:22.568Z (7 months ago)
- Topics: golang-application, make, makefile, run, runfile, scripting, task-runner, wrappers
- Language: Go
- Homepage:
- Size: 294 KB
- Stars: 475
- Watchers: 9
- Forks: 9
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Run: Easily manage and invoke small scripts and wrappers
![GitHub repo size](https://img.shields.io/github/repo-size/TekWizely/run)
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
![GitHub stars](https://img.shields.io/github/stars/TekWizely/run?style=social)
![GitHub forks](https://img.shields.io/github/forks/TekWizely/run?style=social)Do you find yourself using tools like `make` to manage non-build-related scripts?
Build tools are great, but they are not optimized for general script management.
Run aims to be better at managing small scripts and wrappers, while incorporating a familiar make-like syntax.
#### Runfile
Where make has the ubiquitous Makefile, run has the cleverly-named `"Runfile"`
By default, run will look for a file named `"Runfile"` in the current directory, exiting with error if not found.
Read below for details on specifying alternative runfiles, as well as other special modes you might find useful.
#### Commands
In place of make's targets, runfiles contain `'commands'`.
Similar to make, a command's label is used to invoke it from the command-line.
#### Scripts
Instead of recipes, each runfile command contains a `'script'` which is executed when the command is invoked.
You might be used to make's (default) behavior of executing each line of a recipe in a separate sub-shell.
In run, the entire script is executed within a single sub-shell.
#### TOC
- [Examples](#examples)
- [Special Modes](#special-modes)
- [Installing](#installing)
- [Contributing](#contributing)
- [Contact](#contact)
- [License](#license)
- [Just Looking for Bash Arg Parsing?](#just-looking-for-bash-arg-parsing)-----------
## Examples- [Simple Command Definitions](#simple-command-definitions)
- [Naming Commands](#naming-commands)
- [Simple Title Definitions](#simple-title-definitions)
- [Title & Description](#title--description)
- [Arguments](#arguments)
- [Command-Line Options](#command-line-options)
- [Making Options Required](#making-options-required)
- [Providing A Default Option Value](#providing-a-default-option-value)
- [Boolean (Flag) Options](#boolean--flag--options)
- [Getting `-h` & `--help` For Free](#getting--h----help-for-free)
- [Passing Options Directly Through to the Command Script](#passing-options-directly-through-to-the-command-script)
- [Run Tool Help](#run-tool-help)
- [Using an Alternative Runfile](#using-an-alternative-runfile)
- [Via Command-Line](#via-command-line)
- [Via Environment Variables](#via-environment-variables)
- [`$RUNFILE`](#runfile-1)
- [Using Direnv](#using-direnv-to-auto-configure-runfile)
- [`$RUNFILE_ROOTS`](#runfileroots)
- [Runfile Variables](#runfile-variables)
- [Local By Default](#local-by-default)
- [Exporting Variables](#exporting-variables)
- [Per-Command Variables](#per-command-variables)
- [Exporting Previously-Defined Variables](#exporting-previously-defined-variables)
- [Pre-Declaring Exports](#pre-declaring-exports)
- [Forgetting To Define An Exported Variable](#forgetting-to-define-an-exported-variable)
- [Referencing Other Variables](#referencing-other-variables)
- [Shell Substitution](#shell-substitution)
- [Conditional Assignment](#conditional-assignment)
- [Runfile Attributes](#runfile-attributes)
- [`.SHELL`](#runfile-attributes)
- [`.RUN`](#runfile-attributes)
- [`.RUNFILE`](#runfile-attributes)
- [`.RUNFILE.DIR`](#runfile-attributes)
- [`.SELF`](#runfile-attributes)
- [`.SELF.DIR`](#runfile-attributes)
- [Exporting Attributes](#exporting-attributes)
- [Simple Export](#simple-export)
- [Export With Name](#export-with-name)
- [Assertions](#assertions)
- [Includes](#includes---runfiles)
- [File Globbing](#file-globbing)
- [Working Directory](#working-directory)
- [Required vs Optional](#file--s--not-found)
- [Avoiding Include Loops](#avoiding-include-loops)
- [Overriding Commands](#overriding-commands)
- [Cannot Re-Register Command In Same Runfile](#cannot-re-register-command-in-same-runfile)
- [Overrides Are Case-Insensitive](#overrides-are-case-insensitive)
- [First Registered Command Defines Case For Help](#first-registered-command-defines-case-for-help)
- [First Registered Command Defines Default Documentation](#first-registered-command-defines-default-documentation)
- [Commands Are Listed In The Order They Are Registered](#commands-are-listed-in-the-order-they-are-registered)
- [Includes - .ENV](#includes---env)
- [Required vs Optional](#file--s--not-found-1)
- [Invoking Other Commands & Runfiles](#invoking-other-commands--runfiles)
- [RUN / RUN.AFTER / RUN.ENV Actions](#run--runafter--runenv-actions)
- [.RUN / .RUNFILE Attributes](#run--runfile-attributes)
- [Hidden / Private Commands](#hidden--private-commands)
- [Hidden Commands](#hidden-commands)
- [Private Commands](#private-commands)
- [Script Shells](#script-shells)
- [Per-Command Shell Config](#per-command-shell-config)
- [Global Default Shell Config](#global-default-shell-config)
- [Other Executors](#other-executors)
- [Python Example](#python-example)
- [Custom `#!` Support](#custom--support)
- [C Example](#c-example)------------------------------
### Simple Command Definitions_Runfile_
```
hello:
echo "Hello, world"
```We'll see that `hello` shows as an invokable command, but has no other help text.
_list commands_
```
$ run listCommands:
list (builtin) List available commands
help (builtin) Show help for a command
version (builtin) Show run version
hello
```_show help for hello command_
```
$ run help hellohello: no help available.
```_invoke hello command_
```
$ run helloHello, world
```#### Naming Commands
Run accepts the following pattern for command names:
```
alpha ::= 'a' .. 'z' | 'A' .. 'Z'
digit ::= '0' .. '9'CMD_NAME ::= [ alpha | '_' ] ( [ alpha | digit | '_' | '-' ] )*
```Some examples:
* `hello`
* `hello_world`
* `hello-world`
* `HelloWorld`##### Case Sensitivity
###### Registering Commands
When registering commands, run treats the command name as case-insensitive and subject to [command override](#overriding-commands) rules.
*case-insensitive override example*
For example, run will generate an error if a command name is defined multiple times in the same runfile, even if the names use different cases:
_Runfile_
```
hello-world:
echo "Hello, world"HELLO-WORLD:
echo "HELLO, WORLD"
```_list commands_
```
$ run listrun: Runfile: command hello-world defined multiple times in the same file: lines 1 and 4
```###### Invoking Commands
When invoking commands, run treats the command name as case-insensitive:
_Runfile_
```
Hello-World:
echo "Hello, world"
```_output_
```
$ run Hello-World
$ run Hello-world
$ run hello-worldHello, world
```###### Displaying Help
When displaying help text, run displays command names as they are originally defined:
_list commands_
```
$ run listCommands:
...
Hello-World
...
```_show help for Hello-World command_
```
$ run help hello-worldHello-World: no help available.
```----------------------------
### Simple Title DefinitionsWe can add a simple title to our command, providing some help content.
_Runfile_
```
## Hello world example.
hello:
echo "Hello, world"
```_output_
```
$ run listCommands:
list (builtin) List available commands
help (builtin) Show help for a command
version (builtin) Show run version
hello Hello world example.
...
``````
$ run help hellohello:
Hello world example.
```-----------------------
### Title & DescriptionWe can further flesh out the help content by adding a description.
_Runfile_
```
##
# Hello world example.
# Prints "Hello, world".
hello:
echo "Hello, world"
```_output_
```
$ run listCommands:
list (builtin) List available commands
help (builtin) Show help for a command
version (builtin) Show run version
hello Hello world example.
...
``````
$ run help hellohello:
Hello world example.
Prints "Hello, world".
```-------------
### ArgumentsPositional arguments are passed through to your command script.
_Runfile_
```
##
# Hello world example.
hello:
echo "Hello, ${1}"
```_output_
```
$ run hello NewmanHello, Newman
```------------------------
### Command-Line OptionsYou can configure command-line options and access their values with environment variables.
_Runfile_
```
##
# Hello world example.
# Prints "Hello, ".
# OPTION NAME -n,--name Name to say hello to
hello:
echo "Hello, ${NAME}"
```_output_
```
$ run help hellohello:
Hello world example.
Prints "Hello, ".
Options:
-h, --help
Show full help screen
-n, --name
Name to say hello to
``````
$ run hello --name=Newman
$ run hello -n NewmanHello, Newman
```#### Making Options Required
You can use `!` to indicate that an option is required:
```
# OPTION NAME! -n,--name Name to say hello to
```##### Required Indicator on Help Text
Required options will be indicated in help text:
```
-n, --name (required)
Name to say hello to
```##### Error When Required Option Not Provided
An error will be generated if a required option is not provided:
```
hello: ERROR: Missing required option:
-n, --name
Name to say hello to
```#### Explicitly Marking Options as "Optional"
Although options are already *optional* by default, you can use `?` to explicitly indicate that an option is optional:
```
# OPTION NAME? -n,--name Name to say hello to
```**NOTE**: This exists mostly for parity with `!` and behaves the same as when it is not used
#### Providing A Default Option Value
You can use `?=` to specify a default value for an option, which will be used if the option is not provided:
```
# OPTION NAME ?= Newman -n,--name Name to say hello to
```_output_
```
$ run helloHello, Newman
```**Note**: Any standard variable assignment value can be used (quoted strings, variable references, etc)
##### Default Indicator on Help Text
Default values will be indicated in help text:
```
-n, --name (default: Newman)
```#### Boolean (Flag) Options
Declare flag options by omitting the `'<...>'` segment.
_Runfile_
```
##
# Hello world example.
# OPTION NEWMAN --newman Say hello to Newman
hello:
NAME="World"
[[ -n "${NEWMAN}" ]] && NAME="Newman"
echo "Hello, ${NAME}"
```_output_
```
$ run help hellohello:
Hello world example.
...
--newman
Say hello to Newman
```##### Boolean Default Option Values
You can specify a default value for boolean options, but they behave slightly different from standard options:
```
# OPTION NEWMAN ?= enabled --newman Say hello to Newman
```###### Defaulted Value Always Assumed to be True
The content of the default value text is not used to determine the option's default true/false value.
Why?
Since boolean values are already *always* `false` by default, providing a "default value" can *only* have the effect of defaulting the value to `true`.
_output_
```
$ run helloHello, Newman
```###### Default Indicator on Help Text
Even though a boolean option with provided default is always assumed to default to true, the default value text is still useful in that it will be displayed in the help text:
```
--newman (default: enabled)
```This allows you to give better messaging than just "true" or "1" (i.e "enabled" in this example)
##### Setting a Flag Option to TRUE
```
$ run help --newman=true # true | True | TRUE
$ run help --newman=1 # 1 | t | T
$ run help --newman # Empty value = true
$ run help # Default value = true if option has ?=Hello, Newman
```##### Setting a Flag Option to FALSE
```
$ run help --newman=false # false | False | FALSE
$ run help --newman=0 # 0 | f | F
$ run help # Default value = false if option does not have ?=Hello, World
```
#### Getting `-h` & `--help` For Free
If your command defines one or more options, but does not explicitly configure options `-h` or `--help`, then they are automatically registered to display the command's help text.
_Runfile_
```
##
# Hello world example.
# Prints "Hello, world".
hello:
echo "Hello, world"
```_output_
```
$ run hello -h
$ run hello --helphello:
Hello world example.
Prints "Hello, world".
```#### Passing Options Directly Through to the Command Script
If your command does not define any options within the Runfile, then run will pass all command line arguments directly through to the command script.
_Runfile_
```
##
# Echo example
# Prints the arguments passed into the script
#
echo:
echo script arguments = "${@}"
```_output_
```
$ run echo -h --help Hello Newmanscript arguments = -h --help Hello Newman
```NOTE: As you likely surmised, help options (`-h` & `--help`) are not automatically registered when the command does not define any other options.
##### What if My Command Script DOES Define Options?
If your command script does define one or more options within the Runfile, you can still pass options directly through to the command script, but the syntax is a bit different:
_Runfile_
```
##
# Echo example
# Prints the arguments passed into the script
# Use -- to separate run options from script options
# OPTION ARG -a Contrived argument
#
echo:
echo ARG = "${ARG}"
echo script arguments = "${@}"
```_output_
```
$ run echo -a my-arg -- -h --help Hello NewmanARG = my-arg
script arguments = -h --help Hello Newman
```Notice the `'--'` in the argument list - Run will stop parsing options when it encounters the `'--'` and pass the rest of the arguments through to the command script.
-----------------
### Run Tool HelpInvoking `-h` or `--help` with no command shows the help page for the run tool itself.
```
$ run --helpUsage:
run [option ...]
(run )
or run list
(list commands)
or run help
(show help for )
Options:
-r, --runfile
Specify runfile (default='${RUNFILE:-Runfile}')
ex: run -r /my/runfile list
Note:
Options accept '-' | '--'
Values can be given as:
-o value | -o=value
Flags (booleans) can be given as:
-f | -f=true | -f=false
Short options cannot be combined
```--------------------------------
### Using an Alternative Runfile#### Via Command Line
You can specify a runfile using the `-r | --runfile` option:
```
$ run --runfile /path/to/my/Runfile
```NOTE: When specifying a runfile, the file does **not** have to be named `"Runfile"`.
#### Via Environment Variables
##### $RUNFILE
You can specify a runfile using the `$RUNFILE` environment variable:
```
$ export RUNFILE="/path/to/my/Runfile"$ run
```For some other interesting uses of `$RUNFILE`, see:
* [Invoking Other Commands & Runfiles](#invoking-other-commands--runfiles)
* [Using direnv to auto-configure $RUNFILE](#using-direnv-to-auto-configure-runfile)NOTE: When specifying a runfile, the file does **not** have to be named `"Runfile"`.
##### $RUNFILE_ROOTS
You can instruct run to look _up_ the directory path in search of a runfile.
You do this using the `$RUNFILE_ROOTS` path variable.
* `$RUNFILE_ROOTS` is treated as a list of path entries (using standard os path separator)
* Behaves largely similar to [GIT_CEILING_DIRECTORIES](https://git-scm.com/docs/git#Documentation/git.txt-codeGITCEILINGDIRECTORIEScode)
* If `$PWD` is a child of a root entry, then run will walk up the folder hierarchy, checking each folder for the currently-configured Runfile.
* Roots themselves are generally treated as _exclusive_ (ie not checked)
* `$HOME`, if a configured root, is treated as _inclusive_ (ie it **is** checked)_general usage_
export RUNFILE_ROOTS="${HOME}" # Will walk up to $HOME (inclusively)
_most permissive_
export RUNFILE_ROOTS="/" # Will walk up to / (exclusively)
NOTE: `$HOME` is given special treatment to support the case where a project is given its own _user_ account and the Runfile lives in the _home_ folder of that user.
For the case of creating globally available tasks, see the [Special Modes](#special-modes) section.
---------------------
### Runfile VariablesYou can define variables within your runfile:
_Runfile_
```
NAME := "Newman"##
# Hello world example.
# Tries to print "Hello, ${NAME}"
hello:
echo "Hello, ${NAME:-world}"
```#### Local By Default
By default, variables are local to the runfile and are not part of your command's environment.
For example, you can access them within your command's description:
```
$ run help hellohello:
Hello world example.
Tries to print "Hello, Newman"
```But not within your commands script:
```
$ run helloHello, world
```#### Exporting Variables
To make a variable available to your command script, you need to `export` it:
_Runfile_
```
EXPORT NAME := "Newman"##
# Hello world example.
# Tries to print "Hello, ${NAME}"
hello:
echo "Hello, ${NAME:-world}"
```_output_
```
$ run helloHello, Newman
```##### Per-Command Variables
You can create variables on a per-command basis:
_Runfile_
```
##
# Hello world example.
# Prints "Hello, ${NAME}"
# EXPORT NAME := "world"
hello:
echo "Hello, ${NAME}"
```_help output_
```
$ run help hellohello:
Hello world example.
Prints "Hello, world"
```_command output_
```
$ run helloHello, world
```##### Exporting Previously-Defined Variables
You can export previously-defined variables by name:
_Runfile_
```
HELLO := "Hello"
NAME := "world"##
# Hello world example.
# EXPORT HELLO, NAME
hello:
echo "${HELLO}, ${NAME}"
```##### Pre-Declaring Exports
You can declare exported variables before they are defined:_Runfile_
```
EXPORT HELLO, NAMEHELLO := "Hello"
NAME := "world"##
# Hello world example.
hello:
echo "${HELLO}, ${NAME}"
```###### Forgetting To Define An Exported Variable
If you export a variable, but don't define it, you will get a `WARNING`_Runfile_
```
EXPORT HELLO, NAMENAME := "world"
##
# Hello world example.
hello:
echo "Hello, ${NAME}"
```_output_
```
$ run hellorun: WARNING: exported variable not defined: 'HELLO'
Hello, world
```#### Referencing Other Variables
You can reference other variables within your assignment:
_Runfile_
```
SALUTATION := "Hello"
NAME := "Newman"EXPORT MESSAGE := "${SALUTATION}, ${NAME}"
##
# Hello world example.
hello:
echo "${MESSAGE}"
```#### Shell Substitution
You can invoke sub-shells and capture their output within your assignment:
_Runfile_
```
SALUTATION := "Hello"
NAME := "$( echo 'Newman )" # Trivial exampleEXPORT MESSAGE := "${SALUTATION}, ${NAME}"
##
# Hello world example.
hello:
echo "${MESSAGE}"
```#### Conditional Assignment
You can conditionally assign a variable, which only assigns a value if one does not already exist.
_Runfile_
```
EXPORT NAME ?= "world"##
# Hello world example.
hello:
echo "Hello, ${NAME}"
```_example with default_
```
$ run helloHello, world
```_example with override_
```
NAME="Newman" run helloHello, Newman
```----------------------
### Runfile AttributesAttributes are special variables used by the Run engine.
Their names start with `.` to avoid colliding with [runfile variables](#runfile-variables) and environment variables.
Following is the list of Run's attributes:
| Attribute | Description |
|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| `.SHELL` | Contains the shell command that will be used to execute command scripts. See [Script Shells](#script-shells) for more details. |
| `.RUN` | Contains the absolute path of the run binary currently in use. Useful for [Invoking Other Commands & Runfiles](#invoking-other-commands--runfiles). |
| `.RUNFILE` | Contains the absolute path of the **primary** Runfile. |
| `.RUNFILE.DIR` | Contains the absolute path of the parent folder of the **primary** runfile. |
| `.SELF` | Contains the absolute path of the **current** (primary or included) runfile. |
| `.SELF.DIR` | Contains the absolute path of the parent folder of the **current** runfile. |#### Exporting Attributes
In order to access an attribute's value within your commands, you'll need to assign them to an [exported variable](#exporting-variables).
Older versions of Run required you to use a variable assignment:
_Runfile_
```
EXPORT RUNFILE := ${.RUNFILE}
EXPORT RUNFILE_DIR := ${.RUNFILE.DIR}## Prints the value of .RUNFILE
runfile:
echo "${RUNFILE}"## Prints the value of .RUNFILE.DIR
runfile-dir:
echo "${RUNFILE_DIR}"
```Newer versions of Run now support less verbose options:
##### Simple Export
You can quickly export an attribute with a default variable name:
_Runfile_
```
EXPORT .RUNFILE, .RUNFILE.DIR## Prints the value of .RUNFILE
runfile:
echo "${RUNFILE}"## Prints the value of .RUNFILE.DIR
runfile-dir:
echo "${RUNFILE_DIR}"
```With this technique, Run uses the attribute's name to determine the exported variable's name by:
* Removing the leading `.` character
* Substituting any remaining `.` characters with `_`##### Export With Name
If you want to export an attribute with a non-default variable name, you can use the `AS` syntax:
```
EXPORT .RUNFILE AS RF
EXPORT .RUNFILE.DIR AS RFD## Prints the value of .RUNFILE
runfile:
echo "${RF}"## Prints the value of .RUNFILE.DIR
runfile-dir:
echo "${RFD}"
```--------------
### AssertionsAssertions let you check against expected conditions, exiting with an error message when checks fail.
Assertions have the following syntax:
```
ASSERT [ "" | '' ]
```*Note:* The error message is optional and will default to `"assertion failed"` if not provided
#### Condition
The following condition patterns are supported:
* `[ ... ]`
* `[[ ... ]]`
* `( ... )`
* `(( ... ))`*Note:* Run does not interpret the condition. The condition text will be executed, unmodified (including surrounding braces/parens/etc), by the configured shell. Run will inspect the exit status of the check and pass/fail the assertion accordingly.
#### Assertion Example
Here's an example that uses both global and command-level assertions:
_Runfile_
```
##
# Not subject to any assertions
world:
echo Hello, World# Assertion applies to ALL following commands
ASSERT [ -n "${HELLO}" ] "Variable HELLO not defined"##
# Subject to HELLO assertion, even though it doesn't use it
newman:
echo Hello, Newman##
# Subject to HELLO assertion, and adds another
# ASSERT [ -n "${NAME}" ] 'Variable NAME not defined'
name:
echo ${HELLO}, ${NAME}
```_example with no vars_
```
$ run worldHello, World
$ run newman
run: ERROR: Runfile:7: Variable HELLO not defined
$ run name
run: ERROR: Runfile:7: Variable HELLO not defined
```_example with HELLO_
```
$ HELLO=Hello run newmanHello, Newman
$ HELLO=Hello run name
run: ERROR: Runfile:16: Variable NAME not defined
```_example with HELLO and NAME_
```
$ HELLO=Hello NAME=Everybody run nameHello, Everybody
```*Note:* Assertions apply only to commands and are only checked when a command is invoked. Any globally-defined assertions will apply to ALL commands defined after the assertion.
-----------------------
### Includes - RunfilesIncludes let you organize and configure commands across multiple Runfiles.
You can include other Runfiles using the following syntax:
```
INCLUDE | "" | ''
```Simple example:
_file layout_
```
Runfile
Runfile-hello
```_Runfile_
```
INCLUDE Runfile-hello
```_Runfile-hello_
```
hello:
echo "Hello from Runfile-hello"
```_output_
```
$ run helloHello from Runfile-hello
```#### File Globbing
Run utilizes [goreleaser/fileglob](https://github.com/goreleaser/fileglob) in order support file globbing for includes.
According to their README, `fileglob` supports:
* Asterisk wildcards (`*`)
* Super-asterisk wildcards (`**`)
* Single symbol wildcards (`?`)
* Character list matchers with negation and ranges (`[abc]`, `[!abc]`, `[a-c]`)
* Alternative matchers (`{a,b}`)
* Nested globbing (`{a,[bc]}`)
* Escapable wildcards (`\{a\}/\*`)**Fileglob Example:**
_file layout_
```
Runfile
1/1/Runfile-1
2/2/Runfile-2
3/3/Runfile-3
```_Runfile_
```
INCLUDE **/Runfile-*
```#### Working Directory
Include names / glob-patterns are resolved relative to the Primary runfile's containing directory.
#### File(s) Not Found
##### Default: OK For Glob
When using a globbing pattern, Run considers it OK if the pattern results in no files being found.
This makes it possible to support features like an optional Runfile include directory, or the ability to start a project folder with no includes but have them automatically picked up as you add them.
_Runfile_
```
INCLUDE maybe_some_runfiles/Runfile-* # OK if no files found
```##### Force Error If No Files Found
To force an error if no files are found when using a globbing pattern, use `!` :
_Runfile_
```
INCLUDE ! maybe_some_runfiles/Runfile-* # ERROR if no files found
```##### Default: BAD For Single File
When using a single filename (no globbing), Run considers it an error if the include file is not found.
_Runfile_
```
INCLUDE Runfile-must-exist # Errors if file not found
```_output_
```
$ run listrun: include runfile not found: 'Runfile-must-exist'
```##### Skip Error If File Not Found
To skip generating an error if no file is found when using a single filename, use `?` :
_Runfile_
```
INCLUDE ? Runfile-might-exist # OK if file not found
```#### Avoiding Include Loops
Run keeps track of already-included runfiles and will silently avoid including the same runfile multiple times.
_Runfile_
```
INCLUDE Runfile-hello
INCLUDE Runfile-hello # Silently skipped
```#### Overriding Commands
Run allows you override commands, as long as they were originally registered in a _different_ Runfile.
_Runfile_
```
## defined in Runfile
command1:
echo command1 from RunfileINCLUDE Runfile-include
## defined in Runfile
command2:
echo command2 from Runfile
```_Runfile-include_
```
## defined in Runfile-include
command1:
echo command1 from Runfile-include## defined in Runfile-include
command2:
echo command2 from Runfile-include
```_list commands_
```
$ run listCommands:
...
command1 defined in Runfile-include
command2 defined in Runfile
```Notice that the _included_ runfile overrides `command1`, but the _primary_ runfile overrides `command2`.
##### Cannot Re-Register Command In Same Runfile
Run will error when attempting to register a command multiple times within the _same_ Runfile:
_Runfile_
```
hello-world:
echo "Hello, world"hello-world:
echo "Hello, world"
```_list commands_
```
$ run listrun: Runfile: command hello-world defined multiple times in the same file: lines 1 and 4
```##### Overrides Are Case-Insensitive
Run's override matching is case-insensitive:
_Runfile_
```
## defined in Runfile
command1:
echo command1 from Runfileinclude Runfile-include
```_Runfile-include_
```
## defined in Runfile-include
COMMAND1:
echo command1 from Runfile-include
```_list commands_
```
$ run listCommands:
...
command1 defined in Runfile-include
```Notice that `COMMAND1` from the _included_ runfile overrides `command1` from the _primary_ runfile.
###### First Registered Command Defines Case For Help
Run keeps track of the original case used when a command is first registered, and uses it when displaying help:
_Runfile_
```
## defined in Runfile
COMMAND1:
echo command1 from Runfileinclude Runfile-include
```_Runfile-include_
```
## defined in Runfile-include
command1:
echo command1 from Runfile-include
```_list commands_
```
$ run listCommands:
...
COMMAND1 defined in Runfile-include
```Notice that the displayed name comes from the original registration in the _primary_ runfile.
##### First Registered Command Defines Default Documentation
Run keeps track of the title & description when a command is first registered, and uses it if an overriding command does not define its own documentation:
_Runfile_
```
## title defined in Runfile
command1:
echo command1 from Runfileinclude Runfile-include
```_Runfile-include_
```
command1:
echo command1 from Runfile-include
```_list commands_
```
$ run listCommands:
...
commmand1 title defined in Runfile
```_command output_
```
$ run command1command1 from Runfile-include
```Notice that, even though `command1` from the _included_ runfile was invoked, the displayed title comes from the original registration in the _primary_ runfile.
##### Commands Are Listed In The Order They Are Registered
Run keeps track of the _order_ in which commands are registered, and maintains that order even if a command is later overridden:
_Runfile_
```
## defined in Runfile
command1:
echo command1 from Runfile## defined in Runfile
command2:
echo command2 from Runfile## defined in Runfile
command3:
echo command3 from Runfileinclude Runfile-include
```_Runfile-include_
```
## defined in Runfile-include
command2:
echo command2 from Runfile-include
```_list commands_
```
$ run listCommands:
...
command1 defined in Runfile
command2 defined in Runfile-include
command3 defined in Runfile
```Notice that `command2` is still shown _between_ `command1` and `command3`, matching the order in which it was originally registered.
-------------------
### Includes - .ENV`.env` files allow users to manage runfile configuration without modifying the Runfile directly.
Your Runfile can include .env files using the following syntax:
```
INCLUDE.ENV | "" | ''
```Simple example:
_Runfile.env_
```
HELLO=Newman
```_Runfile_
```
INCLUDE.ENV Runfile.env##
# export HELLO
hello:
echo "Hello, ${HELLO:-World}"
```_output_
```
$ run helloHello, Newman
```*Notes:*
* Variables are immediately available, as if they had been defined in the same place in the Runfile.
* Variables are not automatically exported.
* Run uses the [subosito/gotenv](https://github.com/subosito/gotenv) library to parse command output
* `#` comments are supported and will be safely ignored
* `export` keyword is optional and is (currently) ignored - This may be addressed in a future release
* Simple variable references in assignments are supported, **but** variables defined _within_ your Runfile are not (currently) accessible - This may be addressed in a future release
* Visit the [gotenv project page](https://github.com/subosito/gotenv) to learn more about which `.env` features are supported#### File(s) Not Found
By default, Run considers it OK no .env file is found (using either a single filename or a globbing pattern).
To force an error if no file(s) are found, use `!`:
_Runfile_
```
INCLUDE.ENV ! Runfile-might-not-exist.env # ERROR if no file(s) found
```--------------------------------------
### Invoking Other Commands & Runfiles#### RUN / RUN.AFTER / RUN.ENV Actions
You can invoke other commands (with arguments) from your Runfile before or after your command executes:
_Runfile_
```
##
# RUN hello "Newman"
# RUN.AFTER goodbye
test:
echo "How are you?"hello:
echo "Hello, ${1:-World}"goodbye:
echo "Goodbye, now"
```_output_
```
$ run testHello, Newman
How are you?
Goodbye, now
```*Note*: Any standard variable assignment value can be used (quoted strings, variable references, etc)
##### Exported Variables
Your command's exported environment variables are also exported to the invoked command:
_exported variable example_
```
##
# EXPORT NAME := "Newman"
# RUN hello
test:
echo "Goodbye, now"hello:
echo "Hello, ${NAME:-world}"
```_output_
```
$ run testHello, Newman
Goodbye, now
```*Notes*:
* `RUN.BEFORE` is also supported, and behaves just like `RUN`
* Commands are invoked in the order they are defined
* Your command only runs if all previous RUN commands return exit code zero (0)
* _After_ commands only run if your command returns exit code zero (0)
* Execution halts if *any* RUN returns a non-zero exit code
* You cannot invoke _builtin_ commands (help, version, etc)#### Setting Variables via RUN.ENV
A common occurrence in Runfiles is to have a central command which computes a set of variables, which is then invoked by multiple other commands that need to use those variables:
_eval example_
```
##
# export .RUN, .RUNFILE
test:
eval $( "$RUN" newman )
echo "Hello, ${HELLO:-World}"## Generates script suitable for 'eval' by caller
newman:
echo "HELLO=Newman"
```This technique works well, but Run also supports a similar feature using `RUN.ENV`:
_run.env example_
```
##
# RUN.ENV newman
# ASSERT [ -n "${HELLO}" ] "HELLO not defined"
test:
echo "Hello, ${HELLO:-World}"## Generates output compatible with simplified .env assignments
newman:
echo "# Let's say hi to Newman"
echo "export HELLO=Newman"
```_output_
```
$ run newman# Let's say hi to Newman
export HELLO=Newman
``````
$ run testHello, Newman
```*Notes*:
* `RUN.ENV` commands are run **after** EXPORTS
* `RUN.ENV` commands are run **before** ASSERTS
* Commands invoked via `RUN.ENV` are expected to generate _relatively_ simple variable assignments
* Run uses the [subosito/gotenv](https://github.com/subosito/gotenv) library to parse command output
* `#` comments are supported and will be safely ignored
* `export` keyword is optional and will be safely ignored
* Simple variable references in assignments are supported, **but** variables defined _within_ your Runfile are not (currently) accessible - This may be addressed in a future release
* Visit the [gotenv project page](https://github.com/subosito/gotenv) to learn more about which `.env` features are supported#### .RUN / .RUNFILE Attributes
If you need more control while invoking other commands, Run makes it possible to invoke commands, or even other Runfiles, from _within_ your command script.
Run exposes the following attributes:
* `.RUN` - Absolute path of the run binary currently in use
* `.RUNFILE` - Absolute path of the current **primary** RunfileNOTE: Even from inside an [included](#includes) Runfile, `.RUNFILE` will always reference the _primary_ Runfile
Your command script can use these to invoke other commands:
_Runfile_
```
##
# EXPORT .RUN, .RUNFILE
test:
"${RUN}" hellohello:
echo "Hello, World"
```_output_
```
$ run testHello, World
```-----------------------------
### Hidden / Private Commands#### Hidden Commands
You can mark a command as _Hidden_ using a leading `.`:
_hidden command example_
```
##
# Prints 'Hello, Newman', then 'Goodbye, now'
# RUN hello Newman
test:
echo "Goodbye, now"## Hello command is hidden
.hello:
echo "Hello, ${1:-world}"
```Hidden commands don't show up when listing commands:
_list commands_
```
$ run listCommands:
...
test Prints 'Hello, Newman', then 'Goodbye, now'
```But they can still be invoked by using their full name, with `.`:
_run hidden command_
```
$ run .helloHello, world
```#### Private Commands
You can mark a command as _Private_ using a leading `!`:
_private command example_
```
##
# Prints 'Hello, Newman', then 'Goodbye, now'
# RUN hello Newman
test:
echo "Goodbye, now"## Hello command is private
!hello:
echo "Hello, ${1:-world}"
```Private commands don't show up when listing commands:
_list commands_
```
$ run listCommands:
...
test Prints 'Hello, Newman', then 'Goodbye, now'
```And they cannot be invoked from outside the Runfile:
_try to run private command_
```
$ run hellorun: command not found: hello
$ run '!hello'
run: command not found: !hello
```
-----------------
### Script ShellsRun's default shell is `'sh'`, but you can specify other shells.
All the standard shells should work.
#### Per-Command Shell Config
Each command can specify its own shell:
```
##
# Hello world example.
# NOTE: Requires ${.SHELL}
hello (bash):
echo "Hello, world"
```#### Global Default Shell Config
You can set the default shell for the entire runfile:
_Runfile_
```
# Set default shell for all actions
.SHELL = bash##
# Hello world example.
# NOTE: Requires ${.SHELL}
hello:
echo "Hello, world"
```#### Other Executors
You can even specify executors that are not technically shells.
##### Python Example
_Runfile_
```
## Hello world python example.
hello (python):
print("Hello, world from python!")
```##### Script Execution : env
Run executes scripts using the following command:
```
/usr/bin/env $SHELL $TMP_SCRIPT_FILE [ARG ...]
```Any executor that is on the `PATH`, can be invoked via `env`, and takes a filename as its first argument should work.
#### Custom `#!` Support
Run allows you to define custom `#!` lines in your command script:
##### C Example
Here's an example of running a `c` program from a shell script using a custom `#!` header:
_Runfile_
```
##
# Hello world c example using #! executor.
# NOTE: Requires gcc
hello:
#!/usr/bin/env sh
sed -n -e '7,$p' < "$0" | gcc -x c -o "$0.$$.out" -
$0.$$.out "$0" "$@"
STATUS=$?
rm $0.$$.out
exit $STATUS
#includeint main(int argc, char **argv)
{
printf("Hello, world from c!\n");
return 0;
}
```##### Script Execution: Direct
*NOTE:* The `#!` executor does not use `/user/bin/env` to invoke your script. Instead, it attempts to make the temporary script file executable then invoke it directly.
-----------------
### Misc Features#### Ignoring Script Lines
You can use a `#` on the first column of a command script to ignore a line:
_Runfile_
```
hello:
# This comment WILL be present in the executed command script
echo "Hello, Newman"
# This comment block WILL NOT be present in the executed command script
# echo "Hello, World"
echo "Goodbye, now"
```*Note:* Run detects and skips these comment lines when parsing the runfile, so the `#` will work regardless of what language the script text is written in (i.e even if the target language doesn't support `#` for comments).
----------------
## Special Modes### Shebang Mode
In `shebang mode`, you make your runfile executable and invoke commands directly through it:
_runfile.sh_
```
#!/usr/bin/env run shebang## Hello example using shebang mode
hello:
echo "Hello, world"```
_output_
```
$ chmod +x runfile.sh
$ ./runfile.sh helloHello, world
```
#### Filename used in help text
In shebang mode, the runfile filename replaces references to the `run` command:_shebang mode help example_
```
$ ./runfile.sh helpUsage:
runfile.sh [option ...]
(run )
or runfile.sh list
(list commands)
or runfile.sh help
(show help for )
...
```_shebang mode list example_
```
$ ./runfile.sh listCommands:
list (builtin) List available commands
help (builtin) Show help for a command
run-version (builtin) Show run version
hello Hello example using shebang mode
```#### Version command name
In shebang mode, the `version` command is renamed to `run-version`. This enables you to create your own `version` command, while still providing access to run's version info, if needed.
_runfile.sh_
```
#!/usr/bin/env run shebang## Show runfile.sh version
version:
echo "runfile.sh v1.2.3"## Hello example using shebang mode
hello:
echo "Hello, world"
```_shebang mode version example_
```
$ ./runfile.sh list
...
run-version (builtin) Show Run version
version Show runfile.sh version
...$ ./runfile.sh version
runfile.sh v1.2.3
$ ./runfile.sh run-version
runfile.sh is powered by run v0.0.0. learn more at https://github.com/TekWizely/run
```-------------
### Main ModeIn main mode you use an executable runfile that consists of a single command, aptly named `main`:
_runfile.sh_
```
#!/usr/bin/env run shebang## Hello example using main mode
main:
echo "Hello, world"
```In this mode, run's built-in commands are disabled and the `main` command is invoked directly:
_output_
```
$ ./runfile.shHello, world
```#### Filename used in help text
In main mode, the runfile filename replaces references to `command` name:_main mode help example_
```
$ ./runfile.sh --helprunfile.sh:
Hello example using main mode```
#### Help options
In main mode, help options (`-h` & `--help`) are automatically configured, even if no other options are defined.
This means you will need to use `--` in order to pass options through to the main script.
------------------------------------------
## Using direnv to auto-configure $RUNFILEA nice hack to make executing run tasks within your project more convenient is to use [direnv](https://direnv.net/) to autoconfigure the `$RUNFILE` environment variable:
_create + edit + activate rc file_
```
$ cd ~/my-project
$ direnv edit .
```_edit .envrc_
```
export RUNFILE="${PWD}/Runfile"
```Save & exit. This will activate _immediately_ but will also activate whenever you `cd` into your project's root folder.
```
$ cd ~/my-projectdirenv: export +RUNFILE
```_verify_
```
$ echo $RUNFILE/home/user/my-project/Runfile
```With this, you can execute `run ` from anywhere in your project.
-------------
## Installing### Via Bingo
[Bingo](https://github.com/TekWizely/bingo) makes it easy to install (and update) golang apps directly from source:
_install_
```
$ bingo install github.com/TekWizely/run
```_update_
```
$ bingo update run
```### Pre-Compiled Binaries
See the [Releases](https://github.com/TekWizely/run/releases) page as recent releases are accompanied by pre-compiled binaries for various platforms.
##### Not Seeing Binaries For Your Platform?
Run currently uses [goreleaser](https://goreleaser.com/) to generate release assets.
Feel free to [open an issue](https://github.com/TekWizely/run/issues/new) to discuss additional target platforms, or even create a PR against the [.goreleaser.yml](https://github.com/TekWizely/run/blob/master/.goreleaser.yml) configuration.
### Brew
#### Brew Core
Run is now available on homebrew core:
* https://formulae.brew.sh/formula/run
_install run via brew core_
```
$ brew install run
```#### Brew Tap
In addition to being available in brew core, I have also created a tap to ensure the latest version is always available:
* https://github.com/TekWizely/homebrew-tap
_install run directly from tap_
```
$ brew install tekwizely/tap/run
```_install tap to track updates_
```
$ brew tap tekwizely/tap$ brew install run
```#### NIX
For Nix users, a package is available on nixpkgs:
* [Nix package for Run](https://search.nixos.org/packages?show=run&from=0&size=1&sort=relevance&type=packages&query=run)
Supported Platforms:
* x86_64-darwin
* aarch64-darwin
* aarch64-linux
* i686-linux
* x86_64-linux_install run on NixOS_
```bash
$ nix-env -iA nixos.run
```_install run on non-NixOs_
```bash
$ nix-env -iA nixpkgs.run
```#### AUR
For Archlinux users, a package is available on the AUR:
* https://aur.archlinux.org/packages/run-git
_install run from AUR using yay_
```bash
$ yay -S run-git
```
#### NPM / YarnNPM & Yarn users can install run via the `@tekwizely/run` package:
* [Run NPM Package Page](https://www.npmjs.com/package/@tekwizely/run)
```
$ npm i '@tekwizely/run'$ yarn add '@tekwizely/run'
```### Other Package Managers
I hope to have other packages available soon and will update the README as they become available.---------------
## ContributingTo contribute to Run, follow these steps:
1. Fork this repository.
2. Create a branch: `git checkout -b `.
3. Make your changes and commit them: `git commit -m ''`
4. Push to the original branch: `git push origin /`
5. Create the pull request.Alternatively see the GitHub documentation on [creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request).
----------
## ContactIf you want to contact me you can reach me at [email protected].
----------
## LicenseThe `tekwizely/run` project is released under the [MIT](https://opensource.org/licenses/MIT) License. See `LICENSE` file.
-------------------------------------
## Just Looking for Bash Arg Parsing?If you happened to find this project on your quest for bash-specific arg parsing solutions, I found this fantastic S/O post with many great suggestions:
* [Parsing Command-Line Arguments in Bash (S/O)](https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash)
---------------
## Contributors ✨Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
chabad360
📖 🚇 🐛
Dawid Dziurla
🚇
Bob "Wombat" Hogg
📖
Gys
🐛
Robin Burchell
💻
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!