Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions .github/workflows/llvm-update-autocheck.yml

This file was deleted.

190 changes: 190 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Publish to PyPI
#
# Runs daily to check for new LLVM versions and automatically publish.
# Checks all major.minor versions >= 16 and publishes the latest patch for each.
#
# Manual trigger:
# 1. Go to Actions → Publish to PyPI → Run workflow
# 2. Optionally enter LLVM versions (comma-separated, e.g., "18.1.0,19.1.0,20.1.0")
# 3. Check "force" to re-publish if tag exists but PyPI publish failed
# 4. Click "Run workflow"

name: Publish to PyPI

on:
workflow_dispatch:
inputs:
llvm_versions:
description: 'LLVM versions to release, comma-separated (leave empty for latest)'
required: false
type: string
force:
description: 'Force re-publish even if tag exists (use if PyPI publish failed)'
required: false
type: boolean
default: false
schedule:
- cron: '0 10 * * *'

jobs:
publish:
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: write
id-token: write

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Determine versions to publish
id: versions
run: |
if [ -n "${{ inputs.llvm_versions }}" ]; then
# Convert comma-separated input to space-separated
versions=$(echo "${{ inputs.llvm_versions }}" | tr ',' ' ' | xargs)
echo "Using manually specified versions: $versions"
else
# Get latest patch version for each major.minor >= 16
versions=$(git -c 'versionsort.suffix=-' \
ls-remote --exit-code --refs --sort='version:refname' --tags https://github.com/llvm/llvm-project 'llvmorg-*.*.*' \
| sed 's/.*llvmorg-//' \
| awk -F. '$1 >= 16' \
| sort -t. -k1,1n -k2,2n -k3,3n \
| awk -F. '{key=$1"."$2; versions[key]=$0} END {for (k in versions) print versions[k]}' \
| sort -t. -k1,1n -k2,2n \
| tr '\n' ' ')
echo "Detected LLVM versions (latest patch per major.minor): $versions"
fi
echo "versions=$versions" >> $GITHUB_OUTPUT

- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Publish each version sequentially
id: publish
env:
GH_TOKEN: ${{ github.token }}
FORCE: ${{ inputs.force }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

failed_versions=""
published_versions=""

for version in ${{ steps.versions.outputs.versions }}; do
echo "::group::Processing LLVM $version"

# Fetch latest to check for existing tags and get any new commits
git fetch origin --tags

# Check if tag already exists (skip unless force is set)
tag_exists=false
if git rev-parse "v$version" >/dev/null 2>&1; then
if [ "$FORCE" != "true" ]; then
echo "Tag v$version already exists, skipping (use force=true to re-publish)"
echo "::endgroup::"
continue
fi
echo "Tag v$version exists but force=true, re-publishing..."
tag_exists=true
fi

# Track if this version fails
version_failed=false

# Pull latest changes (from previous iterations or other sources)
if ! git pull --rebase origin master; then
echo "::error::Failed to pull latest changes for $version"
failed_versions="$failed_versions $version"
echo "::endgroup::"
continue
fi

# Download and compute hash
echo "Downloading clang-${version}.src.tar.xz..."
if ! curl -fSL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/clang-${version}.src.tar.xz" -o "clang-${version}.src.tar.xz"; then
echo "::error::Failed to download LLVM $version"
failed_versions="$failed_versions $version"
echo "::endgroup::"
continue
fi

hash=$(sha256sum "clang-${version}.src.tar.xz" | cut -d ' ' -f1)
rm -f "clang-${version}.src.tar.xz"

# Update version file
echo "set(LLVM_VERSION $version)" > llvm_version.cmake
echo "set(LLVM_SHA256 $hash)" >> llvm_version.cmake
cat llvm_version.cmake

# Commit and tag (skip if force and tag already exists)
if [ "$tag_exists" = "false" ]; then
git add llvm_version.cmake
git commit -m "Update LLVM to version $version"
git tag "v$version"
if ! git push origin HEAD:master --tags; then
echo "::error::Failed to push tag for $version"
failed_versions="$failed_versions $version"
echo "::endgroup::"
continue
fi
fi

# Build
echo "Building wheel..."
if ! uv build; then
echo "::error::Failed to build wheel for $version"
failed_versions="$failed_versions $version"
version_failed=true
fi

# Publish to PyPI
if [ "$version_failed" = "false" ]; then
echo "Publishing to PyPI..."
if ! uv publish --trusted-publishing always; then
echo "::error::Failed to publish $version to PyPI"
failed_versions="$failed_versions $version"
version_failed=true
fi
fi

# Create GitHub Release (even if PyPI failed, so we track the tag)
if [ "$version_failed" = "false" ]; then
if ! gh release view "v$version" >/dev/null 2>&1; then
echo "Creating GitHub release for v$version"
if ! gh release create "v$version" \
--title "v$version" \
--notes $'LLVM '"$version"$' Python bindings\n\n[LLVM Release Notes](https://releases.llvm.org/'"$version"$'/docs/ReleaseNotes.html)'; then
echo "::warning::Failed to create GitHub release for $version"
fi
else
echo "Release v$version already exists"
fi
published_versions="$published_versions $version"
fi

# Clean dist for next iteration
rm -rf dist/

echo "::endgroup::"
done

# Report results
if [ -n "$published_versions" ]; then
echo "Successfully published:$published_versions"
fi

if [ -n "$failed_versions" ]; then
echo "failed_versions=$failed_versions" >> $GITHUB_OUTPUT
echo "::error::Failed to publish versions:$failed_versions"
fi

- name: Check for failures
if: always() && steps.publish.outputs.failed_versions != ''
run: |
echo "The following versions failed to publish: ${{ steps.publish.outputs.failed_versions }}"
exit 1
47 changes: 0 additions & 47 deletions .github/workflows/release.yml

This file was deleted.

11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ The wheel is built from the LLVM libclang python binding source code directly. T

This package does not contain binary files for the libclang library, only the python bindings.

## Documentation

- [Official libclang documentation](https://clang.llvm.org/docs/Tooling.html#libclang)
- [Python bindings source](https://github.com/llvm/llvm-project/tree/main/clang/bindings/python)

## Installation

Expand All @@ -30,9 +34,6 @@ pip install clang==20

This repository follows the [license agreement](https://github.com/llvm/llvm-project/blob/main/LICENSE.TXT) of the LLVM project, see Apache-2.0 WITH LLVM-exception.

## Release SOP
## Releases

- GitHub Workflows will create new pull requests weekly when LLVM releases new major versions. You can always manually trigger the GitHub Actions.
- Merge the PR created by GH Action.
- Create a new release, with a new tag "v<version>".
- The creation of the tag will trigger a push, which will trigger the release/pypi upload workflow, through Trusted Publishing
Releases are automated. When LLVM publishes a new version, this repository automatically detects it (checked daily), builds the wheel, and publishes to PyPI.
10 changes: 8 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@ classifiers = [
"Development Status :: 5 - Production/Stable",
"Topic :: Software Development :: Compilers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
requires-python = ">=3.9"
dynamic = ["version"]

[project.urls]
Homepage = "http://clang.llvm.org/"
Download = "http://llvm.org/releases/download.html"

[dependecy-groups]
[dependency-groups]
dev = ["build", "twine"]

[tool.scikit-build]
Expand All @@ -38,4 +44,4 @@ input = "llvm_version.cmake"
regex = '''(?sx)
set\(\s*LLVM_VERSION\s+(?P<version>\d+(?:\.\d+)+)\s*\)
'''
result = "{version}"
result = "{version}"