diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 811a30b..7dbfcd9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b2c25f9 --- /dev/null +++ b/.github/workflows/release.yml @@ -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 \ No newline at end of file diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..1a9ea0f --- /dev/null +++ b/.goreleaser.yaml @@ -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 }}" \ No newline at end of file diff --git a/Makefile b/Makefile index efdbad7..b8711cc 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 0bbcdad..566796b 100644 --- a/README.md +++ b/README.md @@ -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--` +**Available tags:** +- **Releases**: `latest`, `v1.0.0`, `v1.1.0`, etc. +- **Continuous**: `main` (latest main branch build) +- **Development**: `main--` (specific commit builds) @@ -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. diff --git a/cmd/publisher/build.sh b/cmd/publisher/build.sh deleted file mode 100755 index 7661afd..0000000 --- a/cmd/publisher/build.sh +++ /dev/null @@ -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 diff --git a/cmd/publisher/main.go b/cmd/publisher/main.go index e84aa78..f622749 100644 --- a/cmd/publisher/main.go +++ b/cmd/publisher/main.go @@ -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: diff --git a/cmd/registry/main.go b/cmd/registry/main.go index 56c5c28..749c21b 100644 --- a/cmd/registry/main.go +++ b/cmd/registry/main.go @@ -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") diff --git a/cmd/registry/version.go b/cmd/registry/version.go deleted file mode 100644 index a49e0bc..0000000 --- a/cmd/registry/version.go +++ /dev/null @@ -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" -) diff --git a/docs/releasing.md b/docs/releasing.md new file mode 100644 index 0000000..d718f4c --- /dev/null +++ b/docs/releasing.md @@ -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 \ No newline at end of file