From 94c803d452ea18a40f524c4b8905b913c1d1d573 Mon Sep 17 00:00:00 2001 From: Aleksandr Platonenkov Date: Mon, 3 Aug 2026 11:51:13 -0300 Subject: [PATCH 1/3] ci: fail when the plugin's version manifests disagree The plugin's version is written in three places a client reads independently, and nothing made them agree. They drifted silently once: marketplace.json stayed at 0.2.1 while plugin.json moved to 0.2.2 and then 0.2.3, so two releases existed that no installed client was ever offered -- the marketplace entry is what a client compares against to decide an update exists, and it never moved. Checks marketplace metadata.version and the code-index entry's version against plugin.json's version, and the version named in bin/server.sha256 against plugin.json's serverVersion. The latter has the same silent-failure shape: bump serverVersion without renaming the checksum line and every install refuses to run, which today is only caught by the release workflow after a tag is already pushed. Its own job rather than a step in build-and-test -- no SDK, no restore, no matrix, and a mismatch reads better on its own line in the checks list than buried in a build log. --- .github/workflows/ci.yml | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 378e161..3ee7e47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,72 @@ permissions: contents: read jobs: + # The plugin's version is written down in three places that a client reads + # independently, and nothing but this job makes them agree. They have already + # drifted silently once: marketplace.json sat at 0.2.1 while plugin.json went + # to 0.2.2 and then 0.2.3, so two releases existed that no installed client + # was ever offered — the marketplace entry is what a client compares against + # to decide whether an update exists, and it never moved. + # + # Runs as its own job rather than a step inside build-and-test: it needs no + # SDK, no restore and no matrix, and a manifest mismatch should be legible on + # its own line in the checks list instead of buried in a build log. + manifests: + name: Manifest versions agree + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + + - name: Check plugin version and serverVersion consistency + run: | + set -euo pipefail + + marketplace='.claude-plugin/marketplace.json' + manifest='plugins/code-index/.claude-plugin/plugin.json' + checksum='plugins/code-index/bin/server.sha256' + + plugin_version="$(jq -r '.version' "${manifest}")" + server_version="$(jq -r '.serverVersion' "${manifest}")" + marketplace_version="$(jq -r '.metadata.version' "${marketplace}")" + # Selected by name rather than by index so adding a second plugin to + # this marketplace later does not silently start checking the wrong + # entry. + entry_version="$(jq -r '.plugins[] | select(.name == "code-index") | .version' "${marketplace}")" + # The committed checksum line is " code-index-server-.tar.gz". + # The launcher fetches the asset named after serverVersion and verifies + # it against this line, so a serverVersion bump that leaves the file + # naming the previous version makes every install refuse to run — + # today that is only caught at release time, after the tag is pushed. + # tr -d '\r' so a stray CRLF checkout cannot make the version parse + # as "0.2.1\r" and fail a comparison that is actually fine. + checksum_version="$(tr -d '\r' < "${checksum}" | sed -n 's/.*code-index-server-\(.*\)\.tar\.gz$/\1/p')" + + status=0 + + if [ "${marketplace_version}" != "${plugin_version}" ]; then + echo "::error file=${marketplace}::marketplace metadata.version (${marketplace_version}) does not match plugin.json version (${plugin_version})" + status=1 + fi + + if [ "${entry_version}" != "${plugin_version}" ]; then + echo "::error file=${marketplace}::marketplace code-index entry version (${entry_version}) does not match plugin.json version (${plugin_version})" + status=1 + fi + + if [ "${checksum_version}" != "${server_version}" ]; then + echo "::error file=${checksum}::server.sha256 names version ${checksum_version:-}, but plugin.json serverVersion is ${server_version}" + status=1 + fi + + if [ "${status}" -eq 0 ]; then + echo "plugin ${plugin_version}, server ${server_version} — all manifests agree" + fi + + exit "${status}" + build-and-test: name: Build & Test (${{ matrix.os }}) runs-on: ${{ matrix.os }} From 18ac55cf044a796c21317f78e8dd78c035d9566a Mon Sep 17 00:00:00 2001 From: Aleksandr Platonenkov Date: Mon, 3 Aug 2026 11:53:08 -0300 Subject: [PATCH 2/3] TEMP: deliberately desync manifests to prove the check fails --- .claude-plugin/marketplace.json | 2 +- plugins/code-index/bin/server.sha256 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c6c5629..6a23536 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Marketplace for the code-index-mcp plugin — local semantic code search over C# codebases, backed by Ollama embeddings.", - "version": "0.2.3" + "version": "0.2.1" }, "plugins": [ { diff --git a/plugins/code-index/bin/server.sha256 b/plugins/code-index/bin/server.sha256 index 92d6fbc..894d26b 100644 --- a/plugins/code-index/bin/server.sha256 +++ b/plugins/code-index/bin/server.sha256 @@ -1 +1 @@ -480eddc8b0ad89163ee6403b2b408d8821edf3a586f1e34805bdd2f57b87b065 code-index-server-0.2.1.tar.gz +480eddc8b0ad89163ee6403b2b408d8821edf3a586f1e34805bdd2f57b87b065 code-index-server-0.2.0.tar.gz From 5e55f661cd938f5444f860911f61068399939c0a Mon Sep 17 00:00:00 2001 From: Aleksandr Platonenkov Date: Mon, 3 Aug 2026 11:56:30 -0300 Subject: [PATCH 3/3] Revert "TEMP: deliberately desync manifests to prove the check fails" This reverts commit 18ac55cf044a796c21317f78e8dd78c035d9566a. --- .claude-plugin/marketplace.json | 2 +- plugins/code-index/bin/server.sha256 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6a23536..c6c5629 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,7 +6,7 @@ }, "metadata": { "description": "Marketplace for the code-index-mcp plugin — local semantic code search over C# codebases, backed by Ollama embeddings.", - "version": "0.2.1" + "version": "0.2.3" }, "plugins": [ { diff --git a/plugins/code-index/bin/server.sha256 b/plugins/code-index/bin/server.sha256 index 894d26b..92d6fbc 100644 --- a/plugins/code-index/bin/server.sha256 +++ b/plugins/code-index/bin/server.sha256 @@ -1 +1 @@ -480eddc8b0ad89163ee6403b2b408d8821edf3a586f1e34805bdd2f57b87b065 code-index-server-0.2.0.tar.gz +480eddc8b0ad89163ee6403b2b408d8821edf3a586f1e34805bdd2f57b87b065 code-index-server-0.2.1.tar.gz