Skip to content

Commit b79d4fb

Browse files
authored
Merge pull request #67 from ran-codes/feat/release-binary-format
feat: standalone binary releases with install script
2 parents 8e63094 + 94684c4 commit b79d4fb

8 files changed

Lines changed: 283 additions & 54 deletions

File tree

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,56 @@
1-
# Deployment Checklist
1+
# Deployment
22

3-
## 1. Pre-Release — Feature Readiness
3+
## Matrix Build vs Goreleaser
4+
5+
**Matrix build** (used by notion-sync): You write explicit `go build` commands in a GitHub Actions matrix — one job per OS/arch combo. Simple but verbose. Outputs standalone binaries.
6+
7+
**Goreleaser** (used by zenodo-cli): A declarative tool that reads `.goreleaser.yaml` and handles building, packaging, checksums, and changelogs in one step. Less CI code, supports multi-binary projects natively. By default produces archives (tar.gz/zip), but can output raw binaries with `format: binary`.
8+
9+
zenodo-cli uses goreleaser with `format: binary` so we get the simplicity of goreleaser config with the same standalone-binary UX as notion-sync.
10+
11+
## Feature Comparison
12+
13+
| Feature | notion-sync (matrix) | zenodo-cli (goreleaser) | ELI5 |
14+
|---|---|---|---|
15+
| Build system | Manual `go build` in CI matrix | Goreleaser (declarative yaml) | How the binaries get compiled in CI |
16+
| Release artifacts | Standalone binaries (`name-os-arch`) | Standalone binaries (via `format: binary`) | What users actually download |
17+
| macOS/Linux install | `curl \| bash` install script | `curl \| bash` install script | One-liner to install on Mac/Linux |
18+
| Windows install | Scoop (points at `.exe`) | Scoop (points at `.exe`) | One-liner to install on Windows |
19+
| Checksums | `sha256sum` in CI step | Goreleaser generates `checksums.txt` | Verifies download wasn't corrupted |
20+
| Version injection | `-X main.version=` in CI | `-X internal/cli.version=` via ldflags | Embeds version number into the binary at build time |
21+
| Scoop manifest update | CI step with `jq` | CI step with `jq` | Auto-updates the Scoop package after release |
22+
| Multi-binary support | Separate build line per binary | Multiple `builds:` entries in yaml | How we ship both `zenodo` and `zenodo-mcp` |
23+
| Changelog | GitHub auto-generated | Goreleaser with commit filters | Release notes on the GitHub release page |
24+
25+
## Checklist
26+
27+
### 1. Pre-Release — Feature Readiness
428

529
- [ ] All features intended for this release are merged to `main`
6-
- [ ] Any commands not ready are commented out (e.g. deposit write, access links in v0.1)
730
- [ ] `go build ./...` and `go test ./...` pass on `main`
8-
- [ ] Rebuild binary (`go build -o zenodo.exe ./cmd/zenodo`) and smoke-test key commands
31+
- [ ] Rebuild binaries and smoke-test key commands
932

10-
## 2. Pre-Release — CI/CD Readiness
33+
### 2. Pre-Release — CI/CD Readiness
1134

1235
- [ ] `.github/workflows/release.yml` exists and triggers on `v*` tags
13-
- [ ] `.goreleaser.yaml` is configured (platforms, ldflags, archives, checksums)
14-
- [ ] `bucket/zenodo.json` Scoop manifest exists
36+
- [ ] `.goreleaser.yaml` is configured (platforms, ldflags, format: binary)
37+
- [ ] `bucket/zenodo-cli.json` Scoop manifest exists
1538
- [ ] Release workflow includes Scoop manifest auto-update step
39+
- [ ] `scripts/install.sh` exists for macOS/Linux
1640
- [ ] Version injection works (`internal/cli/version.go` has ldflags vars)
1741

18-
## 3. Tag & Release
42+
### 3. Tag & Release
1943

2044
- [ ] Ensure you're on `main` with a clean working tree
2145
- [ ] Tag: `git tag v<VERSION>` (e.g. `git tag v0.1.0`)
2246
- [ ] Push tag: `git push origin v<VERSION>`
2347
- [ ] Monitor the GitHub Actions release workflow for success
2448

