This is an automated email from the ASF dual-hosted git repository.

epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git


The following commit(s) were added to refs/heads/main by this push:
     new b862e15  fix(ci): repair release workflows that silently skipped or 
never ran (#174)
b862e15 is described below

commit b862e156db9b4d89d81261a108907694357ceab7
Author: Aditya Parikh <[email protected]>
AuthorDate: Fri Sep 11 10:06:34 2026 -0400

    fix(ci): repair release workflows that silently skipped or never ran (#174)
    
    Verified findings from a CodeRabbit review, rebased onto current main.
    
    Jobs that never run:
    - release-publish.yml: publish-mcp-registry gates on
      needs.validate-release.outputs.proceed but does not list validate-release
      in needs:. A needs.<job> reference to a job absent from needs: resolves to
      empty rather than erroring, so the condition is always falsy and MCP
      Registry publishing never happens.
    - atr-release.yml / atr-release-test.yml: finish-release has no needs: at
      all, only `if: always() && inputs.skip_vote`, so it announces the release
      in parallel with the jobs that produce it.
    
    Steps that fail outright:
    - atr-release.yml uses actions/upload-artifact@v3, retired by GitHub. Pinned
      to the SHA already used elsewhere in this repo.
    - The MCP Publisher download 404s today: the asset is
      mcp-publisher_linux_amd64.tar.gz, not mcp-publisher-linux-amd64.tar.gz.
      Because `curl -L` has no --fail and is piped straight into tar, the error
      page is streamed to tar instead of failing the step. Pinned to v1.8.0 and
      verified against the release's published checksum.
    
    Other correctness and hardening:
    - apache/tooling-actions upload-to-atr / release-on-atr were referenced at
      @main; pinned to a commit SHA. ASF's Actions allow-list matches by exact
      SHA, and a mutable ref is not reproducible.
    - Pass workflow_dispatch inputs through step env instead of interpolating
      them into shell text.
    - The Docker Hub publish step warned and exited 0 when credentials were
      absent, so a release could report success while publishing nothing; it now
      fails. Credentials move to JIB_TO_AUTH_* env so the token is not in the
      process argument list.
    - The build.gradle.kts version sed was unanchored; restrict it to the
      top-level `version = ` declaration.
    - Drop an unresolved ${VOTE_THREAD_NOTE} from the announcement heredoc; the
      vote thread URL is already appended by the block below it.
    - The release step rewrote only .packages[0].version in server.json, leaving
      the second package on -SNAPSHOT so the registry entry advertised an image
      tag that is never published. Rewrite every package and fail if a SNAPSHOT
      survives.
    
    Signed-off-by: Aditya Parikh <[email protected]>
    Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
 .github/workflows/atr-release-test.yml | 19 +++++--
 .github/workflows/atr-release.yml      | 26 +++++++---
 .github/workflows/release-publish.yml  | 92 ++++++++++++++++++++++------------
 server.json                            |  4 +-
 4 files changed, 93 insertions(+), 48 deletions(-)

diff --git a/.github/workflows/atr-release-test.yml 
b/.github/workflows/atr-release-test.yml
index cfe4882..a37d51c 100644
--- a/.github/workflows/atr-release-test.yml
+++ b/.github/workflows/atr-release-test.yml
@@ -115,8 +115,11 @@ jobs:
                     fetch-depth: 0  # Full history for proper tagging
 
             -   name: Create test tag if not exists
+                env:
+                    RELEASE_VERSION: ${{ inputs.release_version }}
+                    RELEASE_CANDIDATE: ${{ inputs.release_candidate }}
                 run: |
-                    TEST_TAG="v${{ inputs.release_version }}-${{ 
inputs.release_candidate }}"
+                    TEST_TAG="v${RELEASE_VERSION}-${RELEASE_CANDIDATE}"
                     if ! git rev-parse "${TEST_TAG}" >/dev/null 2>&1; then
                       echo "Creating test tag: ${TEST_TAG}"
                       git config user.email "[email protected]"
@@ -221,7 +224,7 @@ jobs:
 
             -   name: Upload artifacts to ATR (Real)
                 if: ${{ !inputs.dry_run }}
