Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1ef22ae
chore: use umbrella targets for LLVM distribution components
16bit-ykiko Jun 6, 2026
93b5310
fix: reduce LLVM_TARGETS_TO_BUILD to X86;AArch64;ARM;RISCV
16bit-ykiko Jun 6, 2026
7f612d9
fix: free disk space on macOS runners before LLVM build
16bit-ykiko Jun 6, 2026
d734725
ci: add temporary workflow to test macOS disk cleanup
16bit-ykiko Jun 6, 2026
fc7e2bc
ci(temp): test macOS disk cleanup only
16bit-ykiko Jun 6, 2026
99c8b0e
ci: restore full build matrix and remove test workflow
16bit-ykiko Jun 6, 2026
c7e7462
chore: adapt codebase to LLVM 22.1.4 API changes
16bit-ykiko Jun 6, 2026
9e004a2
ci: add prune-llvm workflow for discovering unused LLVM libraries
16bit-ykiko Jun 6, 2026
be37e95
fix(prune): record file sizes during discover and fix skip-pattern
16bit-ykiko Jun 6, 2026
b5be360
refactor(cmake): switch to find_package(LLVM/Clang) for dependency re…
16bit-ykiko Jun 6, 2026
25a6e85
fix(prune): nullify shared libs and force relink during discovery
16bit-ykiko Jun 6, 2026
115fc4a
fix(prune): replace pruned libs with empty archives and track shared …
16bit-ykiko Jun 6, 2026
baffc35
ci: add release-llvm workflow and rewrite cmake download
16bit-ykiko Jun 6, 2026
42ae5f3
ci: add push trigger for release-llvm on feature branch
16bit-ykiko Jun 6, 2026
9d83938
refactor: rename prune script to release-llvm and add parallel repackage
16bit-ykiko Jun 6, 2026
8a1ff57
ci: split repackage into parallel matrix jobs with direct release upload
16bit-ykiko Jun 6, 2026
2701592
fix(release): use xz -6 compression to keep assets under 2GB limit
16bit-ykiko Jun 6, 2026
b6eaf21
fix(release): use xz -6 compression to keep assets under 2GB limit
16bit-ykiko Jun 6, 2026
a98c370
fix(release): retry with xz -9 when artifact exceeds 2GB limit
16bit-ykiko Jun 6, 2026
3e6e9c7
refactor(release): use xz -9 compression and key-based manifest format
16bit-ykiko Jun 7, 2026
3ccd229
refactor(release): use xz -9e extreme compression for maximum ratio
16bit-ykiko Jun 7, 2026
0bb19b6
chore: update llvm-manifest.json to 22.1.4
16bit-ykiko Jun 7, 2026
5a06cd0
chore: remove unused setup-llvm.py
16bit-ykiko Jun 8, 2026
c609dc8
fix: address review findings for LLVM 22 upgrade
16bit-ykiko Jun 9, 2026
4adaa1e
fix: address second round of review findings
16bit-ykiko Jun 9, 2026
ea07d04
chore: strip build-llvm.yml to LLVM-only build
16bit-ykiko Jun 9, 2026
4d82e38
fix: add clangOptions to link deps and apply formatting
16bit-ykiko Jun 9, 2026
a8720c9
chore: remove dead upload-llvm workflow and scripts
16bit-ykiko Jun 9, 2026
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
339 changes: 47 additions & 292 deletions .github/workflows/build-llvm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,9 @@ on:
workflow_dispatch:
inputs:
llvm_version:
description: "LLVM version to build (e.g., 21.1.8)"
description: "LLVM version to build (e.g., 22.1.4)"
required: true
type: string
skip_upload:
description: "Skip upload and PR creation (build-only mode)"
required: false
type: boolean
default: false
skip_pr:
description: "Skip PR creation (upload only, no PR)"
required: false
type: boolean
default: false
pull_request:
# if you want to run this workflow, change the branch name to main,
# if you want to turn off it, change it to non existent branch.
branches: [main-turn-off]

