mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-26 01:12:21 +00:00
ci: cut rc/<X.Y.0> off main every Friday at 3am Pacific (#43121)
* ci: cut rc/<X.Y.0> off main every Friday at 3am Pacific Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * ci: refuse to cut rc branch from a ref other than main Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * ci: move rc version check into .github/scripts/read_rc_version.py with tests Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * ci: fall back to tomli for the rc version script on Python 3.10 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test: type the read_rc_version test helper Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: yuneng <yuneng@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
c976c16a82
commit
c601dfc134
3 changed files with 159 additions and 0 deletions
44
.github/scripts/read_rc_version.py
vendored
Normal file
44
.github/scripts/read_rc_version.py
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Print `version=X.Y.0` from [project].version in pyproject.toml for $GITHUB_OUTPUT.
|
||||
|
||||
Usage
|
||||
-----
|
||||
python3 read_rc_version.py [path/to/pyproject.toml] >> "$GITHUB_OUTPUT"
|
||||
|
||||
Exit code 1 with a `::error::` line on stderr when the version is not an X.Y.0 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_VERSION: Final = re.compile(r"[0-9]+\.[0-9]+\.0")
|
||||
|
||||
|
||||
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:
|
||||
pyproject: Final = pathlib.Path(argv[1]) if len(argv) > 1 else pathlib.Path("pyproject.toml")
|
||||
version: Final = read_version(pyproject)
|
||||
if RELEASE_VERSION.fullmatch(version) 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.0 release version", file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
print(f"version={version}") # noqa: T201 # stdout line is appended to $GITHUB_OUTPUT
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
66
.github/workflows/create-rc-branch.yml
vendored
Normal file
66
.github/workflows/create-rc-branch.yml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
name: Create RC Branch
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 3 * * 5"
|
||||
timezone: "America/Los_Angeles"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
create-rc-branch:
|
||||
name: Create RC Branch
|
||||
if: github.event_name != 'schedule' || github.repository == 'BerriAI/litellm'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Require main
|
||||
env:
|
||||
REF: ${{ github.ref }}
|
||||
run: |
|
||||
if [ "$REF" != "refs/heads/main" ]; then
|
||||
echo "::error::rc branches are cut from refs/heads/main only, got $REF"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Read release version
|
||||
id: version
|
||||
run: python3 .github/scripts/read_rc_version.py >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Create rc branch
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
script: |
|
||||
const branchName = `rc/${process.env.VERSION}`;
|
||||
const ref = `heads/${branchName}`;
|
||||
|
||||
const existing = await github.rest.git.getRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref,
|
||||
}).catch((error) => {
|
||||
if (error.status === 404) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
if (existing !== null) {
|
||||
core.setFailed(`Branch ${branchName} already exists at ${existing.data.object.sha}; leaving it untouched`);
|
||||
return;
|
||||
}
|
||||
|
||||
await github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `refs/${ref}`,
|
||||
sha: context.sha,
|
||||
});
|
||||
core.info(`Created branch ${branchName} at ${context.sha}`);
|
||||
49
tests/test_litellm/test_read_rc_version.py
Normal file
49
tests/test_litellm/test_read_rc_version.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
"""Tests for .github/scripts/read_rc_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" / "read_rc_version.py"
|
||||
_spec: Final = importlib.util.spec_from_file_location("read_rc_version", _MODULE_PATH)
|
||||
read_rc_version: Final = importlib.util.module_from_spec(_spec)
|
||||
sys.modules[_spec.name] = read_rc_version
|
||||
_spec.loader.exec_module(read_rc_version)
|
||||
|
||||
|
||||
def _run(tmp_path: Path, 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 = read_rc_version.main(["read_rc_version.py", str(pyproject)])
|
||||
captured: Final = capsys.readouterr()
|
||||
return code, captured.out, captured.err
|
||||
|
||||
|
||||
@pytest.mark.parametrize("version", ["1.104.0", "2.0.0", "10.250.0"])
|
||||
def test_an_x_y_0_version_is_printed_as_a_github_output_line(
|
||||
tmp_path: Path, capsys: pytest.CaptureFixture[str], version: str
|
||||
) -> None:
|
||||
code, out, err = _run(tmp_path, version, capsys)
|
||||
assert (code, out, err) == (0, f"version={version}\n", "")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("version", ["1.104.1", "1.104.0rc1", "1.104", "v1.104.0", "1.104.0.dev1"])
|
||||
def test_a_non_release_version_exits_1_without_printing_a_version(
|
||||
tmp_path: Path, capsys: pytest.CaptureFixture[str], version: str
|
||||
) -> None:
|
||||
code, out, err = _run(tmp_path, version, capsys)
|
||||
assert code == 1
|
||||
assert out == ""
|
||||
assert err == f"::error::pyproject.toml version {version} is not an X.Y.0 release version\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)
|
||||
assert read_rc_version.main(["read_rc_version.py"]) == 0
|
||||
assert capsys.readouterr().out == f"version={read_rc_version.read_version(_REPO_ROOT / 'pyproject.toml')}\n"
|
||||
Loading…
Add table
Reference in a new issue