3838 lint-outcome : ${{ steps.lint.outcome }}
3939 mk_all-outcome : ${{ steps.mk_all.outcome }}
4040 noisy-outcome : ${{ steps.noisy.outcome }}
41- shake-outcome : ${{ steps.shake.outcome }}
41+ # shake-outcome: ${{ steps.shake.outcome }}
4242 test-outcome : ${{ steps.test.outcome }}
4343 defaults : # On Hoskinson runners, landrun is already installed.
4444 run : # note that .pr-branch/.lake must be created in a step below before we use this
7878
7979 # Checkout the master branch into a subdirectory
8080 - name : Checkout master branch
81- uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 .0.0
81+ uses : actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 .0.1
8282 with :
8383 # Recall that on the `leanprover-community/mathlib4-nightly-testing` repository,
8484 # we don't maintain a `master` branch at all.
8888
8989 # Checkout the PR branch into a subdirectory
9090 - name : Checkout PR branch
91- uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 .0.0
91+ uses : actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 .0.1
9292 with :
9393 ref : " ${{ PR_BRANCH_REF }}"
9494 path : pr-branch
@@ -223,7 +223,7 @@ jobs:
223223
224224 # Extract all inputRevs from the manifest
225225 invalid_revs=$(jq -r '.packages[].inputRev // empty' lake-manifest.json | \
226- grep -v -E '^(main|master|v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$' || true)
226+ grep -v -E '^(main|master|v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)? )$' || true)
227227
228228 if [ -n "$invalid_revs" ]; then
229229 echo "❌ Error: Found invalid inputRevs in lake-manifest.json:"
@@ -232,12 +232,57 @@ jobs:
232232 echo "All inputRevs must be one of:"
233233 echo " - 'main'"
234234 echo " - 'master'"
235- echo " - 'vX.Y.Z' (semantic version, e.g., v1.2.3 or v1.2.3-pre)"
235+ echo " - 'vX.Y.Z' (semantic version, e.g., v1.2.3, v1.2.3-pre, or v1.2.3+build )"
236236 exit 1
237237 else
238238 echo "✅ All inputRevs in lake-manifest.json are valid"
239239 fi
240240
241+ - name : verify ProofWidgets lean-toolchain matches on versioned releases
242+ # Only enforce this on the main mathlib4 repository, not on nightly-testing
243+ if : github.repository == 'leanprover-community/mathlib4'
244+ shell : bash
245+ run : |
246+ cd pr-branch
247+
248+ # Read the lean-toolchain file
249+ TOOLCHAIN=$(cat lean-toolchain | tr -d '[:space:]')
250+ echo "Lean toolchain: $TOOLCHAIN"
251+
252+ # Check if toolchain matches the versioned release pattern: leanprover/lean4:vX.Y.Z (with optional suffix like -rc1)
253+ if [[ "$TOOLCHAIN" =~ ^leanprover/lean4:v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
254+ echo "✓ Detected versioned Lean release: $TOOLCHAIN"
255+ echo "Verifying ProofWidgets lean-toolchain matches..."
256+
257+ # Check if ProofWidgets lean-toolchain exists
258+ if [ ! -f .lake/packages/proofwidgets/lean-toolchain ]; then
259+ echo "❌ Error: .lake/packages/proofwidgets/lean-toolchain does not exist"
260+ echo "This file should be created by 'lake env' during dependency download."
261+ exit 1
262+ fi
263+
264+ # Read ProofWidgets lean-toolchain
265+ PROOFWIDGETS_TOOLCHAIN=$(cat .lake/packages/proofwidgets/lean-toolchain | tr -d '[:space:]')
266+ echo "ProofWidgets toolchain: $PROOFWIDGETS_TOOLCHAIN"
267+
268+ # Compare the two
269+ if [ "$TOOLCHAIN" != "$PROOFWIDGETS_TOOLCHAIN" ]; then
270+ echo "❌ Error: Lean toolchain mismatch!"
271+ echo " Main lean-toolchain: $TOOLCHAIN"
272+ echo " ProofWidgets lean-toolchain: $PROOFWIDGETS_TOOLCHAIN"
273+ echo ""
274+ echo "When using a versioned Lean release (leanprover/lean4:vX.Y.Z),"
275+ echo "the ProofWidgets dependency must use the same toolchain."
276+ echo "Please update the ProofWidgets dependency to use $TOOLCHAIN"
277+ exit 1
278+ else
279+ echo "✅ ProofWidgets lean-toolchain matches: $TOOLCHAIN"
280+ fi
281+ else
282+ echo "ℹ Lean toolchain is not a versioned release (pattern: leanprover/lean4:vX.Y.Z)"
283+ echo "Skipping ProofWidgets toolchain verification."
284+ fi
285+
241286 - name : get cache (1/3 - setup and initial fetch)
242287 id : get_cache_part1_setup
243288 shell : bash # only runs `cache get` from `master-branch`, so doesn't need to be inside landrun
@@ -320,7 +365,7 @@ jobs:
320365
321366 - name : upload artifact containing contents of pr-branch
322367 # temporary measure for debugging no-build failures
323- uses : actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 .0.0
368+ uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 .0.0
324369 with :
325370 name : mathlib4_artifact
326371 include-hidden-files : true
@@ -350,7 +395,7 @@ jobs:
350395 # do not try to upload files just downloaded
351396
352397 echo "Uploading cache to Azure..."
353- USE_FRO_CACHE =0 ../master-branch/.lake/build/bin/cache --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }} put-unpacked
398+ MATHLIB_CACHE_USE_CLOUDFLARE =0 ../master-branch/.lake/build/bin/cache --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }} put-unpacked
354399 env :
355400 MATHLIB_CACHE_SAS_RAW : ${{ secrets.MATHLIB_CACHE_SAS }}
356401
@@ -406,13 +451,13 @@ jobs:
406451 export MATHLIB_CACHE_SAS="${MATHLIB_CACHE_SAS_RAW%"${MATHLIB_CACHE_SAS_RAW##*[![:space:]]}"}"
407452
408453 echo "Uploading Archive and Counterexamples cache to Azure..."
409- USE_FRO_CACHE =0 ../master-branch/.lake/build/bin/cache --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }} put-unpacked Archive.lean
410- USE_FRO_CACHE =0 ../master-branch/.lake/build/bin/cache --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }} put-unpacked Counterexamples.lean
454+ MATHLIB_CACHE_USE_CLOUDFLARE =0 ../master-branch/.lake/build/bin/cache --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }} put-unpacked Archive.lean
455+ MATHLIB_CACHE_USE_CLOUDFLARE =0 ../master-branch/.lake/build/bin/cache --repo=${{ github.event.pull_request.head.repo.full_name || github.repository }} put-unpacked Counterexamples.lean
411456 env :
412457 MATHLIB_CACHE_SAS_RAW : ${{ secrets.MATHLIB_CACHE_SAS }}
413458
414459 - name : Check {Mathlib, Tactic, Counterexamples, Archive}.lean
415- if : always()
460+ if : ${{ always() && steps.mk_all.outcome != 'skipped' }}
416461 run : |
417462 if [[ "${{ steps.mk_all.outcome }}" != "success" ]]; then
418463 echo "Please run 'lake exe mk_all' to regenerate the import all files"
@@ -443,18 +488,72 @@ jobs:
443488 action : add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after.
444489 linters : gcc
445490
446- - name : check for unused imports
447- id : shake
491+ # With the arrival of the module system, the old `shake` is no longer functional.
492+ # This will be replaced soon.
493+ # - name: check for unused imports
494+ # id: shake
495+ # run: |
496+ # cd pr-branch
497+ # env LEAN_ABORT_ON_PANIC=1 lake exe shake --gh-style
498+
499+ - name : kill stray runLinter processes
500+ if : ${{ always() && steps.build.outcome == 'success' || steps.build.outcome == 'failure' }}
501+ continue-on-error : true
502+ shell : bash
448503 run : |
449- cd pr-branch
450- env LEAN_ABORT_ON_PANIC=1 lake exe shake --gh-style
504+ echo "Checking for runLinter processes..."
505+ if ps -eo pid,lstart,command | grep -F runLinter | grep -v grep; then
506+ echo "Killing runLinter processes..."
507+ pkill -f runLinter || true
508+ else
509+ echo "No stray runLinter processes found."
510+ fi
451511
452512 - name : lint mathlib
453513 if : ${{ always() && steps.build.outcome == 'success' || steps.build.outcome == 'failure' }}
454514 id : lint
515+ timeout-minutes : 40
455516 run : |
456517 cd pr-branch
457- env LEAN_ABORT_ON_PANIC=1 ../master-branch/scripts/lake-build-wrapper.py .lake/build_summary_lint.json lake exe runLinter Mathlib
518+ set -o pipefail
519+ # Try running with --trace; if it fails due to argument parsing, the PR needs to merge master
520+ # We use .lake/ for the output file because landrun restricts /tmp access
521+ for attempt in 1 2; do
522+ if timeout 15m env LEAN_ABORT_ON_PANIC=1 stdbuf -oL lake exe runLinter --trace Mathlib 2>&1 | tee ".lake/lint_output_attempt_${attempt}.txt"; then
523+ break
524+ fi
525+ status=${PIPESTATUS[0]}
526+ if grep -q "cannot parse arguments" ".lake/lint_output_attempt_${attempt}.txt"; then
527+ echo ""
528+ echo "=============================================================================="
529+ echo "ERROR: Your branch uses an older version of runLinter that doesn't support"
530+ echo "the --trace flag. Please merge 'master' into your PR branch to get the"
531+ echo "updated linter, then push again."
532+ echo ""
533+ echo "You can do this with:"
534+ echo " git fetch upstream"
535+ echo " git merge upstream/master"
536+ echo " git push"
537+ echo "=============================================================================="
538+ echo ""
539+ exit 1
540+ fi
541+ if [ "$status" -eq 124 ]; then
542+ echo "runLinter timed out (attempt $attempt)."
543+ if [ "$attempt" -lt 2 ]; then
544+ echo "Checking for runLinter processes..."
545+ if ps -eo pid,lstart,command | grep -F runLinter | grep -v grep; then
546+ echo "Killing runLinter processes..."
547+ pkill -f runLinter || true
548+ else
549+ echo "No stray runLinter processes found."
550+ fi
551+ echo "Retrying runLinter after timeout..."
552+ continue
553+ fi
554+ fi
555+ exit $status
556+ done
458557
459558 - name : end gh-problem-match-wrap for shake and lint steps
460559 uses : leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23
@@ -480,6 +579,15 @@ jobs:
480579 exit 1
481580 fi
482581
582+ - name : list stray runLinter processes
583+ shell : bash
584+ if : always()
585+ run : |
586+ echo "Checking for runLinter processes..."
587+ if ! ps -eo pid,lstart,command | grep -F runLinter | grep -v grep; then
588+ echo "No stray runLinter processes found."
589+ fi
590+
483591 - name : Post comments for lean-pr-testing-NNNN and batteries-pr-testing-NNNN branches
484592 if : always()
485593 shell : bash
@@ -505,7 +613,7 @@ jobs:
505613 runs-on : ubuntu-latest # Note these steps run on disposable GitHub runners, so no landrun sandboxing is needed.
506614 steps :
507615
508- - uses : actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 .0.0
616+ - uses : actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 .0.1
509617 with :
510618 ref : " ${{ PR_BRANCH_REF }}"
511619
@@ -553,7 +661,7 @@ jobs:
553661 lake exe graph
554662
555663 - name : upload the import graph
556- uses : actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 .0.0
664+ uses : actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 .0.0
557665 with :
558666 name : import-graph
559667 path : import_graph.dot
@@ -577,34 +685,7 @@ jobs:
577685 lake build AesopTest
578686
579687 # We no longer run `lean4checker` in regular CI, as it is quite expensive for little benefit.
580- # Instead we run it in a cron job on master: see `lean4checker.yml`.
581- # Output is posted to the zulip topic
582- # https://leanprover.zulipchat.com/#narrow/stream/345428-mathlib-reviewers/topic/lean4checker
583-
584- - name : checkout lean4checker
585- if : ${{ (always() && needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') && contains(github.event.pull_request.changed_files, 'lean-toolchain') }}
586- shell : bash
587- run : |
588- git clone https://github.com/leanprover/lean4checker
589- cd lean4checker
590- # Read lean-toolchain file and checkout appropriate branch
591- TOOLCHAIN=$(cat ../lean-toolchain)
592- if [[ "$TOOLCHAIN" =~ ^leanprover/lean4:v ]]; then
593- VERSION=${TOOLCHAIN#leanprover/lean4:}
594- git checkout "$VERSION"
595- else
596- git checkout master
597- fi
598-
599- - name : run leanchecker on itself
600- if : ${{ (always() && needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') && contains(github.event.pull_request.changed_files, 'lean-toolchain') }}
601- run : |
602- cd lean4checker
603- # Build lean4checker using the same toolchain
604- cp ../lean-toolchain .
605- lake build
606- lake -q exe lean4checker Lean4Checker
607-
688+ # Instead we run it in a cron job on master: see `daily.yml`.
608689 style_lint :
609690 name : Lint styleJOB_NAME
610691 if : FORK_CONDITION
@@ -641,15 +722,6 @@ jobs:
641722 echo "number=${{ steps.PR_from_push.outputs.number || github.event.pull_request.number }}" | tee -a "$GITHUB_OUTPUT"
642723 echo "pr_labels=${{ steps.PR_from_push.outputs.pr_labels || join(github.event.pull_request.labels.*.name, ',') }}" | tee -a "$GITHUB_OUTPUT"
643724
644- - if : contains(steps.PR.outputs.pr_labels, 'bench-after-CI')
645- name : If `bench-after-CI` is present, add a `!bench` comment.
646- uses : GrantBirki/comment@608e41b19bc973020ec0e189ebfdae935d7fe0cc # v2.1.1
647- with :
648- token : ${{ secrets.AUTO_MERGE_TOKEN }}
649- issue-number : ${{ steps.PR.outputs.number }}
650- body : |
651- !bench
652-
653725 - id : remove_labels
654726 name : Remove "awaiting-CI"
655727 # we use curl rather than octokit/request-action so that the job won't fail
@@ -666,7 +738,7 @@ jobs:
666738 # unfortunately we cannot query only for 'auto-merge-after-CI' events
667739 # so we have to process this with jq in the next step
668740 id : get-timeline
669- uses : octokit/graphql-action@8ad880e4d437783ea2ab17010324de1075228110 # v2.3 .2
741+ uses : octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0 .2
670742 with :
671743 query : |
672744 query($owner: String!, $name: String!, $number: Int!) {
0 commit comments