Introduce Goreleaser (#366)

<!-- Provide a brief summary of your changes -->

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
The following PR introduces Goreleaser for the releases of the registry
server and the publisher CLI.

**Details:**
- Builds registry and publisher binaries for Linux, macOS, and Windows
(amd64 + arm64)
- Leverage GitHub's release process by creating releases directly from
GitHub's interface
- Added .goreleaser.yaml configuration
- New .github/workflows/release.yml workflow triggered by GitHub
releases
- Updated Docker tagging in deploy workflow (:latest and :semver(the
tag) for releases, :main-YYYYMMDD-sha for testing/development)
- Consolidated version handling in main.go files
- Added release documentation

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->

Locally (but we have to test it by creating a release, i.e. v0.0.1 once
this gets merged)

## Breaking Changes
<!-- Will users need to update their code or configurations? -->
Yes, the notion of `latest` for the container image is now different

## Types of changes
<!-- What types of changes does your code introduce? Put an `x` in all
the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [ ] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [ ] My code follows the repository's style guidelines
- [ ] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed

## Additional context
<!-- Add any other context, implementation notes, or design decisions
-->
Related to #358

---------

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
This commit is contained in:
Radoslav Dimitrov 2025-09-08 21:03:39 +03:00 • committed by GitHub
commit 16790150e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 273 additions and 65 deletions

View file

@ -36,8 +36,8 @@ jobs:
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha,prefix=main-{{date 'YYYYMMDD'}}-,enable={{is_default_branch}}
type=raw,value=main,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5

74
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,74 @@
name: Release
on:
release:
types: [published]
permissions:
contents: write
packages: write
id-token: write # needed for signing
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24.x'
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker:
runs-on: ubuntu-latest
needs: goreleaser
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

121
.goreleaser.yaml Normal file
View file

@ -0,0 +1,121 @@
# GoReleaser configuration for MCP Registry
version: 2
# Build configuration
builds:
# Registry server binary
- id: registry
binary: registry
main: ./cmd/registry
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.Version={{.Version}}
- -X main.GitCommit={{.FullCommit}}
- -X main.BuildTime={{.Date}}
# Publisher CLI tool binary
- id: publisher
binary: mcp-publisher
main: ./cmd/publisher
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w
- -X main.Version={{.Version}}
- -X main.GitCommit={{.FullCommit}}
- -X main.BuildTime={{.Date}}
# This section defines whether we want to release the source code too.
source:
enabled: true
# This section defines how to generate the changelog
changelog:
sort: asc
use: github
filters:
exclude:
- '^docs:'
- '^test:'
- '^ci:'
- '^chore:'
- '^style:'
- 'merge conflict'
- 'Merge pull request'
- 'Merge remote-tracking branch'
- 'Merge branch'
groups:
- title: 'New Features'
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 0
- title: 'Bug Fixes'
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 1
- title: 'Performance Improvements'
regexp: '^.*?perf(\([[:word:]]+\))??!?:.+$'
order: 2
- title: 'Refactors'
regexp: '^.*?refactor(\([[:word:]]+\))??!?:.+$'
order: 3
- title: 'Documentation'
regexp: '^.*?docs?(\([[:word:]]+\))??!?:.+$'
order: 4
- title: 'Other Changes'
order: 999
# This section defines for which artifact types to generate SBOMs.
sboms:
- artifacts: archive
# This section defines the release policy.
release:
github:
owner: modelcontextprotocol
name: registry
# This section defines how and which artifacts we want to sign for the release.
signs:
- id: archives
cmd: cosign
args:
- "sign-blob"
- "--output-signature=${signature}"
- "--output-certificate=${certificate}"
- "${artifact}"
- "--yes" # needed on cosign 2.0.0+
artifacts: archive
output: true
certificate: '{{ trimsuffix (trimsuffix .Env.artifact ".zip") ".tar.gz" }}.pem'
# Also sign checksums file for additional verification
- id: checksums
cmd: cosign
args:
- "sign-blob"
- "--output-signature=${signature}"
- "--output-certificate=${certificate}"
- "${artifact}"
- "--yes"
artifacts: checksum
output: true
certificate: '{{ trimsuffix .Env.artifact ".txt" }}.pem'
# This section defines the release format.
archives:
- name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

View file

@ -6,11 +6,13 @@ help: ## Show this help message
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}'
# Build targets
build: ## Build the registry application
go build -o bin/registry ./cmd/registry
build: ## Build the registry application with version info
@mkdir -p bin
go build -ldflags="-X main.Version=dev-$(shell git rev-parse --short HEAD) -X main.GitCommit=$(shell git rev-parse HEAD) -X main.BuildTime=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)" -o bin/registry ./cmd/registry
publisher: ## Build the publisher tool
cd cmd/publisher && ./build.sh
publisher: ## Build the publisher tool with version info
@mkdir -p bin
go build -ldflags="-X main.Version=dev-$(shell git rev-parse --short HEAD) -X main.GitCommit=$(shell git rev-parse HEAD) -X main.BuildTime=$(shell date -u +%Y-%m-%dT%H:%M:%SZ)" -o bin/mcp-publisher ./cmd/publisher
# Test targets
test-unit: ## Run unit tests with coverage
@ -64,7 +66,6 @@ dev-local: ## Run registry locally
clean: ## Clean build artifacts and coverage files
rm -rf bin
rm -f coverage.out coverage.html
cd cmd/publisher && rm -f publisher
.DEFAULT_GOAL := help

View file

