Repository navigation
release-verify-rc: validate rc jars #1173
Description
I had Claude AI draft this. It is pretty verbose but I have read the text to validate that there is nothing obviously incorrect.
I'm aware that it would be useful to validate other convenience binaries or packages (cargo files, pypi releases, etc.) but I think they would need their own separate discussions.
What should happen
release-verify-rc should validate published JVM artefacts — the jar, its
companion .pom, and the -sources.jar / -javadoc.jar published alongside
them — not just treat .jar as contraband inside the source tree. Four
blocking checks, three informational ones.
Blocking (FAIL):
-
.pomlicense entry. The companion POM must declare Apache-2.0 in its
<licenses>block, plus<developers>and<scm>. -
Incubator disclaimer in
<description>— for podlings only. The
POM's<description>element must contain the incubation disclaimer text. -
Companion
-sources.jarand-javadoc.jarpresent and signed. For
every published jar, both companions must exist and each must carry its own
.ascsignature and checksums — the same coverage the main jar gets today. -
Nexus staging repository at
repository.apache.org— ASF projects
only. Verify the staged repo isclosed(notopen), that its coordinates
and version match the RC under vote, and that every artefact in it carries
its.ascand checksums with a complete companion set.
Informational (INFO / WARN, never FAIL):
- Reproducibility signal from jar entry timestamps. If every entry in the
jar shares one identical timestamp, the project has almost certainly set
project.build.outputTimestamp. If timestamps vary across entries, it
almost certainly has not. Report the observation; do not assert
reproducibility either way. org.apachenamespace and package/groupIdcorrespondence. Two
related observations, both informational even for ASF projects:
(a) whether the declaredgroupIdsits underorg.apache.*, and
(b) whether the class-file path roots in the jar (org/apache/foo/...)
correspond to thatgroupId(org.apache.foo). Report what is observed;
never fail on either.- Companion jar content sanity. Whether the
-sources.jaractually
contains.java(or.scala/.kt) sources and no.classfiles, and
whether the-javadoc.jarcarries generated documentation or is a
placeholder. Both observations only — see boundary conditions, placeholder
jars are explicitly permitted by Central.
Why
The two blocking checks are stated ASF Incubator policy, verbatim from
the Incubator distribution guidelines § Maven distribution:
To comply with ASF release and distributions, please ensure the following:
- The project description should include the incubator disclaimer.
- Artifacts should be under the groupId of org.apache.<project>.
- The official release artifacts should not contain unapproved code.
- Release candidates, nightlys or snapshots need to be clearly marked by
adding suffix in their version, like
org.apache.<project>.<component>:0.1-SNAPSHOT- The POM file should set the ALv2 license as its license, Apache
<project> as its developer name, and proper source control information.
Nothing in the framework checks any of these. Today the release-* family
handles jars in exactly three places, none of which look inside one:
| Where | What it does |
|---|---|
skills/release-verify-rc/SKILL.md:440, :457 |
Puts .class / .jar on the prohibited-binary baseline and greps the unpacked source tree for them — the opposite of validating a jar |
skills/release-verify-rc/SKILL.md:239 |
Step 2/3 extend GPG + checksum verification to "any convenience binary", so a jar gets byte-integrity coverage — but Steps 4–8 (RAT, NOTICE/LICENSE, binary exclusion, symlinks, version strings) all operate on the unpacked source tree |
skills/pr-management-code-review/criteria.md:306 |
Flags .jar / .war / .ear as binaries that shouldn't land in a PR — again contraband |
dependency-license-audit does cover Maven, but via license-maven-plugin
against pom.xml dependency trees (SKILL.md:210, :287) — it never
opens a built artefact.
The informational checks (3) and (4) are cheap signals a reviewer currently
has to derive by hand, and both catch real mistakes: a jar built without
outputTimestamp when the project claims reproducible builds, and a groupId
that was never renamed to org.apache.<project> after entering incubation.
Note that although the org.apache.<project> groupId bullet above is quoted
policy, the corresponding check is deliberately proposed as informational,
not blocking, for ASF projects as well as podlings — see boundary conditions.
It is a signal for a human reviewer, not a gate.
On the companion jars (checks 3 and 7). These are a hard Maven Central
requirement, not a nicety —
Central's publishing requirements
state that "Projects with packaging other than pom have to supply JAR files
that contain Javadoc and sources." A staged repo missing them will fail Nexus's
close-time validation, so an RM who discovers this during the vote has to
re-cut. release-verify-rc should catch it before the [VOTE] opens. Today
projects/_template/release-build.md models an "Expected artefact list" that
distinguishes canonical source from convenience binaries, but has no notion of
the -sources.jar / -javadoc.jar / .pom set that must accompany each jar.
On repository.apache.org (check 4). For a JVM ASF project the Nexus
staging repository is the release — the dist/ tree carries the voted source
artefact, but the jars downstream consumers actually resolve come from the
staging repo promoted to Maven Central. The framework models the dist/ half
in detail and the Nexus half not at all: grepping nexus|repository.apache.org
across skills/, docs/release-management/, and projects/_template/ returns
nothing. That leaves three concrete holes:
release-verify-rccannot confirm the staged artefacts correspond to the RC
being voted on.release-vote-draftdoes not emit the staging-repo URL, which a JVM[VOTE]
thread must carry for voters to check the jars at all.release-promotehandles only thesvn mv dist/dev → dist/releasesequence
(SKILL.md:51); it has no counterpart for the Nexus
close / drop / promote lifecycle that runs in parallel with it.
Per the infra guide,
mvn release:perform "automatically inserts the release into a temporary
staging repository"; the RM then closes it to "trigger the quality checks and
prepare it for a vote", drops it if the vote fails, and on success promotes
"the artifacts to the release repository where they will get picked up by
Central".
Which layer
Skill (.claude/skills/<name>/)
Boundary conditions
Check 1 — POM license
- Inherited POMs.
<licenses>is commonly inherited from the ASF parent POM
(org.apache:apache) rather than declared locally. The check must resolve
against the effective POM, or explicitly report "inherited from parent,
not verified" rather than failing a correct POM. - Multi-module reactors publish one POM per module; all must be checked, and
the aggregator POM (<packaging>pom</packaging>) has no jar.
Check 2 — disclaimer in description
- Only applies when the project stage is
incubating(see release-* family has no ASF incubator/podling handling (IPMC vote, DISCLAIMER, incubator dist paths, -incubating suffix) #1172, which
proposes theproject_stageplumbing this check depends on — these two
issues share that dependency). - The Incubator permits two disclaimer texts (standard and
DISCLAIMER-WIP); matching must accept either, and should tolerate
whitespace/line-wrapping differences since the text is embedded in XML. - The
<description>may also be inherited or absent entirely — absent is a
FAIL for a podling, inherited needs effective-POM resolution as above.
Check 3 — companion jars
packaging=pommodules are exempt. Central's rule is explicit that it
applies to "packaging other thanpom", so aggregator and BOM modules have
no jar and must not be flagged for missing companions.- Placeholder jars are legitimate. Central states: "If you cannot provide
sources or documentation, we allow uploading placeholder.jarfiles in
order to pass validation." So an empty or near-empty-javadoc.jaris a
sanctioned pattern, not a defect. Check 7 must report it as an observation
and must never fail on it. - Non-Java JVM languages. Scala and Kotlin projects publish scaladoc/dokka
output under the-javadocclassifier for Central compliance. Don't assert
a Javadoc-specific internal structure (index.html,element-list); check
for non-emptiness, not for a particular doc tool's layout. - Other classifiers are not companions.
-tests,-shaded,-linux-x86_64
and similar classified artefacts are not part of the required set and should
not be mistaken for it, in either direction. META-INF/LICENSEandMETA-INF/NOTICEinside each jar — needs a policy
citation before it can be blocking. In practice the ASF parent POM injects
these into every jar viamaven-remote-resources-plugin, and it is widely
treated as required. But I could not confirm it verbatim:
licensing-howto says only
that "LICENSE and NOTICE files belong at the top level of the source tree"
and that binary distributions may need different content — it does not state
a META-INF requirement for jars. Someone should pin the authority before
this is implemented. Proposed interim behaviour: WARN, not FAIL. The same
caveat applies to a podling DISCLAIMER inside each jar.
Check 5 — timestamp reproducibility signal
- This is a heuristic, not a proof. Only a rebuild-and-compare
demonstrates reproducibility. The report wording must not claim the jar is
reproducible — only that its timestamps are or aren't consistent with a
reproducible configuration. - ZIP stores MS-DOS local times at 2-second granularity with no timezone, so
exact-equality comparison needs a tolerance and must not assume UTC. META-INF/MANIFEST.MFlegitimately carries varyingCreated-By/
Build-Jdkvalues; those are a separate signal and shouldn't be conflated
with entry timestamps.- An empty jar, or one with a single entry, gives no signal — report
INSUFFICIENT-DATA, not PASS.
Check 6 — org.apache namespace and package vs groupId
- Both halves are INFO-only, including for ASF top-level projects. The
org.apache.<project>guidance is real policy, but a released artefact can
sit outside that namespace for legitimate historical reasons — established
projects that entered the ASF with existing Maven coordinates and kept them
for downstream compatibility, and modules published under coordinates
predating a rename. A FAIL would misfire on correct, long-standing releases,
and the groupId of a published artefact cannot be changed retroactively
anyway, so failing an RC over it gives the RM no available remedy. - Divergence between package roots and groupId is likewise frequently
legitimate: shaded or
relocated dependencies,META-INF/entries,module-info.class,
multi-release jars (META-INF/versions/<N>/), and the very common case of a
groupId that intentionally differs from the package root. A FAIL here would
be wrong more often than right. - Report correspondence as a proportion of class entries plus the actual
roots found, so the reviewer can judge — not a boolean. - Useful specifically for podlings, where it surfaces whether the
org.apache.<project>rename has actually happened.
Check 4 — Nexus staging repository
- ASF-only. Gate on the ASF organization adapter; skip entirely for
non-ASF adopters, consistent with the existing--non-asfflag on
release-promote(SKILL.md:179). - JVM-only. Skip when the project publishes no Maven artefacts. A Python
or Rust ASF project has no staging repo and must not be warned about one. - Promotion is irreversible and must be gated accordingly. Once a staging
repo is promoted it syncs to Maven Central, where artefacts are immutable and
can never be withdrawn — strictly less recoverable than thedist/release/
case that already justifies a hard denylist and a PMC gate
(skills/release-promote/SKILL.md:92,:107). Any promote/drop step this
work adds torelease-promoteneeds an equivalent golden rule; a promote
must never be emitted as an autopilot action. - Verification stays read-only. In
release-verify-rcthis check must only
read the staging repo. It must never close, drop, or promote — any voter
may runrelease-verify-rc, including someone with no karma on the repo. - Repo state matters. An
open(not yet closed) staging repo is still
mutable and is not a valid vote target; report that distinctly from a missing
repo. The snapshots repository
(repository.apache.org/content/repositories/snapshots/) is never a valid
vote target. - Staging repo id is not derivable. Nexus assigns
orgapache<project>-NNNNat deploy time; the number cannot be predicted, so
it must be read from the planning issue or passed in — the same way
staging_urlis sourced today (skills/release-promote/SKILL.md:241). - Multiple staging repos. A reactor deployed in more than one pass, or a
retried deploy, can leave several staging repos for one version. Surface all
of them rather than silently picking one; stale repos from an earlier RC are
a common real-world footgun. - Checks 1–3 and 5–7 should run against the jars and POMs in the staging
repo when one is supplied, so every check shares a single artefact source.
Family-wide
- Non-JVM projects must be unaffected: all four checks skip cleanly when the
artefact set contains no jars. - Requires a new config surface.
projects/_template/release-build.mdalready
has an "Expected artefact list" that distinguishes canonical source from
convenience binaries; the companion.pom/-sources.jar/-javadoc.jar
set is not modelled there yet.
Out of scope
- Rebuilding the jar to prove reproducibility — check 3 is a signal only.
- Bytecode target-level verification against the declared minimum JDK.
- Automating the Nexus close / drop / promote actions. This issue covers
read-only verification inrelease-verify-rc, the staging-repo URL in
release-vote-draft, and the config surface plus emitted instructions
forrelease-promote. Actually performing an irreversible promote on the
RM's behalf is out of scope. - Relaxing the existing prohibited-binary rule at
skills/release-verify-rc/SKILL.md:440. Jars must still be absent from the
source artefact; this proposal validates separately-published jars.
References
- https://incubator.apache.org/guides/distribution.html § Maven distribution
— source of both blocking checks - https://infra.apache.org/publishing-maven-artifacts.html — the staging /
close / drop / promote workflow behind check 5 skills/release-promote/SKILL.md:51,:92,:107,:179,:241— the
dist/promote sequence, denylist and PMC gate that the Nexus promote needs
a counterpart to- https://central.sonatype.org/publish/requirements/ — mandatory sources /
javadoc jars behind check 3, and the placeholder-jar exception behind check 7 - https://maven.apache.org/guides/mini/guide-reproducible-builds.html —
project.build.outputTimestampsemantics behind check 3 - release-* family has no ASF incubator/podling handling (IPMC vote, DISCLAIMER, incubator dist paths, -incubating suffix) #1172 — podling handling in the
release-*family; check 2 depends on the
project_stageplumbing proposed there skills/release-verify-rc/SKILL.md:239,:440,:457— current jar handlingprojects/_template/release-build.md— expected-artefact-list config surface
Activity
[-]release-verify-rc: validate published jars and their .pom (ALv2 license entry, incubator disclaimer in description, reproducibility + groupId signals)[/-][+]release-verify-rc: validate published jars, their .pom, and the repository.apache.org staging repo (ALv2 license entry, incubator disclaimer, staging completeness, reproducibility + groupId signals)[/+]pjfanning commentedon Sep 8, 2026
Duplicate check: none found. Searched
jar OR maven OR pom OR nexusandrelease-verify-rc OR artefact OR stagingacross open and closed issues.Nearest neighbours, none overlapping:
pr-management-code-review. That is the contraband-handling path (criteria.md:306) this issue cites, not artefact validation.dist.apache.orgonly; no Maven or Nexus surface.release-verify-rcstep 6 internals, unrelated.Implementation-shape note surfaced by that search: #602 establishes that ASF infra integrations in this repo go through a
tools/<system>/adapter rather than inline skill logic. Check 5 probably wants atools/nexus(ortools/asf-nexus) adapter exposing read-only staging-repo queries, withrelease-verify-rcconsuming it — matching how the SVN side is being modelled. That also keeps the read-only verification path cleanly separated from any future write-capable close/drop/promote surface, which this issue explicitly scopes out.Related: #1172 (podling handling) — check 2 depends on the
project_stageplumbing proposed there.[-]release-verify-rc: validate published jars, their .pom, and the repository.apache.org staging repo (ALv2 license entry, incubator disclaimer, staging completeness, reproducibility + groupId signals)[/-][+]release-verify-rc: validate rc jars[/+]liwenjie200543 commentedon Sep 26, 2026
I'd like to pick this up. To keep the first PR reviewable, I'd split
the work:
PR 1 — blocking checks 1–3, local artifacts only:
.pomlicense /<developers>/<scm>check (check 1)<description>(check 2)-sources.jar/-javadoc.jarpresence, each with itsown
.ascand checksums (check 3)tools/(stdlib XML parsing,no new dependencies), with unit tests over fixture POMs and jars,
wired into
release-verify-rcwhere the source tree is verifiedtoday.
PR 2+ — the Nexus staging-repository check (4), which needs
network egress and an endpoint contract, then the informational
checks (5–7).
Two questions before I start:
rather see the informational checks first since they never block?
tools/adapter withdeclared egress (per the framework's egress rules), or be scripted
inline in the skill?
potiuk commentedon Sep 26, 2026
docs(release): wire jvm_artefact_checks and jvm_digest_set into Step 6b
fix(maven-artifact-verify): resolve <scm> per field along the parent …
feat(tools): add maven-artifact-verify and wire JVM artefact checks i…
docs(release): wire jvm_artefact_checks and jvm_digest_set into Step 6b
fix(maven-artifact-verify): resolve <scm> per field along the parent …
feat(tools): add maven-artifact-verify and wire JVM artefact checks i…
liwenjie200543 commentedon Oct 4, 2026
PR 2 (check 4) is up as #1505:
tools/asf-nexus+release-verify-rcStep 6c.Following potiuk's call that the probe should live in a
tools/adapter, it is a doc-only, read-only adapter (the shapeasf-svn/osvuse), with a newcontract:release-stagingcapability. One design point I pinned empirically before writing it — probingrepository.apache.organonymously on 2026-10-04:/service/local/staging/repository/<id>,/service/local/staging/profile_repositories, and/service/local/repositories/<id>/contentall answer 401 anonymously (even for non-existent ids, and even for the publicreleasesrepository)./content/repositories/<id>/answers 404 for a non-existent id and 200 for real ones — the web content tree is anonymous-readable.So the adapter documents two read paths: the anonymous content tree (existence, inventory,
.asc/checksum coverage — everything a voter needs) and the authenticated staging API (authoritativestate, plus the profile-wide listing that surfaces stale repositories from an earlier RC). On the anonymous path the closed-not-open rule is reported asSTATE-UNVERIFIED(a warning naming what to verify by hand in the Nexus UI), never a failure — the issue's "any voter may run this, including someone with no karma" requirement, and the same warn-don't-fail direction asINHERITED-UNVERIFIEDin check 1.If anyone who runs Nexus releases regularly can confirm the two-path split and the
statefield naming against repository.apache.org, that would settle the one thing this PR guesses at.fix(tools): link the asf-nexus reference by PR number until it lands
feat(tools): add asf-nexus and wire Nexus staging check into verify-rc
fix(tools): apply adversarial-review fixes to the asf-nexus staging c…
28 remaining items
chore(skills): re-apply the observations text on the reflowed sibling
feat(tools): add informational JVM checks 5-7 to maven-artifact-verify (
ci(labeler): count only explicit references when passing labels to is…
feat(bitbucket): add guarded cloud PR merge (#1471)
feat(tools): add asf-nexus and wire Nexus staging check into verify-rc (