Skip to content

Ship the front page in the release zip, and record the link scheme it depends on - #2099

Merged
wmdietl merged 13 commits into
masterfrom
ship-release-front-page
Sep 18, 2026
Merged

wmdietl merged 13 commits into
masterfrom
ship-release-front-page

Conversation

@wmdietl

@wmdietl wmdietl commented Sep 16, 2026

Copy link
Copy Markdown
Member

Follows #2097, now merged; master is merged in, so this reduces to the front page alone. This must merge before the next release, since only a release built after it can carry the page.

Why

docs/checker-framework-webpage.html is the Checker Framework's front page, and eisop does not publish it. The website generates its own from cf-template.md, a Markdown port of this file. So the page here has been version-stamped by every release and then thrown away, while the port drifted — it silently lost the Dataflow Framework bullet, and nobody noticed for two years.

The deeper problem is that one template shared across every release is a standing mismatch. A link added for content that first ships in release N is broken on the archived page of every release before N — permanently, not just until the next release. That is not a transition cost; it recurs every time per-release content is added, which is exactly what #2097 did with the quick-start guide and the dataflow manual.

A page that travels with the release it describes cannot have that problem, and this file already is such a page: release.xml stamps the Checker Framework version and date and the AFU zip name and date into it, so it is self-contained at release time.

What changed

The page is added to checker-includes, the favicon it references ships beside it via a zipfileset, and its links are aligned from the old typetools site layout to the layout the zip and the website share.

The page is served at two depths — as cf/index.html for the newest release and as cf/<release>/index.html for an archived one — so the link scheme has to be mixed, the same trick cf-template.md already used for its download link:

relative manual/manual.html, manual/manual.pdf, manual/checker-framework-dataflow-manual.pdf, tutorial/, api/checker-javadoc/, CHANGELOG.md, quick-start.html, CFLogo.png, favicon-checkerframework.png
absolute /cf/checker-framework-<ver>.zip, /cf/releases/releases.html, /afu/annotation-tools-<ver>.zip, /afu/annotation-file-utilities.html

Uniformly relative breaks the singletons at release depth; uniformly absolute makes every archived page link the newest release's manual rather than its own. That constraint is now recorded in a comment at the top of the page itself, along with the three targets the website generator produces rather than the zip carrying them. The quick-start guide, which #2097 shipped and which is served at the same two depths for the same reason, gets a short comment pointing at it.

Testing

  • Every one of the page's 19 local link targets traced against the zip's contents and against EisopSiteGenerator's source. On the website all resolve, or will once Lift the quick-start guide out of the release zip eisop.github.io#103 and Add more aliases for nullness annotations; fix formatting #105 land. Three resolve only there and not in an unzipped release, because the generator produces them: quick-start.html (the zip has docs/checker-framework-quick-start.html, which the generator renames), api/checker-javadoc/ (the zip has checker/dist/checker-javadoc.jar, which it unpacks), and CFLogo.png (copied up from tutorial/). Each was considered and left as is — see the comment in the page.
  • Confirmed against the published 3.49.3 zip and the live site rather than assumed: /cf/<release>/{CFLogo.png,api/checker-javadoc/,manual/manual.html,tutorial/,CHANGELOG.md} are all 200, and the zip's docs/ holds only CHANGELOG.md, examples, manual, tutorial.
  • update-checker-framework-versions run end-to-end on a scratch copy with the real AFU build.properties: both rewritten absolute links pick up the new version, and all four marker-wrapped values still update. The byline regex that rewrites checker-framework-<version> cannot chew the new comment prose, since it requires a digit after the hyphen.
  • The two zipfileset entries verified in isolation: docs/checker-framework-webpage.html and docs/favicon-checkerframework.png land where the generator will look for them.
  • spotlessCheck passes and release.xml parses. htmlValidate covers both pages and is left to CI, since html5validator is not installed locally; the comments avoid -- for that reason.

Not in this PR

The website half — teaching the generator to use this page as cf/<release>/index.html and retiring cf-template.md — needs no release and follows separately in eisop/eisop.github.io#105. Until it lands, the page rides along in the zip unused, exactly as the quick-start guide does. Releases predating this PR have no such page, so the generator keeps a fallback for them.

Nothing verifies these links automatically yet. #2111 proposes checking the shipped pages against the zip's contents at PR time, and gating a release on a link check of the generated site before anything is published — which needs eisop/eisop.github.io#106.

🤖 Generated with Claude Code

wmdietl and others added 9 commits September 16, 2026 09:07
…l release step

Two asks: publish nightly snapshots, and stop finishing every release by hand
on the Central Portal website. Both turn out to need less than expected.

