From 4695e373588671c06ee9f255e09da1b4ac1ea272 Mon Sep 17 00:00:00 2001 From: Brian Miller Date: Sat, 22 Aug 2026 19:33:19 +0000 Subject: [PATCH 1/2] chore: add the MIT license The CLI is documented as a first-class component of the platform but carried no license file, which means all rights reserved by default. Same MIT text as the platform repo. Co-Authored-By: Claude Opus 5 (1M context) --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6607043 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025-2026 Brian Miller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From c90a0533a5749641c28a8ddecce6c8b949d6c7dd Mon Sep 17 00:00:00 2001 From: Brian Miller Date: Sat, 22 Aug 2026 19:40:52 +0000 Subject: [PATCH 2/2] build: goreleaser release pipeline and CI The CLI is documented as a first-class component of the platform but had no release tooling and no CI at all: the only way to get a binary was `go build -o stone`, which meant every user compiled their own and `--version` reported "dev". goreleaser builds six targets from one runner (nothing needs cgo) and stamps cmd.version, so an installed `stone` can say what it is. windows/arm64 is included here and excluded in the platform on purpose: this is a laptop and CI-runner tool. CI cross-compiles the whole release matrix on every pull request, so a broken target is found before a tag is pushed rather than by goreleaser failing on it. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 53 ++++++++++++++++++++ .github/workflows/release.yml | 39 +++++++++++++++ .gitignore | 3 ++ .goreleaser.yaml | 92 +++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .goreleaser.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..93804d1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +# Ordinary build hygiene on every push and pull request. +# +# There are no tests in this repo yet; `go test ./...` is here anyway so that the +# first one added is run rather than merely committed. The build step is what +# currently catches breakage, and it is a real check for a CLI whose command +# table is hand-maintained. +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Check formatting + run: | + unformatted=$(gofmt -l .) + if [ -n "$unformatted" ]; then + echo "Files not gofmt-formatted:" + echo "$unformatted" + exit 1 + fi + + - name: Vet + run: go vet ./... + + - name: Test + run: go test ./... + + - name: Build + run: go build ./... + + # The release matrix, checked on every PR rather than discovered on a tag. + # Cheap: the stdlib for each target is cached by setup-go. + - name: Cross-compile the release targets + run: | + for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do + echo "==> $target" + CGO_ENABLED=0 GOOS=${target%/*} GOARCH=${target#*/} go build -o /dev/null . + done diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5830a5c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +# Cuts a release when a v* tag is pushed: cross-compiled binaries via goreleaser, +# checksums, and a GitHub release whose notes come from the commits in the tag. +# +# No container image here, unlike the platform's release workflow -- this binary +# is installed onto a host, not run as a service in a cluster, and an image would +# be a second definition of the same artifact with nobody to consume it. +name: Release + +on: + push: + tags: ['v*'] + +permissions: + contents: write # create the GitHub release + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # goreleaser derives the version and the changelog from tags, and a + # shallow clone has neither. + fetch-depth: 0 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Test + run: go test ./... + + - uses: goreleaser/goreleaser-action@v6 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index a0abd47..7a1416c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ *.test *.out .DS_Store + +# goreleaser output +dist/ diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..399fd47 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,92 @@ +# Release configuration for the client CLI. +# +# One binary, six targets, no build hooks -- `stone` is a plain Go client with +# nothing embedded in it, which is the whole difference between this file and the +# platform's. It talks to a running server over HTTPS and NATS, so there is no +# console to compile first and no reason to ship anything but the binary. +# +# The binary is `stone`; the repo and module are `stone-cli`. Archives are named +# after the binary, because that is the name a user types and searches for. +version: 2 + +project_name: stone-cli + +builds: + - id: stone + main: . + binary: stone + env: + # Nothing in the dependency tree needs cgo, so every target below + # cross-compiles from one runner. + - CGO_ENABLED=0 + ldflags: + # cmd.version is unexported, which -X handles fine; it is read into + # rootCmd.Version at init, after the linker has written it. + - -s -w -X github.com/stone-age-io/stone-cli/cmd.version={{.Version}} + goos: [linux, darwin, windows] + # windows/arm64 is included here and excluded in the platform, deliberately: + # this is a laptop and CI-runner tool, and ARM Windows laptops exist. A + # server binary has no such audience. + goarch: [amd64, arm64] + +archives: + - id: stone + ids: [stone] + formats: [tar.gz] + format_overrides: + - goos: windows + formats: [zip] + files: + - LICENSE + - README.md + # The audience-neutral description of the command surface, for people + # driving the CLI from another tool or assistant. It travels with the + # binary because it documents exactly this version's commands. + - SKILLS.md + name_template: >- + stone_ + {{- .Version }}_ + {{- .Os }}_ + {{- .Arch }} + +checksum: + name_template: 'checksums.txt' + algorithm: sha256 + +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' + - '^chore:' + - '^ci:' + +release: + github: + owner: stone-age-io + name: stone-cli + header: | + ## stone {{ .Tag }} + + The command-line client for the Stone-Age.io platform. Extract the binary + onto a laptop or CI runner and point it at a running server: + + ``` + stone context create local --url https://platform.example.com + stone auth login + ``` + + **`stone` is not `stone-age`.** This is the client; `stone-age` is the + control-plane server, released from the + [platform](https://github.com/stone-age-io/platform) repo. They share a + project, not a job. + footer: | + Pre-1.0: treat a minor version as potentially breaking, and pin what you + deploy. + + The CLI's field list is hand-maintained rather than derived from the + platform's `schema.json`, so a given `stone` can lag a platform release. If a + field exists in the console but has no flag, that is why. + + **Full changelog**: https://github.com/stone-age-io/stone-cli/commits/{{ .Tag }}