jobs:
build:
Expand Down Expand Up @@ -121,20 +107,48 @@ jobs:
with:
environments: ${{ matrix.pixi_env || 'package' }}

- name: Free Disk Space (macOS)
if: runner.os == 'macOS'
run: |
echo "=== Before cleanup ==="
df -h /

# Remove iOS/tvOS/watchOS/visionOS simulators
if [ -d "/Library/Developer/CoreSimulator" ]; then
sudo rm -rf /Library/Developer/CoreSimulator
echo "Removed CoreSimulator"
fi

# Remove unnecessary Xcode platforms (keep macOS only)
XCODE_PATH="$(xcode-select -p)"
PLATFORMS_DIR="${XCODE_PATH}/Platforms"
for platform in iPhoneOS.platform iPhoneSimulator.platform AppleTVOS.platform AppleTVSimulator.platform WatchOS.platform WatchSimulator.platform XROS.platform XRSimulator.platform; do
if [ -d "${PLATFORMS_DIR}/${platform}" ]; then
sudo rm -rf "${PLATFORMS_DIR}/${platform}"
echo "Removed ${platform}"
fi
done

# Remove Android SDK
sudo rm -rf ~/Library/Android

# Remove .NET/PowerShell
sudo rm -rf /usr/local/share/dotnet
sudo rm -rf /usr/local/share/powershell

# Remove Haskell
sudo rm -rf ~/.ghcup

echo "=== After cleanup ==="
df -h /

- name: Clone llvm-project
shell: bash
run: |
VERSION="${{ inputs.llvm_version || '21.1.8' }}"
VERSION="${{ inputs.llvm_version }}"
echo "Cloning LLVM ${VERSION}..."
git clone --branch "llvmorg-${VERSION}" --depth 1 https://github.com/llvm/llvm-project.git .llvm
Comment on lines 145 to 150

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Template injection vulnerability from user-controlled input.

Direct interpolation of ${{ inputs.llvm_version }} into the shell script enables code injection if a malicious value is provided. While the attack surface is limited to users with workflow dispatch permissions, defense-in-depth recommends using the env context instead.

🔒 Recommended fix: pass input via env context
       - name: Clone llvm-project
         shell: bash
+        env:
+          LLVM_VERSION: ${{ inputs.llvm_version }}
         run: |
-          VERSION="${{ inputs.llvm_version }}"
+          VERSION="${LLVM_VERSION}"
           echo "Cloning LLVM ${VERSION}..."
           git clone --branch "llvmorg-${VERSION}" --depth 1 https://github.com/llvm/llvm-project.git .llvm
🧰 Tools
🪛 zizmor (1.25.2)