Snapshots already go to the Central Portal snapshot repository -- the URL in
gradle-mvn-push.gradle is the right one -- and they are deliberately unsigned,
so a nightly job needs no publishing changes and no GPG key. What is missing is
a scheduled workflow, a per-namespace "enable SNAPSHOT" setting in the Portal,
and two secrets.

The manual click on a release exists because releases upload through
ossrh-staging-api.central.sonatype.com, Sonatype's compatibility layer for
builds that predate the Portal API, which leaves the deployment for a human to
publish. That service has an API for the last step:

  POST /manual/upload/defaultRepository/io.github.eisop?publishing_type=automatic

so the click can go away without changing how the build uploads anything.

The document recommends against adopting a Portal-native publishing plugin.
com.vanniktech.maven.publish and the alternatives configure publications for
you, and this build has eighteen, several hand-tuned: checker publishes
components.shadow rather than components.java, with a comment explaining that
the latter would ship the skinny jar under the fat jar's name, and a Gradle
attribute copied onto the shadow configuration by hand. Re-expressing that
inside another plugin's model is risk for no gain when the upload already
works. The document says which plugin to revisit if the publications are ever
simplified.

Adds .github/workflows/publish-snapshot.yml as a starting point, inert until
the secrets exist: a first job checks for SONATYPE_NEXUS_USERNAME and the
publish job is skipped without it, so merging publishes nothing by itself. It
also refuses to run if the version does not end in SNAPSHOT, since a release
upload cannot be undone. The version-extraction command and the workflow's
shell were both checked locally.

Nothing here is enabled. Enabling it is adding the secrets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The six links to search.maven.org in the manual no longer resolve: that host
answers 403 for both its /search and /artifact URLs, with a browser user agent
as well as without, while the same queries on central.sonatype.com answer 200.

Rewrite them, and drop the trailing "/jar" segment that the artifact URL form
no longer takes. Each rewritten URL was fetched to confirm it resolves. Only
the URLs change; the surrounding LaTeX is untouched.

The publishing URLs in the build were already current: snapshots go to
central.sonatype.com/repository/maven-snapshots/ and releases to the
ossrh-staging-api host, and no oss.sonatype.org or s01.oss.sonatype.org
reference remains anywhere in the tree. The occurrence in docs/CHANGELOG.md is
in a released section and is left as the historical record.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found while tracing how a release reaches Maven Central.

release_push.py steps 5b and 5c walk the releaser through the OSSRH Nexus
staging UI -- "click on iogithubeisop-XXXX", "Click close at the top", "Copy
the URL of the closed artifacts (in the bottom pane)" -- and then ask them to
paste that URL back into the script. The Central Portal has no close step and
no such panes, so anyone following the script looks for a button that no
longer exists. Adopting the one-call automation removes these steps rather
than requiring them to be reworded.

A release has to bump the version in 34 places across 12 files besides
build.gradle: the examples under docs/examples, and several places in the
manual and the webpage. They are consistent today, and keeping them so is
manual; a missed one leaves the manual telling readers to depend on a version
that is no longer newest.

docs/examples/publish-smoketest builds against publishToMavenLocal, so it
checks the artifacts the build would publish but not that a deployment
arrived. Once nightly snapshots exist, pointing it at the published snapshot
repository would catch a broken publication on the day it breaks.

And the documented release command hardcodes one maintainer's GPG key, which
reads as the value to use rather than as the releaser's own.

README-eisop.md now says that the key is the releaser's own, that the upload
is staged and not live until published, where that is done, and that other
files carry the version too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Push steps 5a/5b/5c and 10 drove the retired OSSRH Nexus staging UI: they
told the releaser to "close" artifacts in a top pane and paste a staging
repository URL back into the script. The Central Portal has no such step.
The staging call itself could never have worked here either: it read the
signing passphrase from /projects/swlab1/checker-framework/hosting-info/,
a University of Washington path that does not exist, and signed with
checker-framework-dev@googlegroups.com rather than the releaser's key.

Maven Central publishing is in practice a separate ./gradlew publish run,
so remove those steps rather than reword them, and renumber the remaining
GitHub/website steps 1-12. Removing step 5c retires the only caller that
passed a staging repository URL, so maven_sanity_check's repo_url
parameter and add_repo_information -- which edited poms to point at
org/checkerframework artifacts -- go with it. Document the separate
publish step in README-release-process.html.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The target rewrote files that no longer exist and keyed off marker
comments that pages no longer carry. Ant's <replaceregexp> only prints
"The following file is missing" and still reports BUILD SUCCESSFUL, and a
marker regex that matches nothing is not an error at all, so both kinds of
drift were silent.

