Repository navigation
Publish a rolling nightly prerelease from main - #824
Conversation
Downstream projects can currently only test against tagged releases. The
install script takes --version and resolves it to releases/download/v<x>/..., so
an unreleased commit cannot be installed at all, and there are no prereleases.
Anyone wanting to know whether jolt's main still builds their project has to
build jolt from source against Chez on every platform they care about, which is
enough work that in practice nobody does it, and a breaking change is found
after the release rather than before it.
So: a daily scheduled build of main, published as a rolling `vnightly`
prerelease.
The tag name is chosen to need no installer change. install does
`tag="v${version#v}"` and `filename="jolt-${tag}-${target}.tar.gz"`, so tag
`vnightly` means `--version nightly` already resolves to
`jolt-vnightly-<target>.tar.gz`:
curl -sSL .../install | bash -s -- --version nightly
The tagged-release path is untouched. `publish` and `homebrew` were already
guarded to refs/tags, so a scheduled run never reaches either, and the only
change to an existing line is Package taking its name from a new JOLT_TAG,
which is github.ref_name for a tag push exactly as before.
Nightlies go through the same gates as a release: they upload as a DRAFT from
the build matrix, and publish-nightly un-drafts only after libraries,
libraries-db, examples and bench all pass. A nightly that breaks them stays
invisible and the previous one remains newest.
Two details worth stating:
- --latest=false when publishing, so a prerelease never displaces the newest
real release. A bare `install` and the Homebrew tap both read the latest
PUBLISHED release, and neither should ever pick up a nightly.
- the vnightly tag is moved with an explicit git-ref PATCH.
softprops/action-gh-release creates the tag at the current sha when it does
not exist but leaves it alone when it does, so without this the tag drifts
behind the assets attached to it.
JOLT_VERSION is deliberately left as-is, which means it stays empty on a
scheduled run and the binary self-reports its git-describe version
(v0.8.0-N-g<sha>) rather than the string "nightly". That is more useful in a
bug report than a label that cannot identify the commit.
Verified: the workflow parses as YAML and the new run body passes bash -n.
Not verified end to end, because that needs a scheduled run on the real repo.
Two gaps in the commit below, both of which made the feature impossible to try
before merging it.
The nightly path was reachable only from the schedule, so the single way to
exercise it was to merge and wait for the cron. workflow_dispatch now takes a
`nightly` boolean, and one JOLT_NIGHTLY expression feeds every guard so they
cannot drift apart.
The install script hardcoded jolt-lang/jolt, so a nightly built on a fork was
unreachable by the very command the release notes tell people to run: --version
nightly looks in the upstream repo, where a fork's rolling release does not
exist. It now honours JOLT_REPO and a --repo flag, defaulting exactly as before.
That matters beyond forks. It is what lets anyone test a candidate build of jolt
without it first being published upstream, which is the same reason this
nightly exists.
JOLT_REPO=someone/jolt install --version nightly
install --repo someone/jolt --version nightly
Verified against a real release, all three paths installing v0.8.0: the env
override, the flag, and the untouched default.
|
Exercised this via The nightly caught three first-party libraries broken on current
|
|
Correction to my comment above. I wrote that The library failures are real, and the cause is one step over.
So they resolve a
The fix is a pin bump in each consumer rather than anything in What this does not change, and arguably sharpens. The library gates still caught a real break that a push to It also makes the case for running the library matrix daily rather than only at tag time, independently of whether the nightly artifact itself is wanted. And a correction to my own claim about what I verified. My local reproduction of |
The nightly step in the build matrix uploaded to the vnightly release per platform row. softprops looks that release up by tag, so from the second night on it finds yesterday's PUBLISHED nightly and swaps its assets out one platform at a time, an hour before the gates run; a red gate left the broken build live. Now the rows hand their assets to publish-nightly as artifacts and that job is the only writer: it stages a draft with every asset, deletes the old release, publishes under the tag and moves the tag. A red gate leaves the previous nightly untouched, and the uninstallable window is one API call. A meta job decides what a run is (release, nightly, dry-run) and the version the binary bakes. Job-level if: can't read env, so the guards were going to be spelled out per job; and the version needs tags plus history that the build rows' shallow checkout doesn't have, so a nightly reported a bare short sha, which :jolt/min-version reads as a version when it starts with a digit (a sha starting with 0 fails every floor). An unchanged main is not rebuilt. tools/version.sh is now the one place git describe runs (bin/jolt, build-jolt.ss, the workflow), against release tags only: vnightly is the nearest tag from main, so every clone that fetched it would have reported "vnightly". Gated by make versionsmoke. Also: the push trigger is v[0-9]* so the rolling tag can't enter the release path, a nightly only publishes from main, and the README had the install directory wrong (~/.local/bin for non-root, not /usr/local/bin). Claude-Session: https://claude.ai/code/session_019tGgRzPx3vtr5siPkKjjLE
|
Merged with a fix commit on top (46eadbb); its message has the details. Short version of what the review found: softprops/action-gh-release v2 looks the release up by tag, so from the second night on the matrix rows would have edited the live published nightly in place before the gates ran; the build rows' shallow checkout has no tags, so the nightly's binary would have reported a bare short sha, which :jolt/min-version reads as a version when it starts with a digit; and a vnightly tag at main's head is the nearest tag from main, so every clone that fetched it would have reported jolt --version as vnightly. The nightly is now published only by publish-nightly after the gates (stage draft, delete old, publish, move tag), a meta job decides the run kind and version once, and tools/version.sh is the single git describe. Thanks for the PR and the write-up, both made the review straightforward. |
Both files asserted that jolt ships no prereleases or nightlies, and that testing main would mean building it from source against Chez on two operating systems. That was true when written on 2026-09-02 at 08:35Z. jolt-lang/jolt#824 merged the same day and the first vnightly published at 19:45Z, about eleven hours later. v0.8.1's announcement confirms it: "main getting built every night as a rolling prerelease, so you can test unreleased jolt without building it yourself." The old text even predicted its own fix, saying a rolling dev release would make this a one-line change. It is smaller than that: every project already takes a jolt-version input, so `-f jolt-version=nightly` is the whole feature. Run 33691047983 did it, all five green, each log showing "Successfully installed jolt vnightly". Nightly is still NOT in the matrix, and the new text says why, because the reason is not cost. jolt publishes the nightly by staging a draft, deleting the old release, then moving the tag, and only after its own downstream-library gates pass. A lagging library withholds the new artifact while the previous vnightly stays up and installs fine. A green nightly row could not tell "main is healthy" from "main broke on Tuesday and we are still testing Tuesday's binary". That needs a published_at freshness check first. A release row has none of this problem, since a published release is immutable.
Publish a rolling nightly prerelease from main
A rolling prerelease built from
maindaily, so a downstream project can test against unreleased jolt without building it from source.Why
A break lands on
mainand nobody downstream sees it until the next tag. jolt moved 45 commits in about 30 hours betweenv0.8.0and this branch's base — that is a long window in which a regression is only visible to whoever builds from source.Two recent breaks make the case concretely.
ffi/write's argument order moved in #802, and the two spellings are indistinguishable at runtime because an offset and a value are both integers, so an unmigrated call writes to the wrong place and reports nothing. And #813's provider-declaration change means a library that has not declared:jolt/providesfails to resolve its classes — where a consuming project's namespace is dropped from a test suite that then exits 0. Both are the kind of thing a downstream suite catches immediately and a release cadence surfaces late.What it does
scheduleat 17:00 UTC buildsmainand publishes it as a draft prerelease at the rolling tagvnightly, replaced daily.The tag name is not arbitrary. jolt's own install script does
tag="v${version#v}"andfilename="jolt-${tag}-${target}.tar.gz", sovnightlymakes--version nightlyresolve tojolt-vnightly-<target>.tar.gz— a nightly is installable with the shipped installer, unchanged.It publishes as a draft first, exactly as a real release does and for the same reason: a nightly that fails the gates stays a draft nobody can install, and the previous nightly remains the newest thing available.
The tagged-release path is untouched.
publishandhomebreware already guarded torefs/tags, so a scheduled run never reaches them.releases/latestis unaffected — verified, not assumedThe nightly sets
prerelease: true, and GitHub'sreleases/latestreturns the most recent non-prerelease, non-draft release. Checked empirically rather than from the docs, against a repo that actually publishes both:This matters downstream rather than in the abstract: at least one project in this ecosystem installs jolt in CI via a script that resolves
releases/latest. A nightly that changed what that returns would silently move every such CI job onto an unreleased build. It does not.Second commit: making it testable before it merges
Two gaps that made the feature impossible to try.
It was reachable only from the schedule, so the only way to exercise it was to merge and wait for cron.
workflow_dispatchnow takes anightlyboolean, and a singleJOLT_NIGHTLYexpression feeds every guard so they cannot drift apart.The install script hardcoded
jolt-lang/jolt, so a nightly built on a fork was unreachable by the very command the release notes tell people to run —--version nightlylooked in the upstream repo, where a fork's rolling release does not exist. It now honoursJOLT_REPOand a--repoflag, defaulting exactly as before:That is useful beyond forks: it is what lets anyone test a candidate build without it first being published upstream, which is the same reason the nightly exists.
Notes for review
The cron time, the
vnightlytag name, and whether a rolling prerelease is wanted at all are the maintainer's calls — happy to change any of them or to drop this if the cadence is not what you want.Not verified: I have not observed a scheduled run complete end to end, since that requires the workflow to be on a branch the schedule reaches.
workflow_dispatchin the second commit exists precisely so that can be tried before merging.