diff --git a/flake/devShell.nix b/flake/devShell.nix index 0a1aceb..17f8557 100644 --- a/flake/devShell.nix +++ b/flake/devShell.nix @@ -9,6 +9,7 @@ _: { packages = (with pkgs; [ go + just ]) ++ lib.attrValues config.treefmt.build.programs; diff --git a/internal/cli/man.go b/internal/cli/man.go index 3586000..ec5d585 100644 --- a/internal/cli/man.go +++ b/internal/cli/man.go @@ -31,7 +31,7 @@ func newManCommand(_ *app.Service) *cobra.Command { header := &doc.GenManHeader{ Title: "tg", Section: "1", - Source: "tg", + Source: "tg " + version, } return doc.GenManTree(cmd.Root(), header, dir) }, diff --git a/internal/cli/root.go b/internal/cli/root.go index a246131..9db4b3a 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -17,6 +17,11 @@ const ( // here, to avoid clashes with other local services. oauthCallbackHost = "127.0.0.1" oauthCallbackPath = "/callback" + + // version is the CLI version reported by --version and the man page + // header. It is bumped by `just release` at tag time; on master between + // releases it reads as the last released version. + version = "0.3.0" ) func NewRoot(service *app.Service) *cobra.Command { @@ -27,6 +32,7 @@ func newRoot(service *app.Service, defaultKnot, defaultSSHPort, defaultProtocol rootCmd := &cobra.Command{ Use: "tg", Short: "A CLI for Tangled", + Version: version, SilenceErrors: true, SilenceUsage: true, } diff --git a/internal/cli/root_test.go b/internal/cli/root_test.go index b72edc7..91bd058 100644 --- a/internal/cli/root_test.go +++ b/internal/cli/root_test.go @@ -32,6 +32,17 @@ func TestNewRootCreatesIndependentCommandState(t *testing.T) { } } +func TestVersionFlag(t *testing.T) { + var output bytes.Buffer + err := ExecuteWith([]string{"--version"}, nil, &output, &bytes.Buffer{}) + if err != nil { + t.Fatalf("ExecuteWith(--version) error = %v", err) + } + if want := "tg version " + version + "\n"; output.String() != want { + t.Errorf("--version output = %q, want %q", output.String(), want) + } +} + func TestExecuteWithRendersErrors(t *testing.T) { var errorOutput bytes.Buffer err := ExecuteWith([]string{"issue", "edit", "abc123"}, nil, &bytes.Buffer{}, &errorOutput) diff --git a/justfile b/justfile new file mode 100644 index 0000000..6d6e2e2 --- /dev/null +++ b/justfile @@ -0,0 +1,50 @@ +# Show available recipes +default: + @just --list + +# Cut a new release: bump version, run the gate, commit, tag, push +release version: + #!/usr/bin/env bash + set -euo pipefail + version="{{version}}" + tag="v${version}" + versionfile="internal/cli/root.go" + + if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "error: '$version' is not valid semver (expected X.Y.Z)" >&2 + exit 1 + fi + + branch="$(git symbolic-ref --short HEAD)" + if [ "$branch" != "master" ]; then + echo "error: must be on master (currently on $branch)" >&2 + exit 1 + fi + + if git rev-parse --verify --quiet "refs/tags/${tag}" >/dev/null; then + echo "error: tag ${tag} already exists" >&2 + exit 1 + fi + + nix fmt + + dirty="$(git status --porcelain -- ':!'"${versionfile}")" + if [ -n "$dirty" ]; then + echo "error: working tree has changes outside ${versionfile}:" >&2 + echo "$dirty" >&2 + exit 1 + fi + + nix build + + sed -i -E "s/^(\s*version\s*=\s*)\"[^\"]+\"/\1\"${version}\"/" "${versionfile}" + if ! grep -qE "version\s*=\s*\"${version}\"" "${versionfile}"; then + echo "error: failed to update version in ${versionfile}" >&2 + exit 1 + fi + + git add "${versionfile}" + git commit -m "release: ${tag}" + git tag -a "${tag}" -m "${tag}" + git push upstream master "${tag}" + git push origin master "${tag}"