-                uses: apache/tooling-actions/upload-to-atr@main
+                uses: 
apache/tooling-actions/upload-to-atr@81f0a6cf2202b362ee6434b82c271b1849133581 # 
main @ 2026-07-27
                 with:
                     project: ${{ env.ATR_PROJECT_NAME }}
                     version: ${{ inputs.release_version }}-${{ 
inputs.release_candidate }}
@@ -261,7 +264,9 @@ jobs:
         name: Vote Instructions (TEST)
         runs-on: ubuntu-latest
         needs: [ compose-release ]
-        if: ${{ !inputs.skip_vote && (success() || inputs.skip_compose) }}
+        # A skipped `needs` job skips its dependents unless the if: uses 
always().
+        # Without it the skip_compose=true path could never reach this job.
+        if: ${{ always() && !inputs.skip_vote && (needs.compose-release.result 
== 'success' || inputs.skip_compose) }}
 
         steps:
             -   name: Generate test vote email
@@ -307,6 +312,10 @@ jobs:
     finish-release:
         name: Finalize Release (TEST)
         runs-on: ubuntu-latest
+        # Must wait for the compose and vote jobs even though both may be 
skipped
+        # (skip_compose / skip_vote). always() keeps this reachable in that 
case,
+        # but without needs: it would otherwise start in parallel with them.
+        needs: [ compose-release, vote-instructions ]
         if: ${{ always() && inputs.skip_vote }}
 
         steps:
@@ -343,7 +352,7 @@ jobs:
 
             -   name: Resolve vote and announce on ATR (Real)
                 if: ${{ !inputs.dry_run }}
-                uses: apache/tooling-actions/release-on-atr@main
+                uses: 
apache/tooling-actions/release-on-atr@81f0a6cf2202b362ee6434b82c271b1849133581 
# main @ 2026-07-27
                 with:
                     version: ${{ inputs.release_version }}
                     atr-host: release-test.apache.org
@@ -382,7 +391,7 @@ jobs:
                     
                     # Test main platform
                     echo "1. Testing main platform 
(https://release-test.apache.org):"
-                    if curl -s -o /dev/null -w "   HTTP Status: 
%{http_code}\n" https://release-test.apache.org; then
+                    if curl -fsS --max-time 15 -o /dev/null -w "   HTTP 
Status: %{http_code}\n" https://release-test.apache.org; then
                       echo "   ✅ Platform is reachable"
                     else
                       echo "   ❌ Platform unreachable"
diff --git a/.github/workflows/atr-release.yml 
b/.github/workflows/atr-release.yml
index 9053e1c..9f901f9 100644
--- a/.github/workflows/atr-release.yml
+++ b/.github/workflows/atr-release.yml
@@ -158,9 +158,13 @@ jobs:
                     fetch-depth: 0  # Full history for proper tagging
 
             -   name: Verify release tag
+                env:
+                    RELEASE_VERSION: ${{ inputs.release_version }}
+                    RELEASE_CANDIDATE: ${{ inputs.release_candidate }}
                 run: |
-                    if ! git rev-parse "v${{ inputs.release_version }}-${{ 
inputs.release_candidate }}" >/dev/null 2>&1; then
-                      echo "ERROR: Release tag not found: v${{ 
inputs.release_version }}-${{ inputs.release_candidate }}"
+                    RELEASE_TAG="v${RELEASE_VERSION}-${RELEASE_CANDIDATE}"
+                    if ! git rev-parse "${RELEASE_TAG}" >/dev/null 2>&1; then
+                      echo "ERROR: Release tag not found: ${RELEASE_TAG}"
                       exit 1
                     fi
                     echo "✓ Release tag verified"
@@ -236,7 +240,7 @@ jobs:
                     # gpg --armor --detach-sign build/distributions/*.jar
 
             -   name: Upload artifacts to ATR
-                uses: apache/tooling-actions/upload-to-atr@main
+                uses: 
apache/tooling-actions/upload-to-atr@81f0a6cf2202b362ee6434b82c271b1849133581 # 
main @ 2026-07-27
                 with:
                     asf-uid: ${{ secrets.ASF_USERNAME }}
                     project: ${{ env.ATR_PROJECT_NAME }}
