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
21 changes: 4 additions & 17 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,13 @@ permissions:
contents: read

jobs:
publish:
# Nudges proxy.golang.org to fetch the new version so it shows up on
# pkg.go.dev without waiting for the next scheduled crawl. Build and
# test validation happens in release.yml before the release is cut.
ping-pkg-go-dev:
if: startsWith(github.ref_name, 'v')
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod

- name: Vet
run: go vet ./...

- name: Build
run: go build ./cmd/dashboard

- name: Test
run: go test ./...

- name: Request pkg.go.dev indexing
run: |
URL="https://proxy.golang.org/github.com/agent-receipts/dashboard/@v/${GITHUB_REF_NAME}.info"
Expand Down
71 changes: 71 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
# Deployment environment lets you attach protection rules (required
# reviewers, wait timer, branch restrictions) to gate who can trigger
# a release and push to the Homebrew tap. Configure at:
# https://github.com/agent-receipts/dashboard/settings/environments
environment: release
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0

- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod

- name: Vet
run: go vet ./...

- name: Test
run: go test ./...

# Single GoReleaser run: builds, archives, pushes Homebrew formula.
# Release creation + binary upload happen in the next step with `gh`
# so the same dist/ artifacts (and therefore matching SHA256s) are
# used for both the formula and the release assets — re-running
# GoReleaser would produce different tar hashes.
- name: Build and publish Homebrew formula
uses: goreleaser/goreleaser-action@e24998b8b67b290c2fa8b7c14fcfa7de2c5c9b8c # v7
with:
distribution: goreleaser
version: "~> v2"
args: release --clean --skip=validate,announce
env:
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}

# Idempotent: if the release already exists (e.g. workflow re-run
# after a transient tap-push failure), upload assets to the
# existing release instead of failing on duplicate-create.
- name: Create GitHub release with binaries
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
prerelease_flag=""
if [[ "${GITHUB_REF_NAME}" == *-* ]]; then
prerelease_flag="--prerelease"
fi

