chore: vendor sglang v0.5.10 snapshot

This commit is contained in:
2026-04-24 12:29:36 +00:00
parent 78f0d15221
commit bded08301f
4308 changed files with 1200894 additions and 2 deletions

View File

@@ -0,0 +1,483 @@
"""
List commits in the private repo that need to be synced to the OSS repo.
NOTE:
1. This script resolves the git root automatically and can be run anywhere
inside the repo.
This script will:
1. Find the most recent sync commit (message starts with
"[Automated PR] Copy OSS code from commit").
2. Scan commits after that point and keep those that touch the configured paths.
3. Compare added diff lines in relevant files against OSS main.
4. Print a markdown summary with commit links and write it to GitHub Step Summary.
Usage:
python3 scripts/code_sync/check_commits.py
"""
import argparse
import os
import shutil
import subprocess
import sys
from dataclasses import dataclass
from typing import Dict, List, Optional, Set, Tuple
# Allow sibling imports regardless of the working directory.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from utils import ( # noqa: E402
FOLDER_NAMES,
get_last_sync_commit,
write_github_step_summary,
)
# --- Configuration Begin ---
private_repo = "your-org/sglang-private-repo"
oss_repo_url = "https://github.com/sgl-project/sglang.git"
oss_repo_branch = "main"
default_oss_repo_dir = ".oss_repo"
# --- Configuration End ---
@dataclass
class CommitInfo:
commit_hash: str
subject: str
commit_date: str
relevant_files: List[str]
synced_lines: int
total_added_lines: int
def check_dependencies() -> None:
"""Check for required command-line tools."""
if not shutil.which("git"):
raise EnvironmentError("git is not installed or not in PATH.")
def get_repo_root() -> str:
try:
output = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
check=True,
).stdout.strip()
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Unable to determine git repo root: {e.stderr or e}") from e
if not output:
raise RuntimeError("Unable to determine git repo root.")
return os.path.abspath(output)
def get_repo_from_origin(repo_root: str) -> str:
"""Try to infer the repo slug (owner/name) from git remote.origin.url."""
try:
url = subprocess.run(
["git", "config", "--get", "remote.origin.url"],
capture_output=True,
text=True,
check=True,
cwd=repo_root,
).stdout.strip()
except subprocess.CalledProcessError:
return private_repo
if url.startswith("git@github.com:"):
repo = url.split("git@github.com:", 1)[1]
elif url.startswith("https://github.com/"):
repo = url.split("https://github.com/", 1)[1]
else:
return private_repo
if repo.endswith(".git"):
repo = repo[: -len(".git")]
return repo or private_repo
def get_default_oss_repo_path(repo_root: str) -> str:
env_path = os.environ.get("OSS_REPO_PATH")
if env_path:
return os.path.abspath(env_path)
return os.path.abspath(os.path.join(repo_root, default_oss_repo_dir))
def ensure_oss_repo(oss_repo_path: str, repo_url: str, branch: str) -> str:
oss_repo_path = os.path.abspath(oss_repo_path)
if os.path.exists(oss_repo_path) and not os.path.isdir(oss_repo_path):
raise RuntimeError(f"OSS repo path is not a directory: {oss_repo_path}")
if os.path.isdir(os.path.join(oss_repo_path, ".git")):
try:
subprocess.run(
["git", "-C", oss_repo_path, "rev-parse", "--is-inside-work-tree"],
capture_output=True,
text=True,
check=True,
)
except subprocess.CalledProcessError as e:
raise RuntimeError(
f"OSS repo path exists but is not a git repo: {oss_repo_path}"
) from e
subprocess.run(
["git", "-C", oss_repo_path, "fetch", "origin", branch, "--depth", "1"],
check=True,
)
return oss_repo_path
parent_dir = os.path.dirname(oss_repo_path)
if parent_dir and not os.path.isdir(parent_dir):
os.makedirs(parent_dir, exist_ok=True)
subprocess.run(
["git", "clone", "--depth", "1", "--branch", branch, repo_url, oss_repo_path],
check=True,
)
return oss_repo_path
def get_commits_since(repo_root: str, last_sync_hash: Optional[str]) -> List[str]:
"""Get commit hashes from last sync commit (exclusive) to HEAD."""
try:
if last_sync_hash:
command = ["git", "rev-list", f"{last_sync_hash}..HEAD"]
else:
command = ["git", "rev-list", "HEAD"]
result = subprocess.run(
command, capture_output=True, text=True, check=True, cwd=repo_root
).stdout.strip()
return [line for line in result.split("\n") if line]
except subprocess.CalledProcessError as e:
print(f"Error getting commit list: {e.stderr}")
return []
def get_changed_files(repo_root: str, commit_hash: str) -> List[str]:
try:
output = subprocess.run(
["git", "diff-tree", "--no-commit-id", "--name-only", "-r", commit_hash],
capture_output=True,
text=True,
check=True,
cwd=repo_root,
).stdout.strip()
return [line for line in output.split("\n") if line]
except subprocess.CalledProcessError as e:
print(f"Error getting changed files for {commit_hash}: {e.stderr}")
return []
def is_relevant_path(changed_file: str, path_prefix: str) -> bool:
if changed_file == path_prefix:
return True
return changed_file.startswith(f"{path_prefix}/")
def get_relevant_files(changed_files: List[str]) -> List[str]:
return [
changed_file
for changed_file in changed_files
if any(is_relevant_path(changed_file, path) for path in FOLDER_NAMES)
]
def get_added_lines_by_file(
repo_root: str, commit_hash: str, relevant_files: List[str]
) -> Dict[str, List[str]]:
if not relevant_files:
return {}
command = [
"git",
"show",
"--no-color",
"--unified=0",
"--format=",
commit_hash,
"--",
] + relevant_files
try:
output = subprocess.run(
command, capture_output=True, text=True, check=True, cwd=repo_root
).stdout
except subprocess.CalledProcessError as e:
print(f"Error getting diff for {commit_hash}: {e.stderr}")
return {}
added_lines: Dict[str, List[str]] = {path: [] for path in relevant_files}
relevant_set = set(relevant_files)
current_file: Optional[str] = None
for line in output.splitlines():
if line.startswith("diff --git "):
current_file = None
continue
if line.startswith("+++ "):
file_path = None
if line.startswith("+++ b/"):
file_path = line[6:]
else:
candidate = line[4:]
if candidate == "/dev/null":
file_path = None
elif candidate.startswith("b/") or candidate.startswith("a/"):
file_path = candidate[2:]
else:
file_path = candidate
if file_path in relevant_set:
current_file = file_path
else:
current_file = None
continue
if current_file and line.startswith("+") and not line.startswith("+++ "):
added_lines[current_file].append(line[1:])
return added_lines
def get_oss_file_lines(
oss_repo_path: str,
oss_ref: str,
file_path: str,
cache: Dict[str, Optional[Set[str]]],
) -> Optional[Set[str]]:
if file_path in cache:
return cache[file_path]
try:
output = subprocess.run(
["git", "-C", oss_repo_path, "show", f"{oss_ref}:{file_path}"],
capture_output=True,
text=True,
errors="replace",
check=True,
).stdout
except subprocess.CalledProcessError:
cache[file_path] = None
return None
lines = output.splitlines()
line_set = set(lines)
cache[file_path] = line_set
return line_set
def count_synced_lines(
added_lines_by_file: Dict[str, List[str]],
oss_repo_path: str,
oss_ref: str,
oss_file_cache: Dict[str, Optional[Set[str]]],
) -> Tuple[int, int]:
total_added_lines = 0
synced_lines = 0
for file_path, lines in added_lines_by_file.items():
total_added_lines += len(lines)
if not lines:
continue
oss_lines = get_oss_file_lines(
oss_repo_path, oss_ref, file_path, oss_file_cache
)
if not oss_lines:
continue
for line in lines:
if line in oss_lines:
synced_lines += 1
return synced_lines, total_added_lines
def get_commit_summary(repo_root: str, commit_hash: str) -> Tuple[str, str]:
"""Return (subject, date) for a commit."""
try:
output = subprocess.run(
["git", "show", "-s", "--format=%s%x00%ad", "--date=short", commit_hash],
capture_output=True,
text=True,
check=True,
cwd=repo_root,
).stdout.strip()
subject, commit_date = output.split("\x00", 1)
except subprocess.CalledProcessError as e:
print(f"Error getting commit subject for {commit_hash}: {e.stderr}")
subject = "(unknown subject)"
commit_date = "(unknown date)"
return subject, commit_date
def format_files_list(relevant_files: List[str]) -> str:
return "\n".join([f"- {file_path}" for file_path in relevant_files])
def format_last_sync_block(
repo: str, subject: str, commit_hash: str, commit_date: str
) -> str:
short_hash = commit_hash[:9]
commit_url = f"https://github.com/{repo}/commit/{commit_hash}"
return "\n".join(
[
"## Last sync",
"",
f"#### {subject}",
f"date: {commit_date}",
f"commit: [{short_hash}]({commit_url})",
"",
]
)
def format_commit_block(
repo: str,
subject: str,
commit_hash: str,
commit_date: str,
relevant_files: List[str],
synced_lines: int,
total_added_lines: int,
) -> str:
short_hash = commit_hash[:9]
commit_url = f"https://github.com/{repo}/commit/{commit_hash}"
files_str = format_files_list(relevant_files) if relevant_files else "- None"
status_icon = "" if synced_lines == total_added_lines else ""
status_line = (
f"status: {status_icon} {synced_lines}/{total_added_lines} lines synced"
)
return "\n".join(
[
f"#### {subject}",
status_line,
f"date: {commit_date}",
"files to sync:",
files_str,
"",
f"commit: [{short_hash}]({commit_url})",
"",
]
)
def format_output(
repo: str,
last_sync: Optional[Tuple[str, str, str]],
commits: List[CommitInfo],
) -> str:
lines: List[str] = []
if last_sync:
subject, commit_hash, commit_date = last_sync
lines.append(format_last_sync_block(repo, subject, commit_hash, commit_date))
else:
lines.extend(["## Last sync", "", "No sync commit found.", ""])
lines.extend(["## Commits to sync", ""])
if not commits:
lines.append("No commits need to be synced.")
return "\n".join(lines) + "\n"
for commit in commits:
lines.append(
format_commit_block(
repo,
commit.subject,
commit.commit_hash,
commit.commit_date,
commit.relevant_files,
commit.synced_lines,
commit.total_added_lines,
)
)
return "\n".join(lines)
def main() -> None:
parser = argparse.ArgumentParser(
description="List commits in the private repo that need to be synced to OSS."
)
parser.add_argument(
"--limit",
type=int,
default=0,
help="Limit number of commits printed (0 means no limit).",
)
parser.add_argument(
"--oss-repo-path",
default=None,
help="Path to OSS repo clone (default: $OSS_REPO_PATH or .oss_repo).",
)
parser.add_argument(
"--oss-repo-url",
default=oss_repo_url,
help="OSS repo URL (default: https://github.com/sgl-project/sglang.git).",
)
parser.add_argument(
"--oss-branch",
default=oss_repo_branch,
help="OSS repo branch to check (default: main).",
)
args = parser.parse_args()
check_dependencies()
repo_root = get_repo_root()
oss_repo_path = (
os.path.abspath(args.oss_repo_path)
if args.oss_repo_path
else get_default_oss_repo_path(repo_root)
)
repo = get_repo_from_origin(repo_root)
last_sync_hash = get_last_sync_commit(repo_root)
last_sync_block = None
if last_sync_hash:
last_sync_subject, last_sync_date = get_commit_summary(
repo_root, last_sync_hash
)
last_sync_block = (last_sync_subject, last_sync_hash, last_sync_date)
commits = get_commits_since(repo_root, last_sync_hash)
if args.limit > 0:
commits = commits[: args.limit]
relevant_commit_inputs: List[Tuple[str, List[str]]] = []
for commit_hash in commits:
changed_files = get_changed_files(repo_root, commit_hash)
if not changed_files:
continue
relevant_files = get_relevant_files(changed_files)
if relevant_files:
relevant_commit_inputs.append((commit_hash, relevant_files))
relevant_commits: List[CommitInfo] = []
if relevant_commit_inputs:
oss_repo_path = ensure_oss_repo(
oss_repo_path, args.oss_repo_url, args.oss_branch
)
oss_ref = f"origin/{args.oss_branch}"
oss_file_cache: Dict[str, Optional[Set[str]]] = {}
for commit_hash, relevant_files in relevant_commit_inputs:
subject, commit_date = get_commit_summary(repo_root, commit_hash)
added_lines_by_file = get_added_lines_by_file(
repo_root, commit_hash, relevant_files
)
synced_lines, total_added_lines = count_synced_lines(
added_lines_by_file, oss_repo_path, oss_ref, oss_file_cache
)
relevant_commits.append(
CommitInfo(
commit_hash=commit_hash,
subject=subject,
commit_date=commit_date,
relevant_files=relevant_files,
synced_lines=synced_lines,
total_added_lines=total_added_lines,
)
)
output = format_output(repo, last_sync_block, relevant_commits)
print(output)
if os.environ.get("GITHUB_STEP_SUMMARY"):
write_github_step_summary(output)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,273 @@
"""
Sync code from OSS repo to the local repo and open a PR if changes exist.
NOTE:
1. You need to execute this script in the git root folder.
2. A GH_TOKEN environment variable is required to create the pull request.
- see also https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
This script will:
1. Clone the sgl-project/sglang repository (or use a local copy).
2. Sync specified files and directories using rsync.
3. Check if the sync operation resulted in any changes.
4. If there are changes:
a. Create a new branch.
b. Commit and push the changes.
c. Open a pull request using the GitHub CLI (gh).
Usage:
# Run the full sync and PR creation process
python3 scripts/copy_from_oss.py
# Perform a dry run without making any actual changes
python3 scripts/copy_from_oss.py --dry-run
# Use a local directory as the source instead of cloning
python3 scripts/copy_from_oss.py --local-dir ~/projects/sglang
"""
import argparse
import datetime
import os
import shutil
import subprocess
import sys
import tempfile
# Allow sibling imports regardless of the working directory.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from utils import FOLDER_NAMES, write_github_step_summary # noqa: E402
# --- Configuration Begin ---
private_repo = "your-org/sglang-private-repo"
# --- Configuration End ---
def check_dependencies():
"""Check for required command-line tools."""
if not shutil.which("git"):
raise EnvironmentError("git is not installed or not in PATH.")
if not shutil.which("gh"):
raise EnvironmentError("GitHub CLI (gh) is not installed or not in PATH.")
print("✅ All dependencies (git, gh) are available.")
def checkout_main(dry_run):
"""Checkout to the main branch."""
commands = [
"git checkout main",
"git reset --hard",
]
for cmd in commands:
print(f"Run: {cmd}")
if not dry_run:
try:
subprocess.run(cmd, shell=True, check=True, capture_output=True)
except subprocess.CalledProcessError as e:
print(f"Git command failed: {e.stderr.decode()}")
raise
print("✅ Checkout the main branch.")
def get_source_folder(args):
"""
Prepare the source repository, either by cloning from GitHub or using a local directory.
Returns the path to the source repo root, a temporary directory path (if created),
and the short commit hash.
"""
temp_dir = None
if args.local_dir:
oss_root = os.path.expanduser(args.local_dir)
if not os.path.exists(oss_root):
raise FileNotFoundError(
f"Specified local directory {oss_root} does not exist."
)
print(f"Using local directory as the source: {oss_root}")
else:
temp_dir = tempfile.mkdtemp()
oss_root = temp_dir
print(f"Created temporary directory: {oss_root}")
repo_url = "https://github.com/sgl-project/sglang.git"
try:
subprocess.run(
[
"git",
"clone",
"--single-branch",
"--branch",
"main",
repo_url,
temp_dir,
],
check=True,
capture_output=True,
)
print(f"Successfully cloned repository to {temp_dir}")
except subprocess.CalledProcessError as e:
print(f"Error cloning repository: {e.stderr.decode()}")
raise
commit_hash = subprocess.run(
["git", "-C", oss_root, "rev-parse", "HEAD"],
capture_output=True,
text=True,
check=True,
).stdout.strip()[:8]
print(f"✅ Get source OSS code at commit: {commit_hash}")
return oss_root, temp_dir, commit_hash
def sync_directories(oss_root, sync_paths, dry_run):
"""Sync specified directories from oss_root to current working directory."""
rsync_commands = []
for folder_name in sync_paths:
target_name = f"{oss_root}/{folder_name}"
src_name = "./" + "/".join(folder_name.split("/")[:-1])
cmd = f"rsync -r --delete {target_name} {src_name}"
rsync_commands.append(cmd)
for cmd in rsync_commands:
try:
print(f"Run: {cmd}")
if not dry_run:
subprocess.run(cmd, shell=True, check=True)
except subprocess.CalledProcessError as e:
print(f"Error executing command '{cmd}': {e}")
raise
print(f"✅ Sync all folders.")
def check_for_changes():
"""Check if there are any uncommitted git changes."""
# This command exits with 1 if there are changes, 0 otherwise.
result = subprocess.run(["git", "diff", "--quiet"])
return result.returncode != 0
def create_and_push_branch(branch_name, commit_message, dry_run):
"""Create a new branch, commit all changes, and push to origin."""
commands = [
f"git checkout -b {branch_name}",
"git config user.name 'github-actions[bot]'",
"git config user.email 'github-actions[bot]@users.noreply.github.com'",
"git add .",
f"git commit -m '{commit_message}'",
f"git push origin {branch_name} --force",
]
print("\nCreating and pushing git branch...")
for cmd in commands:
print(f"Run: {cmd}")
if not dry_run:
try:
subprocess.run(cmd, shell=True, check=True, capture_output=True)
except subprocess.CalledProcessError as e:
print(f"Git command failed: {e.stderr.decode()}")
raise
def create_pull_request(branch_name, title, body, dry_run):
"""Create a pull request using the GitHub CLI."""
gh_token = os.getenv("GH_TOKEN")
if not gh_token:
print(
"\n⚠️ Warning: GH_TOKEN environment variable not set. Skipping PR creation."
)
if not dry_run:
return
print("\nCreating pull request...")
command = [
"gh",
"pr",
"create",
"--base",
"main",
"--head",
branch_name,
"--repo",
private_repo,
"--title",
title,
"--body",
body,
]
print(f"Run: {' '.join(command)}")
if not dry_run:
env = os.environ.copy()
env["GH_TOKEN"] = gh_token
try:
result = subprocess.run(
command, check=True, capture_output=True, text=True, env=env
)
pr_url = result.stdout.strip()
msg = f"✅ Successfully created pull request: {pr_url}"
print(msg)
write_github_step_summary(msg)
except subprocess.CalledProcessError as e:
print(f"Error creating pull request: {e.stderr}")
raise
def main():
parser = argparse.ArgumentParser(
description="Copy code from OSS and open a PR if changes are detected."
)
parser.add_argument(
"--local-dir",
type=str,
help="Path to local SGLang directory to use instead of cloning from GitHub.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Dry run the script without executing git, rsync, or gh commands.",
)
args = parser.parse_args()
check_dependencies()
checkout_main(args.dry_run)
oss_root, temp_dir, oss_commit = get_source_folder(args)
try:
# Sync directories
sync_directories(oss_root, FOLDER_NAMES, args.dry_run)
# Check for changes and create PR if necessary
if not check_for_changes():
msg = "😴 No changes detected. The code is already in sync."
print(msg)
write_github_step_summary(msg)
return
print("✅ Changes detected. Proceeding to create a PR.")
current_date = datetime.datetime.now().strftime("%Y%m%d")
branch_name = f"copy-from-oss-{oss_commit}-{current_date}"
commit_message = f"Copy OSS code from {oss_commit} on {current_date}"
pr_title = (
f"[Automated PR] Copy OSS code from commit {oss_commit} on {current_date}"
)
pr_body = (
f"Copy OSS code from https://github.com/sgl-project/sglang/commit/{oss_commit} on {current_date}."
"\n\n---\n\n"
"*This is an automated PR created by scripts/copy_from_oss.py.*"
)
create_and_push_branch(branch_name, commit_message, args.dry_run)
create_pull_request(branch_name, pr_title, pr_body, args.dry_run)
finally:
# Remove temporary directory if it was created
if temp_dir:
try:
shutil.rmtree(temp_dir)
print(f"\nRemoved temporary directory: {temp_dir}")
except OSError as e:
print(f"Error removing temporary directory {temp_dir}: {e}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,591 @@
"""
Sync a specific commit from the local private repo to the OSS upstream and open a PR.
NOTE:
1. You need to execute this script in the git root folder.
2. A GH_TOKEN environment variable is required to create the pull request.
- see also https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
This script will:
1. Take a commit hash as an argument (or use the latest commit by default).
2. Create a patch for that commit.
3. Filter the patch to only include changes in specified directories.
4. Clone the sgl-project/sglang repository.
5. Create a new branch in the OSS repo.
6. Apply the filtered patch, commit, and force push.
7. Open a pull request to the OSS repo using the GitHub CLI (gh).
Usage:
# Sync the latest commit from the current branch
python3 scripts/copy_to_oss.py
# Run the full sync and PR creation process for a given commit
python3 scripts/copy_to_oss.py --commit <commit_hash>
# Perform a dry run without making any actual changes
python3 scripts/copy_to_oss.py --commit <commit_hash> --dry-run
"""
import argparse
import datetime
import os
import re
import shutil
import subprocess
import sys
import tempfile
# Allow sibling imports regardless of the working directory.
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from utils import ( # noqa: E402
FOLDER_NAMES,
find_latest_oss_sync_commit,
write_github_step_summary,
)
def get_commit_info(commit_ref):
"""
Retrieves the hash and message of a specific commit.
Args:
commit_ref (str): The commit hash, tag, or branch to inspect (e.g., 'HEAD').
Returns:
A tuple containing the (commit_hash, commit_message),
or (None, None) if an error occurs.
"""
try:
# Use a custom format to get the hash (%H) and the full message (%B)
# separated by a null character for safe parsing.
command = ["git", "log", "-1", f"--pretty=%H%x00%B", commit_ref]
result = subprocess.run(
command, capture_output=True, text=True, check=True, encoding="utf-8"
)
# Split the output by the null character separator
commit_hash, commit_message = result.stdout.strip().split("\x00", 1)
return commit_hash, commit_message
except FileNotFoundError:
print("❌ Error: 'git' command not found. Is Git installed and in your PATH?")
except subprocess.CalledProcessError as e:
print(f"❌ Error getting commit info for '{commit_ref}': {e.stderr.strip()}")
print(
"Hint: Make sure you are running this from within a Git repository and the commit exists."
)
return None, None
def check_dependencies():
"""Check for required command-line tools."""
if not shutil.which("git"):
raise EnvironmentError("git is not installed or not in PATH.")
if not shutil.which("gh"):
raise EnvironmentError("GitHub CLI (gh) is not installed or not in PATH.")
print("✅ All dependencies (git, gh) are available.")
def create_filtered_patch(commit_hash, dry_run):
"""
Create a patch file for the given commit, containing only changes
to files and directories specified in `folder_names`.
"""
print(f"Creating a filtered patch for commit {commit_hash}")
try:
# Get the list of all files changed in the commit
changed_files_raw = subprocess.run(
["git", "diff-tree", "--no-commit-id", "--name-only", "-r", commit_hash],
capture_output=True,
text=True,
check=True,
).stdout
changed_files = changed_files_raw.strip().split("\n")
# Filter the list of files
relevant_files = [
f for f in changed_files if any(f.startswith(path) for path in FOLDER_NAMES)
]
if not relevant_files:
msg = "\n😴 No relevant file changes found in this commit. Exiting."
print(msg)
write_github_step_summary(msg)
return None, None
print("Found relevant changes in the following files:")
for f in relevant_files:
print(f" - {f}")
# Create a patch containing only the changes for the relevant files
patch_command = [
"git",
"format-patch",
"--stdout",
f"{commit_hash}^..{commit_hash}",
"--",
] + relevant_files
print(f"Run: {' '.join(patch_command)}")
patch_content = subprocess.run(
patch_command, capture_output=True, text=True, check=True
).stdout
# Save the patch to a temporary file
patch_file = tempfile.NamedTemporaryFile(
mode="w", delete=False, suffix=".patch", encoding="utf-8"
)
patch_file.write(patch_content)
patch_file.close()
print(f"✅ Filtered patch created successfully at: {patch_file.name}")
return patch_file.name, relevant_files
except subprocess.CalledProcessError as e:
print(f"Error creating patch: {e.stderr}")
raise
def get_oss_repo(dry_run):
"""
Clones the OSS repository into a temporary directory.
Returns the path to the repo root and the temp directory itself.
"""
gh_token = os.getenv("GH_TOKEN")
if not gh_token:
print(
"⚠️ Warning: GH_TOKEN environment variable not set. Skipping PR creation."
)
if not dry_run:
return
temp_dir = tempfile.mkdtemp()
oss_root = os.path.join(temp_dir, "sglang")
print(f"\nCreated temporary directory for OSS repo: {temp_dir}")
repo_url = f"https://{gh_token}@github.com/sgl-project/sglang.git"
command = ["git", "clone", repo_url, oss_root]
print(f"Run: {' '.join(command)}")
if not dry_run:
try:
subprocess.run(command, check=True, capture_output=True)
print(f"✅ Successfully cloned repository to {oss_root}")
except subprocess.CalledProcessError as e:
print(f"Error cloning repository: {e.stderr.decode()}")
shutil.rmtree(temp_dir)
raise
return oss_root, temp_dir
def _apply_patch(patch_file, dry_run):
"""
Try to apply a patch, falling back to --3way merge if a clean apply fails.
Returns True if the patch was applied cleanly.
Returns False if conflicts were encountered (changes are still staged
with conflict markers so a PR can be created for manual resolution).
"""
# --- Attempt 1: clean git apply ---
apply_cmd = ["git", "apply", patch_file]
print(f"Run: {' '.join(apply_cmd)}")
if dry_run:
return True
result = subprocess.run(apply_cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ Patch applied cleanly.")
return True
print(f"⚠️ Clean apply failed:\n{result.stderr.strip()}")
print("Falling back to git apply --3way ...\n")
# --- Attempt 2: three-way merge ---
threeway_cmd = ["git", "apply", "--3way", patch_file]
print(f"Run: {' '.join(threeway_cmd)}")
result_3way = subprocess.run(threeway_cmd, capture_output=True, text=True)
if result_3way.returncode == 0:
print("✅ Patch applied via --3way merge (no conflicts).")
return True
# --- --3way left conflict markers in the working tree ---
print(f"⚠️ --3way merge had conflicts:\n{result_3way.stderr.strip()}\n")
# Show which hunks conflict
check_cmd = ["git", "apply", "--check", "--verbose", patch_file]
print(f"Run: {' '.join(check_cmd)}")
check_result = subprocess.run(check_cmd, capture_output=True, text=True)
conflict_details = (check_result.stdout + check_result.stderr).strip()
print(
f"\n--- Conflict details ---\n{conflict_details}\n--- End conflict details ---\n"
)
# Show git diff if --3way left conflict markers
diff_result = subprocess.run(["git", "diff"], capture_output=True, text=True)
if diff_result.stdout.strip():
print(
f"\n--- git diff (conflict markers) ---\n"
f"{diff_result.stdout.strip()}\n"
f"--- End git diff ---\n"
)
# Read the patch content for the summary
with open(patch_file, "r", encoding="utf-8") as pf:
patch_content = pf.read()
# Print the patch to stdout so it's visible in the CI logs
separator = "=" * 72
print(
f"\n{separator}\n"
f"PATCH CONTENT (apply this manually):\n"
f"{separator}\n"
f"{patch_content}\n"
f"{separator}\n"
)
# Write a rich summary to the GitHub Actions step summary
summary_lines = [
"\n## ⚠️ Patch had conflicts — PR created for manual resolution\n",
"### Conflict details\n",
f"```\n{conflict_details}\n```\n",
]
if diff_result.stdout.strip():
summary_lines.append("### git diff (conflict markers)\n")
summary_lines.append(f"```diff\n{diff_result.stdout.strip()}\n```\n")
summary_lines.append("### Patch to apply manually\n")
summary_lines.append(
"<details><summary>Click to expand full patch</summary>\n\n"
f"```diff\n{patch_content}\n```\n"
"</details>\n"
)
write_github_step_summary("".join(summary_lines))
return False
def apply_patch_and_push(
oss_root, patch_file, branch_name, commit_message, base_oss_commit, dry_run
):
"""
In the OSS repo, create a branch from base_oss_commit, apply the patch,
commit, and push.
Args:
base_oss_commit: The OSS commit hash to branch from (the last sync
point). If None, the current HEAD (main) is used.
Returns True if the patch applied cleanly, False if there were conflicts
(the conflicted state is still committed and pushed so a PR can be opened).
"""
print("\nApplying patch and pushing to OSS repo...")
original_cwd = os.getcwd()
if not dry_run:
os.chdir(oss_root)
applied_cleanly = True
try:
# Check out a new branch from the base OSS commit
if base_oss_commit:
checkout_cmd = ["git", "checkout", "-b", branch_name, base_oss_commit]
else:
checkout_cmd = ["git", "checkout", "-b", branch_name]
print(f"Run: {' '.join(checkout_cmd)}")
if not dry_run:
subprocess.run(checkout_cmd, check=True, capture_output=True, text=True)
# Apply the patch (with --3way fallback and diagnostics)
applied_cleanly = _apply_patch(patch_file, dry_run)
# Configure git user and stage changes
post_apply_commands = [
["git", "config", "user.name", "github-actions[bot]"],
[
"git",
"config",
"user.email",
"github-actions[bot]@users.noreply.github.com",
],
["git", "add", "."],
]
for cmd_list in post_apply_commands:
print(f"Run: {' '.join(cmd_list)}")
if not dry_run:
subprocess.run(cmd_list, check=True, capture_output=True, text=True)
# Handle commit separately to pass multi-line message safely via stdin
commit_cmd = ["git", "commit", "-F", "-"]
print(f"Run: {' '.join(commit_cmd)}")
if not dry_run:
print(f"Commit Message:\n---\n{commit_message}\n---")
subprocess.run(
commit_cmd,
input=commit_message,
text=True,
check=True,
capture_output=True,
)
# Push the changes
push_cmd = ["git", "push", "origin", branch_name, "--force"]
print(f"Run: {' '.join(push_cmd)}")
if not dry_run:
subprocess.run(push_cmd, check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
print(f"Git command failed: {e.stderr}")
raise
finally:
if not dry_run:
os.chdir(original_cwd)
if applied_cleanly:
print("✅ Branch created, patch applied cleanly, and pushed successfully.")
else:
print(
"⚠️ Branch created and pushed with conflict markers. "
"A PR will be opened for manual resolution."
)
return applied_cleanly
def create_pull_request(oss_root, branch_name, title, body, dry_run):
"""Create a pull request in the OSS repo using the GitHub CLI."""
gh_token = os.getenv("GH_TOKEN")
if not gh_token:
print(
"⚠️ Warning: GH_TOKEN environment variable not set. Skipping PR creation."
)
if not dry_run:
return
print("\nCreating pull request...")
command = [
"gh",
"pr",
"create",
"--base",
"main",
"--head",
branch_name,
"--repo",
"sgl-project/sglang",
"--title",
title,
"--body",
body,
]
print(f"Run: {' '.join(command)}")
if not dry_run:
env = os.environ.copy()
env["GH_TOKEN"] = gh_token
try:
result = subprocess.run(
command,
check=True,
capture_output=True,
text=True,
env=env,
cwd=oss_root,
)
msg = f"✅ Successfully created pull request: {result.stdout.strip()}"
print(msg)
write_github_step_summary(msg)
except subprocess.CalledProcessError as e:
print(f"Error creating pull request: {e.stderr}")
# Check if a PR already exists
if "A pull request for" in e.stderr and "already exists" in e.stderr:
print(" A PR for this branch likely already exists.")
else:
raise
def get_commit_author(commit_hash):
"""Get the author name and email of a commit."""
try:
author_name = subprocess.run(
["git", "show", "-s", "--format=%an", commit_hash],
capture_output=True,
text=True,
check=True,
).stdout.strip()
author_email = subprocess.run(
["git", "show", "-s", "--format=%ae", commit_hash],
capture_output=True,
text=True,
check=True,
).stdout.strip()
return author_name, author_email
except subprocess.CalledProcessError as e:
print(f"Error getting commit author for {commit_hash}: {e.stderr}")
raise
def get_all_co_author_lines(commit_hash, commit_message):
"""
Build a deduplicated list of Co-authored-by lines that includes both
the primary commit author and any Co-authored-by trailers already
present in the commit message.
Returns a list of unique "Co-authored-by: Name <email>" strings.
"""
seen = set()
co_author_lines = []
def _add(name, email):
key = (name.strip(), email.strip().lower())
if key not in seen:
seen.add(key)
co_author_lines.append(f"Co-authored-by: {name.strip()} <{email.strip()}>")
# 1. Primary author of the commit
author_name, author_email = get_commit_author(commit_hash)
_add(author_name, author_email)
# 2. Existing Co-authored-by trailers in the commit message
for line in commit_message.splitlines():
m = re.match(r"^\s*Co-authored-by:\s*(.+?)\s*<([^>]+)>", line, re.IGNORECASE)
if m:
_add(m.group(1), m.group(2))
return co_author_lines
def main():
parser = argparse.ArgumentParser(
description="Copy a commit from the private repo to OSS and open a PR."
)
parser.add_argument(
"--commit",
type=str,
default="LAST",
help="The commit hash to sync. Defaults to 'LAST' to use the latest commit.",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Dry run the script without executing git, rsync, or gh commands.",
)
args = parser.parse_args()
check_dependencies()
commit_ref = "HEAD" if args.commit == "LAST" else args.commit
commit_hash, original_commit_message = get_commit_info(commit_ref)
if not commit_hash:
return # Exit if we couldn't get commit info
# Display the details of the commit being processed
if args.commit == "LAST":
summary = (
f"\n No commit specified. Using the last commit:\n"
f" - **Hash:** `{commit_hash}`\n"
f" - **Message:** {original_commit_message}\n\n"
)
else:
summary = (
f"\n Using specified commit:\n"
f" - **Hash:** `{commit_hash}`\n"
f" - **Message:** {original_commit_message}\n\n"
)
print(summary)
write_github_step_summary(summary)
short_hash = commit_hash[:8]
patch_file = None
temp_dir = None
try:
# 1. Create a filtered patch from the local repo
patch_file, relevant_files = create_filtered_patch(commit_hash, args.dry_run)
if not patch_file:
return
# 2. Get the OSS repo
oss_root, temp_dir = get_oss_repo(args.dry_run)
# 3. Find the latest OSS commit that was synced into sglang-private.
# This is the correct base for our patch, since the private repo's
# code is based on this sync point.
base_oss_commit = find_latest_oss_sync_commit()
if base_oss_commit:
print(f" Will branch from OSS commit {base_oss_commit}")
else:
print(
"⚠️ Could not determine latest OSS sync commit. "
"Falling back to OSS main HEAD."
)
# 4. Get all co-author lines (primary author + trailers from commit message)
co_author_lines = get_all_co_author_lines(commit_hash, original_commit_message)
authors_display = "\n".join(co_author_lines)
# 5. Prepare content for the commit and PR based on changed files
file_list_str = "\n".join([f"- {f}" for f in relevant_files])
filename_list_str = ", ".join([f.split("/")[-1] for f in relevant_files])
if len(filename_list_str) > 40:
filename_list_str = filename_list_str[:40] + "..."
current_date = datetime.datetime.now().strftime("%Y%m%d")
pr_title = f"[Auto Sync] Update {filename_list_str} ({current_date})"
# 6. Create branch from the last synced OSS commit, apply patch, and push
branch_name = f"sync-{short_hash}-{current_date}"
co_authors_block = "\n".join(co_author_lines)
commit_message = f"{pr_title}\n\n{co_authors_block}"
applied_cleanly = apply_patch_and_push(
oss_root,
patch_file,
branch_name,
commit_message,
base_oss_commit,
args.dry_run,
)
# 7. Adjust PR title and body when there are conflicts
if not applied_cleanly:
pr_title = (
f"[Auto Sync][⚠️ Conflicts] Update {filename_list_str} ({current_date})"
)
pr_body_parts = [
f"Sync changes from commit `{short_hash}`.\n",
f"**Files Changed:**\n{file_list_str}\n",
f"**Authors:**\n{authors_display}",
]
if not applied_cleanly:
pr_body_parts.append(
"\n\n⚠️ **This patch had merge conflicts.** "
"The branch contains conflict markers that must be resolved manually. "
"Please check the CI logs for the full patch and conflict details."
)
pr_body_parts.append(
f"\n\n---\n\n"
f"*This is an automated PR created by scripts/copy_to_oss.py.*"
)
pr_body = "\n".join(pr_body_parts)
# 8. Create Pull Request
create_pull_request(oss_root, branch_name, pr_title, pr_body, args.dry_run)
finally:
# Cleanup temporary files
if patch_file and os.path.exists(patch_file):
os.remove(patch_file)
print(f"\nRemoved temporary patch file: {patch_file}")
if temp_dir and os.path.exists(temp_dir):
shutil.rmtree(temp_dir)
print(f"Removed temporary directory: {temp_dir}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,51 @@
### Sync Code Between OSS and Private Fork
You can use the following principles and tools to sync the code between a private fork and the OSS repo [sgl-project/sglang](https://github.com/sgl-project/sglang/tree/main).
It learns from [Copybara](https://github.com/google/copybara), a tool used at Google for maintaining open-source code synchronization.
## Principals
- The core folders (e.g., `python/sglang/srt`) are 100% mirrored between the private fork and OSS repo.
- The OSS repo is the single source of truth. If one commit changes `python/sglang/srt` in the private repo, the change should be synced to the OSS repo as soon as possible with the action B below.
- The common code (e.g., base classes, well-known techniques in the industry without private secrets) goes to `python/sglang/srt`. The private-specific code (e.g., with private-specific features, confidential info) goes to `python/sglang/private` .
- Anytime you want to make private changes to a file or class under `python/sglang/srt`, duplicate the file and move it under `python/sglang/private`. You can achieve code reuse by importing and inheriting.
## How to sync the code bidirectionally
### Action A: Copy code from OSS to private
- We can run this action: [Open A PR to Copy Code From OSS](https://github.com/sgl-project/sglang/tree/main/.github/workflows/open-pr-copy-from-oss.yml)
- It opens a PR to copy all files under certain folders (e.g., `python/sglang/srt` , `test/srt` , `sgl-kernel` ) from the OSS main branch to the private fork.
- Since the OSS repo is the single source of truth, this action copies files and overwrites any changes in the private fork. To prevent the private changes from being overwritten, you need to ensure all private changes are merged into the OSS repo before running this action.
- This action will be run automatically every day and can also be triggered manually.
### Action B: Copy diff from private to OSS
- We can run this action: [Open A PR to Copy Code To OSS](https://github.com/sgl-project/sglang/tree/main/.github/workflows/open-pr-copy-to-oss.yml)
- It opens a PR to apply the diff of one specific commit of the private fork to the OSS main branch. It will only pick the changes under certain folders (e.g., `python/sglang/srt` , `test/srt` , `sgl-kernel` ) and ignore changes under private folders (e.g., `python/sglang/private` )
- For example, you can have a PR that changes both `python/sglang/srt` and `python/sglang/private/srt`. Once you merge the PR into the private repo, `python/sglang/srt` becomes desynced between the two repos. You need to run this action on your merge commit immediately to open a PR to send your diff to the OSS repo. Then, we need to merge the OSS PR as soon as possible. Once your OSS PR is merged, we can run action A again.
- Action A copies files directly, but Action B applies diff. This is because OSS is the source of truth; action A can just copy files. Action B cannot copy, so it uses diff instead.
- This action currently needs a manual trigger in order to prevent incidental code leaks. One can also consider making it automatic.
## Examples
- If you want to have some private server arguments, you can create a new file `python/sglang/private/server_args.py`. It defines a class that inherits the oss ServerArgs.
```python
from sglang.srt.server_args import ServerArgs as ServerArgsOSS
@dataclasses.dataclass
class ServerArgs(ServerArgsOSS):
private_flag: str = "foo"
@staticmethod
def add_cli_args(parser: argparse.ArgumentParser):
# Get all public args
ServerArgsOSS.add_cli_args(parser)
# Add your private flags
parser.add_argument(
"--private-flag",
type=str,
default=ServerArgs.private_flag,
)
```
- Similarly, you can inherit `Engine` and override its fields. You can override `server_args_class` to use your own ServerArgs,
override `init_tokenizer_manager_func` to use your own TokenizerManager, override `run_scheduler_process_func` to use your own scheduler.

View File

@@ -0,0 +1,18 @@
#!/bin/bash
# Check if gh is installed before attempting to install it
if ! command -v gh &> /dev/null
then
echo "GitHub CLI not found. Installing now..."
(type -p wget >/dev/null || ( apt update && apt install wget -y)) \
&& mkdir -p -m 755 /etc/apt/keyrings \
&& out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& cat $out | tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& mkdir -p -m 755 /etc/apt/sources.list.d \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& apt update \
&& apt install gh -y
else
echo "GitHub CLI is already installed. Skipping installation."
fi

View File

@@ -0,0 +1,136 @@
"""
Shared constants and helpers for code-sync scripts.
"""
import os
import re
import subprocess
from typing import Optional
# --- Configuration Begin ---
# List of folders and files to copy to / from the OSS repo.
# Changes outside these paths will be ignored.
FOLDER_NAMES = [
"3rdparty",
"assets",
"benchmark",
"docker",
"docs",
"examples",
"python/sglang/lang",
"python/sglang/jit_kernel",
"python/sglang/srt",
"python/sglang/test",
"python/sglang/utils.py",
"python/sglang/README.md",
"sgl-kernel",
"test/manual",
"test/registered",
"test/srt",
"test/README.md",
"test/run_suite.py",
"README.md",
]
SYNC_COMMIT_PREFIX = r"\[Automated PR\] Copy OSS code from commit"
# --- Configuration End ---
def write_github_step_summary(content: str) -> None:
"""Append *content* to the GitHub Actions step summary (no-op outside CI)."""
summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
if not summary_path:
return
with open(summary_path, "a") as f:
f.write(content)
def get_last_sync_commit(repo_root: Optional[str] = None) -> Optional[str]:
"""
Find the most recent sync commit that copied from OSS.
Returns the full private-repo commit hash, or None if not found.
The match is restricted to commits whose **subject** starts with the
sync prefix so that unrelated commits mentioning the phrase in their
body are ignored.
"""
subject_pattern = re.compile("^" + SYNC_COMMIT_PREFIX)
try:
cmd = [
"git",
"log",
"--all",
"--grep",
SYNC_COMMIT_PREFIX,
"--format=%H %s",
]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True,
cwd=repo_root,
).stdout.strip()
for line in result.splitlines():
# Format: "<full_hash> <subject>"
parts = line.split(" ", 1)
if len(parts) != 2:
continue
commit_hash, subject = parts
if subject_pattern.search(subject):
return commit_hash
return None
except subprocess.CalledProcessError as e:
print(f"Error finding last sync commit: {e.stderr}")
return None
def find_latest_oss_sync_commit(repo_root: Optional[str] = None) -> Optional[str]:
"""
Search the private repo history for the latest commit whose **subject**
matches "[Automated PR] Copy OSS code from commit {commit_id} on {date}"
and return the embedded **OSS** commit hash.
Returns the short OSS commit hash string, or None if not found.
"""
oss_hash_pattern = re.compile("^" + SYNC_COMMIT_PREFIX + r" ([0-9a-f]+)")
try:
# --grep filters on the full message body, so we request subject-only
# output and validate the pattern against the subject ourselves.
result = subprocess.run(
[
"git",
"log",
"--all",
"--grep",
SYNC_COMMIT_PREFIX,
"--pretty=%s",
],
capture_output=True,
text=True,
check=True,
cwd=repo_root,
)
for subject in result.stdout.strip().splitlines():
m = oss_hash_pattern.search(subject)
if m:
oss_commit = m.group(1)
print(
f"✅ Latest OSS sync commit found: {oss_commit} "
f"(from: {subject})"
)
return oss_commit
print(
"⚠️ No '[Automated PR] Copy OSS code from commit ...' " "found in history."
)
return None
except subprocess.CalledProcessError as e:
print(f"Error searching for OSS sync commits: {e.stderr.strip()}")
return None