[model-gateway] Add Gateway Release Tooling (#13420)

This commit is contained in:
Simo Lin
2025-11-17 03:26:48 -08:00
committed by GitHub
parent 8b5e2c5368
commit b436113fc2
3 changed files with 393 additions and 1 deletions
+26 -1
View File
@@ -19,7 +19,8 @@ else
endif endif
.PHONY: help build test clean docs check fmt dev-setup pre-commit setup-sccache sccache-stats sccache-clean sccache-stop \ .PHONY: help build test clean docs check fmt dev-setup pre-commit setup-sccache sccache-stats sccache-clean sccache-stop \
python-dev python-build python-build-release python-install python-clean python-test python-check python-dev python-build python-build-release python-install python-clean python-test python-check \
release-notes
help: ## Show this help message help: ## Show this help message
@echo "Model Gateway Development Commands" @echo "Model Gateway Development Commands"
@@ -132,3 +133,27 @@ python-check: ## Check Python package with twine
dev: python-dev ## Quick development setup (build Python bindings in dev mode) dev: python-dev ## Quick development setup (build Python bindings in dev mode)
install: python-install ## Build and install everything install: python-install ## Build and install everything
# Release management
release-notes: ## Generate release notes for gateway (usage: make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0)
@if [ -z "$(PREV)" ] || [ -z "$(CURR)" ]; then \
echo "Usage: make release-notes PREV=<previous-tag> CURR=<current-tag>"; \
echo "Example: make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0"; \
echo ""; \
echo "Options:"; \
echo " OUTPUT=<file> Save to file (default: stdout)"; \
echo " CREATE_RELEASE=1 Create GitHub draft release via gh CLI (default: draft)"; \
echo " DRAFT=0 Publish release immediately (skip draft)"; \
exit 1; \
fi
@ARGS="$(PREV) $(CURR)"; \
if [ -n "$(OUTPUT)" ]; then \
ARGS="$$ARGS --output $(OUTPUT)"; \
fi; \
if [ "$(CREATE_RELEASE)" = "1" ]; then \
ARGS="$$ARGS --create-release"; \
if [ "$(DRAFT)" = "0" ]; then \
ARGS="$$ARGS --no-draft"; \
fi; \
fi; \
./scripts/generate_gateway_release_notes.sh $$ARGS
+55
View File
@@ -587,4 +587,59 @@ For production builds, use `maturin build --release --out dist` from the `bindin
--- ---
## Release Management
### Creating Gateway Releases
Create releases for the Gateway/Router component with filtered commits:
```bash
# Using make
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0
# Save to file
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0 OUTPUT=RELEASE_NOTES.md
# Create draft release (requires gh CLI, DEFAULT behavior)
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0 CREATE_RELEASE=1
# Publish release immediately (requires gh CLI)
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0 CREATE_RELEASE=1 DRAFT=0
```
**Tag Naming**: Use `gateway-*` or `router-*` prefixes to avoid triggering unrelated CI workflows.
### Release Workflow
1. **Create and push tag**:
```bash
git tag -a gateway-v1.0.0 <commit-hash> -m "Gateway release v1.0.0"
git push origin gateway-v1.0.0
```
2. **Generate release notes** (automatically filters gateway-related commits):
```bash
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0
```
3. **Create GitHub release**:
```bash
# Create draft (DEFAULT - review before publishing)
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0 CREATE_RELEASE=1
# Or publish immediately (skip draft)
make release-notes PREV=gateway-v0.2.2 CURR=gateway-v1.0.0 CREATE_RELEASE=1 DRAFT=0
```
### Filtered Paths
Release notes only include commits touching:
- `sgl-router/` - Router codebase
- `python/sglang/srt/grpc/` - gRPC protocol
- `python/sglang/srt/entrypoints/grpc_server.py` - gRPC server
The script automatically extracts author attribution, PR links, and identifies new contributors.
---
SGLang Model Gateway continues to evolve alongside the core SGLang runtime. Contributions should keep CLI flags, documentation, and Python bindings in sync with the Rust implementation. SGLang Model Gateway continues to evolve alongside the core SGLang runtime. Contributions should keep CLI flags, documentation, and Python bindings in sync with the Rust implementation.
+312
View File
@@ -0,0 +1,312 @@
#!/bin/bash
# Generate release notes for SGLang Gateway/Router
# Only includes commits that affect gateway-related paths
set -e
# Configuration
GATEWAY_PATHS=(
"sgl-router"
"python/sglang/srt/grpc"
"python/sglang/srt/entrypoints/grpc_server.py"
)
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to display usage
usage() {
echo "Usage: $0 <previous-tag> <current-tag>"
echo ""
echo "Example: $0 gateway-v0.2.2 gateway-v0.2.3"
echo ""
echo "Options:"
echo " -o, --output FILE Save output to file (default: stdout)"
echo " -f, --format FORMAT Output format: markdown|github|plain (default: markdown)"
echo " --create-release Create GitHub release using gh CLI (default: draft)"
echo " --draft Create as draft release (default when using --create-release)"
echo " --no-draft Publish release immediately (skip draft)"
echo " -h, --help Show this help message"
exit 1
}
# Parse arguments
OUTPUT_FILE=""
FORMAT="markdown"
CREATE_RELEASE=false
DRAFT_RELEASE="default" # Default to draft unless explicitly disabled
PREV_TAG=""
CURR_TAG=""
while [[ $# -gt 0 ]]; do
case $1 in
-o|--output)
OUTPUT_FILE="$2"
shift 2
;;
-f|--format)
FORMAT="$2"
shift 2
;;
--create-release)
CREATE_RELEASE=true
shift
;;
--draft)
DRAFT_RELEASE=true
shift
;;
--no-draft)
DRAFT_RELEASE=false
shift
;;
-h|--help)
usage
;;
*)
if [[ -z "$PREV_TAG" ]]; then
PREV_TAG="$1"
elif [[ -z "$CURR_TAG" ]]; then
CURR_TAG="$1"
else
echo "Error: Too many arguments"
usage
fi
shift
;;
esac
done
# Validate arguments
if [[ -z "$PREV_TAG" ]] || [[ -z "$CURR_TAG" ]]; then
echo "Error: Both previous and current tags are required"
usage
fi
# Navigate to repo root (main sglang repo, not sgl-router)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$REPO_ROOT"
echo -e "${BLUE}Generating Gateway/Router release notes...${NC}" >&2
echo -e "${BLUE}Previous: $PREV_TAG${NC}" >&2
echo -e "${BLUE}Current: $CURR_TAG${NC}" >&2
echo "" >&2
# Verify tags exist
if ! git rev-parse "$PREV_TAG" >/dev/null 2>&1; then
echo -e "${YELLOW}Warning: Tag $PREV_TAG not found, using initial commit${NC}" >&2
PREV_TAG=$(git rev-list --max-parents=0 HEAD)
fi
if ! git rev-parse "$CURR_TAG" >/dev/null 2>&1; then
echo -e "${YELLOW}Warning: Tag $CURR_TAG not found, using HEAD${NC}" >&2
CURR_TAG="HEAD"
fi
# Build path filter arguments
PATH_ARGS=()
for path in "${GATEWAY_PATHS[@]}"; do
PATH_ARGS+=("--" "$path")
done
# Get filtered commit list
COMMITS=$(git log "$PREV_TAG..$CURR_TAG" --oneline --no-merges "${PATH_ARGS[@]}")
if [[ -z "$COMMITS" ]]; then
echo -e "${YELLOW}No commits found for gateway paths between $PREV_TAG and $CURR_TAG${NC}" >&2
exit 0
fi
COMMIT_COUNT=$(echo "$COMMITS" | wc -l | tr -d ' ')
echo -e "${GREEN}Found $COMMIT_COUNT gateway-related commits${NC}" >&2
# Get contributors
echo -e "${BLUE}Analyzing contributors...${NC}" >&2
# Get all contributors in this release (with commit count)
CONTRIBUTORS=$(git log "$PREV_TAG..$CURR_TAG" --format='%aN <%aE>' --no-merges "${PATH_ARGS[@]}" | sort | uniq -c | sort -rn)
# Get all contributors before this release (from initial commit up to PREV_TAG)
# Using $(git rev-list --max-parents=0 HEAD) to get initial commit ensures we check entire history
INITIAL_COMMIT=$(git rev-list --max-parents=0 HEAD | tail -1)
PREV_CONTRIBUTORS=$(git log "$INITIAL_COMMIT..$PREV_TAG" --format='%aN <%aE>' --no-merges "${PATH_ARGS[@]}" | sort | uniq)
# Find new contributors
NEW_CONTRIBUTORS=""
while IFS= read -r line; do
contributor=$(echo "$line" | sed 's/^[[:space:]]*[0-9]*[[:space:]]*//')
if [[ -n "$contributor" ]] && ! echo "$PREV_CONTRIBUTORS" | grep -Fxq "$contributor"; then
NEW_CONTRIBUTORS="$NEW_CONTRIBUTORS$contributor"$'\n'
fi
done <<< "$CONTRIBUTORS"
CONTRIBUTOR_COUNT=$(echo "$CONTRIBUTORS" | grep -c '^' || echo 0)
NEW_CONTRIBUTOR_COUNT=$(echo "$NEW_CONTRIBUTORS" | grep -c '^' || echo 0)
echo -e "${GREEN}Found $CONTRIBUTOR_COUNT contributors ($NEW_CONTRIBUTOR_COUNT new)${NC}" >&2
echo "" >&2
# Generate release notes based on format
generate_notes() {
case $FORMAT in
markdown|github)
echo "## What's Changed in Gateway"
echo ""
echo "### Gateway Changes ($COMMIT_COUNT commits)"
echo ""
# Categorize commits with author attribution
echo "$COMMITS" | while IFS= read -r line; do
commit_hash=$(echo "$line" | awk '{print $1}')
commit_msg=$(echo "$line" | cut -d' ' -f2-)
# Get PR number from commit message
pr_num=$(echo "$commit_msg" | grep -o '(#[0-9]*' | grep -o '[0-9]*' | head -1)
# Try to get GitHub username from PR if gh CLI is available
gh_user=""
if [[ -n "$pr_num" ]] && command -v gh &> /dev/null; then
gh_user=$(gh pr view "$pr_num" --json author --jq '.author.login' 2>/dev/null || echo "")
fi
# Fallback: try to extract from email (works for users.noreply.github.com emails)
if [[ -z "$gh_user" ]]; then
email=$(git show -s --format='%aE' "$commit_hash")
gh_user=$(echo "$email" | sed 's/@users\.noreply\.github\.com$//' | sed 's/^[0-9]*+//')
# If still contains @, it's not a GitHub username
if [[ "$gh_user" == *"@"* ]]; then
gh_user=""
fi
fi
# Format author link
if [[ -n "$gh_user" ]]; then
author_link="by @$gh_user"
else
# Final fallback: use full name
author=$(git show -s --format='%aN' "$commit_hash")
author_link="by $author"
fi
# Format PR link
if [[ -n "$pr_num" ]]; then
pr_link="in https://github.com/sgl-project/sglang/pull/$pr_num"
else
pr_link=""
fi
echo "- $commit_msg $author_link $pr_link"
done
# New Contributors section
if [[ -n "$NEW_CONTRIBUTORS" ]] && [[ "$NEW_CONTRIBUTOR_COUNT" -gt 0 ]]; then
echo ""
echo "### New Contributors"
echo ""
while IFS= read -r contributor; do
if [[ -n "$contributor" ]]; then
# Extract name and email
name=$(echo "$contributor" | sed 's/ <.*//')
email=$(echo "$contributor" | sed 's/.*<\(.*\)>/\1/')
# Get their first commit
first_commit=$(git log "$PREV_TAG..$CURR_TAG" --author="$contributor" --format='%h' --reverse --no-merges "${PATH_ARGS[@]}" | head -1)
# Try to get GitHub username from first commit's PR
gh_user=""
if command -v gh &> /dev/null; then
commit_msg=$(git log --format=%s -n 1 "$first_commit")
pr_num=$(echo "$commit_msg" | grep -o '(#[0-9]*' | grep -o '[0-9]*' | head -1)
if [[ -n "$pr_num" ]]; then
gh_user=$(gh pr view "$pr_num" --json author --jq '.author.login' 2>/dev/null || echo "")
fi
fi
# Fallback: try to get GitHub username from email
if [[ -z "$gh_user" ]]; then
gh_user=$(echo "$email" | sed 's/@users\.noreply\.github\.com$//' | sed 's/^[0-9]*+//')
# If still contains @, it's not a GitHub username
if [[ "$gh_user" == *"@"* ]]; then
gh_user=""
fi
fi
if [[ -n "$gh_user" ]]; then
echo "* @$gh_user made their first contribution in https://github.com/sgl-project/sglang/commit/$first_commit"
else
echo "* $name made their first contribution in https://github.com/sgl-project/sglang/commit/$first_commit"
fi
fi
done <<< "$NEW_CONTRIBUTORS"
fi
echo ""
echo "### Paths Included"
echo ""
for path in "${GATEWAY_PATHS[@]}"; do
echo "- \`$path\`"
done
echo ""
echo "**Full Changelog**: https://github.com/sgl-project/sglang/compare/$PREV_TAG...$CURR_TAG"
;;
plain)
echo "Gateway/Router Release Notes: $CURR_TAG"
echo "=========================================="
echo ""
echo "$COMMITS"
echo ""
echo "Contributors: $CONTRIBUTOR_COUNT ($NEW_CONTRIBUTOR_COUNT new)"
;;
esac
}
# Output release notes
if [[ -n "$OUTPUT_FILE" ]]; then
generate_notes > "$OUTPUT_FILE"
echo -e "${GREEN}Release notes saved to: $OUTPUT_FILE${NC}" >&2
else
generate_notes
fi
# Create GitHub release if requested
if [[ "$CREATE_RELEASE" == true ]]; then
if ! command -v gh &> /dev/null; then
echo -e "${YELLOW}Error: gh CLI not found. Install from https://cli.github.com/${NC}" >&2
exit 1
fi
NOTES_FILE=$(mktemp)
generate_notes > "$NOTES_FILE"
# Default to draft if not explicitly set to false
if [[ "$DRAFT_RELEASE" == "default" ]]; then
DRAFT_RELEASE=true
fi
echo "" >&2
if [[ "$DRAFT_RELEASE" == true ]]; then
echo -e "${BLUE}Creating GitHub DRAFT release...${NC}" >&2
else
echo -e "${BLUE}Creating GitHub release (publishing immediately)...${NC}" >&2
fi
# Build gh command with optional --draft flag
GH_ARGS=("$CURR_TAG" --title "Gateway/Router $CURR_TAG" --notes-file "$NOTES_FILE" --repo sgl-project/sglang)
if [[ "$DRAFT_RELEASE" == true ]]; then
GH_ARGS+=(--draft)
fi
gh release create "${GH_ARGS[@]}"
rm -f "$NOTES_FILE"
if [[ "$DRAFT_RELEASE" == true ]]; then
echo -e "${GREEN}Draft release created successfully!${NC}" >&2
echo -e "${YELLOW}Visit https://github.com/sgl-project/sglang/releases to review and publish${NC}" >&2
else
echo -e "${GREEN}Release published successfully!${NC}" >&2
fi
fi