Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .github/scripts/generate_skills.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/bin/bash
set -e

# Ensure VERSION is passed from the environment
if [ -z "$VERSION" ]; then
echo "Error: VERSION environment variable is not set."
exit 1
fi
Comment on lines +1 to +8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It is highly recommended to use set -euo pipefail for robust error handling in Bash scripts. Additionally, when set -u is active, referencing $VERSION directly when it is not set will cause the script to exit immediately with an unbound variable error. Using ${VERSION:-} prevents this and allows the custom error message to be printed. Also, redirecting the error message to >&2 ensures it is printed to stderr.

Suggested change
#!/bin/bash
set -e
# Ensure VERSION is passed from the environment
if [ -z "$VERSION" ]; then
echo "Error: VERSION environment variable is not set."
exit 1
fi
#!/bin/bash
set -euo pipefail
# Ensure VERSION is passed from the environment
if [ -z "${VERSION:-}" ]; then
echo "Error: VERSION environment variable is not set." >&2
exit 1
fi


# SKILL CONFIGURATION
# Format: "toolset" "description"
# The skill name is automatically generated as "alloydb-postgres-<toolset>"
SKILLS=(
"admin"
"Use these skills when you need to provision new AlloyDB clusters and instances, monitor their creation status, and retrieve high-level configuration or health data for the environment."

"access-management"
"Use these skills when you need to manage database users, inspect permissions and roles, and verify global configuration parameters related to security and access control."

"data"
"Use these skills when you need to explore the database schema, identify objects like views and triggers, and execute custom SQL queries to interact with your data."

"monitor"
"Use these skills when you need to troubleshoot slow performance, analyze query execution plans, identify resource-heavy processes, and monitor system-level PromQL metrics."

"health"
"Use these skills when you need to optimize storage, identify index issues, analyze table statistics, or manage autovacuum and tablespace configurations to maintain peak database health."

"optimize"
"Use these skills when you need to discover and manage PostgreSQL extensions or fine-tune engine-level settings such as memory allocation and server configuration parameters."

"replication"
"Use these skills when you need to monitor replication health, manage sync states between nodes, and ensure the high availability and data distribution of your AlloyDB cluster."
)

echo "VALIDATING TOOLSETS BEFORE GENERATION"

# Dynamically build the SUPPORTED_TOOLSETS array from the SKILLS array.
# We use 'set --' to process the array in chunks without index arithmetic.
SUPPORTED_TOOLSETS=()
set -- "${SKILLS[@]}"
while [ $# -gt 0 ]; do
SUPPORTED_TOOLSETS+=("$1")
shift 2
done
Comment on lines +38 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of using set -- and shift which overwrites and clobbers the script's global positional parameters (especially problematic if the script is ever sourced), you can use a standard for loop with index arithmetic to iterate over the array in pairs. This is cleaner and safer.

Suggested change
# Dynamically build the SUPPORTED_TOOLSETS array from the SKILLS array.
# We use 'set --' to process the array in chunks without index arithmetic.
SUPPORTED_TOOLSETS=()
set -- "${SKILLS[@]}"
while [ $# -gt 0 ]; do
SUPPORTED_TOOLSETS+=("$1")
shift 2
done
# Dynamically build the SUPPORTED_TOOLSETS array from the SKILLS array.
SUPPORTED_TOOLSETS=()
for ((i=0; i<${#SKILLS[@]}; i+=2)); do
SUPPORTED_TOOLSETS+=("${SKILLS[i]}")
done


echo "Currently Supported Toolsets: ${SUPPORTED_TOOLSETS[*]}"

# Fetch the upstream source of truth YAML for this specific version
RAW_URL="https://raw.githubusercontent.com/googleapis/mcp-toolbox/v${VERSION}/internal/prebuiltconfigs/tools/alloydb-postgres.yaml"
echo "Fetching upstream config from: $RAW_URL"
UPSTREAM_YAML=$(curl -sL --fail "$RAW_URL" || { echo "Error: Could not fetch upstream YAML for v$VERSION"; exit 1; })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Because this echo is inside a command substitution $(...), its standard output is captured into the UPSTREAM_YAML variable rather than being printed to the terminal. If curl fails, the script will exit due to set -e, and the error message will be completely lost/silenced. Redirecting the message to stderr (>&2) ensures it is visible in the console/logs.

Suggested change
UPSTREAM_YAML=$(curl -sL --fail "$RAW_URL" || { echo "Error: Could not fetch upstream YAML for v$VERSION"; exit 1; })
UPSTREAM_YAML=$(curl -sL --fail "$RAW_URL" || { echo "Error: Could not fetch upstream YAML for v$VERSION" >&2; exit 1; })


# Extract the list of toolsets. Each toolset is its own YAML document:
# kind: toolset
# name: <toolset>
UPSTREAM_TOOLSETS=$(echo "$UPSTREAM_YAML" | awk '$1=="kind:" && $2=="toolset"{f=1; next} f && $1=="name:"{print $2; f=0}')

# Compare upstream toolsets against our supported list
MISSING_TOOLSETS=false

for upstream_tool in $UPSTREAM_TOOLSETS; do
if [ -z "$upstream_tool" ] || [ "$upstream_tool" == "-" ]; then continue; fi

if [[ ! " ${SUPPORTED_TOOLSETS[*]} " =~ " ${upstream_tool} " ]]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using the regular expression operator =~ with an unquoted variable on the right side treats the variable's content as a regex pattern. If upstream_tool contains any regex special characters, it could lead to unexpected matching behavior. Using standard glob matching (== *pattern*) is safer, faster, and avoids regex interpretation.

Suggested change
if [[ ! " ${SUPPORTED_TOOLSETS[*]} " =~ " ${upstream_tool} " ]]; then
if [[ ! " ${SUPPORTED_TOOLSETS[*]} " == *" ${upstream_tool} "* ]]; then

echo "ERROR: Upstream configuration contains a new toolset: '$upstream_tool'"
MISSING_TOOLSETS=true
fi
done

if [ "$MISSING_TOOLSETS" = true ]; then
echo "PIPELINE FAILED: Missing Toolset Generators"
echo "The source of truth file has toolsets that your script does not support."
echo "Please update the SKILLS array in generate_skills.sh to include generators"
echo "for the missing toolsets above, then commit your changes to unblock this PR."
exit 1
fi

echo "Validation passed. All upstream toolsets are supported."

echo "BEGINNING SKILL GENERATION"

LICENSE_HEADER="// 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."

ADDITIONAL_NOTES="Note: The scripts automatically load the environment variables from various .env files. Do not ask the user to set vars unless skill executions fails due to env var absence."

# Base Command Function
generate_skill() {
local TOOLSET="$1"
local SKILL_DESC="$2"
local SKILL_NAME="alloydb-postgres-$TOOLSET"

echo "Generating skill: $SKILL_NAME..."

npx "@toolbox-sdk/server@${VERSION}" --prebuilt alloydb-postgres skills-generate \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In non-interactive environments like CI/CD pipelines, npx may prompt the user to install the package if it is not already cached (e.g., Need to install the following packages... Ok to proceed? (y)). This will cause the build to hang or fail. Adding the --yes flag forces npx to automatically accept the installation prompt.

Suggested change
npx "@toolbox-sdk/server@${VERSION}" --prebuilt alloydb-postgres skills-generate \
npx --yes "@toolbox-sdk/server@${VERSION}" --prebuilt alloydb-postgres skills-generate \

--name "$SKILL_NAME" \
--description "$SKILL_DESC" \
--toolset="$TOOLSET" \
--license-header "$LICENSE_HEADER" \
--additional-notes="$ADDITIONAL_NOTES"
}

set -- "${SKILLS[@]}"
while [ $# -gt 0 ]; do
generate_skill "$1" "$2"
shift 2
done
Comment on lines +115 to +119

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the validation loop, using a standard for loop with index arithmetic is cleaner and avoids clobbering the global positional parameters.

Suggested change
set -- "${SKILLS[@]}"
while [ $# -gt 0 ]; do
generate_skill "$1" "$2"
shift 2
done
for ((i=0; i<${#SKILLS[@]}; i+=2)); do
generate_skill "${SKILLS[i]}" "${SKILLS[i+1]}"
done


echo "All skills generated successfully!"
57 changes: 57 additions & 0 deletions .github/workflows/skills-generate.yml
Original file line number Diff line number Diff line change
@@ -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.

name: Generate Skills

on:
pull_request:
paths:
- "toolbox_version.txt"

jobs:
generate-skills:
# Only run for same-repo PRs (e.g. renovate's toolbox bump), where the
# built-in GITHUB_TOKEN can push back to the PR branch.
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Check out PR branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.head_ref }}

- name: Generate skills
run: |
VERSION="$(tr -d '\n' < toolbox_version.txt)"
echo "Detected toolbox version: $VERSION"
export VERSION
chmod +x ./.github/scripts/generate_skills.sh
./.github/scripts/generate_skills.sh

- name: Commit and push regenerated skills
run: |
if [ -z "$(git status --porcelain)" ]; then
echo "No skill changes generated. Nothing to commit."
exit 0
fi

echo "Changes detected. Committing regenerated skills..."
git config user.name "release-please[bot]"
git config user.email "55107282+release-please[bot]@users.noreply.github.com"

git add .
git commit -m "chore: auto-generate skills for toolbox v$(tr -d '\n' < toolbox_version.txt)"
git push
Loading