Release Clang Tools #12
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Clang Tools | |
| on: | |
| push: | |
| # Only the default branch, so pushing a work branch cannot publish releases. | |
| branches: [master] | |
| schedule: | |
| # Run every 6 hours to check for new LLVM tags | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| llvm_tag: | |
| description: 'Specific LLVM tag(s) to process, space or comma separated (optional)' | |
| required: false | |
| type: string | |
| force: | |
| description: 'Reprocess tags that already have a release and upload any missing binaries. With no llvm_tag this backfills every known tag.' | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: write | |
| jobs: | |
| check-and-release: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_new_tags: ${{ steps.llvm-tags.outputs.has_new_tags }} | |
| tags: ${{ steps.llvm-tags.outputs.tags }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Get LLVM tags | |
| id: llvm-tags | |
| env: | |
| INPUT_TAG: ${{ github.event.inputs.llvm_tag }} | |
| INPUT_FORCE: ${{ github.event.inputs.force }} | |
| run: | | |
| set -euo pipefail | |
| # All upstream release tags from llvmorg-14.0.0 onwards (binary availability | |
| # became consistent there), excluding release candidates. | |
| git ls-remote --tags https://github.com/llvm/llvm-project.git | | |
| grep 'refs/tags/llvmorg-[0-9]' | | |
| grep -v '\^{}' | | |
| sed 's/.*refs\/tags\///' | | |
| grep -E '^llvmorg-(1[4-9]|[2-9][0-9])\.' | | |
| grep -v -- '-rc[0-9]' | | |
| sort -V -u > all_llvm_tags.txt | |
| git tag | grep -E '^llvmorg-(1[4-9]|[2-9][0-9])\.' | grep -v -- '-rc[0-9]' | | |
| sort -V -u > existing_tags.txt || : > existing_tags.txt | |
| echo "Upstream tags: $(wc -l < all_llvm_tags.txt)" | |
| echo "Tags already released here: $(wc -l < existing_tags.txt)" | |
| if [ -n "${INPUT_TAG}" ]; then | |
| # Explicit list wins; accept commas or whitespace as separators. | |
| echo "${INPUT_TAG}" | tr ',' '\n' | tr -s '[:space:]' '\n' | | |
| sed '/^$/d' > selected_tags.txt | |
| elif [ "${INPUT_FORCE}" = "true" ]; then | |
| # Backfill mode: revisit everything so releases missing binaries get fixed. | |
| cp all_llvm_tags.txt selected_tags.txt | |
| else | |
| # grep -vxF compares whole lines via a hash, so neither input has to be | |
| # sorted; comm would silently misbehave on `sort -V` output. | |
| grep -vxF -f existing_tags.txt all_llvm_tags.txt > selected_tags.txt || : > selected_tags.txt | |
| fi | |
| echo "Tags selected for processing:" | |
| cat selected_tags.txt | |
| # A matrix is capped at 256 jobs. | |
| if [ "$(wc -l < selected_tags.txt)" -gt 256 ]; then | |
| echo "::warning::More than 256 tags selected; processing the first 256." | |
| head -256 selected_tags.txt > selected_tags.trimmed && mv selected_tags.trimmed selected_tags.txt | |
| fi | |
| if [ -s selected_tags.txt ]; then | |
| echo "tags=$(jq -R -s -c 'split("\n")[:-1]' < selected_tags.txt)" >> "$GITHUB_OUTPUT" | |
| echo "has_new_tags=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_new_tags=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| create-releases: | |
| needs: check-and-release | |
| if: needs.check-and-release.outputs.has_new_tags == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| llvm_tag: ${{ fromJson(needs.check-and-release.outputs.tags) }} | |
| max-parallel: 5 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Check whether ${{ matrix.llvm_tag }} needs processing | |
| id: precheck | |
| env: | |
| LLVM_TAG: ${{ matrix.llvm_tag }} | |
| INPUT_FORCE: ${{ github.event.inputs.force }} | |
| run: | | |
| set -euo pipefail | |
| if git rev-parse -q --verify "refs/tags/${LLVM_TAG}" >/dev/null && [ "${INPUT_FORCE}" != "true" ]; then | |
| echo "Tag ${LLVM_TAG} already exists; rerun with force to refresh its binaries." | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Free up disk space | |
| if: steps.precheck.outputs.skip != 'true' | |
| run: | | |
| # The LLVM archives reach ~2 GB each and the runner image ships with | |
| # several GB of toolchains this job never touches. | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc | |
| df -h / | |
| - name: Download clang-format and clang-tidy for ${{ matrix.llvm_tag }} | |
| id: fetch | |
| if: steps.precheck.outputs.skip != 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LLVM_TAG: ${{ matrix.llvm_tag }} | |
| run: | | |
| set +e | |
| bash ./scripts/fetch-clang-tools.sh "${LLVM_TAG}" "artifacts/${LLVM_TAG}" | |
| rc=$? | |
| set -e | |
| # Exit code 3 means upstream has not published this release yet. Not an | |
| # error: leave the tag unprocessed so a later run picks it up. | |
| if [ "${rc}" -eq 3 ]; then | |
| echo "nothing_to_publish=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| exit "${rc}" | |
| - name: Publish release ${{ matrix.llvm_tag }} | |
| if: steps.precheck.outputs.skip != 'true' && steps.fetch.outputs.nothing_to_publish != 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LLVM_TAG: ${{ matrix.llvm_tag }} | |
| run: | | |
| set -euo pipefail | |
| VERSION=${LLVM_TAG#llvmorg-} | |
| ARTIFACTS="artifacts/${LLVM_TAG}" | |
| # Describe what this release actually contains rather than what it might. | |
| { | |
| echo "Standalone clang-format and clang-tidy binaries extracted from LLVM ${VERSION}." | |
| echo | |
| echo "Download the binary for your platform and make it executable (\`chmod +x\` on Unix systems)." | |
| echo | |
| echo "| Binary | Size |" | |
| echo "| --- | --- |" | |
| for binary in "${ARTIFACTS}"/*; do | |
| echo "| \`$(basename "${binary}")\` | $(du -h "${binary}" | cut -f1) |" | |
| done | |
| echo | |
| echo "Platforms absent from this list were not published by upstream for this version." | |
| echo | |
| echo "Source: https://github.com/llvm/llvm-project/releases/tag/${LLVM_TAG}" | |
| } > release_notes.md | |
| # No `git tag` + `git push` here: `gh release create` creates the tag at | |
| # --target when it does not exist yet, which keeps this working with | |
| # persist-credentials disabled. | |
| if gh release view "${LLVM_TAG}" >/dev/null 2>&1; then | |
| echo "Updating existing release ${LLVM_TAG}" | |
| gh release edit "${LLVM_TAG}" \ | |
| --title "clang-format and clang-tidy ${VERSION}" \ | |
| --notes-file release_notes.md | |
| else | |
| gh release create "${LLVM_TAG}" \ | |
| --title "clang-format and clang-tidy ${VERSION}" \ | |
| --notes-file release_notes.md \ | |
| --target master | |
| fi | |
| # --clobber so a rerun repairs a release that is missing binaries. | |
| gh release upload "${LLVM_TAG}" "${ARTIFACTS}"/* --clobber | |
| echo "Published $(ls -1 "${ARTIFACTS}" | wc -l) binaries for ${LLVM_TAG}" |