25-
## 4. Post-Release Verification
49+
### 4. Post-Release Verification
2650

2751
- [ ] GitHub release page has binaries for all platforms (linux/darwin/windows × amd64/arm64)
2852
- [ ] `checksums.txt` is included in the release assets
29-
- [ ] `bucket/zenodo.json` was auto-updated with new version and hash (check the commit on `main`)
30-
- [ ] Scoop install works: `scoop bucket add zenodo https://github.com/ran-codes/zenodo-cli && scoop install zenodo`
53+
- [ ] `bucket/zenodo-cli.json` was auto-updated with new version and hash
54+
- [ ] Scoop install works: `scoop bucket add zenodo-cli https://github.com/ran-codes/zenodo-cli && scoop install zenodo-cli`
55+
- [ ] macOS install works: `curl -fsSL ... | bash`
3156
- [ ] `zenodo version` shows correct version, commit, and date
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Deployment — CI/CD Design
2+
3+
## Problem
4+
5+
zenodo-cli ships two binaries (`zenodo` + `zenodo-mcp`) and needs to support three install methods:
6+
7+
1. **macOS/Linux**: `curl | bash` one-liner
8+
2. **Windows**: Scoop package manager
9+
3. **Manual**: Download from GitHub Releases
10+
11+
The original goreleaser config produced tar.gz/zip archives. This made `curl | bash` impractical (download archive → extract → find binary → move to PATH) and diverged from the pattern established in notion-sync.
12+
13+
## Options Considered
14+
15+
### Option A: Matrix build (notion-sync pattern)
16+
17+
Manual `go build` commands in a GitHub Actions matrix — one job per OS/arch.
18+
19+
```yaml
20+
strategy:
21+
matrix:
22+
include:
23+
- goos: darwin
24+
goarch: arm64
25+
- goos: linux
26+
goarch: amd64
27+
# ...
28+
steps:
29+
- run: go build -o zenodo-${{ matrix.goos }}-${{ matrix.goarch }} ./cmd/zenodo
30+
- run: go build -o zenodo-mcp-${{ matrix.goos }}-${{ matrix.goarch }} ./cmd/zenodo-mcp
31+
```
32+
33+
**Pros**: Simple, explicit, identical to notion-sync.
34+
**Cons**: Verbose CI config. Every new binary = more lines. No auto-changelog. Must manually handle checksums, ldflags, etc.
35+
36+
### Option B: Goreleaser with archives (original)
37+
38+
Goreleaser bundles both binaries into tar.gz (Unix) / zip (Windows) per platform.
39+
40+
**Pros**: Minimal config. Multi-binary is one yaml entry.
41+
**Cons**: Archives break `curl | bash`. Users must extract before using. Diverges from notion-sync UX.
42+
43+
### Option C: Goreleaser with `format: binary` (chosen)
44+
45+
Goreleaser outputs standalone binaries (no archive wrapping). Names follow `{name}-{os}-{arch}` pattern.
46+
47+
**Pros**: Declarative config. Multi-binary support. Auto-changelog. Checksums. Same standalone-binary UX as notion-sync.
48+
**Cons**: Slightly different from notion-sync's CI internals (goreleaser vs matrix), but identical end result.
49+
50+
## Implementation
51+
52+
### Goreleaser config (`.goreleaser.yaml`)
53+
54+
Two `builds:` entries — one for `zenodo`, one for `zenodo-mcp`. Both use `binary: {name}-{{ .Os }}-{{ .Arch }}` naming. Archives section set to `format: binary` so no tar.gz/zip wrapping.
55+
56+
### Release workflow (`.github/workflows/release.yml`)
57+
58+
Triggered on `v*` tags. Runs goreleaser, then updates the Scoop manifest with checksums for both `.exe` files and commits to main.
59+
60+
### Install script (`scripts/install.sh`)
61+
62+
Detects OS/arch, downloads both `zenodo` and `zenodo-mcp` binaries from the latest GitHub release, installs to `/usr/local/bin`. Uses `sudo` only if needed.
63+
64+
### Scoop manifest (`bucket/zenodo-cli.json`)
65+
66+
Downloads both `.exe` files. Uses an `installer` script to rename from `zenodo-windows-amd64.exe` → `zenodo.exe` (and same for mcp). Both listed in `bin` array.
67+
68+
## Release artifacts
69+
70+
Each release produces:
71+
72+
| File | Description |
73+
|------|-------------|
74+
| `zenodo-darwin-amd64` | CLI — macOS Intel |
75+
| `zenodo-darwin-arm64` | CLI — macOS Apple Silicon |
76+
| `zenodo-linux-amd64` | CLI — Linux x64 |
77+
| `zenodo-linux-arm64` | CLI — Linux ARM |
78+
| `zenodo-windows-amd64.exe` | CLI — Windows x64 |
79+
| `zenodo-windows-arm64.exe` | CLI — Windows ARM |
80+
| `zenodo-mcp-darwin-amd64` | MCP server — macOS Intel |
81+
| `zenodo-mcp-darwin-arm64` | MCP server — macOS Apple Silicon |
82+
| `zenodo-mcp-linux-amd64` | MCP server — Linux x64 |
83+
| `zenodo-mcp-linux-arm64` | MCP server — Linux ARM |
84+
| `zenodo-mcp-windows-amd64.exe` | MCP server — Windows x64 |
85+
| `zenodo-mcp-windows-arm64.exe` | MCP server — Windows ARM |
86+
| `checksums.txt` | SHA-256 checksums for all files |

