Skip to content

Commit cff0799

Browse files
authored
Refactor CI/CD system based on latest standardization for upstreams (#135)
Specifically, it standardizes around the following pieces and is intended to be compliant with PEP 440, 517, 518, 621, and 660. Package: - Use pyproject.toml to follow the modern pep standards for python project packaging - Use setup.py to augment the packaging enabling dynamic portions - Use tox.ini to standardize everything through reproducible environments Build: - Sources the versions to update to from tags on the repo - Ensure last release version is always represented in code (setup.py) so that builds will work absent a git repo / inside of a sdist package - Utilize setuptools_git_versioning to enable dynamic versioning based on environment variables and git state, if available, while compiling to a static version for compatibility with wheel, sdist, and editable install flows - Specifically, enables build types of dev=#.#.#.dev#, nightly=#.#.#.a#, candidate=#.#.#.rc#, and release=#.#.# - Enables build iterations if a suffix is added based on an environment variable passed in, then the distance for the current commit from the last release tag, then defaults to 0 - Populates a version.txt and version.py under the root repo on build to ensure that info is properly stored and distributed CI/CD: - Enables development flows on PR creation to run standard checks for quality and tests as well as creating a dev build with the iteration set to the PR number - Enables main branch flows on commit push to run standard checks for quality and tests - Enables nightly flows on a cron job every 24hrs to run more substantial tests and push up a '.a' release if tests pass with the iteration number set to the number of commits since the last release tag - Enables release candidate flows that are triggered on creation or push to a release branch to run full test sweeps and push up a '.rc' release if tests pass with the iteration number set to the number of commits since the last release tag - Enables release flows that are triggered on the creation of a version tag that push up the release build and run a full suite of tests after to identify any potential issues post release
1 parent 1b62c45 commit cff0799

11 files changed

+205
-113
lines changed

.github/workflows/development.yml

+17-6
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,20 @@ jobs:
9191
matrix:
9292
python: ["3.9"]
9393
steps:
94-
- uses: actions/checkout@v4
94+
- name: Checkout code
95+
uses: actions/checkout@v4
96+
with:
97+
fetch-depth: 0
9598
- name: Set up Python
9699
uses: actions/setup-python@v5
97100
with:
98101
python-version: ${{ matrix.python }}
99102
- name: Install dependencies
100-
run: pip install toml loguru tox
103+
run: pip install tox
101104
- name: Build the package
102105
run: |
103106
export GUIDELLM_BUILD_TYPE=dev
104-
export GUIDELLM_BUILD_NUMBER=${{ github.event.pull_request.number }}
107+
export GUIDELLM_BUILD_ITERATION=${{ github.event.pull_request.number }}
105108
tox -e build
106109
- name: Upload build artifacts
107110
id: artifact-upload
@@ -112,15 +115,23 @@ jobs:
112115
compression-level: 6
113116
if-no-files-found: error
114117
retention-days: 30
118+
- name: Generate GitHub App token
119+
id: app-token
120+
uses: actions/create-github-app-token@v1
121+
with:
122+
app-id: ${{ secrets.GH_NM_REDHAT_AUTOMATION_APP_ID }}
123+
private-key: ${{ secrets.GH_NM_REDHAT_AUTOMATION_APP_PRIVATE_KEY }}
115124
- name: Comment Install instructions
116125
uses: actions/github-script@v7
117126
with:
118-
github-token: ${{ secrets.GITHUB_TOKEN }}
127+
github-token: ${{ steps.app-token.outputs.token }}
119128
script: |
120129
github.rest.issues.createComment({
121130
issue_number: context.issue.number,
122131
owner: context.repo.owner,
123132
repo: context.repo.repo,
124-
body: `Build artifacts (.whl and .tar.gz) are available for download for up to 30 days.
125-
They are located at ${{ steps.artifact-upload.outputs.artifact-url }}`
133+
body: `📦 **Build Artifacts Available**
134+
The build artifacts (\`.whl\` and \`.tar.gz\`) have been successfully generated and are available for download: ${{ steps.artifact-upload.outputs.artifact-url }}.
135+
They will be retained for **up to 30 days**.
136+
`
126137
})

.github/workflows/nightly.yml

+6-4
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,19 @@ jobs:
6060
matrix:
6161
python: ["3.9"]
6262
steps:
63-
- uses: actions/checkout@v4
63+
- name: Checkout code
64+
uses: actions/checkout@v4
65+
with:
66+
fetch-depth: 0
6467
- name: Set up Python
6568
uses: actions/setup-python@v5
6669
with:
6770
python-version: ${{ matrix.python }}
6871
- name: Install dependencies
69-
run: pip install toml loguru tox
72+
run: pip install tox
7073
- name: Build the package
7174
run: |
7275
export GUIDELLM_BUILD_TYPE=nightly
73-
export GUIDELLM_BUILD_NUMBER=${{ github.run_number }}
7476
tox -e build
7577
- name: Find wheel artifact
7678
id: find-asset-whl
@@ -103,4 +105,4 @@ jobs:
103105
retention-days: 30
104106
- name: Log artifact location
105107
run: |
106-
echo "Artifacts uploaded to: https://api.github.com/repos/neuralmagic/guidellm/actions/artifacts/${{ steps.artifact-upload.outputs.artifact-id }}"
108+
echo "Artifacts uploaded to: ${{ steps.artifact-upload.outputs.artifact-url }}"

.github/workflows/release-candidate.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,19 @@ jobs:
6464
matrix:
6565
python: ["3.9"]
6666
steps:
67-
- uses: actions/checkout@v4
67+
- name: Checkout code
68+
uses: actions/checkout@v4
69+
with:
70+
fetch-depth: 0
6871
- name: Set up Python
6972
uses: actions/setup-python@v5
7073
with:
7174
python-version: ${{ matrix.python }}
7275
- name: Install dependencies
73-
run: pip install toml loguru tox
76+
run: pip install tox
7477
- name: Build the package
7578
run: |
76-
export GUIDELLM_BUILD_TYPE=release_candidate
77-
export GUIDELLM_BUILD_NUMBER=${{ github.run_number }}
79+
export GUIDELLM_BUILD_TYPE=candidate
7880
tox -e build
7981
- name: Upload build artifacts
8082
id: artifact-upload
@@ -87,7 +89,7 @@ jobs:
8789
retention-days: 30
8890
- name: Log artifact location
8991
run: |
90-
echo "Artifacts uploaded to: https://api.github.com/repos/neuralmagic/guidellm/actions/artifacts/${{ steps.artifact-upload.outputs.artifact-id }}"
92+
echo "Artifacts uploaded to: ${{ steps.artifact-upload.outputs.artifact-url }}"
9193
- name: Push wheel to PyPI
9294
uses: neuralmagic/nm-actions/actions/[email protected]
9395
with:

.github/workflows/release.yml

+6-4
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@ jobs:
1212
matrix:
1313
python: ["3.9"]
1414
steps:
15-
- uses: actions/checkout@v4
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
1619
- name: Set up Python
1720
uses: actions/setup-python@v5
1821
with:
1922
python-version: ${{ matrix.python }}
2023
- name: Install dependencies
21-
run: pip install toml loguru tox
24+
run: pip install tox
2225
- name: Build the package
2326
run: |
2427
export GUIDELLM_BUILD_TYPE=release
25-
export GUIDELLM_BUILD_NUMBER=${{ github.run_number }}
2628
tox -e build
2729
- name: Upload build artifacts
2830
id: artifact-upload
@@ -35,7 +37,7 @@ jobs:
3537
retention-days: 90
3638
- name: Log artifact location
3739
run: |
38-
echo "Artifacts uploaded to: https://api.github.com/repos/neuralmagic/guidellm/actions/artifacts/${{ steps.artifact-upload.outputs.artifact-id }}"
40+
echo "Artifacts uploaded to: Artifacts uploaded to: ${{ steps.artifact-upload.outputs.artifact-url }}"
3941
- name: Push wheel to PyPI
4042
uses: neuralmagic/nm-actions/actions/[email protected]
4143
with:

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# build version files
2+
src/guidellm/version.txt
3+
src/guidellm/version.py
4+
15
# Output files
26
benchmarks.json
37
benchmarks.yaml

.pre-commit-config.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ repos:
2626
pydantic_settings,
2727
pyyaml,
2828
respx,
29-
requests,
3029
rich,
30+
setuptools,
31+
setuptools-git-versioning,
3132
transformers,
3233

3334
# dev dependencies
3435
pytest,
3536
pydantic_settings,
36-
requests-mock,
3737

3838
# types
3939
types-click,

pyproject.toml

+35-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["setuptools >= 61.0", "wheel", "build"]
2+
requires = ["setuptools >= 61.0", "setuptools-git-versioning>=2.0,<3"]
33
build-backend = "setuptools.build_meta"
44

55

@@ -16,14 +16,32 @@ include = ["*"]
1616
# ************************************************
1717

1818
[project]
19+
dynamic = ["version"]
1920
name = "guidellm"
20-
version = "0.3.0"
2121
description = "Guidance platform for deploying and managing large language models."
2222
readme = { file = "README.md", content-type = "text/markdown" }
2323
requires-python = ">=3.9.0,<4.0"
24-
license = { file = "LICENSE" }
25-
authors = [ { name = "Neuralmagic, Inc." } ]
26-
urls = { homepage = "https://github.com/neuralmagic/guidellm" }
24+
license = "Apache-2.0"
25+
license-files = ["LICENSE"]
26+
authors = [ { name = "Red Hat" } ]
27+
keywords = [
28+
"ai",
29+
"benchmarking",
30+
"deep-learning",
31+
"deployment",
32+
"evaluation",
33+
"guidance",
34+
"inference",
35+
"language-models",
36+
"large-language-model",
37+
"llm",
38+
"machine-learning",
39+
"model-benchmark",
40+
"model-evaluation",
41+
"nlp",
42+
"performance",
43+
"vllm",
44+
]
2745
dependencies = [
2846
"click",
2947
"datasets",
@@ -36,13 +54,17 @@ dependencies = [
3654
"pydantic>=2.0.0",
3755
"pydantic-settings>=2.0.0",
3856
"pyyaml>=6.0.0",
39-
"requests",
4057
"rich",
4158
"transformers",
4259
]
4360

4461
[project.optional-dependencies]
4562
dev = [
63+
# build
64+
"build>=1.0.0",
65+
"setuptools>=61.0",
66+
"setuptools-git-versioning>=2.0,<3",
67+
4668
# general and configurations
4769
"pre-commit~=3.5.0",
4870
"scipy~=1.10",
@@ -56,7 +78,6 @@ dev = [
5678
"pytest-cov~=5.0.0",
5779
"pytest-mock~=3.14.0",
5880
"pytest-rerunfailures~=14.0",
59-
"requests-mock~=1.12.1",
6081
"respx~=0.22.0",
6182

6283
# code quality
@@ -76,6 +97,12 @@ dev = [
7697
"types-toml",
7798
]
7899

100+
[project.urls]
101+
homepage = "https://github.com/neuralmagic/guidellm"
102+
source = "https://github.com/neuralmagic/guidellm"
103+
issues = "https://github.com/neuralmagic/guidellm/issues"
104+
docs = "https://github.com/neuralmagic/guidellm/tree/main/docs"
105+
79106

80107
[project.entry-points.console_scripts]
81108
guidellm = "guidellm.__main__:cli"
@@ -104,7 +131,7 @@ exclude = ["venv", ".tox"]
104131
follow_imports = 'silent'
105132

106133
[[tool.mypy.overrides]]
107-
module = ["datasets.*"]
134+
module = ["datasets.*", "transformers.*", "setuptools.*", "setuptools_git_versioning.*"]
108135
ignore_missing_imports=true
109136

110137

setup.py

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import os
2+
import re
3+
from pathlib import Path
4+
from typing import Optional, Union
5+
6+
from packaging.version import Version
7+
from setuptools import setup
8+
from setuptools_git_versioning import count_since, get_branch, get_sha, get_tags
9+
10+
LAST_RELEASE_VERSION = Version("0.0.0")
11+
TAG_VERSION_PATTERN = re.compile(r"^v(\d+\.\d+\.\d+)$")
12+
13+
14+
def get_last_version_diff() -> tuple[Version, Optional[str], Optional[int]]:
15+
"""
16+
Get the last version, last tag, and the number of commits since the last tag.
17+
If no tags are found, return the last release version and None for the tag/commits.
18+
19+
:returns: A tuple containing the last version, last tag, and number of commits since
20+
the last tag.
21+
"""
22+
tagged_versions = [
23+
(Version(match.group(1)), tag)
24+
for tag in get_tags(root=Path(__file__).parent)
25+
if (match := TAG_VERSION_PATTERN.match(tag))
26+
]
27+
tagged_versions.sort(key=lambda tv: tv[0])
28+
last_version, last_tag = (
29+
tagged_versions[-1] if tagged_versions else (LAST_RELEASE_VERSION, None)
30+
)
31+
commits_since_last = (
32+
count_since(last_tag + "^{commit}", root=Path(__file__).parent)
33+
if last_tag
34+
else None
35+
)
36+
37+
return last_version, last_tag, commits_since_last
38+
39+
40+
def get_next_version(
41+
build_type: str, build_iteration: Optional[Union[str, int]]
42+
) -> tuple[Version, Optional[str], int]:
43+
"""
44+
Get the next version based on the build type and iteration.
45+
- build_type == release: take the last version and add a post if build iteration
46+
- build_type == candidate: increment to next minor, add 'rc' with build iteration
47+
- build_type == nightly: increment to next minor, add 'a' with build iteration
48+
- build_type == alpha: increment to next minor, add 'a' with build iteration
49+
- build_type == dev: increment to next minor, add 'dev' with build iteration
50+
51+
:param build_type: The type of build (release, candidate, nightly, alpha, dev).
52+
:param build_iteration: The build iteration number. If None, defaults to the number
53+
of commits since the last tag or 0 if no commits since the last tag.
54+
:returns: A tuple containing the next version, the last tag the version is based
55+
off of (if any), and the final build iteration used.
56+
"""
57+
version, tag, commits_since_last = get_last_version_diff()
58+
59+
if not build_iteration and build_iteration != 0:
60+
build_iteration = commits_since_last or 0
61+
elif isinstance(build_iteration, str):
62+
build_iteration = int(build_iteration)
63+
64+
if build_type == "release":
65+
if commits_since_last:
66+
# add post since we have commits since last tag
67+
version = Version(f"{version.base_version}.post{build_iteration}")
68+
return version, tag, build_iteration
69+
70+
# not in release pathway, so need to increment to target next release version
71+
version = Version(f"{version.major}.{version.minor + 1}.0")
72+
73+
if build_type == "candidate":
74+
# add 'rc' since we are in candidate pathway
75+
version = Version(f"{version}.rc{build_iteration}")
76+
elif build_type in ["nightly", "alpha"]:
77+
# add 'a' since we are in nightly or alpha pathway
78+
version = Version(f"{version}.a{build_iteration}")
79+
else:
80+
# assume 'dev' if not in any of the above pathways
81+
version = Version(f"{version}.dev{build_iteration}")
82+
83+
return version, tag, build_iteration
84+
85+
86+
def write_version_files() -> tuple[Path, Path]:
87+
"""
88+
Write the version information to version.txt and version.py files.
89+
version.txt contains the version string.
90+
version.py contains the version plus additional metadata.
91+
92+
:returns: A tuple containing the paths to the version.txt and version.py files.
93+
"""
94+
build_type = os.getenv("GUIDELLM_BUILD_TYPE", "dev").lower()
95+
version, tag, build_iteration = get_next_version(
96+
build_type=build_type,
97+
build_iteration=os.getenv("GUIDELLM_BUILD_ITERATION"),
98+
)
99+
module_path = Path(__file__).parent / "src" / "guidellm"
100+
version_txt_path = module_path / "version.txt"
101+
version_py_path = module_path / "version.py"
102+
103+
with version_txt_path.open("w") as file:
104+
file.write(str(version))
105+
106+
with version_py_path.open("w") as file:
107+
file.writelines(
108+
[
109+
f'version = "{version}"\n',
110+
f'build_type = "{build_type}"\n',
111+
f'build_iteration = "{build_iteration}"\n',
112+
f'git_commit = "{get_sha()}"\n',
113+
f'git_branch = "{get_branch()}"\n',
114+
f'git_last_tag = "{tag}"\n',
115+
]
116+
)
117+
118+
return version_txt_path, version_py_path
119+
120+
121+
setup(
122+
setuptools_git_versioning={
123+
"enabled": True,
124+
"version_file": str(write_version_files()[0]),
125+
}
126+
)

0 commit comments

Comments
 (0)