Skip to content

Task runner. It provides useful help messages and supports interactive prompts and validation of arguments

License

Notifications You must be signed in to change notification settings

suzuki-shunsuke/cmdx

Repository files navigation

cmdx

Build Status Go Report Card GitHub last commit License

Task runner. It provides useful help messages and supports interactive prompts.

Overview

cmdx is the task runner. Using cmdx you can manage tasks for your project such as test, build, format, lint, and release.

For example, This is the tasks for cmdx itself.

$ cmdx -l
init, i - setup git hooks
coverage, c - test a package (fzf is required)
test, t - test
fmt - format the go code
vet, v - go vet
lint, l - lint the go code
release, r - release the new version
durl - check dead links (durl is required)
ci-local - run the Drone pipeline at localhost (drone-cli is required)

$ cmdx help release
NAME:
   cmdx release - release the new version

USAGE:
   cmdx release <version>

DESCRIPTION:
   release the new version
ARGUMENTS:
   version

cmdx searches the configuration file from the current directory to the root directory recursively, and runs the task at the directory where the configuration file exists.

Features

  • Easy to install (one binary)
  • Parse the flag and positional arguments
  • Useful help messages
  • Interactive prompt by AlecAivazis/survey
  • Validate requirements
  • Validate flag and positional arguments
  • Timeout
  • Bash and Zsh completion
  • Nested tasks (Sub tasks)

Install

cmdx is a single binary written in Go. So you only need to put the executable binary into $PATH.

  1. Homebrew
brew install suzuki-shunsuke/cmdx/cmdx
  1. aqua
aqua g -i suzuki-shunsuke/cmdx
  1. Download a prebuilt binary from GitHub Releases and install it into $PATH

  2. Go

go install github.com/suzuki-shunsuke/cmdx/cmd/cmdx@latest

Getting Started

Create the configuration file.

$ cmdx --init
$ cat .cmdx.yaml

Edit the configuration file and register the task hello.

$ vi .cmdx.yaml
$ cat .cmdx.yaml
---
tasks:
- name: hello
  description: hello command
  usage: hello command
  flags:
  - name: source
    short: s
    usage: source file path
    required: true
    input_envs:
    - NAME
  - name: switch
    type: bool
  args:
  - name: name
    usage: name
    default: bb
  environment:
    FOO: foo
  script: "echo hello {{.source}} $NAME {{if .switch}}on{{else}}off{{end}} {{.name}} $FOO" # use Go's text/template

Output the help.

$ cmdx help
NAME:
   cmdx - task runner

USAGE:
   cmdx [global options] command [command options] [arguments...]

VERSION:
   0.2.2

AUTHOR:
   Shunsuke Suzuki

COMMANDS:
   hello     hello command
   help, h   Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --config value, -c value  configuration file path
   --name value, -n value    configuration file name. The configuration file is searched from the current directory to the root directory recursively
   --init, -i                create the configuration file
   --list, -l                list tasks
   --help, -h                show help
   --version, -v             print the version

List tasks.

$ cmdx -l
hello - hello command

Run the task hello.

$ cmdx hello -s README
+ echo hello README $NAME off bb $FOO
hello README README off bb foo

$ cmdx hello -s README --switch
+ echo hello README $NAME on bb $FOO
hello README README on bb foo
$ cmdx hello --target foo
target: foo

Example

In order to understand cmdx, it is good to execute cmdx command actually. We prepare the sample configuration file for you to execute cmdx.

Please see examples.

Configuration

