Skip to content

PyratLabs/ugo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

uGo

The ubiquitous ./go script — as a Go binary.

Overview

uGo is a CLI tool that executes project-specific commands defined in YAML configuration. It follows the ThoughtWorks ./go script pattern: provide a common vocabulary for each project, with verbs that are context-aware based on the current working directory.

The binary name is flexible — rename it and the help text and config file name follow automatically, allowing multiple instances for different projects.

Quick Start

Install

go install github.com/PyratLabs/ugo@latest

Or build from source:

go build -o ugo .

Create a config

Create <binary-name>.yaml in your project root:

tools:
  ansible-playbook:
    min_version: "2.10.0"
    version_cmd: "ansible-playbook --version"
    download_url: "https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html"
  go:
    min_version: "1.25.0"
    version_cmd: "go version"
  kubectl:
    download_url: "https://kubernetes.io/docs/tasks/tools/"

commands:
  plan:
    cmd: ansible-playbook --check environments/${environment}/inventory.yaml playbooks/${playbook}.yaml
    description: "Run an ansible playbook in check mode."
    arguments:
      - name: environment
        match: "environments/*/inventory.yaml"
      - name: playbook
        match: "playbooks/*.yaml"
  lint:
    cmd: go test ./...
    description: "Run test suite"
  deploy:
    cmd: kubectl apply -f ./deployments/${service} --context ${region}
    description: "Deploy a service"
    arguments:
      - name: service
        match: "[a-z][a-z0-9-]+"
      - name: region
        values: [us-east-1, eu-west-1, ap-southeast-1]

Run commands

ugo plan dev ensure-ssh   # runs: ansible-playbook --check environments/dev/inventory.yaml playbooks/ensure-ssh.yaml
ugo lint                  # runs: go test ./...
ugo check                 # verify tool dependencies
ugo version               # print the version
ugo plan --help           # shows argument validation rules
ugo --no-color            # disable colored output

Configuration

uGo loads configuration from two locations and merges them (local overrides global):

Scope Path
Global ~/.config/<binary>/config.yaml
Local <project-root>/<binary>.yaml

Shell options

Set shell_options to prepend shell flags to all commands that run via sh -c (multiline cmd and all cmds items):

shell_options: "set -euo pipefail"

This enables strict mode: exit on error (-e), unset variable error (-u), and pipe failure detection (-o pipefail).

Config format

Tools

tools:
  <binary>:
    min_version: "<semver>"        # optional minimum version
    max_version: "<semver>"        # optional maximum version
    version_cmd: "<cmd>"           # how to get the version string (defaults to "<binary> --version")
    download_url: "<url>"          # shown if tool is missing (optional)

If no min_version or max_version is specified, uGo only checks that the tool exists in $PATH:

tools:
  terraform:                       # just check it exists
    download_url: "https://terraform.io/downloads"

Commands

commands:
  <verb>:
    cmd: "<single command with ${arg} templates>"      # string: runs directly (single-line) or as shell script (multi-line)
    cmds:                                              # list: each item runs via sh -c (supports shell features)
      - "echo ${arg}"
      - |
        echo "multi-line"
        echo "script"
    env:                                               # optional: environment variables for the command
      MY_VAR: "value"
    description: "<short help text>"
    arguments:                     # positional argument definitions (optional)
      - name: <arg1>
        values: [val1, val2]       # optional: restrict to enum values
        match: "<glob or regex>"   # optional: validate with file glob or regex

Argument validation

Arguments support three validation modes:

Mode Config Behavior
Enum values: [dev, staging, prod] Value must be in the list
Glob match: "playbooks/*.yaml" Checks files on disk; accepts full path, basename, basename without extension, or directory name
Regex match: "[a-z]+" Full-string regex match (auto-anchored)
Exclude exclude: [default] Disallowed values (hidden from help output)

Glob vs regex is auto-detected: if the pattern contains * or ? it's treated as a file glob.

Multiline commands

cmd with multiline — runs the entire block as a shell script via sh -c:

commands:
  plan:
    cmd: |
      terraform workspace select -or-create=true "${workspace}"
      terraform plan
    description: "Run a terraform plan"

cmds list — each item runs via sh -c, so shell features (variables, subshells, pipes) work:

commands:
  deploy:
    cmds:
      - 'echo "Deploying to $ENV"'
      - |
        echo "Running in subshell"
        (
          cd /tmp && pwd
        )
    env:
      ENV: "production"
    description: "Deploy with shell features"

Exclude values

Combine match with exclude to hide certain values from glob matches:

commands:
  plan:
    cmd: terraform workspace select "${workspace}" && terraform plan
    arguments:
      - name: workspace
        match: config/*.yaml
        exclude: [default]

Excluded values are rejected during validation and hidden from help output.

Example

# config: cmd: ansible-playbook environments/${environment}/inventory.yaml
# args:   environment with match "environments/*/inventory.yaml"

ugo plan dev    →   ansible-playbook environments/dev/inventory.yaml
ugo plan test   →   ❌ argument 'environment': no file matching pattern "environments/*/inventory.yaml" found for value "test"

Enum values

# config: cmd: kubectl apply -f ./deployments/${service} --context ${region}
# args:   region with values [us-east-1, eu-west-1, ap-southeast-1]

ugo deploy api us-east-1   →   kubectl apply -f ./deployments/api --context us-east-1
ugo deploy api us-west-2   →   ❌ argument 'region': value "us-west-2" not in allowed values: [us-east-1, eu-west-1, ap-southeast-1]

Running a verb without required arguments shows the error followed by argument validation rules:

$ ugo plan

❌ expected 2 argument(s) for 'plan': environment, playbook

Arguments:
  environment         dev, staging, prod
  playbook            ensure-ssh, setup-db

Usage:
  ugo plan <environment> <playbook> [flags]

Tool Dependency Checks

Before executing any verb, uGo checks configured tools:

  • Verifies each binary exists in $PATH
  • Extracts version from version_cmd output
  • Compares against min_version and max_version using semver

Run ugo check manually to inspect all tool status.

$ ugo check
  ✅ ansible-playbook (v2.20.5)
  ✅ docker (v28.5.1)
  ✅ All tool dependencies satisfied

$ ugo check
  ❌ nonexistent-tool is not installed, download at: https://example.com
  ✅ docker (v28.5.1)

  ❌ Tool checks failed

Colored Output

uGo uses UTF-8 icons and colors for status output. Use --no-color to disable:

ugo --no-color plan dev ensure-ssh

Further Reading

Author

Xan Manning, 2020

About

uGo: The ubiquitous `./go` script

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages