From dfe0eaa13e347d9cfba8a96c7fc0a25dcd8207b4 Mon Sep 17 00:00:00 2001 From: Algis Dumbris Date: Wed, 3 Jun 2026 22:12:04 +0300 Subject: [PATCH] ci: de-conflict release.yml from RC tags; document RC pre-release channel An RC tag like `v0.37.0-rc.1` matches release.yml's `tags: ["v*"]` trigger, so it currently fires BOTH release.yml and prerelease.yml. Gate every release.yml job that builds or publishes a stable artifact on `!contains(github.ref_name, '-')` so RC/prerelease tags run prerelease.yml exclusively: - generate-notes, build, sign-windows: add the guard - release: add an explicit `if:` (previously relied only on `needs:`) - deploy-docs, trigger-marketing-update: add the guard - mcp-registry: add the guard (never publish an RC to the official registry) update-homebrew and publish-linux-repos already carry the guard. build-docker is disabled (`false &&`); provenance cascade-skips via `needs: [release]`. Docs: add a Release Candidate (RC) section to docs/prerelease-builds.md (semver `-rc.N` scheme, GH pre-release channel, full build matrix with the Windows-SignPath gap called out, stable-channel opt-in via MCPPROXY_ALLOW_PRERELEASE_UPDATES), correct the GitHub Workflows section, and refresh the CLAUDE.md prerelease note (under the 40k gate). Related MCP-1012 --- .github/workflows/release.yml | 22 ++++++++++------ CLAUDE.md | 3 +-- docs/prerelease-builds.md | 47 +++++++++++++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dcbe91041..183ab379f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,8 @@ jobs: # Generate AI-powered release notes using Claude API generate-notes: runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') + # Stable releases only — RC/prerelease tags (v*-rc.*) are handled by prerelease.yml. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') outputs: notes: ${{ steps.generate.outputs.notes }} notes_file: ${{ steps.generate.outputs.notes_file }} @@ -187,8 +188,8 @@ jobs: build: environment: production - # Only run on version tags - if: startsWith(github.ref, 'refs/tags/v') + # Only run on stable version tags — RC/prerelease tags (v*-rc.*) go through prerelease.yml. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') strategy: fail-fast: false matrix: @@ -961,7 +962,8 @@ jobs: needs: build runs-on: ubuntu-latest environment: production - if: startsWith(github.ref, 'refs/tags/v') + # Stable releases only — RC tags are signed via prerelease.yml. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') strategy: matrix: arch: [amd64, arm64] @@ -1057,6 +1059,9 @@ jobs: needs: [build, sign-windows, generate-notes] # add build-docker when server MVP is ready runs-on: ubuntu-latest environment: production + # Explicit guard (belt-and-suspenders on top of the cascade from needs:) — RC tags + # publish via prerelease.yml's own release step, never through this stable channel. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') outputs: # base64-encoded checksums.txt — consumed by the SLSA provenance job (WP-C3) hashes: ${{ steps.checksums.outputs.hashes }} @@ -1598,7 +1603,8 @@ jobs: deploy-docs: needs: release runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'smart-mcp-proxy/mcpproxy-go' + # Stable releases only — don't publish docs for RC/prerelease tags. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') && github.repository == 'smart-mcp-proxy/mcpproxy-go' # Non-blocking: docs failure doesn't block release continue-on-error: true @@ -1643,7 +1649,8 @@ jobs: trigger-marketing-update: needs: release runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') && github.repository == 'smart-mcp-proxy/mcpproxy-go' + # Stable releases only — don't fire marketing automation for RC/prerelease tags. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') && github.repository == 'smart-mcp-proxy/mcpproxy-go' # Non-blocking: marketing update failure doesn't block release continue-on-error: true @@ -1660,7 +1667,8 @@ jobs: name: Publish to MCP Registry needs: release runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') + # Stable releases only — never publish an RC to the official MCP registry. + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-') continue-on-error: true steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 diff --git a/CLAUDE.md b/CLAUDE.md index ff9a35747..02d62d7cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -723,8 +723,7 @@ See `docs/github-actions-windows-wix-research.md` for CI setup. ## Prerelease Builds - **`main` branch**: Stable releases -- **`next` branch**: Prerelease builds with latest features -- macOS DMG installers are signed and notarized +- **`next` branch** + `v*-rc.*` tags: GitHub pre-releases, opt-in, NOT on stable channels; RC tags skip `release.yml` (brew/linux/registry) See `docs/prerelease-builds.md` for download instructions. diff --git a/docs/prerelease-builds.md b/docs/prerelease-builds.md index 23880d0cc..99b3ac6ee 100644 --- a/docs/prerelease-builds.md +++ b/docs/prerelease-builds.md @@ -37,6 +37,49 @@ gh run download --name versioned-linux-amd64 # Linux - Example: `v0.8.4-next.5b63e2d` - Version embedded in both `mcpproxy` and `mcpproxy-tray` binaries +## Release Candidate (RC) Builds + +Release candidates are opt-in, fully-built prereleases published to the GitHub **pre-release** channel for testers who want to validate an upcoming version before it ships to stable. + +### Version scheme + +- Format: **semver** `vMAJOR.MINOR.PATCH-rc.N` — e.g. `v0.37.0-rc.1`, `v0.37.0-rc.2`. +- The `-rc.N` suffix (hyphen + dot before the number) is required. It is what keeps RC tags off the stable channels: the Homebrew, Linux-repo, docs, marketing, MCP-registry, and core build/release jobs in `release.yml` are gated on `!contains(github.ref_name, '-')`. +- Do **not** use forms like `v0.37.0.RC1` or `v0.37.0RC1` — without the hyphen they read as stable tags and would bypass those guards. + +### What an RC publishes + +A `v*-rc.*` tag runs **only** `prerelease.yml`, which mirrors the full stable build matrix: + +| Platform | Artifacts | Signing | +|----------|-----------|---------| +| macOS (arm64, amd64) | DMG + PKG installers, tar.gz | Apple Developer ID signed **and notarized** | +| Linux (arm64, amd64) | tar.gz, `.deb`, `.rpm` | — | +| Windows (arm64, amd64) | `.zip`, installer (`.exe`) | **Not** SignPath-signed (see note below) | + +The GitHub release is created with `prerelease: true`, so it does **not** become `releases/latest`. + +> **Windows signing:** stable releases are Authenticode-signed via a dedicated SignPath job (`sign-windows`) in `release.yml`. `prerelease.yml` intentionally omits that step (SignPath signing adds ~1h per arch), so **RC Windows installers are unsigned** and will trigger a SmartScreen prompt. If signed Windows RCs become a requirement, port the `sign-windows` job into `prerelease.yml`. + +### What an RC does NOT do + +- Not published to **Homebrew** (`update-homebrew` guarded). +- Not published to the **Linux apt/rpm repos** (`publish-linux-repos` guarded). +- Not published to the **official MCP registry** (`mcp-registry` guarded). +- Does not deploy docs or trigger marketing automation (`deploy-docs`, `trigger-marketing-update` guarded). +- Not offered as an update on **stable channels**: + - The macOS tray uses GitHub `releases/latest`, which excludes prereleases (`native/macos/MCPProxy/MCPProxy/Services/UpdateService.swift`), plus a semver downgrade guard so an `-rc` is never treated as "newer" than the matching stable. + - The backend/tray update check is stable-only by default (`internal/tray/tray.go` → `releases/latest`). Set `MCPPROXY_ALLOW_PRERELEASE_UPDATES=true` to opt in to RC update offers. + +### Installing an RC + +Download the assets directly from the pre-release on the [Releases page](https://github.com/smart-mcp-proxy/mcpproxy-go/releases), or with the CLI: + +```bash +gh release list --repo smart-mcp-proxy/mcpproxy-go # pre-releases are tagged "Pre-release" +gh release download v0.37.0-rc.1 --repo smart-mcp-proxy/mcpproxy-go +``` + ## Security Features - **macOS DMG installers**: Signed with Apple Developer ID and notarized @@ -45,7 +88,7 @@ gh run download --name versioned-linux-amd64 # Linux ## GitHub Workflows -- **Prerelease workflow**: Triggered on `next` branch pushes -- **Release workflow**: Triggered on `main` branch tags +- **Prerelease workflow** (`prerelease.yml`): Triggered on `next` branch pushes and on `v*-rc.*` / `v*-next.*` tags. Publishes a GitHub pre-release. +- **Release workflow** (`release.yml`): Triggered on `v*` tags, but every job is gated on `!contains(github.ref_name, '-')` so RC/prerelease tags are skipped — a `v*-rc.*` tag therefore fires **only** `prerelease.yml`, never the stable release pipeline. - **Unit Tests**: Run on all branches with comprehensive test coverage - **Frontend CI**: Validates web UI components and build process