From aa295c9aa759273ddd65c4890db9619c880824d1 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 7 May 2026 23:08:43 +0200 Subject: [PATCH 1/2] Switch assert CI to use pre-built Buildkite binaries (LLVM.jl pattern) Replace the in-CI Julia-from-source assert builds with GetAsserts/TestAsserts workflows that download pre-built x86_64-linux-gnuassert binaries from Buildkite, matching the approach used in LLVM.jl. Co-Authored-By: Claude Sonnet 4.6 --- .github/download_build.sh | 34 ++++++++ .github/workflows/CI.yml | 47 +---------- .github/workflows/GetAsserts.yml | 46 ++++++++++ .github/workflows/TestAsserts.yml | 136 ++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 45 deletions(-) create mode 100755 .github/download_build.sh create mode 100644 .github/workflows/GetAsserts.yml create mode 100644 .github/workflows/TestAsserts.yml diff --git a/.github/download_build.sh b/.github/download_build.sh new file mode 100755 index 0000000000..f177e31662 --- /dev/null +++ b/.github/download_build.sh @@ -0,0 +1,34 @@ +#!/bin/bash -e + +# handle user inputs +[ $# -ne 3 ] && { echo "Usage: $0 " >&2; exit 1; } +VERSION="$1" +BUILD_NAME="$2" +DEST_FILE="$3" +[ -z "$BUILDKITE_TOKEN" ] && { echo "BUILDKITE_TOKEN not set." >&2; exit 1; } + +API_BASE="https://api.buildkite.com/v2" +ORG="julialang" + +# derive pipeline and branch filter from version +if [ "$VERSION" = "master" ]; then + PIPELINE="julia-master" + BRANCH_FILTER='.branch == "master"' +else + PIPELINE="julia-release-${VERSION//./-dot-}" + BRANCH_FILTER="(.branch == \"release-$VERSION\") or (.branch | startswith(\"v$VERSION\"))" +fi + +# find the first successful job and get its artifacts url +ARTIFACTS_URL=$(curl -s -H "Authorization: Bearer $BUILDKITE_TOKEN" \ + "$API_BASE/organizations/$ORG/pipelines/$PIPELINE/builds?per_page=100" | \ + jq -r "first(.[] | select($BRANCH_FILTER) | .jobs[] | select(.step_key == \"$BUILD_NAME\" and .exit_status == 0) | .artifacts_url)") +[ -z "$ARTIFACTS_URL" ] || [ "$ARTIFACTS_URL" = "null" ] && { echo "No successful build found."; exit 1; } + +# fetch the url of the first artifact +ARTIFACT_URL=$(curl -s -H "Authorization: Bearer $BUILDKITE_TOKEN" "$ARTIFACTS_URL" | \ + jq -r '.[0].download_url') +[ -z "$ARTIFACT_URL" ] || [ "$ARTIFACT_URL" = "null" ] && { echo "No artifact found."; exit 1; } + +curl -s -L -H "Authorization: Bearer $BUILDKITE_TOKEN" -o "$DEST_FILE" "$ARTIFACT_URL" +echo "Artifact downloaded as $DEST_FILE" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 946f9e172d..3799f76339 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -34,7 +34,7 @@ concurrency: jobs: test: timeout-minutes: 120 - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ matrix.libEnzyme }} libEnzyme - assertions=${{ matrix.assertions }} llvm_args=${{ matrix.llvm_args }} - ${{ github.event_name }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ matrix.libEnzyme }} libEnzyme - llvm_args=${{ matrix.llvm_args }} - ${{ github.event_name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -52,8 +52,6 @@ jobs: - '' arch: - default - assertions: - - false libEnzyme: [local, packaged] exclude: - os: windows-latest @@ -64,58 +62,17 @@ jobs: arch: x86 libEnzyme: packaged version: '1.10' - assertions: false - - os: ubuntu-24.04 - arch: default - libEnzyme: packaged - version: '1.10' - assertions: true - - os: ubuntu-24.04 - arch: default - libEnzyme: packaged - version: '1.11' - assertions: true - - os: ubuntu-24.04 - arch: default - libEnzyme: packaged - version: '1.12' - assertions: true - - os: ubuntu-24.04 - arch: default - libEnzyme: packaged - version: '1.13' - assertions: true - - os: ubuntu-24.04 - arch: default - libEnzyme: packaged - version: '1.10' - assertions: true - llvm_args: '--opaque-pointers' - os: ubuntu-24.04 arch: default libEnzyme: packaged version: '1.11' - assertions: false llvm_args: '--opaque-pointers' steps: - uses: actions/checkout@v6 - uses: julia-actions/setup-julia@v3 - if: ${{ ! matrix.assertions }} with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - uses: actions/checkout@v6 - if: ${{ matrix.assertions }} - with: - repository: 'JuliaLang/julia' - ref: release-${{ matrix.version }} - path: 'julia' - - name: Compile Julia - if: ${{ matrix.assertions }} - run: | - sed -i.bak 's/exit 2/exit 0/g' julia/deps/tools/jlchecksum - make -C julia -j $(nproc) FORCE_ASSERTIONS=1 LLVM_ASSERTIONS=1 JULIA_PRECOMPILE=0 - echo $PWD/julia/usr/bin >> $GITHUB_PATH - uses: julia-actions/cache@v3 - name: add EnzymeCore EnzymeTestUtils shell: julia --color=yes --project=. {0} @@ -164,7 +121,7 @@ jobs: id: run_tests # TODO restore coverage post https://github.com/JuliaGPU/GPUCompiler.jl/pull/711 with: - coverage: ${{ matrix.version != '1.11' || !matrix.assertions }} + coverage: true test_args: ${{ env.runtest_test_args }} env: JULIA_PKG_SERVER_REGISTRY_PREFERENCE: eager diff --git a/.github/workflows/GetAsserts.yml b/.github/workflows/GetAsserts.yml new file mode 100644 index 0000000000..a673ffcd06 --- /dev/null +++ b/.github/workflows/GetAsserts.yml @@ -0,0 +1,46 @@ +name: GetAsserts + +# This workflow downloads Julia assert builds using the BUILDKITE_TOKEN secret. +# It never checks out PR code, so the secret cannot be exfiltrated. +# The builds are passed as artifacts to the TestAsserts workflow. + +on: + push: + branches: [main, master] + tags: ["*"] + pull_request_target: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + download: + name: Download Julia ${{ matrix.version }} (${{ matrix.build }}) + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + version: ['master', '1.11', '1.12', '1.13'] + build: ['x86_64-linux-gnuassert'] + steps: + # Only checks out the base branch (never PR code) + - uses: actions/checkout@v6 + + - name: Download Julia + env: + BUILDKITE_TOKEN: ${{ secrets.BUILDKITE_TOKEN }} + run: | + ./.github/download_build.sh ${{ matrix.version }} build_${{ matrix.build }} julia.tar.gz + + - name: Upload Julia build + uses: actions/upload-artifact@v7 + with: + name: julia-${{ matrix.version }}-${{ matrix.build }} + path: julia.tar.gz + retention-days: 1 diff --git a/.github/workflows/TestAsserts.yml b/.github/workflows/TestAsserts.yml new file mode 100644 index 0000000000..fe5de59ea0 --- /dev/null +++ b/.github/workflows/TestAsserts.yml @@ -0,0 +1,136 @@ +name: TestAsserts + +# This workflow runs tests using assert builds downloaded by GetAsserts. +# It does not use any secrets (BUILDKITE_TOKEN access is confined to GetAsserts). + +on: + workflow_run: + workflows: ["GetAsserts"] + types: + - completed + +permissions: + contents: read + # needed to report check runs since workflow_run can't report back to PRs natively + checks: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }} + cancel-in-progress: true + +jobs: + test: + name: Julia ${{ matrix.version }} ${{ matrix.llvm_args }} (assert) - Linux + runs-on: ubuntu-latest + timeout-minutes: 120 + if: github.event.workflow_run.conclusion == 'success' + strategy: + fail-fast: false + matrix: + include: + - version: 'master' + build: 'x86_64-linux-gnuassert' + llvm_args: '' + - version: '1.11' + build: 'x86_64-linux-gnuassert' + llvm_args: '--opaque-pointers' + - version: '1.11' + build: 'x86_64-linux-gnuassert' + llvm_args: '' + - version: '1.12' + build: 'x86_64-linux-gnuassert' + llvm_args: '' + - version: '1.13' + build: 'x86_64-linux-gnuassert' + llvm_args: '' + steps: + - name: Report in-progress + uses: actions/github-script@v9 + with: + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + head_sha: '${{ github.event.workflow_run.head_sha }}', + name: 'Julia ${{ matrix.version }} ${{ matrix.llvm_args }} (assert) - Linux', + status: 'in_progress', + details_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + }); + + - name: Download Julia build + uses: actions/download-artifact@v8 + with: + name: julia-${{ matrix.version }}-${{ matrix.build }} + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ github.token }} + + - name: Install Julia + run: | + tar -xf julia.tar.gz -C ../ + rm julia.tar.gz + echo $PWD/../julia-*/bin >> $GITHUB_PATH + + - uses: actions/checkout@v6 + with: + ref: ${{ github.event.workflow_run.head_sha }} + fetch-depth: 0 + + - uses: julia-actions/cache@v3 + - uses: julia-actions/julia-buildpkg@v1 + + - name: Run tests + run: julia --project -e 'using Pkg; Pkg.test(; coverage=true)' + env: + JULIA_LLVM_ARGS: ${{ matrix.llvm_args }} + + - name: Report check run + if: always() + uses: actions/github-script@v9 + with: + script: | + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + head_sha: '${{ github.event.workflow_run.head_sha }}', + name: 'Julia ${{ matrix.version }} ${{ matrix.llvm_args }} (assert) - Linux', + status: 'completed', + conclusion: '${{ job.status }}', + details_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + output: { + title: 'Assert build test ${{ job.status }}', + summary: 'Tested against `${{ matrix.build }}` build of Julia ${{ matrix.version }}.' + } + }); + + report-failure: + name: Report download failure + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'failure' + steps: + - name: Report check runs + uses: actions/github-script@v9 + with: + script: | + // Keep in sync with the matrix above + const entries = [ + {version: 'master', llvm_args: ''}, + {version: '1.11', llvm_args: '--opaque-pointers'}, + {version: '1.11', llvm_args: ''}, + {version: '1.12', llvm_args: ''}, + {version: '1.13', llvm_args: ''}, + ]; + await Promise.all(entries.map(({version, llvm_args}) => + github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + head_sha: '${{ github.event.workflow_run.head_sha }}', + name: `Julia ${version} ${llvm_args} (assert) - Linux`, + status: 'completed', + conclusion: 'failure', + details_url: `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${{ github.event.workflow_run.id }}`, + output: { + title: 'Assert build download failed', + summary: 'The GetAsserts workflow failed to download the Julia build.' + } + }) + )); From c2ebeddd0f18ea9c459338eaab55a9342a438ecc Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Thu, 7 May 2026 23:11:32 +0200 Subject: [PATCH 2/2] Fix shell bugs and reduce Buildkite API payload in download_build.sh - Replace `||/&&` chains with `if` to fix shell operator precedence bug that silently skipped the null-check on ARTIFACTS_URL/ARTIFACT_URL - Add `&branch=` query param and drop per_page from 100 to 10 to filter server-side instead of streaming the full build history through jq - Quote matrix interpolations in GetAsserts.yml run step - Remove what-not-why comments Co-Authored-By: Claude Sonnet 4.6 --- .github/download_build.sh | 18 +++++++++++------- .github/workflows/GetAsserts.yml | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/download_build.sh b/.github/download_build.sh index f177e31662..4a2f53fae1 100755 --- a/.github/download_build.sh +++ b/.github/download_build.sh @@ -1,6 +1,5 @@ #!/bin/bash -e -# handle user inputs [ $# -ne 3 ] && { echo "Usage: $0 " >&2; exit 1; } VERSION="$1" BUILD_NAME="$2" @@ -10,25 +9,30 @@ DEST_FILE="$3" API_BASE="https://api.buildkite.com/v2" ORG="julialang" -# derive pipeline and branch filter from version if [ "$VERSION" = "master" ]; then PIPELINE="julia-master" + BRANCH="master" BRANCH_FILTER='.branch == "master"' else PIPELINE="julia-release-${VERSION//./-dot-}" + BRANCH="release-$VERSION" BRANCH_FILTER="(.branch == \"release-$VERSION\") or (.branch | startswith(\"v$VERSION\"))" fi -# find the first successful job and get its artifacts url ARTIFACTS_URL=$(curl -s -H "Authorization: Bearer $BUILDKITE_TOKEN" \ - "$API_BASE/organizations/$ORG/pipelines/$PIPELINE/builds?per_page=100" | \ + "$API_BASE/organizations/$ORG/pipelines/$PIPELINE/builds?per_page=10&branch=$BRANCH" | \ jq -r "first(.[] | select($BRANCH_FILTER) | .jobs[] | select(.step_key == \"$BUILD_NAME\" and .exit_status == 0) | .artifacts_url)") -[ -z "$ARTIFACTS_URL" ] || [ "$ARTIFACTS_URL" = "null" ] && { echo "No successful build found."; exit 1; } +if [ -z "$ARTIFACTS_URL" ] || [ "$ARTIFACTS_URL" = "null" ]; then + echo "No successful build found." + exit 1 +fi -# fetch the url of the first artifact ARTIFACT_URL=$(curl -s -H "Authorization: Bearer $BUILDKITE_TOKEN" "$ARTIFACTS_URL" | \ jq -r '.[0].download_url') -[ -z "$ARTIFACT_URL" ] || [ "$ARTIFACT_URL" = "null" ] && { echo "No artifact found."; exit 1; } +if [ -z "$ARTIFACT_URL" ] || [ "$ARTIFACT_URL" = "null" ]; then + echo "No artifact found." + exit 1 +fi curl -s -L -H "Authorization: Bearer $BUILDKITE_TOKEN" -o "$DEST_FILE" "$ARTIFACT_URL" echo "Artifact downloaded as $DEST_FILE" diff --git a/.github/workflows/GetAsserts.yml b/.github/workflows/GetAsserts.yml index a673ffcd06..a6eae2fd41 100644 --- a/.github/workflows/GetAsserts.yml +++ b/.github/workflows/GetAsserts.yml @@ -36,7 +36,7 @@ jobs: env: BUILDKITE_TOKEN: ${{ secrets.BUILDKITE_TOKEN }} run: | - ./.github/download_build.sh ${{ matrix.version }} build_${{ matrix.build }} julia.tar.gz + ./.github/download_build.sh "${{ matrix.version }}" "build_${{ matrix.build }}" julia.tar.gz - name: Upload Julia build uses: actions/upload-artifact@v7