From 9e505b26dd70a90717f50470c6fde70fdff76596 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 8 Aug 2026 04:13:15 +0000 Subject: [PATCH] fix(ci): authenticate PlantUML release lookup and fail on unresolved versions Build #723 failed the java image with a 404 fetching .../releases/download//plantuml-.jar. The empty path segments came from prepare-versions: the anonymous api.github.com call for the PlantUML latest release was rate-limited (403, 60/hour shared per runner IP), so PLANTUML_TAG resolved to an empty string and the next echo built a malformed URL anyway. The failure was silent because `curl -fsSL ... | jq` takes its exit status from jq, so the step's `bash -e` never aborted. Every version lookup in the step shares that shape. - Send the repo GITHUB_TOKEN on the api.github.com call, raising the limit from 60/hour per IP to 1000/hour per repo. - Add `set -euo pipefail` plus fetch/require helpers so a failed download or an empty/null value fails the step at the lookup instead of building a broken URL for the image build to trip over. - Replace `curl | head -1` for the Go version with a parameter expansion; under pipefail the early pipe close could fail the command. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016Dsw1e9DPk8V5WoPf8MmPo --- .github/workflows/publish.yml | 60 +++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5571320..0d16f26 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -410,23 +410,53 @@ jobs: - name: Get latest dependency versions id: versions + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + set -euo pipefail + + # Every value resolved here feeds a download URL in a Dockerfile. A + # failed lookup used to yield an empty string that silently built a + # broken URL — a rate-limited GitHub API call left PLANTUML_TAG empty + # and the java image tried to fetch .../download//plantuml-.jar, which + # 404s. Fail the step at the lookup instead of in the image build. + fetch() { + local url="$1"; shift + if ! curl -fsSL "$url" "$@"; then + echo "::error::failed to fetch $url" >&2 + exit 1 + fi + } + + require() { + if [ -z "$2" ] || [ "$2" = "null" ]; then + echo "::error::could not resolve $1" >&2 + exit 1 + fi + } + COPILOT_VERSION=$(npm view @github/copilot version) + require copilot_version "$COPILOT_VERSION" echo "copilot_version=$COPILOT_VERSION" >> $GITHUB_OUTPUT PLAYWRIGHT_VERSION=$(npm view playwright version) + require playwright_version "$PLAYWRIGHT_VERSION" echo "playwright_version=$PLAYWRIGHT_VERSION" >> $GITHUB_OUTPUT - DOTNET_8_VERSION=$(curl -s https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/8.0/releases.json | jq -r '."latest-sdk"') + DOTNET_8_VERSION=$(fetch https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/8.0/releases.json | jq -r '."latest-sdk"') + require dotnet_8_version "$DOTNET_8_VERSION" echo "dotnet_8_version=$DOTNET_8_VERSION" >> $GITHUB_OUTPUT - DOTNET_9_VERSION=$(curl -s https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/9.0/releases.json | jq -r '."latest-sdk"') + DOTNET_9_VERSION=$(fetch https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/9.0/releases.json | jq -r '."latest-sdk"') + require dotnet_9_version "$DOTNET_9_VERSION" echo "dotnet_9_version=$DOTNET_9_VERSION" >> $GITHUB_OUTPUT - DOTNET_10_VERSION=$(curl -s https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/10.0/releases.json | jq -r '."latest-sdk"') + DOTNET_10_VERSION=$(fetch https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/10.0/releases.json | jq -r '."latest-sdk"') + require dotnet_10_version "$DOTNET_10_VERSION" echo "dotnet_10_version=$DOTNET_10_VERSION" >> $GITHUB_OUTPUT - CSHARP_LS_VERSION=$(curl -s https://api.nuget.org/v3-flatcontainer/csharp-ls/index.json | jq -r '.versions[-1]') + CSHARP_LS_VERSION=$(fetch https://api.nuget.org/v3-flatcontainer/csharp-ls/index.json | jq -r '.versions[-1]') + require csharp_ls_version "$CSHARP_LS_VERSION" echo "csharp_ls_version=$CSHARP_LS_VERSION" >> $GITHUB_OUTPUT # Toolchains pinned per-build so the resolved version is the layer cache @@ -434,19 +464,31 @@ jobs: # The snippets resolve these themselves when the arg is empty (local dev # builds), but CI must resolve here or the registry layer cache freezes # the version. Go strips the "go" prefix; the snippet re-adds it. - GOLANG_VERSION=$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -1) + GOLANG_VERSION=$(fetch 'https://go.dev/VERSION?m=text') + GOLANG_VERSION=${GOLANG_VERSION%%$'\n'*} + require golang_version "$GOLANG_VERSION" echo "golang_version=${GOLANG_VERSION#go}" >> $GITHUB_OUTPUT - MAVEN_VERSION=$(curl -fsSL https://dlcdn.apache.org/maven/maven-3/ | grep -oE '3\.[0-9]+\.[0-9]+/' | tr -d / | sort -uV | tail -1) + MAVEN_INDEX=$(fetch https://dlcdn.apache.org/maven/maven-3/) + MAVEN_VERSION=$(printf '%s\n' "$MAVEN_INDEX" | grep -oE '3\.[0-9]+\.[0-9]+/' | tr -d / | sort -uV | tail -1 || true) + require maven_version "$MAVEN_VERSION" echo "maven_version=$MAVEN_VERSION" >> $GITHUB_OUTPUT - GRADLE_VERSION=$(curl -fsSL https://services.gradle.org/versions/current | jq -r '.version') + GRADLE_VERSION=$(fetch https://services.gradle.org/versions/current | jq -r '.version') + require gradle_version "$GRADLE_VERSION" echo "gradle_version=$GRADLE_VERSION" >> $GITHUB_OUTPUT - JDTLS_FILE=$(curl -fsSL https://download.eclipse.org/jdtls/snapshots/latest.txt) + JDTLS_FILE=$(fetch https://download.eclipse.org/jdtls/snapshots/latest.txt) + require jdtls_file "$JDTLS_FILE" echo "jdtls_url=https://download.eclipse.org/jdtls/snapshots/${JDTLS_FILE}" >> $GITHUB_OUTPUT - PLANTUML_TAG=$(curl -fsSL https://api.github.com/repos/plantuml/plantuml/releases/latest | jq -r '.tag_name') + # Authenticated: the anonymous api.github.com limit is 60/hour shared + # across everything on the runner's IP, and a 403 here is what broke + # the java image build. + PLANTUML_TAG=$(fetch https://api.github.com/repos/plantuml/plantuml/releases/latest \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H 'X-GitHub-Api-Version: 2022-11-28' | jq -r '.tag_name') + require plantuml_tag "$PLANTUML_TAG" echo "plantuml_url=https://github.com/plantuml/plantuml/releases/download/${PLANTUML_TAG}/plantuml-${PLANTUML_TAG#v}.jar" >> $GITHUB_OUTPUT # Build all container images in parallel (each builds independently from node:20-slim)