| #!/bin/bash |
| |
| |
|
|
| set -e |
|
|
| |
| GATEWAY_PATHS=( |
| "sgl-model-gateway" |
| "python/sglang/srt/grpc" |
| "python/sglang/srt/entrypoints/grpc_server.py" |
| ) |
|
|
| |
| GREEN='\033[0;32m' |
| BLUE='\033[0;34m' |
| YELLOW='\033[1;33m' |
| NC='\033[0m' |
|
|
| |
| 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 |
| } |
|
|
| |
| OUTPUT_FILE="" |
| FORMAT="markdown" |
| CREATE_RELEASE=false |
| DRAFT_RELEASE="default" |
| 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 |
|
|
| |
| if [[ -z "$PREV_TAG" ]] || [[ -z "$CURR_TAG" ]]; then |
| echo "Error: Both previous and current tags are required" |
| usage |
| fi |
|
|
| |
| 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 |
|
|
| |
| 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 |
|
|
| |
| PATH_ARGS=() |
| for path in "${GATEWAY_PATHS[@]}"; do |
| PATH_ARGS+=("--" "$path") |
| done |
|
|
| |
| 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 |
|
|
| |
| echo -e "${BLUE}Analyzing contributors...${NC}" >&2 |
|
|
| |
| CONTRIBUTORS=$(git log "$PREV_TAG..$CURR_TAG" --format='%aN <%aE>' --no-merges "${PATH_ARGS[@]}" | sort | uniq -c | sort -rn) |
|
|
| |
| |
| 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) |
|
|
| |
| 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_notes() { |
| case $FORMAT in |
| markdown|github) |
| echo "## What's Changed in Gateway" |
| echo "" |
| echo "### Gateway Changes ($COMMIT_COUNT commits)" |
| echo "" |
|
|
| |
| echo "$COMMITS" | while IFS= read -r line; do |
| commit_hash=$(echo "$line" | awk '{print $1}') |
| commit_msg=$(echo "$line" | cut -d' ' -f2-) |
|
|
| |
| pr_num=$(echo "$commit_msg" | grep -o '(#[0-9]*' | grep -o '[0-9]*' | head -1) |
|
|
| |
| 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 |
|
|
| |
| 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 [[ "$gh_user" == *"@"* ]]; then |
| gh_user="" |
| fi |
| fi |
|
|
| |
| if [[ -n "$gh_user" ]]; then |
| author_link="by @$gh_user" |
| else |
| |
| author=$(git show -s --format='%aN' "$commit_hash") |
| author_link="by $author" |
| fi |
|
|
| |
| 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 |
|
|
| |
| 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 |
| |
| name=$(echo "$contributor" | sed 's/ <.*//') |
| email=$(echo "$contributor" | sed 's/.*<\(.*\)>/\1/') |
|
|
| |
| first_commit=$(git log "$PREV_TAG..$CURR_TAG" --author="$contributor" --format='%h' --reverse --no-merges "${PATH_ARGS[@]}" | head -1) |
|
|
| |
| 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 |
|
|
| |
| if [[ -z "$gh_user" ]]; then |
| gh_user=$(echo "$email" | sed 's/@users\.noreply\.github\.com$//' | sed 's/^[0-9]*+//') |
| |
| 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 |
| } |
|
|
| |
| if [[ -n "$OUTPUT_FILE" ]]; then |
| generate_notes > "$OUTPUT_FILE" |
| echo -e "${GREEN}Release notes saved to: $OUTPUT_FILE${NC}" >&2 |
| else |
| generate_notes |
| fi |
|
|
| |
| 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" |
|
|
| |
| 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 |
|
|
| |
| 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 |
|
|