@@ -246,7 +250,7 @@ jobs:
                     ssh-port: 2222
 
             -   name: Upload artifacts for review
-                uses: actions/upload-artifact@v3
+                uses: 
actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # 6.0.0
                 with:
                     name: release-artifacts-${{ inputs.release_version }}-${{ 
inputs.release_candidate }}
                     path: build/distributions/
@@ -274,7 +278,9 @@ jobs:
         name: Vote Instructions
         runs-on: ubuntu-latest
         needs: [ compose-release ]
-        if: ${{ !inputs.skip_vote && (success() || inputs.skip_compose) }}
+        # A skipped `needs` job skips its dependents unless the if: uses 
always().
+        # Without it the skip_compose=true path could never reach this job.
+        if: ${{ always() && !inputs.skip_vote && (needs.compose-release.result 
== 'success' || inputs.skip_compose) }}
 
         steps:
             -   name: Generate vote email template
@@ -329,7 +335,7 @@ jobs:
                     echo "**Note**: ATR voting automation is not yet 
available. Manual process required." >> $GITHUB_STEP_SUMMARY
 
             -   name: Upload vote email template
-                uses: actions/upload-artifact@v3
+                uses: 
actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # 6.0.0
                 with:
                     name: vote-email-template
                     path: vote-email.txt
@@ -338,6 +344,10 @@ jobs:
     finish-release:
         name: Finalize and Announce Release
         runs-on: ubuntu-latest
+        # Must wait for the compose and vote jobs even though both may be 
skipped
+        # (skip_compose / skip_vote). always() keeps this reachable in that 
case,
+        # but without needs: it would otherwise start in parallel with them.
+        needs: [ compose-release, vote-instructions ]
         if: ${{ always() && inputs.skip_vote }}  # Manual trigger after vote 
passes
 
         steps:
@@ -347,7 +357,7 @@ jobs:
                     ref: "v${{ inputs.release_version }}-${{ 
inputs.release_candidate }}"
 
             -   name: Resolve vote and announce on ATR
-                uses: apache/tooling-actions/release-on-atr@main
+                uses: 
apache/tooling-actions/release-on-atr@81f0a6cf2202b362ee6434b82c271b1849133581 
# main @ 2026-07-27
                 with:
                     version: ${{ inputs.release_version }}
                     atr-host: release-test.apache.org
@@ -428,7 +438,7 @@ jobs:
                     echo "" >> $GITHUB_STEP_SUMMARY
                     echo "#### Distribution:" >> $GITHUB_STEP_SUMMARY
                     echo "- Apache Mirrors: 
