File size: 5,916 Bytes
61ba51e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env python3
"""
Update the wheel index for PR SGLang releases.

This script generates a single PyPI-compatible index.html file at pr/index.html
containing all PR builds, ordered by PR number and commit count (newest first).

Similar to update_nightly_whl_index.py but for PR builds.
"""

import argparse
import hashlib
import pathlib
import re


def compute_sha256(file_path: pathlib.Path) -> str:
    """Compute SHA256 hash of a file."""
    sha256_hash = hashlib.sha256()
    with open(file_path, "rb") as f:
        for byte_block in iter(lambda: f.read(4096), b""):
            sha256_hash.update(byte_block)
    return sha256_hash.hexdigest()


def update_wheel_index(
    pr_number: str, commit_hash: str, wheel_version: str, build_date: str
):
    """Update the wheel index for PR releases.

    Creates a single index at pr/index.html containing all PR builds.

    Args:
        pr_number: PR number (e.g., '123')
        commit_hash: Short git commit hash (e.g., 'c5f1e86')
        wheel_version: Full wheel version string (e.g., '0.5.6.dev7716+pr-123.gc5f1e86')
        build_date: Build date in YYYY-MM-DD format (e.g., '2025-12-13')
    """
    dist_dir = pathlib.Path("dist")
    whl_repo_dir = pathlib.Path("sgl-whl")

    if not dist_dir.exists():
        print(f"Warning: {dist_dir} does not exist, skipping index update")
        return

    # Base URL for wheels stored in GitHub Releases
    base_url = "https://github.com/sgl-project/whl/releases/download"
    release_tag = f"pr-{pr_number}-{build_date}-{commit_hash}"

    # Create pr directory structure following PEP 503
    # /pr/index.html -> links to sglang/
    # /pr/sglang/index.html -> contains wheel links
    pr_dir = whl_repo_dir / "pr"
    pr_dir.mkdir(parents=True, exist_ok=True)

    sglang_dir = pr_dir / "sglang"
    sglang_dir.mkdir(parents=True, exist_ok=True)

    root_index = pr_dir / "index.html"
    package_index = sglang_dir / "index.html"

    print(f"\nUpdating PR wheel index")
    print(f"  Root index: {root_index}")
    print(f"  Package index: {package_index}")

    # Read existing package index if it exists
    existing_links = []
    if package_index.exists():
        with open(package_index, "r") as f:
            content = f.read()
            # Extract existing links (skip header and HTML boilerplate)
            existing_links = [
                line for line in content.split("\n") if line.startswith("<a href=")
            ]

    # Generate new links for current wheels
    new_links = []
    for wheel_path in sorted(dist_dir.glob("*.whl")):
        try:
            filename = wheel_path.name
            sha256 = compute_sha256(wheel_path)

            # URL format: {base_url}/{release_tag}/{filename}#sha256={hash}
            wheel_url = f"{base_url}/{release_tag}/{filename}#sha256={sha256}"
            link = f'<a href="{wheel_url}">{filename}</a><br>'

            new_links.append(link)
            print(f"  Added: {filename}")
        except Exception as e:
            print(f"  Error processing {wheel_path.name}: {e}")
            continue

    if not new_links:
        print("  No new wheels to add")
        return

    # Combine existing and new links (new links first for latest)
    all_links = new_links + existing_links

    # Remove duplicates while preserving order (newer first)
    seen = set()
    unique_links = []
    for link in all_links:
        # Extract filename from link to check for duplicates
        filename_match = re.search(r">([^<]+\.whl)</a>", link)
        if filename_match:
            filename = filename_match.group(1)
            if filename not in seen:
                seen.add(filename)
                unique_links.append(link)

    # Write root index (links to sglang package directory)
    with open(root_index, "w") as f:
        f.write("<!DOCTYPE html>\n")
        f.write('<a href="sglang/">sglang</a>\n')

    print(f"  Written root index: {root_index}")

    # Write package index in minimal format
    with open(package_index, "w") as f:
        f.write("<!DOCTYPE html>\n")
        f.write("<h1>SGLang PR Wheels</h1>\n")
        # Write links only
        f.write("\n".join(unique_links))
        f.write("\n")

    print(f"  Written {len(unique_links)} total wheels to {package_index}")
    print(f"\nDone! Users can install with:")
    print(
        f"  pip install sglang --pre --extra-index-url https://sgl-project.github.io/whl/pr/"
    )
    print(f"\nOr install specific PR #{pr_number} wheel directly:")
    if new_links:
        first_wheel_match = re.search(r'href="([^"]+)"', new_links[0])
        if first_wheel_match:
            wheel_url = first_wheel_match.group(1).split("#")[0]  # Remove sha256 hash
            print(f"  pip install {wheel_url}")


def main():
    parser = argparse.ArgumentParser(
        description="Update wheel index for PR SGLang releases"
    )
    parser.add_argument(
        "--pr-number",
        type=str,
        required=True,
        help="PR number (e.g., '123')",
    )
    parser.add_argument(
        "--commit-hash",
        type=str,
        required=True,
        help="Short git commit hash (e.g., 'c5f1e86')",
    )
    parser.add_argument(
        "--wheel-version",
        type=str,
        required=True,
        help="Full wheel version string (e.g., '0.5.6.dev7716+pr-123.gc5f1e86')",
    )
    parser.add_argument(
        "--build-date",
        type=str,
        required=True,
        help="Build date in YYYY-MM-DD format (e.g., '2025-12-13')",
    )

    args = parser.parse_args()

    print(f"Updating PR wheel index")
    print(f"  PR: #{args.pr_number}")
    print(f"  Commit: {args.commit_hash}")
    print(f"  Version: {args.wheel_version}")
    print(f"  Build date: {args.build_date}")

    update_wheel_index(
        args.pr_number, args.commit_hash, args.wheel_version, args.build_date
    )


if __name__ == "__main__":
    main()