if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
gh release upload "${GITHUB_REF_NAME}" --clobber \
dist/*.tar.gz \
dist/checksums.txt
else
gh release create "${GITHUB_REF_NAME}" \
--title "dashboard ${GITHUB_REF_NAME}" \
--generate-notes \
${prerelease_flag} \
dist/*.tar.gz \
dist/checksums.txt
fi
81 changes: 81 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
version: 2

project_name: dashboard

before:
hooks:
- go mod download

builds:
- id: dashboard
main: ./cmd/dashboard
binary: dashboard
env:
- CGO_ENABLED=0
goos:
- darwin
- linux
goarch:
- amd64
- arm64
ldflags:
- -s -w -X main.version=v{{.Version}}

archives:
- name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
formats: [tar.gz]
files:
- README.md
- LICENSE

checksum:
name_template: "checksums.txt"

# GitHub release is created outside GoReleaser so the workflow can upload
# the same dist/ artifacts to the release as were hashed into the Homebrew
# formula. Re-running GoReleaser would regenerate tarballs with different
# SHA256s and break `brew install`.
release:
disable: true

brews:
- name: dashboard
repository:
owner: agent-receipts
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
# Push the formula to a feature branch, then open a PR against
# main. Without an explicit branch, GoReleaser pushes to the
# default branch (main) and tries to open a main→main PR, which
# the GitHub API rejects — so the formula sneaks onto main and
# skips the `brew audit` gate.
branch: "bump-{{.ProjectName}}-{{.Version}}"
pull_request:
enabled: true
base:
branch: main
directory: Formula
url_template: "https://github.com/agent-receipts/dashboard/releases/download/v{{ .Version }}/{{ .ArtifactName }}"
homepage: "https://github.com/agent-receipts/dashboard"
description: "Local web UI for browsing Agent Receipts SQLite databases"
license: "Apache-2.0"
commit_author:
name: agent-receipts-bot
email: bot@agentreceipts.ai
commit_msg_template: "chore(dashboard): bump to v{{ .Version }}"
install: |
bin.install "dashboard"
test: |
system "#{bin}/dashboard", "--version"
# `brew outdated` and `brew livecheck` use this to detect new releases.
# Dashboard tags as plain `vX.Y.Z`, so `:github_latest` would work, but
# `:github_releases` with a regex keeps the strategy consistent with
# the other agent-receipts formulas. The regex matches stable tags
# only — prereleases like `v1.0.0-beta.1` are intentionally skipped
# so Homebrew users don't get bumped onto unstable versions.
custom_block: |
livecheck do
url "https://github.com/agent-receipts/dashboard"
strategy :github_releases
regex(/^v?(\d+(?:\.\d+)+)$/i)
Comment on lines +70 to +80
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The livecheck block comment says the regex “tolerates prereleases”, but regex(/^v?(\d+(?:\.\d+)+)$/i) will not match tags like v1.0.0-beta.1 (which this repo’s scripts/release.sh allows). Either adjust the regex to include prerelease identifiers, or update the comment to match the actual behavior (stable-only).

Copilot uses AI. Check for mistakes.
end
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ Lightweight local web UI for browsing [Agent Receipt](https://github.com/agent-r

## Install

Homebrew (macOS / Linux):

```sh
brew install agent-receipts/tap/dashboard
```

Pre-built binaries for darwin/linux (amd64, arm64) are attached to each [GitHub release](https://github.com/agent-receipts/dashboard/releases).

With a Go toolchain:

```sh
go install github.com/agent-receipts/dashboard/cmd/dashboard@latest
```
Expand Down
24 changes: 24 additions & 0 deletions cmd/dashboard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,36 @@ import (
"log"
"net/http"
"os"
"runtime/debug"

"github.com/agent-receipts/dashboard/internal/server"
"github.com/agent-receipts/dashboard/internal/store"
)

// version is set at build time via -ldflags "-X main.version=vX.Y.Z".
// Falls back to the module version from Go's build info (set automatically
// for binaries installed with `go install`), then to "dev".
var version string

func resolveVersion() string {
if version != "" {
return version
}
if info, ok := debug.ReadBuildInfo(); ok && info.Main.Version != "" && info.Main.Version != "(devel)" {
return info.Main.Version
}
return "dev"
}

func main() {
if len(os.Args) > 1 {
switch os.Args[1] {
case "-version", "--version":
fmt.Printf("dashboard %s\n", resolveVersion())
return
}
}

dbPath := flag.String("db", "", "path to receipts SQLite database")
host := flag.String("host", "127.0.0.1", "address to bind to (use 0.0.0.0 for all interfaces)")
port := flag.Int("port", 8080, "HTTP server port")
Expand Down
31 changes: 20 additions & 11 deletions scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ cd "$(git rev-parse --show-toplevel)"

REMOTE_NAME="${REMOTE:-origin}"

# Preflight: ensure required tools are available
# Preflight: ensure required tools are available. The release workflow
# handles the GitHub Release itself, so this script only needs git + go.
command -v go >/dev/null 2>&1 || fail "go is not installed"
command -v gh >/dev/null 2>&1 || fail "gh CLI is not installed — see https://cli.github.com"
gh auth status >/dev/null 2>&1 || fail "gh is not authenticated — run gh auth login"

[[ $# -eq 1 ]] || usage

Expand Down Expand Up @@ -76,17 +75,27 @@ go test ./... -count=1
echo ""
echo "--- All checks passed"
echo ""
echo "Will create release:"
echo "Will push tag:"
echo " Tag: $TAG"
echo " Title: v$VERSION"
echo ""
echo "The Release workflow (.github/workflows/release.yml) will build binaries,"
echo "publish the Homebrew formula, and create the GitHub release."
echo ""
read -rp "Proceed? [y/N] " confirm
[[ "$confirm" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; }

REPO_URL=$(gh repo view --json url -q '.url')
release_args=("$TAG" --title "v$VERSION" --generate-notes)
[[ "$VERSION" == *-* ]] && release_args+=(--prerelease)
gh release create "${release_args[@]}"
# Derive the GitHub web URL from the remote so this script doesn't need
# the `gh` CLI. Handles GitHub HTTPS remotes and common GitHub SSH forms
# (SCP-style `git@github.com:` and URL-style `ssh://git@github.com/`).
REMOTE_URL=$(git remote get-url "$REMOTE_NAME")
REPO_URL=$(echo "$REMOTE_URL" | sed -E \
-e 's|^git@github\.com:|https://github.com/|' \
-e 's|^ssh://git@github\.com/|https://github.com/|' \
-e 's|\.git$||')

git tag -a "$TAG" -m "dashboard $TAG"
git push "$REMOTE_NAME" "$TAG"
echo ""
echo "==> Released dashboard v$VERSION"
echo " ${REPO_URL}/releases/tag/$TAG"
echo "==> Pushed tag $TAG"
echo " Follow the release workflow: ${REPO_URL}/actions/workflows/release.yml"
echo " Release page: ${REPO_URL}/releases/tag/$TAG"
Loading