@ -67,14 +67,23 @@ The service runs on [`localhost:8080`](http://localhost:8080) by default. This c
Pre-built Docker images are automatically published to GitHub Container Registry:
```bash
# Run latest from main branch
# Run latest stable release
docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:latest
# Run specific commit build
docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main-20250806-a1b2c3d
# Run latest from main branch (continuous deployment)
docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main
# Run specific release version
docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:v1.0.0
# Run development build from main branch
docker run -p 8080:8080 ghcr.io/modelcontextprotocol/registry:main-20250906-abc123d
```
**Available tags:** `latest`, `main-<date>-<sha>`
**Available tags:**
- **Releases**: `latest`, `v1.0.0`, `v1.1.0`, etc.
- **Continuous**: `main` (latest main branch build)
- **Development**: `main-<date>-<sha>` (specific commit builds)
</details>
@ -87,7 +96,7 @@ To publish a server, we've built a simple CLI. You can use it with:
make publisher
# Use it!
./cmd/publisher/bin/mcp-publisher --help
./bin/mcp-publisher --help
```
See [the publisher guide](./docs/guides/publishing/publish-server.md) for more details.

View file

@ -1,41 +0,0 @@
#!/bin/bash
# Build script for MCP Registry Publisher Tool
# Set variables
OUTPUT_DIR="./bin"
BINARY_NAME="mcp-publisher"
# Create output directory if it doesn't exist
mkdir -p $OUTPUT_DIR
# Print build information
echo "Building MCP Registry Publisher Tool..."
# Build for current platform
echo "Building for $(go env GOOS)/$(go env GOARCH)..."
go build -o "$OUTPUT_DIR/$BINARY_NAME" .
# Make the binary executable
chmod +x "$OUTPUT_DIR/$BINARY_NAME"
echo "Build complete: $(realpath $OUTPUT_DIR/$BINARY_NAME)"
# Optional: Build for multiple platforms
if [ "$1" == "--all" ]; then
echo "Building for all supported platforms..."
# Linux AMD64
GOOS=linux GOARCH=amd64 go build -o "$OUTPUT_DIR/${BINARY_NAME}-linux-amd64" .
# MacOS AMD64
GOOS=darwin GOARCH=amd64 go build -o "$OUTPUT_DIR/${BINARY_NAME}-darwin-amd64" .
# MacOS ARM64 (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o "$OUTPUT_DIR/${BINARY_NAME}-darwin-arm64" .
# Windows AMD64
GOOS=windows GOARCH=amd64 go build -o "$OUTPUT_DIR/${BINARY_NAME}-windows-amd64.exe" .
echo "Multi-platform build complete in $(realpath $OUTPUT_DIR)"
fi

View file

@ -2,11 +2,25 @@ package main
import (
"fmt"
"log"
"os"
"github.com/modelcontextprotocol/registry/cmd/publisher/commands"
)
// Version info for the MCP Publisher tool
// These variables are injected at build time via ldflags by goreleaser
var (
// Version is the current version of the MCP Publisher tool
Version = "dev"
// BuildTime is the time at which the binary was built
BuildTime = "unknown"
// GitCommit is the git commit that was compiled
GitCommit = "unknown"
)
func main() {
if len(os.Args) < 2 {
printUsage()
@ -23,6 +37,9 @@ func main() {
err = commands.LogoutCommand()
case "publish":
err = commands.PublishCommand(os.Args[2:])
case "--version", "-v", "version":
log.Printf("mcp-publisher %s (commit: %s, built: %s)", Version, GitCommit, BuildTime)
return
case "--help", "-h", "help":
printUsage()
default:

View file

@ -19,6 +19,19 @@ import (
"github.com/modelcontextprotocol/registry/internal/telemetry"
)
// Version info for the MCP Registry application
// These variables are injected at build time via ldflags
var (
// Version is the current version of the MCP Registry application
Version = "dev"
// BuildTime is the time at which the binary was built
BuildTime = "unknown"
// GitCommit is the git commit that was compiled
GitCommit = "unknown"
)
func main() {
// Parse command line flags
showVersion := flag.Bool("version", false, "Display version information")

View file

@ -1,13 +0,0 @@
package main
// Version info for the MCP Registry application
var (
// Version is the current version of the MCP Registry application
Version = "0.1.0"
// BuildTime is the time at which the binary was built
BuildTime = "undefined"
// GitCommit is the git commit that was compiled
GitCommit = "undefined"
)

27
docs/releasing.md Normal file
View file

@ -0,0 +1,27 @@
# Release Guide
## Creating a Release
1. **Go to GitHub**: Navigate to https://github.com/modelcontextprotocol/registry/releases
2. **Click "Draft a new release"**
3. **Choose a tag**: Click "Choose a tag" and type a new semantic version that follows the last one available (e.g., `v1.0.0`)
5. **Generate notes**: Click "Generate release notes" to auto-populate the name and description
6. **Publish**: Click "Publish release"
The release workflow will automatically:
- Build binaries for 6 platforms (Linux, macOS, Windows × amd64, arm64)
- Create and push Docker images with `:latest` and `:vX.Y.Z` tags
- Attach all artifacts to the GitHub release
- Generate checksums and signatures
## After Release
- Docker images will be available at `ghcr.io/modelcontextprotocol/registry:latest`
- Binaries can be downloaded from the GitHub release page
## Versioning
We use semantic versioning (SemVer):
- `v1.0.0` - Major release with breaking changes
- `v1.1.0` - Minor release with new features
- `v1.0.1` - Patch release with bug fixes