path type description required default
.timeout timeout the task command timeout false
.input_envs []string default environment variable binding false []
.script_envs []string default environment variable binding false []
.environment map[string]string top level environment variables false {}
.quiet bool Default configuration whether the content of script is outputted false
.tasks []task the list of tasks true
task.name string the task name true
task.short string the task short name false
task.description string the task description false ""
task.usage string the task usage false ""
task.flags []flag the task flag arguments false []
task.args []arg the task positional arguments false []
task.input_envs []string task level environment variable binding false []
task.script_envs []string task level environment variable binding false []
task.environment map[string]string the task's environment variables false {}
task.script string the task command. This is run by sh -c true
task.quiet bool task level default configuration whether the content of script is outputted false
task.shell []string shell command to run the script ["sh", "-c"]
task.timeout timeout the task command timeout false
task.require require requirement of task false {}
task.tasks []task sub tasks false []
require.exec []stringArray required executable files false []
require.environment []stringArray required environment variables false []
stringArray array whose element is string or array of string
timeout.duration int the task command timeout (second) false 36000 (10 hours)
timeout.kill_after int the duration the kill signal is sent after timeout.duration false 0, which means the command isn't killed
flag.name string the flag name true
flag.short string the flag short name false
flag.usage string the flag usage false ""
flag.default string the flag argument's default value false ""
flag.input_envs []string flag level environment variable binding false []
flag.script_envs []string flag level environment variable binding false []
flag.type string the flag type. Either "string" or "bool" false "string"
flag.required bool whether the flag argument is required false false
flag.validate []validate parameters to validate the value of flag false []
flag.prompt prompt prompt false prompt is disabled
prompt.type string prompt type true
prompt.message string prompt message false flag.name or arg.name
prompt.help string prompt help false
prompt.options []string entries of select or multi_select prompt true if the prompt type is select or multi_select
arg.name string the positional argument name true
arg.usage string the positional argument usage false ""
arg.default string the positional argument's default value false ""
arg.input_envs []string the positional argument level environment variable binding false []
arg.script_envs []string the positional argument level environment variable binding false []
arg.required bool whether the argument is required false false
arg.prompt prompt prompt false prompt is disabled
arg.validate []validate parameters to validate the value of arg false []
validate.type string value type (email, url, int) false
validate.regexp string the regular expression false
validate.min_length int the minimum string length false
validate.max_length int the maximum string length false
validate.prefix string the prefix false
validate.suffix string the suffix false
validate.contain string the string which the value should contain false
validate.enum []string enum false

script

task.script is the task command. This is parsed by Go's text/template package. sprig functions can be used. The value of the flag and positional argument can be referred by the argument name.

For example,

# refer the value of the argument "source"
script: "echo {{.source}}"

Multiple lines

script: |
  echo foo
  echo bar

If the positional argument is optional and the argument isn't passed and the default value isn't set, the value is an empty string "".

And some special variables are defined.

name type description
_builtin.args []string the list of positional arguments which aren't defined by the configuration args
_builtin.args_string string the string which joins _builtin.args by the space " "
_builtin.all_args []string the list of all positional arguments
_builtin.args_string string the string which joins _builtin.all_args by the space " "

input_envs, script_envs

input_envs is a list of environment variables that are bound to the variable.

tasks:
- name: foo
  script: "echo {{.source}}"
  args:
  - name: source
    input_envs:
    - command_env
$ COMMAND_ENV=zzz cmdx foo
+ echo zzz
zzz

script_envs is a list of environment variables which the variable is bound to.

tasks:
- name: foo
  script: "echo $COMMAND_ENV"
  args:
  - name: source
    script_envs:
    - command_env
$ cmdx foo zzz
+ echo $COMMAND_ENV
zzz

timeout

cmdx supports the configuration about the timeout of the task.

  1. send SIGINT after timeout.duration seconds (default 36,000 seconds)
  2. if timeout.kill_after isn't 0, send SIGKILL after timeout.duration + timeout.kill_after seconds. By default timeout.kill_after is 0 so SIGKILL isn't sent

For example, the following task foo's timeout is 3 seconds.

tasks:
- name: foo
  script: sleep 100
  timeout:
    duration: 3
$ cmdx foo
+ sleep 100
the command is timeout: 3 sec

The task timeout configuration inherits the top level timeout configuration.

timeout:
  duration: 3
tasks:
- name: foo # the timeout.duration is 3
  script: sleep 100

require

task.require is the requirement to run the task.

require.exec

For example, in the following example both of curl and wget is required.

tasks:
- name: foo
  script: curl http://example.com
  require:
    exec:
    - curl
    - wget

If curl isn't installed, the task is failed.

$ cmdx foo
curl is required

Note that the shell's alias is ignored. Internally, exec.LookupPath is used.

In the following example, either curl or wget is required.

tasks:
- name: foo
  script: curl http://example.com
  require:
    exec:
    - - curl
      - wget
$ cmdx foo
one of the following is required: curl, wget

