{"id":13903099,"url":"https://github.com/suzuki-shunsuke/cmdx","last_synced_at":"2025-04-15T12:15:31.510Z","repository":{"id":39875497,"uuid":"203486960","full_name":"suzuki-shunsuke/cmdx","owner":"suzuki-shunsuke","description":"Task runner. It provides useful help messages and supports interactive prompts and validation of arguments","archived":false,"fork":false,"pushed_at":"2025-04-14T13:33:04.000Z","size":1609,"stargazers_count":38,"open_issues_count":5,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-15T12:15:03.124Z","etag":null,"topics":["cli","oss","task-runner"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/suzuki-shunsuke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-08-21T02:04:39.000Z","updated_at":"2025-04-14T13:33:07.000Z","dependencies_parsed_at":"2023-12-07T07:27:50.846Z","dependency_job_id":"8c1930b9-8e32-44d7-a26d-512245b08a05","html_url":"https://github.com/suzuki-shunsuke/cmdx","commit_stats":{"total_commits":1085,"total_committers":6,"mean_commits":"180.83333333333334","dds":0.5235023041474655,"last_synced_commit":"9a41fd3838cadd8dce6a57f7863d1ae9f2fd4e30"},"previous_names":[],"tags_count":45,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suzuki-shunsuke%2Fcmdx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suzuki-shunsuke%2Fcmdx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suzuki-shunsuke%2Fcmdx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suzuki-shunsuke%2Fcmdx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/suzuki-shunsuke","download_url":"https://codeload.github.com/suzuki-shunsuke/cmdx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249067779,"owners_count":21207396,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","oss","task-runner"],"created_at":"2024-08-06T22:01:37.039Z","updated_at":"2025-04-15T12:15:31.499Z","avatar_url":"https://github.com/suzuki-shunsuke.png","language":"Go","funding_links":[],"categories":["cli","Go"],"sub_categories":[],"readme":"# cmdx\n\n[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/suzuki-shunsuke/cmdx/main/LICENSE) | [Install](INSTALL.md)\n\nTask runner. It provides useful help messages and supports interactive prompts.\n\n## Overview\n\n`cmdx` is the task runner.\nUsing `cmdx` you can manage tasks for your project such as test, build, format, lint, and release.\n\nFor example, This is the tasks for `cmdx` itself.\n\n```console\n$ cmdx -l\ninit, i - setup git hooks\ncoverage, c - test a package (fzf is required)\ntest, t - test\nfmt - format the go code\nvet, v - go vet\nlint, l - lint the go code\nrelease, r - release the new version\ndurl - check dead links (durl is required)\nci-local - run the Drone pipeline at localhost (drone-cli is required)\n\n$ cmdx help release\nNAME:\n   cmdx release - release the new version\n\nUSAGE:\n   cmdx release \u003cversion\u003e\n\nDESCRIPTION:\n   release the new version\nARGUMENTS:\n   version\n```\n\n`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.\n\n## Features\n\n- Easy to install (one binary)\n- Parse the flag and positional arguments\n- Useful help messages\n- Interactive prompt by [AlecAivazis/survey](https://github.com/AlecAivazis/survey)\n- Validate requirements\n- Validate flag and positional arguments\n- Timeout\n- Bash and Zsh completion\n- Nested tasks (Sub tasks)\n\n## Getting Started\n\nCreate the configuration file.\n\n```console\n$ cmdx --init\n$ cat .cmdx.yaml\n```\n\nEdit the configuration file and register the task `hello`.\n\n```console\n$ vi .cmdx.yaml\n$ cat .cmdx.yaml\n---\ntasks:\n- name: hello\n  description: hello command\n  usage: hello command\n  flags:\n  - name: source\n    short: s\n    usage: source file path\n    required: true\n    input_envs:\n    - NAME\n  - name: switch\n    type: bool\n  args:\n  - name: name\n    usage: name\n    default: bb\n  environment:\n    FOO: foo\n  script: \"echo hello {{.source}} $NAME {{if .switch}}on{{else}}off{{end}} {{.name}} $FOO\" # use Go's text/template\n```\n\nOutput the help.\n\n```console\n$ cmdx help\nNAME:\n   cmdx - task runner\n\nUSAGE:\n   cmdx [global options] command [command options] [arguments...]\n\nVERSION:\n   0.2.2\n\nAUTHOR:\n   Shunsuke Suzuki\n\nCOMMANDS:\n   hello     hello command\n   help, h   Shows a list of commands or help for one command\n\nGLOBAL OPTIONS:\n   --config value, -c value  configuration file path\n   --name value, -n value    configuration file name. The configuration file is searched from the current directory to the root directory recursively\n   --init, -i                create the configuration file\n   --list, -l                list tasks\n   --help, -h                show help\n   --version, -v             print the version\n```\n\nList tasks.\n\n```console\n$ cmdx -l\nhello - hello command\n```\n\nRun the task `hello`.\n\n```console\n$ cmdx hello -s README\n+ echo hello README $NAME off bb $FOO\nhello README README off bb foo\n\n$ cmdx hello -s README --switch\n+ echo hello README $NAME on bb $FOO\nhello README README on bb foo\n```\n\n```console\n$ cmdx hello --target foo\ntarget: foo\n```\n\n## Example\n\nIn order to understand `cmdx`, it is good to execute `cmdx` command actually.\nWe prepare the sample configuration file for you to execute `cmdx`.\n\nPlease see [examples](examples).\n\n## Configuration\n\npath | type | description | required | default\n--- | --- | --- | --- | ---\n.timeout | timeout | the task command timeout | false |\n.input_envs | []string | default environment variable binding | false | []\n.script_envs | []string | default environment variable binding | false | []\n.environment | map[string]string | top level environment variables | false | {}\n.quiet | bool | Default configuration whether the content of script is outputted | false |\n.tasks | []task | the list of tasks | true |\ntask.name | string | the task name | true |\ntask.short | string | the task short name | false |\ntask.description | string | the task description | false | \"\"\ntask.usage | string | the task usage | false | \"\"\ntask.flags | []flag | the task flag arguments | false | []\ntask.args | []arg | the task positional arguments | false | []\ntask.input_envs | []string | task level environment variable binding | false | []\ntask.script_envs | []string | task level environment variable binding | false | []\ntask.environment | map[string]string | the task's environment variables | false | {}\ntask.script | string | the task command. This is run by `task.shell` | true |\ntask.quiet | bool | task level default configuration whether the content of script is outputted | false |\ntask.shell | []string | shell command to run the script | `[\"bash\", \"-euo\", \"pipefail\", \"-c\"]` (falls back to `[\"sh\", \"-c\"]` if bash isn't available)\ntask.timeout | timeout | the task command timeout | false |\ntask.require | require | requirement of task | false | {}\ntask.tasks | []task | sub tasks | false | `[]`\nrequire.exec | []stringArray | required executable files | false | []\nrequire.environment | []stringArray | required environment variables | false | []\nstringArray | array whose element is string or array of string | |\ntimeout.duration | int | the task command timeout (second) | false | 36000 (10 hours)\ntimeout.kill_after | int | the duration the kill signal is sent after `timeout.duration` | false | 0, which means the command isn't killed\nflag.name | string | the flag name | true |\nflag.short | string | the flag short name | false |\nflag.usage | string | the flag usage | false | \"\"\nflag.default | string | the flag argument's default value | false | \"\"\nflag.input_envs | []string | flag level environment variable binding | false | []\nflag.script_envs | []string | flag level environment variable binding | false | []\nflag.type | string | the flag type. Either \"string\" or \"bool\" | false | \"string\"\nflag.required | bool | whether the flag argument is required | false | false\nflag.validate | []validate | parameters to validate the value of flag | false | []\nflag.prompt | prompt | prompt | false | prompt is disabled\nprompt.type | string | prompt type | true |\nprompt.message | string | prompt message | false | `flag.name` or `arg.name`\nprompt.help | string | prompt help | false |\nprompt.options | []string | entries of `select` or `multi_select` prompt | true if the prompt type is `select` or `multi_select` |\narg.name | string | the positional argument name | true |\narg.usage | string | the positional argument usage | false | \"\"\narg.default | string | the positional argument's default value | false | \"\"\narg.input_envs | []string | the positional argument level environment variable binding | false | []\narg.script_envs | []string | the positional argument level environment variable binding | false | []\narg.required | bool | whether the argument is required | false | false\narg.prompt | prompt | prompt | false | prompt is disabled\narg.validate | []validate | parameters to validate the value of arg | false | []\nvalidate.type | string | value type (`email`, `url`, `int`) | false |\nvalidate.regexp | string | the regular expression | false |\nvalidate.min_length | int | the minimum string length | false |\nvalidate.max_length | int | the maximum string length | false |\nvalidate.prefix | string | the prefix | false |\nvalidate.suffix | string | the suffix | false |\nvalidate.contain | string | the string which the value should contain | false |\nvalidate.enum | []string | enum | false |\n\n### JSON Schema\n\n- [cmdx.json](json-schema/cmdx.json)\n- https://raw.githubusercontent.com/suzuki-shunsuke/cmdx/refs/heads/main/json-schema/cmdx.json\n\nIf you look for a CLI tool to validate configuration with JSON Schema, [ajv-cli](https://ajv.js.org/packages/ajv-cli.html) is useful.\n\n```sh\najv --spec=draft2020 -s json-schema/cmdx.json -d cmdx.yaml\n```\n\n#### Input Complementation by YAML Language Server\n\n[Please see the comment too.](https://github.com/szksh-lab/.github/issues/67#issuecomment-2564960491)\n\nVersion: `main`\n\n```yaml\n# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/cmdx/main/json-schema/cmdx.json\n```\n\nOr pinning version:\n\n```yaml\n# yaml-language-server: $schema=https://raw.githubusercontent.com/suzuki-shunsuke/cmdx/v1.7.6/json-schema/cmdx.json\n```\n\n### script\n\n`task.script` is the task command.\nThis is parsed by Go's [text/template](https://golang.org/pkg/text/template/) package.\n[sprig](http://masterminds.github.io/sprig/) functions can be used.\nThe value of the flag and positional argument can be referred by the argument name.\n\nFor example,\n\n```yaml\n# refer the value of the argument \"source\"\nscript: \"echo {{.source}}\"\n```\n\nMultiple lines\n\n```yaml\nscript: |\n  echo foo\n  echo bar\n```\n\nIf the positional argument is optional and the argument isn't passed and the default value isn't set,\nthe value is an empty string `\"\"`.\n\nAnd some special variables are defined.\n\nname | type | description\n--- | --- | ---\n`_builtin.args` | []string | the list of positional arguments which aren't defined by the configuration `args`\n`_builtin.args_string` | string | the string which joins `_builtin.args` by the space \" \"\n`_builtin.all_args` | []string | the list of all positional arguments\n`_builtin.args_string` | string | the string which joins `_builtin.all_args` by the space \" \"\n\n### input_envs, script_envs\n\n`input_envs` is a list of environment variables that are bound to the variable.\n\n```yaml\ntasks:\n- name: foo\n  script: \"echo {{.source}}\"\n  args:\n  - name: source\n    input_envs:\n    - command_env\n```\n\n```console\n$ COMMAND_ENV=zzz cmdx foo\n+ echo zzz\nzzz\n```\n\n`script_envs` is a list of environment variables which the variable is bound to.\n\n```yaml\ntasks:\n- name: foo\n  script: \"echo $COMMAND_ENV\"\n  args:\n  - name: source\n    script_envs:\n    - command_env\n```\n\n```console\n$ cmdx foo zzz\n+ echo $COMMAND_ENV\nzzz\n```\n\n### timeout\n\n`cmdx` supports the configuration about the timeout of the task.\n\n1. send SIGINT after `timeout.duration` seconds (default 36,000 seconds)\n2. 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\n\nFor example, the following task `foo`'s timeout is 3 seconds.\n\n```yaml\ntasks:\n- name: foo\n  script: sleep 100\n  timeout:\n    duration: 3\n```\n\n```console\n$ cmdx foo\n+ sleep 100\nthe command is timeout: 3 sec\n```\n\nThe task timeout configuration inherits the top level timeout configuration.\n\n```yaml\ntimeout:\n  duration: 3\ntasks:\n- name: foo # the timeout.duration is 3\n  script: sleep 100\n```\n\n### require\n\n`task.require` is the requirement to run the task.\n\n#### require.exec\n\nFor example, in the following example both of `curl` and `wget` is required.\n\n```yaml\ntasks:\n- name: foo\n  script: curl http://example.com\n  require:\n    exec:\n    - curl\n    - wget\n```\n\nIf `curl` isn't installed, the task is failed.\n\n```\n$ cmdx foo\ncurl is required\n```\n\nNote that the shell's alias is ignored.\nInternally, [exec.LookupPath](https://golang.org/pkg/os/exec/#LookPath) is used.\n\nIn the following example, either `curl` or `wget` is required.\n\n```yaml\ntasks:\n- name: foo\n  script: curl http://example.com\n  require:\n    exec:\n    - - curl\n      - wget\n```\n\n```\n$ cmdx foo\none of the following is required: curl, wget\n```\n\n#### require.environment\n\n`require.environment` is the required environment variables.\nNote that if the value of the environment variable is an emtpy string, the environment variable is treated as unset.\n\n```yaml\ntasks:\n- name: foo\n  script: curl http://example.com\n  require:\n    environment:\n    - GITHUB_TOKEN\n```\n\n```\n$ cmdx foo\nthe environment variable 'GITHUB_TOKEN' is required\n```\n\n```yaml\ntasks:\n- name: foo\n  script: curl http://example.com\n  require:\n    environment:\n    - - GITHUB_TOKEN\n      - GITHUB_ACCESS_TOKEN\n```\n\n```\n$ cmdx foo\none of the following environment variables is required: GITHUB_TOKEN, GITHUB_ACCESS_TOKEN\n```\n\n## validation\n\n`cmdx` supports to validate `args` and `flags`.\n\nFor example,\n\n```yaml\n# .cmdx.yaml\ntasks:\n- name: hello\n  script: echo hello\n  args:\n  - name: age\n    validate:\n    - type: int\n```\n\n```\n$ cmdx hello foo\nage is invalid: must be int: foo\n```\n\n## quiet\n\nBy default `cmdx` outputs the content of task's `script` when the task is run.\n\nIn case of the following example, `+ echo hello` is outputted.\n\n```yaml\n# .cmdx.yaml\ntasks:\n- name: hello\n  script: echo hello\n```\n\n```console\n$ cmdx hello\n+ echo hello\nhello\n```\n\nYou can suppress the output by `--quiet (-q)` option.\n\n```console\n# BAD: cmdx hello -q\n$ cmdx -q hello\nhello\n```\n\nAnd you can change the default configuration of `quiet` at the global level or the task level.\n\n```yaml\n# .cmdx.yaml\ntasks:\n- name: hello\n  script: echo hello\n  quiet: true  # task level\n```\n\n```yaml\n# .cmdx.yaml\nquiet: true # global level\ntasks:\n- name: hello\n  script: echo hello\n  quiet: true\n```\n\nThe priority is\n\n1. command line flag\n2. task level configuration\n3. global level configuration\n\nIf the quiet is enabled by configuration but you want to output `script`, please set the flag `-q=false`.\n\n```\n# \"=\" is needed\n$ cmdx -q=false hello\n+ echo hello\nhello\n```\n\n## prompt\n\n`cmdx` supports the interactive prompt by [AlecAivazis/survey](https://github.com/AlecAivazis/survey).\n`cmdx` supports the following prompt types.\n\n* input\n* multiline\n* password\n* confirm\n* select\n* multi_select\n* editor\n\nAbout prompt type, please see [AlecAivazis/survey's document](https://github.com/AlecAivazis/survey#prompts).\n\n## value source priority\n\n1. command line arguments\n2. environment variable (input_envs)\n3. prompt (prompt isn't launched if the value is set by command line argument or environment variable)\n4. default value\n\n## shell\n\n**This is an advanced feature.**\n\nBy default task's `script` is run by the command `bash -euo pipefail -c` (or `sh -c` if bash isn't available).\nYou can change the command by the `shell` option.\n\nFor example, you can run Python script.\n\n```yaml\n- name: hello\n  shell:\n  - python\n  - -c\n  script: |\n    print(\"hello\")\n```\n\nAnd you can run the shell script in the container.\n\n```yaml\n- name: hello\n  shell:\n  - docker\n  - exec\n  - -ti\n  - foo\n  - sh\n  - -c\n  script: |\n    whoami\n    read -p \"name?\" name\n    echo \"name: $name\"\n```\n\n## Bash (Zsh) Completion\n\nhttps://github.com/urfave/cli/blob/477292c8d462a3f51cd18bc77c0542193a62274d/docs/v2/manual.md#bash-completion\n\n`cmdx` supports Bash (Zsh) Completion powered by urfave/cli.\n\nWe test the completion with Zsh, but we don't test the completion with other shell.\n\nTo enable the completion, you have to load a shell script.\nFor detail, please see the [document of urfave/cli](https://github.com/urfave/cli/blob/477292c8d462a3f51cd18bc77c0542193a62274d/docs/v2/manual.md#bash-completion).\n\nPlease set `cmdx` to `PROG`\n\n## Sub tasks\n\n`cmdx` supports sub tasks.\n\nFor example,\n\n```yaml\ntasks:\n- name: admin\n  usage: administrator feature\n  tasks:\n  - name: cluster\n    usage: manage clusters\n    tasks:\n    - name: create\n      usage: create a cluster\n      script: echo \"create a cluster\"\n```\n\n```\n$ cmdx admin cluster create\n+ echo \"create a cluster\"\ncreate a cluster\n```\n\nThe following attributes are inherited from the parent tasks.\n\n* input_envs\n* script_envs\n* quiet\n* environment\n* timeout\n* requires\n\nFor example,\n\n```yaml\ntasks:\n- name: admin\n  usage: administrator feature\n  require:\n    exec:\n    - yamllint\n  tasks:\n  - name: cluster\n    usage: manage clusters\n    tasks:\n    - name: create\n      usage: create a cluster\n      script: echo \"create a cluster\"\n```\n\n```\n$ cmdx admin cluster create\nyamllint is required\n```\n\nWe can't set both `task.script` and `task.tasks`.\n\nFor example,\n\n```yaml\ntasks:\n- name: hello\n  script: echo hello\n  tasks:\n  - name: world\n    script: echo \"hello world\"\n```\n\n```\n$ cmdx hello\nplease fix the configuration file: the task `hello` is invalid. when sub tasks are set, 'script' can't b\ne set\n```\n\n## Contributing\n\nPlease see the [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuzuki-shunsuke%2Fcmdx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuzuki-shunsuke%2Fcmdx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuzuki-shunsuke%2Fcmdx/lists"}