{"id":26571748,"url":"https://github.com/sdsc-ordes/quitsh","last_synced_at":"2026-04-13T21:31:47.946Z","repository":{"id":283712086,"uuid":"952568011","full_name":"sdsc-ordes/quitsh","owner":"sdsc-ordes","description":"Define your build/CI tooling in a statically typed language. (supports Nix and component repositories - a.k.a. mono-repos)","archived":false,"fork":false,"pushed_at":"2026-04-08T18:17:33.000Z","size":945,"stargazers_count":9,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-08T19:16:16.614Z","etag":null,"topics":["automation","ci","cli","component-based-development","devenv","go","mono-repository","monorepo","nix"],"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/sdsc-ordes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"docs/code-of-conduct.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-03-21T13:53:35.000Z","updated_at":"2026-04-08T18:17:36.000Z","dependencies_parsed_at":"2025-04-13T03:25:55.219Z","dependency_job_id":"2cb8f22a-ab86-4143-8908-e9124e430c69","html_url":"https://github.com/sdsc-ordes/quitsh","commit_stats":null,"previous_names":["sdsc-ordes/quitsh"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/sdsc-ordes/quitsh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsc-ordes%2Fquitsh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsc-ordes%2Fquitsh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsc-ordes%2Fquitsh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsc-ordes%2Fquitsh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sdsc-ordes","download_url":"https://codeload.github.com/sdsc-ordes/quitsh/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdsc-ordes%2Fquitsh/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31771813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T20:17:16.280Z","status":"ssl_error","status_checked_at":"2026-04-13T20:17:08.216Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["automation","ci","cli","component-based-development","devenv","go","mono-repository","monorepo","nix"],"created_at":"2025-03-22T23:15:55.055Z","updated_at":"2026-04-13T21:31:47.925Z","avatar_url":"https://github.com/sdsc-ordes.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/assets/images/logo.svg\" width=\"400px\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://codecov.io/gh/sdsc-ordes/quitsh\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/sdsc-ordes/quitsh/graph/badge.svg?token=35RP18MHFR\" alt=\"Coverage\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/sdsc-ordes/quitsh/releases/latest\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/release/sdsc-ordes/quitsh.svg?label=release\" alt=\"Current Release\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://pkg.go.dev/github.com/sdsc-ordes/quitsh\"\u003e\n    \u003cimg src=\"https://pkg.go.dev/badge/github.com/sdsc-ordes/quitsh\" alt=\"Package\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/sdsc-ordes/quitsh/actions/workflows/normal.yaml\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/sdsc-ordes/quitsh/normal.yaml?label=ci\" alt=\"Pipeline Status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://mit-license.org/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/License-Apache2.0-blue.svg?\" alt=\"License label\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n\u003e [!CAUTION]\n\u003e\n\u003e This repository is in `beta`. The design space of this tool is still explored\n\u003e and breaking changes might occure. Documentation is incomplete.\n\nThe `quitsh` framework (`/kwɪʧ/`) is a build-tooling **CLI library/framework**\ndesigned to replace loosely-typed scripting languages (e.g., `bash`, `python`,\nand similar alternatives) with the statically-typed language `Go`. Its goal is\nto simplify tooling tasks while providing robust, extendable solutions for\ncomponent repositories (mono-repositories).\n\n`quitsh` is an opinionated framework born out of frustration with the lack of\nsimple and extendable tooling for mono-repos. It is language-agnostic and\ntoolchain-independent, allowing users to focus on their workflows without being\nconstrained by specific technologies.\n\nQuitsh can be used in two non-coupled ways (enhancing each other):\n\n- Build/use \u0026 extend the CLI `quitsh` for your tooling/CI/CD scripts in your\n  repository. You write `quitsh mycommand --do-it` and decide what it does,\n  using the [library modules](#built-in-libraries) for running subprocesses,\n  logging etc.\n\n- Use its [component feature](#component-system) which gives you the ability to\n  register your tooling scripts (build/lint/test etc.) as\n  [runners](#runner-system) which can be reused across\n  [components](#components). Since runners depend heavily on available\n  executables in your `PATH`, runners run over a toolchain (currently Nix\n  development shell etc.).\n\n## Key Features\n\n### Code-First Approach\n\n- All tooling logic is implemented in `Go`.\n- Tooling logic is defined primarily in code, avoiding declarative\n  configurations or templated non-typed languages, which often add unnecessary\n  complexity despite their flexibility. _Note: Supporting configuration\n  languages like `jsonnet` or \"turing-complete\" YAML etc. is particularly\n  avoided. **Everything stays strongly-typed and fast compiled.**_\n\n### Extendability\n\n- `quitsh` serves as a library to build **your customized** CLI tool for your\n  specific tasks.\n- Users can add custom commands and specialized tooling features using libraries\n  like [`cobra`](https://github.com/spf13/cobra), `quitsh mycommand --do-it`.\n\n### Component System\n\n- [Components](#components) (i.e., buildable units) are identified by placing a\n  configuration file (default: `.component.yaml`) in the corresponding\n  subdirectory.\n\n#### Targets and Steps\n\n- Each component defines **targets**, which consist of multiple **steps**.\n- Targets can depend on other targets across the repository.\n- Input change sets can be specified for each target to track modifications and\n  determine if the target is outdated.\n\n#### Runner System\n\n- Steps within targets are executed by **runners**, which are written by you in\n  Go and act as reusable replacements for traditional build/tooling scripts.\n- Runners can have custom YAML configuration options specified per component in\n  `.component.yaml`.\n\n#### Toolchain Dispatch\n\n- Runners are associated with specific toolchains.\n- By default, `quitsh` includes a\n  [Nix development shell dispatch](https://nix.dev/tutorials/first-steps/declarative-shell.html),\n  providing stable and reproducible environments.\n- While container-based dispatching is not a primary goal, it can be implemented\n  by extending the dispatch interface.\n\n- The tool was built to replicate the same procedure one executes during local\n  development and also in CI. Having CI align with what you execute locally is\n  not a nice thing to have, its a necessity. Nix development shells (or\n  containers) help with this. A Nix shell provides a simple and robust\n  abstraction to pin a toolchain. The following visualization gives an overview\n  about how `quitsh` is used:\n\n  \u003cp align=\"center\"\u003e\n\n  ![quitsh-design](docs/assets/images/quitsh-design.drawio.svg)\n\n  \u003c/p\u003e\n\n### Built-in Libraries\n\nThe `pkg` folder offers utilities for common development needs, such as:\n\n- Command Execution: [`pkg/exec`](pkg/exec) provides utilities for process\n  execution and command chaining.\n- Structured Logging: [`pkg/log`](pkg/log) enables consistent and readable\n  logging.\n- Error Handling: [`pkg/error`](pkg/error) facilitates contextual error\n  management.\n- Dependency Graphs: Tools for managing and resolving dependency graphs across\n  targets.\n- Some Go `test` runners (here as an example) for running Go tests (its used\n  internally to test `quitsh` it-self).\n\n#### Performance\n\n- Since all tooling is written in `Go`, `quitsh` provides type safety and fast\n  performance by default.\n- Combined with a Nix-based toolchain dispatch and the ability to write tests\n  easily, the framework significantly accelerates the \"change, test, improve\"\n  workflow.\n\n#### Nix Integration\n\n- A CLI tool built with `quitsh` can be\n  [seamlessly packaged](./tools/nix/packages/cli) into a Nix development shells,\n  ensuring accessibility for all users of a given repository.\n\n---\n\n# How To Use It?\n\nUsing this library follows instantiating the CLI (also demonstrated in this\nrepository in [`main.go`](./tools/cli/cmd/cli/main.go), e.g.:\n\n```go\nargs := cliconfig.New()\n\ncli, err := cli.New(\n  \u0026args.Commands.Root,\n  \u0026args,\n  cli.WithName(\"cli\"),\n  cli.WithDescription(\"This is the 🐔-🥚 CLI tool for 'quitsh', yes its built with 'quitsh'.\"),\n  cli.WithCompFindOptions(\n    query.WithFindOptions(\n      fs.WithWalkDirFilterPatterns(nil,\n        []string{\"**/test/repo/**\"}, true))),\n  cli.WithStages(\"lint\", \"build\", \"test\"),\n  cli.WithSignalContext(true),\n  cli.WithTargetToStageMapperDefault(),\n  cli.WithToolchainDispatcherNix(\n    \"tools/nix\",\n    func(c config.IConfig) *toolchain.DispatchArgs {\n      cc := common.Cast[*cliconfig.Config](c)\n\n      return \u0026cc.Commands.DispatchArgs\n    },\n  ),\n)\nlog.PanicE(err, \"Could not initialize CLI app.\")\n\ndefer func() {\n  e := cli.Shutdown()\n  log.WarnE(e, \"Could not shutdown CLI app.\")\n  if err != nil {\n    os.Exit(1)\n  }\n}()\n\nerr = cli.Run()\n```\n\nYou can now add runners and your own commands depending on the needs of your\nrepository. For example in [`main.go`](./tools/cli/cmd/cli/main.go):\n\n```go\nlistcmd.AddCmd(cli, cli.RootCmd())\nconfigcmd.AddCmd(cli.RootCmd(), \u0026conf)\nexectarget.AddCmd(cli, cli.RootCmd())\nexecrunner.AddCmd(cli, cli.RootCmd(), \u0026conf.Commands.DispatchArgs)\n```\n\nadds essential `quitsh` commands\n\n- `listcmd` to list all components etc: `quitsh list`.\n- `configcmd` to inspect/write the config file: `quitsh config ...`.\n- `exectarget` to execute specific targets `quitsh exec-target`.\n- `execrunner` to let `quitsh` dispatch over toolchains (see\n  `cli.WithToolchainDispatcherNix` above): `quitsh exec-runner ...`\n\nThere are lots of more useful commands in [`pkg/cli/cmd`](./pkg/cli/cmd) which\nyou might use.\n\n## Useful References\n\nA reference repository with commands and runner can be looked at here:\n\n- [`Custodian`](https://gitlab.com/data-custodian/custodian/tree/main/tools/quitsh)\n- [`DAC-Portal`](https://gitlab.com/data-custodian/dac-portal/tree/main/tools/quitsh)\n- [`Quitsh (this repo)`](https://github.com/sdsc-ordes/quitsh)\n\n## Config\n\nQuitsh runs with global config YAML file which it loads (or defaults) at startup\nfor any invocation. The above CLI instantiation constructs a new config with\n`cliconfig.New()` (this is custom for each usecase and can be adjusted and\nmodified). The config defines global settings (output directories, logging etc.)\nand also various custom, use-case specific settings. These might include\nsettings which runners (or custom commands) might use during execution. For\nexample build runners might use a\n[`build.BuildType`](./tools/cli/pkg/config/config.go) property which could be\n`debug` or `release` etc. The CLI does not care about your custom settings, they\nonly need to be fully serializable to YAML (for toolchain dispatching) and you\ncan override defaults from custom added commands for example.\n\n### Modifying Config Values\n\nYou have the ability to set the config file `quitsh` uses with `--config` or\nread it from stdin with `--config -` or set options (YAML) on the command line\nwith `--config-value`. For example `--config-value \"build.buildType: release\"`\nwould set the `build.BuildType` setting to `release` on startup.\n\n## Components\n\nQuitsh builds around **components**. A **component** should be treated as an own\ncompartment/Git repository or directory with its own independent source code and\noutput.\n\nA component in `quitsh` is defined by a `.component.yaml` (name is\nconfigurable):\n\n```yaml\n# The name of the component: Must be a unique.\nname: my-component\n\n# A semantic version.\n# This is useful for Nix packaging etc.\nversion: 0.2.1\n\n# A simple annotation (not used internally) what main language this component uses.\nlanguage: go\n\n# The `.general` object is not parsed by `quitsh` and\n# allows arbitrary values mainly used for YAML anchors.\n.general:\n  value: \u0026val 60\n\ntargets:\n  # A target called `test` with two steps.\n  my-test:\n    # The stage to which this target belongs. Does not need to be provided\n    # if the CLI is setup to map target names to stages.\n    stage: test\n\n    steps:\n      # Step 1: Using runner with ID (how it was registered).\n      - runner-id: banana-project::my-test-runner\n        config: # Your custom runner YAML config, (optional).\n\n      # Step 2: Using a runner with registered key (stage: `test`, name `my-test`)\n      - runner: my-test\n\n  # A target called `build-all` with one step.\n  build-all:\n    stage: build\n\n    # Defining when this target is considered changed:\n    # i.e. whenever `self::sources` input change set is changed.\n    # `self` maps to this component.\n    inputs: [\"self::sources\"]\n\n    # Defining dependencies on other targets such that this\n    # target is executed after target `my-test` above.\n    # You can also link to other components (e.g `other-comp::build`).\n    depends: [\"self::my-test\"]\n\n    steps:\n      # Step 1: Using a runner with registered key (stage: `build`, name `my-test`)\n      - runner: my-build\n        config:\n          tags: [\"doit\"]\n\n  lint:\n    steps:\n      - ... other steps ...\n\ninputs:\n  # An input change set with name `sources` which defines\n  # patterns to match all source files.\n  sources:\n    # A regex which matches `*.go` files in `./src` in the components folder.\n    patterns:\n      - '^./src/.*\\.go$'\n```\n\n## Execution of Targets\n\nThe execution of steps by `quitsh` is done by reading a\n[`.component.yaml`](.component.yaml) for each component. The\n[`.component.yaml`](.component.yaml) file contains _inputs_ and _targets_.\n\nQuitsh's own [`.component.yaml`](./.component.yaml) looks like:\n\n```yaml\nname: quitsh\nlanguage: go\n\ninputs:\n  srcs:\n    patterns:\n      - \"^.*$\"\n\ntargets:\n  test:\n    stage: test\n    steps:\n      - runner: go\n      - runner: go-bin\n        config:\n          # Build everything instrumented.\n          # Execute the binaries via a `go test` provided in the following\n          # pkg and with tags.\n          buildPkg: test/cmd\n          testPkg: test\n          buildTags: [\"integration\"]\n          testTags: [\"integration\"]\n\n  build:\n    steps:\n      - runner: go\n  lint:\n    steps:\n      - runner: go\n```\n\nEach **target** defines is a set of **steps** which itself are further specified\nby the field `runner`. A **runner** is **Go code** applicable for a certain step\nwhich should work for all components.\n\nA runner is registered in [`factory`](./pkg/runner/factory/runner.go), for\nexample [here](./tools/cli/pkg/runner/go/lint.go). Runners can be written by\nimplementing the interface [`Runner`](./pkg/runner/runner.go) inside\n[`./pkg/runner/runners`](./pkg/runner/runners) and registering them in\n[`./pkg/runner/factory/init-runners.go`], for example\n[here](./tools/cli/pkg/runner/go/register.go).\n\n\u003e [!NOTE]\n\u003e\n\u003e **You can execute targets in parallel with `--parallel`**.\n\n## Runner Configuration\n\nRunners can load independent YAML config under `config` to make them\nconfigurable, e.g. the `go` build runner loads the following\n[config](./pkg/runner/runners/go/build-config.go):\n\n```yaml\nsteps:\n  build:\n    - runner: go\n      config:\n        version-module: \"pkg/myversion-module\" # defaults to `pkg/build`\n```\n\n## Target Stages\n\nEach target also maps to a _stage_ which `quitsh` uses to group targets together\nif you want to find them and make gathering commands such as the example\n[`build` here](https://gitlab.com/data-custodian/custodian/-/blob/main/tools/quitsh/cmd/quitsh/cmd/build/build.go#L83).\nIt collects and runs all targets in the stage `build`.\n\n## How to Extend Functionality\n\nFor users using the `quitsh` CLI framework, it is suggested to follow the\nfollowing points when thinking about new functionality in a repository which\nuses this library. If you need new functionality for CI and local development\nwhich you normally would write in `bash`/`python` follow the following steps:\n\n- If the functionality is **a feature needed in an existing runner and step**:\n  Extend the runner and make it work with your new test/build/lint feature.\n\n- If the functionality is **not related to a runner or the same for each\n  component with that language**: Extend `quitsh` by providing another\n  subcommand which does what you need, see this example\n  [`fix-hash`](https://gitlab.com/data-custodian/custodian/-/blob/main/tools/quitsh/cmd/quitsh/cmd/nix/fix-hash/fix.go).\n\n- If the functionality is **for a certain language, e.g. `go` or `python` and\n  applies to each component which is written in that language**: consider adding\n  a new runner for an already pre-defined stage, e.g. `lint`, `build` etc.\n\n## Example Applications\n\nUnderstand what this framework does, is best accomplished by understanding how\nwe use this framework in our\n[components repo (mono-repo)](https://gitlab.com/data-custodian/custodian). Our\nmajor components are located in\n[./components](https://gitlab.com/data-custodian/custodian/-/tree/main/components).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdsc-ordes%2Fquitsh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsdsc-ordes%2Fquitsh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdsc-ordes%2Fquitsh/lists"}