ci(release): integrate Kokoro code signing into release workflow#3528
ci(release): integrate Kokoro code signing into release workflow#3528duwenxin99 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request integrates automated binary signing into the release pipeline by uploading unsigned binaries to an unsigned bucket, triggering Kokoro signing jobs for Linux, macOS, and Windows, and waiting for the signed assets before publishing. It also updates the quickstart documentation with instructions on how to verify these signatures. The reviewer feedback suggests adding timeout limits to the polling loops in the CI scripts to prevent indefinite hangs, fixing a potential 404 error for Windows downloads in the documentation, wrapping the GPG signature URL in release-please markers for automated versioning, and standardizing markdown indentation to avoid Hugo rendering issues.
| ```bash | ||
| export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, windows/amd64, or windows/arm64 | ||
| curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox | ||
| ``` |
There was a problem hiding this comment.
If the user selects windows/amd64 or windows/arm64 as their OS, the binary on GCS is named toolbox.exe rather than toolbox. Running curl -O .../toolbox will result in a 404 error. Dynamically append the .exe extension when the OS is Windows:
export OS="linux/amd64" # one of linux/amd64, darwin/arm64, darwin/amd64, windows/amd64, or windows/arm64
export EXE="" && [ "${OS%%/*}" = "windows" ] && EXE=".exe"
curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox$EXE| # We wait for each of the main binaries | ||
| for file_key in "${FILES[@]}"; do | ||
| OS=$(echo "$file_key" | cut -d '.' -f 1) | ||
| ARCH=$(echo "$file_key" | cut -d '.' -f 2) | ||
|
|
||
| if [ "$OS" = 'windows' ]; then | ||
| SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox.exe" | ||
| else | ||
| SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox" | ||
| fi | ||
|
|
||
| until gsutil stat "${SIGNED_URL}" > /dev/null 2>&1; do | ||
| echo "Waiting for signed binary: ${SIGNED_URL}..." | ||
| sleep 30 | ||
| done | ||
| echo "Found signed binary: ${SIGNED_URL}!" | ||
| done | ||
|
|
||
| # Wait for the Linux GPG signature | ||
| LINUX_SIG_URL="gs://mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.sig" | ||
| until gsutil stat "${LINUX_SIG_URL}" > /dev/null 2>&1; do | ||
| echo "Waiting for Linux GPG signature: ${LINUX_SIG_URL}..." | ||
| sleep 30 | ||
| done | ||
| echo "Found Linux GPG signature: ${LINUX_SIG_URL}!" |
There was a problem hiding this comment.
To prevent the CI build from hanging indefinitely if Kokoro fails or takes too long to sign the binaries, add a maximum attempt limit (timeout) to the polling loops.
# We wait for each of the main binaries
for file_key in "${FILES[@]}"; do
OS=$(echo "$file_key" | cut -d '.' -f 1)
ARCH=$(echo "$file_key" | cut -d '.' -f 2)
if [ "$OS" = 'windows' ]; then
SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox.exe"
else
SIGNED_URL="gs://mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox"
fi
attempts=0
max_attempts=40 # 20 minutes
until gsutil stat "${SIGNED_URL}" > /dev/null 2>&1; do
if [ $attempts -ge $max_attempts ]; then
echo "Error: Timeout waiting for signed binary: ${SIGNED_URL}" >&2
exit 1
fi
echo "Waiting for signed binary: ${SIGNED_URL}..."
sleep 30
attempts=$((attempts + 1))
done
echo "Found signed binary: ${SIGNED_URL}!"
done
# Wait for the Linux GPG signature
LINUX_SIG_URL="gs://mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.sig"
attempts=0
until gsutil stat "${LINUX_SIG_URL}" > /dev/null 2>&1; do
if [ $attempts -ge $max_attempts ]; then
echo "Error: Timeout waiting for Linux GPG signature: ${LINUX_SIG_URL}" >&2
exit 1
fi
echo "Waiting for Linux GPG signature: ${LINUX_SIG_URL}..."
sleep 30
attempts=$((attempts + 1))
done
echo "Found Linux GPG signature: ${LINUX_SIG_URL}!"| echo "Waiting for Kokoro to sign and upload all binaries to gs://$_BUCKET_NAME..." | ||
| for f in "${EXPECTED_FILES[@]}"; do | ||
| until gcloud storage stat "gs://$_BUCKET_NAME/$f" >/dev/null 2>&1; do | ||
| echo "Waiting for gs://$_BUCKET_NAME/$f..." | ||
| sleep 30 | ||
| done | ||
| echo "Found signed file gs://$_BUCKET_NAME/$f!" | ||
| done |
There was a problem hiding this comment.
Add a timeout limit to the polling loop in the download-signed-binaries step to prevent the Cloud Build pipeline from hanging indefinitely if any of the signing jobs fail.
echo "Waiting for Kokoro to sign and upload all binaries to gs://$_BUCKET_NAME..."
for f in "${EXPECTED_FILES[@]}"; do
attempts=0
max_attempts=40 # 20 minutes per file
until gcloud storage stat "gs://$_BUCKET_NAME/$f" >/dev/null 2>&1; do
if [ $attempts -ge $max_attempts ]; then
echo "Error: Timeout waiting for gs://$_BUCKET_NAME/$f" >&2
exit 1
fi
echo "Waiting for gs://$_BUCKET_NAME/$f..."
sleep 30
attempts=$((attempts + 1))
done
echo "Found signed file gs://$_BUCKET_NAME/$f!"
done| # 1. Download the detached GPG signature file (.sig) | ||
|
|
||
| curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox.sig |
There was a problem hiding this comment.
Wrap the GPG signature download URL in release-please markers so that the version is automatically updated in future releases, keeping it in sync with the binary version:
# 1. Download the detached GPG signature file (.sig)
<!-- {x-release-please-start-version} -->
curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v1.5.0/$OS/toolbox.sig
<!-- {x-release-please-end} -->| 1. Run the Toolbox server, pointing to the `tools.yaml` file created earlier: | ||
|
|
||
| ```bash | ||
| ./toolbox --config "tools.yaml" | ||
| ``` | ||
| ```bash | ||
| ./toolbox --config "tools.yaml" | ||
| ``` | ||
|
|
||
| {{< notice note >}} | ||
|
|
||
| {{< notice note >}} | ||
| Toolbox enables dynamic reloading by default. To disable, use the | ||
| `--disable-reload` flag. | ||
| Toolbox enables dynamic reloading by default. To disable, use the | ||
| `--disable-reload` flag. | ||
| {{< /notice >}} |
There was a problem hiding this comment.
Standardize the indentation of the nested blocks under the list item to 4 spaces. The current 8-space indentation for the code block and notice tags, combined with 4-space indentation for the notice body, is inconsistent and can cause rendering issues in Hugo:
1. Run the toolbox server, pointing to the `tools.yaml` file created earlier:
```bash
./toolbox --config "tools.yaml"
```
{{< notice note >}}
toolbox enables dynamic reloading by default. To disable, use the
`--disable-reload` flag.
{{< /notice >}}References
- Tool names must be in snake_case.
Sign all binaries in versioned release script.
Fix: #996