Skip to content

Commit 91511f7

Browse files
Merge branch 'main' into releases/rc-trulens-2.5.2
2 parents 524266d + 6a18e5e commit 91511f7

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,12 @@ bump-version-%: $(POETRY_DIRS)
336336
popd; \
337337
done
338338

339+
## Step: Update meta.yaml files with versions and SHA256 from PyPI
340+
# This should be run AFTER the packages have been released to PyPI
341+
update-meta-yaml:
342+
poetry run python update_meta_yaml.py
343+
344+
339345
## Step: Upload wheels to pypi
340346
# Usage: TOKEN=... make upload-trulens-instrument-langchain
341347
# In all cases, we need to clean, build, zip-wheels, then build again. The reason is because we want the final build to have the zipped wheels.

update_meta_yaml.py

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to update meta.yaml files with version and SHA256 from PyPI.
4+
5+
This script:
6+
1. Finds all meta.yaml files in the repository
7+
2. Extracts the package name from each meta.yaml
8+
3. Reads the version from the corresponding pyproject.toml
9+
4. Fetches the SHA256 hash from PyPI for that package/version
10+
5. Updates the meta.yaml with the new version and SHA256
11+
12+
Usage:
13+
python update_meta_yaml.py
14+
15+
# Or via make:
16+
make update-meta-yaml
17+
"""
18+
19+
from pathlib import Path
20+
import re
21+
import sys
22+
23+
import requests
24+
25+
# Repository root (same directory as this script)
26+
REPO_ROOT = Path(__file__).parent
27+
28+
29+
def find_meta_yaml_files() -> list[Path]:
30+
"""Find all meta.yaml files in the repository."""
31+
return list(REPO_ROOT.glob("**/meta.yaml"))
32+
33+
34+
def extract_package_name(meta_yaml_path: Path) -> str | None:
35+
"""Extract package name from meta.yaml file.
36+
37+
Looks for: {% set name = "package-name" %}
38+
"""
39+
content = meta_yaml_path.read_text()
40+
match = re.search(r'\{%\s*set\s+name\s*=\s*"([^"]+)"\s*%\}', content)
41+
if match:
42+
return match.group(1)
43+
return None
44+
45+
46+
def extract_version_from_pyproject(pyproject_path: Path) -> str | None:
47+
"""Extract version from pyproject.toml file.
48+
49+
Looks for: version = "X.Y.Z"
50+
"""
51+
if not pyproject_path.exists():
52+
return None
53+
content = pyproject_path.read_text()
54+
match = re.search(r'^version\s*=\s*"([^"]+)"', content, re.MULTILINE)
55+
if match:
56+
return match.group(1)
57+
return None
58+
59+
60+
def fetch_sha256_from_pypi(package_name: str, version: str) -> str | None:
61+
"""Fetch SHA256 hash from PyPI for a specific package version.
62+
63+
Uses the PyPI JSON API to get the sdist (tar.gz) SHA256.
64+
"""
65+
url = f"https://pypi.org/pypi/{package_name}/{version}/json"
66+
try:
67+
response = requests.get(url, timeout=30)
68+
response.raise_for_status()
69+
data = response.json()
70+
71+
# Find the sdist (source distribution) URL entry
72+
for url_info in data.get("urls", []):
73+
if url_info.get("packagetype") == "sdist":
74+
digests = url_info.get("digests", {})
75+
return digests.get("sha256")
76+
77+
print(f" Warning: No sdist found for {package_name}=={version}")
78+
return None
79+
80+
except requests.exceptions.HTTPError as e:
81+
if e.response.status_code == 404:
82+
print(
83+
f" Error: Package {package_name}=={version} not found on PyPI"
84+
)
85+
print(
86+
" Make sure the version has been released to PyPI first."
87+
)
88+
else:
89+
print(f" Error fetching from PyPI: {e}")
90+
return None
91+
except requests.exceptions.RequestException as e:
92+
print(f" Error fetching from PyPI: {e}")
93+
return None
94+
95+
96+
def update_meta_yaml(meta_yaml_path: Path, version: str, sha256: str) -> bool:
97+
"""Update the meta.yaml file with new version and SHA256.
98+
99+
Updates:
100+
- {% set version = "X.Y.Z" %}
101+
- sha256: <hash>
102+
"""
103+
content = meta_yaml_path.read_text()
104+
original_content = content
105+
106+
# Update version
107+
content = re.sub(
108+
r'(\{%\s*set\s+version\s*=\s*")[^"]+("\s*%\})',
109+
rf"\g<1>{version}\g<2>",
110+
content,
111+
)
112+
113+
# Update sha256
114+
content = re.sub(r"(sha256:\s*)[a-fA-F0-9]+", rf"\g<1>{sha256}", content)
115+
116+
if content != original_content:
117+
meta_yaml_path.write_text(content)
118+
return True
119+
return False
120+
121+
122+
def main():
123+
"""Main function to update all meta.yaml files."""
124+
print(
125+
"Updating meta.yaml files with versions and SHA256 hashes from PyPI...\n"
126+
)
127+
128+
meta_yaml_files = find_meta_yaml_files()
129+
130+
if not meta_yaml_files:
131+
print("No meta.yaml files found!")
132+
sys.exit(1)
133+
134+
print(f"Found {len(meta_yaml_files)} meta.yaml files\n")
135+
136+
success_count = 0
137+
error_count = 0
138+
skip_count = 0
139+
140+
for meta_yaml_path in sorted(meta_yaml_files):
141+
relative_path = meta_yaml_path.relative_to(REPO_ROOT)
142+
print(f"Processing: {relative_path}")
143+
144+
# Extract package name
145+
package_name = extract_package_name(meta_yaml_path)
146+
if not package_name:
147+
print(
148+
f" Error: Could not extract package name from {relative_path}"
149+
)
150+
error_count += 1
151+
continue
152+
print(f" Package: {package_name}")
153+
154+
# Find corresponding pyproject.toml
155+
pyproject_path = meta_yaml_path.parent / "pyproject.toml"
156+
version = extract_version_from_pyproject(pyproject_path)
157+
if not version:
158+
print(
159+
f" Error: Could not find version in {pyproject_path.relative_to(REPO_ROOT)}"
160+
)
161+
error_count += 1
162+
continue
163+
print(f" Version: {version}")
164+
165+
# Fetch SHA256 from PyPI
166+
sha256 = fetch_sha256_from_pypi(package_name, version)
167+
if not sha256:
168+
error_count += 1
169+
continue
170+
print(f" SHA256: {sha256[:16]}...")
171+
172+
# Update meta.yaml
173+
if update_meta_yaml(meta_yaml_path, version, sha256):
174+
print(f" ✓ Updated {relative_path}")
175+
success_count += 1
176+
else:
177+
print(" - No changes needed (already up to date)")
178+
skip_count += 1
179+
180+
print()
181+
182+
# Summary
183+
print("-" * 50)
184+
print(
185+
f"Summary: {success_count} updated, {skip_count} unchanged, {error_count} errors"
186+
)
187+
188+
if error_count > 0:
189+
print(
190+
"\nNote: Errors may occur if the version hasn't been released to PyPI yet."
191+
)
192+
sys.exit(1)
193+
194+
sys.exit(0)
195+
196+
197+
if __name__ == "__main__":
198+
main()

0 commit comments

Comments
 (0)