require.environment

require.environment is the required environment variables. Note that if the value of the environment variable is an emtpy string, the environment variable is treated as unset.

tasks:
- name: foo
  script: curl http://example.com
  require:
    environment:
    - GITHUB_TOKEN
$ cmdx foo
the environment variable 'GITHUB_TOKEN' is required
tasks:
- name: foo
  script: curl http://example.com
  require:
    environment:
    - - GITHUB_TOKEN
      - GITHUB_ACCESS_TOKEN
$ cmdx foo
one of the following environment variables is required: GITHUB_TOKEN, GITHUB_ACCESS_TOKEN

validation

cmdx supports to validate args and flags.

For example,

# .cmdx.yaml
tasks:
- name: hello
  script: echo hello
  args:
  - name: age
    validate:
    - type: int
$ cmdx hello foo
age is invalid: must be int: foo

quiet

By default cmdx outputs the content of task's script when the task is run.

In case of the following example, + echo hello is outputted.

# .cmdx.yaml
tasks:
- name: hello
  script: echo hello
$ cmdx hello
+ echo hello
hello

You can suppress the output by --quiet (-q) option.

# BAD: cmdx hello -q
$ cmdx -q hello
hello

And you can change the default configuration of quiet at the global level or the task level.

# .cmdx.yaml
tasks:
- name: hello
  script: echo hello
  quiet: true  # task level
# .cmdx.yaml
quiet: true # global level
tasks:
- name: hello
  script: echo hello
  quiet: true

The priority is

  1. command line flag
  2. task level configuration
  3. global level configuration

If the quiet is enabled by configuration but you want to output script, please set the flag -q=false.

# "=" is needed
$ cmdx -q=false hello
+ echo hello
hello

prompt

cmdx supports the interactive prompt by AlecAivazis/survey. cmdx supports the following prompt types.

  • input
  • multiline
  • password
  • confirm
  • select
  • multi_select
  • editor

About prompt type, please see AlecAivazis/survey's document.

value source priority

  1. command line arguments
  2. environment variable (input_envs)
  3. prompt (prompt isn't launched if the value is set by command line argument or environment variable)
  4. default value

shell

This is an advanced feature.

By default task's script is run by the command sh -c. You can change the command by the shell option.

For example, you can run Python script.

- name: hello
  shell:
  - python
  - -c
  script: |
    print("hello")

And you can run the shell script in the container.

- name: hello
  shell:
  - docker
  - exec
  - -ti
  - foo
  - sh
  - -c
  script: |
    whoami
    read -p "name?" name
    echo "name: $name"

Bash (Zsh) Completion

https://github.com/urfave/cli/blob/477292c8d462a3f51cd18bc77c0542193a62274d/docs/v2/manual.md#bash-completion

cmdx supports Bash (Zsh) Completion powered by urfave/cli.

We test the completion with Zsh, but we don't test the completion with other shell.

To enable the completion, you have to load a shell script. For detail, please see the document of urfave/cli.

Please set cmdx to PROG

Sub tasks

cmdx supports sub tasks.

For example,

tasks:
- name: admin
  usage: administrator feature
  tasks:
  - name: cluster
    usage: manage clusters
    tasks:
    - name: create
      usage: create a cluster
      script: echo "create a cluster"
$ cmdx admin cluster create
+ echo "create a cluster"
create a cluster

The following attributes are inherited from the parent tasks.

  • input_envs
  • script_envs
  • quiet
  • environment
  • timeout
  • requires

For example,

tasks:
- name: admin
  usage: administrator feature
  require:
    exec:
    - yamllint
  tasks:
  - name: cluster
    usage: manage clusters
    tasks:
    - name: create
      usage: create a cluster
      script: echo "create a cluster"
$ cmdx admin cluster create
yamllint is required

We can't set both task.script and task.tasks.

For example,

tasks:
- name: hello
  script: echo hello
  tasks:
  - name: world
    script: echo "hello world"
$ cmdx hello
please fix the configuration file: the task `hello` is invalid. when sub tasks are set, 'script' can't b
e set

Contributing

Please see the CONTRIBUTING.md.

License

MIT

About

Task runner. It provides useful help messages and supports interactive prompts and validation of arguments

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published