Gone from the tree: docs/manual/checkerframework.gradle,
build-common.properties, checker/build.properties. Wrong path:
checkerQuickStartPage pointed into docs/manual/ rather than docs/.
Absent markers: checker-framework-version and compiler-version on
docs/checker-framework-webpage.html.

Drop those five rules, fix the quick-start path, and add require-file and
require-marker macros that assert every file and marker up front, so the
target now fails before rewriting anything. Also drop checker.tutorial and
checker.release, unused in this target, and the release.properties entries
left with no reader.

docs/checker-framework-quick-start.html still advertised
checker-framework-2.1.7.zip, having missed every release since the path
broke; bring it to the current release and fix its javac path, which had
bin/checker inverted relative to every other document.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ents

The release scripts and Renovate were both bumping docs/examples, and
fighting. Renovate rewrites a pom property in place and drops marker
comments around it -- it did that to MavenExample-framework-all/pom.xml in
PR #2017. The Ant target keyed off those same markers, including in
update-and-copy-maven-example, which sets the version on the copy the
Maven sanity check compiles. Had Renovate reached MavenExample/pom.xml
first, that check would have gone on compiling the previous release and
still passed.

Give the examples to Renovate alone: drop both example rules from
update-checker-framework-versions, match the <checkerFrameworkVersion>
element rather than markers when preparing the sanity-check copy, and
remove the now-vestigial markers from MavenExample/pom.xml.

Two renovate.json changes make that reliable. Every Renovate edit ever
made to the example Gradle builds was a plugin version; the eisop version
in them was last bumped by Dependabot, removed in PR #1997. These builds
hold it in a variable (an ext.versions map entry, or a plain def), so add
an explicit customManagers regex rather than rely on Renovate's Gradle
variable resolution. Second, the repository-wide minimumReleaseAge of 7
days delayed our own artifact a week behind every release, so set it to 0
for io.github.eisop and give it its own PR.

Separately, checker-includes had drifted the same silent way, and an Ant
include pattern that matches nothing is not an error either. README.html
was renamed away in 2015, so no README shipped in the distribution at
all. "Make class name and file name the same" moved the units-extension
qualifiers into qual/ and renamed Demo.java in 2020, so that example
shipped its Makefile, README and Expected.txt and not one Java source --
the shipped Makefile refers to files that are not in the zip. Verified on
the published site, whose newest release is 3.49.3-eisop1: under
/cf/checker-framework-3.49.3-eisop1/examples/units-extension/, README is
200 while UnitsExtensionDemo.java is 404, and README.md is 404.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Maven Central does not pin a signing key to a namespace; it only checks
that each signature verifies against a public key it can fetch from a
keyserver. Several maintainers can therefore each sign with their own key,
and nothing in this repository has to change when a new one starts
releasing. Only the documentation implied otherwise, by presenting one
maintainer's key as the value to pass.

Move the key out of the command line and into the releaser's own
~/.gradle/gradle.properties, next to the Portal tokens already documented
there, so the release command names no key at all. Fail the signing task
with an explicit message when signing.gnupg.keyName is unset: gpg would
otherwise fall back to whichever secret key happens to be its default,
which may not be one Central can verify. Snapshots are unsigned, so the
check does not affect them.

Record the release-process findings from this audit in
maven-central-publishing.md: two web pages that the release still
rewrites but that nothing publishes (the main one has already been ported
to the website repository's cf-template.md, minus its Dataflow Framework
bullet), and the fact that the website is still on 3.49.3-eisop1 because
nothing runs the site generator as part of a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two documents were produced at release time and then discarded, because
they only ever reached the interm site directory that nothing publishes
for eisop.

docs/checker-framework-quick-start.html was kept version-correct by
update-checker-framework-versions but shipped nowhere and appeared on no
website. Add it to checker-includes so it travels in the release zip
alongside the manual, the tutorial and the CHANGELOG. Its links pointed at
https://eisop.github.io/cf/manual/#anchor, which resolves only through the
404 page's JavaScript redirect; make them site-relative so they work both
at cf/quick-start.html and in an archived cf/<release>/quick-start.html.

release_build.py already runs `make` in dataflow/manual, and
checker-framework-website-docs copies the result to the interm directory
under the very name the old front page linked to. Add it to the zip at
docs/manual/checker-framework-dataflow-manual.pdf with a fullpath
zipfileset, the mechanism already used for CFLogo.png. Because the website
generator lifts docs/manual wholesale, it surfaces at
cf/manual/checker-framework-dataflow-manual.pdf with no generator change.

eisop/eisop.github.io#103 lifts the quick-start guide to cf/quick-start.html,
restores the Dataflow Framework bullet that was dropped when the front page
was ported to cf-template.md, and links both from the front page.

