diff --git a/.github/workflows/llvm-update-autocheck.yml b/.github/workflows/llvm-update-autocheck.yml deleted file mode 100644 index cacea16..0000000 --- a/.github/workflows/llvm-update-autocheck.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Auto-update LLVM Version -on: - workflow_dispatch: - schedule: - - cron: '0 10 * * 1' -jobs: - update-dep: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check and update LLVM version - id: check-llvm - run: | - old_version="$(grep -Po '(?<=LLVM_VERSION )\d+(\.\d+)+' llvm_version.cmake)" - echo "Old version: $old_version" - echo "old_version=$old_version" >> $GITHUB_OUTPUT - - new_version=$(git -c 'versionsort.suffix=-' \ - ls-remote --exit-code --refs --sort='version:refname' --tags https://github.com/llvm/llvm-project 'llvmorg-*.*.*'\ - | grep -Po '\d+\.\d+(\.\d+)?$' \ - | tail --lines=1) - - echo "New version: $new_version" - echo "new_version=$new_version" >> $GITHUB_OUTPUT - - if [ "$old_version" != "$new_version" ]; then - echo "set(LLVM_VERSION $new_version)" > llvm_version.cmake - curl -L https://github.com/llvm/llvm-project/releases/download/llvmorg-${new_version}/clang-${new_version}.src.tar.xz -o clang-${new_version}.src.tar.xz - hash=$(sha256sum clang-${new_version}.src.tar.xz | cut -d ' ' -f1) - rm -f clang-${new_version}.src.tar.xz - echo "set(LLVM_SHA256 $hash)" >> llvm_version.cmake - echo "LLVM version updated" - else - echo "No change in LLVM version" - fi - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: Update LLVM to version ${{ steps.check-llvm.outputs.new_version }} - title: Update LLVM version (${{ steps.check-llvm.outputs.new_version }}) - body: | - - Update LLVM version to ${{ steps.check-llvm.outputs.new_version }} - Auto-generated by ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.job }}?check_suite_focus=true - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - branch: llvm-version-update/${{ steps.check-llvm.outputs.new_version }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..bb48db4 --- /dev/null +++ b/.github/workflows/publish.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index bc9510e..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Build and Release on PyPI - -on: - workflow_dispatch: - push: - tags: - - v* - -jobs: - build-release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - name: Build wheel - run: | - pip install build twine - python -m build - python -m twine check dist/* - - name: Upload Python package dist artifacts - uses: actions/upload-artifact@v4 - with: - name: python-package-dist - path: dist - - pypi-publish: - name: Upload release to PyPI - runs-on: ubuntu-latest - needs: build-release - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - environment: - name: pypi - url: https://pypi.org/p/clang - permissions: - id-token: write # IMPORTANT: this permission is mandatory for trusted publishing - steps: - - name: Download Python package dist artifacts - uses: actions/download-artifact@v4 - with: - name: python-package-dist - path: dist - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 \ No newline at end of file diff --git a/README.md b/README.md index 522b1fe..6c3271d 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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". -- 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. diff --git a/pyproject.toml b/pyproject.toml index 66814cf..92597fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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] @@ -38,4 +44,4 @@ input = "llvm_version.cmake" regex = '''(?sx) set\(\s*LLVM_VERSION\s+(?P\d+(?:\.\d+)+)\s*\) ''' -result = "{version}" \ No newline at end of file +result = "{version}"