.github/workflows/release.yml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,29 @@ jobs:
3030
- name: Update Scoop manifest
3131
run: |
3232
VERSION="${GITHUB_REF_NAME#v}"
33-
SHA256=$(grep "zenodo_${VERSION}_windows_amd64.zip" dist/checksums.txt | awk '{print $1}')
33+
SHA256_ZENODO=$(grep 'zenodo-windows-amd64.exe' dist/checksums.txt | awk '{print $1}')
34+
SHA256_MCP=$(grep 'zenodo-mcp-windows-amd64.exe' dist/checksums.txt | awk '{print $1}')
3435
3536
jq \
3637
--arg ver "$VERSION" \
37-
--arg hash "$SHA256" \
38-
'.version = $ver | .architecture."64bit".url = "https://github.com/ran-codes/zenodo-cli/releases/download/v\($ver)/zenodo_\($ver)_windows_amd64.zip" | .architecture."64bit".hash = $hash' \
39-
bucket/zenodo.json > bucket/zenodo.json.tmp
38+
--arg hash_zenodo "$SHA256_ZENODO" \
39+
--arg hash_mcp "$SHA256_MCP" \
40+
'.version = $ver
41+
| .architecture."64bit".url[0] = "https://github.com/ran-codes/zenodo-cli/releases/download/v\($ver)/zenodo-windows-amd64.exe"
42+
| .architecture."64bit".url[1] = "https://github.com/ran-codes/zenodo-cli/releases/download/v\($ver)/zenodo-mcp-windows-amd64.exe"
43+
| .architecture."64bit".hash[0] = $hash_zenodo
44+
| .architecture."64bit".hash[1] = $hash_mcp' \
45+
bucket/zenodo-cli.json > bucket/zenodo-cli.json.tmp
4046
41-
mv bucket/zenodo.json.tmp bucket/zenodo.json
47+
mv bucket/zenodo-cli.json.tmp bucket/zenodo-cli.json
4248
4349
- name: Commit updated Scoop manifest
4450
run: |
4551
git config user.name "github-actions[bot]"
4652
git config user.email "github-actions[bot]@users.noreply.github.com"
4753
git fetch origin main
4854
git checkout main
49-
git add bucket/zenodo.json
55+
git add bucket/zenodo-cli.json
5056
git diff --cached --quiet && echo "No changes to commit" && exit 0
5157
git commit -m "scoop: update manifest to v${GITHUB_REF_NAME#v}"
5258
git push origin main