checker-framework-webpage.html is deliberately left untouched here: a
follow-up PR ships it in the release zip as that release's own page, which
is the opposite of the "not published, do not edit" note that would
otherwise have landed with this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/checker-framework-webpage.html is the Checker Framework's front page.
It is not published by eisop: the website generates its own from
cf-template.md in the eisop.github.io repository, a Markdown port of this
file. So this page has been version-stamped by every release and then
discarded, while its port drifted -- the port silently lost the Dataflow
Framework bullet, and nobody noticed for two years.

One template shared across every release is also a standing mismatch. A
link added for content that first ships in release N is broken on the
archived page of every release before N, permanently, not merely until the
next release. Shipping the page with the release it describes removes that
by construction, and this file is already exactly such a page: release.xml
stamps the Checker Framework version and date, and the AFU zip name and
date, into it, so it is self-contained at release time.

Add it to checker-includes. Align its links, which were still the old
typetools site layout, to the layout the zip and the website share. Because
the page is served at two depths -- as cf/index.html and as
cf/<release>/index.html -- the scheme has to be mixed: relative for what
exists at both (manual/manual.html, manual/manual.pdf, tutorial/,
api/checker-javadoc/, CHANGELOG.md, CFLogo.png) and absolute for site
singletons (/cf/<zip>, /cf/releases/releases.html, /afu/<zip>,
/afu/annotation-file-utilities.html). Checked against the live site:
manual/manual.pdf resolves at both depths while
manual/checker-framework-manual.pdf exists only at cf/. The favicon the
page references existed nowhere on the site, so ship it alongside.

The website half -- using this page and retiring cf-template.md -- needs no
release and follows separately. Until then the page rides along unused, as
the quick-start guide does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 22:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The front page did not link the quick-start guide at all, and linked the
dataflow manual by absolute URL into the current website. Both matter now
that this page travels with the release: if the links are not in the
shipped page, correcting them later needs another release.

Link quick-start.html from the Quick start entry and from both Documentation
lists. Point the dataflow manual at manual/checker-framework-dataflow-manual.pdf,
where this release now puts it, so an archived page links its own release's
manual rather than whatever the site currently serves. No hardcoded
eisop.github.io links remain in the page.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wmdietl added a commit to eisop/eisop.github.io that referenced this pull request Sep 16, 2026
The Dataflow bullet and the quick-start links added here are superseded by
eisop/checker-framework#2099, which ships docs/checker-framework-webpage.html
in the release zip so that each release carries its own front page. That
page already has the Dataflow bullet -- it is the page cf-template.md was
ported from, and the port is where the bullet was lost -- and #2099 adds the
quick-start links to it.

Editing the template here as well would mean landing links to files that no
published release contains, then reverting them when the template retires.
Drop that half; the generator change stays, since the quick-start guide has
to be lifted out of the release zip either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Base automatically changed from maven-central-publishing to master September 18, 2026 00:31
wmdietl and others added 3 commits September 17, 2026 20:34
PR #2097 was squash-merged, so master carries its twelve commits as one commit
that is not an ancestor of this branch, and the eight of them that this branch
also contains came through a different route.  Four files conflicted:

- `publish-snapshot.yml` and `maven-central-publishing.md`: master's versions
  are newer -- the workflow's pins were aligned with the other workflows, and
  the document was trimmed -- and this branch never touched either after
  forking, so master's win.
- `checker-includes` and `release.xml`: both sides add entries.  Kept the union
  -- master's `README.md` and units-extension corrections, and this branch's
  front page and favicon.

One thing was lost with the trimmed document: the front page's relative versus
absolute link scheme, which is a trap for a future editor.  It is restored as a
comment in the page itself in the next commit.
The page is served at two depths, as /cf/index.html and as
/cf/<release>/index.html, so its links have to be mixed: relative for what sits
beside it at both depths, absolute for site singletons.  Making them uniformly
one or the other breaks one of the two depths, which is a trap for anyone
editing the links later.

That constraint was written down in maven-central-publishing.md, which no
longer carries the release-process archaeology.  Put it in the page it
constrains, together with the three targets that the website generator
produces rather than the zip, and a reminder that release.xml keys off the
marker comments.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
It is served at the same two depths as the front page, so it has the same
mixed relative/absolute links and the same trap for a later editor.  Point at
the front page's comment rather than repeating the explanation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wmdietl wmdietl changed the title Ship the front page in the release zip, so each release carries its own Ship the front page in the release zip, and record the link scheme it depends on Sep 18, 2026
@wmdietl
wmdietl merged commit 0ad4964 into master Sep 18, 2026
37 of 45 checks passed
@wmdietl
wmdietl deleted the ship-release-front-page branch September 18, 2026 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants