Skip to content

Commit ea9086b

Browse files
committed
fix sglang al2023 dlc version
Signed-off-by: sirutBuasai <sirutbuasai27@outlook.com>
1 parent f98b17f commit ea9086b

6 files changed

Lines changed: 215 additions & 4 deletions

File tree

.github/config/image/sglang/ec2-amzn2023.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ image:
44

55
metadata:
66
framework: "sglang_server"
7-
framework_version: "0.5.13+dlc1"
7+
framework_version: "0.5.14+dlc1"
88
os_version: "amzn2023"
99
customer_type: "ec2"
1010
arch_type: "x86"

.github/config/image/sglang/sagemaker-amzn2023.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ image:
44

55
metadata:
66
framework: "sglang_server"
7-
framework_version: "0.5.13+dlc1"
7+
framework_version: "0.5.14+dlc1"
88
os_version: "amzn2023"
99
customer_type: "sagemaker"
1010
platform: "sagemaker"

.github/workflows/_reusable.sanity-tests.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,17 @@ jobs:
145145
-e EXPECTED_CUDA_VERSION=${{ needs.preflight.outputs.cuda-version }} \
146146
${CONTAINER_ID} python3 /workdir/test/sanity/scripts/test_sanity_vllm_sglang.py
147147
148+
- name: Check model containers framework version currency
149+
if: |
150+
needs.preflight.outputs.framework == 'vllm_server' ||
151+
needs.preflight.outputs.framework == 'sglang_server'
152+
env:
153+
GITHUB_TOKEN: ${{ github.token }}
154+
run: |
155+
source .venv/bin/activate
156+
python3 test/sanity/scripts/check_framework_version_currency.py \
157+
--config-file "${{ inputs.config-file }}"
158+
148159
- name: Run OSS compliance check
149160
run: |
150161
docker exec ${CONTAINER_ID} /usr/local/bin/testOSSCompliance /root

docker/sglang/Dockerfile.amzn2023

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,6 @@ RUN git clone --branch ${SGLANG_REF} --depth 1 https://github.com/sgl-project/sg
8686
&& cd /sgl-workspace/sglang && git checkout ${SGLANG_REF})
8787

8888
# Stamp the sglang package version for CVE scanner compatibility.
89-
# PEP 440 local version: 0.5.13+dlc1.amzn2023.66ab5c9c
90-
# This ensures: version >= 0.5.13 (not flagged) and < 0.5.14 (correct ordering).
9189
ARG FRAMEWORK_VERSION
9290
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${FRAMEWORK_VERSION}.amzn2023.${SGLANG_REF}
9391

