Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: release

# CLI-MCP-13R2 — release pipeline for the `instant` CLI.
#
# Fires on a semver tag push (`v*.*.*`). Cross-compiles via GoReleaser,
# generates SBOMs, signs the checksum file with sigstore cosign (keyless
# OIDC), and publishes everything to the GitHub Release page.
#
# Why tag-driven instead of branch-driven: the other backend services in
# instanode.dev auto-deploy on every push to `master` (CLAUDE.md rule 15),
# but a CLI binary has a different shape — users install once and pin to
# the latest published release. Tagging is the canonical "this is a real
# release, not a transient build" signal.

on:
push:
tags:
- "v*.*.*"

# Default permissions are read-only. Each job grants the minimum scope it
# needs. `id-token: write` is required for sigstore keyless signing via
# the GitHub OIDC issuer.
permissions:
contents: read

jobs:
goreleaser:
name: build, sign, publish
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write # publish artifacts to the release page
id-token: write # sigstore OIDC for keyless signing
attestations: write # SBOM attestation
steps:
# Full history + tags are required so GoReleaser can read the tag
# message and infer changelog scope.
- name: Checkout (full history + tags)
uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true

- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod

# Third-party actions are PINNED to a commit SHA per CSO supply-chain
# policy. Renovate / Dependabot manages bumps; never use a floating
# tag in this workflow.
- name: Install cosign (sigstore)
# pinned: tag v3.7.0
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac
with:
cosign-release: 'v2.4.1'

- name: Install syft (SBOM)
# pinned: tag v0.20.0
uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610

- name: Run GoReleaser
# pinned: tag v6.4.0
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a
with:
distribution: goreleaser
version: "~> v2"
args: release --clean
env:
# GITHUB_TOKEN is the per-job, repo-scoped, short-lived token —
# NOT a long-lived PAT. GoReleaser uses it to upload the
# release artifacts to the same repo.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ bin/

# Internal Claude Code skills
.claude/

# GoReleaser snapshot/release artifacts
dist/
164 changes: 164 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# .goreleaser.yml — release pipeline for the `instant` CLI.
#
# CLI-MCP-13R2 (BugBash QA round 2 strategic gap): the CLI had no release
# workflow. Every other backend service in instanode.dev auto-builds on push
# (CLAUDE.md rule 15); the CLI's only install path was `go install`, which
# requires a Go toolchain and pins the user to whatever HEAD happens to be.
#
# This config + .github/workflows/release.yml + install.sh together close
# that gap:
#
# * Tagging `vX.Y.Z` on master triggers cross-compiled builds for
# darwin / linux / windows × amd64 / arm64.
# * Binaries are stamped with the tag's version, the commit SHA, and the
# UTC build time (matches the Makefile's ldflag scheme — CLAUDE.md rule
# 14 build-SHA gate still applies via `instant --version`).
# * `instant_v0.2.0_darwin_arm64.tar.gz` etc. land on the GitHub release
# page; `checksums.txt` is signed by sigstore cosign (keyless OIDC).
# * `install.sh` curl-pipe-sh fetches the right archive for the user's
# platform.
#
# We intentionally do NOT publish to Homebrew / Scoop / apt yet — that's a
# follow-up PR. This release pipeline keeps the dependency footprint to:
# - goreleaser (pinned action SHA in release.yml)
# - syft (SBOM, pinned)
# - cosign (signing, pinned)
# Everything else is removed.

version: 2

project_name: instant

before:
hooks:
- go mod tidy

builds:
- id: instant
binary: instant
main: ./
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
# No 32-bit / mips targets — the agent surface is `INSTANT_API_URL` over
# HTTPS, which is hard to use from constrained devices anyway.
ignore:
# Windows on ARM64 ships, but it's a niche; revisit if anyone files
# an issue. Linux/arm64 + Darwin/arm64 cover the GH Actions runner
# matrix and Apple Silicon developer laptops.
- goos: windows
goarch: arm64
# ldflags mirror the Makefile's `make build` target so the release
# binary's `--version` line matches the source of truth. CLAUDE.md
# rule 14 (build-SHA gate) reads from these via `instant --version`.
ldflags:
- -s -w
- -X main.Version={{.Version}}
- -X main.Commit={{.ShortCommit}}
- -X main.BuildTime={{.Date}}

