README
¶
lets
Simple CLI task runner
Lets takes the best from Makefile and bash and presents you a simple yet powerful tool for defining and running cli tasks and commands.
Just describe your commands in lets.yaml and lets will do the rest.
Very alpha software. Things may and will change/break
- Install
- Getting Started
- Creating new config
- Lets directory
- Config
- Env
- Build
- Test
- Release
- Versioning
- Completion
Install
Shell script:
This will install lets binary to /usr/local/bin directory. But you can change install location to any directory you want
sudo curl -sfL https://raw.githubusercontent.com/lets-cli/lets/master/install.sh | sudo sh -s -- -b /usr/local/bin
Binary (Cross-platform):
Download the version you need for your platform from Lets Releases.
Once downloaded, the binary can be run from anywhere.
Ideally, you should install it somewhere in your PATH for easy use. /usr/local/bin is the most probable location.
Arch Linux:
You can get binary release from https://aur.archlinux.org/packages/lets-bin/
If you are using yay as AUR helper:
yay -S lets-bin
Also you can get bleeding edge version from https://aur.archlinux.org/packages/lets-git/
yay -S lets-git
Getting Started
If you already have lets.yaml then just go to that directory and run lets to see all available commands.
If you start from scratch and want to create a new lets.yaml see Creating new config
Config lookup
lets will be looking for a config starting from where you call lets and all the way up to the /.
For example:
cd /home/me
touch lets.yaml
mkdir ./project
cd ./project
lets # it will use lets.yaml at /home/me/lets.yaml
touch lets.yaml
lets # it will use lets.yaml right here (at /home/me/project/lets.yaml)
Creating new config
- Create
lets.yamlfile in your project directory - Add commands to
lets.yamlconfig. Config reference
commands:
echo:
description: Echo text
cmd: |
echo "Hello"
run:
description: Run some command
options: |
Usage: lets run [--debug] [--level=<level>]
Options:
--debug, -d Run with debug
--level=<level> Log level
cmd: |
env
- Run commands
lets echo
# will print Hello
lets run --debug --level=info
# will print
# LETSOPT_DEBUG=true
# LETSOPT_LEVEL=info#
# LETSCLI_DEBUG=--debug
# LETSCLI_LEVEL=--level info
Lets directory
At first run lets will create .lets directory in the current directory.
lets uses .lets to store some specific data such as checksums, etc.
It's better to add .lets to your .gitignore end exclude it in your favorite ide.
lets.yaml
Config schema
Top-level directives:
version
key: version
type: semver string
Specify minimum required lets version to run this config.
Example:
version: '0.0.20'
shell
key: shell
type: string
required: true
Specify shell to use when running commands
Example:
shell: bash
global env
key: env
type: string
Specify global env for all commands.
Example:
shell: bash
env:
MY_GLOBAL_ENV: "123"
global eval_env
key: env
type: string
Specify global eval_env for all commands.
Example:
shell: bash
eval_env:
CURRENT_UID: echo "`id -u`:`id -g`"
mixins
key: mixins
type: list of string
Allows to split lets.yaml into mixins (mixin config files).
To make lets.yaml small and readable its convenient to split main config into many smaller ones and include them
Example:
# in lets.yaml
...
shell: bash
mixins:
- test.yaml
commands:
echo:
cmd: echo Hi
# in test.yaml
...
commands:
test:
cmd: echo Testing...
And lets test works fine.
commands
key: commands
type: mapping
required: true
Mapping of all available commands
Example:
commands:
test:
description: Test something
Command directives:
descriptionkey: description
type: string
Short description of command - shown in help message
Example:
commands:
test:
description: Test something
cmdkey: cmd
type:
- string
- array of strings
- map of string => string
Actual command to run in shell.
Can be either:
- a string (also a multiline string)
- an array of strings - it will allow to append all arguments passed to command as is (see bellow)
- a map of string => string - this will allow run commands in parallel
Example single string:
commands:
test:
description: Test something
cmd: go test ./... -v
Example multiline string:
commands:
test:
description: Test something
cmd: |
echo "Running go tests..."
go test ./... -v
Example array of strings:
commands:
test:
description: Test something
cmd:
- go
- test
- ./...
When run with cmd as array of strings:
lets test -v
the -v will be appended, so the resulting command to run will be go test ./... -v
Example of map of string => string
commands:
run:
description: Test something
cmd:
app: npm run app
nginx: docker-compose up nginx
redis: docker-compsoe up redis
There are two flags --only and --exclude you can use with cmd map.
There must be used before command name:
lets --only app run
dependskey: depends
type: array of string
Specify what commands to run before the actual command. May be useful, when have one shared command.
For example, lets say you have command build, which builds docker image.
Example:
commands:
build:
description: Build docker image
cmd: docker build -t myimg . -f Dockerfile
test:
description: Test something
depends: [build]
cmd: go test ./... -v
fmt:
description: Format the code
depends: [build]
cmd: go fmt
build command will be executed each time you run lets test or lets fmt
optionskey: options
type: string (multiline string)
One of the most cool things about lets than it has built in docopt parsing.
All you need is to write a valid docopt for a command and lets will parse and inject all values for you.
More info http://docopt.org
When parsed, lets will provide two kind of env variabled:
LETSOPT_<VAR>LETSCLI_<VAR>
How does it work?
Lets see an example:
commands:
echo-env:
description: Echo env vars
options:
Usage: lets [--log-level=<level>] [--debug] <args>...
Options:
<args>... List of required positional args
--log-level,-l Log level
--debug,-d Run with debug
cmd: |
echo ${LETSOPT_ARGS}
app ${LETSCLI_DEBUG}
So here we have:
args - is a list of required positional args
--log-level - is a key-value flag, must be provided with some value
--debug - is a bool flag, if specified, means true, if no specified means false
In the env of cmd command there will be available two types of env variables:
lets echo-env --log-level=info --debug one two three
Parsed and formatted key values
echo LETSOPT_ARGS=${LETSOPT_ARGS} # LETSOPT_ARGS=one two three
echo LETSOPT_LOG_LEVEL=${LETSOPT_LOG_LEVEL} # LETSOPT_LOG_LEVEL=info
echo LETSOPT_DEBUG=${LETSOPT_DEBUG} # LETSOPT_DEBUG=true
Raw flags (useful if for example you wan to pass --debug as is)
echo LETSCLI_ARGS=${LETSCLI_ARGS} # LETSCLI_ARGS=one two three
echo LETSCLI_LOG_LEVEL=${LETSCLI_LOG_LEVEL} # LETSCLI_LOG_LEVEL=--log-level info
echo LETSCLI_DEBUG=${LETSCLI_DEBUG} # LETSCLI_DEBUG=--debug
envkey: env
type: mapping string => string
Env is as simple as it sounds. Define additional env for a commmand:
Example:
commands:
test:
description: Test something
env:
GO111MODULE: "on"
GOPROXY: https://goproxy.io
cmd: go build -o lets *.go
eval_envkey: eval_env
type: mapping string => string
Same as env but allows you to dynamically compute env:
Example:
commands:
test:
description: Test something
eval_env:
CURRENT_UID: echo "`id -u`:`id -g`"
CURRENT_USER_NAME: echo "`id -un`"
cmd: go build -o lets *.go
Value will be executed in shell and result will be saved in env.
checksumkey: checksum
type: array of string | mapping string => array of string
Checksum used for computing file hashed. It is useful when you depend on some files content changes.
In checksum you can specify:
- a list of file names
- a list of file regext patterns (parsed via go
path/filepath.Glob)
or
- a mapping where key is name of env variable and value is:
- a list of file names
- a list of file regext patterns (parsed via go
path/filepath.Glob)
Each time a command runs, lets will calculate the checksum of all files specified in checksum.
Result then can be accessed via LETS_CHECKSUM env variable.
If checksum is a mapping, e.g:
commands:
build:
checksum:
deps:
- package.json
doc:
- Readme.md
Resulting env will be:
LETS_CHECKSUM_DEPS- checksum of deps filesLETS_CHECKSUM_DOC- checksum of doc filesLETS_CHECKSUM- checksum of all checksums (deps and doc)
Checksum is calculated with sha1.
If you specify patterns, lets will try to find all matches and will calculate checksum of that files.
Example:
shell: bash
commands:
app-build:
checksum:
- requirements-*.txt
cmd: |
docker pull myrepo/app:${LETS_CHECKSUM}
docker run --rm myrepo/app${LETS_CHECKSUM} python -m app
persist_checksumkey: persist_checksum
type: bool
This feature is useful when you want to know that something has changed between two executions of a command.
persist_checksum can be used only if checksum declared for command.
If set to true, each run all calculated checksums will be stored to disk.
After each subsequent run lets will check if new checksum and stored checksum are different.
Result of that check will be exposed via LETS_CHECKSUM_CHANGED and LETS_CHECKSUM_[checksum-name]_CHANGED env variables.
IMPORTANT: New checksum will override old checksum only if cmd has exit code 0
Example:
commands:
build:
persist_checksum: true
checksum:
deps:
- package.json
doc:
- Readme.md
Resulting env will be:
-
LETS_CHECKSUM_DEPS- checksum of deps files -
LETS_CHECKSUM_DOC- checksum of doc files -
LETS_CHECKSUM- checksum of all checksums (deps and doc) -
LETS_CHECKSUM_DEPS_CHANGED- is checksum of deps files changed -
LETS_CHECKSUM_DOC_CHANGED- is checksum of doc files changed -
LETS_CHECKSUM_CHANGED- is checksum of all checksums (deps and doc) changed
Environment
lets has builtin environ variables.
LETS_DEBUG- enable debug messagesLETS_CONFIG- changes defaultlets.yamlfile path (e.g. LETS_CONFIG=lets.my.yaml)LETS_CONFIG_DIR- changes path to dir wherelets.yamlfile placedLETS_NO_COLOR_OUTPUT- disables colored output
Build
To build a binary:
go build -o lets *.go
To install in system
go build -o lets *.go && sudo mv ./lets /usr/local/bin/lets
Or if you already have lets installed in your system:
lets build-and-install
After install - check version of lets - lets --version - it should be development
It will install lets to /usr/local/bin/lets and set version to development with current tag and timestamp
Test
To run all tests:
lets test
To run unit tests:
lets test-unit
To get coverage:
lets coverage
To test lets output we using bats - bash automated testing:
lets test-bats
# or run one test
lets test-bats global_env.bats
Release
To release a new version:
lets release 0.0.<n> -m "implement some new feature"
This will create annotated tag with 0.0. and run git push --tags
Versioning
lets releases must be backward compatible. That means every new lets release must work with old configs.
For situations like e.g. new functionality, there is a version in lets.yaml which specifies minimum required lets version.
If lets version installed on user machine is less than the one specified in config it will show and error and ask user to upgrade lets version.
Completion
You can use Bash/Zsh/Oh-My-Zsh completion in you terminal
-
Bash
Add
source <(lets completion -s bash)to your~/.bashrcor~/.bash_profile -
Zsh/Oh-My-Zsh
There is a repo with zsh plugin with instructions
Documentation
¶
There is no documentation for this package.