Skip to content

Commit da41574

Browse files
authored
Hotfix Release: v2.12.0.1rc1+gr - collective changes (#14)
- CI/CD script
1 parent 3a25bfa commit da41574

File tree

4 files changed

+313
-8
lines changed

4 files changed

+313
-8
lines changed

.github/scripts/generate_index.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import os
2+
import sys
3+
import re
4+
import itertools
5+
import requests
6+
import hashlib
7+
8+
from urllib.parse import quote
9+
from pathlib import Path
10+
from github import Github
11+
from typing import List, Dict, Set
12+
13+
# Define yanked versions - modify this dictionary as needed
14+
yanked_versions = {
15+
"confluent-kafka": {
16+
"2.11.0+gr",
17+
"2.11.0+gr.1",
18+
},
19+
}
20+
21+
HTML_TEMPLATE = """<!DOCTYPE html>
22+
<html>
23+
<head>
24+
<title>{package_name}</title>
25+
</head>
26+
<body>
27+
<h1>{package_name}</h1>
28+
{package_links}
29+
</body>
30+
</html>
31+
"""
32+
33+
def normalize(name):
34+
"""Normalize package name according to PEP 503."""
35+
return re.sub(r"[-_.]+", "-", name).lower()
36+
37+
def calculate_sha256(file_path):
38+
with open(file_path, "rb") as f:
39+
digest = hashlib.file_digest(f, "sha256")
40+
41+
return digest.hexdigest()
42+
43+
def extract_version_from_filename(filename: str) -> str:
44+
"""Extract version from wheel or sdist filename."""
45+
# Remove extension
46+
name = filename.replace('.tar.gz', '').replace('.whl', '')
47+
48+
# For wheels: package-version-python-abi-platform
49+
# For sdist: package-version
50+
parts = name.split('-')
51+
if len(parts) >= 2:
52+
return parts[1]
53+
return ""
54+
55+
class PackageIndexBuilder:
56+
def __init__(self, token: str, repo_name: str, output_dir: str, yanked_versions: Dict[str, Set[str]] = None):
57+
self.github = Github(token)
58+
self.repo = self.github.get_repo(repo_name)
59+
self.output_dir = Path(output_dir)
60+
self.packages: Dict[str, List[Dict]] = {}
61+
self.yanked_versions = yanked_versions or {}
62+
63+
# Set up authenticated session
64+
self.session = requests.Session()
65+
self.session.headers.update({
66+
"Authorization": f"token {token}",
67+
"Accept": "application/octet-stream",
68+
})
69+
70+
def is_version_yanked(self, package_name: str, version: str) -> bool:
71+
"""Check if a specific version of a package is yanked."""
72+
normalized_package = normalize(package_name)
73+
return normalized_package in self.yanked_versions and version in self.yanked_versions[normalized_package]
74+
75+
def collect_packages(self):
76+
print("Query release assets")
77+
78+
for release in self.repo.get_releases():
79+
for asset in release.get_assets():
80+
if asset.name.endswith(('.whl', '.tar.gz')):
81+
package_name = normalize(asset.name.split('-')[0])
82+
if package_name not in self.packages:
83+
self.packages[package_name] = []
84+
85+
version = extract_version_from_filename(asset.name)
86+
self.packages[package_name].append({
87+
'filename': asset.name,
88+
'url': asset.url,
89+
'size': asset.size,
90+
'upload_time': asset.created_at.strftime('%Y-%m-%d %H:%M:%S'),
91+
'version': version,
92+
})
93+
94+
def generate_index_html(self):
95+
# Generate main index
96+
package_list = self.packages.keys()
97+
main_index = HTML_TEMPLATE.format(
98+
package_name="Simple Package Index",
99+
package_links="\n".join([f'<a href="{x}/">{x}</a><br/>' for x in package_list])
100+
)
101+
102+
with open(self.output_dir / "index.html", "w") as f:
103+
f.write(main_index)
104+
105+
for package, assets in self.packages.items():
106+
107+
package_dir = self.output_dir / package
108+
package_dir.mkdir(exist_ok=True)
109+
110+
# Generate package-specific index.html
111+
file_links = []
112+
assets = sorted(assets, key=lambda x: x["filename"])
113+
for filename, items in itertools.groupby(assets, key=lambda x: x["filename"]):
114+
asset_info = next(items)
115+
url = asset_info['url']
116+
version = asset_info['version']
117+
118+
# Download the file
119+
with open(package_dir / filename, 'wb') as f:
120+
print (f"Downloading '{filename}' from '{url}'")
121+
response = self.session.get(url, stream=True)
122+
response.raise_for_status()
123+
for chunk in response.iter_content(chunk_size=8192):
124+
if chunk:
125+
f.write(chunk)
126+
127+
sha256_hash = calculate_sha256(package_dir / filename)
128+
129+
# Check if this version is yanked
130+
yanked_attr = ""
131+
if self.is_version_yanked(package, version):
132+
yanked_attr = ' data-yanked="true"'
133+
134+
file_links.append(
135+
f'<a href="{quote(filename)}#sha256={sha256_hash}"{yanked_attr}>{filename}</a><br/>'
136+
)
137+
138+
package_index = HTML_TEMPLATE.format(
139+
package_name=f"Links for {package}",
140+
package_links="\n".join(file_links)
141+
)
142+
143+
with open(package_dir / "index.html", "w") as f:
144+
f.write(package_index)
145+
146+
def build(self):
147+
# Create output directory
148+
self.output_dir.mkdir(parents=True, exist_ok=True)
149+
150+
# Collect and generate
151+
self.collect_packages()
152+
self.generate_index_html()
153+
154+
155+
def main():
156+
# Get environment variables
157+
token = os.environ.get("GITHUB_TOKEN")
158+
repo = os.environ.get("GITHUB_REPOSITORY")
159+
print (repo)
160+
output_dir = os.environ.get("OUTPUT_DIR", "dist")
161+
162+
if not all([token, repo]):
163+
print ("Missing required environment variables")
164+
sys.exit(1)
165+
166+
builder = PackageIndexBuilder(token, repo, output_dir, yanked_versions)
167+
builder.build()
168+
169+
if __name__ == "__main__":
170+
main()

.github/workflows/package.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# .github/workflows/build-wheels.yml
2+
name: Build and Package Wheels
3+
4+
on:
5+
pull_request:
6+
push:
7+
8+
env:
9+
LIBRDKAFKA_VERSION: v2.12.0.1-RC1
10+
11+
jobs:
12+
13+
build-linux:
14+
name: Build wheels for Linux ${{ matrix.arch }}
15+
strategy:
16+
matrix:
17+
include:
18+
- arch: x64
19+
runner: ubuntu-24.04
20+
- arch: arm64
21+
runner: ubuntu-24.04-arm
22+
runs-on: ${{ matrix.runner }}
23+
env:
24+
OS_NAME: linux
25+
ARCH: ${{ matrix.arch }}
26+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27+
steps:
28+
- uses: actions/checkout@v4
29+
- name: Build wheels
30+
run: |
31+
./tools/wheels/build-wheels.sh "${LIBRDKAFKA_VERSION#v}" wheelhouse
32+
- uses: actions/upload-artifact@v4
33+
with:
34+
name: wheels-${{ env.OS_NAME }}-${{ env.ARCH }}
35+
path: wheelhouse/confluent_kafka*.whl
36+
37+
build-windows:
38+
name: Build wheels for Windows
39+
runs-on: windows-latest
40+
env:
41+
OS_NAME: windows
42+
ARCH: x64
43+
CHERE_INVOKING: yes
44+
MSYSTEM: UCRT64
45+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46+
steps:
47+
- uses: actions/checkout@v4
48+
- name: Setup MSYS2
49+
uses: msys2/setup-msys2@v2
50+
- name: Build wheels
51+
shell: bash
52+
run: |
53+
./tools/mingw-w64/msys2-dependencies.sh
54+
bash tools/mingw-w64/semaphore_commands.sh
55+
bash tools/wheels/install-librdkafka.sh ${LIBRDKAFKA_VERSION#v} dest
56+
tools/wheels/build-wheels.bat x64 win_amd64 dest wheelhouse
57+
- uses: actions/upload-artifact@v4
58+
with:
59+
name: wheels-${{ env.OS_NAME }}-${{ env.ARCH }}
60+
path: wheelhouse/confluent_kafka*.whl
61+
62+
create_release_artifacts:
63+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
64+
name: Create release artifacts
65+
needs: [build-linux, build-windows]
66+
runs-on: ubuntu-latest
67+
steps:
68+
- uses: actions/checkout@v4
69+
- uses: astral-sh/setup-uv@v6
70+
- name: Check version consistency
71+
run: |
72+
tag_version=${GITHUB_REF/refs\/tags\/v/}
73+
proj_version=$(uv run --no-project --with poetry poetry version -s)
74+
if [ "$tag_version" != "$proj_version" ]; then
75+
echo "::error title=Version mismatch::Tag version \"$tag_version\" does not match project version \"$proj_version\"."
76+
exit 1
77+
fi
78+
- uses: actions/download-artifact@v4
79+
with:
80+
path: artifacts
81+
pattern: wheels-*
82+
merge-multiple: true
83+
84+
- name: Create release
85+
uses: softprops/action-gh-release@v2
86+
with:
87+
files: |
88+
artifacts/confluent_kafka*
89+
env:
90+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
91+
92+
publish_simple_package_index:
93+
name: Build&publish a PyPI-compatible package index
94+
runs-on: ubuntu-latest
95+
needs: [create_release_artifacts]
96+
if: always() && !failure() && !cancelled() && (github.event_name == 'push')
97+
concurrency:
98+
group: simple_package_index
99+
cancel-in-progress: true
100+
permissions:
101+
contents: write
102+
actions: read
103+
packages: read
104+
pages: write
105+
id-token: write
106+
steps:
107+
- uses: actions/checkout@v2
108+
- name: Generate Package Index
109+
run: |
110+
python -m pip install --upgrade pip
111+
pip install PyGithub
112+
python .github/scripts/generate_index.py
113+
env:
114+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
115+
OUTPUT_DIR: dist
116+
117+
- name: Upload Site Artifact
118+
uses: actions/upload-pages-artifact@v3
119+
with:
120+
path: 'dist'
121+
122+
- name: Deploy to GitHub Pages
123+
id: deployment
124+
uses: actions/deploy-pages@v4
125+
126+
- name: Display GitHub Pages URL
127+
run: |
128+
echo "Package Index URL: ${{ steps.deployment.outputs.page_url }}"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "confluent-kafka"
7-
version = "2.12.0"
7+
version = "2.12.0.1rc1+gr"
88
description = "Confluent's Python client for Apache Kafka"
99
classifiers = [
1010
"Development Status :: 5 - Production/Stable",

tools/wheels/install-librdkafka.sh

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,19 @@ echo "$0: Installing librdkafka $VER to $DEST"
1919
[[ -d "$DEST" ]] || mkdir -p "$DEST"
2020
pushd "$DEST"
2121

22-
curl -L -o lrk$VER.zip https://www.nuget.org/api/v2/package/librdkafka.redist/$VER
22+
# Check if variable exists
23+
if [ -z "${GITHUB_TOKEN}" ]; then
24+
echo "Error: GITHUB_TOKEN is not set"
25+
exit 1
26+
fi
27+
28+
curl -H "Authorization: Bearer ${GITHUB_TOKEN}" \
29+
-H "Accept: application/vnd.github.v3+json" \
30+
-L \
31+
-o lrk$VER.zip \
32+
https://nuget.pkg.github.com/G-Research/download/librdkafka.redist/$VER/librdkafka.redist.$VER.nupkg
33+
34+
#curl -L -o lrk$VER.zip https://www.nuget.org/api/v2/package/librdkafka.redist/$VER
2335

2436
unzip lrk$VER.zip
2537

@@ -28,12 +40,7 @@ ARCH=${ARCH:-x64}
2840
if [[ $OSTYPE == linux* ]]; then
2941
# Linux
3042

31-
# Copy the librdkafka build with least dependencies to librdkafka.so.1
32-
if [[ $ARCH == arm64* ]]; then
33-
cp -v runtimes/linux-$ARCH/native/{librdkafka.so,librdkafka.so.1}
34-
else
35-
cp -v runtimes/linux-$ARCH/native/{centos8-librdkafka.so,librdkafka.so.1}
36-
fi
43+
cp -v runtimes/linux-$ARCH/native/{librdkafka.so,librdkafka.so.1}
3744
ldd runtimes/linux-$ARCH/native/librdkafka.so.1
3845

3946
elif [[ $OSTYPE == darwin* ]]; then

0 commit comments

Comments
 (0)