Skip to content

Add Go CLI to improve developer experience#11

Merged
jcardozo-eth merged 35 commits intomainfrom
chore/dx
Feb 11, 2026
Merged

Add Go CLI to improve developer experience#11
jcardozo-eth merged 35 commits intomainfrom
chore/dx

Conversation

@jcardozo-eth
Copy link
Copy Markdown
Member

Add Go CLI to replace justfile and improve developer experience

Summary

As the justfile grew, its shell recipes became difficult to read, debug, and maintain — shell is the wrong language for non-trivial logic like conditional deployments, health checks, or environment validation. This PR replaces the justfile with dap, a purpose-built CLI written in Go where each command is real, structured code that developers can read, step through, and reason about. Because the CLI is proper software — unit-tested, linted, statically typed — the developer tooling can now be maintained to the same standard as production code.

What changed

The CLI itself

A new cli/ directory contains the full Go source for the dap binary, built on Cobra with terminal styling via Lipgloss. Commands are organized into five groups:

Group Commands Purpose
Development dev, test, check, lint, typecheck Day-to-day Python development
Environment versions, clean, reset, welcome Environment introspection and maintenance
Dagster materialize, run Pipeline operations
Kubernetes k8s up/down/restart/status/logs/shell Local K8s deployment lifecycle
CLI Development cli build/test/lint Meta commands for working on the CLI itself (lint supports --fix for auto-fixing)

Two internal packages support all commands:

  • internal/ui — terminal output styled with the ETH corporate design color palette, with adaptive light/dark terminal support via lipgloss.AdaptiveColor. Output degrades gracefully: in a TTY you get full colors and an ASCII art DAP-O banner; in CI or piped output, colors are disabled automatically and the banner falls back to a plain text one-liner. Supports the NO_COLOR standard and a global --no-color flag.
  • internal/exec — thin wrapper around os/exec providing Run, RunInteractive, RunPassthrough, and Which helpers

Nix integration

  • Split devShells: The monolithic nix develop shell is replaced by four purpose-built shells — default (everything), minimal (Python + CLI), k8s (adds kubectl/helm), and cli-dev (adds Go toolchain). CI pipelines can use smaller shells for faster startup.
  • CLI packaging: A dedicated cli/flake.nix uses gomod2nix to build the CLI hermetically. The root flake composes it as a flake input and exposes dap in all devShells.
  • Simplified .envrc: The 37-line .envrc with inline bash for venv creation, dependency sync, and a hand-rolled welcome banner is replaced by 19 lines that delegate to dap welcome and a small uv-sync.sh script.