test/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
boto3
22
botocore
33
fabric
4+
packaging
45
pytest
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
#!/usr/bin/env python3
2+
"""Sanity check: an AL2023 vLLM/SGLang *server* config must not declare a
3+
framework version that is *behind* the upstream commit it pins.
4+
5+
Version contract for these images:
6+
7+
framework_version == "<upstream_release>[+dlc<n>]"
8+
9+
and the pinned source ref (``sglang_ref`` / ``vllm_ref``) must resolve to an
10+
upstream state that is *at least* ``<upstream_release>``. When the ref is moved
11+
ahead of the latest upstream release tag (e.g. onto ``main`` past ``v0.5.14``),
12+
``framework_version`` must be bumped so its ``<upstream_release>`` equals that
13+
latest tag.
14+
15+
This guards against the failure mode where the source ref is bumped but
16+
``framework_version`` is left stale (e.g. ref advanced past ``v0.5.14`` while
17+
``framework_version`` stayed ``0.5.13+dlc1``). That stale value gets baked into
18+
the container as ``SETUPTOOLS_SCM_PRETEND_VERSION`` and into the CVE-scanner
19+
metadata, mislabelling the shipped framework version.
20+
21+
Runs on the CI host (needs network + the GitHub API), one config at a time:
22+
23+
python3 test/sanity/scripts/check_framework_version_currency.py \\
24+
--config-file .github/config/image/sglang/ec2-amzn2023.yml
25+
26+
Set ``GITHUB_TOKEN`` (or ``GH_TOKEN``) to raise the API rate limit.
27+
"""
28+
29+
import argparse
30+
import json
31+
import os
32+
import re
33+
import subprocess
34+
import sys
35+
import urllib.error
36+
import urllib.request
37+
38+
from packaging.version import InvalidVersion, Version
39+
40+
# metadata.framework -> (upstream repo, config field holding the source ref).
41+
# Only the from-source *server* images follow the "<release>+dlc<n>" contract.
42+
FRAMEWORK_SOURCES = {
43+
"sglang_server": ("sgl-project/sglang", "sglang_ref"),
44+
"vllm_server": ("vllm-project/vllm", "vllm_ref"),
45+
}
46+
47+
# Strict release tags only (no rc/dev/post/a/b): v1.2.3 or 1.2.3
48+
_RELEASE_TAG_RE = re.compile(r"^v?\d+(?:\.\d+)*$")
49+
50+
51+
def load_config(path):
52+
"""Return the parsed config dict. Uses PyYAML if available, else yq."""
53+
try:
54+
import yaml
55+
56+
with open(path) as f:
57+
return yaml.safe_load(f)
58+
except ImportError:
59+
out = subprocess.check_output(["yq", "-o=json", ".", path], text=True)
60+
return json.loads(out)
61+
62+
63+
def to_version(tag_or_version):
64+
"""Parse a tag name or version string into a ``Version`` (drops a ``v``
65+
prefix, e.g. ``v0.5.14`` -> ``Version('0.5.14')``)."""
66+
return Version(str(tag_or_version).lstrip("v"))
67+
68+
69+
def github_get(url):
70+
"""GET a GitHub API URL, returning parsed JSON. Uses a token if present."""
71+
headers = {
72+
"Accept": "application/vnd.github+json",
73+
"User-Agent": "dlc-framework-version-currency-check",
74+
}
75+
token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
76+
if token:
77+
headers["Authorization"] = f"Bearer {token}"
78+
req = urllib.request.Request(url, headers=headers)
79+
with urllib.request.urlopen(req, timeout=30) as resp:
80+
return json.loads(resp.read().decode())
81+
82+
83+
def release_tags(repo):
84+
"""Return upstream strict-release tags as ``(Version, tag_name)`` pairs,
85+
sorted newest-first."""
86+
tags = []
87+
for page in range(1, 6): # up to 500 tags; newest first
88+
batch = github_get(f"https://api.github.com/repos/{repo}/tags?per_page=100&page={page}")
89+
if not batch:
90+
break
91+
for entry in batch:
92+
name = entry["name"]
93+
if _RELEASE_TAG_RE.match(name):
94+
try:
95+
tags.append((to_version(name), name))
96+
except InvalidVersion:
97+
continue
98+
if len(batch) < 100:
99+
break
100+
return sorted(set(tags), key=lambda t: t[0], reverse=True)
101+
102+
103+
def ref_at_or_ahead_of(repo, tag, ref):
104+
"""True if ``ref`` is at or ahead of ``tag`` in ``repo`` history."""
105+
data = github_get(f"https://api.github.com/repos/{repo}/compare/{tag}...{ref}")
106+
status = data.get("status")
107+
if status in ("identical", "ahead"):
108+
return True
109+
if status == "behind":
110+
return False
111+
if status == "diverged":
112+
# ref is on a newer/parallel line (e.g. main past a release-branch tag).
113+
# Treat it as "at least" the tag when it carries at least as much unique
114+
# history as the tag does.
115+
return data.get("ahead_by", 0) >= data.get("behind_by", 0)
116+
return False
117+
118+
119+
def resolve_ref_release(repo, ref):
120+
"""The highest strict-release tag that ``ref`` is at or ahead of, as a
121+
``(Version, tag_name)`` pair, or ``None`` if it precedes all tags."""
122+
for version, name in release_tags(repo):
123+
if ref_at_or_ahead_of(repo, name, ref):
124+
return version, name
125+
return None
126+
127+
128+
def check_config(config_path):
129+
"""Check one config file. Returns (ok: bool, message: str)."""
130+
config = load_config(config_path)
131+
metadata = config.get("metadata", {})
132+
build = config.get("build", {})
133+
134+
framework = metadata.get("framework", "")
135+
if framework not in FRAMEWORK_SOURCES:
136+
return True, f"SKIP: framework {framework!r} is not a vLLM/SGLang server image"
137+
138+
repo, ref_field = FRAMEWORK_SOURCES[framework]
139+
ref = build.get(ref_field)
140+
if not ref:
141+
return True, f"SKIP: no {ref_field} in {config_path} (not a from-source build)"
142+
143+
declared = to_version(metadata.get("framework_version"))
144+
if declared.is_devrelease:
145+
# A ".devN" version is a legacy setuptools_scm snapshot of an unreleased
146+
# upstream commit (e.g. the out-of-support hyperpod image) and does not
147+
# use the "<release>+dlc<n>" contract, so the check does not apply. Such
148+
# an image would be checked again once it adopts the +dlc convention.
149+
return True, f"SKIP: framework_version {declared} is a legacy dev snapshot"
150+
151+
resolved = resolve_ref_release(repo, ref)
152+
if resolved is None:
153+
return True, f"SKIP: {ref[:10]} precedes all release tags of {repo}"
154+
resolved_version, resolved_tag = resolved
155+
156+
# Compare on the release portion only, so the "+dlc<n>" local segment does
157+
# not read as newer than the bare upstream tag.
158+
declared_release = Version(declared.base_version)
159+
resolved_release = Version(resolved_version.base_version)
160+
161+
detail = (
162+
f"framework_version={metadata.get('framework_version')!r} vs "
163+
f"{ref_field}={ref[:10]} which is at upstream {resolved_tag} in {repo}"
164+
)
165+
if declared_release < resolved_release:
166+
return False, (
167+
f"framework_version is BEHIND the pinned ref: {detail}. "
168+
f"Bump framework_version's upstream to {resolved_release} "
169+
f"(keeping the +dlc<n> suffix)."
170+
)
171+
if declared_release > resolved_release:
172+
# The ref has not (fully) reached the declared release. Common for a ref
173+
# pinned just before a release tag; warn but do not fail.
174+
return True, (
175+
f"WARNING: framework_version is ahead of the pinned ref's latest "
176+
f"reached release: {detail}."
177+
)
178+
return True, f"OK: {detail}"
179+
180+
181+
def main():
182+
parser = argparse.ArgumentParser(description=__doc__)
183+
parser.add_argument(
184+
"--config-file",
185+
required=True,
186+
help="Path to the image config YAML to check.",
187+
)
188+
args = parser.parse_args()
189+
190+
try:
191+
ok, message = check_config(args.config_file)
192+
except (urllib.error.URLError, urllib.error.HTTPError) as exc:
193+
print(f"ERROR: GitHub API request failed: {exc}", file=sys.stderr)
194+
return 2
195+
196+
print(message)
197+
return 0 if ok else 1
198+
199+
200+
if __name__ == "__main__":
201+
sys.exit(main())

0 commit comments

Comments
 (0)