-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
generate a multi-level PyPi index as per PEP 503 #1
base: main
Are you sure you want to change the base?
Changes from all commits
93e97ac
ed13d3e
c37af35
31acff8
b869d38
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,118 @@ | ||
from __future__ import annotations | ||
|
||
import argparse | ||
from collections import defaultdict | ||
import json | ||
import os | ||
from pathlib import Path | ||
import sys | ||
from packaging.utils import parse_wheel_filename | ||
from urllib.parse import urlparse | ||
from textwrap import dedent | ||
|
||
import github | ||
|
||
## | ||
## Output a PEP 503 compliant package repository for Pants wheels. | ||
## See https://peps.python.org/pep-0503/ | ||
## | ||
|
||
|
||
def main() -> str: | ||
gh = github.Github(auth=github.Auth.Token(os.environ["GH_TOKEN"])) | ||
def get_pants_python_packages(gh: github.Github) -> tuple[str, ...]: | ||
repo = gh.get_repo("pantsbuild/pants") | ||
releases = repo.get_releases() | ||
index = "\n".join( | ||
[ | ||
"<html>", | ||
"<body>", | ||
"<h1>Links for Pantsbuild Wheels</h1>", | ||
*( | ||
f'<a href="{asset.browser_download_url}">{asset.name}</a><br>' | ||
for release in releases | ||
if release.tag_name.startswith("release_2") | ||
for asset in release.assets | ||
if asset.name.endswith(".whl") | ||
), | ||
"</body>", | ||
"</html>", | ||
] | ||
) | ||
return index | ||
all_releases = repo.get_releases() | ||
|
||
pants_wheel_assets = [ | ||
asset | ||
for release in all_releases | ||
if release.tag_name.startswith("release_2") | ||
for asset in release.assets | ||
if asset.name.endswith(".whl") | ||
] | ||
|
||
packages = defaultdict(lambda: defaultdict(list)) | ||
|
||
for asset in pants_wheel_assets: | ||
name, version, build_tag, tags = parse_wheel_filename(asset.name) | ||
packages[name][version].append(asset) | ||
|
||
return packages | ||
|
||
|
||
def main(args): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--url-path-prefix", default="/", action="store") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My read of pep-0503 is that the URLs must always be exactly There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My read of PEP 503 is that they are the full path with trailing |
||
parser.add_argument("output_dir", action="store") | ||
opts = parser.parse_args(args) | ||
|
||
github_client = github.Github(auth=github.Auth.Token(os.environ["GH_TOKEN"])) | ||
packages = get_pants_python_packages(github_client) | ||
package_names = sorted(packages.keys()) | ||
|
||
prefix = opts.url_path_prefix | ||
if prefix and prefix.endswith("/"): | ||
prefix = prefix[0:-1] | ||
|
||
output_dir = Path(opts.output_dir) | ||
if output_dir.exists(): | ||
raise Exception(f"Output directory `{output_dir}` already exists.") | ||
output_dir.mkdir(parents=True) | ||
|
||
# http://repository.example.com/simple/ | ||
with open(output_dir / "index.html", "w") as f: | ||
f.write(dedent( | ||
"""\ | ||
<!DOCTYPE html> | ||
<html> | ||
<body> | ||
<h1>Links for Pantsbuild Wheels</h1> | ||
<ul> | ||
""" | ||
)) | ||
for package_name in package_names: | ||
f.write(f"""<li><a href="{prefix}/{package_name}/">{package_name}</a></li>\n""") | ||
f.write(dedent( | ||
"""\ | ||
</ul> | ||
</body> | ||
</html> | ||
""" | ||
)) | ||
|
||
# http://repository.example.com/simple/PACKAGE_NAME/ | ||
for package_name in package_names: | ||
package = packages[package_name] | ||
|
||
package_output_dir = output_dir / package_name | ||
package_output_dir.mkdir() | ||
|
||
package_version_keys = sorted(package.keys()) | ||
|
||
with open(package_output_dir / "index.html", "w") as f: | ||
f.write(dedent( | ||
f"""\ | ||
<!DOCTYPE html> | ||
<html> | ||
<body> | ||
<h1>Links for Pantsbuild Wheels - {package_name}</h1> | ||
<ul> | ||
""" | ||
)) | ||
|
||
for package_version_key in package_version_keys: | ||
package_version_assets = package[package_version_key] | ||
package_version_assets.sort(key=lambda x: x.name) | ||
for asset in package_version_assets: | ||
f.write(f"""<li><a href="{asset.browser_download_url}">{asset.name}</a></li>\n""") | ||
|
||
f.write(dedent( | ||
"""\ | ||
</ul> | ||
</body> | ||
</html> | ||
""" | ||
)) | ||
|
||
|
||
if __name__ == "__main__": | ||
print(main()) | ||
sys.exit(main(sys.argv[1:])) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
packaging==24.2 | ||
pygithub==2.5.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PEP 503 seems to suggest we MUST be using the normalised names in various places. Does this guarantee
name
is the normalised package name? Or maybe the wheel file names are guaranteed to be normalised?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the normalized name is returned: https://packaging.pypa.io/en/stable/utils.html#packaging.utils.parse_wheel_filename