Merge pull request #266 from tannergooding/tannergooding-layer2-core #16
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: regenerate-native | |
| # Regenerates the native runtime packages: | |
| # * libLLVM.runtime.* (the prebuilt shared libLLVM lifted per platform) | |
| # * libLLVMSharp.runtime.* (our helper library, compiled against the matching LLVM release) | |
| # | |
| # libLLVM regenerates when the tracked LLVM version changes (the version in the | |
| # top-level CMakeLists.txt, which maps to the llvmorg-<version> release tag). libLLVMSharp | |
| # regenerates for that same reason or whenever sources/libLLVMSharp changes. Both can also be | |
| # forced via workflow_dispatch. See the "Regenerating native binaries" section in README.md. | |
| # | |
| # Unlike ClangSharp (which lifts the shared libclang shipped for every platform), the official | |
| # LLVM releases only ship a shared libLLVM on Windows (as LLVM-C.dll). Linux and macOS are | |
| # therefore lifted from the most-official prebuilt source per platform: apt.llvm.org (the LLVM | |
| # project's own Debian/Ubuntu repository) on Linux and Homebrew on macOS. This is why libLLVM | |
| # runs as a per-runtime matrix on native runners rather than on a single Windows runner. | |
| # | |
| # The jobs upload the resulting .nupkg files as build artifacts and sign them via the | |
| # separate sign-nuget leg; publishing to NuGet is manual. | |
| on: | |
| push: | |
| branches: [ main ] | |
| workflow_dispatch: | |
| inputs: | |
| libllvm: | |
| description: "Force regenerating the libLLVM runtime packages" | |
| type: boolean | |
| default: false | |
| libllvmsharp: | |
| description: "Force regenerating the libLLVMSharp runtime packages" | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: regenerate-native-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| detect: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| llvm-version: ${{ steps.detect.outputs.llvm-version }} | |
| libllvm: ${{ steps.detect.outputs.libllvm }} | |
| libllvmsharp: ${{ steps.detect.outputs.libllvmsharp }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - id: detect | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| parse_version() { sed -n 's/^project(LLVMSharp VERSION \([0-9.]*\)).*/\1/p' "$1"; } | |
| # The top-level CMakeLists.txt is the source of truth for the tracked LLVM version. | |
| version="$(parse_version CMakeLists.txt)" | |
| if [ -z "${version}" ]; then echo "could not parse LLVM version from CMakeLists.txt" >&2; exit 1; fi | |
| echo "llvm-version=${version}" >> "$GITHUB_OUTPUT" | |
| libllvm=false | |
| libllvmsharp=false | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| if [ "${{ inputs.libllvm }}" = "true" ]; then libllvm=true; fi | |
| if [ "${{ inputs.libllvmsharp }}" = "true" ]; then libllvmsharp=true; fi | |
| else | |
| before="${{ github.event.before }}" | |
| if [ -z "${before}" ] || [ "${before}" = "0000000000000000000000000000000000000000" ] || ! git cat-file -e "${before}^{commit}" 2>/dev/null; then | |
| # No usable baseline to diff against (new/recreated branch, force-push, | |
| # unfetched history); regenerate everything conservatively. | |
| libllvm=true | |
| libllvmsharp=true | |
| else | |
| # libLLVM regenerates when the tracked LLVM version changes. The libLLVM package | |
| # version tracks the full patch (e.g. 21.1.8), so compare the full version. | |
| prev_version="$(git show "${before}:CMakeLists.txt" 2>/dev/null | sed -n 's/^project(LLVMSharp VERSION \([0-9.]*\)).*/\1/p')" | |
| if [ "${version}" != "${prev_version}" ]; then | |
| libllvm=true | |
| fi | |
| # libLLVMSharp regenerates for the same reason or when its sources change. | |
| if [ "${libllvm}" = "true" ] || ! git diff --quiet "${before}" HEAD -- sources/libLLVMSharp/; then | |
| libllvmsharp=true | |
| fi | |
| fi | |
| fi | |
| echo "libllvm=${libllvm}" >> "$GITHUB_OUTPUT" | |
| echo "libllvmsharp=${libllvmsharp}" >> "$GITHUB_OUTPUT" | |
| echo "Resolved: llvm=${version} (major.minor=${major_minor}) libllvm=${libllvm} libllvmsharp=${libllvmsharp}" | |
| - name: Verify package versions match the tracked LLVM version | |
| # Guards against bumping CMakeLists.txt without updating the nuspec/runtime.json | |
| # versions. libLLVM versions must equal the LLVM version exactly; libLLVMSharp | |
| # versions must be that version plus an independent build revision (e.g. 21.1.8.1). | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version="${{ steps.detect.outputs.llvm-version }}" | |
| rc=0 | |
| nuspec_version() { sed -n 's:.*<version>\([^<]*\)</version>.*:\1:p' "$1" | head -n1; } | |
| nuspec_branch() { sed -n 's:.*<repository[^>]*branch="\([^"]*\)".*:\1:p' "$1" | head -n1; } | |
| json_versions() { grep -oE '"[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?"' "$1" | tr -d '"'; } | |
| fail() { echo "::error file=$1::$2"; rc=1; } | |
| if [ "${{ steps.detect.outputs.libllvm }}" = "true" ]; then | |
| for f in packages/libLLVM/libLLVM/libLLVM.nuspec packages/libLLVM/libLLVM.runtime.*/*.nuspec; do | |
| v="$(nuspec_version "$f")" | |
| [ "${v}" = "${version}" ] || fail "$f" "version '${v}' does not match LLVM version '${version}'" | |
| b="$(nuspec_branch "$f")" | |
| if [ -n "${b}" ] && [ "${b}" != "llvmorg-${version}" ]; then | |
| fail "$f" "repository branch '${b}' does not match 'llvmorg-${version}'" | |
| fi | |
| done | |
| for v in $(json_versions packages/libLLVM/libLLVM/runtime.json); do | |
| [ "${v}" = "${version}" ] || fail "packages/libLLVM/libLLVM/runtime.json" "mapped version '${v}' does not match LLVM version '${version}'" | |
| done | |
| fi | |
| if [ "${{ steps.detect.outputs.libllvmsharp }}" = "true" ]; then | |
| for f in packages/libLLVMSharp/libLLVMSharp/libLLVMSharp.nuspec packages/libLLVMSharp/libLLVMSharp.runtime.*/*.nuspec; do | |
| v="$(nuspec_version "$f")" | |
| case "${v}" in | |
| "${version}".*) : ;; | |
| *) fail "$f" "version '${v}' is not 'llvm-version.<revision>' (expected '${version}.<n>')" ;; | |
| esac | |
| done | |
| for v in $(json_versions packages/libLLVMSharp/libLLVMSharp/runtime.json); do | |
| case "${v}" in | |
| "${version}".*) : ;; | |
| *) fail "packages/libLLVMSharp/libLLVMSharp/runtime.json" "mapped version '${v}' is not '${version}.<revision>'" ;; | |
| esac | |
| done | |
| fi | |
| if [ "${rc}" -ne 0 ]; then | |
| echo "Update the package versions to match the tracked LLVM version (${version}) before regenerating." >&2 | |
| exit 1 | |
| fi | |
| echo "Package versions verified against LLVM ${version}" | |
| # -------- libLLVM: lift the shared libLLVM from the most-official prebuilt source -------- | |
| libllvm-build: | |
| needs: detect | |
| if: ${{ needs.detect.outputs.libllvm == 'true' }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { rid: win-x64, os: windows-latest } | |
| - { rid: win-arm64, os: windows-11-arm } | |
| - { rid: linux-x64, os: ubuntu-latest } | |
| - { rid: linux-arm64, os: ubuntu-24.04-arm } | |
| - { rid: osx-arm64, os: macos-latest } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Lift libLLVM (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: ./scripts/build.ps1 -regeneratenative -target libLLVM -rid ${{ matrix.rid }} | |
| - name: Lift libLLVM (Unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: ./scripts/build.sh --regeneratenative --target libLLVM --rid ${{ matrix.rid }} | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: libllvm-native-${{ matrix.rid }} | |
| path: artifacts/native/${{ matrix.rid }}/* | |
| if-no-files-found: error | |
| libllvm-pack: | |
| needs: [ detect, libllvm-build ] | |
| if: ${{ needs.detect.outputs.libllvm == 'true' }} | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: NuGet/setup-nuget@v2 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: libllvm-native-* | |
| path: staging | |
| - name: Pack libLLVM packages | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| for rid in win-x64 win-arm64 linux-x64 linux-arm64 osx-arm64; do | |
| cp staging/"libllvm-native-${rid}"/* "packages/libLLVM/libLLVM.runtime.${rid}/" | |
| nuget pack "packages/libLLVM/libLLVM.runtime.${rid}/libLLVM.runtime.${rid}.nuspec" -OutputDirectory out | |
| done | |
| nuget pack "packages/libLLVM/libLLVM/libLLVM.nuspec" -OutputDirectory out | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: libLLVM-packages | |
| path: out/*.nupkg | |
| if-no-files-found: error | |
| # -------- libLLVMSharp: compile our helper against that same LLVM release -------- | |
| libllvmsharp-build: | |
| needs: detect | |
| if: ${{ needs.detect.outputs.libllvmsharp == 'true' }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - { rid: win-x64, os: windows-latest } | |
| - { rid: win-arm64, os: windows-11-arm } | |
| - { rid: linux-x64, os: ubuntu-latest } | |
| - { rid: linux-arm64, os: ubuntu-24.04-arm } | |
| - { rid: osx-arm64, os: macos-latest } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build libLLVMSharp (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: ./scripts/build.ps1 -regeneratenative -target libLLVMSharp -rid ${{ matrix.rid }} | |
| - name: Build libLLVMSharp (Unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: ./scripts/build.sh --regeneratenative --target libLLVMSharp --rid ${{ matrix.rid }} | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: libllvmsharp-native-${{ matrix.rid }} | |
| path: artifacts/native/${{ matrix.rid }}/* | |
| if-no-files-found: error | |
| libllvmsharp-pack: | |
| needs: [ detect, libllvmsharp-build ] | |
| if: ${{ needs.detect.outputs.libllvmsharp == 'true' }} | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: NuGet/setup-nuget@v2 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: libllvmsharp-native-* | |
| path: staging | |
| - name: Pack libLLVMSharp packages | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p out | |
| for rid in win-x64 win-arm64 linux-x64 linux-arm64 osx-arm64; do | |
| cp staging/"libllvmsharp-native-${rid}"/* "packages/libLLVMSharp/libLLVMSharp.runtime.${rid}/" | |
| nuget pack "packages/libLLVMSharp/libLLVMSharp.runtime.${rid}/libLLVMSharp.runtime.${rid}.nuspec" -OutputDirectory out | |
| done | |
| nuget pack "packages/libLLVMSharp/libLLVMSharp/libLLVMSharp.nuspec" -OutputDirectory out | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: libLLVMSharp-packages | |
| path: out/*.nupkg | |
| if-no-files-found: error | |
| # -------- sign the native packages (separate leg, native-specific file list) -------- | |
| sign-nuget: | |
| needs: [ libllvm-pack, libllvmsharp-pack ] | |
| # Sign on pushes to main whenever the legs that actually ran succeeded. A leg that | |
| # was version-gated out is 'skipped' (not a failure), so a libLLVMSharp-only | |
| # regeneration still gets signed; a real failure or cancellation blocks signing. | |
| if: >- | |
| ${{ github.event_name == 'push' | |
| && !cancelled() | |
| && needs.libllvm-pack.result != 'failure' | |
| && needs.libllvmsharp-pack.result != 'failure' | |
| && (needs.libllvm-pack.result == 'success' || needs.libllvmsharp-pack.result == 'success') }} | |
| runs-on: windows-latest | |
| permissions: | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| merge-multiple: true | |
| path: ./artifacts/pkg | |
| pattern: "lib*-packages" | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| global-json-file: ./global.json | |
| - run: dotnet tool install --tool-path ./artifacts/tools --prerelease sign | |
| - uses: azure/login@v2 | |
| with: | |
| allow-no-subscriptions: true | |
| client-id: "${{ secrets.AZURE_CLIENT_ID }}" | |
| tenant-id: "${{ secrets.AZURE_TENANT_ID }}" | |
| - run: ./artifacts/tools/sign code azure-key-vault "**/*.nupkg" --base-directory "${{ github.workspace }}/artifacts/pkg" --file-list "${{ github.workspace }}/scripts/SignClientFileListNative.txt" --publisher-name ".NET Foundation" --description "LLVMSharp" --description-url "https://github.com/dotnet/llvmsharp" --azure-credential-type "azure-cli" --azure-key-vault-url "${{ secrets.KEY_VAULT_URL }}" --azure-key-vault-certificate "${{ secrets.KEY_VAULT_CERTIFICATE_ID }}" | |
| shell: pwsh | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: sign_nuget_native | |
| path: | | |
| ./artifacts/pkg/**/* | |
| if-no-files-found: error |