Caution
This repository is in beta
and not stable enough since the design space of
this tool is still explored.
The quitsh
framework (/kwɪʧ/
) is a build-tooling CLI framework designed to
replace loosely-typed scripting languages (e.g., bash
, python
, and similar
alternatives) with the statically-typed language Go
. Its goal is to simplify
tooling tasks while providing robust, extendable solutions for component
repositories (mono-repositories).
quitsh
is an opinionated framework born out of frustration with the lack of
simple and extendable tooling for mono-repos. It is language-agnostic and
toolchain-independent, allowing users to focus on their workflows without being
constrained by specific technologies.
- All tooling logic is implemented in
Go
. - Tasks are defined primarily in code, avoiding declarative configurations or templated non-typed languages, which often add unnecessary complexity despite their flexibility.
- Components (i.e., buildable units) are identified by placing a configuration
file (default:
.component.yaml
) in the corresponding subdirectory.
quitsh
serves as a library to build your customized CLI tool for your specific tasks.- Users can add custom commands and specialized tooling features using libraries
like
cobra
.
- Each component defines targets, which consist of multiple steps.
- Targets can depend on other targets across the repository.
- Input change sets can be specified for each target to track modifications and determine if the target is outdated.
- Steps within targets are executed by runners, which are written by you in Go and act as reusable replacements for traditional build/tooling scripts.
- Runners can have custom YAML configuration options specified per component in
.component.yaml
.
-
Runners are associated with specific toolchains.
-
By default,
quitsh
includes a Nix development shell dispatch, providing stable and reproducible environments. -
While container-based dispatching is not a primary goal, it can be implemented by extending the dispatch interface.
-
The tool was built to replicate the same procedure one executes during local development and also in CI. Having CI align with what you execute locally is not a nice thing to have, its a necessity. Nix development shells (or containers) help with this. A Nix shell provides a simple and robust abstraction to pin a toolchain. The following visualization gives an overview about how
quitsh
is used:
The pkg
folder offers utilities for common development needs, such as:
- Command Execution:
pkg/exec
provides utilities for process execution and command chaining. - Structured Logging:
pkg/log
enables consistent and readable logging. - Error Handling:
pkg/error
facilitates contextual error management. - Dependency Graphs: Tools for managing and resolving dependency graphs across targets.
- Some Go
test
runners (here as an example) for running Go tests (its used internally to testquitsh
it-self).
- Since all tooling is written in
Go
,quitsh
provides type safety and fast performance by default. - Combined with Nix-based toolchain dispatch and the ability to write tests easily, the framework significantly accelerates the "change, test, improve" workflow.
- CLI tools built with
quitsh
can be seamlessly packaged into Nix development shells, ensuring accessibility for all users of a given repository.
TODO: unfinished.
Understand what this framework does, is best accomplished by understanding how we use this framework in our components repo (mono-repo).
Our major components are located in
./components.
Each component in quitsh
is defined by a .component.yaml
(name is
customizable) file which more or less looks like:
# The name of the component: Must be a unique.
name: my-component
# A semantic version.
# This is useful for Nix packaging etc.
version: 0.2.1
# A simple annotation (not used internally) what main language this component uses.
language: go
targets:
# A target called `test` with two steps.
my-test:
# The stage to which this target belongs. Does not need to be provided
# if the CLI is setup to map target names to stages.
stage: test
steps:
# Step 1: Using runner with ID (how it was registered).
- runner-id: banana-project::my-test-runner
config: # Your custom runner YAML config, (optional).
# Step 2: Using a runner with registered key (stage: `test`, name `my-test`)
- runner: my-test
# A target called `build-all` with one step.
build-all:
stage: build
# Defining when this target is considered changed:
# i.e. whenever `self::sources` input change set is changed.
inputs: ["self::sources"]
# Defining dependencies on other targets such that this
# target is executed after target `my-test` above.
# You can also link to other components (e.g `other-comp::build`).
depends: ["self::my-test"]
steps:
# Step 1: Using a runner with registered key (stage: `build`, name `my-test`)
- runner: my-build
config:
tags: ["doit"]
lint:
steps:
- ... other steps ...
inputs:
# An input change set with name `sources` which defines
# patterns to match all source files.
sources:
# A regex which matches `*.go` files in `./src` in the components folder.
patterns:
- '^./src/.*\.go$'
- Quitsh's own
.component.yaml