.goreleaser.yaml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ before:
88
builds:
99
- id: zenodo
1010
main: ./cmd/zenodo
11-
binary: zenodo
11+
binary: zenodo-{{ .Os }}-{{ .Arch }}
12+
no_unique_dist_dir: true
1213
env:
1314
- CGO_ENABLED=0
1415
goos:
@@ -26,7 +27,8 @@ builds:
2627

2728
- id: zenodo-mcp
2829
main: ./cmd/zenodo-mcp
29-
binary: zenodo-mcp
30+
binary: zenodo-mcp-{{ .Os }}-{{ .Arch }}
31+
no_unique_dist_dir: true
3032
env:
3133
- CGO_ENABLED=0
3234
goos:
@@ -40,11 +42,7 @@ builds:
4042
- -s -w
4143

4244
archives:
43-
- format: tar.gz
44-
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
45-
format_overrides:
46-
- goos: windows
47-
format: zip
45+
- format: binary
4846

4947
checksum:
5048
name_template: "checksums.txt"

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,24 @@ Wraps the Zenodo REST API for record lookup, community browsing, license search,
66

77
## Install
88

9+
### Install script (macOS / Linux)
10+
11+
```sh
12+
curl -fsSL https://raw.githubusercontent.com/ran-codes/zenodo-cli/main/scripts/install.sh | bash
13+
```
14+
915
### Scoop (Windows)
1016

1117
```powershell
1218
## Install Scoop if you don't have it: https://scoop.sh
1319
# irm get.scoop.sh | iex
14-
scoop bucket add zenodo https://github.com/ran-codes/zenodo-cli
15-
scoop install zenodo
20+
scoop bucket add zenodo-cli https://github.com/ran-codes/zenodo-cli
21+
scoop install zenodo-cli
1622
```
1723

1824
### Manual download
1925

20-
Download the binary for your platform from [GitHub Releases](https://github.com/ran-codes/zenodo-cli/releases), rename it to `zenodo` (or `zenodo.exe` on Windows), and add it to your PATH.
26+
Download the binaries for your platform from [GitHub Releases](https://github.com/ran-codes/zenodo-cli/releases) and add them to your PATH.
2127

2228
## Usage
2329

bucket/zenodo-cli.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"version": "0.0.0",
3+
"description": "CLI tool for managing metadata, searching records, and querying assets on Zenodo.",
4+
"homepage": "https://github.com/ran-codes/zenodo-cli",
5+
"license": "MIT",
6+
"architecture": {
7+
"64bit": {
8+
"url": [
9+
"https://github.com/ran-codes/zenodo-cli/releases/download/v0.0.0/zenodo-windows-amd64.exe",
10+
"https://github.com/ran-codes/zenodo-cli/releases/download/v0.0.0/zenodo-mcp-windows-amd64.exe"
11+
],
12+
"hash": [
13+
"",
14+
""
15+
]
16+
}
17+
},
18+
"installer": {
19+
"script": [
20+
"Copy-Item \"$dir\\zenodo-windows-amd64.exe\" \"$dir\\zenodo.exe\"",
21+
"Copy-Item \"$dir\\zenodo-mcp-windows-amd64.exe\" \"$dir\\zenodo-mcp.exe\""
22+
]
23+
},
24+
"bin": ["zenodo.exe", "zenodo-mcp.exe"],
25+
"checkver": {
26+
"github": "https://github.com/ran-codes/zenodo-cli"
27+
},
28+
"autoupdate": {
29+
"architecture": {
30+
"64bit": {
31+
"url": [
32+
"https://github.com/ran-codes/zenodo-cli/releases/download/v$version/zenodo-windows-amd64.exe",
33+
"https://github.com/ran-codes/zenodo-cli/releases/download/v$version/zenodo-mcp-windows-amd64.exe"
34+
]
35+
}
36+
},
37+
"hash": {
38+
"url": "https://github.com/ran-codes/zenodo-cli/releases/download/v$version/checksums.txt"
39+
}
40+
}
41+
}

bucket/zenodo.json

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)