[error] 148-148: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-llvm.yml around lines 145 - 150, The workflow step
"Clone llvm-project" currently interpolates the user input directly into the run
script via `${{ inputs.llvm_version }}`, which allows template injection;
instead pass the input through the step's env context (set an env key like
VERSION: ${{ inputs.llvm_version }}) and then reference the safe shell variable
(`$VERSION`) inside the run script used by that step (keep the step name "Clone
llvm-project" and the shell variable `VERSION` so you can find it). Ensure the
run script uses the env variable (quoted) when printing and when supplying the
git --branch argument to avoid direct template interpolation.

Source: Linters/SAST tools


- name: Validate distribution components
shell: bash
run: |
python3 scripts/validate-llvm-components.py \
--llvm-src=.llvm \
--components-file=scripts/llvm-components.json

- name: Build LLVM (install-distribution)
shell: bash
run: |
Expand All @@ -150,158 +164,30 @@ jobs:
--build-dir=build \
${EXTRA_ARGS}

- name: Build clice using installed LLVM
if: ${{ !matrix.target_triple }}
shell: bash
run: |
pixi run cmake-config ${{ matrix.llvm_mode }} ON -- \
"-DCLICE_ENABLE_LTO=${{ matrix.lto }}" \
"-DLLVM_INSTALL_PATH=.llvm/build-install"
pixi run cmake-build ${{ matrix.llvm_mode }}

- name: Build clice using installed LLVM (cross-compile)
if: ${{ matrix.target_triple }}
shell: bash
run: |
ENV="${{ matrix.pixi_env || 'package' }}"
pixi run -e "$ENV" cmake-config ${{ matrix.llvm_mode }} ON -- \
"-DCLICE_ENABLE_LTO=${{ matrix.lto }}" \
"-DCLICE_TARGET_TRIPLE=${{ matrix.target_triple }}" \
"-DLLVM_INSTALL_PATH=.llvm/build-install"
pixi run -e "$ENV" cmake-build ${{ matrix.llvm_mode }}

- name: Verify cross-compiled binary architecture
if: ${{ matrix.target_triple && runner.os != 'Windows' }}
shell: bash
run: |
BINARY="build/${{ matrix.llvm_mode }}/bin/clice"
echo "Binary info:"
file "$BINARY"
case "${{ matrix.target_triple }}" in
aarch64-linux-gnu) file "$BINARY" | grep -q "aarch64" ;;
x86_64-apple-darwin) file "$BINARY" | grep -q "x86_64" ;;
esac

- name: Upload cross-compiled clice for functional test
if: ${{ matrix.target_triple && matrix.lto == 'OFF' }}
uses: actions/upload-artifact@v4
with:
name: cross-clice-${{ matrix.target_triple }}-${{ matrix.llvm_mode }}
path: |
build/${{ matrix.llvm_mode }}/bin/
build/${{ matrix.llvm_mode }}/lib/
if-no-files-found: error
retention-days: 1

- name: Run tests
if: ${{ !matrix.target_triple }}
shell: bash
run: pixi run test ${{ matrix.llvm_mode }}

# Prune is only supported for native builds (requires linking clice to test).
# Cross-compiled targets reuse the native prune manifest of the same OS.
- name: Prune LLVM static libraries (Debug/RelWithDebInfo no LTO)
if: (!matrix.target_triple) && (matrix.llvm_mode == 'Debug' || (matrix.llvm_mode == 'RelWithDebInfo' && matrix.lto == 'OFF'))
shell: bash
run: |
MANIFEST="pruned-libs-${{ matrix.os }}.json"
echo "LLVM_PRUNED_MANIFEST=${MANIFEST}" >> "${GITHUB_ENV}"
python3 scripts/prune-llvm-bin.py \
--action discover \
--install-dir ".llvm/build-install/lib" \
--build-dir "build/${{ matrix.llvm_mode }}" \
--max-attempts 60 \
--sleep-seconds 60 \
--manifest "${MANIFEST}"

- name: Upload pruned-libs manifest
if: (!matrix.target_triple) && matrix.llvm_mode == 'RelWithDebInfo' && matrix.lto == 'OFF'
uses: actions/upload-artifact@v4
with:
name: llvm-pruned-libs-${{ matrix.os }}
path: ${{ env.LLVM_PRUNED_MANIFEST }}
if-no-files-found: error
compression-level: 0

- name: Apply pruned-libs manifest (RelWithDebInfo + LTO, native only)
if: (!matrix.target_triple) && matrix.llvm_mode == 'RelWithDebInfo' && matrix.lto == 'ON'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
MANIFEST="pruned-libs-${{ matrix.os }}.json"
python3 scripts/prune-llvm-bin.py \
--action apply \
--manifest "${MANIFEST}" \
--install-dir ".llvm/build-install/lib" \
--build-dir "build/${{ matrix.llvm_mode }}" \
--gh-run-id "${{ github.run_id }}" \
--gh-artifact "llvm-pruned-libs-${{ matrix.os }}" \
--gh-download-dir "artifacts" \
--max-attempts 60 \
--sleep-seconds 60

