Skip to content

Commit afa440a

Browse files
authored
Hotfix Release: v2.12.1.1+gr - collective changes (#16)
- CI/CD script
1 parent caca3cd commit afa440a

File tree

4 files changed

+319
-8
lines changed

4 files changed

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