Skip to content

A build-tooling CLI Go framework designed to replace duck-typed scripting languages. (supports Nix)

License

Notifications You must be signed in to change notification settings

sdsc-ordes/quitsh

Repository files navigation

Coverage Current Release Pipeline Status License label


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.

Key Features

Code-First Approach

  • 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.

Component Identification

  • Components (i.e., buildable units) are identified by placing a configuration file (default: .component.yaml) in the corresponding subdirectory.

Extendability

  • 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.

Targets and Steps

  • 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.

Runner System

  • 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.

Toolchain Dispatch

  • 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:

    quitsh-design

Built-in Libraries

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 test quitsh it-self).

Performance

  • 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.

Nix Integration

  • CLI tools built with quitsh can be seamlessly packaged into Nix development shells, ensuring accessibility for all users of a given repository.

How We Use It?

TODO: unfinished.

Understand what this framework does, is best accomplished by understanding how we use this framework in our components repo (mono-repo).

Components

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$'