# For cross-compiled LTO builds, apply the native prune manifest.
# The unused library set is arch-independent (same API surface).
- name: Apply pruned-libs manifest (cross-compile + LTO)
if: matrix.target_triple && matrix.lto == 'ON'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
MANIFEST="pruned-libs-${{ matrix.os }}.json"
python3 scripts/prune-llvm-bin.py \
--action apply \
--manifest "${MANIFEST}" \
--install-dir ".llvm/build-install/lib" \
--build-dir "build/${{ matrix.llvm_mode }}" \
--gh-run-id "${{ github.run_id }}" \
--gh-artifact "llvm-pruned-libs-${{ matrix.os }}" \
--gh-download-dir "artifacts" \
--max-attempts 60 \
--sleep-seconds 60

- name: Package LLVM install directory
shell: bash
run: |
MODE_TAG="releasedbg"
if [[ "${{ matrix.llvm_mode }}" == "Debug" ]]; then
MODE_TAG="debug"
fi

# Determine arch/platform/toolchain from target triple or runner OS
# Determine platform/arch from target triple or runner OS
if [[ -n "${{ matrix.target_triple }}" ]]; then
case "${{ matrix.target_triple }}" in
x86_64-apple-darwin)
ARCH="x64"; PLATFORM="macos"; TOOLCHAIN="clang" ;;
aarch64-linux-gnu)
ARCH="aarch64"; PLATFORM="linux"; TOOLCHAIN="gnu" ;;
aarch64-pc-windows-msvc)
ARCH="aarch64"; PLATFORM="windows"; TOOLCHAIN="msvc" ;;
x86_64-apple-darwin) PLATFORM="macos"; ARCH="x64" ;;
aarch64-linux-gnu) PLATFORM="linux"; ARCH="aarch64" ;;
aarch64-pc-windows-msvc) PLATFORM="windows"; ARCH="aarch64" ;;
esac
else
ARCH="x64"
PLATFORM="linux"
TOOLCHAIN="gnu"
PLATFORM="linux"; ARCH="x64"
if [[ "${{ matrix.os }}" == windows-* ]]; then
PLATFORM="windows"
TOOLCHAIN="msvc"
elif [[ "${{ matrix.os }}" == macos-* ]]; then
ARCH="arm64"
PLATFORM="macos"
TOOLCHAIN="clang"
PLATFORM="macos"; ARCH="arm64"
fi
fi

SUFFIX=""
if [[ "${{ matrix.lto }}" == "ON" ]]; then
SUFFIX="-lto"
fi
if [[ "${{ matrix.llvm_mode }}" == "Debug" && "${{ matrix.os }}" != windows-* ]]; then
SUFFIX="${SUFFIX}-asan"
fi
NAME_ARGS="--platform $PLATFORM --arch $ARCH --mode ${{ matrix.llvm_mode }}"
if [[ "${{ matrix.lto }}" == "ON" ]]; then NAME_ARGS+=" --lto"; fi
if [[ "${{ matrix.llvm_mode }}" == "Debug" && "${{ matrix.os }}" != windows-* ]]; then NAME_ARGS+=" --asan"; fi

ARCHIVE="${ARCH}-${PLATFORM}-${TOOLCHAIN}-${MODE_TAG}${SUFFIX}.tar.xz"
ARCHIVE=$(python3 scripts/release-llvm.py artifact-name $NAME_ARGS)

set -eo pipefail
tar -C .llvm -cf - build-install | xz -T0 -9 -c > "${ARCHIVE}"
Comment on lines +190 to 193

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the default upload path from publishing unpruned archives

With the build-llvm dispatch defaults (skip_upload is false), the later upload job still downloads this archive and scripts/upload-llvm.py publishes it and saves the manifest used by update-clice; because this commit removed all pruning/apply steps from this workflow, that default path now records hashes for unpruned packages, or gets out of sync if the new release-llvm workflow subsequently overwrites the same assets with pruned repackages. Either gate the old upload path behind the repackaging workflow or make skip_upload the default for LLVM-only source builds.

