mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-17 10:24:07 +00:00
feat: support revised nightly releases (nightly-YYYY-MM-DD-revK) (#12461)
This PR adds support for manually re-releasing nightlies when a build issue or critical fix requires it. When a `workflow_dispatch` triggers the nightly release job and a `nightly-YYYY-MM-DD` tag already exists, the CI now creates `nightly-YYYY-MM-DD-rev1` (then `-rev2`, etc.) instead of silently skipping. ### Lake `ToolchainVer` - Extend `ToolchainVer.nightly` with an optional `rev : Option Nat` field - Parse `-revK` suffixes from nightly tags in `ofString` - Ordering: `nightly-YYYY-MM-DD` < `nightly-YYYY-MM-DD-rev1` < `-rev2` < `nightly-YYYY-MM-DD+1` - Round-trip: `toString (ofString s) == s` for both variants ### CI workflow - "Set Nightly" step probes existing tags on `workflow_dispatch` to find next available `-revK` - Scheduled nightlies retain existing behavior (skip if commit already tagged) - Changelog grep updated from `nightly-[-0-9]*` to `nightly-[^ ,)]*` to match `-revK` suffixes ### `lean-bisect` - Updated `NIGHTLY_PATTERN` regex, sort key, error messages, and help text ### Companion PRs - https://github.com/leanprover-community/mathlib4/pull/35220: update `nightly_bump_and_merge.yml` tag grep and `nightly_detect_failure.yml` warning message - https://github.com/leanprover-community/leanprover-community.github.io/pull/787: update `tags_and_branches.md` documentation 🤖 Prepared with Claude Code Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
23
.github/workflows/ci.yml
vendored
23
.github/workflows/ci.yml
vendored
@@ -60,10 +60,25 @@ jobs:
|
||||
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
|
||||
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
|
||||
git fetch nightly --tags
|
||||
LEAN_VERSION_STRING="nightly-$(date -u +%F)"
|
||||
# do nothing if commit already has a different tag
|
||||
if [[ "$(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo "$LEAN_VERSION_STRING")" == "$LEAN_VERSION_STRING" ]]; then
|
||||
BASE_NIGHTLY="nightly-$(date -u +%F)"
|
||||
if [[ '${{ github.event_name }}' == 'workflow_dispatch' ]]; then
|
||||
# Manual re-release: find next available revision if base nightly exists
|
||||
if git rev-parse "refs/tags/$BASE_NIGHTLY" >/dev/null 2>&1; then
|
||||
REV=1
|
||||
while git rev-parse "refs/tags/${BASE_NIGHTLY}-rev${REV}" >/dev/null 2>&1; do
|
||||
REV=$((REV + 1))
|
||||
done
|
||||
LEAN_VERSION_STRING="${BASE_NIGHTLY}-rev${REV}"
|
||||
else
|
||||
LEAN_VERSION_STRING="$BASE_NIGHTLY"
|
||||
fi
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
# Scheduled: do nothing if commit already has a different tag
|
||||
LEAN_VERSION_STRING="$BASE_NIGHTLY"
|
||||
if [[ "$(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo "$LEAN_VERSION_STRING")" == "$LEAN_VERSION_STRING" ]]; then
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -475,7 +490,7 @@ jobs:
|
||||
git tag "${{ needs.configure.outputs.nightly }}"
|
||||
git push nightly "${{ needs.configure.outputs.nightly }}"
|
||||
git push -f origin refs/tags/${{ needs.configure.outputs.nightly }}:refs/heads/nightly
|
||||
last_tag="$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[-0-9]*" | head -n 1)"
|
||||
last_tag="$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[^ ,)]*" | head -n 1)"
|
||||
echo -e "*Changes since ${last_tag}:*\n\n" > diff.md
|
||||
git show "$last_tag":RELEASES.md > old.md
|
||||
#./script/diff_changelogs.py old.md doc/changes.md >> diff.md
|
||||
|
||||
@@ -36,7 +36,7 @@ sys.path.insert(0, str(Path(__file__).parent))
|
||||
import build_artifact
|
||||
|
||||
# Constants
|
||||
NIGHTLY_PATTERN = re.compile(r'^nightly-(\d{4})-(\d{2})-(\d{2})$')
|
||||
NIGHTLY_PATTERN = re.compile(r'^nightly-(\d{4})-(\d{2})-(\d{2})(-rev(\d+))?$')
|
||||
VERSION_PATTERN = re.compile(r'^v4\.(\d+)\.(\d+)(-rc\d+)?$')
|
||||
# Accept short SHAs (7+ chars) - we'll resolve to full SHA later
|
||||
SHA_PATTERN = re.compile(r'^[0-9a-f]{7,40}$')
|
||||
@@ -158,7 +158,7 @@ def parse_identifier(s: str) -> Tuple[str, str]:
|
||||
return ('sha', full_sha)
|
||||
error(f"Invalid identifier format: '{s}'\n"
|
||||
f"Expected one of:\n"
|
||||
f" - nightly-YYYY-MM-DD (e.g., nightly-2024-06-15)\n"
|
||||
f" - nightly-YYYY-MM-DD or nightly-YYYY-MM-DD-revK (e.g., nightly-2024-06-15, nightly-2024-06-15-rev1)\n"
|
||||
f" - v4.X.Y or v4.X.Y-rcK (e.g., v4.8.0, v4.9.0-rc1)\n"
|
||||
f" - commit SHA (short or full)")
|
||||
|
||||
@@ -244,8 +244,13 @@ def fetch_nightly_tags() -> List[str]:
|
||||
if len(data) < 100:
|
||||
break
|
||||
|
||||
# Sort by date (nightly-YYYY-MM-DD format sorts lexicographically)
|
||||
tags.sort()
|
||||
# Sort by date and revision (nightly-YYYY-MM-DD-revK needs numeric comparison on rev)
|
||||
def nightly_sort_key(tag):
|
||||
m = NIGHTLY_PATTERN.match(tag)
|
||||
if m:
|
||||
return (int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(5) or 0))
|
||||
return (0, 0, 0, 0)
|
||||
tags.sort(key=nightly_sort_key)
|
||||
return tags
|
||||
|
||||
def get_commit_for_nightly(nightly: str) -> str:
|
||||
@@ -1024,6 +1029,7 @@ Range Syntax:
|
||||
Identifier Formats:
|
||||
|
||||
nightly-YYYY-MM-DD Nightly build date (e.g., nightly-2024-06-15)
|
||||
nightly-YYYY-MM-DD-revK Revised nightly (e.g., nightly-2024-06-15-rev1)
|
||||
Uses pre-built toolchains from leanprover/lean4-nightly.
|
||||
Fast: downloads via elan (~30s each).
|
||||
|
||||
@@ -1151,9 +1157,9 @@ Examples:
|
||||
# Validate --nightly-only
|
||||
if args.nightly_only:
|
||||
if from_val is not None and from_type != 'nightly':
|
||||
error("--nightly-only requires FROM to be a nightly identifier (nightly-YYYY-MM-DD)")
|
||||
error("--nightly-only requires FROM to be a nightly identifier (nightly-YYYY-MM-DD or nightly-YYYY-MM-DD-revK)")
|
||||
if to_type != 'nightly':
|
||||
error("--nightly-only requires TO to be a nightly identifier (nightly-YYYY-MM-DD)")
|
||||
error("--nightly-only requires TO to be a nightly identifier (nightly-YYYY-MM-DD or nightly-YYYY-MM-DD-revK)")
|
||||
|
||||
if from_val:
|
||||
info(f"From: {from_val} ({from_type})")
|
||||
|
||||
@@ -235,13 +235,14 @@ open ToolchainVer in
|
||||
/-- A Lean toolchain version. -/
|
||||
inductive ToolchainVer
|
||||
| release (ver : LeanVer)
|
||||
| nightly (date : Date)
|
||||
| nightly (date : Date) (rev : Option Nat := none)
|
||||
| pr (n : Nat)
|
||||
| other (v : String)
|
||||
with
|
||||
@[computed_field] toString : ToolchainVer → String
|
||||
| .release ver => s!"{defaultOrigin}:v{ver}"
|
||||
| .nightly date => s!"{defaultOrigin}:nightly-{date}"
|
||||
| .nightly date rev => s!"{defaultOrigin}:nightly-{date}" ++
|
||||
match rev with | none => "" | some r => s!"-rev{r}"
|
||||
| .pr n => s!"{prOrigin}:pr-release-{n}"
|
||||
| .other v => v
|
||||
deriving Repr, DecidableEq
|
||||
@@ -264,13 +265,20 @@ public def ofString (ver : String) : ToolchainVer := Id.run do
|
||||
if let .ok ver := StdVer.parse (tag.drop 1).copy then
|
||||
if noOrigin|| origin == defaultOrigin then
|
||||
return .release ver
|
||||
else if let some date := tag.dropPrefix? "nightly-" then
|
||||
if let some date := Date.ofString? date.toString then
|
||||
if noOrigin then
|
||||
return .nightly date
|
||||
else if let some suffix := origin.dropPrefix? defaultOrigin then
|
||||
if suffix.isEmpty || suffix == "-nightly" then
|
||||
return .nightly date
|
||||
else if let some rest := tag.dropPrefix? "nightly-" then
|
||||
let rest := rest.toString
|
||||
-- Dates are exactly 10 characters (YYYY-MM-DD); anything after must be a -revK suffix
|
||||
if let some date := Date.ofString? (rest.take 10).toString then
|
||||
let rev? : Option Nat := do
|
||||
let suffix ← (rest.drop 10).dropPrefix? "-rev"
|
||||
suffix.toString.toNat?
|
||||
-- Accept if no suffix (plain nightly) or valid -revK suffix
|
||||
if rest.length ≤ 10 || rev?.isSome then
|
||||
if noOrigin then
|
||||
return .nightly date rev?
|
||||
else if let some suffix := origin.dropPrefix? defaultOrigin then
|
||||
if suffix.isEmpty || suffix == "-nightly" then
|
||||
return .nightly date rev?
|
||||
else if let some n := tag.dropPrefix? "pr-release-" then
|
||||
if let some n := n.toNat? then
|
||||
if noOrigin || origin == prOrigin then
|
||||
@@ -301,7 +309,8 @@ public instance : FromJson ToolchainVer := ⟨(ToolchainVer.ofString <$> fromJso
|
||||
public def blt (a b : ToolchainVer) : Bool :=
|
||||
match a, b with
|
||||
| .release v1, .release v2 => v1 < v2
|
||||
| .nightly d1, .nightly d2 => d1 < d2
|
||||
| .nightly d1 r1, .nightly d2 r2 =>
|
||||
d1 < d2 || (d1 == d2 && (r1.getD 0) < (r2.getD 0))
|
||||
| _, _ => false
|
||||
|
||||
public instance : LT ToolchainVer := ⟨(·.blt ·)⟩
|
||||
@@ -311,7 +320,8 @@ public instance decLt (a b : ToolchainVer) : Decidable (a < b) :=
|
||||
public def ble (a b : ToolchainVer) : Bool :=
|
||||
match a, b with
|
||||
| .release v1, .release v2 => v1 ≤ v2
|
||||
| .nightly d1, .nightly d2 => d1 ≤ d2
|
||||
| .nightly d1 r1, .nightly d2 r2 =>
|
||||
d1 < d2 || (d1 == d2 && (r1.getD 0) ≤ (r2.getD 0))
|
||||
| .pr n1, .pr n2 => n1 = n2
|
||||
| .other v1, .other v2 => v1 = v2
|
||||
| _, _ => false
|
||||
|
||||
@@ -12,21 +12,33 @@ def test := do
|
||||
IO.println ""
|
||||
checkParse "leanprover/lean4:v4.13.0-rc1"
|
||||
checkParse "leanprover/lean4:nightly-2024-09-15"
|
||||
checkParse "leanprover/lean4:nightly-2024-09-15-rev1"
|
||||
checkParse "nightly-2024-09-15-rev2"
|
||||
checkParse "leanprover/lean4-pr-releases:pr-release-101"
|
||||
checkParse "leanprover/lean:v4.1.0"
|
||||
checkParse "4.12.0"
|
||||
checkParse "nightly-2024-09-15-foo" -- malformed suffix → .other
|
||||
checkLt "4.6.0-rc1" "v4.6.0"
|
||||
checkLt "4.12.0" "leanprover/lean4:v4.13.0-rc1"
|
||||
checkLt "nightly-2024-09-08" "nightly-2024-10-09"
|
||||
checkLt "nightly-2024-09-08" "nightly-2024-09-08-rev1"
|
||||
checkLt "nightly-2024-09-08-rev1" "nightly-2024-09-08-rev2"
|
||||
checkLt "nightly-2024-09-08-rev2" "nightly-2024-09-09"
|
||||
checkLt "nightly-2024-09-08" "4.0.0"
|
||||
|
||||
/--
|
||||
info:
|
||||
Lake.ToolchainVer.release { toSemVerCore := { major := 4, minor := 13, patch := 0 }, specialDescr := "rc1" }
|
||||
Lake.ToolchainVer.nightly { year := 2024, month := 9, day := 15 }
|
||||
Lake.ToolchainVer.nightly { year := 2024, month := 9, day := 15 } none
|
||||
Lake.ToolchainVer.nightly { year := 2024, month := 9, day := 15 } (some 1)
|
||||
Lake.ToolchainVer.nightly { year := 2024, month := 9, day := 15 } (some 2)
|
||||
Lake.ToolchainVer.pr 101
|
||||
Lake.ToolchainVer.other "leanprover/lean:v4.1.0"
|
||||
Lake.ToolchainVer.release { toSemVerCore := { major := 4, minor := 12, patch := 0 }, specialDescr := "" }
|
||||
Lake.ToolchainVer.other "nightly-2024-09-15-foo"
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
|
||||
@@ -42,6 +42,12 @@ echo nightly-2024-10-01 > a/lean-toolchain
|
||||
echo nightly-2024-01-10 > b/lean-toolchain
|
||||
test_update leanprover/lean4:nightly-2024-10-01
|
||||
|
||||
# Test nightly revision comparison
|
||||
./clean.sh
|
||||
echo nightly-2024-10-01-rev1 > a/lean-toolchain
|
||||
echo nightly-2024-01-10 > b/lean-toolchain
|
||||
test_update leanprover/lean4:nightly-2024-10-01-rev1
|
||||
|
||||
# Test up-to-date root
|
||||
./clean.sh
|
||||
echo v4.4.0 > a/lean-toolchain
|
||||
|
||||
Reference in New Issue
Block a user