mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
Merge 3b0c0d4f8b into e6b4774ac6
This commit is contained in:
commit
988754e9e1
3 changed files with 190 additions and 0 deletions
83
.github/scripts/check_release_tag_version.py
vendored
Normal file
83
.github/scripts/check_release_tag_version.py
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Fail when a release tag and the pyproject version at that tag disagree.
|
||||
|
||||
Usage
|
||||
-----
|
||||
python3 check_release_tag_version.py <tag> [path/to/pyproject.toml]
|
||||
|
||||
The tag may carry a leading `v` and a PEP 440 suffix (`rc`, `dev`, `post`, or a
|
||||
vendor suffix such as `-stable`). Only the X.Y.Z release segment is compared, so
|
||||
`v1.107.0-rc.2` matches a pyproject version of `1.107.0`.
|
||||
|
||||
Exit code 1 with a `::error::` line on stderr when the tag and the version do
|
||||
not name the same release.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
from typing import Final
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
import tomllib
|
||||
else:
|
||||
import tomli as tomllib
|
||||
|
||||
_RELEASE: Final = re.compile(r"^(?P<release>[0-9]+\.[0-9]+\.[0-9]+)(?P<suffix>.*)$")
|
||||
_ALLOWED_SUFFIX: Final = re.compile(r"^(?:$|[.\-+].*|(?:a|b|rc|dev|post)[0-9].*)$")
|
||||
|
||||
|
||||
def _release_segment(value: str, *, leading_v: bool) -> str | None:
|
||||
candidate: Final = value[1:] if leading_v and value.startswith("v") else value
|
||||
match: Final = _RELEASE.fullmatch(candidate)
|
||||
if match is None or _ALLOWED_SUFFIX.fullmatch(match.group("suffix")) is None:
|
||||
return None
|
||||
return match.group("release")
|
||||
|
||||
|
||||
def read_version(pyproject: pathlib.Path) -> str:
|
||||
with pyproject.open("rb") as f:
|
||||
return tomllib.load(f)["project"]["version"]
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
if len(argv) < 2:
|
||||
print( # noqa: T201 # the ::error:: line to stderr is the workflow's failure signal
|
||||
"::error::usage: check_release_tag_version.py <tag> [path/to/pyproject.toml]",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
tag: Final = argv[1]
|
||||
pyproject: Final = pathlib.Path(argv[2]) if len(argv) > 2 else pathlib.Path("pyproject.toml")
|
||||
tag_release: Final = _release_segment(tag, leading_v=True)
|
||||
if tag_release is None:
|
||||
print( # noqa: T201 # the ::error:: line to stderr is the workflow's failure signal
|
||||
f"::error::{tag} is not a release tag with an X.Y.Z version", file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
version: Final = read_version(pyproject)
|
||||
version_release: Final = _release_segment(version, leading_v=False)
|
||||
if version_release is None:
|
||||
print( # noqa: T201 # the ::error:: line to stderr is the workflow's failure signal
|
||||
f"::error::pyproject.toml version {version} is not an X.Y.Z version", file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
if tag_release != version_release:
|
||||
print( # noqa: T201 # the ::error:: line to stderr is the workflow's failure signal
|
||||
f"::error::{tag} points at a commit whose pyproject.toml version is {version}. "
|
||||
f"Tag the commit for {tag_release}, or correct the version before tagging.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
print(f"{tag} matches pyproject.toml version {version}") # noqa: T201 # the workflow shows this line
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
24
.github/workflows/validate-release-tag.yml
vendored
Normal file
24
.github/workflows/validate-release-tag.yml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
name: Validate Release Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Check the tag against pyproject.toml
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check the tag against pyproject.toml
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.ref_name }}
|
||||
run: python3 .github/scripts/check_release_tag_version.py "$RELEASE_TAG"
|
||||
83
tests/unit/test_check_release_tag_version.py
Normal file
83
tests/unit/test_check_release_tag_version.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
"""Tests for .github/scripts/check_release_tag_version.py."""
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
import pytest
|
||||
|
||||
_REPO_ROOT: Final = Path(__file__).resolve().parents[2]
|
||||
_MODULE_PATH: Final = _REPO_ROOT / ".github" / "scripts" / "check_release_tag_version.py"
|
||||
_spec: Final = importlib.util.spec_from_file_location("check_release_tag_version", _MODULE_PATH)
|
||||
check_release_tag_version: Final = importlib.util.module_from_spec(_spec)
|
||||
sys.modules[_spec.name] = check_release_tag_version
|
||||
_spec.loader.exec_module(check_release_tag_version)
|
||||
|
||||
|
||||
def _run(tmp_path: Path, tag: str, version: str, capsys: pytest.CaptureFixture[str]) -> tuple[int, str, str]:
|
||||
pyproject: Final = tmp_path / "pyproject.toml"
|
||||
pyproject.write_text(f'[project]\nname = "litellm"\nversion = "{version}"\n', encoding="utf-8")
|
||||
code: Final = check_release_tag_version.main(["check_release_tag_version.py", tag, str(pyproject)])
|
||||
captured: Final = capsys.readouterr()
|
||||
return code, captured.out, captured.err
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("tag", "version"),
|
||||
[
|
||||
("v1.107.0", "1.107.0"),
|
||||
("1.107.0", "1.107.0"),
|
||||
("v1.107.0-rc.2", "1.107.0"),
|
||||
("1.84.0rc1", "1.84.0"),
|
||||
("v1.84.0.dev42", "1.84.0.dev42"),
|
||||
("v1.107.0.post1", "1.107.0.post1"),
|
||||
("v1.83.10-stable", "1.83.10"),
|
||||
("v1.83.3-stable.opus-4.7", "1.83.3"),
|
||||
],
|
||||
)
|
||||
def test_a_tag_that_matches_the_pyproject_version_passes(
|
||||
tmp_path: Path, capsys: pytest.CaptureFixture[str], tag: str, version: str
|
||||
) -> None:
|
||||
code, out, err = _run(tmp_path, tag, version, capsys)
|
||||
assert (code, err) == (0, "")
|
||||
assert out == f"{tag} matches pyproject.toml version {version}\n"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("tag", "version"), [("v1.107.0", "1.108.0"), ("v1.106.2", "1.107.0")])
|
||||
def test_a_tag_that_does_not_match_the_pyproject_version_fails(
|
||||
tmp_path: Path, capsys: pytest.CaptureFixture[str], tag: str, version: str
|
||||
) -> None:
|
||||
code, out, err = _run(tmp_path, tag, version, capsys)
|
||||
assert code == 1
|
||||
assert out == ""
|
||||
assert err.startswith(f"::error::{tag} points at a commit whose pyproject.toml version is {version}.")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("tag", ["release-1.107.0", "v1.107", "v1.107.0foo"])
|
||||
def test_a_tag_that_is_not_an_x_y_z_release_fails(tmp_path: Path, capsys: pytest.CaptureFixture[str], tag: str) -> None:
|
||||
code, out, err = _run(tmp_path, tag, "1.107.0", capsys)
|
||||
assert code == 1
|
||||
assert out == ""
|
||||
assert err == f"::error::{tag} is not a release tag with an X.Y.Z version\n"
|
||||
|
||||
|
||||
def test_a_malformed_pyproject_version_fails(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
code, out, err = _run(tmp_path, "v1.107.0", "1.107", capsys)
|
||||
assert code == 1
|
||||
assert out == ""
|
||||
assert err == "::error::pyproject.toml version 1.107 is not an X.Y.Z version\n"
|
||||
|
||||
|
||||
def test_a_missing_tag_argument_fails(capsys: pytest.CaptureFixture[str]) -> None:
|
||||
assert check_release_tag_version.main(["check_release_tag_version.py"]) == 1
|
||||
assert capsys.readouterr().err == ("::error::usage: check_release_tag_version.py <tag> [path/to/pyproject.toml]\n")
|
||||
|
||||
|
||||
def test_the_repo_pyproject_is_read_when_no_path_is_given(
|
||||
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
) -> None:
|
||||
monkeypatch.chdir(_REPO_ROOT)
|
||||
version: Final = check_release_tag_version.read_version(_REPO_ROOT / "pyproject.toml")
|
||||
assert check_release_tag_version.main(["check_release_tag_version.py", f"v{version}"]) == 0
|
||||
assert capsys.readouterr().out == f"v{version} matches pyproject.toml version {version}\n"
|
||||
Loading…
Add table
Reference in a new issue