chore: import upstream snapshot with attribution
CF: Deploy Dev Docs / deploy (push) Waiting to run
Sync Labels / build (push) Waiting to run
tests / unit tests (macos-latest) (push) Waiting to run
tests / unit tests (ubuntu-latest) (push) Waiting to run
tests / unit tests (windows-latest) (push) Waiting to run
CF: Deploy Dev Docs / deploy (push) Waiting to run
Sync Labels / build (push) Waiting to run
tests / unit tests (macos-latest) (push) Waiting to run
tests / unit tests (ubuntu-latest) (push) Waiting to run
tests / unit tests (windows-latest) (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
# Copyright 2024 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- id: "build-docker"
|
||||
name: "gcr.io/cloud-builders/docker"
|
||||
waitFor: ['-']
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
docker buildx create --name container-builder --driver docker-container --bootstrap --use
|
||||
|
||||
export TAGS="-t ${_DOCKER_URI}:$SHORT_SHA -t ${_DOCKER_URI}:$REF_NAME"
|
||||
docker buildx build --platform linux/amd64,linux/arm64 --build-arg COMMIT_SHA=$(git rev-parse --short HEAD) $TAGS --push .
|
||||
|
||||
- id: "install-dependencies"
|
||||
name: golang:1
|
||||
waitFor: ['-']
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
script: |
|
||||
go get -d ./...
|
||||
|
||||
- id: "install-zig"
|
||||
name: golang:1
|
||||
waitFor: ['-']
|
||||
volumes:
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
apt-get update && apt-get install -y xz-utils
|
||||
curl -fL "https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz" -o zig.tar.xz
|
||||
tar -xf zig.tar.xz -C /zig-tools --strip-components=1
|
||||
|
||||
- id: "install-macos-sdk"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor: ['-']
|
||||
volumes:
|
||||
- name: 'macos-sdk'
|
||||
path: '/macos-sdk'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
apt-get update && apt-get install -y xz-utils
|
||||
echo "Downloading macOS 14.5 SDK..."
|
||||
gcloud storage cp gs://${_ASSETS_BUCKET}/MacOSX14.5.tar.xz sdk.tar.xz
|
||||
|
||||
mkdir -p /macos-sdk/MacOSX14.5.sdk
|
||||
echo "Unpacking macOS 14.5 SDK..."
|
||||
tar -xf sdk.tar.xz -C /macos-sdk/MacOSX14.5.sdk --strip-components=1
|
||||
|
||||
- id: "build-linux-amd64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=linux'
|
||||
- 'GOARCH=amd64'
|
||||
- 'CC=/zig-tools/zig cc -target x86_64-linux-gnu'
|
||||
- 'CXX=/zig-tools/zig c++ -target x86_64-linux-gnu'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.linux.amd64
|
||||
|
||||
- id: "store-linux-amd64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-linux-amd64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
gcloud storage cp toolbox.linux.amd64 gs://$_BUCKET_NAME/$REF_NAME/linux/amd64/toolbox
|
||||
|
||||
- id: "build-linux-amd64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=linux'
|
||||
- 'GOARCH=amd64'
|
||||
- 'CC=/zig-tools/zig cc -target x86_64-linux-gnu'
|
||||
- 'CXX=/zig-tools/zig c++ -target x86_64-linux-gnu'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.linux.amd64
|
||||
|
||||
- id: "build-darwin-arm64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=darwin'
|
||||
- 'GOARCH=arm64'
|
||||
- 'SDK_PATH=/macos-sdk/MacOSX14.5.sdk'
|
||||
- 'MACOS_MIN_VER=10.14'
|
||||
- 'CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib'
|
||||
- 'COMMON_FLAGS=-mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
- name: 'macos-sdk'
|
||||
path: '/macos-sdk'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.darwin.arm64
|
||||
|
||||
- id: "store-darwin-arm64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-darwin-arm64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
gcloud storage cp toolbox.darwin.arm64 gs://$_BUCKET_NAME/$REF_NAME/darwin/arm64/toolbox
|
||||
|
||||
- id: "build-darwin-arm64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=darwin'
|
||||
- 'GOARCH=arm64'
|
||||
- 'SDK_PATH=/macos-sdk/MacOSX14.5.sdk'
|
||||
- 'MACOS_MIN_VER=10.14'
|
||||
- 'CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib'
|
||||
- 'COMMON_FLAGS=-mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
- name: 'macos-sdk'
|
||||
path: '/macos-sdk'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.darwin.arm64
|
||||
|
||||
- id: "build-darwin-amd64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=darwin'
|
||||
- 'GOARCH=amd64'
|
||||
- 'SDK_PATH=/macos-sdk/MacOSX14.5.sdk'
|
||||
- 'MACOS_MIN_VER=10.14'
|
||||
- 'CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib'
|
||||
- 'COMMON_FLAGS=-mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
- name: 'macos-sdk'
|
||||
path: '/macos-sdk'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.darwin.amd64
|
||||
|
||||
- id: "store-darwin-amd64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-darwin-amd64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
gcloud storage cp toolbox.darwin.amd64 gs://$_BUCKET_NAME/$REF_NAME/darwin/amd64/toolbox
|
||||
|
||||
- id: "build-darwin-amd64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=darwin'
|
||||
- 'GOARCH=amd64'
|
||||
- 'SDK_PATH=/macos-sdk/MacOSX14.5.sdk'
|
||||
- 'MACOS_MIN_VER=10.14'
|
||||
- 'CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib'
|
||||
- 'COMMON_FLAGS=-mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
- 'CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
- name: 'macos-sdk'
|
||||
path: '/macos-sdk'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.darwin.amd64
|
||||
|
||||
- id: "build-windows-amd64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=windows'
|
||||
- 'GOARCH=amd64'
|
||||
- 'CC=/zig-tools/zig cc -target x86_64-windows-gnu'
|
||||
- 'CXX=/zig-tools/zig c++ -target x86_64-windows-gnu'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.windows.amd64
|
||||
|
||||
- id: "store-windows-amd64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-windows-amd64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
gcloud storage cp toolbox.windows.amd64 gs://$_BUCKET_NAME/$REF_NAME/windows/amd64/toolbox.exe
|
||||
|
||||
- id: "build-windows-amd64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=windows'
|
||||
- 'GOARCH=amd64'
|
||||
- 'CC=/zig-tools/zig cc -target x86_64-windows-gnu'
|
||||
- 'CXX=/zig-tools/zig c++ -target x86_64-windows-gnu'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.windows.amd64
|
||||
|
||||
- id: "build-windows-arm64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=windows'
|
||||
- 'GOARCH=arm64'
|
||||
- 'CC=/zig-tools/zig cc -target aarch64-windows-gnu'
|
||||
- 'CXX=/zig-tools/zig c++ -target aarch64-windows-gnu'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.windows.arm64
|
||||
|
||||
- id: "store-windows-arm64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-windows-arm64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
gcloud storage cp toolbox.windows.arm64 gs://$_BUCKET_NAME/$REF_NAME/windows/arm64/toolbox.exe
|
||||
|
||||
- id: "build-windows-arm64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- 'GOPATH=/gopath'
|
||||
- 'CGO_ENABLED=1'
|
||||
- 'GOOS=windows'
|
||||
- 'GOARCH=arm64'
|
||||
- 'CC=/zig-tools/zig cc -target aarch64-windows-gnu'
|
||||
- 'CXX=/zig-tools/zig c++ -target aarch64-windows-gnu'
|
||||
volumes:
|
||||
- name: 'go'
|
||||
path: '/gopath'
|
||||
- name: 'zig'
|
||||
path: '/zig-tools'
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.windows.arm64
|
||||
|
||||
options:
|
||||
automapSubstitutions: true
|
||||
dynamicSubstitutions: true
|
||||
logging: CLOUD_LOGGING_ONLY # Necessary for custom service account
|
||||
pool:
|
||||
name: projects/$PROJECT_ID/locations/us-central1/workerPools/run-release # to increase resource for running releases
|
||||
|
||||
substitutions:
|
||||
_REGION: us-central1
|
||||
_AR_HOSTNAME: ${_REGION}-docker.pkg.dev
|
||||
_AR_REPO_NAME: toolbox-dev
|
||||
_BUCKET_NAME: mcp-toolbox-for-databases-dev
|
||||
_ASSETS_BUCKET: toolbox-build-assets
|
||||
_DOCKER_URI: ${_AR_HOSTNAME}/${PROJECT_ID}/${_AR_REPO_NAME}/toolbox
|
||||
@@ -0,0 +1 @@
|
||||
(^|/)internal/server/|(^|/)internal/util/|internal/tools/[^/]+\.go|internal/sources/[^/]+\.go|(^|/)internal/auth/|(^|/)internal/telemetry/|(^|/)internal/log/|(^|/)internal/testutils/|(^|/)internal/embeddingmodels/|(^|/)internal/sources/sqlcommenter/|(^|/)internal/prebuiltconfigs/|(^|/)internal/prompts/|tests/[^/]+\.go|\.ci/|go\.mod|go\.sum|main\.go|(^|/)cmd/
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# NOTE: The _CLUSTER_NAME substitution variable defined here must match the
|
||||
# DATAPROC_LIST_JOBS_CLUSTER parameter defined in .ci/integration.cloudbuild.yaml
|
||||
# (default: dataproc-testing-cluster).
|
||||
|
||||
steps:
|
||||
- id: "recreate-dataproc-cluster"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
env:
|
||||
- "PROJECT_ID=$PROJECT_ID"
|
||||
- "CLUSTER_NAME=$_CLUSTER_NAME"
|
||||
- "REGION=$_REGION"
|
||||
- "IMAGE_VERSION=$_IMAGE_VERSION"
|
||||
- "SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_EMAIL"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/dataproc/recreate_dataproc_cluster.sh "$${PROJECT_ID}" "$${REGION}" "$${IMAGE_VERSION}" "$${CLUSTER_NAME}" "$${SERVICE_ACCOUNT_EMAIL}"
|
||||
|
||||
options:
|
||||
automapSubstitutions: true
|
||||
dynamicSubstitutions: true
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
pool:
|
||||
name: projects/$PROJECT_ID/locations/us-central1/workerPools/integration-testing
|
||||
|
||||
substitutions:
|
||||
_CLUSTER_NAME: "dataproc-testing-cluster"
|
||||
_REGION: "us-central1"
|
||||
_IMAGE_VERSION: "2.3-debian12"
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
if [ $# -lt 5 ]; then
|
||||
echo "Error: Missing required arguments." >&2
|
||||
echo "Usage: $0 <project_id> <region> <image_version> <cluster_name> <service_account>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROJECT_ID="$1"
|
||||
REGION="$2"
|
||||
IMAGE_VERSION="$3"
|
||||
CLUSTER_NAME="$4"
|
||||
SERVICE_ACCOUNT="$5"
|
||||
|
||||
echo "=========================================================="
|
||||
echo "Recreating Dataproc cluster in project: ${PROJECT_ID}"
|
||||
echo "Region: ${REGION}"
|
||||
echo "Image Version: ${IMAGE_VERSION}"
|
||||
echo "Cluster Name: ${CLUSTER_NAME}"
|
||||
echo "Service Account: ${SERVICE_ACCOUNT}"
|
||||
echo "=========================================================="
|
||||
|
||||
# Check if the cluster exists and get its status, capturing output to handle NOT_FOUND
|
||||
echo "Checking if cluster '${CLUSTER_NAME}' exists..."
|
||||
set +e
|
||||
STATE=$(gcloud dataproc clusters describe "${CLUSTER_NAME}" --region="${REGION}" --project="${PROJECT_ID}" --format="value(status.state)" 2>&1)
|
||||
DESCRIBE_STATUS=$?
|
||||
set -e
|
||||
|
||||
if [ ${DESCRIBE_STATUS} -eq 0 ]; then
|
||||
if [ "${STATE}" = "DELETING" ]; then
|
||||
echo "Cluster '${CLUSTER_NAME}' is currently in DELETING state. Waiting for deletion to complete..."
|
||||
while gcloud dataproc clusters describe "${CLUSTER_NAME}" --region="${REGION}" --project="${PROJECT_ID}" >/dev/null 2>&1; do
|
||||
echo "Waiting 10 seconds for cluster deletion..."
|
||||
sleep 10
|
||||
done
|
||||
echo "Cluster '${CLUSTER_NAME}' has been successfully deleted."
|
||||
else
|
||||
echo "Cluster '${CLUSTER_NAME}' exists (state: ${STATE}). Deleting it..."
|
||||
gcloud dataproc clusters delete "${CLUSTER_NAME}" \
|
||||
--region="${REGION}" \
|
||||
--project="${PROJECT_ID}" \
|
||||
--quiet
|
||||
echo "Cluster '${CLUSTER_NAME}' deleted successfully."
|
||||
fi
|
||||
elif echo "${STATE}" | grep -q "NOT_FOUND"; then
|
||||
echo "Cluster '${CLUSTER_NAME}' does not exist. Skipping deletion."
|
||||
else
|
||||
echo "Error querying cluster existence: ${STATE}" >&2
|
||||
exit ${DESCRIBE_STATUS}
|
||||
fi
|
||||
|
||||
# Create the cluster
|
||||
echo "Creating Dataproc cluster '${CLUSTER_NAME}'..."
|
||||
gcloud dataproc clusters create "${CLUSTER_NAME}" \
|
||||
--region="${REGION}" \
|
||||
--project="${PROJECT_ID}" \
|
||||
--image-version="${IMAGE_VERSION}" \
|
||||
--service-account="${SERVICE_ACCOUNT}" \
|
||||
--scopes=cloud-platform \
|
||||
--no-address \
|
||||
--network=default \
|
||||
--master-machine-type=n4-standard-2 \
|
||||
--worker-machine-type=n4-standard-2 \
|
||||
--num-workers=2
|
||||
|
||||
echo "Cluster '${CLUSTER_NAME}' created successfully."
|
||||
|
||||
# Integration tests require at least one job to exist on the test cluster
|
||||
echo "Submitting a test Spark job to cluster '${CLUSTER_NAME}'..."
|
||||
gcloud dataproc jobs submit spark \
|
||||
--project="${PROJECT_ID}" \
|
||||
--region="${REGION}" \
|
||||
--cluster="${CLUSTER_NAME}" \
|
||||
--class=org.apache.spark.examples.SparkPi \
|
||||
--jars=file:///usr/lib/spark/examples/jars/spark-examples.jar \
|
||||
-- 100
|
||||
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#! /bin/bash
|
||||
|
||||
|
||||
# Check if VERSION has been set
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "Error: VERSION env var is not set" >&2 # Print to stderr
|
||||
exit 1 # Exit with a non-zero status to indicate an error
|
||||
fi
|
||||
|
||||
|
||||
FILES=("linux.amd64" "darwin.arm64" "darwin.amd64" "windows.amd64" "windows.arm64")
|
||||
|
||||
echo "Waiting for Kokoro to sign and upload binaries to gs://mcp-toolbox-for-databases..."
|
||||
|
||||
# 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
|
||||
URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox.exe"
|
||||
else
|
||||
URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox"
|
||||
fi
|
||||
|
||||
until curl --fail --silent --head "${URL}" > /dev/null 2>&1; do
|
||||
echo "Waiting for signed binary: ${URL}..."
|
||||
sleep 30
|
||||
done
|
||||
echo "Found signed binary: ${URL}!"
|
||||
done
|
||||
|
||||
# Wait for the Linux GPG signature
|
||||
LINUX_SIG_URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.asc"
|
||||
until curl --fail --silent --head "${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}!"
|
||||
|
||||
|
||||
output_string=""
|
||||
|
||||
# Define the descriptions - ensure this array's order matches FILES
|
||||
DESCRIPTIONS=(
|
||||
"For **Linux** systems running on **Intel/AMD 64-bit processors**."
|
||||
"For **macOS** systems running on **Apple Silicon** (M1, M2, M3, etc.) processors."
|
||||
"For **macOS** systems running on **Intel processors**."
|
||||
"For **Windows** systems running on **Intel/AMD 64-bit processors**."
|
||||
"For **Windows** systems running on **ARM 64-bit processors**."
|
||||
)
|
||||
|
||||
# Write the table header
|
||||
ROW_FMT="| %-105s | %-120s | %-67s |\n"
|
||||
output_string+=$(printf "$ROW_FMT" "**OS/Architecture**" "**Description**" "**SHA256 Hash**")$'\n'
|
||||
output_string+=$(printf "$ROW_FMT" "$(printf -- '-%0.s' {1..105})" "$(printf -- '-%0.s' {1..120})" "$(printf -- '-%0.s' {1..67})")$'\n'
|
||||
|
||||
|
||||
# Loop through all files matching the pattern "toolbox.*.*"
|
||||
for i in "${!FILES[@]}"
|
||||
do
|
||||
file_key="${FILES[$i]}" # e.g., "linux.amd64"
|
||||
description_text="${DESCRIPTIONS[$i]}"
|
||||
|
||||
# Extract OS and ARCH from the filename
|
||||
OS=$(echo "$file_key" | cut -d '.' -f 1)
|
||||
ARCH=$(echo "$file_key" | cut -d '.' -f 2)
|
||||
|
||||
# Get release URL
|
||||
if [ "$OS" = 'windows' ];
|
||||
then
|
||||
URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox.exe"
|
||||
else
|
||||
URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/$OS/$ARCH/toolbox"
|
||||
fi
|
||||
|
||||
curl "$URL" --fail --output toolbox || exit 1
|
||||
|
||||
# Calculate the SHA256 checksum of the file
|
||||
SHA256=$(shasum -a 256 toolbox | awk '{print $1}')
|
||||
|
||||
# Write the table row
|
||||
if [ "$OS" = 'linux' ]; then
|
||||
SIG_URL="https://storage.googleapis.com/mcp-toolbox-for-databases/$VERSION/linux/amd64/toolbox.asc"
|
||||
output_string+=$(printf "$ROW_FMT" "[$OS/$ARCH]($URL) ([Signature]($SIG_URL))" "$description_text" "$SHA256")$'\n'
|
||||
else
|
||||
output_string+=$(printf "$ROW_FMT" "[$OS/$ARCH]($URL)" "$description_text" "$SHA256")$'\n'
|
||||
fi
|
||||
|
||||
rm toolbox
|
||||
done
|
||||
|
||||
printf "$output_string\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+58
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ==============================================================================
|
||||
# Script: lint-sample-filters.sh
|
||||
# Description: Checks markdown files to ensure 'sample_filters' match the
|
||||
# allowed tags in filters.yaml.
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
FILTERS_FILE=".hugo/data/filters.yaml"
|
||||
DOCS_DIR="docs/en/"
|
||||
FAILED=0
|
||||
|
||||
# Load valid filters from the YAML file (remove dashes and quotes)
|
||||
VALID_FILTERS=$(grep -Eo "^\s*-\s*.*" "$FILTERS_FILE" | sed -E 's/^\s*-\s*["\047]?//; s/["\047]?\s*$//')
|
||||
|
||||
echo "Scanning $DOCS_DIR for invalid sample filters..."
|
||||
|
||||
# Find and check each markdown file
|
||||
while IFS= read -r file; do
|
||||
|
||||
# CASE A: Inline Array (e.g., sample_filters: ["Tag 1", "Tag 2"])
|
||||
if grep -q "^sample_filters:\s*\[" "$file"; then
|
||||
TAGS=$(grep "^sample_filters:\s*\[" "$file" | grep -Eo '"[^"]+"|\x27[^\x27]+\x27' | sed "s/['\"]//g")
|
||||
|
||||
# CASE B: Vertical List (e.g., - Tag 1 \n - Tag 2)
|
||||
elif grep -q "^sample_filters:" "$file"; then
|
||||
TAGS=$(awk '/^sample_filters:/ {flag=1; next} /^[a-zA-Z]/ {flag=0} flag && /-/ {sub(/^[ \t]*-[ \t]*["\x27]?/, ""); sub(/["\x27]?\s*$/, ""); print}' "$file")
|
||||
|
||||
# Skip file if no sample_filters exist
|
||||
else
|
||||
continue
|
||||
fi
|
||||
|
||||
# Validate extracted tags against the allowed list
|
||||
while IFS= read -r tag; do
|
||||
# Skip empty lines
|
||||
[[ -z "$tag" ]] && continue
|
||||
|
||||
# Check if the exact tag exists in our valid list
|
||||
if ! grep -Fxq "$tag" <<< "$VALID_FILTERS"; then
|
||||
echo "Invalid filter found: '$tag' in $file"
|
||||
FAILED=1
|
||||
fi
|
||||
done <<< "$TAGS"
|
||||
|
||||
done < <(find "$DOCS_DIR" -name "*.md")
|
||||
|
||||
# Final Output
|
||||
if [[ $FAILED -eq 1 ]]; then
|
||||
echo "------------------------------------------------------"
|
||||
echo "Build failed: Unapproved sample_filter detected."
|
||||
echo "Check your spelling/spaces or add it to $FILTERS_FILE."
|
||||
exit 1
|
||||
else
|
||||
echo "All sample filters are valid!"
|
||||
exit 0
|
||||
fi
|
||||
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
|
||||
python3 - << 'EOF'
|
||||
"""
|
||||
MCP TOOLBOX: SOURCE PAGE LINTER
|
||||
===============================
|
||||
This script enforces a standardized structure for integration Source pages
|
||||
(source.md files). It ensures users can predictably find connection details
|
||||
and configurations across all database integrations.
|
||||
|
||||
Note: The structural _index.md folder wrappers are intentionally ignored
|
||||
by this script as they should only contain YAML frontmatter.
|
||||
|
||||
MAINTENANCE GUIDE:
|
||||
------------------
|
||||
1. TO ADD A NEW HEADING:
|
||||
Add the exact heading text to the 'ALLOWED_ORDER' list in the desired
|
||||
sequence.
|
||||
|
||||
2. TO MAKE A HEADING MANDATORY/OPTIONAL:
|
||||
Add or remove the heading text in the 'REQUIRED' set.
|
||||
|
||||
3. TO IGNORE NEW CONTENT TYPES:
|
||||
Update the regex in the 'clean_body' variable to strip out
|
||||
Markdown before linting.
|
||||
|
||||
4. SCOPE:
|
||||
This script only targets docs/en/integrations/**/source.md.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
ALLOWED_ORDER = [
|
||||
"About",
|
||||
"Available Tools",
|
||||
"Requirements",
|
||||
"Example",
|
||||
"Reference",
|
||||
"Advanced Usage",
|
||||
"Troubleshooting",
|
||||
"Additional Resources"
|
||||
]
|
||||
REQUIRED = {"About", "Example", "Reference"}
|
||||
|
||||
# Regex to catch any variation of the list-tools shortcode
|
||||
SHORTCODE_PATTERN = r"\{\{<\s*list-tools.*?>\}\}"
|
||||
# ---------------------
|
||||
|
||||
integration_dir = Path("./docs/en/integrations")
|
||||
if not integration_dir.exists():
|
||||
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
|
||||
sys.exit(0)
|
||||
|
||||
has_errors = False
|
||||
source_pages_found = 0
|
||||
|
||||
# ONLY scan files specifically named "source.md"
|
||||
for filepath in integration_dir.rglob("source.md"):
|
||||
source_pages_found += 1
|
||||
file_errors = False
|
||||
|
||||
if filepath.parent.parent != integration_dir:
|
||||
continue
|
||||
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
match = re.match(r'^\s*---\s*\n(.*?)\n---\s*(.*)', content, re.DOTALL)
|
||||
if match:
|
||||
frontmatter, body = match.group(1), match.group(2)
|
||||
else:
|
||||
print(f"[{filepath}] Error: Missing or invalid YAML frontmatter.")
|
||||
has_errors = True
|
||||
continue
|
||||
|
||||
# 1. Check for linkTitle: "Source" in frontmatter
|
||||
link_title_match = re.search(r"^linkTitle:\s*[\"']?(.*?)[\"']?\s*$", frontmatter, re.MULTILINE)
|
||||
if not link_title_match or link_title_match.group(1).strip() != "Source":
|
||||
print(f"[{filepath}] Error: Frontmatter must contain exactly linkTitle: \"Source\".")
|
||||
file_errors = True
|
||||
|
||||
# 2. Check for weight: 1 in frontmatter
|
||||
weight_match = re.search(r"^weight:\s*[\"']?(\d+)[\"']?\s*$", frontmatter, re.MULTILINE)
|
||||
if not weight_match or weight_match.group(1).strip() != "1":
|
||||
print(f"[{filepath}] Error: Frontmatter must contain exactly weight: 1.")
|
||||
file_errors = True
|
||||
|
||||
# 3. Check Shortcode Placement & Available Tools Section (Only if present)
|
||||
tools_section_match = re.search(r"^##\s+Available Tools\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
|
||||
if tools_section_match:
|
||||
if not re.search(SHORTCODE_PATTERN, tools_section_match.group(1)):
|
||||
print(f"[{filepath}] Error: The list-tools shortcode must be placed under the '## Available Tools' heading.")
|
||||
file_errors = True
|
||||
|
||||
# Strip code blocks from body to avoid linting example markdown headings
|
||||
clean_body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)
|
||||
|
||||
if re.search(r"^#\s+\w+", clean_body, re.MULTILINE):
|
||||
print(f"[{filepath}] Error: H1 (#) headings are forbidden in the body.")
|
||||
file_errors = True
|
||||
|
||||
h2s = [h.strip() for h in re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)]
|
||||
|
||||
# Missing Required Headings
|
||||
missing = REQUIRED - set(h2s)
|
||||
if missing:
|
||||
print(f"[{filepath}] Error: Missing required H2 headings: {missing}")
|
||||
file_errors = True
|
||||
|
||||
if unauthorized := (set(h2s) - set(ALLOWED_ORDER)):
|
||||
print(f"[{filepath}] Error: Unauthorized H2s found: {unauthorized}")
|
||||
file_errors = True
|
||||
|
||||
# 5. Order Check
|
||||
if [h for h in h2s if h in ALLOWED_ORDER] != [h for h in ALLOWED_ORDER if h in h2s]:
|
||||
print(f"[{filepath}] Error: Headings out of order. Reference: {ALLOWED_ORDER}")
|
||||
file_errors = True
|
||||
|
||||
if file_errors: has_errors = True
|
||||
|
||||
# Handle final output based on what was found
|
||||
if source_pages_found == 0:
|
||||
print("Info: No 'source.md' files found in integrations. Passing gracefully.")
|
||||
sys.exit(0)
|
||||
elif has_errors:
|
||||
print(f"\nLinting failed. Please fix the structure errors in the {source_pages_found} 'source.md' file(s) above.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"Success: {source_pages_found} 'source.md' file(s) passed structure validation.")
|
||||
EOF
|
||||
Executable
+155
@@ -0,0 +1,155 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
python3 - << 'EOF'
|
||||
"""
|
||||
MCP TOOLBOX: TOOL PAGE LINTER
|
||||
=============================
|
||||
This script enforces a standardized structure for individual Tool pages
|
||||
and their parent directory wrappers. It ensures LLM agents can parse
|
||||
tool capabilities and parameter definitions reliably.
|
||||
|
||||
MAINTENANCE GUIDE:
|
||||
------------------
|
||||
1. TO ADD A NEW HEADING:
|
||||
Add the exact heading text to the 'ALLOWED_ORDER' list in the desired
|
||||
sequence.
|
||||
|
||||
2. TO MAKE A HEADING MANDATORY/OPTIONAL:
|
||||
Add or remove the heading text in the 'REQUIRED' set.
|
||||
|
||||
3. TO UPDATE SHORTCODE LOGIC:
|
||||
If the shortcode name changes, update the 'SHORTCODE_PATTERN' variable.
|
||||
|
||||
4. SCOPE & BEHAVIOR:
|
||||
This script targets all .md files in docs/en/integrations/**/tools/.
|
||||
- For `_index.md` files: It only validates the frontmatter (requiring
|
||||
`title: "Tools"` and `weight: 2`) and ignores the body.
|
||||
- For regular tool files: It validates H1/H2 hierarchy, checks for
|
||||
required headings ("About", "Example"), and enforces that the
|
||||
`{{< compatible-sources >}}` shortcode is paired with the
|
||||
"## Compatible Sources" heading.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
ALLOWED_ORDER = [
|
||||
"About",
|
||||
"Compatible Sources",
|
||||
"Requirements",
|
||||
"Parameters",
|
||||
"Example",
|
||||
"Output Format",
|
||||
"Reference",
|
||||
"Advanced Usage",
|
||||
"Troubleshooting",
|
||||
"Additional Resources"
|
||||
]
|
||||
REQUIRED = {"About", "Example"}
|
||||
SHORTCODE_PATTERN = r"\{\{<\s*compatible-sources.*?>\}\}"
|
||||
# ---------------------
|
||||
|
||||
integration_dir = Path("./docs/en/integrations")
|
||||
if not integration_dir.exists():
|
||||
print("Info: Directory './docs/en/integrations' not found. Skipping linting.")
|
||||
sys.exit(0)
|
||||
|
||||
has_errors = False
|
||||
tools_pages_found = 0
|
||||
|
||||
# Specifically target the tools directories
|
||||
for filepath in integration_dir.rglob("tools/*.md"):
|
||||
tools_pages_found += 1
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
|
||||
# Separate YAML frontmatter from the markdown body
|
||||
match = re.match(r'^\s*---\s*\n(.*?)\n---\s*(.*)', content, re.DOTALL)
|
||||
if match:
|
||||
frontmatter = match.group(1)
|
||||
body = match.group(2)
|
||||
else:
|
||||
print(f"[{filepath}] Error: Missing or invalid YAML frontmatter.")
|
||||
has_errors = True
|
||||
continue
|
||||
|
||||
file_errors = False
|
||||
|
||||
# --- SPECIAL VALIDATION FOR tools/_index.md ---
|
||||
if filepath.name == "_index.md":
|
||||
title_match = re.search(r"^title:\s*[\"']?(.*?)[\"']?\s*$", frontmatter, re.MULTILINE)
|
||||
if not title_match or title_match.group(1).strip() != "Tools":
|
||||
print(f"[{filepath}] Error: tools/_index.md must have exactly title: \"Tools\"")
|
||||
file_errors = True
|
||||
|
||||
weight_match = re.search(r"^weight:\s*(\d+)\s*$", frontmatter, re.MULTILINE)
|
||||
if not weight_match or weight_match.group(1).strip() != "2":
|
||||
print(f"[{filepath}] Error: tools/_index.md must have exactly weight: 2")
|
||||
file_errors = True
|
||||
|
||||
if file_errors:
|
||||
has_errors = True
|
||||
continue # Skip the rest of the body linting for this structural file
|
||||
|
||||
# --- VALIDATION FOR REGULAR TOOL PAGES ---
|
||||
# If the file has no markdown content (metadata placeholder only), skip it entirely
|
||||
if not body.strip():
|
||||
continue
|
||||
|
||||
# 1. Check Shortcode Placement
|
||||
sources_section_match = re.search(r"^##\s+Compatible Sources\s*(.*?)(?=^##\s|\Z)", body, re.MULTILINE | re.DOTALL)
|
||||
if sources_section_match:
|
||||
if not re.search(SHORTCODE_PATTERN, sources_section_match.group(1)):
|
||||
print(f"[{filepath}] Error: The compatible-sources shortcode must be placed under '## Compatible Sources'.")
|
||||
file_errors = True
|
||||
elif re.search(SHORTCODE_PATTERN, body):
|
||||
print(f"[{filepath}] Error: Shortcode found, but '## Compatible Sources' heading is missing.")
|
||||
file_errors = True
|
||||
|
||||
# 2. Strip code blocks from body to avoid linting example markdown headings
|
||||
clean_body = re.sub(r"```.*?```", "", body, flags=re.DOTALL)
|
||||
|
||||
# 3. Check H1 Headings
|
||||
if re.search(r"^#\s+\w+", clean_body, re.MULTILINE):
|
||||
print(f"[{filepath}] Error: H1 headings (#) are forbidden in the body.")
|
||||
file_errors = True
|
||||
|
||||
# 4. Check H2 Headings
|
||||
h2s = re.findall(r"^##\s+(.*)", clean_body, re.MULTILINE)
|
||||
h2s = [h2.strip() for h2 in h2s]
|
||||
|
||||
# Missing Required
|
||||
if missing := (REQUIRED - set(h2s)):
|
||||
print(f"[{filepath}] Error: Missing required H2 headings: {missing}")
|
||||
file_errors = True
|
||||
|
||||
# Unauthorized Headings
|
||||
if unauthorized := (set(h2s) - set(ALLOWED_ORDER)):
|
||||
print(f"[{filepath}] Error: Unauthorized H2 headings found: {unauthorized}")
|
||||
file_errors = True
|
||||
|
||||
# Strict Ordering
|
||||
filtered_h2s = [h for h in h2s if h in ALLOWED_ORDER]
|
||||
expected_order = [h for h in ALLOWED_ORDER if h in h2s]
|
||||
if filtered_h2s != expected_order:
|
||||
print(f"[{filepath}] Error: Headings are out of order.")
|
||||
print(f" Expected: {expected_order}")
|
||||
print(f" Found: {filtered_h2s}")
|
||||
file_errors = True
|
||||
|
||||
if file_errors:
|
||||
has_errors = True
|
||||
|
||||
if tools_pages_found == 0:
|
||||
print("Info: No tool directories found. Passing gracefully.")
|
||||
sys.exit(0)
|
||||
elif has_errors:
|
||||
print("Linting failed for Tool pages. Please fix the structure errors above.")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"Success: All {tools_pages_found} Tool page(s) passed structure validation.")
|
||||
EOF
|
||||
@@ -0,0 +1,51 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Retry-only pipeline for the OSS Exit Gate npm publish steps. Use when the
|
||||
# npm portion of the versioned release fails after Go binaries are already in
|
||||
# GCS. Each platform package's prepack script downloads its binary from GCS by
|
||||
# version, and publish_npm_to_ar.sh's idempotency check skips packages already
|
||||
# in AR.
|
||||
#
|
||||
# Invoke after checking out the version's tag locally:
|
||||
# git checkout v<X.Y.Z>
|
||||
# gcloud builds submit \
|
||||
# --config=.ci/npm_retry.cloudbuild.yaml \
|
||||
# --region=us-central1 \
|
||||
# --service-account=projects/database-toolbox/serviceAccounts/release@database-toolbox.iam.gserviceaccount.com \
|
||||
# .
|
||||
|
||||
steps:
|
||||
- id: "publish-npm-to-ar"
|
||||
name: node:20
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/publish_npm_to_ar.sh
|
||||
|
||||
- id: "trigger-exit-gate"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "publish-npm-to-ar"
|
||||
env:
|
||||
- "BUILD_ID=$BUILD_ID"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/trigger_exit_gate.sh npm
|
||||
|
||||
options:
|
||||
automapSubstitutions: true
|
||||
dynamicSubstitutions: true
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
pool:
|
||||
name: projects/$PROJECT_ID/locations/us-central1/workerPools/run-release
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Publishes the six @toolbox-sdk npm packages (5 platform-specific + 1 wrapper)
|
||||
# to the OSS Exit Gate internal Artifact Registry. The Exit Gate then ships
|
||||
# them externally to npmjs.org once trigger_exit_gate.sh uploads the manifest.
|
||||
#
|
||||
# Required CWD: repo root (so npm/server-*/ paths resolve).
|
||||
#
|
||||
# Binaries (toolbox.<os>.<arch>) are picked up from the workspace if present
|
||||
# (versioned-release pipeline) and otherwise downloaded by each package's own
|
||||
# prepack script from GCS by version (retry pipeline).
|
||||
#
|
||||
# Each publish is gated by an "already in AR?" check so the script is safe to
|
||||
# re-run after a partial failure.
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# OSS Exit Gate constants — owned by Exit Gate, not build configuration.
|
||||
readonly AR_REGISTRY="https://us-npm.pkg.dev/oss-exit-gate-prod/mcp-toolbox--npm/"
|
||||
readonly AR_HOST="us-npm.pkg.dev"
|
||||
|
||||
# Write to $HOME so npm finds it as user config from inside any package dir.
|
||||
# A .npmrc at /workspace would be ignored — npm's per-project .npmrc must sit
|
||||
# next to package.json, and each platform package has its own package.json.
|
||||
cat > "$HOME/.npmrc" <<EOF
|
||||
@toolbox-sdk:registry=${AR_REGISTRY}
|
||||
//${AR_HOST}/:always-auth=true
|
||||
EOF
|
||||
|
||||
# Run from $HOME so google-artifactregistry-auth finds the .npmrc above and
|
||||
# writes the short-lived AR auth token into it.
|
||||
(cd "$HOME" && npx --yes google-artifactregistry-auth)
|
||||
|
||||
# Publishes the npm package at npm/${pkg} to the Exit Gate AR, unless that
|
||||
# exact version is already there (idempotency check makes retries safe).
|
||||
# --registry is passed explicitly so we don't depend on scope-config
|
||||
# resolution at publish time.
|
||||
publish_pkg() {
|
||||
local pkg="$1"
|
||||
local npm_name="@toolbox-sdk/${pkg}"
|
||||
local version
|
||||
version=$(cd "npm/${pkg}" && node -p "require('./package.json').version")
|
||||
|
||||
if npm view "${npm_name}@${version}" version --registry "${AR_REGISTRY}" 2>/dev/null | grep -q .; then
|
||||
echo "Skipping ${npm_name}@${version}: already in AR"
|
||||
return
|
||||
fi
|
||||
|
||||
(cd "npm/${pkg}" && npm publish --registry "${AR_REGISTRY}")
|
||||
}
|
||||
|
||||
# Stages a Cloud-Build-produced binary into a platform npm package and publishes
|
||||
# it. Copying the binary into bin/ short-circuits the package's prepack
|
||||
# download script (it skips when the binary already exists). When the binary
|
||||
# isn't in the workspace (retry pipeline), prepack downloads it from GCS.
|
||||
#
|
||||
# Args:
|
||||
# pkg: npm folder under npm/ (e.g., server-linux-x64)
|
||||
# src: go build output in the workspace (e.g., toolbox.linux.amd64)
|
||||
# dest: binary name inside the package's bin/ (toolbox or toolbox.exe)
|
||||
publish_platform() {
|
||||
local pkg="$1" src="$2" dest="$3"
|
||||
if [[ -f "${src}" ]]; then
|
||||
mkdir -p "npm/${pkg}/bin"
|
||||
cp "${src}" "npm/${pkg}/bin/${dest}"
|
||||
chmod +x "npm/${pkg}/bin/${dest}"
|
||||
fi
|
||||
publish_pkg "${pkg}"
|
||||
}
|
||||
|
||||
publish_platform "server-linux-x64" "toolbox.linux.amd64" "toolbox"
|
||||
publish_platform "server-darwin-arm64" "toolbox.darwin.arm64" "toolbox"
|
||||
publish_platform "server-darwin-x64" "toolbox.darwin.amd64" "toolbox"
|
||||
publish_platform "server-win32-x64" "toolbox.windows.amd64" "toolbox.exe"
|
||||
publish_platform "server-win32-arm64" "toolbox.windows.arm64" "toolbox.exe"
|
||||
|
||||
publish_pkg "server"
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Builds one toolbox-server wheel per supported platform and uploads them all
|
||||
# to the OSS Exit Gate internal Artifact Registry. Exit Gate ships them
|
||||
# externally to pypi.org once trigger_exit_gate.sh uploads the manifest.
|
||||
#
|
||||
# Required CWD: repo root.
|
||||
#
|
||||
# Binaries (toolbox.<os>.<arch>) must be pre-staged in the workspace by an
|
||||
# earlier Cloud Build step. The retry pipeline downloads them from GCS first.
|
||||
#
|
||||
# AR doesn't support twine's `--skip-existing` flag. If this version already
|
||||
# has wheels in AR (e.g., from a prior run where Exit Gate failed to externally
|
||||
# publish and left the AR contents in place), twine will error. Manually clean
|
||||
# the AR version before re-running:
|
||||
#
|
||||
# gcloud artifacts versions delete <VERSION> \
|
||||
# --package=toolbox-server \
|
||||
# --repository=mcp-toolbox--pypi \
|
||||
# --location=us \
|
||||
# --project=oss-exit-gate-prod
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
# OSS Exit Gate constants — owned by Exit Gate, not build configuration.
|
||||
readonly AR_URL="https://us-python.pkg.dev/oss-exit-gate-prod/mcp-toolbox--pypi/"
|
||||
readonly PYPI_DIR="pypi"
|
||||
readonly BIN_DIR="${PYPI_DIR}/src/toolbox_server/bin"
|
||||
readonly DIST_DIR="${PYPI_DIR}/dist"
|
||||
|
||||
# Install build + upload tooling. keyrings.google-artifactregistry-auth is the
|
||||
# keyring backend that twine uses to obtain a short-lived AR token from ADC.
|
||||
pip install --quiet --upgrade build twine keyring keyrings.google-artifactregistry-auth
|
||||
|
||||
# Start each build run with empty dist/ to make the final upload deterministic.
|
||||
rm -rf "${DIST_DIR}"
|
||||
|
||||
# Builds one wheel.
|
||||
#
|
||||
# Args:
|
||||
# src: go build output staged in the workspace (e.g., toolbox.linux.amd64)
|
||||
# plat: PEP 425 platform tag for the wheel (e.g., manylinux2014_x86_64)
|
||||
# dest: binary name inside the wheel's bin/ (toolbox or toolbox.exe)
|
||||
build_wheel() {
|
||||
local src="$1" plat="$2" dest="$3"
|
||||
|
||||
if [[ ! -f "${src}" ]]; then
|
||||
echo "ERROR: ${src} not found in workspace. Earlier build step must run first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Replace bin/ so a previous iteration's binary doesn't leak into this wheel.
|
||||
rm -rf "${BIN_DIR}"
|
||||
mkdir -p "${BIN_DIR}"
|
||||
cp "${src}" "${BIN_DIR}/${dest}"
|
||||
chmod +x "${BIN_DIR}/${dest}"
|
||||
|
||||
# Also wipe setuptools' build/ staging tree. It caches the previously-copied
|
||||
# binary at build/lib/toolbox_server/bin/, which would otherwise be packaged
|
||||
# into this iteration's wheel alongside the new binary (e.g., the Windows
|
||||
# wheel would end up shipping both toolbox AND toolbox.exe).
|
||||
rm -rf "${PYPI_DIR}/build"
|
||||
|
||||
(cd "${PYPI_DIR}" && TOOLBOX_PLATFORM="${plat}" python -m build --wheel)
|
||||
}
|
||||
|
||||
build_wheel "toolbox.linux.amd64" "manylinux2014_x86_64" "toolbox"
|
||||
build_wheel "toolbox.darwin.arm64" "macosx_11_0_arm64" "toolbox"
|
||||
build_wheel "toolbox.darwin.amd64" "macosx_10_14_x86_64" "toolbox"
|
||||
build_wheel "toolbox.windows.amd64" "win_amd64" "toolbox.exe"
|
||||
build_wheel "toolbox.windows.arm64" "win_arm64" "toolbox.exe"
|
||||
|
||||
twine upload \
|
||||
--repository-url "${AR_URL}" \
|
||||
"${DIST_DIR}"/*.whl
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Retry-only pipeline for the OSS Exit Gate PyPI publish steps. Use when the
|
||||
# PyPI portion of the versioned release fails after Go binaries are already in
|
||||
# GCS. The fetch-binaries step downloads the five platform binaries from GCS
|
||||
# by version into the workspace, then publish_pypi_to_ar.sh builds and
|
||||
# uploads the wheels (twine's --skip-existing makes uploads idempotent).
|
||||
#
|
||||
# Invoke after checking out the version's tag locally:
|
||||
# git checkout v<X.Y.Z>
|
||||
# gcloud builds submit \
|
||||
# --config=.ci/pypi_retry.cloudbuild.yaml \
|
||||
# --region=us-central1 \
|
||||
# --service-account=projects/database-toolbox/serviceAccounts/release@database-toolbox.iam.gserviceaccount.com \
|
||||
# .
|
||||
|
||||
steps:
|
||||
- id: "fetch-binaries"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
VERSION="v$(cat ./cmd/version.txt)"
|
||||
BUCKET="gs://mcp-toolbox-for-databases/${VERSION}"
|
||||
|
||||
gcloud storage cp "${BUCKET}/linux/amd64/toolbox" toolbox.linux.amd64
|
||||
gcloud storage cp "${BUCKET}/darwin/arm64/toolbox" toolbox.darwin.arm64
|
||||
gcloud storage cp "${BUCKET}/darwin/amd64/toolbox" toolbox.darwin.amd64
|
||||
gcloud storage cp "${BUCKET}/windows/amd64/toolbox.exe" toolbox.windows.amd64
|
||||
gcloud storage cp "${BUCKET}/windows/arm64/toolbox.exe" toolbox.windows.arm64
|
||||
|
||||
- id: "publish-pypi-to-ar"
|
||||
name: python:3.12
|
||||
waitFor:
|
||||
- "fetch-binaries"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/publish_pypi_to_ar.sh
|
||||
|
||||
- id: "trigger-exit-gate-pypi"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "publish-pypi-to-ar"
|
||||
env:
|
||||
- "BUILD_ID=$BUILD_ID"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/trigger_exit_gate.sh pypi
|
||||
|
||||
options:
|
||||
automapSubstitutions: true
|
||||
dynamicSubstitutions: true
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
pool:
|
||||
name: projects/$PROJECT_ID/locations/us-central1/workerPools/run-release
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- name: "${_IMAGE}"
|
||||
id: "go-pre-post-processing-test"
|
||||
entrypoint: "bash"
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -ex
|
||||
chmod +x .ci/sample_tests/run_tests.sh
|
||||
.ci/sample_tests/run_tests.sh
|
||||
env:
|
||||
- "CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}"
|
||||
- "GCP_PROJECT=${_GCP_PROJECT}"
|
||||
- "DATABASE_NAME=${_DATABASE_NAME}"
|
||||
- "DB_USER=${_DB_USER}"
|
||||
- "TARGET_ROOT=${_TARGET_ROOT}"
|
||||
- "TARGET_LANG=${_TARGET_LANG}"
|
||||
- "TABLE_NAME=${_TABLE_NAME}"
|
||||
- "SQL_FILE=${_SQL_FILE}"
|
||||
- "AGENT_FILE_PATTERN=${_AGENT_FILE_PATTERN}"
|
||||
secretEnv: ["TOOLS_YAML_CONTENT", "GOOGLE_API_KEY", "DB_PASSWORD"]
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/8
|
||||
env: "TOOLS_YAML_CONTENT"
|
||||
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
|
||||
env: "GOOGLE_API_KEY"
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
|
||||
env: "DB_PASSWORD"
|
||||
|
||||
timeout: 1200s
|
||||
|
||||
substitutions:
|
||||
_TARGET_LANG: "go"
|
||||
_IMAGE: "golang:1.25.7"
|
||||
_TARGET_ROOT: "docs/en/documentation/configuration/pre-post-processing/go"
|
||||
_TABLE_NAME: "hotels_go_pre_post_processing"
|
||||
_SQL_FILE: ".ci/sample_tests/setup_hotels.sql"
|
||||
_AGENT_FILE_PATTERN: "agent.go"
|
||||
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- name: "${_IMAGE}"
|
||||
id: "js-pre-post-processing-test"
|
||||
entrypoint: "bash"
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -ex
|
||||
chmod +x .ci/sample_tests/run_tests.sh
|
||||
.ci/sample_tests/run_tests.sh
|
||||
env:
|
||||
- "CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}"
|
||||
- "GCP_PROJECT=${_GCP_PROJECT}"
|
||||
- "DATABASE_NAME=${_DATABASE_NAME}"
|
||||
- "DB_USER=${_DB_USER}"
|
||||
- "TARGET_ROOT=${_TARGET_ROOT}"
|
||||
- "TARGET_LANG=${_TARGET_LANG}"
|
||||
- "TABLE_NAME=${_TABLE_NAME}"
|
||||
- "SQL_FILE=${_SQL_FILE}"
|
||||
- "AGENT_FILE_PATTERN=${_AGENT_FILE_PATTERN}"
|
||||
secretEnv: ["TOOLS_YAML_CONTENT", "GOOGLE_API_KEY", "DB_PASSWORD"]
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/8
|
||||
env: "TOOLS_YAML_CONTENT"
|
||||
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
|
||||
env: "GOOGLE_API_KEY"
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
|
||||
env: "DB_PASSWORD"
|
||||
|
||||
timeout: 1200s
|
||||
|
||||
substitutions:
|
||||
_TARGET_LANG: "js"
|
||||
_IMAGE: "node:22"
|
||||
_TARGET_ROOT: "docs/en/documentation/configuration/pre-post-processing/js"
|
||||
_TABLE_NAME: "hotels_js_pre_post_processing"
|
||||
_SQL_FILE: ".ci/sample_tests/setup_hotels.sql"
|
||||
_AGENT_FILE_PATTERN: "agent.js"
|
||||
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- name: "${_IMAGE}"
|
||||
id: "py-pre-post-processing-test"
|
||||
entrypoint: "bash"
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
set -ex
|
||||
chmod +x .ci/sample_tests/run_tests.sh
|
||||
.ci/sample_tests/run_tests.sh
|
||||
env:
|
||||
- "CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}"
|
||||
- "GCP_PROJECT=${_GCP_PROJECT}"
|
||||
- "DATABASE_NAME=${_DATABASE_NAME}"
|
||||
- "DB_USER=${_DB_USER}"
|
||||
- "TARGET_ROOT=${_TARGET_ROOT}"
|
||||
- "TARGET_LANG=${_TARGET_LANG}"
|
||||
- "TABLE_NAME=${_TABLE_NAME}"
|
||||
- "SQL_FILE=${_SQL_FILE}"
|
||||
- "AGENT_FILE_PATTERN=${_AGENT_FILE_PATTERN}"
|
||||
secretEnv: ["TOOLS_YAML_CONTENT", "GOOGLE_API_KEY", "DB_PASSWORD"]
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/8
|
||||
env: "TOOLS_YAML_CONTENT"
|
||||
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
|
||||
env: "GOOGLE_API_KEY"
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
|
||||
env: "DB_PASSWORD"
|
||||
|
||||
timeout: 1200s
|
||||
|
||||
substitutions:
|
||||
_TARGET_LANG: "python"
|
||||
_IMAGE: "gcr.io/google.com/cloudsdktool/cloud-sdk:537.0.0"
|
||||
_TARGET_ROOT: "docs/en/documentation/configuration/pre-post-processing/python"
|
||||
_TABLE_NAME: "hotels_py_pre_post_processing"
|
||||
_SQL_FILE: ".ci/sample_tests/setup_hotels.sql"
|
||||
_AGENT_FILE_PATTERN: "agent.py"
|
||||
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- name: 'golang:1.25.7'
|
||||
id: 'go-quickstart-test'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
# The '-c' flag tells bash to execute the following string as a command.
|
||||
# The 'set -ex' enables debug output and exits on error for easier troubleshooting.
|
||||
- -c
|
||||
- |
|
||||
set -ex
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
chmod +x .ci/sample_tests/run_tests.sh
|
||||
.ci/sample_tests/run_tests.sh
|
||||
env:
|
||||
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
|
||||
- 'GCP_PROJECT=${_GCP_PROJECT}'
|
||||
- 'DATABASE_NAME=${_DATABASE_NAME}'
|
||||
- 'DB_USER=${_DB_USER}'
|
||||
- 'TARGET_ROOT=docs/en/documentation/getting-started/quickstart/go'
|
||||
- 'TARGET_LANG=go'
|
||||
- 'TABLE_NAME=hotels_go'
|
||||
- 'SQL_FILE=.ci/sample_tests/setup_hotels.sql'
|
||||
- 'AGENT_FILE_PATTERN=quickstart.go'
|
||||
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/10
|
||||
env: 'TOOLS_YAML_CONTENT'
|
||||
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
|
||||
env: 'GOOGLE_API_KEY'
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
|
||||
env: 'DB_PASSWORD'
|
||||
|
||||
timeout: 1000s
|
||||
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- name: 'node:22'
|
||||
id: 'js-quickstart-test'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
# The '-c' flag tells bash to execute the following string as a command.
|
||||
# The 'set -ex' enables debug output and exits on error for easier troubleshooting.
|
||||
- -c
|
||||
- |
|
||||
set -ex
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
chmod +x .ci/sample_tests/run_tests.sh
|
||||
.ci/sample_tests/run_tests.sh
|
||||
env:
|
||||
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
|
||||
- 'GCP_PROJECT=${_GCP_PROJECT}'
|
||||
- 'DATABASE_NAME=${_DATABASE_NAME}'
|
||||
- 'DB_USER=${_DB_USER}'
|
||||
- 'TARGET_ROOT=docs/en/documentation/getting-started/quickstart/js'
|
||||
- 'TARGET_LANG=js'
|
||||
- 'TABLE_NAME=hotels_js'
|
||||
- 'SQL_FILE=.ci/sample_tests/setup_hotels.sql'
|
||||
- 'AGENT_FILE_PATTERN=quickstart.js'
|
||||
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/9
|
||||
env: 'TOOLS_YAML_CONTENT'
|
||||
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
|
||||
env: 'GOOGLE_API_KEY'
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
|
||||
env: 'DB_PASSWORD'
|
||||
|
||||
timeout: 1000s
|
||||
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:537.0.0'
|
||||
id: 'python-quickstart-test'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
# The '-c' flag tells bash to execute the following string as a command.
|
||||
# The 'set -ex' enables debug output and exits on error for easier troubleshooting.
|
||||
- -c
|
||||
- |
|
||||
set -ex
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
chmod +x .ci/sample_tests/run_tests.sh
|
||||
.ci/sample_tests/run_tests.sh
|
||||
env:
|
||||
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
|
||||
- 'GCP_PROJECT=${_GCP_PROJECT}'
|
||||
- 'DATABASE_NAME=${_DATABASE_NAME}'
|
||||
- 'DB_USER=${_DB_USER}'
|
||||
- 'TARGET_ROOT=docs/en/documentation/getting-started/quickstart/python'
|
||||
- 'TARGET_LANG=python'
|
||||
- 'TABLE_NAME=hotels_python'
|
||||
- 'SQL_FILE=.ci/sample_tests/setup_hotels.sql'
|
||||
- 'AGENT_FILE_PATTERN=quickstart.py'
|
||||
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/8
|
||||
env: 'TOOLS_YAML_CONTENT'
|
||||
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
|
||||
env: 'GOOGLE_API_KEY'
|
||||
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
|
||||
env: 'DB_PASSWORD'
|
||||
|
||||
timeout: 1000s
|
||||
|
||||
options:
|
||||
logging: CLOUD_LOGGING_ONLY
|
||||
@@ -0,0 +1,202 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
# --- Configuration (from Environment Variables) ---
|
||||
# TARGET_ROOT: The directory to search for tests (e.g., docs/en/getting-started/quickstart/js)
|
||||
# TARGET_LANG: python, js, go
|
||||
# TABLE_NAME: Database table name to use
|
||||
# SQL_FILE: Path to the SQL setup file
|
||||
# AGENT_FILE_PATTERN: Filename to look for (e.g., quickstart.js or agent.py)
|
||||
|
||||
VERSION=$(cat ./cmd/version.txt)
|
||||
|
||||
# Process IDs & Logs
|
||||
PROXY_PID=""
|
||||
TOOLBOX_PID=""
|
||||
PROXY_LOG="cloud_sql_proxy.log"
|
||||
TOOLBOX_LOG="toolbox_server.log"
|
||||
|
||||
install_system_packages() {
|
||||
echo "Installing system packages..."
|
||||
apt-get update && apt-get install -y \
|
||||
postgresql-client \
|
||||
wget \
|
||||
gettext-base \
|
||||
netcat-openbsd
|
||||
|
||||
if [[ "$TARGET_LANG" == "python" ]]; then
|
||||
apt-get install -y python3-venv
|
||||
fi
|
||||
}
|
||||
|
||||
start_cloud_sql_proxy() {
|
||||
echo "Starting Cloud SQL Proxy..."
|
||||
wget -q "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
|
||||
chmod +x /usr/local/bin/cloud-sql-proxy
|
||||
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" > "$PROXY_LOG" 2>&1 &
|
||||
PROXY_PID=$!
|
||||
|
||||
# Health Check
|
||||
for i in {1..30}; do
|
||||
if nc -z 127.0.0.1 5432; then
|
||||
echo "Cloud SQL Proxy is up and running."
|
||||
return
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "ERROR: Cloud SQL Proxy failed to start. Logs:"
|
||||
cat "$PROXY_LOG"
|
||||
exit 1
|
||||
}
|
||||
|
||||
setup_toolbox() {
|
||||
echo "Setting up Toolbox server..."
|
||||
TOOLBOX_YAML="/tools.yaml"
|
||||
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
|
||||
wget -q "https://storage.googleapis.com/mcp-toolbox-for-databases/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
|
||||
chmod +x "/toolbox"
|
||||
/toolbox --tools-file "$TOOLBOX_YAML" > "$TOOLBOX_LOG" 2>&1 &
|
||||
TOOLBOX_PID=$!
|
||||
|
||||
# Health Check
|
||||
for i in {1..15}; do
|
||||
if nc -z 127.0.0.1 5000; then
|
||||
echo "Toolbox server is up and running."
|
||||
return
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "ERROR: Toolbox server failed to start. Logs:"
|
||||
cat "$TOOLBOX_LOG"
|
||||
exit 1
|
||||
}
|
||||
|
||||
setup_db_table() {
|
||||
echo "Setting up database table $TABLE_NAME using $SQL_FILE..."
|
||||
export TABLE_NAME
|
||||
envsubst < "$SQL_FILE" | psql -h 127.0.0.1 -p 5432 -U "$DB_USER" -d "$DATABASE_NAME"
|
||||
}
|
||||
|
||||
run_python_test() {
|
||||
local dir=$1
|
||||
local name=$(basename "$dir")
|
||||
echo "--- Running Python Test: $name ---"
|
||||
(
|
||||
cd "$dir"
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -q -r requirements.txt pytest
|
||||
|
||||
cd ..
|
||||
local test_file=$(find . -maxdepth 1 -name "*test.py" | head -n 1)
|
||||
if [ -n "$test_file" ]; then
|
||||
echo "Found native test: $test_file. Running pytest..."
|
||||
export ORCH_NAME="$name"
|
||||
export PYTHONPATH="../"
|
||||
pytest "$test_file"
|
||||
else
|
||||
echo "No native test found. running agent directly..."
|
||||
export PYTHONPATH="../"
|
||||
python3 "${name}/${AGENT_FILE_PATTERN}"
|
||||
fi
|
||||
rm -rf "${name}/.venv"
|
||||
)
|
||||
}
|
||||
|
||||
run_js_test() {
|
||||
local dir=$1
|
||||
local name=$(basename "$dir")
|
||||
echo "--- Running JS Test: $name ---"
|
||||
(
|
||||
cd "$dir"
|
||||
if [ -f "package-lock.json" ]; then npm ci -q; else npm install -q; fi
|
||||
|
||||
cd ..
|
||||
# Looking for a JS test file in the parent directory
|
||||
local test_file=$(find . -maxdepth 1 -name "*test.js" | head -n 1)
|
||||
if [ -n "$test_file" ]; then
|
||||
echo "Found native test: $test_file. Running node --test..."
|
||||
export ORCH_NAME="$name"
|
||||
node --test "$test_file"
|
||||
else
|
||||
echo "No native test found. running agent directly..."
|
||||
node "${name}/${AGENT_FILE_PATTERN}"
|
||||
fi
|
||||
rm -rf "${name}/node_modules"
|
||||
)
|
||||
}
|
||||
|
||||
run_go_test() {
|
||||
local dir=$1
|
||||
local name=$(basename "$dir")
|
||||
|
||||
if [ "$name" == "openAI" ]; then
|
||||
echo -e "\nSkipping framework '${name}': Temporarily excluded."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "--- Running Go Test: $name ---"
|
||||
(
|
||||
cd "$dir"
|
||||
if [ -f "go.mod" ]; then
|
||||
go mod tidy
|
||||
fi
|
||||
|
||||
cd ..
|
||||
local test_file=$(find . -maxdepth 1 -name "*test.go" | head -n 1)
|
||||
if [ -n "$test_file" ]; then
|
||||
echo "Found native test: $test_file. Running go test..."
|
||||
export ORCH_NAME="$name"
|
||||
go test -v ./...
|
||||
else
|
||||
echo "No native test found. running agent directly..."
|
||||
cd "$name"
|
||||
go run "."
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
echo "Cleaning up background processes..."
|
||||
[ -n "$TOOLBOX_PID" ] && kill "$TOOLBOX_PID" || true
|
||||
[ -n "$PROXY_PID" ] && kill "$PROXY_PID" || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# --- Execution ---
|
||||
install_system_packages
|
||||
start_cloud_sql_proxy
|
||||
|
||||
export PGHOST=127.0.0.1
|
||||
export PGPORT=5432
|
||||
export PGPASSWORD="$DB_PASSWORD"
|
||||
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
|
||||
|
||||
setup_toolbox
|
||||
setup_db_table
|
||||
|
||||
echo "Scanning $TARGET_ROOT for tests with pattern $AGENT_FILE_PATTERN..."
|
||||
|
||||
find "$TARGET_ROOT" -name "$AGENT_FILE_PATTERN" | while read -r agent_file; do
|
||||
sample_dir=$(dirname "$agent_file")
|
||||
if [[ "$TARGET_LANG" == "python" ]]; then
|
||||
run_python_test "$sample_dir"
|
||||
elif [[ "$TARGET_LANG" == "js" ]]; then
|
||||
run_js_test "$sample_dir"
|
||||
elif [[ "$TARGET_LANG" == "go" ]]; then
|
||||
run_go_test "$sample_dir"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,28 @@
|
||||
-- Copyright 2025 Google LLC
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License");
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
TRUNCATE TABLE $TABLE_NAME;
|
||||
|
||||
INSERT INTO $TABLE_NAME (id, name, location, price_tier, checkin_date, checkout_date, booked)
|
||||
VALUES
|
||||
(1, 'Hilton Basel', 'Basel', 'Luxury', '2024-04-22', '2024-04-20', B'0'),
|
||||
(2, 'Marriott Zurich', 'Zurich', 'Upscale', '2024-04-14', '2024-04-21', B'0'),
|
||||
(3, 'Hyatt Regency Basel', 'Basel', 'Upper Upscale', '2024-04-02', '2024-04-20', B'0'),
|
||||
(4, 'Radisson Blu Lucerne', 'Lucerne', 'Midscale', '2024-04-24', '2024-04-05', B'0'),
|
||||
(5, 'Best Western Bern', 'Bern', 'Upper Midscale', '2024-04-23', '2024-04-01', B'0'),
|
||||
(6, 'InterContinental Geneva', 'Geneva', 'Luxury', '2024-04-23', '2024-04-28', B'0'),
|
||||
(7, 'Sheraton Zurich', 'Zurich', 'Upper Upscale', '2024-04-27', '2024-04-02', B'0'),
|
||||
(8, 'Holiday Inn Basel', 'Basel', 'Upper Midscale', '2024-04-24', '2024-04-09', B'0'),
|
||||
(9, 'Courtyard Zurich', 'Zurich', 'Upscale', '2024-04-03', '2024-04-13', B'0'),
|
||||
(10, 'Comfort Inn Bern', 'Bern', 'Midscale', '2024-04-04', '2024-04-16', B'0');
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
# .ci/test_prompts_with_coverage.sh
|
||||
#
|
||||
# This script runs a specific prompt integration test, calculates its
|
||||
# code coverage, and checks if it meets a minimum threshold.
|
||||
#
|
||||
# It is called with one argument: the type of the prompt.
|
||||
# Example usage: .ci/test_prompts_with_coverage.sh "custom"
|
||||
|
||||
# Exit immediately if a command fails.
|
||||
set -e
|
||||
|
||||
# --- 1. Define Variables ---
|
||||
|
||||
# The first argument is the prompt type (e.g., "custom").
|
||||
PROMPT_TYPE=$1
|
||||
COVERAGE_THRESHOLD=80 # Minimum coverage percentage required.
|
||||
|
||||
if [ -z "$PROMPT_TYPE" ]; then
|
||||
echo "Error: No prompt type provided. Please call this script with an argument."
|
||||
echo "Usage: .ci/test_prompts_with_coverage.sh <prompt_type>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Construct names based on the prompt type.
|
||||
TEST_BINARY="./prompt.${PROMPT_TYPE}.test"
|
||||
TEST_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${PROMPT_TYPE:0:1})${PROMPT_TYPE:1} Prompts"
|
||||
COVERAGE_FILE="coverage.prompts-${PROMPT_TYPE}.out"
|
||||
|
||||
|
||||
# --- 2. Run Integration Tests ---
|
||||
|
||||
echo "--- Running integration tests for ${TEST_NAME} ---"
|
||||
|
||||
# Safety check for the binary's existence.
|
||||
if [ ! -f "$TEST_BINARY" ]; then
|
||||
echo "Error: Test binary not found at ${TEST_BINARY}. Aborting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Execute the test binary and generate the coverage file.
|
||||
# If the tests fail, the 'set -e' command will cause the script to exit here.
|
||||
if ! ./"${TEST_BINARY}" -test.v -test.coverprofile="${COVERAGE_FILE}"; then
|
||||
echo "Error: Tests for ${TEST_NAME} failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "--- Tests for ${TEST_NAME} passed successfully ---"
|
||||
|
||||
|
||||
# --- 3. Calculate and Check Coverage ---
|
||||
|
||||
echo "Calculating coverage for ${TEST_NAME}..."
|
||||
|
||||
# Calculate the total coverage percentage from the generated file.
|
||||
# The '2>/dev/null' suppresses warnings if the coverage file is empty.
|
||||
total_coverage=$(go tool cover -func="${COVERAGE_FILE}" 2>/dev/null | grep "total:" | awk '{print $3}')
|
||||
|
||||
if [ -z "$total_coverage" ]; then
|
||||
echo "Warning: Could not calculate coverage for ${TEST_NAME}. The coverage report might be empty."
|
||||
total_coverage="0%"
|
||||
fi
|
||||
|
||||
echo "${TEST_NAME} total coverage: $total_coverage"
|
||||
|
||||
# Remove the '%' sign for numerical comparison.
|
||||
coverage_numeric=$(echo "$total_coverage" | sed 's/%//')
|
||||
|
||||
# Check if the coverage is below the defined threshold.
|
||||
if awk -v coverage="$coverage_numeric" -v threshold="$COVERAGE_THRESHOLD" 'BEGIN {exit !(coverage < threshold)}'; then
|
||||
echo "Coverage failure: ${TEST_NAME} total coverage (${total_coverage}) is below the ${COVERAGE_THRESHOLD}% threshold."
|
||||
exit 1
|
||||
else
|
||||
echo "Coverage for ${TEST_NAME} is sufficient."
|
||||
fi
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Arguments:
|
||||
# $1: Display name for logs (e.g., "Cloud SQL Postgres")
|
||||
# $2: Integration test's package name (e.g., cloudsqlpg)
|
||||
# $3, $4, ...: Tool package names for grep (e.g., postgressql), if the
|
||||
# integration test specifically check a separate package inside a folder, please
|
||||
# specify the full path instead (e.g., postgressql/postgresexecutesql)
|
||||
|
||||
DISPLAY_NAME="$1"
|
||||
SOURCE_PACKAGE_NAME="$2"
|
||||
|
||||
# Construct the test binary name
|
||||
TEST_BINARY="${SOURCE_PACKAGE_NAME}.test"
|
||||
|
||||
# Construct the full source path
|
||||
SOURCE_PATH="sources/${SOURCE_PACKAGE_NAME}/"
|
||||
|
||||
# Shift arguments so that $3 and onwards become the list of tool package names
|
||||
shift 2
|
||||
TOOL_PACKAGE_NAMES=("$@")
|
||||
|
||||
COVERAGE_FILE="${TEST_BINARY%.test}_coverage.out"
|
||||
FILTERED_COVERAGE_FILE="${TEST_BINARY%.test}_filtered_coverage.out"
|
||||
|
||||
export path="github.com/googleapis/mcp-toolbox/internal/"
|
||||
|
||||
GREP_PATTERN="^mode:|${path}${SOURCE_PATH}"
|
||||
# Add each tool package path to the grep pattern
|
||||
for tool_name in "${TOOL_PACKAGE_NAMES[@]}"; do
|
||||
if [ -n "$tool_name" ]; then
|
||||
full_tool_path="tools/${tool_name}/"
|
||||
GREP_PATTERN="${GREP_PATTERN}|${path}${full_tool_path}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Run integration test
|
||||
if ! ./"${TEST_BINARY}" -test.v ${EXTRA_TEST_ARGS:-} -test.coverprofile="${COVERAGE_FILE}"; then
|
||||
echo "Error: Tests for ${DISPLAY_NAME} failed. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Filter source/tool packages
|
||||
if ! grep -E "${GREP_PATTERN}" "${COVERAGE_FILE}" > "${FILTERED_COVERAGE_FILE}"; then
|
||||
echo "Warning: Could not filter coverage for ${DISPLAY_NAME}. Filtered file might be empty or invalid."
|
||||
fi
|
||||
|
||||
# Calculate coverage
|
||||
echo "Calculating coverage for ${DISPLAY_NAME}..."
|
||||
total_coverage=$(go tool cover -func="${FILTERED_COVERAGE_FILE}" 2>/dev/null | grep "total:" | awk '{print $3}')
|
||||
|
||||
|
||||
echo "${DISPLAY_NAME} total coverage: $total_coverage"
|
||||
coverage_numeric=$(echo "$total_coverage" | sed 's/%//')
|
||||
|
||||
# Check coverage threshold
|
||||
if awk -v coverage="$coverage_numeric" 'BEGIN {exit !(coverage < 50)}'; then
|
||||
echo "Coverage failure: ${DISPLAY_NAME} total coverage($total_coverage) is below 50%."
|
||||
exit 1
|
||||
else
|
||||
echo "Coverage for ${DISPLAY_NAME} is sufficient."
|
||||
fi
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# Uploads the OSS Exit Gate publishing manifest, which triggers external
|
||||
# publication of whichever package set was just pushed to the internal
|
||||
# Artifact Registry.
|
||||
#
|
||||
# Usage:
|
||||
# bash .ci/trigger_exit_gate.sh <registry>
|
||||
# registry: "npm" or "pypi" — determines the GCS subpath Exit Gate watches
|
||||
#
|
||||
# Required env vars (supplied by Cloud Build):
|
||||
# BUILD_ID Cloud Build's auto-provided unique build ID
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
REGISTRY="${1:?registry argument required: npm or pypi}"
|
||||
case "${REGISTRY}" in
|
||||
npm|pypi) ;;
|
||||
*)
|
||||
echo "ERROR: unsupported registry '${REGISTRY}' (expected: npm or pypi)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# OSS Exit Gate constants — owned by Exit Gate, not build configuration.
|
||||
readonly EXIT_GATE_PROJECT="mcp-toolbox"
|
||||
readonly MANIFEST_BUCKET="oss-exit-gate-prod-projects-bucket"
|
||||
|
||||
VERSION="v$(cat ./cmd/version.txt)"
|
||||
MANIFEST="${VERSION}-${BUILD_ID}.json"
|
||||
MANIFEST_PATH="gs://${MANIFEST_BUCKET}/${EXIT_GATE_PROJECT}/${REGISTRY}/manifests/${MANIFEST}"
|
||||
|
||||
echo '{"publish_all": true}' > "${MANIFEST}"
|
||||
|
||||
gcloud storage cp "${MANIFEST}" "${MANIFEST_PATH}"
|
||||
|
||||
echo "Manifest uploaded: ${MANIFEST_PATH}"
|
||||
echo "Exit Gate will email a confirmation when the external publish completes."
|
||||
@@ -0,0 +1,473 @@
|
||||
# Copyright 2024 Google LLC
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
steps:
|
||||
- id: "build-docker"
|
||||
name: "gcr.io/cloud-builders/docker"
|
||||
waitFor: ["-"]
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
export VERSION=$(cat ./cmd/version.txt)
|
||||
docker buildx create --name container-builder --driver docker-container --bootstrap --use
|
||||
|
||||
export TAGS="-t ${_DOCKER_URI}:$VERSION"
|
||||
if [[ "$_PUSH_LATEST" == "true" ]]; then
|
||||
export TAGS="$TAGS -t ${_DOCKER_URI}:latest"
|
||||
fi
|
||||
|
||||
# Build and push
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--build-arg BUILD_TYPE=container.release \
|
||||
--build-arg COMMIT_SHA=$(git rev-parse --short HEAD) \
|
||||
$TAGS \
|
||||
--push .
|
||||
|
||||
# Pull the image to ensure it's available for provenance generation.
|
||||
docker pull ${_DOCKER_URI}:${VERSION}
|
||||
docker tag ${_DOCKER_URI}:${VERSION} ${_DOCKER_URI}:latest
|
||||
|
||||
- id: "install-dependencies"
|
||||
name: golang:1
|
||||
waitFor: ["-"]
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
script: |
|
||||
go get -d ./...
|
||||
|
||||
- id: "install-zig"
|
||||
name: golang:1
|
||||
waitFor: ["-"]
|
||||
volumes:
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
apt-get update && apt-get install -y xz-utils
|
||||
curl -fL "https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz" -o zig.tar.xz
|
||||
tar -xf zig.tar.xz -C /zig-tools --strip-components=1
|
||||
|
||||
- id: "install-macos-sdk"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor: ["-"]
|
||||
volumes:
|
||||
- name: "macos-sdk"
|
||||
path: "/macos-sdk"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
apt-get update && apt-get install -y xz-utils
|
||||
echo "Downloading macOS 14.5 SDK..."
|
||||
gcloud storage cp gs://${_ASSETS_BUCKET}/MacOSX14.5.tar.xz sdk.tar.xz
|
||||
|
||||
mkdir -p /macos-sdk/MacOSX14.5.sdk
|
||||
echo "Unpacking macOS 14.5 SDK..."
|
||||
tar -xf sdk.tar.xz -C /macos-sdk/MacOSX14.5.sdk --strip-components=1
|
||||
|
||||
- id: "build-linux-amd64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=linux"
|
||||
- "GOARCH=amd64"
|
||||
- "CC=/zig-tools/zig cc -target x86_64-linux-gnu"
|
||||
- "CXX=/zig-tools/zig c++ -target x86_64-linux-gnu"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.buildType=binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.linux.amd64
|
||||
|
||||
- id: "store-linux-amd64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-linux-amd64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.linux.amd64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/linux/amd64/toolbox
|
||||
|
||||
- id: "build-linux-amd64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=linux"
|
||||
- "GOARCH=amd64"
|
||||
- "CC=/zig-tools/zig cc -target x86_64-linux-gnu"
|
||||
- "CXX=/zig-tools/zig c++ -target x86_64-linux-gnu"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.buildType=geminicli.binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.linux.amd64
|
||||
|
||||
- id: "store-linux-amd64-geminicli"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-linux-amd64-geminicli"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.geminicli.linux.amd64 gs://$_BUCKET_NAME/geminicli/$VERSION/linux/amd64/toolbox
|
||||
|
||||
- id: "build-darwin-arm64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=darwin"
|
||||
- "GOARCH=arm64"
|
||||
- "SDK_PATH=/macos-sdk/MacOSX14.5.sdk"
|
||||
- "MACOS_MIN_VER=10.14"
|
||||
- "CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib"
|
||||
- "COMMON_FLAGS=-mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
- name: "macos-sdk"
|
||||
path: "/macos-sdk"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.buildType=binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.darwin.arm64
|
||||
|
||||
- id: "store-darwin-arm64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-darwin-arm64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.darwin.arm64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/darwin/arm64/toolbox
|
||||
|
||||
- id: "build-darwin-arm64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=darwin"
|
||||
- "GOARCH=arm64"
|
||||
- "SDK_PATH=/macos-sdk/MacOSX14.5.sdk"
|
||||
- "MACOS_MIN_VER=10.14"
|
||||
- "CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib"
|
||||
- "COMMON_FLAGS=-mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target aarch64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
- name: "macos-sdk"
|
||||
path: "/macos-sdk"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.buildType=geminicli.binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.darwin.arm64
|
||||
|
||||
- id: "store-darwin-arm64-geminicli"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-darwin-arm64-geminicli"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.geminicli.darwin.arm64 gs://$_BUCKET_NAME/geminicli/$VERSION/darwin/arm64/toolbox
|
||||
|
||||
- id: "build-darwin-amd64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=darwin"
|
||||
- "GOARCH=amd64"
|
||||
- "SDK_PATH=/macos-sdk/MacOSX14.5.sdk"
|
||||
- "MACOS_MIN_VER=10.14"
|
||||
- "CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib"
|
||||
- "COMMON_FLAGS=-mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
- name: "macos-sdk"
|
||||
path: "/macos-sdk"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.buildType=binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.darwin.amd64
|
||||
|
||||
- id: "store-darwin-amd64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-darwin-amd64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.darwin.amd64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/darwin/amd64/toolbox
|
||||
|
||||
- id: "build-darwin-amd64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
- "install-macos-sdk"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=darwin"
|
||||
- "GOARCH=amd64"
|
||||
- "SDK_PATH=/macos-sdk/MacOSX14.5.sdk"
|
||||
- "MACOS_MIN_VER=10.14"
|
||||
- "CGO_LDFLAGS=-mmacosx-version-min=10.14 --sysroot /macos-sdk/MacOSX14.5.sdk -F/macos-sdk/MacOSX14.5.sdk/System/Library/Frameworks -L/usr/lib"
|
||||
- "COMMON_FLAGS=-mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CC=/zig-tools/zig cc -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
- "CXX=/zig-tools/zig c++ -mmacosx-version-min=10.14 -target x86_64-macos.11.0.0-none -isysroot /macos-sdk/MacOSX14.5.sdk -iwithsysroot /usr/include -iframeworkwithsysroot /System/Library/Frameworks"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
- name: "macos-sdk"
|
||||
path: "/macos-sdk"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -trimpath -buildmode=pie -ldflags "-s -w -X github.com/googleapis/mcp-toolbox/cmd.buildType=geminicli.binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.darwin.amd64
|
||||
|
||||
- id: "store-darwin-amd64-geminicli"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-darwin-amd64-geminicli"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.geminicli.darwin.amd64 gs://$_BUCKET_NAME/geminicli/$VERSION/darwin/amd64/toolbox
|
||||
|
||||
- id: "build-windows-amd64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=windows"
|
||||
- "GOARCH=amd64"
|
||||
- "CC=/zig-tools/zig cc -target x86_64-windows-gnu"
|
||||
- "CXX=/zig-tools/zig c++ -target x86_64-windows-gnu"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.buildType=binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.windows.amd64
|
||||
|
||||
- id: "store-windows-amd64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-windows-amd64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.windows.amd64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/windows/amd64/toolbox.exe
|
||||
|
||||
- id: "build-windows-amd64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=windows"
|
||||
- "GOARCH=amd64"
|
||||
- "CC=/zig-tools/zig cc -target x86_64-windows-gnu"
|
||||
- "CXX=/zig-tools/zig c++ -target x86_64-windows-gnu"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.buildType=geminicli.binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.windows.amd64
|
||||
|
||||
- id: "store-windows-amd64-geminicli"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-windows-amd64-geminicli"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.geminicli.windows.amd64 gs://$_BUCKET_NAME/geminicli/$VERSION/windows/amd64/toolbox.exe
|
||||
|
||||
- id: "build-windows-arm64"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=windows"
|
||||
- "GOARCH=arm64"
|
||||
- "CC=/zig-tools/zig cc -target aarch64-windows-gnu"
|
||||
- "CXX=/zig-tools/zig c++ -target aarch64-windows-gnu"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.buildType=binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.windows.arm64
|
||||
|
||||
- id: "store-windows-arm64"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-windows-arm64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.windows.arm64 gs://$_UNSIGNED_BUCKET_NAME/$VERSION/windows/arm64/toolbox.exe
|
||||
|
||||
- id: "build-windows-arm64-geminicli"
|
||||
name: golang:1
|
||||
waitFor:
|
||||
- "install-dependencies"
|
||||
- "install-zig"
|
||||
env:
|
||||
- "GOPATH=/gopath"
|
||||
- "CGO_ENABLED=1"
|
||||
- "GOOS=windows"
|
||||
- "GOARCH=arm64"
|
||||
- "CC=/zig-tools/zig cc -target aarch64-windows-gnu"
|
||||
- "CXX=/zig-tools/zig c++ -target aarch64-windows-gnu"
|
||||
volumes:
|
||||
- name: "go"
|
||||
path: "/gopath"
|
||||
- name: "zig"
|
||||
path: "/zig-tools"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
go build -ldflags "-X github.com/googleapis/mcp-toolbox/cmd.buildType=geminicli.binary -X github.com/googleapis/mcp-toolbox/cmd.commitSha=$(git rev-parse --short HEAD)" -o toolbox.geminicli.windows.arm64
|
||||
|
||||
- id: "store-windows-arm64-geminicli"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "build-windows-arm64-geminicli"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
export VERSION=v$(cat ./cmd/version.txt)
|
||||
gcloud storage cp toolbox.geminicli.windows.arm64 gs://$_BUCKET_NAME/geminicli/$VERSION/windows/arm64/toolbox.exe
|
||||
|
||||
- id: "publish-npm-to-ar"
|
||||
name: node:20
|
||||
waitFor:
|
||||
- "build-linux-amd64"
|
||||
- "build-darwin-arm64"
|
||||
- "build-darwin-amd64"
|
||||
- "build-windows-amd64"
|
||||
- "build-windows-arm64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/publish_npm_to_ar.sh
|
||||
|
||||
- id: "trigger-exit-gate-npm"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "publish-npm-to-ar"
|
||||
env:
|
||||
- "BUILD_ID=$BUILD_ID"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/trigger_exit_gate.sh npm
|
||||
|
||||
- id: "publish-pypi-to-ar"
|
||||
name: python:3.12
|
||||
waitFor:
|
||||
- "build-linux-amd64"
|
||||
- "build-darwin-arm64"
|
||||
- "build-darwin-amd64"
|
||||
- "build-windows-amd64"
|
||||
- "build-windows-arm64"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/publish_pypi_to_ar.sh
|
||||
|
||||
- id: "trigger-exit-gate-pypi"
|
||||
name: "gcr.io/cloud-builders/gcloud:latest"
|
||||
waitFor:
|
||||
- "publish-pypi-to-ar"
|
||||
env:
|
||||
- "BUILD_ID=$BUILD_ID"
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
bash .ci/trigger_exit_gate.sh pypi
|
||||
|
||||
images:
|
||||
- "${_DOCKER_URI}:latest"
|
||||
|
||||
options:
|
||||
requestedVerifyOption: VERIFIED # This ensures provenance is generated
|
||||
automapSubstitutions: true
|
||||
dynamicSubstitutions: true
|
||||
logging: CLOUD_LOGGING_ONLY # Necessary for custom service account
|
||||
pool:
|
||||
name: projects/$PROJECT_ID/locations/us-central1/workerPools/run-release # to increase resource for running releases
|
||||
|
||||
substitutions:
|
||||
_REGION: us-central1
|
||||
_AR_HOSTNAME: ${_REGION}-docker.pkg.dev
|
||||
_AR_REPO_NAME: toolbox
|
||||
_BUCKET_NAME: mcp-toolbox-for-databases
|
||||
_UNSIGNED_BUCKET_NAME: mcp-toolbox-for-databases-unsigned
|
||||
_ASSETS_BUCKET: toolbox-build-assets
|
||||
_DOCKER_URI: ${_AR_HOSTNAME}/${PROJECT_ID}/${_AR_REPO_NAME}/toolbox
|
||||
_PUSH_LATEST: "false" # Substituted in trigger
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ignore_patterns:
|
||||
- "package-lock.json"
|
||||
- "go.sum"
|
||||
- "requirements.txt"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../GEMINI.md
|
||||
@@ -0,0 +1,17 @@
|
||||
# This file controls who is tagged for review for any given pull request.
|
||||
#
|
||||
# For syntax help see:
|
||||
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax
|
||||
|
||||
* @googleapis/senseai-eco-team
|
||||
# Code & Tests
|
||||
**/alloydb*/ @googleapis/toolbox-alloydb-team @googleapis/senseai-eco-team
|
||||
**/bigquery/ @googleapis/toolbox-bigquery-team @googleapis/senseai-eco-team
|
||||
**/bigtable/ @googleapis/toolbox-bigtable-team @googleapis/senseai-eco-team
|
||||
**/cloudsqlmssql/ @googleapis/toolbox-cloud-sql-mssql-team @googleapis/senseai-eco-team
|
||||
**/cloudsqlmysql/ @googleapis/toolbox-cloud-sql-mysql-team @googleapis/senseai-eco-team
|
||||
**/cloudsqlpg/ @googleapis/toolbox-cloud-sql-postgres-team @googleapis/senseai-eco-team
|
||||
**/dataplex/ @googleapis/toolbox-dataplex-team @googleapis/senseai-eco-team
|
||||
**/firestore/ @googleapis/toolbox-firestore-team @googleapis/senseai-eco-team
|
||||
**/looker/ @googleapis/toolbox-looker-team @googleapis/senseai-eco-team
|
||||
**/spanner/ @googleapis/toolbox-spanner-team @googleapis/senseai-eco-team
|
||||
@@ -0,0 +1,119 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: 🐞 Bug Report
|
||||
description: File a report for unexpected or undesired behavior.
|
||||
title: "<brief summary of what bug or error was observed>"
|
||||
labels: ["type: bug"]
|
||||
type: "bug"
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for helping us improve! 🙏 Please answer these questions and provide as much information as possible about your problem.
|
||||
|
||||
- id: preamble
|
||||
type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
description: |
|
||||
Please run through the following list and make sure you've tried the usual "quick fixes":
|
||||
- Search the [current open issues](https://github.com/googleapis/mcp-toolbox/issues)
|
||||
- Update to the [latest version of Toolbox](https://github.com/googleapis/mcp-toolbox/releases)
|
||||
options:
|
||||
- label: "I've searched the current open issues"
|
||||
required: true
|
||||
- label: "I've updated to the latest version of Toolbox"
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Toolbox version
|
||||
description: |
|
||||
What version of Toolbox are you using (`toolbox --version`)? e.g.
|
||||
- toolbox version 0.3.0
|
||||
- us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:0.3.0
|
||||
placeholder: ex. toolbox version 0.3.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment
|
||||
description: "Let us know some details about the environment in which you are seeing the bug!"
|
||||
value: |
|
||||
1. OS type and version: (output of `uname -a`)
|
||||
2. How are you running Toolbox:
|
||||
- As a downloaded binary (e.g. from `curl -O https://storage.googleapis.com/mcp-toolbox-for-databases/v$VERSION/linux/amd64/toolbox`)
|
||||
- As a container (e.g. from `us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION`)
|
||||
- Compiled from source (include the command used to build)
|
||||
|
||||
- type: textarea
|
||||
id: client
|
||||
attributes:
|
||||
label: Client
|
||||
description: "How are you connecting to Toolbox?"
|
||||
value: |
|
||||
1. Client: <name and link to the client are you using>
|
||||
2. Version: <what exact version of the client are you using>
|
||||
3. Example: If possible, please include your code of configuration:
|
||||
|
||||
```python
|
||||
# Code goes here!
|
||||
```
|
||||
|
||||
- id: expected-behavior
|
||||
type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: |
|
||||
Please enter a detailed description of the behavior you expected, and any information about what behavior you
|
||||
noticed and why it is defective or unintentional.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- id: current-behavior
|
||||
type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: "Please enter a detailed description of the behavior you encountered instead."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Steps to reproduce?
|
||||
description: |
|
||||
How can we reproduce this bug? Please walk us through it step by step,
|
||||
with as much relevant detail as possible. A 'minimal' reproduction is
|
||||
preferred, which means removing as much of the examples as possible so
|
||||
only the minimum required to run and reproduce the bug is left.
|
||||
value: |
|
||||
1. ?
|
||||
2. ?
|
||||
3. ?
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional-details
|
||||
attributes:
|
||||
label: Additional Details
|
||||
description: |
|
||||
Any other information you want us to know? Things such as tools config,
|
||||
server logs, etc. can be included here.
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Google Cloud Support
|
||||
url: https://cloud.google.com/support/
|
||||
about: If you have a support contract with Google, please both open an issue here and open Google Cloud Support portal with a link to the issue.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: ✨ Feature Request
|
||||
description: Suggest an idea for new or improved behavior.
|
||||
title: "<brief summary of the proposed feature>"
|
||||
labels: ["type: feature request"]
|
||||
type: feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for helping us improve! 🙏 Please answer these questions and provide as much information as possible about your feature request.
|
||||
|
||||
- id: preamble
|
||||
type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
description: |
|
||||
Please run through the following list and make sure you've tried the usual "quick fixes":
|
||||
options:
|
||||
- label: "Search the [current open issues](https://github.com/googleapis/mcp-toolbox/issues)"
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: use-case
|
||||
attributes:
|
||||
label: What are you trying to do that currently feels hard or impossible?
|
||||
description: "A clear and concise description of what the end goal for the feature should be -- avoid generalizing and try to provide a specific use-case."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: suggested-solution
|
||||
attributes:
|
||||
label: Suggested Solution(s)
|
||||
description: "If you have a suggestion for how this use-case can be solved, please feel free to include it."
|
||||
|
||||
- type: textarea
|
||||
id: alternatives-considered
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: "Are there any workaround or third party tools to replicate this behavior? Why would adding this feature be preferred over them?"
|
||||
|
||||
- type: textarea
|
||||
id: additional-details
|
||||
attributes:
|
||||
label: Additional Details
|
||||
description: "Any additional information we should know? Please reference it here (issues, PRs, descriptions, or screenshots)"
|
||||
@@ -0,0 +1,55 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: 💬 Question
|
||||
description: Questions on how something works or the best way to do something?
|
||||
title: "<brief summary of the question>"
|
||||
labels: ["type: question"]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for helping us improve! 🙏 Please provide as much information as possible about your question.
|
||||
|
||||
- id: preamble
|
||||
type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
description: |
|
||||
Please run through the following list and make sure you've tried the usual "quick fixes":
|
||||
options:
|
||||
- label: "Search the [current open issues](https://github.com/googleapis/mcp-toolbox/issues)"
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Question
|
||||
description: "What's your question? Please provide as much relevant information as possible to reduce turnaround time. Include information like what environment, language, or framework you are using."
|
||||
placeholder: "Example: How do I connect using private IP with the AlloyDB source?"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: code
|
||||
attributes:
|
||||
label: Code
|
||||
description: "Please paste any useful application code that might be relevant to your question. (if your code is in a public repo, feel free to paste a link!)"
|
||||
|
||||
- type: textarea
|
||||
id: additional-details
|
||||
attributes:
|
||||
label: Additional Details
|
||||
description: "Any other information you want us to know that might be helpful in answering your question? (link issues, PRs, descriptions, or screenshots)."
|
||||
@@ -0,0 +1,22 @@
|
||||
## Description
|
||||
|
||||
> Should include a concise description of the changes (bug or feature), it's
|
||||
> impact, along with a summary of the solution
|
||||
|
||||
## PR Checklist
|
||||
|
||||
> Thank you for opening a Pull Request! Before submitting your PR, there are a
|
||||
> few things you can do to make sure it goes smoothly:
|
||||
|
||||
- [ ] Make sure you reviewed
|
||||
[CONTRIBUTING.md](https://github.com/googleapis/mcp-toolbox/blob/main/CONTRIBUTING.md)
|
||||
- [ ] Make sure to open an issue as a
|
||||
[bug/issue](https://github.com/googleapis/mcp-toolbox/issues/new/choose)
|
||||
before writing your code! That way we can discuss the change, evaluate
|
||||
designs, and agree on the general idea
|
||||
- [ ] Ensure the tests and linter pass
|
||||
- [ ] Code coverage does not decrease (if any source code was changed)
|
||||
- [ ] Appropriate docs were updated (if necessary)
|
||||
- [ ] Make sure to add `!` if this involve a breaking change
|
||||
|
||||
🛠️ Fixes #<issue_number_goes_here>
|
||||
@@ -0,0 +1,15 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
enabled: false
|
||||
@@ -0,0 +1,106 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
assign_issues:
|
||||
- Yuan325
|
||||
- duwenxin99
|
||||
- anubhav756
|
||||
- twishabansal
|
||||
- dishaprakash
|
||||
assign_issues_by:
|
||||
- labels:
|
||||
- 'product: alloydb'
|
||||
to:
|
||||
- 'googleapis/toolbox-alloydb-team'
|
||||
- labels:
|
||||
- 'product: bigquery'
|
||||
to:
|
||||
- 'googleapis/toolbox-bigquery-team'
|
||||
- labels:
|
||||
- 'product: bigtable'
|
||||
to:
|
||||
- 'googleapis/toolbox-bigtable-team'
|
||||
- labels:
|
||||
- 'product: mssql'
|
||||
to:
|
||||
- 'googleapis/toolbox-cloud-sql-mssql-team'
|
||||
- labels:
|
||||
- 'product: mysql'
|
||||
to:
|
||||
- 'googleapis/toolbox-cloud-sql-mysql-team'
|
||||
- labels:
|
||||
- 'product: postgres'
|
||||
to:
|
||||
- 'googleapis/toolbox-cloud-sql-postgres-team'
|
||||
- labels:
|
||||
- 'product: dataplex'
|
||||
to:
|
||||
- 'googleapis/toolbox-dataplex-team'
|
||||
- labels:
|
||||
- 'product: firestore'
|
||||
to:
|
||||
- 'googleapis/toolbox-firestore-team'
|
||||
- labels:
|
||||
- 'product: looker'
|
||||
to:
|
||||
- 'googleapis/toolbox-looker-team'
|
||||
- labels:
|
||||
- 'product: spanner'
|
||||
to:
|
||||
- 'googleapis/toolbox-spanner-team'
|
||||
assign_prs:
|
||||
- Yuan325
|
||||
- duwenxin99
|
||||
assign_prs_by:
|
||||
- labels:
|
||||
- 'product: alloydb'
|
||||
to:
|
||||
- 'googleapis/toolbox-alloydb-team'
|
||||
- labels:
|
||||
- 'product: bigquery'
|
||||
to:
|
||||
- 'googleapis/toolbox-bigquery-team'
|
||||
- labels:
|
||||
- 'product: bigtable'
|
||||
to:
|
||||
- 'googleapis/toolbox-bigtable-team'
|
||||
- labels:
|
||||
- 'product: mssql'
|
||||
to:
|
||||
- 'googleapis/toolbox-cloud-sql-mssql-team'
|
||||
- labels:
|
||||
- 'product: mysql'
|
||||
to:
|
||||
- 'googleapis/toolbox-cloud-sql-mysql-team'
|
||||
- labels:
|
||||
- 'product: postgres'
|
||||
to:
|
||||
- 'googleapis/toolbox-cloud-sql-postgres-team'
|
||||
- labels:
|
||||
- 'product: dataplex'
|
||||
to:
|
||||
- 'googleapis/toolbox-dataplex-team'
|
||||
- labels:
|
||||
- 'product: firestore'
|
||||
to:
|
||||
- 'googleapis/toolbox-firestore-team'
|
||||
- labels:
|
||||
- 'product: looker'
|
||||
to:
|
||||
- 'googleapis/toolbox-looker-team'
|
||||
- labels:
|
||||
- 'product: spanner'
|
||||
to:
|
||||
- 'googleapis/toolbox-spanner-team'
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
allowedCopyrightHolders:
|
||||
- 'Google LLC'
|
||||
- 'Oracle'
|
||||
allowedLicenses:
|
||||
- 'Apache-2.0'
|
||||
sourceFileExtensions:
|
||||
- 'go'
|
||||
- 'yaml'
|
||||
- 'yml'
|
||||
ignoreFiles:
|
||||
- 'docs/en/documentation/**/*.go'
|
||||
- 'docs/en/samples/**/*'
|
||||
- '**/*oracle*'
|
||||
@@ -0,0 +1,2 @@
|
||||
---
|
||||
ignored: true
|
||||
@@ -0,0 +1,194 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
- name: duplicate
|
||||
color: ededed
|
||||
description: ""
|
||||
|
||||
- name: wontfix
|
||||
color: ededed
|
||||
description: Issues that we are not planning to fix but would like to keep
|
||||
track of.
|
||||
|
||||
- name: 'type: bug'
|
||||
color: db4437
|
||||
description: Error or flaw in code with unintended results or allowing sub-optimal
|
||||
usage patterns.
|
||||
- name: 'type: cleanup'
|
||||
color: c5def5
|
||||
description: An internal cleanup or hygiene concern.
|
||||
- name: 'type: docs'
|
||||
color: 0000A0
|
||||
description: Improvement to the documentation for an API.
|
||||
- name: 'type: feature request'
|
||||
color: c5def5
|
||||
description: ‘Nice-to-have’ improvement, new feature or different behavior or design.
|
||||
- name: 'type: process'
|
||||
color: c5def5
|
||||
description: A process-related concern. May include testing, release, or the like.
|
||||
- name: 'type: question'
|
||||
color: c5def5
|
||||
description: Request for information or clarification.
|
||||
|
||||
- name: 'priority: p0'
|
||||
color: b60205
|
||||
description: Highest priority. Critical issue. P0 implies highest priority.
|
||||
- name: 'priority: p1'
|
||||
color: ffa03e
|
||||
description: Important issue which blocks shipping the next release. Will be fixed
|
||||
prior to next release.
|
||||
- name: 'priority: p2'
|
||||
color: fef2c0
|
||||
description: Moderately-important priority. Fix may not be included in next release.
|
||||
- name: 'priority: p3'
|
||||
color: ffffc7
|
||||
description: Desirable enhancement or fix. May not be included in next release.
|
||||
|
||||
- name: 'do not merge'
|
||||
color: d93f0b
|
||||
description: Indicates a pull request not ready for merge, due to either quality
|
||||
or timing.
|
||||
|
||||
- name: 'autorelease: pending'
|
||||
color: ededed
|
||||
description: Release please needs to do its work on this.
|
||||
- name: 'autorelease: triggered'
|
||||
color: ededed
|
||||
description: Release please has triggered a release for this.
|
||||
- name: 'autorelease: tagged'
|
||||
color: ededed
|
||||
description: Release please has completed a release for this.
|
||||
|
||||
- name: 'blunderbuss: assign'
|
||||
color: 3DED97
|
||||
description: Have blunderbuss assign this to someone new.
|
||||
|
||||
- name: 'tests: run'
|
||||
color: 3DED97
|
||||
description: Label to trigger Github Action tests.
|
||||
|
||||
- name: 'docs: deploy-preview'
|
||||
color: BFDADC
|
||||
description: Label to trigger Github Action docs preview.
|
||||
|
||||
- name: 'status: help wanted'
|
||||
color: 8befd7
|
||||
description: 'Status: Unplanned work open to contributions from the community.'
|
||||
- name: 'status: feedback wanted'
|
||||
color: 8befd7
|
||||
description: 'Status: waiting for feedback from community or issue author.'
|
||||
|
||||
- name: 'good first issue'
|
||||
color: 7057ff
|
||||
description: 'Good for newcomers'
|
||||
- name: 'ready for work'
|
||||
color: 0e8a16
|
||||
description: 'Ready to be picked up'
|
||||
|
||||
- name: 'status: waiting for response'
|
||||
color: 8befd7
|
||||
description: 'Status: reviewer is awaiting feedback or responses from the author before proceeding.'
|
||||
|
||||
- name: 'release candidate'
|
||||
color: 32CD32
|
||||
description: 'Use label to signal PR should be included in the next release.'
|
||||
|
||||
# Product Labels
|
||||
- name: 'product: alloydb'
|
||||
color: 5065c7
|
||||
description: 'AlloyDB'
|
||||
- name: 'product: bigquery'
|
||||
color: 5065c7
|
||||
description: 'BigQuery'
|
||||
- name: 'product: bigtable'
|
||||
color: 5065c7
|
||||
description: 'Bigtable'
|
||||
- name: 'product: cassandra'
|
||||
color: 5065c7
|
||||
description: 'Cassandra'
|
||||
- name: 'product: clickhouse'
|
||||
color: 5065c7
|
||||
description: 'ClickHouse'
|
||||
- name: 'product: mssql'
|
||||
color: 5065c7
|
||||
description: 'SQL Server'
|
||||
- name: 'product: mysql'
|
||||
color: 5065c7
|
||||
description: 'MySQL'
|
||||
- name: 'product: postgres'
|
||||
color: 5065c7
|
||||
description: 'PostgreSQL'
|
||||
- name: 'product: couchbase'
|
||||
color: 5065c7
|
||||
description: 'Couchbase'
|
||||
- name: 'product: dataplex'
|
||||
color: 5065c7
|
||||
description: 'Dataplex'
|
||||
- name: 'product: dgraph'
|
||||
color: 5065c7
|
||||
description: 'Dgraph'
|
||||
- name: 'product: elasticsearch'
|
||||
color: 5065c7
|
||||
description: 'Elasticsearch'
|
||||
- name: 'product: firebird'
|
||||
color: 5065c7
|
||||
description: 'Firebird'
|
||||
- name: 'product: firestore'
|
||||
color: 5065c7
|
||||
description: 'Firestore'
|
||||
- name: 'product: looker'
|
||||
color: 5065c7
|
||||
description: 'Looker'
|
||||
- name: 'product: mindsdb'
|
||||
color: 5065c7
|
||||
description: 'MindsDB'
|
||||
- name: 'product: mongodb'
|
||||
color: 5065c7
|
||||
description: 'MongoDB'
|
||||
- name: 'product: neo4j'
|
||||
color: 5065c7
|
||||
description: 'Neo4j'
|
||||
- name: 'product: oceanbase'
|
||||
color: 5065c7
|
||||
description: 'OceanBase'
|
||||
- name: 'product: oracle'
|
||||
color: 5065c7
|
||||
description: 'Oracle'
|
||||
- name: 'product: redis'
|
||||
color: 5065c7
|
||||
description: 'Redis'
|
||||
- name: 'product: serverlessspark'
|
||||
color: 5065c7
|
||||
description: 'Serverless Spark'
|
||||
- name: 'product: singlestore'
|
||||
color: 5065c7
|
||||
description: 'SingleStore'
|
||||
- name: 'product: spanner'
|
||||
color: 5065c7
|
||||
description: 'Spanner'
|
||||
- name: 'product: sqlite'
|
||||
color: 5065c7
|
||||
description: 'SQLite'
|
||||
- name: 'product: tidb'
|
||||
color: 5065c7
|
||||
description: 'TiDB'
|
||||
- name: 'product: trino'
|
||||
color: 5065c7
|
||||
description: 'Trino'
|
||||
- name: 'product: valkey'
|
||||
color: 5065c7
|
||||
description: 'Valkey'
|
||||
- name: 'product: yugabytedb'
|
||||
color: 5065c7
|
||||
description: 'YugabyteDB'
|
||||
@@ -0,0 +1,105 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
handleGHRelease: true
|
||||
packageName: genai-toolbox
|
||||
releaseType: simple
|
||||
versionFile: "cmd/version.txt"
|
||||
extraFiles:
|
||||
[
|
||||
"README.md",
|
||||
"docs/en/documentation/getting-started/colab_quickstart.ipynb",
|
||||
"docs/en/documentation/introduction/_index.md",
|
||||
"docs/en/documentation/getting-started/mcp_quickstart/_index.md",
|
||||
"docs/en/documentation/getting-started/quickstart/shared/configure_toolbox.md",
|
||||
"docs/en/integrations/alloydb/_index.md",
|
||||
"docs/en/integrations/alloydb/mcp_quickstart.md",
|
||||
"docs/en/integrations/alloydb/ai-nl/alloydb_ai_nl.ipynb",
|
||||
"docs/en/integrations/bigquery/local_quickstart.md",
|
||||
"docs/en/integrations/bigquery/mcp_quickstart/_index.md",
|
||||
"docs/en/integrations/bigquery/colab_quickstart_bigquery.ipynb",
|
||||
"docs/en/integrations/looker/samples/looker_gemini.md",
|
||||
"docs/en/integrations/looker/samples/looker_gemini_oauth/_index.md",
|
||||
"docs/en/integrations/looker/samples/looker_mcp_inspector/_index.md",
|
||||
"docs/en/documentation/connect-to/ides/looker_mcp.md",
|
||||
"docs/en/documentation/connect-to/ides/mysql_mcp.md",
|
||||
"docs/en/documentation/connect-to/ides/mssql_mcp.md",
|
||||
"docs/en/documentation/connect-to/ides/postgres_mcp.md",
|
||||
"docs/en/documentation/connect-to/ides/neo4j_mcp.md",
|
||||
"docs/en/documentation/connect-to/ides/sqlite_mcp.md",
|
||||
"docs/en/documentation/connect-to/ides/oracle_mcp.md",
|
||||
"gemini-extension.json",
|
||||
"pypi/src/toolbox_server/__init__.py",
|
||||
{ "type": "json", "path": "server.json", "jsonpath": "$.version" },
|
||||
{
|
||||
"type": "json",
|
||||
"path": "server.json",
|
||||
"jsonpath": "$.packages[0].identifier",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server/package.json",
|
||||
"jsonpath": "$.version",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server/package.json",
|
||||
"jsonpath": "$.optionalDependencies['@toolbox-sdk/server-darwin-arm64']",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server/package.json",
|
||||
"jsonpath": "$.optionalDependencies['@toolbox-sdk/server-darwin-x64']",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server/package.json",
|
||||
"jsonpath": "$.optionalDependencies['@toolbox-sdk/server-linux-x64']",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server/package.json",
|
||||
"jsonpath": "$.optionalDependencies['@toolbox-sdk/server-win32-arm64']",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server/package.json",
|
||||
"jsonpath": "$.optionalDependencies['@toolbox-sdk/server-win32-x64']",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server-darwin-arm64/package.json",
|
||||
"jsonpath": "$.version",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server-darwin-x64/package.json",
|
||||
"jsonpath": "$.version",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server-linux-x64/package.json",
|
||||
"jsonpath": "$.version",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server-win32-arm64/package.json",
|
||||
"jsonpath": "$.version",
|
||||
},
|
||||
{
|
||||
"type": "json",
|
||||
"path": "npm/server-win32-x64/package.json",
|
||||
"jsonpath": "$.version",
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,110 @@
|
||||
{
|
||||
extends: [
|
||||
'config:recommended',
|
||||
':semanticCommitTypeAll(chore)',
|
||||
':ignoreUnstable',
|
||||
':separateMajorReleases',
|
||||
':prConcurrentLimitNone',
|
||||
':prHourlyLimitNone',
|
||||
':preserveSemverRanges',
|
||||
],
|
||||
minimumReleaseAge: '3 days',
|
||||
rebaseWhen: 'conflicted',
|
||||
dependencyDashboardLabels: [
|
||||
'type: process',
|
||||
],
|
||||
postUpdateOptions: [
|
||||
'gomodTidy',
|
||||
],
|
||||
customManagers: [
|
||||
{
|
||||
customType: 'regex',
|
||||
managerFilePatterns: [
|
||||
'/^\\.github/workflows/.*\\.ya?ml$/',
|
||||
],
|
||||
matchStrings: [
|
||||
'hugo-version:\\s*["\']?(?<currentValue>\\d+\\.\\d+\\.\\d+)["\']?',
|
||||
],
|
||||
depNameTemplate: 'gohugoio/hugo',
|
||||
datasourceTemplate: 'github-releases',
|
||||
extractVersionTemplate: '^v?(?<version>.*)$',
|
||||
},
|
||||
],
|
||||
packageRules: [
|
||||
{
|
||||
groupName: 'Hugo',
|
||||
matchPackageNames: [
|
||||
'gohugoio/hugo',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'GitHub Actions',
|
||||
matchManagers: [
|
||||
'github-actions',
|
||||
],
|
||||
pinDigests: true,
|
||||
},
|
||||
{
|
||||
groupName: 'Go',
|
||||
matchManagers: [
|
||||
'gomod',
|
||||
],
|
||||
ignorePaths: [
|
||||
'docs/**',
|
||||
'.hugo/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'Go Samples',
|
||||
matchManagers: [
|
||||
'gomod',
|
||||
],
|
||||
matchFileNames: [
|
||||
'docs/**',
|
||||
'.hugo/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'Node',
|
||||
matchManagers: [
|
||||
'npm',
|
||||
],
|
||||
matchUpdateTypes: [
|
||||
'minor',
|
||||
'patch',
|
||||
],
|
||||
ignorePaths: [
|
||||
'docs/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'Node Samples',
|
||||
matchManagers: [
|
||||
'npm',
|
||||
],
|
||||
// Explicitly match the sample directories
|
||||
matchFileNames: [
|
||||
'docs/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'Pip',
|
||||
matchManagers: [
|
||||
'pip_requirements',
|
||||
],
|
||||
ignorePaths: [
|
||||
'docs/**',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupName: 'Python Samples',
|
||||
matchManagers: [
|
||||
'pip_requirements',
|
||||
],
|
||||
// Explicitly match the sample directories
|
||||
matchFileNames: [
|
||||
'docs/**',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Trigger presubmit tests for trusted contributors
|
||||
# https://github.com/googleapis/repo-automation-bots/tree/main/packages/trusted-contribution
|
||||
# Install: https://github.com/apps/trusted-contributions-gcf
|
||||
|
||||
trustedContributors:
|
||||
- "dependabot[bot]"
|
||||
- "renovate-bot"
|
||||
annotations:
|
||||
# Trigger Cloud Build tests
|
||||
- type: comment
|
||||
text: "/gcbrun"
|
||||
- type: label
|
||||
text: "tests: run"
|
||||
@@ -0,0 +1,181 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Cloud Build Failure Reporter
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
trigger_names:
|
||||
required: true
|
||||
type: string
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
trigger_names:
|
||||
description: 'Cloud Build trigger names separated by comma.'
|
||||
required: true
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
report:
|
||||
|
||||
permissions:
|
||||
issues: 'write'
|
||||
checks: 'read'
|
||||
|
||||
runs-on: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- uses: 'actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3' # v9
|
||||
env:
|
||||
TRIGGER_NAMES: ${{ inputs.trigger_names }}
|
||||
with:
|
||||
script: |-
|
||||
// parse test names
|
||||
const testNameSubstring = process.env.TRIGGER_NAMES;
|
||||
const testNameFound = new Map(); //keeps track of whether each test is found
|
||||
testNameSubstring.split(',').forEach(testName => {
|
||||
testNameFound.set(testName, false);
|
||||
});
|
||||
|
||||
// label for all issues opened by reporter
|
||||
const periodicLabel = 'periodic-failure';
|
||||
|
||||
// check if any reporter opened any issues previously
|
||||
const prevIssues = await github.paginate(github.rest.issues.listForRepo, {
|
||||
...context.repo,
|
||||
state: 'open',
|
||||
creator: 'github-actions[bot]',
|
||||
labels: [periodicLabel]
|
||||
});
|
||||
|
||||
// createOrCommentIssue creates a new issue or comments on an existing issue.
|
||||
const createOrCommentIssue = async function (title, txt) {
|
||||
if (prevIssues.length < 1) {
|
||||
console.log('no previous issues found, creating one');
|
||||
await github.rest.issues.create({
|
||||
...context.repo,
|
||||
title: title,
|
||||
body: txt,
|
||||
labels: [periodicLabel]
|
||||
});
|
||||
return;
|
||||
}
|
||||
// only comment on issue related to the current test
|
||||
for (const prevIssue of prevIssues) {
|
||||
if (prevIssue.title.includes(title)){
|
||||
console.log(
|
||||
`found previous issue ${prevIssue.html_url}, adding comment`
|
||||
);
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: prevIssue.number,
|
||||
body: txt
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// updateIssues comments on any existing issues. No-op if no issue exists.
|
||||
const updateIssues = async function (checkName, txt) {
|
||||
if (prevIssues.length < 1) {
|
||||
console.log('no previous issues found.');
|
||||
return;
|
||||
}
|
||||
// only comment on issue related to the current test
|
||||
for (const prevIssue of prevIssues) {
|
||||
if (prevIssue.title.includes(checkName)){
|
||||
console.log(`found previous issue ${prevIssue.html_url}, adding comment`);
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: prevIssue.number,
|
||||
body: txt
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Find status of check runs.
|
||||
// We will find check runs for each commit and then filter for the periodic.
|
||||
// Checks API only allows for ref and if we use main there could be edge cases where
|
||||
// the check run happened on a SHA that is different from head.
|
||||
const commits = await github.paginate(github.rest.repos.listCommits, {
|
||||
...context.repo
|
||||
});
|
||||
|
||||
const relevantChecks = new Map();
|
||||
for (const commit of commits) {
|
||||
console.log(
|
||||
`checking runs at ${commit.html_url}: ${commit.commit.message}`
|
||||
);
|
||||
const checks = await github.rest.checks.listForRef({
|
||||
...context.repo,
|
||||
ref: commit.sha
|
||||
});
|
||||
|
||||
// Iterate through each check and find matching names
|
||||
for (const check of checks.data.check_runs) {
|
||||
console.log(`Handling test name ${check.name}`);
|
||||
for (const testName of testNameFound.keys()) {
|
||||
if (testNameFound.get(testName) === true){
|
||||
//skip if a check is already found for this name
|
||||
continue;
|
||||
}
|
||||
if (check.name.includes(testName)) {
|
||||
relevantChecks.set(check, commit);
|
||||
testNameFound.set(testName, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Break out of the loop early if all tests are found
|
||||
const allTestsFound = Array.from(testNameFound.values()).every(value => value === true);
|
||||
if (allTestsFound){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle each relevant check
|
||||
relevantChecks.forEach((commit, check) => {
|
||||
if (
|
||||
check.status === 'completed' &&
|
||||
check.conclusion === 'success'
|
||||
) {
|
||||
updateIssues(
|
||||
check.name,
|
||||
`[Tests are passing](${check.html_url}) for commit [${commit.sha}](${commit.html_url}).`
|
||||
);
|
||||
} else if (check.status === 'in_progress') {
|
||||
console.log(
|
||||
`Check is pending ${check.html_url} for ${commit.html_url}. Retry again later.`
|
||||
);
|
||||
} else {
|
||||
createOrCommentIssue(
|
||||
`Cloud Build Failure Reporter: ${check.name} failed`,
|
||||
`Cloud Build Failure Reporter found test failure for [**${check.name}** ](${check.html_url}) at [${commit.sha}](${commit.html_url}). Please fix the error and then close the issue after the **${check.name}** test passes.`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// no periodic checks found across all commits, report it
|
||||
const noTestFound = Array.from(testNameFound.values()).every(value => value === false);
|
||||
if (noTestFound){
|
||||
createOrCommentIssue(
|
||||
`Missing periodic tests: ${process.env.TRIGGER_NAMES}`,
|
||||
`No periodic test is found for triggers: ${process.env.TRIGGER_NAMES}. Last checked from ${
|
||||
commits[0].html_url
|
||||
} to ${commits[commits.length - 1].html_url}.`
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "Sync v1 docsite to Cloudflare"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: cloudflare-sync
|
||||
cancel-in-progress: true
|
||||
|
||||
# zizmor flags workflow_run, but this is safe because it only deploys internal branches and is strictly gated to the base repository.
|
||||
on: # zizmor: ignore[dangerous-triggers]
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["CF: Deploy Dev Docs", "CF: Deploy Versioned Docs", "CF: Deploy Previous Version Docs"]
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
# Hardened workflow_run: strictly verifies the triggering run originated from the base repository to prevent execution from forks.
|
||||
if: >
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository)
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: 'cloudflare-pages'
|
||||
persist-credentials: false
|
||||
- name: Cleanup
|
||||
run: |
|
||||
rm -rf .git
|
||||
- name: Cloudflare Deploy
|
||||
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy . --project-name=toolbox-docs --branch=main
|
||||
@@ -0,0 +1,79 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: conformance
|
||||
# zizmor flags pull_request_target, but it is safely gated by the 'tests: run' label manually applied by maintainers.
|
||||
on: # zizmor: ignore[dangerous-triggers]
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
conformance:
|
||||
if: "${{ github.event.action != 'labeled' || github.event.label.name == 'tests: run' }}"
|
||||
name: conformance
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: 'read'
|
||||
issues: 'write'
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: Remove PR Label
|
||||
if: "${{ github.event.action == 'labeled' && github.event.label.name == 'tests: run' }}"
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
name: 'tests: run',
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Failed to remove label. Another job may have already removed it!');
|
||||
}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- name: Start MCP Toolbox Server
|
||||
run: |
|
||||
go build -o toolbox
|
||||
./toolbox --allowed-hosts localhost --config tests/conformance/tools.yaml &
|
||||
sleep 5 # Give the server a moment to bind to port 5000
|
||||
|
||||
- name: Run Conformance Tests
|
||||
uses: modelcontextprotocol/conformance@21a9a2febd7100d7c17ac1021ee7f2ed9f66a1e0 # v0.1.16
|
||||
with:
|
||||
mode: server
|
||||
url: 'http://localhost:5000/mcp'
|
||||
suite: active
|
||||
expected-failures: tests/conformance/conformance-baseline.yml
|
||||
@@ -0,0 +1,91 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "CF: Deploy Dev Docs"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/docs*_cf.yaml'
|
||||
- '.github/workflows/deploy*_cf.yaml'
|
||||
- '.hugo/**'
|
||||
|
||||
# Allow triggering manually.
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: .hugo
|
||||
# This shared concurrency group ensures only one docs deployment runs at a time.
|
||||
concurrency:
|
||||
group: cf-docs-update
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3
|
||||
with:
|
||||
hugo-version: "0.163.3"
|
||||
extended: true
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- run: npm ci
|
||||
- run: hugo --minify --config hugo.cloudflare.toml
|
||||
env:
|
||||
HUGO_BASEURL: https://mcp-toolbox.dev/dev/
|
||||
HUGO_RELATIVEURLS: false
|
||||
|
||||
- name: Build Pagefind Search Index
|
||||
run: npx pagefind --site public
|
||||
|
||||
- name: Create Staging Directory
|
||||
run: |
|
||||
mkdir staging
|
||||
mv public staging/dev
|
||||
mv staging/dev/releases.releases staging/releases.releases
|
||||
|
||||
- name: Push to Cloudflare Branch
|
||||
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./.hugo/staging
|
||||
publish_branch: cloudflare-pages
|
||||
keep_files: true
|
||||
commit_message: "deploy: ${{ github.event.head_commit.message }}"
|
||||
@@ -0,0 +1,119 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "CF: Deploy Previous Version Docs"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version_tag:
|
||||
description: 'The old version tag to build docs for (e.g., v0.15.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout main branch (for latest templates and theme)
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: 'main'
|
||||
submodules: 'recursive'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Checkout old content from tag into a temporary directory
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: ${{ github.event.inputs.version_tag }}
|
||||
path: 'old_version_source'
|
||||
sparse-checkout: |
|
||||
docs
|
||||
persist-credentials: false
|
||||
|
||||
- name: Replace content with old version
|
||||
run: |
|
||||
# Remove the current content directory from the main branch checkout
|
||||
rm -rf docs/
|
||||
# Move the old content directory into place
|
||||
mv ./old_version_source/docs docs
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3
|
||||
with:
|
||||
hugo-version: "0.163.3"
|
||||
extended: true
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
working-directory: .hugo
|
||||
|
||||
- name: Build Hugo Site for Archived Version
|
||||
run: |
|
||||
hugo --minify --config hugo.cloudflare.toml
|
||||
rm -f public/releases.releases
|
||||
working-directory: .hugo
|
||||
env:
|
||||
HUGO_BASEURL: https://mcp-toolbox.dev/${{ github.event.inputs.version_tag }}/
|
||||
HUGO_RELATIVEURLS: false
|
||||
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
|
||||
|
||||
- name: Build Pagefind Index (Archived Version)
|
||||
run: npx pagefind --site public
|
||||
working-directory: .hugo
|
||||
|
||||
- name: Deploy to cloudflare-pages
|
||||
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: .hugo/public
|
||||
publish_branch: cloudflare-pages
|
||||
destination_dir: ./${{ github.event.inputs.version_tag }}
|
||||
keep_files: true
|
||||
allow_empty_commit: true
|
||||
commit_message: "docs(backport): deploy docs for ${{ github.event.inputs.version_tag }}"
|
||||
|
||||
- name: Clean Build Directory
|
||||
run: rm -rf .hugo/public
|
||||
|
||||
- name: Build Hugo Site
|
||||
run: hugo --minify --config hugo.cloudflare.toml
|
||||
working-directory: .hugo
|
||||
env:
|
||||
HUGO_BASEURL: https://mcp-toolbox.dev/
|
||||
HUGO_RELATIVEURLS: false
|
||||
HUGO_PARAMS_VERSION: ${{ github.event.inputs.version_tag }}
|
||||
|
||||
- name: Build Pagefind Index (Root)
|
||||
run: npx pagefind --site public
|
||||
working-directory: .hugo
|
||||
|
||||
- name: Deploy to root
|
||||
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: .hugo/public
|
||||
publish_branch: cloudflare-pages
|
||||
keep_files: true
|
||||
allow_empty_commit: true
|
||||
commit_message: "deploy: docs to root for ${{ github.event.inputs.version_tag }}"
|
||||
@@ -0,0 +1,108 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "CF: Deploy Versioned Docs"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-24.04
|
||||
# This shared concurrency group ensures only one docs deployment runs at a time.
|
||||
concurrency:
|
||||
group: cf-docs-update
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: Checkout Code at Tag
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get Version from Release Tag
|
||||
id: get_version
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
run: echo "VERSION=${RELEASE_TAG}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3
|
||||
with:
|
||||
hugo-version: "0.163.3"
|
||||
extended: true
|
||||
|
||||
- name: Setup Node
|
||||
# zizmor flags setup-node for cache-poisoning on releases, but since we don't pass `cache: npm`, no caching occurs. Safe to ignore.
|
||||
# zizmor: ignore[cache-poisoning]
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
working-directory: .hugo
|
||||
|
||||
- name: Build Hugo Site
|
||||
run: |
|
||||
hugo --minify --config hugo.cloudflare.toml
|
||||
rm -f public/releases.releases
|
||||
working-directory: .hugo
|
||||
env:
|
||||
HUGO_BASEURL: https://mcp-toolbox.dev/${{ steps.get_version.outputs.VERSION }}/
|
||||
HUGO_RELATIVEURLS: false
|
||||
HUGO_PARAMS_VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build Pagefind Index (Versioned)
|
||||
run: npx pagefind --site public
|
||||
working-directory: .hugo
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: .hugo/public
|
||||
publish_branch: cloudflare-pages
|
||||
destination_dir: ./${{ steps.get_version.outputs.VERSION }}
|
||||
keep_files: true
|
||||
commit_message: "deploy: docs for ${{ steps.get_version.outputs.VERSION }}"
|
||||
|
||||
- name: Clean Build Directory
|
||||
run: rm -rf .hugo/public
|
||||
|
||||
- name: Build Hugo Site
|
||||
run: hugo --minify --config hugo.cloudflare.toml
|
||||
working-directory: .hugo
|
||||
env:
|
||||
HUGO_BASEURL: https://mcp-toolbox.dev/
|
||||
HUGO_RELATIVEURLS: false
|
||||
HUGO_PARAMS_VERSION: ${{ steps.get_version.outputs.VERSION }}
|
||||
|
||||
- name: Build Pagefind Index (Root)
|
||||
run: npx pagefind --site public
|
||||
working-directory: .hugo
|
||||
|
||||
- name: Deploy to root
|
||||
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: .hugo/public
|
||||
publish_branch: cloudflare-pages
|
||||
keep_files: true
|
||||
allow_empty_commit: true
|
||||
commit_message: "deploy: docs to root for ${{ steps.get_version.outputs.VERSION }}"
|
||||
@@ -0,0 +1,62 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Lint Documentation
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/docs**'
|
||||
- '.ci/lint-docs-*.sh'
|
||||
- '.hugo/data/filters.yaml'
|
||||
|
||||
jobs:
|
||||
lint-source-pages:
|
||||
name: Lint Documentation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Check for large files (>24MB)
|
||||
run: |
|
||||
LARGE_FILES=$(find docs/ -type f -size +24M)
|
||||
if [ -n "$LARGE_FILES" ]; then
|
||||
echo "Error: Files exceed 24MB limit: $LARGE_FILES"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Make scripts executable
|
||||
run: chmod +x .ci/lint-docs-*.sh
|
||||
|
||||
- name: Run Structure Linter for Source Pages
|
||||
run: bash .ci/lint-docs-source-page.sh
|
||||
|
||||
- name: Run Structure Linter for Tool Pages
|
||||
run: bash .ci/lint-docs-tool-page.sh
|
||||
|
||||
- name: Run Sample Filters Linter
|
||||
run: bash .ci/lint-docs-sample-filters.sh
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
name: "CF: Build Docs Preview"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/workflows/docs*_cf.yaml'
|
||||
- '.hugo/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-preview:
|
||||
if: "contains(github.event.pull_request.labels.*.name, 'docs: deploy-preview')"
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
concurrency:
|
||||
group: "cf-preview-${{ github.event.number }}"
|
||||
cancel-in-progress: true
|
||||
defaults:
|
||||
run:
|
||||
working-directory: .hugo
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: ${{ env.HEAD_SHA }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Hugo
|
||||
uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3
|
||||
with:
|
||||
hugo-version: "0.163.3"
|
||||
extended: true
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "22"
|
||||
cache: 'npm'
|
||||
cache-dependency-path: '.hugo/package-lock.json'
|
||||
|
||||
- run: npm ci --ignore-scripts
|
||||
- run: hugo --minify --config hugo.cloudflare.toml
|
||||
env:
|
||||
HUGO_BASEURL: "/"
|
||||
HUGO_ENVIRONMENT: preview
|
||||
HUGO_RELATIVEURLS: false
|
||||
|
||||
- name: Build Pagefind Search Index
|
||||
run: npx pagefind --site public
|
||||
|
||||
- name: Prepare Artifact Payload
|
||||
run: |
|
||||
mkdir -p ../artifact-payload
|
||||
cp -r public ../artifact-payload/public
|
||||
echo ${PR_NUMBER} > ../artifact-payload/pr_number.txt
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: cf-preview-data
|
||||
path: artifact-payload/
|
||||
retention-days: 1
|
||||
|
||||
- name: Deployment Link
|
||||
run: |
|
||||
DEPLOY_URL="https://github.com/${{ github.repository_owner }}/${GITHUB_EVENT_REPOSITORY_NAME}/actions/workflows/docs_deploy_cf.yaml"
|
||||
|
||||
echo "### Build Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "The build for PR #${PR_NUMBER} succeeded." >> $GITHUB_STEP_SUMMARY
|
||||
echo "The Cloudflare deployment workflow is now starting." >> $GITHUB_STEP_SUMMARY
|
||||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||||
echo "#### [Track Deployment Progress]($DEPLOY_URL)" >> $GITHUB_STEP_SUMMARY
|
||||
env:
|
||||
GITHUB_EVENT_REPOSITORY_NAME: ${{ github.event.repository.name }}
|
||||
|
||||
remove-label:
|
||||
needs: build-preview
|
||||
if: "always() && contains(github.event.pull_request.labels.*.name, 'docs: deploy-preview')"
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Remove deploy-preview label
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
name: 'docs: deploy-preview'
|
||||
});
|
||||
console.log("Label 'docs: deploy-preview' removed successfully.");
|
||||
} catch (error) {
|
||||
console.log(`Error removing label: ${error}`);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "CF: Cleanup PR Preview"
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
# This Workflow depends on 'github.event.number',
|
||||
# not compatible with branch or manual triggers.
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
|
||||
jobs:
|
||||
clean:
|
||||
# Only run for PRs from the same repository to ensure secret access
|
||||
if: "${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}"
|
||||
runs-on: ubuntu-24.04
|
||||
concurrency:
|
||||
# Shared concurrency group with preview staging.
|
||||
group: "cf-preview-${{ github.event.number }}"
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Delete Cloudflare Pages Deployments via API
|
||||
env:
|
||||
ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
PROJECT_NAME: toolbox-docs
|
||||
BRANCH_NAME: pr-${{ github.event.number }}
|
||||
run: |
|
||||
echo "Fetching deployments for preview branch: $BRANCH_NAME"
|
||||
|
||||
# Fetch the most recent deployments from your Cloudflare project
|
||||
RESPONSE=$(curl -s -X GET "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/pages/projects/$PROJECT_NAME/deployments" \
|
||||
-H "Authorization: Bearer $API_TOKEN")
|
||||
|
||||
# Use 'jq' to extract all deployment IDs that match this specific PR branch alias
|
||||
IDS=$(echo "$RESPONSE" | jq -r --arg branch "$BRANCH_NAME" '.result[] | select(.deployment_trigger.metadata.branch? == $branch) | .id')
|
||||
|
||||
if [ -z "$IDS" ]; then
|
||||
echo "No preview deployments found to clean up."
|
||||
else
|
||||
for id in $IDS; do
|
||||
echo "Deleting Cloudflare deployment ID: $id"
|
||||
curl -s -X DELETE "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/pages/projects/$PROJECT_NAME/deployments/$id?force=true" \
|
||||
-H "Authorization: Bearer $API_TOKEN"
|
||||
done
|
||||
echo "Successfully removed preview environment."
|
||||
fi
|
||||
|
||||
- name: Comment
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.payload.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: "🧨 **Preview deployments removed.**\n\nCloudflare Pages environments for `pr-${{ github.event.number }}` have been deleted."
|
||||
})
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: "CF: Deploy Docs Preview"
|
||||
|
||||
# zizmor flags workflow_run input, but this workflow safely sanitizes the untrusted PR number artifact before use.
|
||||
on: # zizmor: ignore[dangerous-triggers]
|
||||
workflow_run:
|
||||
workflows: ["CF: Build Docs Preview"]
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR Number to deploy (Manual override)'
|
||||
required: true
|
||||
type: string
|
||||
build_run_id:
|
||||
description: 'The Run ID from the successful "CF: Build Docs Preview" workflow'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
deploy-preview:
|
||||
if: >
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success')
|
||||
runs-on: ubuntu-24.04
|
||||
concurrency:
|
||||
group: "cf-deploy-${{ github.event.inputs.pr_number || github.event.workflow_run.pull_requests[0].number }}"
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Checkout base repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
||||
with:
|
||||
name: cf-preview-data
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.inputs.build_run_id || github.event.workflow_run.id }}
|
||||
path: downloaded-artifact
|
||||
|
||||
- name: Read PR Number
|
||||
id: get_pr
|
||||
run: |
|
||||
if [ -n "${GITHUB_EVENT_INPUTS_PR_NUMBER}" ]; then
|
||||
PR_NUMBER="${GITHUB_EVENT_INPUTS_PR_NUMBER}"
|
||||
else
|
||||
PR_NUMBER=$(cat downloaded-artifact/pr_number.txt)
|
||||
fi
|
||||
if ! [[ "$PR_NUMBER" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: PR number [$PR_NUMBER] is invalid."
|
||||
exit 1
|
||||
fi
|
||||
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GITHUB_EVENT_INPUTS_PR_NUMBER: ${{ github.event.inputs.pr_number }}
|
||||
|
||||
- name: Deploy to Cloudflare Pages
|
||||
id: cf_deploy
|
||||
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy downloaded-artifact/public --project-name toolbox-docs --branch pr-${{ steps.get_pr.outputs.pr_number }}
|
||||
|
||||
- name: Post Preview URL Comment
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
env:
|
||||
PR_NUMBER: ${{ steps.get_pr.outputs.pr_number }}
|
||||
DEPLOY_URL: ${{ steps.cf_deploy.outputs.pages-deployment-alias-url }}
|
||||
with:
|
||||
script: |
|
||||
const prNumber = parseInt(process.env.PR_NUMBER, 10);
|
||||
const deployUrl = process.env.DEPLOY_URL;
|
||||
const marker = '<!-- cf-preview-comment-marker -->';
|
||||
|
||||
// Fetch all comments on the PR
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
|
||||
// Look for the invisible HTML marker
|
||||
const existingComment = comments.find(c => c.body.includes(marker));
|
||||
|
||||
// Exit early if we've already posted a comment for this PR to avoid duplicates
|
||||
if (existingComment) {
|
||||
console.log("Preview link already posted on this PR. Skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the comment since it's the first deployment for this PR
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: `${marker}\n🚀 **Cloudflare Preview Ready!**\n\n🔎 View Preview: ${deployUrl}\n\n*(Note: Subsequent pushes to this PR will automatically update the preview at this same URL)*`
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: Link Checker
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Identify Changed Files
|
||||
id: changed-files
|
||||
shell: bash
|
||||
run: |
|
||||
git fetch origin main
|
||||
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT origin/main...HEAD -- '*.md')
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No markdown files changed. Skipping checks."
|
||||
echo "HAS_CHANGES=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "--- Changed Files to Scan ---"
|
||||
echo "$CHANGED_FILES"
|
||||
echo "-----------------------------"
|
||||
|
||||
FILES_QUOTED=$(echo "$CHANGED_FILES" | sed 's/^/"/;s/$/"/' | tr '\n' ' ')
|
||||
|
||||
# Use EOF to write multiline or long strings to GITHUB_OUTPUT
|
||||
echo "HAS_CHANGES=true" >> "$GITHUB_OUTPUT"
|
||||
echo "CHECK_FILES<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$FILES_QUOTED" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Restore lychee cache
|
||||
if: steps.changed-files.outputs.HAS_CHANGES == 'true'
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
- name: Link Checker
|
||||
id: lychee-check
|
||||
if: steps.changed-files.outputs.HAS_CHANGES == 'true'
|
||||
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
args: >
|
||||
--quiet
|
||||
--no-progress
|
||||
--cache
|
||||
--max-cache-age 1d
|
||||
--exclude '^neo4j\+.*' --exclude '^bolt://.*'
|
||||
--max-retries 10
|
||||
${{ steps.changed-files.outputs.CHECK_FILES }}
|
||||
output: lychee-report.md
|
||||
format: markdown
|
||||
fail: true
|
||||
jobSummary: false
|
||||
debug: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Prepare Report
|
||||
if: steps.changed-files.outputs.HAS_CHANGES == 'true' && steps.lychee-check.outcome == 'failure'
|
||||
run: |
|
||||
echo "## Link Resolution Note" > full-report.md
|
||||
echo "Local links and directory changes work differently on GitHub than on the docsite. You must ensure fixes pass the **GitHub check** and also work with **\`hugo server\`**." >> full-report.md
|
||||
echo "See [Link Checking and Fixing with Lychee](https://github.com/googleapis/mcp-toolbox/blob/main/DEVELOPER.md#link-checking-and-fixing-with-lychee) for more details." >> full-report.md
|
||||
echo "" >> full-report.md
|
||||
sed -E '/(Redirect|Redirects per input)/d' lychee-report.md >> full-report.md
|
||||
|
||||
|
||||
- name: Display Failure Report
|
||||
# Run this ONLY if the link checker failed
|
||||
if: steps.lychee-check.outcome == 'failure'
|
||||
run: |
|
||||
# We can now simply output the prepared file to the job summary
|
||||
cat full-report.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Fail the job
|
||||
exit 1
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
name: Link Checks
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
jobs:
|
||||
linkChecker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Link Checker
|
||||
id: lychee-check
|
||||
uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2
|
||||
continue-on-error: true
|
||||
with:
|
||||
args: >
|
||||
--quiet
|
||||
--no-progress
|
||||
--exclude '^neo4j\+.*' --exclude '^bolt://.*'
|
||||
README.md
|
||||
docs/
|
||||
output: lychee-report.md
|
||||
format: markdown
|
||||
fail: true
|
||||
jobSummary: false
|
||||
debug: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Prepare Report
|
||||
run: |
|
||||
echo "## Link Resolution Note" > full-report.md
|
||||
echo "Local links and directory changes work differently on GitHub than on the docsite.You must ensure fixes pass the **GitHub check** and also work with **\`hugo server\`**." >> full-report.md
|
||||
echo "See [Link Checking and Fixing with Lychee](https://github.com/googleapis/mcp-toolbox/blob/main/DEVELOPER.md#link-checking-and-fixing-with-lychee) for more details." >> full-report.md
|
||||
echo "" >> full-report.md
|
||||
sed -E '/(Redirect|Redirects per input)/d' lychee-report.md >> full-report.md
|
||||
|
||||
- name: Create Issue From File
|
||||
if: steps.lychee-check.outcome == 'failure'
|
||||
uses: peter-evans/create-issue-from-file@fca9117c27cdc29c6c4db3b86c48e4115a786710 # v6
|
||||
with:
|
||||
title: Link Checker Report
|
||||
content-filepath: full-report.md
|
||||
labels: |
|
||||
priority: p2
|
||||
type: process
|
||||
|
||||
- name: Display Failure Report
|
||||
# Run this ONLY if the link checker failed
|
||||
if: steps.lychee-check.outcome == 'failure'
|
||||
run: |
|
||||
# We can now simply output the prepared file to the job summary
|
||||
cat full-report.md >> $GITHUB_STEP_SUMMARY
|
||||
# Fail the job
|
||||
exit 1
|
||||
@@ -0,0 +1,58 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: lint
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**"
|
||||
- "!docs/**"
|
||||
- "!**.md"
|
||||
- "!.github/**"
|
||||
- ".github/workflows/lint.yaml"
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
permissions:
|
||||
contents: 'read'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- name: >
|
||||
Verify go mod tidy. If you're reading this and the check has
|
||||
failed, run `goimports -w . && go mod tidy && golangci-lint run`
|
||||
run: |
|
||||
go mod tidy && git diff --exit-code
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@82606bf257cbaff209d206a39f5134f0cfbfd2ee # v9.2.1
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout 10m
|
||||
@@ -0,0 +1,42 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: lint
|
||||
|
||||
# zizmor flags pull_request_target, but this fallback workflow never checks out code.
|
||||
on: # zizmor: ignore[dangerous-triggers]
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/lint.yaml"
|
||||
pull_request_target:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/lint.yaml"
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Skip Lint
|
||||
run: |
|
||||
echo "Skipping lint for documentation/config-only changes."
|
||||
echo "This job exists to satisfy the required status check."
|
||||
@@ -0,0 +1,109 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Weekly Server Tier Assessment
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * 0' # Runs at 6 AM every Sunday
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
tier-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- name: Start MCP Toolbox Server
|
||||
run: |
|
||||
go build -o toolbox
|
||||
./toolbox --allowed-hosts localhost --config tests/conformance/tools.yaml &
|
||||
sleep 5 # Give the server a moment to bind to port 5000
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
- name: Run Server Tier Assessment
|
||||
id: assessment
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
npx --yes @modelcontextprotocol/conformance@latest tier-check --repo ${{ github.repository }} --output markdown --conformance-server-url "http://localhost:5000/mcp" > tier_report.txt
|
||||
continue-on-error: true
|
||||
|
||||
- name: Report Tier Status
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const report = fs.readFileSync('tier_report.txt', 'utf8');
|
||||
const title = '[Dashboard: Conformance] Weekly Server Tier Assessment Failure';
|
||||
|
||||
const prevIssues = await github.rest.issues.listForRepo({
|
||||
...context.repo,
|
||||
state: 'open'
|
||||
});
|
||||
|
||||
// Find any open issue matching the exact dashboard title
|
||||
let existingIssue = prevIssues.data.find(issue => issue.title === title);
|
||||
|
||||
if (report.includes('Tier Assessment: Tier 1')) {
|
||||
console.log('Tier 1 achieved! Closing open issue if it exists.');
|
||||
if (existingIssue) {
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: existingIssue.number,
|
||||
body: "The weekly Server Tier Assessment confirms we are now **Tier 1**! Closing this issue."
|
||||
});
|
||||
await github.rest.issues.update({
|
||||
...context.repo,
|
||||
issue_number: existingIssue.number,
|
||||
state: 'closed'
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const body = `The weekly Server Tier Assessment found gaps preventing Tier 1 status:\n\n${report}\n\nPlease fix these operational gaps.`;
|
||||
|
||||
if (existingIssue) {
|
||||
console.log(`Found previous issue ${existingIssue.html_url}, updating body`);
|
||||
await github.rest.issues.update({
|
||||
...context.repo,
|
||||
issue_number: existingIssue.number,
|
||||
body: body
|
||||
});
|
||||
} else {
|
||||
console.log('No previous issue found, creating one');
|
||||
await github.rest.issues.create({
|
||||
...context.repo,
|
||||
title: title,
|
||||
body: body
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Publish to MCP Registry
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"] # Triggers on version tags like v1.0.0
|
||||
# allow manual triggering with no inputs required
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write # Required for OIDC authentication
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Delay workflow execution
|
||||
run: sleep 1200
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Wait for image in Artifact Registry
|
||||
shell: bash
|
||||
run: |
|
||||
MAX_ATTEMPTS=10
|
||||
VERSION=$(jq -r '.version' server.json)
|
||||
REGISTRY_URL="https://us-central1-docker.pkg.dev/v2/database-toolbox/toolbox/toolbox/manifests/${VERSION}"
|
||||
|
||||
# initially sleep time to wait for the version release
|
||||
sleep 3m
|
||||
|
||||
for i in $(seq 1 ${MAX_ATTEMPTS}); do
|
||||
echo "Attempt $i: Checking for image ${REGISTRY_URL}..."
|
||||
# Use curl to check the manifest header
|
||||
# Using -I to fetch headers only, -s silent, -f fail fast on errors.
|
||||
curl -Isf "${REGISTRY_URL}" > /dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Image found! Continuing to next steps."
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Image not found (likely 404 error) on attempt $i."
|
||||
if [ $i -lt ${MAX_ATTEMPTS} ]; then
|
||||
echo "Sleeping for 5 minutes before next attempt..."
|
||||
sleep 2m
|
||||
else
|
||||
echo "Maximum attempts reached. Image not found."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Install MCP Publisher
|
||||
run: |
|
||||
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
||||
|
||||
- name: Login to MCP Registry
|
||||
run: ./mcp-publisher login github-oidc
|
||||
|
||||
- name: Publish to MCP Registry
|
||||
run: ./mcp-publisher publish
|
||||
@@ -0,0 +1,70 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: pypi
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "pypi/**"
|
||||
- ".github/workflows/pypi.yml"
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build-and-verify:
|
||||
name: build & verify wheel
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install build tooling
|
||||
run: python -m pip install --upgrade pip build pytest
|
||||
|
||||
- name: Build toolbox binary and stage it for the wheel
|
||||
# setup.py now expects the binary pre-staged in src/toolbox_server/bin/
|
||||
# instead of downloading from GCS at build time.
|
||||
run: |
|
||||
go build -o pypi/src/toolbox_server/bin/toolbox .
|
||||
chmod +x pypi/src/toolbox_server/bin/toolbox
|
||||
|
||||
- name: Build wheel
|
||||
working-directory: pypi
|
||||
env:
|
||||
# setup.py requires this explicitly; matches the Linux x86_64 binary
|
||||
# built above. Release pipeline sets it per-platform in a loop.
|
||||
TOOLBOX_PLATFORM: manylinux2014_x86_64
|
||||
run: python -m build --wheel
|
||||
|
||||
- name: Install built wheel
|
||||
run: pip install pypi/dist/*.whl
|
||||
|
||||
- name: Verify console script runs
|
||||
run: toolbox-server --help
|
||||
|
||||
- name: Run wrapper tests
|
||||
run: pytest pypi/tests/ -v
|
||||
@@ -0,0 +1,29 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Schedule Reporter
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * *' # Runs at 6 AM every morning
|
||||
|
||||
jobs:
|
||||
run_reporter:
|
||||
permissions:
|
||||
issues: 'write'
|
||||
checks: 'read'
|
||||
contents: 'read'
|
||||
uses: ./.github/workflows/cloud_build_failure_reporter.yml
|
||||
with:
|
||||
trigger_names: "toolbox-test-nightly,toolbox-test-on-merge,toolbox-continuous-release"
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Sync Labels
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: 'read'
|
||||
issues: 'write'
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: micnncim/action-label-syncer@3abd5ab72fda571e69fffd97bd4e0033dd5f495c # v1.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
manifest: .github/labels.yaml
|
||||
@@ -0,0 +1,95 @@
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- "**"
|
||||
- "!docs/**"
|
||||
- "!**.md"
|
||||
- "!.github/**"
|
||||
- ".github/workflows/tests.yaml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**"
|
||||
- "!docs/**"
|
||||
- "!**.md"
|
||||
- "!.github/**"
|
||||
- ".github/workflows/tests.yaml"
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
name: unit tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
fail-fast: false
|
||||
permissions:
|
||||
contents: "read"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
- name: Install dependencies
|
||||
run: go get .
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Run tests with coverage
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
env:
|
||||
GOTOOLCHAIN: go1.25.0+auto
|
||||
run: |
|
||||
source_dir="./internal/sources/*"
|
||||
tool_dir="./internal/tools/*"
|
||||
prompt_dir="./internal/prompts/*"
|
||||
auth_dir="./internal/auth/*"
|
||||
int_test_dir="./tests/*"
|
||||
included_packages=$(go list ./... | grep -v -e "$source_dir" -e "$tool_dir" -e "$prompt_dir" -e "$auth_dir" -e "$int_test_dir")
|
||||
go test -race -cover -coverprofile=coverage.out -v $included_packages
|
||||
go test -race -v ./internal/sources/... ./internal/tools/... ./internal/prompts/... ./internal/auth/...
|
||||
|
||||
- name: Run tests without coverage
|
||||
if: ${{ runner.os != 'Linux' }}
|
||||
run: |
|
||||
go test -race -v ./internal/... ./cmd/...
|
||||
|
||||
- name: Check coverage
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: |
|
||||
FILE_TO_EXCLUDE="github.com/googleapis/mcp-toolbox/internal/server/config.go"
|
||||
ESCAPED_PATH=$(echo "$FILE_TO_EXCLUDE" | sed 's/\//\\\//g; s/\./\\\./g')
|
||||
sed -i "/^${ESCAPED_PATH}:/d" coverage.out
|
||||
total_coverage=$(go tool cover -func=coverage.out | grep "total:" | awk '{print $3}')
|
||||
echo "Total coverage: $total_coverage"
|
||||
coverage_numeric=$(echo "$total_coverage" | sed 's/%//')
|
||||
if (( $(echo "$coverage_numeric < 40" | bc -l) )); then
|
||||
echo "Coverage failure: total coverage($total_coverage) is below 40%."
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,54 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: tests
|
||||
|
||||
# zizmor flags pull_request_target, but this fallback workflow never checks out code.
|
||||
on: # zizmor: ignore[dangerous-triggers]
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/tests.yaml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/tests.yaml"
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "**.md"
|
||||
- ".github/**"
|
||||
- "!.github/workflows/tests.yaml"
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
name: unit tests
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
steps:
|
||||
- name: Skip Tests
|
||||
run: |
|
||||
echo "Skipping unit tests for documentation/config-only changes."
|
||||
echo "This job exists to satisfy the required status check."
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# direnv
|
||||
.envrc
|
||||
|
||||
# vscode
|
||||
.vscode/
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
|
||||
# hugo
|
||||
.hugo/public/
|
||||
.hugo/resources/_gen
|
||||
.hugo/static/pagefind/
|
||||
.hugo_build.lock
|
||||
|
||||
# coverage
|
||||
.coverage
|
||||
|
||||
# python
|
||||
__pycache__/
|
||||
|
||||
# executable
|
||||
mcp-toolbox
|
||||
toolbox
|
||||
toolbox.exe
|
||||
**/test.db
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "docs2/themes/godocs"]
|
||||
path = docs2/themes/godocs
|
||||
url = https://github.com/themefisher/godocs.git
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright 2024 Google LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- errcheck
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unused
|
||||
exclusions:
|
||||
presets:
|
||||
- std-error-handling
|
||||
formatters:
|
||||
enable:
|
||||
- goimports
|
||||
settings:
|
||||
gofmt:
|
||||
rewrite-rules:
|
||||
- pattern: interface{}
|
||||
replacement: any
|
||||
- pattern: a[b:len(a)]
|
||||
replacement: a[b:]
|
||||
@@ -0,0 +1 @@
|
||||
defaults
|
||||
@@ -0,0 +1,5 @@
|
||||
+++
|
||||
date = '{{ .Date }}'
|
||||
draft = true
|
||||
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||
+++
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 34 KiB |
@@ -0,0 +1,22 @@
|
||||
/* ==========================================================================
|
||||
PROJECT SCSS HUB
|
||||
Custom theme overrides and UI components.
|
||||
========================================================================== */
|
||||
|
||||
@import 'components/typography';
|
||||
@import 'components/layout';
|
||||
@import 'components/header';
|
||||
@import 'components/sidebar';
|
||||
@import 'components/callouts';
|
||||
@import 'components/secondary_nav';
|
||||
@import 'components/search';
|
||||
|
||||
@import 'td/code-dark';
|
||||
|
||||
// Make tabs scrollable horizontally instead of wrapping
|
||||
.nav-tabs {
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
$primary: #80a7e9;
|
||||
$secondary: #4484f4;
|
||||
@@ -0,0 +1,26 @@
|
||||
/* ==========================================================================
|
||||
CALLOUTS & NOTICES
|
||||
Styling for alerts, tips, and admonition blocks.
|
||||
========================================================================== */
|
||||
|
||||
.td-content .alert code,
|
||||
.td-content .notice code,
|
||||
.td-content .admonition code {
|
||||
background-color: #ffffff !important;
|
||||
color: rgba(32, 33, 36, 0.95) !important;
|
||||
padding: 0.2rem 0.4rem !important;
|
||||
border-radius: 4px !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05) !important;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
html[data-bs-theme="dark"] .td-content .alert code,
|
||||
body.dark .td-content .alert code,
|
||||
html[data-bs-theme="dark"] .td-content .notice code,
|
||||
body.dark .td-content .notice code,
|
||||
html[data-bs-theme="dark"] .td-content .admonition code,
|
||||
body.dark .td-content .admonition code {
|
||||
background-color: rgba(255, 255, 255, 0.15) !important;
|
||||
color: #ffffff !important;
|
||||
border-color: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/* ==========================================================================
|
||||
HEADER & NAVIGATION
|
||||
Primary navbar, secondary tabbed nav, utilities, and mobile layouts.
|
||||
========================================================================== */
|
||||
|
||||
/* Main Header Structure */
|
||||
header { position: sticky !important; top: 0; z-index: 1060; width: 100%; background-color: var(--bs-body-bg, #ffffff); transform: translateZ(0); }
|
||||
header, .td-navbar { z-index: 1060 !important; }
|
||||
|
||||
.td-navbar {
|
||||
position: relative !important; width: 100% !important;
|
||||
|
||||
/* Header Utility Buttons */
|
||||
.navbar-nav {
|
||||
gap: 0.35rem; align-items: center;
|
||||
|
||||
li.td-light-dark-menu > button,
|
||||
li.nav-item:has(a[href*="github"]) > a,
|
||||
li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a {
|
||||
background-color: transparent !important; background-image: none !important; border: 1.5px solid transparent !important;
|
||||
box-shadow: none !important; outline: none !important; color: rgba(255, 255, 255, 0.85) !important;
|
||||
font-weight: 500 !important; font-size: 0.95rem !important; text-decoration: none !important; white-space: nowrap;
|
||||
display: flex !important; align-items: center !important; justify-content: center !important;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
|
||||
i, svg { font-size: 1.15rem !important; fill: rgba(255, 255, 255, 0.85) !important; transition: all 0.2s ease-in-out !important; }
|
||||
|
||||
&:hover {
|
||||
background-color: #ffffff !important; color: $primary !important;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(255, 255, 255, 0.2) !important;
|
||||
transform: translateY(-1px) !important; opacity: 1;
|
||||
i, svg { fill: $primary !important; opacity: 1; }
|
||||
}
|
||||
&:active { transform: translateY(0px) !important; background-color: rgba(255, 255, 255, 0.15) !important; }
|
||||
}
|
||||
|
||||
li.nav-item:has(a[href*="github"]) > a,
|
||||
li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a {
|
||||
border-radius: 50px !important; padding: 0.35rem 1.1rem !important;
|
||||
i, svg { margin-right: 0.4rem !important; }
|
||||
}
|
||||
|
||||
li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a.dropdown-toggle::after {
|
||||
display: inline-block !important; margin-left: 0.5rem !important; vertical-align: middle !important;
|
||||
border-top: 0.4em solid; border-right: 0.4em solid transparent; border-bottom: 0; border-left: 0.4em solid transparent;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
li.nav-item.show > a.dropdown-toggle::after,
|
||||
a.dropdown-toggle.show::after { transform: rotate(180deg); }
|
||||
|
||||
li.td-light-dark-menu > button {
|
||||
width: 38px !important; height: 38px !important; padding: 0 !important; border-radius: 50% !important;
|
||||
&::after { display: none !important; }
|
||||
i, svg { margin: 0 !important; width: 1.15rem !important; height: 1.15rem !important; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Universal Dropdowns */
|
||||
.dropdown-menu {
|
||||
z-index: 1065 !important; margin-top: 0.6rem !important; background-color: #ffffff !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08) !important; border-radius: 12px !important;
|
||||
box-shadow: 0 12px 34px rgba(0, 0, 0, 0.15) !important; padding: 0.6rem !important;
|
||||
min-width: 200px !important; max-height: 60vh !important; overflow-y: auto !important;
|
||||
scrollbar-width: thin; scrollbar-color: rgba(0, 0, 0, 0.2) rgba(0, 0, 0, 0.05);
|
||||
animation: dropdownFadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards !important; transform-origin: top right;
|
||||
|
||||
&::-webkit-scrollbar { width: 8px; display: block !important; }
|
||||
&::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.05) !important; border-radius: 10px; margin: 5px; }
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.2) !important; border-radius: 10px; border: 2px solid transparent; background-clip: content-box;
|
||||
&:hover { background: rgba(0, 0, 0, 0.35) !important; background-clip: content-box; }
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
border-radius: 8px !important; padding: 0.6rem 1rem !important; color: #495057 !important;
|
||||
margin-bottom: 4px !important; transition: all 0.2s ease !important; display: flex !important;
|
||||
align-items: center !important; font-weight: 500 !important;
|
||||
|
||||
i, svg { margin-right: 12px !important; width: 16px !important; text-align: center; }
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: rgba(68, 132, 244, 0.08) !important; color: #4484f4 !important; transform: translateX(4px) !important;
|
||||
}
|
||||
|
||||
&:not(.active-version):not(.active):not(:has(i)):not(:has(svg)) {
|
||||
opacity: 0.75 !important; font-weight: 400 !important; transition: opacity 0.2s ease !important;
|
||||
&:hover { opacity: 1 !important; }
|
||||
}
|
||||
|
||||
&.active-version, &.active {
|
||||
background-color: rgba(68, 132, 244, 0.08) !important; color: #4484f4 !important; font-weight: 600 !important;
|
||||
position: relative; padding-left: 2rem !important; border-left: 4px solid #4484f4 !important;
|
||||
&:hover { background-color: rgba(68, 132, 244, 0.12) !important; transform: translateX(4px) !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Tabbed Secondary Navbar */
|
||||
#secondary-nav {
|
||||
position: fixed; top: 4rem; left: 0; right: 0; height: 52px; z-index: 1055 !important;
|
||||
background: linear-gradient(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.08)), $primary;
|
||||
box-shadow: inset 0 4px 12px rgba(0, 0, 0, 0.15); border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
display: flex; align-items: center; position: relative !important; top: 0 !important;
|
||||
width: 100%; max-width: 100vw; box-sizing: border-box;
|
||||
|
||||
.container-fluid {
|
||||
display: flex; align-items: center; width: 100%; height: 100%; overflow-x: auto !important; flex-wrap: nowrap !important;
|
||||
scrollbar-width: none; -ms-overflow-style: none; -webkit-overflow-scrolling: touch;
|
||||
&::-webkit-scrollbar { display: none; }
|
||||
}
|
||||
.sec-nav-list {
|
||||
display: flex; margin: 0; padding: 0 1.5rem; list-style: none; gap: 28px;
|
||||
align-items: center; height: 100%; width: auto; flex-shrink: 0 !important; flex-wrap: nowrap; overflow: visible !important;
|
||||
}
|
||||
.sec-nav-icons { display: flex; gap: 1.5rem; list-style: none; margin: 0 0 0 auto; padding: 0 1.5rem 0 2rem; align-items: center; flex-shrink: 0 !important; }
|
||||
li { height: 100%; display: flex; align-items: center; }
|
||||
a {
|
||||
color: rgba(255, 255, 255, 0.7) !important; font-weight: 500; text-decoration: none;
|
||||
font-size: 14.5px; letter-spacing: 0.2px; height: 100%; display: flex; align-items: center;
|
||||
padding: 0 2px; border-bottom: 2px solid transparent; margin-bottom: 0; transition: color 0.15s ease, border-color 0.15s ease; white-space: nowrap;
|
||||
&:hover { color: #ffffff !important; }
|
||||
&.active { color: #ffffff !important; font-weight: 600; border-bottom: 2px solid #ffffff; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Mode Overrides */
|
||||
html[data-bs-theme="dark"], body.dark {
|
||||
.td-navbar .navbar-nav {
|
||||
li.td-light-dark-menu > button,
|
||||
li.nav-item:has(a[href*="github"]) > a,
|
||||
li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a {
|
||||
background-color: transparent !important; box-shadow: none !important; color: #e8eaed !important;
|
||||
i, svg { fill: #e8eaed !important; }
|
||||
&:hover {
|
||||
background-color: #303134 !important; color: #f8f9fa !important; border-color: #8ab4f8 !important;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(138, 180, 248, 0.15) !important;
|
||||
i, svg { fill: #f8f9fa !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
.td-navbar .dropdown-menu {
|
||||
background-color: #202124 !important; border-color: rgba(255, 255, 255, 0.12) !important;
|
||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6) !important; scrollbar-color: rgba(255, 255, 255, 0.2) rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
.td-navbar .dropdown-item {
|
||||
color: #e8eaed !important;
|
||||
&:hover, &:focus { background-color: rgba(138, 180, 248, 0.12) !important; color: #8ab4f8 !important; }
|
||||
&.active-version, &.active {
|
||||
background-color: rgba(138, 180, 248, 0.12) !important; color: #8ab4f8 !important; border-left-color: #8ab4f8 !important;
|
||||
&::before { color: #8ab4f8; }
|
||||
&:hover { background-color: rgba(138, 180, 248, 0.18) !important; }
|
||||
}
|
||||
}
|
||||
#secondary-nav {
|
||||
background: linear-gradient(rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0.15)), $primary; box-shadow: inset 0 4px 15px rgba(0, 0, 0, 0.3); border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
a { color: rgba(255, 255, 255, 0.7) !important; &:hover { color: #ffffff !important; } &.active { color: #ffffff !important; border-bottom-color: #ffffff; } }
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop Adjustments */
|
||||
@media (min-width: 992px) {
|
||||
.td-navbar .custom-pagefind-wrapper {
|
||||
position: relative !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important;
|
||||
width: 100% !important; max-width: 500px !important; margin: 0 !important; z-index: 1060 !important;
|
||||
transition: opacity 0.15s ease-out, transform 0.15s ease-out, box-shadow 0.15s ease-out !important;
|
||||
}
|
||||
.td-navbar .pagefind-ui__drawer { left: 0 !important; right: 0 !important; width: 100% !important; max-width: 100% !important; }
|
||||
}
|
||||
|
||||
/* Mobile Layout & Scaling */
|
||||
@media (max-width: 991.98px) {
|
||||
header, .td-navbar { position: relative !important; }
|
||||
.td-navbar { height: auto !important; padding-bottom: 0.5rem !important; flex-wrap: nowrap !important; justify-content: space-between !important; min-height: 4rem !important; }
|
||||
header .navbar-toggler { display: inline-flex !important; z-index: 1080 !important; margin-left: auto !important; }
|
||||
.td-navbar .container-fluid { display: flex !important; flex-wrap: wrap !important; justify-content: space-between !important; align-items: center !important; }
|
||||
.td-navbar .navbar-nav, .td-navbar .navbar-toggler { flex-direction: row !important; order: 2 !important; width: auto !important; margin-left: auto !important; }
|
||||
.td-navbar .custom-pagefind-wrapper { display: none !important; }
|
||||
|
||||
.td-navbar .dropdown-menu {
|
||||
background-color: #ffffff !important; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15) !important; border: 1px solid rgba(0, 0, 0, 0.08) !important;
|
||||
min-width: 180px !important; padding: 0.5rem !important; position: absolute !important; z-index: 1065 !important;
|
||||
}
|
||||
.td-navbar .navbar-nav .dropdown-menu { right: 0 !important; left: auto !important; margin-top: 0.5rem !important; }
|
||||
.td-navbar .dropdown-item { padding: 0.6rem 1rem !important; font-size: 0.95rem !important; }
|
||||
|
||||
#secondary-nav { position: relative !important; top: 0 !important; margin-top: 0 !important; height: auto !important; min-height: 52px; padding: 10px 0 !important; z-index: 1050 !important; clear: both !important; }
|
||||
#secondary-nav .container-fluid { padding: 0 15px !important; }
|
||||
#secondary-nav .sec-nav-list { padding: 0 !important; gap: 12px !important; }
|
||||
#secondary-nav .sec-nav-icons { margin-left: 20px !important; padding: 0 !important; }
|
||||
#secondary-nav li { height: auto !important; }
|
||||
#secondary-nav a { height: 34px !important; padding: 0 12px !important; background: rgba(0, 0, 0, 0.1); border-radius: 20px; border-bottom: none !important; }
|
||||
#secondary-nav a.active { background: #ffffff !important; color: $primary !important; }
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.td-navbar .navbar-nav { gap: 0.25rem !important; }
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="github"], a[href*="Releases"]) span { display: none !important; }
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="github"]) > a,
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a { padding: 0.25rem 0.6rem !important; font-size: 0.85rem !important; }
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="github"]) > a i,
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="github"]) > a svg,
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a i,
|
||||
.td-navbar .navbar-nav li.nav-item:has(a[href*="Releases"], .dropdown-toggle):not(.td-light-dark-menu) > a svg { font-size: 1rem !important; margin-right: 0 !important; }
|
||||
.td-navbar .navbar-nav a.dropdown-toggle::after { display: none !important; }
|
||||
.td-navbar .navbar-nav li.td-light-dark-menu > button { width: 32px !important; height: 32px !important; }
|
||||
.td-navbar .navbar-nav li.td-light-dark-menu > button i,
|
||||
.td-navbar .navbar-nav li.td-light-dark-menu > button svg { width: 1rem !important; height: 1rem !important; }
|
||||
|
||||
#secondary-nav a { font-size: 13px !important; padding: 0 10px !important; height: 30px !important; }
|
||||
#secondary-nav .sec-nav-list { gap: 8px !important; }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/* ==========================================================================
|
||||
LAYOUT ALIGNMENT & STACKING CONTEXTS
|
||||
Manages global spacing and z-index hierarchy.
|
||||
========================================================================== */
|
||||
|
||||
/*Global Reset */
|
||||
body {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
/* Zero-Spacing Elements */
|
||||
.td-main, .td-outer, .td-sidebar {
|
||||
margin-top: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
/* =Elements Requiring Top Padding */
|
||||
main[role="main"], #td-section-nav, .td-sidebar-toc {
|
||||
margin-top: 0 !important;
|
||||
padding-top: 1.5rem !important;
|
||||
}
|
||||
|
||||
/* Z-Index Hierarchy */
|
||||
.td-main, .td-content, .td-sidebar-toc, .td-page-meta, .td-toc {
|
||||
position: relative;
|
||||
z-index: 10 !important;
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/* ==========================================================================
|
||||
PAGEFIND SEARCH STYLES & MODAL
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--pagefind-ui-scale: 0.9;
|
||||
--pagefind-ui-primary: #0d6efd;
|
||||
}
|
||||
|
||||
@keyframes simpleSlideDown {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* Base Modal Backdrop & Body State */
|
||||
#global-search-backdrop {
|
||||
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
|
||||
z-index: 2147483646 !important; /* Max value minus 1 */
|
||||
opacity: 0; pointer-events: none; transition: none;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
|
||||
&.active { opacity: 1; pointer-events: auto; }
|
||||
}
|
||||
|
||||
body.global-search-active {
|
||||
/* Explicitly target Docsy's layout structure to avoid blurring injected modals */
|
||||
header, main, footer, .td-main, .td-outer, .td-sidebar, .td-sidebar-toc {
|
||||
filter: blur(6px) !important;
|
||||
opacity: 0.4 !important;
|
||||
pointer-events: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main Component Scoping */
|
||||
body {
|
||||
.custom-pagefind-wrapper {
|
||||
position: relative;
|
||||
z-index: 9999 !important;
|
||||
|
||||
/* Modal Pop-out State */
|
||||
&.active-modal {
|
||||
position: fixed !important;
|
||||
top: 12vh !important;
|
||||
left: 50% !important;
|
||||
transform: translateX(-50%) !important;
|
||||
width: 90vw !important;
|
||||
max-width: 650px !important;
|
||||
z-index: 2147483647 !important; /* Absolute max value possible */
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
|
||||
.pagefind-ui__drawer {
|
||||
position: absolute !important;
|
||||
top: 100% !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
max-height: 70vh !important;
|
||||
z-index: 999999 !important;
|
||||
animation: simpleSlideDown 0.2s ease-out forwards !important;
|
||||
overflow-y: auto !important;
|
||||
overscroll-behavior: contain !important;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
|
||||
box-shadow: none !important;
|
||||
|
||||
&::-webkit-scrollbar { width: 6px; }
|
||||
&::-webkit-scrollbar-track { background: transparent; }
|
||||
&::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.2); border-radius: 10px; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Pagefind Internal Element Overrides */
|
||||
.pagefind-ui__form {
|
||||
--pagefind-ui-text: #ffffff !important;
|
||||
position: relative !important;
|
||||
box-sizing: border-box !important;
|
||||
&:focus-within { --pagefind-ui-text: #4484f4 !important; }
|
||||
&::before {
|
||||
top: 50% !important; transform: translateY(-50%) !important; margin-top: 0 !important;
|
||||
width: 18px !important; height: 18px !important; left: 14px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pagefind-ui__search-input {
|
||||
background-color: rgba(0, 0, 0, 0.1) !important;
|
||||
border: 1.5px solid transparent !important;
|
||||
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.15) !important;
|
||||
color: #ffffff !important; border-radius: 50px !important; font-weight: 400 !important;
|
||||
transition: all 0.2s ease-in-out !important; height: 38px !important; font-size: 0.95rem !important;
|
||||
padding-left: 40px !important; padding-right: 36px !important; box-sizing: border-box !important;
|
||||
|
||||
&::placeholder { color: #ffffff !important; opacity: 0.9 !important; }
|
||||
|
||||
&:focus {
|
||||
background-color: #ffffff !important; border-color: #4484f4 !important; color: #212529 !important; outline: 0 !important;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1), 0 0 0 4px rgba(68, 132, 244, 0.15) !important;
|
||||
&::placeholder { color: #6c757d !important; opacity: 1 !important; }
|
||||
}
|
||||
}
|
||||
|
||||
.pagefind-ui__search-clear {
|
||||
position: absolute !important; top: 50% !important; transform: translateY(-50%) !important; right: 12px !important;
|
||||
width: 24px !important; height: 24px !important; min-height: 24px !important; padding: 0 !important; margin: 0 !important;
|
||||
background: transparent !important; border: none !important; outline: none !important; box-shadow: none !important;
|
||||
font-size: 0 !important; color: transparent !important; display: flex !important; align-items: center !important;
|
||||
justify-content: center !important; overflow: hidden !important; cursor: pointer !important; z-index: 99 !important;
|
||||
|
||||
&::after {
|
||||
content: "\00d7" !important; font-size: 22px !important; font-weight: 300 !important;
|
||||
line-height: 1 !important; color: var(--pagefind-ui-text) !important; display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pagefind-ui__drawer {
|
||||
position: absolute !important; top: 100% !important; width: 500px !important;
|
||||
max-width: calc(100vw - 2rem) !important; margin-top: 12px !important; z-index: 99999 !important;
|
||||
background-color: #ffffff !important; border: 1px solid #dee2e6 !important; border-radius: 12px !important;
|
||||
padding: 1.5rem !important;
|
||||
max-height: 80vh !important; overflow-y: auto !important; overscroll-behavior: contain !important;
|
||||
|
||||
box-sizing: border-box !important;
|
||||
overflow-y: auto !important;
|
||||
overflow-x: hidden !important;
|
||||
overflow-wrap: break-word !important;
|
||||
word-wrap: break-word !important;
|
||||
word-break: break-word !important;
|
||||
white-space: normal !important;
|
||||
|
||||
mark {
|
||||
background-color: rgba(68, 132, 244, 0.15) !important; color: #1a73e8 !important; padding: 0 3px !important;
|
||||
border-radius: 3px !important; font-weight: 600 !important; box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.pagefind-ui__result-title, .pagefind-ui__result-excerpt, .pagefind-ui__message {
|
||||
color: #212529 !important;
|
||||
}
|
||||
|
||||
.pagefind-ui__button { margin-top: 1rem !important; }
|
||||
.pagefind-ui__hidden { display: none !important; }
|
||||
}
|
||||
|
||||
|
||||
/* =======================================================
|
||||
RESPONSIVE LAYOUTS
|
||||
======================================================= */
|
||||
|
||||
body {
|
||||
// DESKTOP SEARCH BAR
|
||||
@media (min-width: 992px) {
|
||||
.td-navbar .custom-pagefind-wrapper:not(.active-modal) {
|
||||
position: absolute !important;
|
||||
left: 50% !important;
|
||||
top: 50% !important;
|
||||
transform: translate(-50%, -50%) !important;
|
||||
width: 100% !important;
|
||||
max-width: 500px !important;
|
||||
margin: 0 !important;
|
||||
z-index: 1060 !important;
|
||||
}
|
||||
.td-navbar .pagefind-ui__drawer { left: 0 !important; right: 0 !important; width: 100% !important; max-width: 100% !important; }
|
||||
.td-sidebar .td-search, .td-sidebar .custom-pagefind-wrapper { display: none !important; }
|
||||
}
|
||||
|
||||
// MOBILE NATIVE STACK
|
||||
@media (max-width: 991.98px) {
|
||||
.td-navbar .custom-pagefind-wrapper, .td-navbar .container-fluid > div.d-none.d-lg-block { display: none !important; }
|
||||
.td-sidebar .td-search, .td-sidebar .custom-pagefind-wrapper:not(.active-modal) {
|
||||
display: block !important; width: 100% !important; max-width: 100% !important; margin-top: 1rem !important; margin-bottom: 1rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
// LEFT SIDEBAR TOGGLE ALIGNMENT & MOBILE MODAL RESIZING
|
||||
@media (max-width: 767.98px) {
|
||||
.td-sidebar__search {
|
||||
display: flex !important; flex-direction: row !important; align-items: center !important; justify-content: space-between !important;
|
||||
width: 100% !important; margin-top: 60px !important; margin-bottom: 1rem !important; padding: 0 15px !important; z-index: 10 !important;
|
||||
|
||||
.custom-pagefind-wrapper:not(.active-modal) { width: 75% !important; flex: 0 0 75% !important; margin: 0 !important; }
|
||||
}
|
||||
|
||||
|
||||
.custom-pagefind-wrapper.active-modal {
|
||||
top: 15px !important;
|
||||
width: calc(100vw - 30px) !important;
|
||||
|
||||
.pagefind-ui__drawer {
|
||||
max-height: calc(100dvh - 100px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.td-sidebar__toggle {
|
||||
display: flex !important; align-items: center !important; justify-content: flex-end !important; width: 20% !important;
|
||||
height: auto !important; background-color: transparent !important; border: none !important; box-shadow: none !important;
|
||||
color: inherit !important; font-size: 1rem !important; padding: 0 !important;
|
||||
&::after { content: none !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =======================================================
|
||||
DARK MODE STYLING
|
||||
======================================================= */
|
||||
|
||||
html[data-bs-theme="dark"], body.td-dark {
|
||||
#global-search-backdrop { background-color: rgba(0, 0, 0, 0.8); }
|
||||
|
||||
.pagefind-ui__form { --pagefind-ui-text: #ffffff !important; &:focus-within { --pagefind-ui-text: #8ab4f8 !important; } }
|
||||
|
||||
.pagefind-ui__search-input {
|
||||
background-color: rgba(0, 0, 0, 0.2) !important; border-color: transparent !important;
|
||||
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.3) !important; color: #ffffff !important;
|
||||
|
||||
&::placeholder { color: #ffffff !important; opacity: 0.9 !important; }
|
||||
|
||||
&:focus {
|
||||
background-color: #303134 !important; border-color: #8ab4f8 !important; color: #f8f9fa !important;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3), 0 0 0 4px rgba(138, 180, 248, 0.15) !important;
|
||||
&::placeholder { color: #e8eaed !important; opacity: 1 !important; }
|
||||
}
|
||||
}
|
||||
|
||||
.pagefind-ui__drawer {
|
||||
background-color: #202124 !important; border-color: #3c4043 !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
mark { background-color: rgba(138, 180, 248, 0.2) !important; color: #8ab4f8 !important; }
|
||||
}
|
||||
|
||||
.pagefind-ui__result-title, .pagefind-ui__result-excerpt, .pagefind-ui__message { color: #e8eaed !important; }
|
||||
|
||||
.pagefind-ui__button { color: #e8eaed !important; border-color: #5f6368 !important; background-color: #303134 !important; }
|
||||
|
||||
// Mobile sidebar toggle dark mode
|
||||
@media (max-width: 767.98px) {
|
||||
.td-sidebar__toggle { color: #e8eaed !important; background-color: transparent !important; border: none !important; &:hover { background-color: transparent !important; opacity: 0.8 !important; } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* ==========================================================================
|
||||
Secondary Navigation Header Styles
|
||||
========================================================================== */
|
||||
|
||||
/* Base Layout */
|
||||
.sec-nav-container { display: flex; align-items: center; height: 100%; width: 100%; overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none; }
|
||||
.sec-nav-container::-webkit-scrollbar { display: none; }
|
||||
.sec-nav-list { display: flex; align-items: center; list-style: none; padding: 0; margin: 0; gap: 1.25rem; flex-shrink: 0; }
|
||||
.sec-nav-icons { display: flex; gap: 1.5rem; list-style: none; margin: 0 0 0 auto; padding: 0 1rem 0 2rem; align-items: center; flex-shrink: 0; }
|
||||
|
||||
/* Typography & Links */
|
||||
.sec-nav-item-left { display: flex; align-items: center; gap: 0.4rem; font-size: 0.95rem; font-weight: 500; color: rgba(255, 255, 255, 0.75) !important; text-decoration: none; transition: all 0.2s; white-space: nowrap; height: 100%; border-bottom: 2px solid transparent; padding-bottom: 2px; }
|
||||
.sec-nav-item-right { display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem; font-weight: 700; color: rgba(255, 255, 255, 0.85) !important; text-decoration: none; transition: all 0.2s; white-space: nowrap; }
|
||||
|
||||
/* Active States */
|
||||
.sec-nav-list a:hover, .sec-nav-list a.active { color: #ffffff !important; opacity: 1; }
|
||||
.sec-nav-list a.active { font-weight: 600; border-bottom: 2px solid #ffffff !important; }
|
||||
.sec-nav-icons a:hover { color: #ffffff !important; opacity: 1; }
|
||||
|
||||
/* Icons */
|
||||
.pop-icon { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 8px; font-size: 1rem; transition: opacity 0.2s ease; }
|
||||
.pop-icon.fa-discord { background: #5865F2 !important; color: #ffffff !important; box-shadow: 0 4px 10px rgba(88, 101, 242, 0.35) !important; }
|
||||
.pop-icon.fa-medium { background: #000000 !important; color: #ffffff !important; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25) !important; }
|
||||
.sec-nav-icons a:hover .pop-icon { opacity: 0.85 !important; }
|
||||
|
||||
/* Dark Mode Overrides */
|
||||
html[data-bs-theme="dark"] .sec-nav-item-left { color: rgba(255, 255, 255, 0.75) !important; }
|
||||
html[data-bs-theme="dark"] .sec-nav-item-right { color: rgba(255, 255, 255, 0.85) !important; }
|
||||
html[data-bs-theme="dark"] .sec-nav-list a.active { color: #ffffff !important; background-color: transparent !important; border-bottom: 2px solid #ffffff !important; font-weight: 600 !important; }
|
||||
html[data-bs-theme="dark"] .pop-icon.fa-discord { background: #5865F2 !important; color: #ffffff !important; box-shadow: 0 4px 10px rgba(88, 101, 242, 0.35) !important; }
|
||||
html[data-bs-theme="dark"] .pop-icon.fa-medium { background: #f8f9fa !important; color: #202124 !important; box-shadow: 0 4px 10px rgba(255, 255, 255, 0.15) !important; }
|
||||
html[data-bs-theme="dark"] .dropdown-item.active-version { color: #8ab4f8 !important; background-color: rgba(138, 180, 248, 0.15) !important; border-left-color: #8ab4f8 !important; }
|
||||
|
||||
/* Responsive Layout */
|
||||
@media (max-width: 991.98px) {
|
||||
.sec-nav-icons { margin-left: 0; padding-left: 1rem; }
|
||||
.sec-nav-list { gap: 1rem; }
|
||||
.sec-nav-item-left, .sec-nav-item-right { font-size: 0.9rem; }
|
||||
html[data-bs-theme="dark"] #secondary-nav .sec-nav-list a.active,
|
||||
body.dark #secondary-nav .sec-nav-list a.active {
|
||||
color: #4484f4 !important; background-color: #ffffff !important; border-bottom: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/* ==========================================================================
|
||||
SIDEBARS (LEFT NAVIGATION & RIGHT TOC)
|
||||
Handles positioning, tree navigation, and page metadata.
|
||||
========================================================================== */
|
||||
|
||||
/* =================================================================
|
||||
LAYOUT & SCROLLING
|
||||
================================================================= */
|
||||
@media (min-width: 768px) {
|
||||
.td-sidebar, .td-sidebar-toc {
|
||||
position: sticky !important;
|
||||
top: calc(4rem + 52px) !important;
|
||||
height: calc(100vh - 4rem - 52px) !important;
|
||||
overflow-y: auto !important;
|
||||
padding-bottom: 2rem !important;
|
||||
scrollbar-width: none; -ms-overflow-style: none;
|
||||
&::-webkit-scrollbar { display: none; }
|
||||
}
|
||||
|
||||
.td-sidebar-toc .td-toc, .td-sidebar-toc .td-page-meta {
|
||||
position: static !important; top: auto !important;
|
||||
height: auto !important; overflow-y: visible !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
LEFT SIDEBAR NAVIGATION
|
||||
================================================================= */
|
||||
.td-sidebar__search {
|
||||
display: none !important; padding: 0 !important; margin: 0 !important;
|
||||
height: 0 !important; border: none !important;
|
||||
}
|
||||
.td-sidebar-nav, #td-section-nav { margin-top: 1.5rem !important; padding-top: 0 !important; }
|
||||
|
||||
#td-section-nav {
|
||||
.ul-1 > li:not(.active-path), > .ul-0 > li > a.tree-root { display: none !important; }
|
||||
> .ul-0 > li > ul.ul-1 { margin-top: 0 !important; padding-top: 0 !important; padding-left: 0 !important; margin-left: -1rem !important; }
|
||||
|
||||
.ul-1 > li.active-path {
|
||||
> a, > label, > input { display: none !important; }
|
||||
> ul { padding-left: 0 !important; margin-top: 0 !important; margin-left: -1rem !important; }
|
||||
> ul > li > a, > ul > li > label > a { padding-left: 0 !important; }
|
||||
}
|
||||
}
|
||||
|
||||
.td-sidebar .form-control {
|
||||
font-family: var(--bs-font-sans-serif) !important; border: 1px solid transparent;
|
||||
background-color: #f1f3f4; border-radius: 8px; padding: 0.5rem 1rem;
|
||||
transition: all 0.2s ease; font-size: 0.9rem;
|
||||
&:focus { background-color: #ffffff; border-color: #4484f4; box-shadow: 0 0 0 3px rgba(68, 132, 244, 0.2); }
|
||||
}
|
||||
|
||||
.td-sidebar-nav {
|
||||
li > label, li > a {
|
||||
font-family: var(--bs-font-sans-serif) !important; font-size: 0.95rem !important; font-weight: 400 !important;
|
||||
display: flex !important; align-items: center; justify-content: space-between; width: 100%;
|
||||
padding: 0.45rem 0.75rem !important; margin-bottom: 2px; border-radius: 6px; color: #5f6368 !important;
|
||||
transition: all 0.15s ease-in-out; cursor: pointer;
|
||||
|
||||
&:hover { background-color: #f1f3f4; color: #202124 !important; text-decoration: none; }
|
||||
&.active { color: #4484f4 !important; background-color: transparent !important; font-weight: 600 !important; }
|
||||
}
|
||||
|
||||
li.active-path > label, li.active-path > a:not(.active) {
|
||||
color: #4484f4 !important; background-color: transparent !important; font-weight: 600 !important;
|
||||
}
|
||||
|
||||
li > label > a { padding: 0 !important; margin: 0 !important; color: inherit !important; flex-grow: 1; }
|
||||
.with-child > label::after { margin-left: 0.5rem; }
|
||||
}
|
||||
|
||||
/* Left Sidebar Dark Mode */
|
||||
html[data-bs-theme="dark"], body.dark {
|
||||
.td-sidebar-nav li > label, .td-sidebar-nav li > a {
|
||||
color: #9aa0a6 !important;
|
||||
&:hover { background-color: rgba(255, 255, 255, 0.05); color: #e8eaed !important; }
|
||||
&.active { color: #8ab4f8 !important; }
|
||||
}
|
||||
.td-sidebar-nav li.active-path > label, .td-sidebar-nav li.active-path > a:not(.active) { color: #8ab4f8 !important; }
|
||||
|
||||
.td-sidebar .form-control {
|
||||
background-color: #303134; color: #e8eaed;
|
||||
&:focus { background-color: #202124; border-color: #8ab4f8; box-shadow: 0 0 0 3px rgba(138, 180, 248, 0.2); }
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
INTEGRATIONS SIDEBAR LOCKS (Tools & Samples Roots Only)
|
||||
================================================================= */
|
||||
|
||||
/* LOCK ALL sections inside the integrations directory */
|
||||
a.td-sidebar-link__section[href*="/integrations/"] {
|
||||
pointer-events: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
/* UNLOCK the top-level "/integrations/" root folder itself so it can be toggled */
|
||||
a.td-sidebar-link__section[href$="/integrations/"] {
|
||||
pointer-events: auto !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
/* UNLOCK any nested sub-folders inside tools or samples (if they exist) */
|
||||
a.td-sidebar-link__section[href*="/integrations/"][href*="/tools/"],
|
||||
a.td-sidebar-link__section[href*="/integrations/"][href*="/samples/"] {
|
||||
pointer-events: auto !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
/* RE-LOCK exactly the "tools/" and "samples/" parent folders */
|
||||
a.td-sidebar-link__section[href*="/integrations/"][href$="/tools/"],
|
||||
a.td-sidebar-link__section[href*="/integrations/"][href$="/samples/"] {
|
||||
pointer-events: none !important;
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
RIGHT SIDEBAR (TABLE OF CONTENTS)
|
||||
================================================================= */
|
||||
.td-sidebar-toc, .td-page-meta {
|
||||
&, a, li, span, #TableOfContents {
|
||||
font-family: var(--bs-font-sans-serif) !important; font-weight: 400 !important; letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 0.95rem !important; color: #5f6368 !important; text-decoration: none !important;
|
||||
display: flex !important; align-items: center; padding: 0.45rem 0.75rem !important;
|
||||
margin-bottom: 2px; border-radius: 6px; transition: all 0.15s ease-in-out;
|
||||
|
||||
i, svg { color: inherit !important; fill: currentColor !important; margin-right: 8px; }
|
||||
&:hover { background-color: #f1f3f4; color: #202124 !important; }
|
||||
&.active { color: #4484f4 !important; background-color: transparent !important; font-weight: 600 !important; }
|
||||
}
|
||||
}
|
||||
|
||||
.td-sidebar-toc #TableOfContents {
|
||||
li a.active { font-weight: 600 !important; }
|
||||
ul { padding-left: 0; list-style: none; margin: 0; }
|
||||
ul ul { padding-left: 1rem; margin-left: 0.75rem; border-left: 1px solid rgba(0,0,0,0.05); }
|
||||
}
|
||||
|
||||
.td-page-meta {
|
||||
margin-bottom: 1.5rem !important; padding-bottom: 1rem; border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
/* Right Sidebar Dark Mode */
|
||||
html[data-bs-theme="dark"], body.dark {
|
||||
.td-sidebar-toc a, .td-page-meta a {
|
||||
color: #9aa0a6 !important;
|
||||
&:hover { background-color: rgba(255, 255, 255, 0.05); color: #e8eaed !important; }
|
||||
&.active { color: #8ab4f8 !important; }
|
||||
}
|
||||
.td-page-meta { border-bottom-color: rgba(255,255,255,0.1); }
|
||||
.td-sidebar-toc #TableOfContents ul ul { border-left-color: rgba(255,255,255,0.1); }
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
HIDe ELEMENTS (Tags & Meta)
|
||||
================================================================= */
|
||||
.td-toc, .td-page-meta {
|
||||
.taxonomy, .td-tags, [class*="taxonomy"], h5.taxonomy-tree-header, ul.taxonomy-terms {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* ==========================================================================
|
||||
GLOBAL TYPOGRAPHY & SCALING
|
||||
Defines core fonts, root variables, and base element typography.
|
||||
========================================================================== */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Readex+Pro:wght@400;500;600;700&display=swap');
|
||||
|
||||
html {
|
||||
font-size: 16px !important;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bs-font-sans-serif: 'Readex Pro', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif) !important;
|
||||
--bs-font-monospace: 'JetBrains Mono', SFMono-Regular, Menlo, Monaco, Consolas, monospace !important;
|
||||
--header-offset: 50px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--bs-font-sans-serif) !important;
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.td-main, .td-sidebar, .navbar, .td-content {
|
||||
font-family: var(--bs-font-sans-serif) !important;
|
||||
}
|
||||
|
||||
code, pre, kbd, samp {
|
||||
font-family: var(--bs-font-monospace) !important;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
h1[id], h2[id], h3[id], h4[id], h5[id], h6[id] {
|
||||
scroll-margin-top: calc(var(--header-offset) + 10px) !important;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
data_sources:
|
||||
- AlloyDB
|
||||
- BigQuery
|
||||
- Looker
|
||||
- Neo4j
|
||||
- Snowflake
|
||||
|
||||
languages:
|
||||
- Go
|
||||
- JavaScript
|
||||
- Python
|
||||
|
||||
frameworks:
|
||||
- ADK
|
||||
- Genkit
|
||||
- Google GenAI
|
||||
- LangChain
|
||||
- LlamaIndex
|
||||
- OpenAI
|
||||
|
||||
categories:
|
||||
- Agent
|
||||
- Claude Desktop
|
||||
- Gemini CLI
|
||||
- Google Cloud Run
|
||||
- Local
|
||||
- MCP Inspector
|
||||
- OAuth
|
||||
- Pre & Post Processing
|
||||
- Prompts
|
||||
- Quickstart
|
||||
@@ -0,0 +1,8 @@
|
||||
module github.com/googleapis/mcp-toolbox
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require (
|
||||
github.com/google/docsy v0.12.0 // indirect
|
||||
github.com/martignoni/hugo-notice v0.0.0-20240707105359-40327ac00cc4 // indirect
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
github.com/FortAwesome/Font-Awesome v0.0.0-20241216213156-af620534bfc3/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
|
||||
github.com/google/docsy v0.12.0 h1:CddZKL39YyJzawr8GTVaakvcUTCJRAAYdz7W0qfZ2P4=
|
||||
github.com/google/docsy v0.12.0/go.mod h1:1bioDqA493neyFesaTvQ9reV0V2vYy+xUAnlnz7+miM=
|
||||
github.com/martignoni/hugo-notice v0.0.0-20240707105359-40327ac00cc4 h1:lxS0B1ta9/uW+orrnvsGHMCC0TgN4DymEgdlb0PL/uU=
|
||||
github.com/martignoni/hugo-notice v0.0.0-20240707105359-40327ac00cc4/go.mod h1:MIQPOMgEcbyRC0gNLzQFSgrS+wIy3RuQ/HbaZYtTOKU=
|
||||
github.com/twbs/bootstrap v5.3.6+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=
|
||||
@@ -0,0 +1,147 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
title = 'MCP Toolbox for Databases'
|
||||
relativeURLs = false
|
||||
|
||||
locale = 'en-us'
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = false
|
||||
|
||||
enableGitInfo = true
|
||||
enableRobotsTXT = true
|
||||
|
||||
ignoreFiles = ["quickstart/shared", "quickstart/python", "quickstart/js", "quickstart/go"]
|
||||
|
||||
[security.node]
|
||||
[security.node.permissions]
|
||||
allowChildProcess = ["tailwindcss", "postcss"]
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
label = "English"
|
||||
weight = 1
|
||||
|
||||
[module]
|
||||
proxy = "direct"
|
||||
[module.hugoVersion]
|
||||
extended = true
|
||||
min = "0.146.0"
|
||||
[[module.mounts]]
|
||||
source = "../docs/en"
|
||||
target = 'content'
|
||||
[[module.imports]]
|
||||
path = "github.com/google/docsy"
|
||||
disable = false
|
||||
[[module.imports]]
|
||||
path = "github.com/martignoni/hugo-notice"
|
||||
|
||||
[params]
|
||||
description = "MCP Toolbox for Databases is an open source MCP server for databases. It enables you to develop tools easier, faster, and more securely by handling the complexities such as connection pooling, authentication, and more."
|
||||
copyright = "Google LLC"
|
||||
github_repo = "https://github.com/googleapis/mcp-toolbox"
|
||||
github_project_repo = "https://github.com/googleapis/mcp-toolbox"
|
||||
github_subdir = "docs"
|
||||
offlineSearch = false
|
||||
version_menu = "Releases"
|
||||
releases_url = "/releases.releases"
|
||||
global_logo_url = "/"
|
||||
pagefind = true
|
||||
version = "dev"
|
||||
[params.ui]
|
||||
ul_show = 100
|
||||
showLightDarkModeMenu = true
|
||||
breadcrumb_disable = false
|
||||
sidebar_menu_foldable = true
|
||||
sidebar_menu_compact = false
|
||||
[params.ui.feedback]
|
||||
enable = true
|
||||
yes = 'Glad to hear it! Please <a href="https://github.com/googleapis/mcp-toolbox/issues/new">tell us how we can improve</a>.'
|
||||
no = 'Sorry to hear that. Please <a href="https://github.com/googleapis/mcp-toolbox/issues/new">tell us how we can improve</a>.'
|
||||
[params.ui.readingtime]
|
||||
enable = true
|
||||
|
||||
[[params.versions]]
|
||||
version = "dev"
|
||||
url = "https://mcp-toolbox.dev/dev/"
|
||||
|
||||
# Add a new version block here before every release
|
||||
# The order of versions in this file is mirrored into the dropdown
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.6.0"
|
||||
url = "https://mcp-toolbox.dev/v1.6.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.5.0"
|
||||
url = "https://mcp-toolbox.dev/v1.5.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.4.0"
|
||||
url = "https://mcp-toolbox.dev/v1.4.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.3.0"
|
||||
url = "https://mcp-toolbox.dev/v1.3.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.2.0"
|
||||
url = "https://mcp-toolbox.dev/v1.2.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.1.0"
|
||||
url = "https://mcp-toolbox.dev/v1.1.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.0.0"
|
||||
url = "https://mcp-toolbox.dev/v1.0.0/"
|
||||
|
||||
[[menu.main]]
|
||||
name = "GitHub"
|
||||
weight = 50
|
||||
url = "https://github.com/googleapis/mcp-toolbox"
|
||||
pre = "<i class='fa-brands fa-github'></i>"
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
unsafe= true
|
||||
|
||||
[markup.highlight]
|
||||
noClasses = false
|
||||
style = "tango"
|
||||
|
||||
[outputFormats]
|
||||
[outputFormats.LLMS]
|
||||
mediaType = "text/plain"
|
||||
baseName = "llms"
|
||||
isPlainText = true
|
||||
root = true
|
||||
[outputFormats.LLMS-FULL]
|
||||
mediaType = "text/plain"
|
||||
baseName = "llms-full"
|
||||
isPlainText = true
|
||||
root = true
|
||||
[outputFormats.releases]
|
||||
baseName = 'releases'
|
||||
isPlainText = true
|
||||
mediaType = 'text/releases'
|
||||
|
||||
[mediaTypes."text/releases"]
|
||||
suffixes = ["releases"]
|
||||
|
||||
[outputs]
|
||||
home = ["HTML", "RSS", "LLMS", "LLMS-FULL", "releases"]
|
||||
|
||||
[services]
|
||||
[services.googleAnalytics]
|
||||
id = "G-GLSV9KD8BF"
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
title = 'MCP Toolbox for Databases'
|
||||
relativeURLs = false
|
||||
|
||||
languageCode = 'en-us'
|
||||
defaultContentLanguage = "en"
|
||||
defaultContentLanguageInSubdir = false
|
||||
|
||||
enableGitInfo = true
|
||||
enableRobotsTXT = true
|
||||
|
||||
ignoreFiles = ["quickstart/shared", "quickstart/python", "quickstart/js", "quickstart/go"]
|
||||
|
||||
[languages]
|
||||
[languages.en]
|
||||
languageName ="English"
|
||||
weight = 1
|
||||
|
||||
[module]
|
||||
proxy = "direct"
|
||||
[module.hugoVersion]
|
||||
extended = true
|
||||
min = "0.146.0"
|
||||
[[module.mounts]]
|
||||
source = "../docs/en"
|
||||
target = 'content'
|
||||
[[module.imports]]
|
||||
path = "github.com/google/docsy"
|
||||
disable = false
|
||||
[[module.imports]]
|
||||
path = "github.com/martignoni/hugo-notice"
|
||||
|
||||
[params]
|
||||
description = "MCP Toolbox for Databases is an open source MCP server for databases. It enables you to develop tools easier, faster, and more securely by handling the complexities such as connection pooling, authentication, and more."
|
||||
copyright = "Google LLC"
|
||||
github_repo = "https://github.com/googleapis/mcp-toolbox"
|
||||
github_project_repo = "https://github.com/googleapis/mcp-toolbox"
|
||||
github_subdir = "docs"
|
||||
offlineSearch = false
|
||||
version_menu = "Releases"
|
||||
releases_url = "/mcp-toolbox/releases.releases"
|
||||
global_logo_url = "/mcp-toolbox/"
|
||||
version = "dev"
|
||||
pagefind = true
|
||||
[params.ui]
|
||||
ul_show = 100
|
||||
showLightDarkModeMenu = true
|
||||
breadcrumb_disable = false
|
||||
sidebar_menu_foldable = true
|
||||
sidebar_menu_compact = false
|
||||
[params.ui.feedback]
|
||||
enable = true
|
||||
yes = 'Glad to hear it! Please <a href="https://github.com/googleapis/mcp-toolbox/issues/new">tell us how we can improve</a>.'
|
||||
no = 'Sorry to hear that. Please <a href="https://github.com/googleapis/mcp-toolbox/issues/new">tell us how we can improve</a>.'
|
||||
[params.ui.readingtime]
|
||||
enable = true
|
||||
[params.mermaid]
|
||||
version = "11.16.0"
|
||||
|
||||
[[params.versions]]
|
||||
version = "dev"
|
||||
url = "https://mcp-toolbox.dev/dev/"
|
||||
|
||||
# Add a new version block here before every release
|
||||
# The order of versions in this file is mirrored into the dropdown
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.6.0"
|
||||
url = "https://mcp-toolbox.dev/v1.6.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.5.0"
|
||||
url = "https://mcp-toolbox.dev/v1.5.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.4.0"
|
||||
url = "https://mcp-toolbox.dev/v1.4.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.3.0"
|
||||
url = "https://mcp-toolbox.dev/v1.3.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.2.0"
|
||||
url = "https://mcp-toolbox.dev/v1.2.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.1.0"
|
||||
url = "https://mcp-toolbox.dev/v1.1.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v1.0.0"
|
||||
url = "https://mcp-toolbox.dev/v1.0.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.32.0"
|
||||
url = "https://mcp-toolbox.dev/v0.32.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.31.0"
|
||||
url = "https://mcp-toolbox.dev/v0.31.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.30.0"
|
||||
url = "https://mcp-toolbox.dev/v0.30.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.29.0"
|
||||
url = "https://mcp-toolbox.dev/v0.29.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.28.0"
|
||||
url = "https://mcp-toolbox.dev/v0.28.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.27.0"
|
||||
url = "https://mcp-toolbox.dev/v0.27.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.26.0"
|
||||
url = "https://mcp-toolbox.dev/v0.26.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.25.0"
|
||||
url = "https://mcp-toolbox.dev/v0.25.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.24.0"
|
||||
url = "https://mcp-toolbox.dev/v0.24.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.23.0"
|
||||
url = "https://mcp-toolbox.dev/v0.23.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.22.0"
|
||||
url = "https://mcp-toolbox.dev/v0.22.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.21.0"
|
||||
url = "https://mcp-toolbox.dev/v0.21.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.20.0"
|
||||
url = "https://mcp-toolbox.dev/v0.20.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.19.1"
|
||||
url = "https://mcp-toolbox.dev/v0.19.1/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.18.0"
|
||||
url = "https://mcp-toolbox.dev/v0.18.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.17.0"
|
||||
url = "https://mcp-toolbox.dev/v0.17.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.16.0"
|
||||
url = "https://mcp-toolbox.dev/v0.16.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.15.0"
|
||||
url = "https://mcp-toolbox.dev/v0.15.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.14.0"
|
||||
url = "https://mcp-toolbox.dev/v0.14.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.13.0"
|
||||
url = "https://mcp-toolbox.dev/v0.13.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.12.0"
|
||||
url = "https://mcp-toolbox.dev/v0.12.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.11.0"
|
||||
url = "https://mcp-toolbox.dev/v0.11.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.10.0"
|
||||
url = "https://mcp-toolbox.dev/v0.10.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.9.0"
|
||||
url = "https://mcp-toolbox.dev/v0.9.0/"
|
||||
|
||||
[[params.versions]]
|
||||
version = "v0.8.0"
|
||||
url = "https://mcp-toolbox.dev/v0.8.0/"
|
||||
|
||||
|
||||
[[menu.main]]
|
||||
name = "GitHub"
|
||||
weight = 50
|
||||
url = "https://github.com/googleapis/mcp-toolbox"
|
||||
pre = "<i class='fa-brands fa-github'></i>"
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
unsafe= true
|
||||
|
||||
[markup.highlight]
|
||||
noClasses = false
|
||||
style = "tango"
|
||||
|
||||
[outputFormats]
|
||||
[outputFormats.LLMS]
|
||||
mediaType = "text/plain"
|
||||
baseName = "llms"
|
||||
isPlainText = true
|
||||
root = true
|
||||
[outputFormats.LLMS-FULL]
|
||||
mediaType = "text/plain"
|
||||
baseName = "llms-full"
|
||||
isPlainText = true
|
||||
root = true
|
||||
[outputFormats.releases]
|
||||
baseName = 'releases'
|
||||
isPlainText = true
|
||||
mediaType = 'text/releases'
|
||||
|
||||
[mediaTypes."text/releases"]
|
||||
suffixes = ["releases"]
|
||||
|
||||
[outputs]
|
||||
home = ["HTML", "RSS", "LLMS", "LLMS-FULL", "releases"]
|
||||
|
||||
# Google Analytics ID
|
||||
[services]
|
||||
[services.googleAnalytics]
|
||||
id = "G-GLSV9KD8BF"
|
||||
@@ -0,0 +1,11 @@
|
||||
<a class="dropdown-item" href="{{ .Site.Params.global_logo_url | default `/` }}">latest</a>
|
||||
|
||||
{{ if .Site.Params.versions -}}
|
||||
{{ $path := "" -}}
|
||||
{{ if .Site.Params.version_menu_pagelinks -}}
|
||||
{{ $path = .Page.RelPermalink -}}
|
||||
{{ end -}}
|
||||
{{ range .Site.Params.versions -}}
|
||||
<a class="dropdown-item" href="{{ .url }}{{ $path }}">{{ .version }}</a>
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ .Title }}</title>
|
||||
<link rel="canonical" href="{{ .Params.external_url }}"/>
|
||||
<meta http-equiv="refresh" content="0;url={{ .Params.external_url }}"/>
|
||||
</head>
|
||||
<body>
|
||||
<p>Redirecting you to <a href="{{ .Params.external_url }}">{{ .Params.external_url }}</a>...</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
{{ define "main" }}
|
||||
<div class="td-content">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
|
||||
<header class="article-meta">
|
||||
{{ partial "taxonomy_terms_article_wrapper.html" . }}
|
||||
{{ if (and (not .Params.hide_readingtime) (.Site.Params.ui.readingtime.enable)) }}
|
||||
{{ partial "reading-time.html" . }}
|
||||
{{ end }}
|
||||
</header>
|
||||
{{ .Content }}
|
||||
{{ partial "section-index.html" . }}
|
||||
{{ partial "pager.html" . }}
|
||||
{{ if (and (not .Params.hide_feedback) (.Site.Params.ui.feedback.enable) (.Site.Config.Services.GoogleAnalytics.ID)) }}
|
||||
<hr class="mt-5 mb-4">
|
||||
<div class="mt-4">
|
||||
{{ partial "feedback.html" . }}
|
||||
<br />
|
||||
{{ end }}
|
||||
{{ if (.Site.Params.DisqusShortname) }}
|
||||
<br />
|
||||
{{ partial "disqus-comment.html" . }}
|
||||
{{ end }}
|
||||
{{ partial "page-meta-lastmod.html" . }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,26 @@
|
||||
{{ define "main" }}
|
||||
<div class="td-content">
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ with .Params.description }}<div class="lead">{{ . | markdownify }}</div>{{ end }}
|
||||
<header class="article-meta">
|
||||
{{ partial "taxonomy_terms_article_wrapper.html" . }}
|
||||
{{ if (and (not .Params.hide_readingtime) (.Site.Params.ui.readingtime.enable)) }}
|
||||
{{ partial "reading-time.html" . }}
|
||||
{{ end }}
|
||||
</header>
|
||||
{{ .Content }}
|
||||
{{ partial "section-index.html" . }}
|
||||
{{ partial "pager.html" . }}
|
||||
{{ if (and (not .Params.hide_feedback) (.Site.Params.ui.feedback.enable) (.Site.Config.Services.GoogleAnalytics.ID)) }}
|
||||
<hr class="mt-5 mb-4">
|
||||
<div class="mt-4">
|
||||
{{ partial "feedback.html" . }}
|
||||
<br />
|
||||
{{ end }}
|
||||
{{ if (.Site.Params.DisqusShortname) }}
|
||||
<br />
|
||||
{{ partial "disqus-comment.html" . }}
|
||||
{{ end }}
|
||||
{{ partial "page-meta-lastmod.html" . }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,22 @@
|
||||
{{- define "print_full_node" }}
|
||||
|
||||
========================================================================
|
||||
## {{ .page.Title }}
|
||||
========================================================================
|
||||
**Hierarchy:** {{ if .page.Parent }}{{ range .page.Ancestors.Reverse }}{{ .Title }} > {{ end }}{{ end }}{{ .page.Title }}
|
||||
**URL:** {{ .page.Permalink }}
|
||||
**Description:** {{ .page.Description | default "None" }}
|
||||
|
||||
{{ .page.RawContent }}
|
||||
{{ if .page.Pages }}
|
||||
{{- range .page.Pages.ByWeight }}
|
||||
{{ template "print_full_node" (dict "page" .) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{ partial "llms-header.html" (dict "Site" .Site "TitleSuffix" "Complete Documentation") }}
|
||||
|
||||
{{- range .Site.Sections.ByWeight }}
|
||||
{{ template "print_full_node" (dict "page" .) }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,17 @@
|
||||
{{- define "print_index_node" }}
|
||||
{{ .indent }}- [{{ .page.Title }}]({{ .page.Permalink }}): {{ .page.Description | default "No description provided." }}
|
||||
{{- $nextIndent := printf "%s " .indent }}
|
||||
{{- if .page.Pages }}
|
||||
{{- range .page.Pages.ByWeight }}
|
||||
{{ template "print_index_node" (dict "page" . "indent" $nextIndent) }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{ partial "llms-header.html" (dict "Site" .Site "TitleSuffix" "Documentation Index") }}
|
||||
|
||||
## Documentation Map
|
||||
|
||||
{{ range .Site.Sections.ByWeight -}}
|
||||
{{ template "print_index_node" (dict "page" . "indent" "") }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,245 @@
|
||||
{{- $navLinks := slice -}}
|
||||
{{- $targetSections := slice -}}
|
||||
|
||||
{{- /* Resolve root navigation section */ -}}
|
||||
{{- $docsRoot := site.GetPage "/docs" -}}
|
||||
{{- if $docsRoot -}}
|
||||
{{- $targetSections = $docsRoot.Sections.ByWeight -}}
|
||||
{{- else -}}
|
||||
{{- $targetSections = site.Home.Sections.ByWeight -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Determine active top-level directory */ -}}
|
||||
{{- if .FirstSection -}}
|
||||
{{- $topLevelFolder := .FirstSection.Section -}}
|
||||
{{- if or (findRE `^v[0-9]+` $topLevelFolder) (eq $topLevelFolder "dev") -}}
|
||||
{{- $targetSections = .FirstSection.Sections.ByWeight -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Construct navigation link dataset */ -}}
|
||||
{{- range $targetSections -}}
|
||||
{{- if .Title -}}
|
||||
{{- $manualLink := .Params.manualLink | default "" -}}
|
||||
{{- $icon := .Params.icon | default "" -}}
|
||||
{{- $isRightSide := ne $manualLink "" -}}
|
||||
{{- $navLinks = $navLinks | append (dict "name" .Title "url" .RelPermalink "redirect" $manualLink "icon" $icon "isRight" $isRightSide) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Compile shared tools data for sidebar injection */ -}}
|
||||
{{- $dynamicLinks := dict -}}
|
||||
{{- range .Site.Pages -}}
|
||||
{{- if .Params.shared_tools -}}
|
||||
{{- $targetURL := .RelPermalink -}}
|
||||
{{- $targetData := slice -}}
|
||||
{{- range .Params.shared_tools -}}
|
||||
{{- $sourceSection := site.GetPage .source -}}
|
||||
{{- if $sourceSection -}}
|
||||
{{- $links := slice -}}
|
||||
{{- range $sourceSection.Pages -}}
|
||||
{{- $links = $links | append (dict "title" .Title "url" .RelPermalink) -}}
|
||||
{{- end -}}
|
||||
{{- $targetData = $targetData | append (dict "header" .header "links" $links) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if gt (len $targetData) 0 -}}
|
||||
{{- $dynamicLinks = merge $dynamicLinks (dict $targetURL $targetData) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
<div id="nav-data-container" data-nav="{{ $navLinks | jsonify }}" style="display: none;"></div>
|
||||
<script>
|
||||
window.dynamicSidebarLinks = {{ $dynamicLinks | jsonify | safeJS }};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
// Add links to GitHub issues for documentation feedback and feature requests
|
||||
const docIssueLink = document.querySelector('.td-page-meta__issue');
|
||||
if (docIssueLink) {
|
||||
const url = new URL(docIssueLink.href);
|
||||
url.searchParams.set('template', 'feature_request.yml');
|
||||
docIssueLink.href = url.toString();
|
||||
}
|
||||
|
||||
const projIssueLink = document.querySelector('.td-page-meta__project-issue');
|
||||
if (projIssueLink) {
|
||||
const url = new URL(projIssueLink.href);
|
||||
url.searchParams.set('template', 'bug_report.yml');
|
||||
projIssueLink.href = url.toString();
|
||||
}
|
||||
|
||||
const container = document.getElementById('nav-data-container');
|
||||
const currentPath = window.location.pathname;
|
||||
const header = document.querySelector('header');
|
||||
|
||||
if (!container || !header || document.getElementById('secondary-nav')) return;
|
||||
|
||||
let links = [];
|
||||
try {
|
||||
links = JSON.parse(container.dataset.nav);
|
||||
} catch (e) {
|
||||
console.error("Failed to parse nav data", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure brand logo routing
|
||||
const logoLink = document.querySelector('.navbar-brand');
|
||||
if (logoLink) {
|
||||
logoLink.href = "{{ site.Params.global_logo_url | default `/` }}";
|
||||
}
|
||||
|
||||
// Sidebar link centering logic
|
||||
const sidebar = document.querySelector('#td-section-nav') || document.querySelector('.td-sidebar-nav') || document.querySelector('.td-sidebar');
|
||||
if (sidebar) {
|
||||
const activeLink = sidebar.querySelector('.active');
|
||||
if (activeLink) {
|
||||
const sidebarRect = sidebar.getBoundingClientRect();
|
||||
const activeRect = activeLink.getBoundingClientRect();
|
||||
sidebar.scrollTop = activeRect.top - sidebarRect.top + sidebar.scrollTop - (sidebar.clientHeight / 2) + (activeRect.height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Build secondary navigation UI
|
||||
let leftNavItems = '';
|
||||
let rightNavItems = '';
|
||||
const versionMatch = currentPath.match(/^\/(v\d+\.\d+\.\d+|dev)\//);
|
||||
const activePrefix = versionMatch ? versionMatch[0] : '/';
|
||||
|
||||
links.forEach(link => {
|
||||
if (!link.name || !link.url) return;
|
||||
|
||||
let rawUrl = link.isRight ? link.redirect : link.url;
|
||||
// Only strip double slashes if it is an internal relative link
|
||||
let finalUrl = rawUrl.startsWith('http') ? rawUrl : rawUrl.replace(/\/+/g, '/');
|
||||
|
||||
// Apply active version prefix mapping
|
||||
if (!link.isRight && activePrefix !== '/' && !finalUrl.startsWith(activePrefix)) {
|
||||
finalUrl = activePrefix + finalUrl.replace(/^\//, '');
|
||||
}
|
||||
|
||||
const iconHtml = link.icon ? `<i class="${link.icon}${link.isRight ? ' pop-icon' : ''}" ${!link.isRight ? 'style="font-size: 1.05rem;"' : ''}></i> ` : '';
|
||||
|
||||
if (link.isRight) {
|
||||
rightNavItems += `
|
||||
<li>
|
||||
<a href="${finalUrl}" target="_blank" rel="noopener noreferrer" class="sec-nav-item-right">
|
||||
${iconHtml}<span class="sec-nav-text">${link.name}</span>
|
||||
</a>
|
||||
</li>`;
|
||||
} else {
|
||||
const isActive = currentPath.startsWith(finalUrl) ? ' active' : '';
|
||||
leftNavItems += `
|
||||
<li>
|
||||
<a class="sec-nav-item-left${isActive}" href="${finalUrl}">
|
||||
${iconHtml}<span>${link.name}</span>
|
||||
</a>
|
||||
</li>`;
|
||||
}
|
||||
});
|
||||
|
||||
header.insertAdjacentHTML('beforeend', `
|
||||
<div id="secondary-nav">
|
||||
<div class="container-fluid sec-nav-container">
|
||||
<ul class="sec-nav-list">${leftNavItems}</ul>
|
||||
<ul class="sec-nav-icons">${rightNavItems}</ul>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// ======================================================================
|
||||
// Configure release version button labeling & Active Highlight
|
||||
// ======================================================================
|
||||
try {
|
||||
const relBtn = Array.from(document.querySelectorAll('.td-navbar a.dropdown-toggle'))
|
||||
.find(el => el.textContent.includes('Releases') || el.textContent.match(/v\d+\./) || el.textContent.includes('dev'));
|
||||
|
||||
if (relBtn) {
|
||||
let label = currentPath.includes('/dev/') ? "dev" : (versionMatch ? versionMatch[1].replace(/\//g, '') : "Releases");
|
||||
|
||||
// Update the text on the dropdown button itself
|
||||
for (let n of relBtn.childNodes) {
|
||||
if (n.nodeType === 3 && n.textContent.trim().length > 1) {
|
||||
n.textContent = ' ' + label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (label !== "Releases") {
|
||||
const waitForLinks = setInterval(() => {
|
||||
const dropdownItems = document.querySelectorAll('.dropdown-menu .dropdown-item');
|
||||
if (dropdownItems.length > 0) {
|
||||
clearInterval(waitForLinks);
|
||||
|
||||
dropdownItems.forEach(item => {
|
||||
const href = item.getAttribute('href');
|
||||
if (href && (href.includes(`/${label}/`) || href.endsWith(`/${label}`))) {
|
||||
item.classList.add('active-version');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 50); // Check every 50 milliseconds
|
||||
|
||||
// Safety fallback: Stop checking after 3 seconds so it doesn't run forever
|
||||
setTimeout(() => clearInterval(waitForLinks), 3000);
|
||||
}
|
||||
}
|
||||
} catch (e) { console.warn("Releases Labeler skipped.", e); }
|
||||
|
||||
// Inject shared tools into sidebar DOM
|
||||
const sidebarData = window.dynamicSidebarLinks;
|
||||
if (sidebarData && Object.keys(sidebarData).length > 0) {
|
||||
Object.keys(sidebarData).forEach(targetUrl => {
|
||||
const targetLink = document.querySelector(`.td-sidebar-nav a[href="${targetUrl}"]`) ||
|
||||
document.querySelector(`.td-sidebar-nav a[href="${targetUrl.replace(/\/$/, '')}"]`);
|
||||
if (!targetLink) return;
|
||||
|
||||
const parentLi = targetLink.closest('li');
|
||||
if (!parentLi) return;
|
||||
|
||||
let innerUl = parentLi.querySelector('ul');
|
||||
if (!innerUl) {
|
||||
innerUl = document.createElement('ul');
|
||||
innerUl.className = "ul-2 list-unstyled pb-2 pl-3";
|
||||
innerUl.style.marginLeft = "1rem";
|
||||
parentLi.appendChild(innerUl);
|
||||
parentLi.classList.add('with-child');
|
||||
}
|
||||
|
||||
sidebarData[targetUrl].forEach(group => {
|
||||
const groupLi = document.createElement('li');
|
||||
groupLi.className = "with-child";
|
||||
const isGroupActive = group.links.some(link => currentPath === link.url);
|
||||
|
||||
const subUl = document.createElement('ul');
|
||||
subUl.className = "ul-3 list-unstyled pb-2 pl-3";
|
||||
subUl.style.marginLeft = "1rem";
|
||||
subUl.style.display = isGroupActive ? "block" : "none";
|
||||
|
||||
if (group.header) {
|
||||
const headerLabel = document.createElement('label');
|
||||
headerLabel.style.cursor = "pointer";
|
||||
headerLabel.innerHTML = `<span style="font-weight: 600; text-transform: uppercase; font-size: 0.85rem; letter-spacing: 0.5px;">${group.header}</span>`;
|
||||
|
||||
headerLabel.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
subUl.style.display = subUl.style.display === "none" ? "block" : "none";
|
||||
});
|
||||
groupLi.appendChild(headerLabel);
|
||||
}
|
||||
|
||||
group.links.forEach(link => {
|
||||
const linkLi = document.createElement('li');
|
||||
linkLi.innerHTML = `<a href="${link.url}" class="td-sidebar-link${currentPath === link.url ? ' active' : ''}" title="${link.title}">${link.title}</a>`;
|
||||
subUl.appendChild(linkLi);
|
||||
});
|
||||
|
||||
groupLi.appendChild(subUl);
|
||||
innerUl.appendChild(groupLi);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,4 @@
|
||||
<script src='{{ "js/w3.js" | relURL }}'></script>
|
||||
{{ if not .Site.Params.disableBanner }}
|
||||
<script src="{{ "js/custom-layout.js" | relURL }}"></script>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,58 @@
|
||||
# {{ .Site.Title }} - {{ .TitleSuffix }}
|
||||
> {{ .Site.Params.description }}
|
||||
|
||||
**DOCUMENTATION VERSION:** {{ .Site.Params.version | default "Latest" }}
|
||||
**BASE URL:** {{ .Site.BaseURL }}
|
||||
**GENERATED ON:** {{ now.Format "2006-01-02T15:04:05Z07:00" }}
|
||||
|
||||
---
|
||||
### System Directives for AI Models
|
||||
**Role:** You are an expert Developer Advocate and Integration Engineer for the **MCP (Model Context Protocol) Toolbox for Databases**.
|
||||
**Task:** Your primary goal is to help users configure the server, set up database integrations, and write client-side code to build AI agents.
|
||||
**Strict Guidelines:**
|
||||
1. **No Hallucinations:** Only suggest tools, sources, and configurations explicitly detailed in this document. Do not invent arbitrary REST endpoints.
|
||||
2. **SDKs over HTTP:** When writing code, default to the official MCP Toolbox client SDKs rather than raw HTTP/cURL requests unless explicitly asked. Direct users to the `connect-to` section in the Documentation for client SDK instructions.
|
||||
3. **Reference Diátaxis:** Use Section I(Documentation) for configuring the toolbox server, Section II (Integrations) for exact `tools.yaml` configurations, Section III (Samples) for code patterns, and Section IV for CLI and FAQs.
|
||||
|
||||
### Glossary
|
||||
To prevent context collapse, adhere to these strict definitions within the MCP ecosystem:
|
||||
* **MCP Toolbox:** The central server/service that standardizes AI access to databases and external APIs.
|
||||
* **Source:** A configured backend connection to an external system (e.g., PostgreSQL, BigQuery, HTTP).
|
||||
* **Tool:** A single, atomic capability exposed to the LLM (e.g., `bigquery-sql-query`), executed against a Source.
|
||||
* **Toolset:** A logical, grouped collection of Tools.
|
||||
* **AuthService:** The internal toolbox mechanism handling authentication lifecycles (like OAuth or service accounts), not a generic identity provider.
|
||||
* **Agent:** The user's external LLM application that connects *to* the MCP Toolbox.
|
||||
|
||||
### Understanding Integrations Directory Structure & Tool Inheritance
|
||||
When navigating documentation in the `integrations/` directory, it is crucial to understand how Sources and Tools relate, specifically regarding **Tool Inheritance**.
|
||||
* **Source Pages (`source.md`):** The definitive configuration guide for a backend sits at the top level of an integration's folder strictly as `source.md` (e.g., `integrations/alloydb/source.md`). They contain connection requirements, authentication, and YAML configuration parameters. *(Note: `_index.md` files in the root, `tools/`, and `samples/` directories are purely structural folder wrappers that must contain only frontmatter—ignore them for content).*
|
||||
* **Native Tools:** Specific capabilities built directly for a Source. If a Source has native tools, they are located in a `tools/` sub-directory (e.g., `integrations/alloydb/tools/alloydb-sql.md`).
|
||||
* **Inherited Tools (Shared Tools):** Managed or compatible databases (e.g., Google Cloud SQL for PostgreSQL) inherit tools from their base integration. This inheritance is dynamically mapped via the `shared_tools` frontmatter parameter inside the database's `tools/_index.md` file (which contains no body content). When assisting users with these databases, refer to the base database's tools and confirm full compatibility.
|
||||
|
||||
### Global Environment & Prerequisites
|
||||
* **Configuration:** `tools.yaml` is the ultimate source of truth for server configuration.
|
||||
* **Database:** PostgreSQL 16+ and the `psql` client.
|
||||
* **Language Requirements:**
|
||||
{{- with .Site.GetPage "/documentation/getting-started/local_quickstart" }}
|
||||
{{- $match := findRE "(?i)Python\\s+\\(\\d+\\.\\d+\\s+or\\s+higher\\)" .RawContent 1 }}
|
||||
* Python: {{ if $match }}{{ index $match 0 | replaceRE "[\\[\\]]" "" }}{{ else }}Refer to Python Quickstart{{ end }}
|
||||
{{- end }}
|
||||
{{- with .Site.GetPage "/documentation/getting-started/local_quickstart_js" }}
|
||||
{{- $match := findRE "(?i)Node\\.js \\(v\\d+ or higher\\)" .RawContent 1 }}
|
||||
* JavaScript/Node: {{ if $match }}{{ index $match 0 }}{{ else }}Refer to JS Quickstart{{ end }}
|
||||
{{- end }}
|
||||
{{- with .Site.GetPage "/documentation/getting-started/local_quickstart_go" }}
|
||||
{{- $match := findRE "(?i)Go \\(v\\d+\\.\\d+\\.\\d+ or higher\\)" .RawContent 1 }}
|
||||
* Go: {{ if $match }}{{ index $match 0 }}{{ else }}Refer to Go Quickstart{{ end }}
|
||||
{{- end }}
|
||||
|
||||
### The Diátaxis Narrative Framework
|
||||
This documentation is structured following the Diátaxis framework to assist in clear navigation and understanding:
|
||||
* **Section I: Documentation:** (Explanation) Theoretical context, high-level understanding, and universal How-To Guides.
|
||||
* **Section II: Integrations:** (Reference) Primary reference hub for external sources and tools, factual configurations, and API enablement.
|
||||
* **Section III: Samples:** (Tutorials) Code patterns and examples. **Note for AI:** Sample code is distributed across three main areas:
|
||||
1. **Quickstarts:** Located in `documentation/getting-started/`.
|
||||
2. **Integration-Specific Samples:** Nested within their respective `integrations/<source_name>/samples/` directories.
|
||||
3. **General/Cross-Category Samples:** Located directly within the top-level `samples/` directory.
|
||||
* **Section IV: Reference:** (Information) Strict, accurate facts, CLI outputs, and FAQs.
|
||||
---
|
||||
@@ -0,0 +1,67 @@
|
||||
{{ if .Site.Params.versions -}}
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ .Site.Params.version_menu }}
|
||||
</a>
|
||||
<div class="dropdown-menu" id="version-dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<div w3-include-html="{{ .Site.Params.releases_url }}" w3-include-html-default='<a class="dropdown-item" href="{{ .Site.Params.global_logo_url }}dev/">dev</a>'></div>
|
||||
|
||||
<script>
|
||||
w3.includeHTML(function() {
|
||||
const basePath = "{{ site.BaseURL | relURL }}";
|
||||
|
||||
function stripBase(path) {
|
||||
if (path.startsWith(basePath)) return path.substring(basePath.length);
|
||||
if (basePath === "/" && path.startsWith("/")) return path.substring(1);
|
||||
return path.replace(/^\//, '');
|
||||
}
|
||||
|
||||
document.getElementById('version-dropdown-menu').addEventListener('click', function(e) {
|
||||
const link = e.target.closest('a.dropdown-item');
|
||||
if (!link) return;
|
||||
e.preventDefault();
|
||||
|
||||
let targetPath = link.pathname;
|
||||
if (!targetPath.endsWith('/')) targetPath += '/';
|
||||
|
||||
let cleanCurrentPath = stripBase(window.location.pathname);
|
||||
const allLinks = document.querySelectorAll('#version-dropdown-menu a.dropdown-item');
|
||||
let currentVersionPrefix = "";
|
||||
|
||||
allLinks.forEach(a => {
|
||||
let cleanVPath = stripBase(a.pathname);
|
||||
if (!cleanVPath.endsWith('/')) cleanVPath += '/';
|
||||
if (cleanVPath !== "" && cleanVPath !== "/" && cleanCurrentPath.startsWith(cleanVPath)) {
|
||||
if (cleanVPath.length > currentVersionPrefix.length) {
|
||||
currentVersionPrefix = cleanVPath;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let deepPath = cleanCurrentPath;
|
||||
if (currentVersionPrefix !== "") {
|
||||
deepPath = cleanCurrentPath.substring(currentVersionPrefix.length);
|
||||
}
|
||||
deepPath = deepPath.replace(/^\//, '');
|
||||
|
||||
const fullTargetPath = targetPath + deepPath;
|
||||
|
||||
// Perform a HEAD request to check if the deep route exists
|
||||
fetch(fullTargetPath, { method: 'HEAD' })
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
// Page exists! Redirect to deep path
|
||||
window.location.href = fullTargetPath;
|
||||
} else {
|
||||
// 404 or other error: Fallback to the version root
|
||||
window.location.href = targetPath;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// If the fetch fails entirely, fallback to root
|
||||
window.location.href = targetPath;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{{ end -}}
|
||||
@@ -0,0 +1,52 @@
|
||||
{{/* cSpell:ignore querify subdir */ -}}
|
||||
{{/* Class names ending with `--KIND` are deprecated in favor of `__KIND`, but we're keeping them for a few releases after 0.9.0 */ -}}
|
||||
|
||||
{{ if .File -}}
|
||||
{{ $path := urls.JoinPath $.Language.Lang $.File.Path -}}
|
||||
{{ $gh_repo := $.Param "github_repo" -}}
|
||||
{{ $gh_url := $.Param "github_url" -}}
|
||||
{{ $gh_subdir := $.Param "github_subdir" | default "" -}}
|
||||
{{ $gh_project_repo := $.Param "github_project_repo" -}}
|
||||
{{ $gh_branch := $.Param "github_branch" | default "main" -}}
|
||||
<div class="td-page-meta ms-2 pb-1 pt-2 mb-0">
|
||||
{{ if $gh_url -}}
|
||||
{{ warnf "Warning: use of `github_url` is deprecated. For details, see https://www.docsy.dev/docs/adding-content/repository-links/#github_url-optional" -}}
|
||||
<a href="{{ $gh_url }}" target="_blank"><i class="fa-solid fa-pen-to-square fa-fw"></i> {{ T "post_edit_this" }}</a>
|
||||
{{ else if $gh_repo -}}
|
||||
|
||||
{{/* Adjust $path based on path_base_for_github_subdir */ -}}
|
||||
{{ $ghs_base := $.Param "path_base_for_github_subdir" -}}
|
||||
{{ $ghs_rename := "" -}}
|
||||
{{ if reflect.IsMap $ghs_base -}}
|
||||
{{ $ghs_rename = $ghs_base.to -}}
|
||||
{{ $ghs_base = $ghs_base.from -}}
|
||||
{{ end -}}
|
||||
{{ with $ghs_base -}}
|
||||
{{ $path = replaceRE . $ghs_rename $path -}}
|
||||
{{ end -}}
|
||||
|
||||
{{ $gh_repo_path := printf "%s/%s/%s" $gh_branch $gh_subdir $path -}}
|
||||
{{ $gh_repo_path = replaceRE "//+" "/" $gh_repo_path -}}
|
||||
|
||||
{{ $viewURL := printf "%s/tree/%s" $gh_repo $gh_repo_path -}}
|
||||
{{ $editURL := printf "%s/edit/%s" $gh_repo $gh_repo_path -}}
|
||||
{{ $issuesURL := printf "%s/issues/new?title=%s" $gh_repo (safeURL $.Title ) -}}
|
||||
{{ $newPageStub := resources.Get "stubs/new-page-template.md" -}}
|
||||
{{ $newPageQS := querify "value" $newPageStub.Content "filename" "change-me.md" | safeURL -}}
|
||||
{{ $newPageURL := printf "%s/new/%s?%s" $gh_repo (path.Dir $gh_repo_path) $newPageQS -}}
|
||||
|
||||
<a href="{{ $viewURL }}" class="td-page-meta--view td-page-meta__view" target="_blank" rel="noopener"><i class="fa-solid fa-file-lines fa-fw"></i> {{ T "post_view_this" }}</a>
|
||||
<a href="{{ $editURL }}" class="td-page-meta--edit td-page-meta__edit" target="_blank" rel="noopener"><i class="fa-solid fa-pen-to-square fa-fw"></i> {{ T "post_edit_this" }}</a>
|
||||
<a href="{{ $newPageURL }}" class="td-page-meta--child td-page-meta__child" target="_blank" rel="noopener"><i class="fa-solid fa-pen-to-square fa-fw"></i> {{ T "post_create_child_page" }}</a>
|
||||
<a href="{{ $issuesURL }}" class="td-page-meta--issue td-page-meta__issue" target="_blank" rel="noopener"><i class="fa-solid fa-list-check fa-fw"></i> {{ T "post_create_issue" }}</a>
|
||||
{{ with $gh_project_repo -}}
|
||||
{{ $project_issueURL := printf "%s/issues/new" . -}}
|
||||
<a href="{{ $project_issueURL }}" class="td-page-meta--project td-page-meta__project-issue" target="_blank" rel="noopener"><i class="fa-solid fa-list-check fa-fw"></i> {{ T "post_create_project_issue" }}</a>
|
||||
{{ end -}}
|
||||
|
||||
{{ end -}}
|
||||
{{ with .CurrentSection.AlternativeOutputFormats.Get "print" -}}
|
||||
<a id="print" href="{{ .RelPermalink | safeURL }}"><i class="fa-solid fa-print fa-fw"></i> {{ T "print_entire_section" }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end -}}
|
||||
@@ -0,0 +1,187 @@
|
||||
{{ $curr := . }}
|
||||
{{ $next := "" }}
|
||||
{{ $prev := "" }}
|
||||
|
||||
{{ $max_skip_attempts := 5 }}
|
||||
|
||||
{{/* 1. Calculate Previous Page */}}
|
||||
{{ if .Parent }}
|
||||
{{ $siblings := .Parent.Pages.ByWeight }}
|
||||
{{ $currIndex := -1 }}
|
||||
|
||||
{{ range $index, $page := $siblings }}
|
||||
{{ if eq $page.RelPermalink $curr.RelPermalink }}
|
||||
{{ $currIndex = $index }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if gt $currIndex 0 }}
|
||||
{{ $prev = index $siblings (sub $currIndex 1) }}
|
||||
{{ else }}
|
||||
{{ if ne .Parent.Type "home" }}
|
||||
{{ $prev = .Parent }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* 2. Calculate Next Page */}}
|
||||
{{ if and .IsNode (gt (len .Pages) 0) }}
|
||||
{{ $next = index .Pages.ByWeight 0 }}
|
||||
{{ else }}
|
||||
{{ if .Parent }}
|
||||
{{ $siblings := .Parent.Pages.ByWeight }}
|
||||
{{ $currIndex := -1 }}
|
||||
|
||||
{{ range $index, $page := $siblings }}
|
||||
{{ if eq $page.RelPermalink $curr.RelPermalink }}
|
||||
{{ $currIndex = $index }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if lt $currIndex (sub (len $siblings) 1) }}
|
||||
{{ $next = index $siblings (add $currIndex 1) }}
|
||||
{{ else }}
|
||||
{{ $p := .Parent }}
|
||||
{{ $foundNext := false }}
|
||||
|
||||
{{ range seq 3 }}
|
||||
{{ if and (not $foundNext) $p }}
|
||||
{{ if $p.Parent }}
|
||||
{{ $pSiblings := $p.Parent.Pages.ByWeight }}
|
||||
{{ $pIndex := -1 }}
|
||||
|
||||
{{ range $index, $page := $pSiblings }}
|
||||
{{ if eq $page.RelPermalink $p.RelPermalink }}
|
||||
{{ $pIndex = $index }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if and (ge $pIndex 0) (lt $pIndex (sub (len $pSiblings) 1)) }}
|
||||
{{ $next = index $pSiblings (add $pIndex 1) }}
|
||||
{{ $foundNext = true }}
|
||||
{{ else }}
|
||||
{{ $p = $p.Parent }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ $p = false }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* 3. Apply Integration Directory Filters */}}
|
||||
{{ range seq $max_skip_attempts }}
|
||||
{{ if $prev }}
|
||||
{{ $isLockedPrev := false }}
|
||||
{{ if and $prev.IsNode (in $prev.RelPermalink "/integrations/") }}
|
||||
{{ if or (strings.HasSuffix $prev.RelPermalink "/tools/") (strings.HasSuffix $prev.RelPermalink "/samples/") (and $prev.Parent (strings.HasSuffix $prev.Parent.RelPermalink "/integrations/")) }}
|
||||
{{ $isLockedPrev = true }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $isLockedPrev }}
|
||||
{{ $steppingOut := strings.HasPrefix $curr.RelPermalink $prev.RelPermalink }}
|
||||
|
||||
{{ if and (not $steppingOut) (gt (len $prev.Pages) 0) }}
|
||||
{{ $prev = index $prev.Pages.ByWeight (sub (len $prev.Pages) 1) }}
|
||||
{{ else }}
|
||||
{{ if $prev.Parent }}
|
||||
{{ if eq $prev.Parent.RelPermalink "/integrations/" }}
|
||||
{{ $prev = $prev.Parent }}
|
||||
{{ else }}
|
||||
{{ $sibs := $prev.Parent.Pages.ByWeight }}
|
||||
{{ $idx := -1 }}
|
||||
{{ range $i, $p := $sibs }}{{ if eq $p.RelPermalink $prev.RelPermalink }}{{ $idx = $i }}{{ end }}{{ end }}
|
||||
|
||||
{{ if gt $idx 0 }}
|
||||
{{ $prev = index $sibs (sub $idx 1) }}
|
||||
{{ else }}
|
||||
{{ $prev = $prev.Parent }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ $prev = "" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ break }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ range seq $max_skip_attempts }}
|
||||
{{ if $next }}
|
||||
{{ $isLockedNext := false }}
|
||||
{{ if and $next.IsNode (in $next.RelPermalink "/integrations/") }}
|
||||
{{ if or (strings.HasSuffix $next.RelPermalink "/tools/") (strings.HasSuffix $next.RelPermalink "/samples/") (and $next.Parent (strings.HasSuffix $next.Parent.RelPermalink "/integrations/")) }}
|
||||
{{ $isLockedNext = true }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $isLockedNext }}
|
||||
{{ if gt (len $next.Pages) 0 }}
|
||||
{{ $next = index $next.Pages.ByWeight 0 }}
|
||||
{{ else }}
|
||||
{{ $sibs := $next.Parent.Pages.ByWeight }}
|
||||
{{ $idx := -1 }}
|
||||
{{ range $i, $p := $sibs }}{{ if eq $p.RelPermalink $next.RelPermalink }}{{ $idx = $i }}{{ end }}{{ end }}
|
||||
|
||||
{{ if lt $idx (sub (len $sibs) 1) }}
|
||||
{{ $next = index $sibs (add $idx 1) }}
|
||||
{{ else }}
|
||||
{{ $p := $next.Parent }}
|
||||
{{ $foundNextSibling := false }}
|
||||
|
||||
{{ range seq 3 }}
|
||||
{{ if and (not $foundNextSibling) $p }}
|
||||
{{ if $p.Parent }}
|
||||
{{ $pSibs := $p.Parent.Pages.ByWeight }}
|
||||
{{ $pIdx := -1 }}
|
||||
|
||||
{{ range $index, $page := $pSibs }}{{ if eq $page.RelPermalink $p.RelPermalink }}{{ $pIdx = $index }}{{ end }}{{ end }}
|
||||
|
||||
{{ if and (ge $pIdx 0) (lt $pIdx (sub (len $pSibs) 1)) }}
|
||||
{{ $next = index $pSibs (add $pIdx 1) }}
|
||||
{{ $foundNextSibling = true }}
|
||||
{{ else }}
|
||||
{{ $p = $p.Parent }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ $p = false }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if not $foundNextSibling }}{{ $next = "" }}{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ break }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* 4. Render Navigation */}}
|
||||
{{ if or $prev $next }}
|
||||
<nav class="mt-5 pt-4 border-top d-flex justify-content-between" aria-label="Page navigation">
|
||||
<div>
|
||||
{{ with $prev }}
|
||||
<a href="{{ .RelPermalink }}" class="text-decoration-none">
|
||||
<small class="text-muted d-block mb-1">« Previous</small>
|
||||
<span class="text-body">{{ .Title }}</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
{{ with $next }}
|
||||
<a href="{{ .RelPermalink }}" class="text-decoration-none">
|
||||
<small class="text-muted d-block mb-1">Next »</small>
|
||||
<span class="text-body">{{ .Title }}</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</nav>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,137 @@
|
||||
{{ .Scratch.Set "docsy-search" 0 }}
|
||||
|
||||
{{ if .Site.Params.pagefind }}
|
||||
{{ .Scratch.Add "docsy-search" 1 }}
|
||||
|
||||
<div class="td-search custom-pagefind-wrapper">
|
||||
<div class="pagefind-search-container"></div>
|
||||
</div>
|
||||
|
||||
<link href="{{ relURL "pagefind/pagefind-ui.css" }}" rel="stylesheet">
|
||||
<script src="{{ relURL "pagefind/pagefind-ui.js" }}"></script>
|
||||
|
||||
<script>
|
||||
if (!window.pagefindScriptLoaded) {
|
||||
window.pagefindScriptLoaded = true;
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
/* Initialize Pagefind UI */
|
||||
document.querySelectorAll('.pagefind-search-container').forEach(el => {
|
||||
new PagefindUI({
|
||||
element: el,
|
||||
showSubResults: true,
|
||||
showImages: false,
|
||||
resetStyles: false,
|
||||
debounceTimeoutMs: 300,
|
||||
bundlePath: "{{ relURL "pagefind/" }}",
|
||||
processResult: function (result) {
|
||||
const ignoredSegments = ["en", "docs", "dev", "mcp-toolbox"];
|
||||
let path = result.url.replace(/^https?:\/\/[^\/]+/, "").split('#')[0].replace(/\/$/, "");
|
||||
let parts = path.split("/").filter(p => p && !ignoredSegments.includes(p));
|
||||
parts.pop();
|
||||
|
||||
if (parts.length > 0) {
|
||||
let breadcrumb = parts.map(part => part.replace(/[-_]/g, ' ').replace(/\b\w/g, l => l.toUpperCase())).join(' › ');
|
||||
if (!result.rawTitle) result.rawTitle = result.meta.title;
|
||||
result.meta.title = breadcrumb + ' › ' + result.rawTitle;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* Modal Behavior Logic */
|
||||
setTimeout(() => {
|
||||
let backdrop = document.getElementById('global-search-backdrop');
|
||||
if (!backdrop) {
|
||||
backdrop = document.createElement('div');
|
||||
backdrop.id = 'global-search-backdrop';
|
||||
document.body.appendChild(backdrop);
|
||||
}
|
||||
|
||||
let isClosing = false;
|
||||
let activeWrapper = null;
|
||||
let placeholder = document.createElement('div');
|
||||
placeholder.style.display = 'none';
|
||||
|
||||
function openModal(input) {
|
||||
if (activeWrapper || isClosing) return;
|
||||
const wrapper = input.closest('.custom-pagefind-wrapper');
|
||||
if (!wrapper) return;
|
||||
|
||||
activeWrapper = wrapper;
|
||||
wrapper.parentNode.insertBefore(placeholder, wrapper);
|
||||
document.body.appendChild(wrapper);
|
||||
|
||||
backdrop.classList.add('active');
|
||||
document.body.classList.add('global-search-active');
|
||||
wrapper.classList.add('active-modal');
|
||||
|
||||
setTimeout(() => {
|
||||
input.focus();
|
||||
const len = input.value.length;
|
||||
input.setSelectionRange(len, len);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
function closeModal(keepFocus = false) {
|
||||
if (!activeWrapper) return;
|
||||
isClosing = true;
|
||||
|
||||
backdrop.classList.remove('active');
|
||||
document.body.classList.remove('global-search-active');
|
||||
activeWrapper.classList.remove('active-modal');
|
||||
|
||||
if (placeholder.parentNode) {
|
||||
placeholder.parentNode.insertBefore(activeWrapper, placeholder);
|
||||
}
|
||||
|
||||
const input = activeWrapper.querySelector('.pagefind-ui__search-input');
|
||||
|
||||
if (!keepFocus) {
|
||||
const clearBtn = activeWrapper.querySelector('.pagefind-ui__search-clear');
|
||||
if (clearBtn) clearBtn.click();
|
||||
if (input) input.blur();
|
||||
} else {
|
||||
if (input) setTimeout(() => input.focus(), 10);
|
||||
}
|
||||
|
||||
activeWrapper = null;
|
||||
setTimeout(() => { isClosing = false; }, 50);
|
||||
}
|
||||
|
||||
/* Event Listeners */
|
||||
document.addEventListener('input', (e) => {
|
||||
if (e.target.classList.contains('pagefind-ui__search-input')) {
|
||||
e.target.value.trim() !== '' ? openModal(e.target) : closeModal(true);
|
||||
}
|
||||
});
|
||||
|
||||
backdrop.addEventListener('click', () => closeModal(false));
|
||||
|
||||
document.addEventListener('mousedown', (e) => {
|
||||
if (!activeWrapper) return;
|
||||
const clickedX = e.target.closest('.pagefind-ui__search-clear');
|
||||
const clickedResult = e.target.closest('.pagefind-ui__result-link');
|
||||
const isClickInside = activeWrapper.contains(e.target);
|
||||
|
||||
if (clickedResult) return;
|
||||
if (clickedX || (!isClickInside && e.target !== backdrop)) closeModal(false);
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') closeModal(false);
|
||||
});
|
||||
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{{ else if .Site.Params.offlineSearch }}
|
||||
{{ .Scratch.Set "docsy-search" 1 }}
|
||||
<div class="td-search custom-pagefind-wrapper flex-grow-1 order-last order-lg-1 w-100 mt-3 mt-lg-0">
|
||||
<div class="pagefind-search-container w-100"></div>
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1 @@
|
||||
{{ template "partials/td/render-heading.html" . }}
|
||||
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
{{ if eq hugo.Environment "preview" }}
|
||||
Disallow: /*
|
||||
{{ end }}
|
||||
@@ -0,0 +1,47 @@
|
||||
{{/* Automatically identify the "Native" source (Grandparent source.md, since this is strictly used inside tools/) */}}
|
||||
{{ $nativeSource := .Page.Parent.Parent.GetPage "source.md" }}
|
||||
|
||||
{{ if not $nativeSource }}
|
||||
{{ $nativeSource = .Page.Parent.Parent.GetPage "source" }}
|
||||
{{ end }}
|
||||
|
||||
<div class="compatibility-section">
|
||||
<p>This tool can be used with the following database sources:</p>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Source Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{/* Display the Native Source automatically */}}
|
||||
{{ if $nativeSource }}
|
||||
<tr>
|
||||
<td><a href="{{ $nativeSource.RelPermalink }}">{{ $nativeSource.Title }}</a></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
|
||||
{{/* Process additional sources passed via the "others" parameter */}}
|
||||
|
||||
{{ $others := .Get "others" }}
|
||||
{{ if $others }}
|
||||
{{ range split $others "," }}
|
||||
{{ $path := trim . " " }}
|
||||
{{ $cleanPath := trim $path "/" }}
|
||||
{{ $remotePage := site.GetPage (printf "%s/source.md" $cleanPath) }}
|
||||
|
||||
{{ if $remotePage }}
|
||||
<tr>
|
||||
<td><a href="{{ $remotePage.RelPermalink }}">{{ $remotePage.Title }}</a></td>
|
||||
</tr>
|
||||
{{ else }}
|
||||
<tr>
|
||||
<td style="color: red;">⚠️ Source not found at path: {{ $path }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,13 @@
|
||||
{{ $file := .Get 0 }}
|
||||
{{ $lang := .Get 1 }}
|
||||
|
||||
{{ $content := (printf "%s%s" .Page.File.Dir $file) | readFile | replaceRE "^---[\\s\\S]+?---" "" | replaceRE "\r\n"
|
||||
"\n" | strings.TrimRight "\n" }}
|
||||
|
||||
{{ if $lang }}
|
||||
```{{ $lang }}
|
||||
{{ $content | safeHTML }}
|
||||
```
|
||||
{{ else }}
|
||||
{{ $content | safeHTML }}
|
||||
{{ end }}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user