{"id":13441825,"url":"https://github.com/mumoshu/variant","last_synced_at":"2025-04-05T09:09:22.834Z","repository":{"id":44768737,"uuid":"64372901","full_name":"mumoshu/variant","owner":"mumoshu","description":"Wrap up your bash scripts into a modern CLI today. Graduate to a full-blown golang app tomorrow.","archived":false,"fork":false,"pushed_at":"2022-01-26T01:07:37.000Z","size":21528,"stargazers_count":305,"open_issues_count":37,"forks_count":32,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-10-13T13:05:42.928Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mumoshu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-28T07:18:19.000Z","updated_at":"2024-06-04T04:05:56.000Z","dependencies_parsed_at":"2022-08-12T11:21:50.042Z","dependency_job_id":null,"html_url":"https://github.com/mumoshu/variant","commit_stats":null,"previous_names":[],"tags_count":67,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fvariant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fvariant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fvariant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mumoshu%2Fvariant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mumoshu","download_url":"https://codeload.github.com/mumoshu/variant/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312082,"owners_count":20918344,"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":[],"created_at":"2024-07-31T03:01:38.486Z","updated_at":"2025-04-05T09:09:22.505Z","avatar_url":"https://github.com/mumoshu.png","language":"Go","readme":"\u003cdiv align=\"center\"\u003e\n\n# Variant\n\n![image](https://user-images.githubusercontent.com/22009/51234992-b1899380-19b1-11e9-83c3-dbfdb1517b1c.png)\n\n##### Build modern command line applications in **YAML** and **any scripting language** of your choice, and eventually enhance it with golang\n\n[![CircleCI](https://circleci.com/gh/mumoshu/variant.svg?style=svg)](https://circleci.com/gh/mumoshu/variant)\n\nIntegrations: [GitHub Actions](https://github.com/mumoshu/github-actions/tree/master/variant)\n\n\u003c/div\u003e\n\n```console\n$ cat \u003c\u003cEOF | variant init mycmd\ntasks:\n  hello:\n   parameters:\n   - name: target\n   script: |\n     echo Hello {{ get \"target\" }}!\nEOF\n```\n\n```yaml\n#!/usr/bin/env variant\n\ntasks:\n  hello:\n   parameters:\n   - name: target\n   script: |\n     echo Hello {{ get \"target\" }}!\n```\n\n```console\n$ ./mycmd hello --target variant\nmycmd ≫ starting task hello\nHello variant!\n```\n\nYou can then [build a single go executable of your command](https://github.com/mumoshu/variant#releasing-a-variant-made-command) and finally [enhance it with golang code](https://github.com/mumoshu/variant/blob/master/cmd/run_test.go).\n\n# Rationale\n\nAutomating DevOps workflows is difficult because it often involve multiple `executables` like shell/ruby/perl/etc scripts and commands.\n\nBecause those executables vary in:\n\n* Their quality; from scripts written in a day, intended as a one-off command, but which wind up sticking around for months or even years, to serious commands which are well-designed and written in richer programming languages with adequate tests.\n* Their interface; some passing parameters via environment variables, others having application specific command-line flags, or configuration files.\n\nWriting a single tool which\n\n* wires up all the executables\n* re-implements all the things currently done in various tools\n\nis time-consuming.\n\n# Install\n\nTo install the latest version, run:\n\n```\ncurl -sL https://raw.githubusercontent.com/variantdev/get/master/get | INSTALL_TO=/usr/local/bin sh\n```\n\nTo install a specific version, run with the `VERSION` shell variable:\n\n```\ncurl -sL https://raw.githubusercontent.com/variantdev/get/master/get | INSTALL_TO=/usr/local/bin VERSION=v0.35.1 sh\n```\n\n# Getting Started\n\nCreate a yaml file named `myfirstcmd` containing:\n\n```yaml\n#!/usr/bin/env variant\n\ntasks:\n  bar:\n    script: |\n      echo \"dude\"\n  foo:\n    parameters:\n    - name: bar\n      type: string\n      description: \"the bar\"\n    - name: environment\n      type: string\n      default: \"heaven\"\n    script: |\n      echo \"Hello {{ get \"bar\" }} you are in the {{ get \"environment\" }}\"\n```\n\nNow run your command by:\n\n```console\n$ chmod +x ./myfirstcmd\n$ ./myfirstcmd\nUsage:\n  myfirstcmd [command]\n\nAvailable Commands:\n  bar\n  env         Print currently selected environment\n  foo\n  help        Help about any command\n  ls          test\n  version     Print the version number of this command\n\nFlags:\n  -c, --config-file string   Path to config file\n  -h, --help                 help for myfirstcmd\n      --logtostderr          write log messages to stderr (default true)\n  -o, --output string        Output format. One of: json|text|bunyan (default \"text\")\n  -v, --verbose              verbose output\n\nUse \"myfirstcmd [command] --help\" for more information about a command.\n```\n\nEach task in the `myfirstcmd` is given a sub-command. Run `myfirstcmd foo` to run the task named `foo`:\n\n```console\n$ ./myfirstcmd foo\nHello dude you are in the heaven\n```\n\nLook at the substring `dude` contained in the output above. The value `dude` is coming from the the parameter `bar` of the task `foo`. As we didn't specify the value for the parameter, `variant` automatically runs the task `bar` to fulfill it.\n\nTo confirm that the task `bar` is emitting the value `dude`, try running it:\n\n```console\n$ ./myfirstcmd bar\nINFO[0000] ≫ sh -c echo \"dude\"\ndude\n```\n\nTo specify the value, use the corresponding command-line flag automatically created and named after the parameter `bar`:\n\n```console\n$ ./myfirstcmd foo --bar=folk\nHello folk you are in the heaven\n```\n\nAlternatively, you can source the value from a YAML file.\n\nCreate `myfirstcmd.yaml` containing:\n\n```yaml\nfoo:\n  bar: variant\n```\n\nNow your task sources `variant` as the value for the parameter:\n\n```console\n$ ./myfirstcmd foo\nHello variant you are in the heaven\n```\n\n# Releasing a variant-made command\n\nWhile Variant makes it easy for you to develop a modern CLI without recompiling,\nit is able to produce a single executable binary of your command.\n\nExample: [examples/hello](https://github.com/mumoshu/variant/tree/master/examples/hello)\n\nWrite a small shell script that wraps your variant command into a simple golang program:\n\n```console\n$ cat \u003c\u003cEOF \u003e main.go\npackage main\nimport \"github.com/mumoshu/variant/cmd\"\nfunc main() {\n    cmd.YAML(\\`\n$(cat yourcmd)\n\\`)\n}\nEOF\n\n$ cat \u003c\u003cEOF \u003e Gopkg.toml\n[[constraint]]\n  name = \"github.com/mumoshu/variant\"\n  version = \"v0.24.0\"\nEOF\n```\n\nAnd then build with the standard golang toolchain:\n\n```console\n$ dep ensure\n$ go build -o dist/yourcmd .\n```\n\n```console\n$ ./mycli --target variant\nHello variant!\n```\n\nIt is recommended to version-control the produced `Gopkg.toml` and `Gopkg.lock` because it is just more straight-forward than managing embedded version of em in the shell snippet.\n\nIt is NOT recommended to version-control `main.go`. One of the benefits of Variant is you don't need to recompile while developing. So it is your Variant command written in YAML that should be version-controlled, rather than `main.go` which is necessary only while releasing.\n\n# How it works\n\nVariant is a framework to build a CLI application which becomes the single entry point to your DevOps workflows.\n\nIt consists of:\n\n* YAML-based DSL\n  * to define a CLI app's commands, inputs\n  * which allows splitting commands into separate source files, decoupled from each others\n* Ways to configure your apps written using Variant via:\n  * defaults\n  * environment variables\n  * command-line parameters\n  * application specific configuration files\n  * environment specific configuration files\n* DI container\n  * to implicitly inject required inputs to a commands from configuration files or outputs from another commands\n  * to explicit inject inputs to commands and its dependencies via command-line parameters\n\n# Features\n\n- Default Command\n- Task grouping\n- Dependency injection\n\n## Default Command\n\nThe top-level `script` is executed whenever there's no sub-task that matches the provided command-line arguments.\n\nIn the below example, `./mycmd bar` runs the task `bar`, while `./mycmd foo bar` fails with an \"unknown command\" error:\n\n```\ntasks:\n  bar:\n    script: |\n      echo bar\n```\n\nWhile in the next example, `./mycmd foo bar` runs the root task(=the top-level `script`):\n\n```\nscript: |\n  echo {{ index .args 0 }}\n\n\ntasks:\n  bar:\n    script: |\n       echo bar\n```\n\n## Dependency injection\n\nAn input named `myinput` for the task `mytask` can be one of follows, in order of precedence:\n\n* Value of the command-line option `--myinput`\n* Value of the configuration variable `mytask.myinput`\n  * from the environment specific config file: `config/environments/\u003cenvironment name\u003e.yaml`\n  * from the common config file: `\u003ccommand name\u003e.yaml`(normally `var.yaml`)\n* Output of the task `myinput`\n\n## Environments\n\nYou can switch `environment` (or context) in which a task is executed by running `var env set \u003cenv name\u003e`.\n\n```\n$ var env set dev\n$ var test\n#=\u003e reads inputs from var.yaml + config/environments/dev.yaml\n\n$ var env set prod\n$ var test\n#=\u003e reads inputs from var.yaml + config/environments/prod.yaml\n```\n\n## Environment Variables\n\n`variant` takes a few envvars for configuration.\n\n`VARIANT_RUN`: Additional command-line arguments to be added to the actual args. For instance, `VARIANT_RUN=\"bar baz\" variant foo --color=false` is equivalent to `variant foo --color=false bar baz`.\n\n`VARIANT_RUN_TRIM_PREFIX`: Prefix to be removed from the `VARIANT_RUN`. For intance, `VARIANT_RUN=\"/myslashcmd --foo=bar\" variant mycmd` is equivalent to `variant mycmd --foo=bar`.\n\n`VARIANT_GITHUB_COMMENT(_ON_[SUCCESS|FAILURE])`: (GitHub Actions v2 only) When this variables is set to a non-empty value, variant tries to obtain the \"source\" GitHub issue/pull request that triggered the run, and sends a issue/pr comment containing the result. Great for giving feedbacks to whom run the variant task from e.g. GitHub comment. \n\n# FAQ\n\nPlease see the collection of answered questions in our [GitHub issues labeled \"question\"](https://github.com/mumoshu/variant/issues?utf8=%E2%9C%93\u0026q=label%3Aquestion+).\n\n# Integrations and useful companion tools\n\n- Use [liujianping/job](https://github.com/liujianping/job) for timeouts, retries, scheduled runs, etc.\n- Use [davidovich/summon](https://github.com/davidovich/summon) to bundle assets into your variant command by using the golang module system and `gobin`\n\n# Alternatives\n\n* [go-task/task](https://github.com/go-task/task)\n* [tj/robo](https://github.com/tj/robo)\n* [goeuro/myke](https://github.com/goeuro/myke)\n* [kevgo/morula](https://github.com/kevgo/morula) - task runner for monorepos\n\n# Interesting Readings\n\n* [How to write killer DevOps automation workflows](http://techbeacon.com/how-write-killer-devops-automation-workflows)\n* [progrium/bashstyle: Let's do Bash right!](https://github.com/progrium/bashstyle)\n* [ralish/bash-script-template: A best practices Bash script template with many useful functions](https://github.com/ralish/bash-script-template)\n\n# Future Goals\n\n* Runners to run tasks in places other than the host running your Variant app\n  * Docker\n  * Kubernetes\n  * etc\n* Tools/instructions to package your Variant app for easier distribution\n  * Single docker image containing\n    * all the scripts written directly in the yaml\n    * maybe all the scripts referenced from scripts in the yaml\n    * maybe all the commands run via the host runner\n* Integration with job queues\n  * to ensure your tasks are run reliably, at-least-once, tolerating temporary failures\n\n# License\n\nApache License 2.0\n\n\n# Attribution\n\nWe use:\n\n- [semtag](https://github.com/pnikosis/semtag) for automated semver tagging. I greatly appreciate the author(pnikosis)'s effort on creating it and their kindness to share it!\n","funding_links":[],"categories":["HarmonyOS","Go","others","Command Line and Tooling"],"sub_categories":["Windows Manager","Observability"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmumoshu%2Fvariant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmumoshu%2Fvariant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmumoshu%2Fvariant/lists"}