make build
make testFor more granular test commands (make test-unit, make test-integration, running a single test), see the "Commands" section in CLAUDE.md.
The development guidelines live in docs/:
- Code style — Go conventions, dependencies, error handling
- Project structure — directory layout, package responsibilities
- CLI naming conventions — command names, aliases, asset kind display names
- Testing — integration tests, fixtures, mock server
- Documentation — prose rules, attribute keys in examples
- GitHub Actions — setup action, send-log-event, keeping actions in sync
The repository is a Nix flake (flake.nix) that packages the CLI and a Home Manager module.
End-user instructions live in the README; this section is for maintaining the packaging itself.
packages.<system>.dash0(also.default) — the CLI built from source withbuildGoModule(nix/package.nix). This is canonical: it builds the current tree, so it validates the repo under Nix, supports unreleased revisions, and is the form a future nixpkgs submission would take.overlays.default— exposes the source package aspkgs.dash0.homeManagerModules.default— declarativeprograms.dash0profiles (see below).checks.<system>.{hm-assertions,hm-merge}— unit tests for the module logic, run bynix flake check.devShells.<system>.default— Go toolchain plus the lint and changelog tooling.
The non-flake default.nix and shell.nix are thin shims for systems without flakes enabled.
buildGoModule fetches Go dependencies in a fixed-output derivation whose content is pinned by vendorHash in nix/package.nix.
That hash is derived from go.mod/go.sum, so it only changes when dependencies change — editing Go source does not affect it.
When go.mod/go.sum change, refresh it:
make update-vendor-hashOn pull requests this is automated: the Nix vendorHash workflow recomputes the hash and commits the fix back to the branch, so Dependabot and other dependency PRs heal themselves.
Note that CI builds the PR's merge commit, so a dependency bump landing on main can make an unrelated open PR's Nix build go red until its branch is updated.
The pre-built binary is not packaged in this repository. A binary derivation that pins release artifact hashes cannot live in the source tree: the hashes depend on artifacts built from a tag, so the tagged commit could never contain the correct hashes for its own release (the same reason the Homebrew cask lives in its own repo).
Instead, the GoReleaser nix publisher (see .goreleaser.yaml) writes pkgs/dash0/default.nix to a separate Nix User Repository, dash0hq/nur, after each release — the direct counterpart to the homebrew_casks publisher.
Because the manifest is generated post-build in its own repo, every released version is exactly reproducible, with no per-tag lag and no in-source hash upkeep.
The release binary is built CGO_ENABLED=0 (see .goreleaser.yaml), so it is statically linked and runs on NixOS without autoPatchelfHook.
The dash0hq/nur repo is already set up: it holds a hand-maintained flake.nix that exposes packages from pkgs/*, and a committed placeholder pkgs/dash0/default.nix that GoReleaser overwrites on each release. Its flake.nix looks like:
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system: {
packages.dash0 = nixpkgs.legacyPackages.${system}.callPackage ./pkgs/dash0/default.nix { };
});
}The GoReleaser GITHUB_TOKEN (REPOSITORY_FULL_ACCESS_GITHUB_TOKEN) must have write access to that repo.
Until the first release publishes through the nix block, github:dash0hq/nur#dash0 builds the committed placeholder, which prints a "no release published yet" message and exits non-zero; the first release replaces it with the real binary.
nix/hm-module.nix renders ~/.dash0/profiles.json from declared profiles.
Because the CLI rewrites that file at runtime (OAuth refresh, login, logout), the module cannot own it outright: an activation script merges declared profiles into the live file rather than replacing it.
The merge logic is factored out so it can be unit-tested:
nix/merge-profiles.jq— the merge program. It upserts declared profiles, preserves runtime-acquired OAuth state for profiles the user has logged into, seedsoauth: {}for new OAuth profiles, and injects static tokens read fromauthTokenFileat activation time. Tested bychecks.<system>.hm-merge.nix/assertions.nix— a purelib -> cfg -> [assertion]function (profile validation, including theactiveProfileguard underpruneUndeclared). Tested bychecks.<system>.hm-assertions.
Run the checks with nix flake check.
After changing the module's logic, add or update a case in nix/tests/.
The nix flake check warning unknown flake output 'homeManagerModules' is benign — that output name is a Home Manager convention the Nix CLI does not validate.
The source package version is pinned in flake.nix (the version binding) and feeds the -X main.version ldflag.
The release automation keeps it in step (see Releasing) — you do not bump it by hand.
Every pull request that affects end users must include a changelog entry. See docs/changelog-maintenance.md for full instructions on creating, validating, and previewing entries.
Quick start:
make chlog-new # create .chloggen/<branch-name>.yaml
# edit the file
make chlog-validate # check it is well-formedIf the change does not affect end users (refactoring, CI, etc.), prefix the PR title with chore or add the "Skip Changelog" label.
Releases are fully automated via GitHub Actions.
- Go to Actions > Prepare Release.
- Click "Run workflow".
- Choose the version bump type (
major,minor, orpatch); the next version is computed from the highest existing tag. - Click "Run workflow".
The workflow automatically:
- Updates
CHANGELOG.mdwith entries from.chloggen/. - Removes the processed
.chloggen/*.yamlfiles. - Bumps the
versioninflake.nix(the source Nix package). - Commits the changes to
main. - Creates and pushes the version tag.
- Triggers the Release workflow, which builds and publishes the release (GitHub Release, Docker images, Homebrew cask, and the NUR binary package).
The flake.nix bump lands in the tagged commit, so nix build github:dash0hq/dash0-cli/v<version> reports the right version.
The binary package is published by GoReleaser to dash0hq/nur (see Binary distribution).
Follow Semantic Versioning:
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes (backward compatible)