Useful? React with 👍 / 👎.

Expand All @@ -313,134 +199,3 @@ jobs:
name: ${{ env.LLVM_INSTALL_ARCHIVE }}
path: ${{ env.LLVM_INSTALL_ARCHIVE }}
if-no-files-found: error

test-cross:
needs: build
strategy:
fail-fast: false
matrix:
include:
- os: macos-15-intel
llvm_mode: RelWithDebInfo
target_triple: x86_64-apple-darwin
- os: ubuntu-24.04-arm
llvm_mode: RelWithDebInfo
target_triple: aarch64-linux-gnu
- os: windows-11-arm
llvm_mode: RelWithDebInfo
target_triple: aarch64-pc-windows-msvc
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: ./.github/actions/setup-pixi
with:
environments: test-run

- name: Download cross-compiled clice
uses: actions/download-artifact@v4
with:
name: cross-clice-${{ matrix.target_triple }}-${{ matrix.llvm_mode }}
path: build/${{ matrix.llvm_mode }}/

- name: Make binaries executable
if: runner.os != 'Windows'
run: chmod +x build/${{ matrix.llvm_mode }}/bin/*

- name: Run tests
run: pixi run -e test-run test ${{ matrix.llvm_mode }}

upload:
needs: build
if: ${{ !cancelled() && inputs.llvm_version && !inputs.skip_upload }}
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Download all build artifacts
env:
GH_TOKEN: ${{ github.token }}
run: scripts/download-llvm.sh "${{ github.run_id }}"

- name: Upload to clice-llvm
env:
GH_TOKEN: ${{ secrets.UPLOAD_LLVM }}
TARGET_REPO: clice-io/clice-llvm
run: python3 scripts/upload-llvm.py "${{ inputs.llvm_version }}" "${TARGET_REPO}" "${{ github.run_id }}"

- name: Save manifest for update-clice job
uses: actions/upload-artifact@v4
with:
name: llvm-manifest-final
path: artifacts/llvm-manifest.json
if-no-files-found: error
compression-level: 0

update-clice:
needs: upload
if: ${{ !inputs.skip_pr }}
runs-on: ubuntu-24.04
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4

- name: Download manifest
uses: actions/download-artifact@v4
with:
name: llvm-manifest-final
path: .

- name: Update manifest and version
run: |
python3 scripts/update-llvm-version.py \
--version "${{ inputs.llvm_version }}" \
--manifest-src llvm-manifest.json \
--manifest-dest config/llvm-manifest.json \
--package-cmake cmake/package.cmake

- name: Create or update PR
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${{ inputs.llvm_version }}"
BRANCH="chore/update-llvm-${VERSION}"
RUN_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
RELEASE_URL="https://github.com/clice-io/clice-llvm/releases/tag/${VERSION}"

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b "${BRANCH}"
git add config/llvm-manifest.json cmake/package.cmake
git commit -m "chore: update LLVM to ${VERSION}"
git push --force-with-lease origin "${BRANCH}"

# Check if PR already exists for this branch
EXISTING_PR=$(gh pr list --head "${BRANCH}" --json number --jq '.[0].number // empty')

BODY="$(cat <<EOF
## Summary
- Update LLVM prebuilt binaries to version ${VERSION}
- Updated \`config/llvm-manifest.json\` with new SHA256 hashes
- Updated \`cmake/package.cmake\` version string

**Artifacts:** [clice-llvm release](${RELEASE_URL})
**Build:** [workflow run](${RUN_URL})

> Auto-generated by build-llvm workflow
EOF
)"

if [[ -n "${EXISTING_PR}" ]]; then
echo "Updating existing PR #${EXISTING_PR}"
gh pr edit "${EXISTING_PR}" --body "${BODY}"
else
gh pr create \
--title "chore: update LLVM to ${VERSION}" \
--body "${BODY}" \
--base main
fi
Loading
Loading