https://www.apache.org/dyn/closer.lua/solr/mcp/${{ inputs.release_version }}/" 
>> $GITHUB_STEP_SUMMARY
-                    echo "- Docker Hub: `apache/solr-mcp:${{ 
inputs.release_version }}`" >> $GITHUB_STEP_SUMMARY
+                    echo "- Docker Hub: \`apache/solr-mcp:${{ 
inputs.release_version }}\`" >> $GITHUB_STEP_SUMMARY
                     echo "" >> $GITHUB_STEP_SUMMARY
                     echo "#### Next Steps:" >> $GITHUB_STEP_SUMMARY
                     echo "1. Send announcement to [email protected]" >> 
$GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/release-publish.yml 
b/.github/workflows/release-publish.yml
index 4c6c6c7..f835274 100644
--- a/.github/workflows/release-publish.yml
+++ b/.github/workflows/release-publish.yml
@@ -191,7 +191,7 @@ jobs:
         run: |
           # Ensure the Gradle project version matches the GA version (removes 
any -SNAPSHOT)
           # This keeps image tags and any generated artifacts consistent with 
the voted release
-          sed -i "s/version = \".*\"/version = \"${RELEASE_VERSION}\"/" 
build.gradle.kts
+          sed -i "0,/^version = \".*\"/s//version = \"${RELEASE_VERSION}\"/" 
build.gradle.kts
 
       - name: Build project
         run: ./gradlew build
@@ -206,34 +206,34 @@ jobs:
           echo "Contact INFRA for setup requirements"
 
       - name: Build and publish to Docker Hub (apache/solr-mcp)
+        env:
+          # Jib reads these directly; passing them as -Djib.to.auth.* would put
+          # the token in the process argument list, visible to anything on the 
runner.
+          JIB_TO_AUTH_USERNAME: ${{ secrets.DOCKERHUB_APACHE_USERNAME }}
+          JIB_TO_AUTH_PASSWORD: ${{ secrets.DOCKERHUB_APACHE_TOKEN }}
+          RELEASE_VERSION: ${{ inputs.release_version }}
         run: |
-          # Publish official release to apache/solr-mcp
-          # This requires Apache PMC credentials
-          if [[ -n "${{ secrets.DOCKERHUB_APACHE_USERNAME }}" ]]; then
-            # Build and push with multiple tags
-            ./gradlew jib \
-              -Djib.to.image=apache/solr-mcp:${{ inputs.release_version }} \
-              -Djib.to.auth.username=${{ secrets.DOCKERHUB_APACHE_USERNAME }} \
-              -Djib.to.auth.password=${{ secrets.DOCKERHUB_APACHE_TOKEN }} \
-              -Djib.to.tags=${{ inputs.release_version }},latest
-
-            # Also tag with major and minor versions
-            MAJOR_VERSION=$(echo "${{ inputs.release_version }}" | cut -d. -f1)
-            MINOR_VERSION=$(echo "${{ inputs.release_version }}" | cut -d. 
-f1-2)
-
-            ./gradlew jib \
-              -Djib.to.image=apache/solr-mcp:${MAJOR_VERSION} \
-              -Djib.to.auth.username=${{ secrets.DOCKERHUB_APACHE_USERNAME }} \
-              -Djib.to.auth.password=${{ secrets.DOCKERHUB_APACHE_TOKEN }}
-
-            ./gradlew jib \
-              -Djib.to.image=apache/solr-mcp:${MINOR_VERSION} \
-              -Djib.to.auth.username=${{ secrets.DOCKERHUB_APACHE_USERNAME }} \
-              -Djib.to.auth.password=${{ secrets.DOCKERHUB_APACHE_TOKEN }}
-          else
-            echo "WARNING: Apache Docker Hub credentials not configured"
+          set -euo pipefail
+          # Publish official release to apache/solr-mcp. This requires Apache 
PMC
+          # credentials; a release that cannot publish must fail loudly rather 
than
+          # reporting success while shipping nothing.
+          if [[ -z "${JIB_TO_AUTH_USERNAME}" || -z "${JIB_TO_AUTH_PASSWORD}" 
]]; then
+            echo "ERROR: Apache Docker Hub credentials 
(DOCKERHUB_APACHE_USERNAME / DOCKERHUB_APACHE_TOKEN) are not configured" >&2
+            exit 1
           fi
 
+          # Build and push with multiple tags
+          ./gradlew jib \
+            -Djib.to.image="apache/solr-mcp:${RELEASE_VERSION}" \
+            -Djib.to.tags="${RELEASE_VERSION},latest"
+
+          # Also tag with major and minor versions
+          MAJOR_VERSION="${RELEASE_VERSION%%.*}"
+          MINOR_VERSION="$(echo "${RELEASE_VERSION}" | cut -d. -f1-2)"
+
+          ./gradlew jib -Djib.to.image="apache/solr-mcp:${MAJOR_VERSION}"
+          ./gradlew jib -Djib.to.image="apache/solr-mcp:${MINOR_VERSION}"
+
       - name: Build and publish to GitHub Container Registry
         run: |
           # Also publish to GitHub Container Registry
@@ -332,7 +332,6 @@ jobs:
           ## Vote Thread
 
           This release was approved by the Apache Solr PMC.
-          ${VOTE_THREAD_NOTE}
           EOF
 
           if [[ -n "${{ inputs.vote_thread_url }}" ]]; then
@@ -400,7 +399,7 @@ jobs:
         env:
           RELEASE_VERSION: ${{ inputs.release_version }}
         run: |
-          sed -i "s/version = \".*\"/version = \"${RELEASE_VERSION}\"/" 
build.gradle.kts
+          sed -i "0,/^version = \".*\"/s//version = \"${RELEASE_VERSION}\"/" 
build.gradle.kts
 
       # Inline `docker login` rather than docker/login-action: that third-party
       # action is not on the ASF GitHub Actions allow-list, which fails the 
whole
@@ -515,7 +514,10 @@ jobs:
     runs-on: ubuntu-latest
     # Wait for both JVM (publish-docker) and native (publish-native-manifests)
     # images so the MCP Registry entry references a fully-published release.
-    needs: [publish-docker, publish-native-manifests]
+    # validate-release must be listed here too: the if: below reads its output,
+    # and a needs.<job> reference to a job not in needs: resolves to empty,
+    # which would silently make this job never run.
+    needs: [validate-release, publish-docker, publish-native-manifests]
     if: ${{ needs.validate-release.outputs.proceed == 'true' }}
 
       # Permissions required for OIDC-based auth to the MCP Registry and read 
access
@@ -543,16 +545,40 @@ jobs:
                 VERSION="${{ steps.mcp_version.outputs.version }}"
                 # Update the top-level server version (e.g., 1.2.3)
                 jq --arg v "$VERSION" '.version = $v' server.json > 
server.json.tmp
-                # Update package version to match GA release (no -SNAPSHOT 
suffix)
-                jq --arg v "$VERSION" '.packages[0].version = $v' 
server.json.tmp > server.json
+                # Update EVERY package version to match the GA release. 
Rewriting
+                # only packages[0] left the other package on -SNAPSHOT, so the
+                # registry entry advertised an image tag that is never 
published.
+                jq --arg v "$VERSION" '.packages[].version = $v' 
server.json.tmp > server.json
                 rm server.json.tmp
+                # Fail if any -SNAPSHOT version survived the rewrite
+                if grep -q 'SNAPSHOT' server.json; then
+                  echo "ERROR: server.json still contains a SNAPSHOT version 
after rewrite" >&2
+                  cat server.json >&2
+                  exit 1
+                fi
                 # Show the final server.json for auditing
                 cat server.json
 
-        # Download the MCP Publisher CLI from its latest GitHub release
+        # Download the MCP Publisher CLI, pinned to an explicit release and
+        # verified against that release's published checksum file. Piping an
+        # unverified `curl -L` straight into tar would silently extract a
+        # GitHub error page on a 404, so download, verify, then extract.
         -   name: Download MCP Publisher
+            env:
+                MCP_PUBLISHER_VERSION: v1.8.0
             run: |
-                curl -L 
https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher-linux-amd64.tar.gz
 | tar xz
+                set -euo pipefail
+                
base="https://github.com/modelcontextprotocol/registry/releases/download/${MCP_PUBLISHER_VERSION}";
+                asset="mcp-publisher_linux_amd64.tar.gz"
+                checksums="registry_${MCP_PUBLISHER_VERSION#v}_checksums.txt"
+
+                curl -fsSL -o "${asset}" "${base}/${asset}"
+                curl -fsSL -o "${checksums}" "${base}/${checksums}"
+
+                # Abort if the archive does not match the release's published 
digest
+                grep " ${asset}\$" "${checksums}" | sha256sum -c -
+
+                tar xzf "${asset}"
                 chmod +x mcp-publisher
                 ./mcp-publisher --version
 
diff --git a/server.json b/server.json
index 5c3f5b4..b3b9e70 100644
--- a/server.json
+++ b/server.json
@@ -6,7 +6,7 @@
     "url": "https://github.com/apache/solr-mcp";,
     "source": "github"
   },
-  "version": "1.0.0",
+  "version": "1.0.0-SNAPSHOT",
   "packages": [
     {
       "registryType": "docker",
@@ -51,4 +51,4 @@
       ]
     }
   ]
-}
\ No newline at end of file
+}

Reply via email to