archives:
- id: instant-archive
# GoReleaser v2 renamed `name_template` keys — the format below is the
# v2 canonical layout: `<project>_<version>_<os>_<arch>`. install.sh
# depends on this exact pattern.
name_template: >-
{{ .ProjectName }}_{{ .Version }}_
{{- if eq .Os "darwin" }}darwin
{{- else if eq .Os "linux" }}linux
{{- else if eq .Os "windows" }}windows{{ end }}_
{{- .Arch }}
format_overrides:
- goos: windows
formats:
- zip
formats:
- tar.gz
files:
- LICENSE
- README.md

checksum:
name_template: "checksums.txt"
algorithm: sha256

# Sigstore keyless signing via GitHub OIDC. The release workflow grants
# `id-token: write` so cosign can mint a short-lived cert from Fulcio. No
# private keys to manage. Verification:
#
# cosign verify-blob \
# --certificate-identity-regexp 'https://github.com/InstaNode-dev/cli/.github/workflows/release.yml@.*' \
# --certificate-oidc-issuer https://token.actions.githubusercontent.com \
# --signature checksums.txt.sig \
# --certificate checksums.txt.pem \
# checksums.txt
signs:
- id: cosign-checksums
cmd: cosign
artifacts: checksums
signature: "${artifact}.sig"
certificate: "${artifact}.pem"
args:
- sign-blob
- "--output-signature=${signature}"
- "--output-certificate=${certificate}"
- "${artifact}"
- --yes
output: true

# SBOM generation (CycloneDX via syft). Lands alongside binaries on the
# release page. cyclonedx is the CSO/CISA-preferred format.
sboms:
- id: instant-sbom
artifacts: archive

release:
github:
owner: InstaNode-dev
name: cli
draft: false
prerelease: auto
name_template: "v{{.Version}}"
header: |
`instant` CLI release {{.Version}}.

## Install (one-liner)

```bash
curl -sSfL https://instanode.dev/install.sh | sh
```

Or download the archive for your platform from the assets below and
drop the binary on `$PATH`.

## Verify the release

Checksums are signed with sigstore cosign (keyless OIDC). To verify:

```bash
cosign verify-blob \
--certificate-identity-regexp 'https://github.com/InstaNode-dev/cli/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--signature checksums.txt.sig \
--certificate checksums.txt.pem \
checksums.txt
```

snapshot:
version_template: "{{ incpatch .Version }}-next"

changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^chore:'
- '^test:'
- '^ci:'
- Merge pull request
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,29 @@ Zero-friction infrastructure CLI for [instanode.dev](https://instanode.dev).

## Install

Pre-built binaries for darwin / linux × amd64 / arm64 (the curl-pipe-sh
script auto-detects your platform):

```bash
curl -sSfL https://instanode.dev/install.sh | sh
```

The installer downloads the latest release archive from
[GitHub Releases](https://github.com/InstaNode-dev/cli/releases), verifies
its SHA-256 against the signed `checksums.txt`, and drops the binary at
`/usr/local/bin/instant`. Set `INSTANT_INSTALL_DIR=$HOME/.local/bin` to
avoid sudo; set `INSTANT_VERSION=v0.2.0` to pin a specific release.

Or, with a Go toolchain already installed:

```bash
go install github.com/InstaNode-dev/cli@latest
```

Windows users: download the `.zip` from the
[releases page](https://github.com/InstaNode-dev/cli/releases) and add
`instant.exe` to your `PATH`.

## Usage

Every provisioning command requires a `--name` flag. The name must be 1–64
Expand Down
Loading
Loading