5 releases
Uses new Rust 2024
new 0.1.4 | Apr 19, 2025 |
---|---|
0.1.3 | Apr 18, 2025 |
0.1.2 | Apr 17, 2025 |
0.1.1 | Apr 16, 2025 |
0.1.0 | Apr 15, 2025 |
#182 in Development tools
263 downloads per month
34KB
541 lines
jiu
A minimal command runner.
🤔 Comparison
This tool is heavily inspired by just
, but is fundamentally different. To summarize:
- Pro: It handles arguments correctly, and without any ambiguity
just
could cause argument splitting issues- Although there are workarounds, corner cases still exist
- Pro: Is independent of shell
- Con: But at the cost of much less customization and features
📥 Installation
Using binstall
cargo binstall jiu
Downloading from Releases
Navigate to the Releases page and download respective binary for your platform. Make sure to give it execute permissions.
Compiling from Source
cargo install jiu
💡 Examples
See .jiu.toml
for a simple example used in this repository, or the . Here's an example involving complex arguments:tests
directory for more complex examples
jiu dummy 1 "2" '"3"' " 4" "" "5 6"
Which will invoke dummy.sh
, printing arguments it received:
TERM = xterm-256color
Arguments:
1
2
"3"
4
5 6
Note that the arguments are all handled correctly.
📖 Usage
Configuration
The config file is a simple TOML file named .jiu.toml
. The format is as follows:
description = "`jiu`: A minimal command runner." # Description of the configuration (Optional)
# - Will be displayed when listing recipes
# - To add some colors to the dull description, use ANSI escape codes like:
# description = "\u001b[1;36mjiu\u001b[22;39m: A minimal command runner."
default = "run" # Default recipe to run when invoked without any arguments (Optional)
# - List all recipes if empty
# - Default recipe must be able to accept no arguments
[[recipes]]
names = ["run", "r"] # Names of the recipe (Required)
description = "Compile and run" # Description of the recipe (Optional)
arguments = ["*rest"] # Arguments to the recipe (Optional)
command = ["cargo", "run", "--", ["*rest"]] # Command to run (Required)
# ...More recipes
Names
The names
field is a list of names that the recipe can be called with. It should contain at least one name, otherwise the recipe will never be matched. Each name:
- Should be unique across all recipes, otherwise only the first one will be matched.
- Should not contain spaces.
- Should not start with special characters, especially
-
, which would be interpreted as an option. - Should not be empty.
Where "should" means that it is a good practice to follow, but not explicitly enforced. For example, you can have a recipe with the name my recipe
, but to call it you would have to escape the space or use quotes, which would be inconvenient.
Arguments
The arguments
field is a list of arguments that the recipe accepts. It should be a list of strings, where each string represents an argument. An argument is made up of an optional leading symbol and a name.
Types
The type of the argument is determined by the leading symbol, which can be one of the following:
*
: A variadic argument. This means that the argument can accept zero or more values.+
: A required variadic argument. This means that the argument must accept one or more values.?
: An optional argument. This means that the argument can accept zero or one value.
If the leading symbol is omitted, the argument is treated as a required argument.
Greedy Matching
Note
This behavior may be changed in the future.
The *
and +
arguments are greedy, meaning that they will consume all remaining arguments. For example, if you have a recipe with the following arguments:
arguments = ["*arg0", "*arg1"]
Then *arg1
will always be empty, since *arg0
will consume all remaining arguments. Also consider:
arguments = ["*arg0", "arg1"]
In this case, *arg0
will consume all remaining arguments, leaving required argument arg1
empty. So jiu
will return an error, although the arguments can be interpreted without ambiguity.
Also be careful when working with optional arguments, since they share the same greedy behavior. For example:
arguments = ["?arg0", "arg1"]
When a single argument is passed, ?arg0
will consume it, leaving arg1
empty. So this will also cause an error.
Command
The command
field is a list representing the command to run, and is made up of strings and arrays of length 1. Each string is treated as a literal, while each array is treated as a placeholder.
The placeholders are interpolated with concrete values when the recipe is run. After interpolation, the command is executed in the directory of the config file.
A placeholder can be one of the following:
$VAR
: An environment variable. This will be replaced with the value of the environment variableVAR
.- If the variable is not set, an error will be returned.
- If the variable is empty, it will still be passed as an empty argument.
- Others: An argument. This will be replaced with the value of the argument. If the argument is variadic, it will be replaced with all values of the argument.
Running
To run a recipe, simply call jiu
with the name of the recipe and arguments for the recipe:
jiu <recipe> [<args>...]
Debugging
Run with environment variable JIU_DEBUG
set to enable debug mode. In bash, you can do this with:
JIU_DEBUG=1 jiu <recipe> [<args>...]
Which would provide additional information for debugging purposes.
✅ TODO
-
env
field on recipes and global - Set working directories
- Where the config file is located (default)
- Where the command is invoked
- Custom working directory, relative to the config file
- Options
-
jiu -l
/jiu --list
: List recipes -
jiu -h
/jiu --help
: Help message -
jiu -v
/jiu --version
: Version
-
- Interpolating environment variables in commands (
["$VAR"]
)
🎉 Credits
just
- where the inspiration came from
Dependencies
~0.6–1.5MB
~33K SLoC