CI/CD

  • CLI test & lint workflow (.github/workflows/cli.yml): Runs dap cli test and dap cli lint on pushes/PRs touching cli/.
  • Release workflow (.github/workflows/release.yml): Triggers on cli/v* tags, runs tests, then builds cross-platform binaries (linux/darwin, amd64/arm64) via GoReleaser.
  • Path filtering: The existing pipeline CI now ignores cli/** changes to avoid unnecessary Python test runs.
  • Pre-commit hooks: Added .pre-commit-config.yaml with trailing-whitespace, ruff lint/format, and automatic gomod2nix.toml regeneration when go.mod/go.sum change.

Removed

  • justfile (213 lines) — fully replaced by the CLI.
  • just dependency from flake.nix — no longer needed.
  • jq, curl, openssl dependencies — version-check functions that shelled out to these tools are replaced by native Go equivalents. Password generation for K8s secrets now uses crypto/rand instead of openssl rand.

Docs

  • cli/CONTRIBUTING.md — comprehensive guide covering project structure, adding commands, UI guidelines, error handling, testing, linting, building, and the Nix build chain.
  • Updated README.md — all command references updated from just to dap, devShell table added, project structure updated to show cli/, tooling section updated.

Why Go?

  • Testable and lintable: The CLI has unit tests for every command group and both internal packages, runnable with go test. Linting uses go vet and gofmt.
  • Statically typed: Catches errors at compile time that a shell script surfaces only at runtime.
  • Single binary: Compiles to one binary with no runtime dependencies — easy to distribute and fast to start.
  • CLI ecosystem: Cobra provides subcommands, help generation, flag parsing, and shell completion out of the box. Lipgloss and termenv handle cross-platform terminal styling. The CLI also supports dap --version with version, commit, and build date injected at compile time via go build -ldflags.
  • Performance: Compiled to a native binary with instant startup, unlike a Python-based CLI where interpreter overhead adds latency to every command
  • Distribution: Can be installed via Nix (nix develop or nix build .#dap) or downloaded as a pre-built binary from GitHub releases.

Why this matters for onboarding and daily work

Onboarding friction typically comes from two places: setting up the environment and discovering what commands exist. This branch eliminates both. nix develop provides a complete, reproducible environment with Python, uv, Go, all linters and formatters included — nothing to install manually. The dap CLI organizes every development task into discoverable command groups with built-in documentation. Contributing new tooling means writing Go with tests, not appending shell scripts. The Nix-based setup also prepares the project for GitHub Codespaces, where the same dev environment can be replicated in the cloud with zero additional configuration.

Appendix

dap welcome (TTY)

  ██████╗  █████╗ ██████╗          ██████╗
  ██╔══██╗██╔══██╗██╔══██╗        ██╔═══██╗
  ██║  ██║███████║██████╔╝ █████╗ ██║   ██║
  ██║  ██║██╔══██║██╔═══╝  ╚════╝ ██║   ██║
  ██████╔╝██║  ██║██║             ╚██████╔╝
  ╚═════╝ ╚═╝  ╚═╝╚═╝              ╚═════╝
  Data Archive Pipeline (DAP) - Orchestrator
  ETH Library Zurich

Versions
  python         3.12.12
  uv             0.9.28
  dagster        1.12.2

Environment
  nix flake      /Users/jc/PycharmProjects/dap ✓
  python venv    /Users/jc/PycharmProjects/dap/.venv ✓
  DAGSTER_HOME   /Users/jc/PycharmProjects/dap/.dagster ✓

Quick Start
  dap dev        Start the Dagster development server
  dap test       Run tests (pytest)
  dap check      Run all quality checks (ruff, mypy, pytest)

  Run 'dap --help' for all commands

dap --help

dap is the CLI for the Data Archive Pipeline (DAP) Orchestrator.

A Dagster-based orchestrator for processing digital assets following
the OAIS reference model. This tool provides commands for local development,
testing, code quality, and Kubernetes deployment.

Usage:
  dap [command]

Development:
  check       Run all quality checks
  dev         Start Dagster development server
  lint        Check code style and formatting
  test        Run pytest tests
  typecheck   Run type checking

Environment:
  clean       Remove .venv and caches
  reset       Clean and reinstall dependencies
  versions    Display tool versions
  welcome     Show welcome banner and environment info

Dagster:
  materialize Materialize Dagster assets
  run         Run Dagster job

Kubernetes:
  k8s         Kubernetes operations

CLI Development:
  cli         CLI development commands

Additional Commands:
  help        Help about any command

Flags:
  -h, --help       help for dap
      --no-color   Disable colored output
  -v, --version    version for dap

Use "dap [command] --help" for more information about a command.

Add styles.go with ETH brand colors, semantic color aliases, and
lipgloss styles for consistent CLI output. Includes NO_COLOR env var
support, TTY detection on stderr, and adaptive colors for terminal
accessibility.
Add root.go defining the main dap CLI entry point. Registers command
groups (Development, Environment, Dagster, Kubernetes, CLI Development)
and wires up subcommands from dev, env, dagster, k8s, and meta packages.
Includes --no-color global flag.
Replace just commands with dap CLI equivalents. Update prerequisites
to include Go, add CLI build instructions, and document all dap
commands. Add .cli/ to project structure overview.
Add tests for:
- ui/styles: color functions, TTY detection, CI detection
- ui/output: Title, Info, Success, Error, Warning output functions
- cmd/root: command groups, subcommand registration, flag parsing
- cmd/dev: command count, GroupID, individual command properties
- internal/exec: RunPassthrough execution
Add dap-cli flake as input and include the dap binary in devShell.
Adds Go and gomod2nix for CLI development. Replaces just with dap.
Delegate Python setup to .dev/scripts/uv-sync.sh and welcome banner
to dap welcome command. Cleaner shell initialization.
Enables direct access without port-forwarding for local k8s development.
Add main.go entry point, go.mod/sum for dependencies. Internal packages:
- exec: RunPassthrough for subprocess execution with live output
- ui: Title, Info, Success, Error, Warning output functions
Commands for Python development workflow:
- check: run all quality checks (lint + typecheck + test)
- dev: start Dagster development server
- lint: check/fix code style with Ruff
- test: run pytest test suite
- typecheck: run mypy type checking
Commands for development environment:
- clean: remove .venv and Python caches
- reset: clean and reinstall dependencies
- versions: show tool versions (Python, uv, Go, kubectl, helm)
- welcome: display project banner and quick start info
Commands for Dagster orchestration:
- materialize: materialize all Dagster assets
- run: execute the ingest_sip_job pipeline
Commands for local Kubernetes development:
- up: build image and deploy with Helm
- down: tear down deployment
- restart: rebuild and restart user code pod
- status: show pods and services
- logs: stream logs from user code pod
- shell: open shell in user code pod
Commands for maintaining the Go CLI itself:
- go test: run Go tests for the CLI
- go build: build the dap binary
- go fmt: format Go source code
Add flake.nix and package.nix for building dap with Nix. Uses
gomod2nix for reproducible Go dependency management.
Document CLI architecture, development workflow, testing, and
coding conventions for contributors.
Simple wrapper that checks for uv and runs sync with dev extras.
Called by .envrc during shell initialization.
All commands migrated to the Go-based dap CLI.
- Shorten description to "Development tooling for the DAP Orchestrator"
- Fix homepage URL to correct repository
- Change license from MIT to Apache 2.0
The dot prefix conventionally means hidden/infrastructure. Since this
is source code, rename to cli/ and update all references in flake.nix,
.gitignore, .pre-commit-config.yaml, README, CONTRIBUTING, and Go source.
Include the built dap binary in the cli-dev shell so that
`dap cli test` works during CLI development and in CI.
Add version/commit/date variables via ldflags so `dap --version`
reports the correct version when built through Nix or goreleaser.
Runs Go tests and linting (go vet, gofmt) inside Nix for environment
parity. Triggers only on cli/** changes.
Skip pipeline CI on CLI-only changes via paths-ignore.
Cross-compile the CLI for linux/darwin (amd64/arm64) on cli/v* tags.
Tests run in Nix for parity; goreleaser uses raw Go for cross-compilation.
Runs go vet and gofmt checks. Replaces inline shell scripts in CI
with a single command for consistency with dap cli test/build.
Replace fragile directory-walking logic with a single call to
`git rev-parse --show-toplevel`. Always returns an absolute path.
Add --fix mode that runs `gofmt -w` to auto-fix formatting before vet.
Extract inCliDir(), goVet(), repoRoot() helpers to DRY up the repeated
findCliDir+chdir pattern across build/test/lint commands. Fix nix build
to explicitly chdir to repo root so `dap cli build` works from any directory.
Update `dap go test/build` references to `dap cli test/build/lint`.
Add Linting section documenting --fix flag. Expand Building section
with step-by-step explanation of the Nix build chain.
Nix no longer injects a version via ldflags, so the binary uses the
default "dev" from version.go. Release builds get the real version
injected by GoReleaser from the git tag.
@jcardozo-eth jcardozo-eth self-assigned this Feb 11, 2026
@jcardozo-eth jcardozo-eth merged commit ba823b7 into main Feb 11, 2026
7 checks passed
@jcardozo-eth jcardozo-eth deleted the chore/dx branch February 19, 2026 01:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant