diff --git a/.github/workflows/bot-bump-kernel-version.yml b/.github/workflows/bot-bump-kernel-version.yml index baebca87b..a6339e465 100644 --- a/.github/workflows/bot-bump-kernel-version.yml +++ b/.github/workflows/bot-bump-kernel-version.yml @@ -26,14 +26,12 @@ jobs: with: python-version: '3.10' - - name: Configure Git + - name: Configure Git and branch run: | git config user.name "sglang-bot" git config user.email "sglang-bot@users.noreply.github.com" - - - name: Create new branch - run: | - BRANCH_NAME="bot/bump-kernel-version-${{ github.event.inputs.new_version }}" + RANDOM_SUFFIX=$(echo $RANDOM | md5sum | head -c 4) + BRANCH_NAME="bot/bump-kernel-version-${{ github.event.inputs.new_version }}-${RANDOM_SUFFIX}" git checkout -b "$BRANCH_NAME" echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV @@ -41,44 +39,8 @@ jobs: run: | python scripts/release/bump_kernel_version.py "${{ github.event.inputs.new_version }}" - - name: Commit changes - run: | - git add -A - git commit -m "chore: bump sgl-kernel version to ${{ github.event.inputs.new_version }} - - This commit updates the sgl-kernel version across all relevant files: - - sgl-kernel/pyproject.toml - - sgl-kernel/pyproject_cpu.toml - - sgl-kernel/pyproject_rocm.toml - - sgl-kernel/python/sgl_kernel/version.py - - 🤖 Generated with GitHub Actions" - - - name: Push changes - run: | - git push origin "$BRANCH_NAME" - - - name: Create Pull Request + - name: Commit and create PR env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GH_PAT_FOR_TAGGING }} run: | - gh pr create \ - --title "chore: bump sgl-kernel version to ${{ github.event.inputs.new_version }}" \ - --body "## Summary - - This PR bumps the sgl-kernel version to \`${{ github.event.inputs.new_version }}\` across all relevant files. - - ## Files Updated - - sgl-kernel/pyproject.toml - - sgl-kernel/pyproject_cpu.toml - - sgl-kernel/pyproject_rocm.toml - - sgl-kernel/python/sgl_kernel/version.py - - ## Testing - - [ ] Verify all version strings are updated correctly - - [ ] Test kernel installation with new version - - [ ] Run CI tests - - 🤖 Generated with GitHub Actions" \ - --base main \ - --head "$BRANCH_NAME" + bash scripts/release/commit_and_pr.sh "sgl-kernel" "${{ github.event.inputs.new_version }}" "$BRANCH_NAME" diff --git a/.github/workflows/bot-bump-sglang-version.yml b/.github/workflows/bot-bump-sglang-version.yml index f06dbd85f..6c947c632 100644 --- a/.github/workflows/bot-bump-sglang-version.yml +++ b/.github/workflows/bot-bump-sglang-version.yml @@ -26,14 +26,12 @@ jobs: with: python-version: '3.10' - - name: Configure Git + - name: Configure Git and branch run: | git config user.name "sglang-bot" git config user.email "sglang-bot@users.noreply.github.com" - - - name: Create new branch - run: | - BRANCH_NAME="bot/bump-sglang-version-${{ github.event.inputs.new_version }}" + RANDOM_SUFFIX=$(echo $RANDOM | md5sum | head -c 4) + BRANCH_NAME="bot/bump-sglang-version-${{ github.event.inputs.new_version }}-${RANDOM_SUFFIX}" git checkout -b "$BRANCH_NAME" echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV @@ -41,54 +39,8 @@ jobs: run: | python scripts/release/bump_sglang_version.py "${{ github.event.inputs.new_version }}" - - name: Commit changes - run: | - git add -A - git commit -m "chore: bump SGLang version to ${{ github.event.inputs.new_version }} - - This commit updates the SGLang version across all relevant files: - - Makefile - - benchmark/deepseek_v3/README.md - - docker/Dockerfile.rocm - - docs/get_started/install.md - - docs/platforms/amd_gpu.md - - docs/platforms/ascend_npu.md - - python/pyproject.toml - - python/pyproject_other.toml - - python/sglang/version.py - - 🤖 Generated with GitHub Actions" - - - name: Push changes - run: | - git push origin "$BRANCH_NAME" - - - name: Create Pull Request + - name: Commit and create PR env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GH_PAT_FOR_TAGGING }} run: | - gh pr create \ - --title "chore: bump SGLang version to ${{ github.event.inputs.new_version }}" \ - --body "## Summary - - This PR bumps the SGLang version to \`${{ github.event.inputs.new_version }}\` across all relevant files. - - ## Files Updated - - Makefile - - benchmark/deepseek_v3/README.md - - docker/Dockerfile.rocm - - docs/get_started/install.md - - docs/platforms/amd_gpu.md - - docs/platforms/ascend_npu.md - - python/pyproject.toml - - python/pyproject_other.toml - - python/sglang/version.py - - ## Testing - - [ ] Verify all version strings are updated correctly - - [ ] Test installation with new version - - [ ] Run CI tests - - 🤖 Generated with GitHub Actions" \ - --base main \ - --head "$BRANCH_NAME" + bash scripts/release/commit_and_pr.sh "SGLang" "${{ github.event.inputs.new_version }}" "$BRANCH_NAME" diff --git a/scripts/release/bump_kernel_version.py b/scripts/release/bump_kernel_version.py index d8aee551a..9e4929ec7 100755 --- a/scripts/release/bump_kernel_version.py +++ b/scripts/release/bump_kernel_version.py @@ -10,7 +10,10 @@ def main(): parser = argparse.ArgumentParser( description="Bump sgl-kernel version across all relevant files" ) - parser.add_argument("new_version", help="New version (e.g., 0.3.12)") + parser.add_argument( + "new_version", + help="New version (e.g., 0.3.12, 0.3.11rc0, or 0.3.11.post1)", + ) args = parser.parse_args() version_file = Path("sgl-kernel/python/sgl_kernel/version.py") @@ -23,7 +26,7 @@ def main(): Path("sgl-kernel/python/sgl_kernel/version.py"), ] - bump_version(args.new_version, version_file, files_to_update, "kernel") + bump_version(args.new_version, version_file, files_to_update) if __name__ == "__main__": diff --git a/scripts/release/bump_sglang_version.py b/scripts/release/bump_sglang_version.py index 604dc2075..ff06af501 100755 --- a/scripts/release/bump_sglang_version.py +++ b/scripts/release/bump_sglang_version.py @@ -10,7 +10,10 @@ def main(): parser = argparse.ArgumentParser( description="Bump SGLang version across all relevant files" ) - parser.add_argument("new_version", help="New version (e.g., 0.5.3 or 0.5.3rc0)") + parser.add_argument( + "new_version", + help="New version (e.g., 0.5.4, 0.5.3rc0, or 0.5.3.post1)", + ) args = parser.parse_args() version_file = Path("python/sglang/version.py") @@ -27,7 +30,7 @@ def main(): Path("python/sglang/version.py"), ] - bump_version(args.new_version, version_file, files_to_update, "sglang") + bump_version(args.new_version, version_file, files_to_update) if __name__ == "__main__": diff --git a/scripts/release/commit_and_pr.sh b/scripts/release/commit_and_pr.sh new file mode 100755 index 000000000..b61ec6aba --- /dev/null +++ b/scripts/release/commit_and_pr.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -e + +# Script to commit version bump changes and create a pull request +# Usage: commit_and_pr.sh +# +# Arguments: +# version_type: "SGLang" or "sgl-kernel" +# new_version: The new version number +# branch_name: The git branch name to push to + +VERSION_TYPE="$1" +NEW_VERSION="$2" +BRANCH_NAME="$3" + +if [ -z "$VERSION_TYPE" ] || [ -z "$NEW_VERSION" ] || [ -z "$BRANCH_NAME" ]; then + echo "Error: Missing required arguments" + echo "Usage: $0 " + exit 1 +fi + +# Get changed files and format them +echo "Getting changed files..." +FILES_LIST=$(git diff --name-only | sed 's/^/- /') +COMMIT_FILES=$(git diff --name-only | sed 's/^/ - /') + +# Commit changes +echo "Committing changes..." +git add -A +git commit -m "chore: bump ${VERSION_TYPE} version to ${NEW_VERSION} + +This commit updates the ${VERSION_TYPE} version across all relevant files: +${COMMIT_FILES} + +🤖 Generated with GitHub Actions" + +# Push changes +echo "Pushing to ${BRANCH_NAME}..." +git push origin "${BRANCH_NAME}" + +# Create pull request +echo "Creating pull request..." +PR_URL=$(gh pr create \ + --title "chore: bump ${VERSION_TYPE} version to ${NEW_VERSION}" \ + --body "## Summary + +This PR bumps the ${VERSION_TYPE} version to \`${NEW_VERSION}\` across all relevant files. + +## Files Updated +${FILES_LIST} + +🤖 Generated with GitHub Actions" \ + --base main \ + --head "${BRANCH_NAME}") + +echo "✓ Pull request created successfully" + +# Add GitHub Actions job summary +if [ -n "$GITHUB_STEP_SUMMARY" ]; then + cat >> "$GITHUB_STEP_SUMMARY" < stable of lower patch + self.assertEqual(compare_versions("0.5.4rc0", "0.5.3"), 1) + self.assertEqual(compare_versions("0.5.3.post1", "0.5.4rc0"), -1) + + def test_compare_versions_different_minor(self): + """Test comparing versions with different minor numbers.""" + self.assertEqual(compare_versions("0.4.9", "0.5.0"), -1) + self.assertEqual(compare_versions("0.5.0", "0.4.9"), 1) + + def test_compare_versions_different_major(self): + """Test comparing versions with different major numbers.""" + self.assertEqual(compare_versions("0.9.9", "1.0.0"), -1) + self.assertEqual(compare_versions("1.0.0", "0.9.9"), 1) + + def test_real_world_scenarios(self): + """Test real-world version bump scenarios.""" + # Scenario 1: RC progression + self.assertEqual(compare_versions("0.5.3rc0", "0.5.3rc1"), -1) + + # Scenario 2: RC to stable release + self.assertEqual(compare_versions("0.5.3rc2", "0.5.3"), -1) + + # Scenario 3: Stable to post-release hotfix + self.assertEqual(compare_versions("0.5.3", "0.5.3.post1"), -1) + + # Scenario 4: Post-release to next RC + self.assertEqual(compare_versions("0.5.3.post1", "0.5.4rc0"), -1) + + # Scenario 5: Next stable version + self.assertEqual(compare_versions("0.5.3", "0.5.4"), -1) + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/release/utils.py b/scripts/release/utils.py index 82897ce6d..efbed9ef6 100644 --- a/scripts/release/utils.py +++ b/scripts/release/utils.py @@ -9,15 +9,69 @@ def normalize_version(version: str) -> str: return version.lstrip("v") -def validate_version(version: str, version_type: str = "sglang") -> bool: - if version_type == "sglang": - pattern = r"^\d+\.\d+\.\d+(rc\d+)?$" - else: - pattern = r"^\d+\.\d+\.\d+$" +def validate_version(version: str) -> bool: + """Validate version format: X.Y.Z, X.Y.Zrc0, or X.Y.Z.post1""" + pattern = r"^\d+\.\d+\.\d+(rc\d+|\.post\d+)?$" + return bool(re.match(pattern, version)) - if not re.match(pattern, version): - return False - return True + +def parse_version(version: str) -> Tuple[int, int, int, int, int]: + """ + Parse version string into comparable components. + + Returns: (major, minor, patch, pre_release, post_release) + - pre_release: -1000 + rc_number for rcN, 0 for stable (rc0 < rc1 < stable) + - post_release: N for .postN, 0 otherwise + + The pre_release field uses negative numbers to ensure RC versions come before + stable versions when tuples are compared. Python compares tuples element by + element, so (0, 5, 3, -1000, 0) < (0, 5, 3, 0, 0) ensures rc0 < stable. + + Examples: + - "0.5.3rc0" → (0, 5, 3, -1000, 0) # rc0 comes before stable + - "0.5.3rc1" → (0, 5, 3, -999, 0) # rc1 comes after rc0 + - "0.5.3" → (0, 5, 3, 0, 0) # stable version + - "0.5.3.post1" → (0, 5, 3, 0, 1) # post comes after stable + """ + # Match version components + match = re.match(r"^(\d+)\.(\d+)\.(\d+)(?:rc(\d+)|\.post(\d+))?$", version) + if not match: + raise ValueError(f"Invalid version format: {version}") + + major, minor, patch, rc, post = match.groups() + major, minor, patch = int(major), int(minor), int(patch) + + if rc is not None: + # RC version: pre_release = -1000 + rc_number (ensures rc0 < rc1 < ... < stable) + return (major, minor, patch, -1000 + int(rc), 0) + elif post is not None: + # Post version: post_release = N + return (major, minor, patch, 0, int(post)) + else: + # Stable version + return (major, minor, patch, 0, 0) + + +def compare_versions(v1: str, v2: str) -> int: + """ + Compare two version strings following PEP 440 ordering. + + Returns: + - -1 if v1 < v2 + - 0 if v1 == v2 + - 1 if v1 > v2 + + Version ordering: X.Y.ZrcN < X.Y.Z < X.Y.Z.postN < X.Y.(Z+1) + """ + parsed_v1 = parse_version(v1) + parsed_v2 = parse_version(v2) + + if parsed_v1 < parsed_v2: + return -1 + elif parsed_v1 > parsed_v2: + return 1 + else: + return 0 def get_repo_root() -> Path: @@ -53,17 +107,14 @@ def bump_version( new_version: str, version_file: Path, files_to_update: List[Path], - version_type: str = "sglang", ) -> None: # Normalize version (remove 'v' prefix if present) new_version = normalize_version(new_version) - if not validate_version(new_version, version_type): + if not validate_version(new_version): print(f"Error: Invalid version format: {new_version}") - if version_type == "sglang": - print("Expected format: X.Y.Z or X.Y.ZrcN (e.g., 0.5.3 or 0.5.3rc0)") - else: - print("Expected format: X.Y.Z (e.g., 0.3.12)") + print("Expected format: X.Y.Z, X.Y.ZrcN, or X.Y.Z.postN") + print("Examples: 0.5.4, 0.5.3rc0, 0.5.3.post1") sys.exit(1) repo_root = get_repo_root() @@ -78,9 +129,17 @@ def bump_version( print(f"New version: {new_version}") print() - if old_version == new_version: - print("Warning: New version is the same as current version") - sys.exit(0) + # Compare versions + comparison = compare_versions(new_version, old_version) + if comparison == 0: + print("Error: New version is the same as current version") + sys.exit(1) + elif comparison < 0: + print( + f"Error: New version ({new_version}) is older than current version ({old_version})" + ) + print("Version must be greater than the current version") + sys.exit(1) updated_count = 0 for file_rel in files_to_update: