feat(publishing): migrate to Maven Central under uk.co.appoly.droid - #111
Merged
Merged
Conversation
JitPack rewrites the Gradle Module Metadata it serves, and that single behaviour caused every publishing problem this library has had. Two shapes were tried against real JitPack builds and both failed: a sources variant has its file entry stripped so Android Studio binds a 404, and no variant at all leaves it nothing to bind. 1.8.2 avoided both by publishing POM-only and broke consumer builds instead. Central serves exactly what is uploaded, so the workarounds are deleted rather than ported. Coordinates change: com.github.appoly.AppolyDroid-Toolbox:BaseRepo becomes uk.co.appoly.droid:baserepo, and AppolyDroid-Toolbox-bom becomes bom. Group now matches the package namespace, which has always been uk.co.appoly.droid. Breaking for consumers, hence 1.9.0 rather than a patch. - com.vanniktech.maven.publish 0.37.0 replaces 26 hand-written publishing blocks. Shared POM metadata and coordinates live in the root build; a module declares only its name and description. Central rejects an incomplete POM, and a missing developers block is a hard rejection, so the required fields are set centrally where a new module cannot omit them. - Deleted: the hand-rolled sources jar, the module-metadata juggling, and the BOM's -Pversion handling. All three existed only to survive JitPack. - The plugin produces sources and javadoc jars itself, so withSourcesJar and the AAR sources problem disappear rather than being worked around. - publishing-check, the release workflow and all 26 READMEs move to the new coordinates. The README's JitPack branch-snapshot section is replaced with local installs and Central snapshots, including a warning that a local install carries the same version string as the real release. - scripts/publish.sh reads credentials from the shared 1Password vault, so anyone with vault access can release. Deliberately not a personal shell function, which is single-user by construction. Verified locally with genuine signing from the vault: 26 modules published, every artifact carries a .asc, every code module carries sources and javadoc variants with correctly named file entries, and the BOM correctly carries neither since a java-platform has no code. Signature checks out against key 3F4AD175B7176969. 650 tests green; publishing-check passes on the new coordinates. Not yet possible: the actual upload. The uk.co.appoly namespace is unverified because the DNS TXT record is not live, and the portal token does not exist yet. Nothing here is merged until a real Central publish has been proven. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the minimal credential shim with the same release ceremony FlexiLogger uses: gates before the prompt, an explicit confirmation, and tagging only after a successful upload. One deliberate difference. FlexiLogger splits this in two — a `flexipublish` function in a personal ~/.zshrc supplies credentials, and ./publish.sh consumes them. That split makes releasing single-user: the credentials live in one person's shell profile and one person's private vault, so nobody else can publish. Here the credential loading is in the script itself, reading the SHARED Appoly vault, so anyone with vault access can release and there is nothing to install but the 1Password CLI. Three modes: (none) gates, confirm, publish to Central, tag, push the tag --dry-run every gate, no publish and no tag --local signed install to ~/.m2 for testing a branch in a consuming app Adapted rather than copied: - Version comes from BuildConfig.kt, not the version catalog. - Release branch is main, and tags are bare (1.9.0), matching this repo's existing tags rather than FlexiLogger's v-prefix. - Gates include the R8 consumer keep rules and the published-metadata check, which FlexiLogger has no equivalent of. - Refuses to run if a tag for the version already exists. Central releases are immutable, so a repeat publish cannot succeed and the clearest place to say so is before the build rather than after the upload fails. - The confirmation prints all 26 coordinates and states the immutability explicitly. This repo has spent the week retagging alphas freely; that stops working here, and the prompt is where that needs to land. - --local warns that a local install carries the same version string as the real release, so leaving mavenLocal() in place means resolving your own artifacts while believing you are testing the published one. Fixed while testing: --dry-run originally skipped credential loading, but its gate sequence runs publishToMavenLocal for the metadata check, which fails without a signing key. Signing is now read in every mode; only the upload token is release-only. Verified: --help, argument rejection, --local (26 modules signed into ~/.m2) and a full --dry-run through every gate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…dle 9.7.1 Pull-request CI failed at `:bom:signMavenPublication` with "No configured signatory". The job runs publishToMavenLocal to feed the variant-resolution gate, but holds no PGP key — deliberately, since a pull-request build must not carry the release signing key — while signAllPublications() was unconditional. The gate therefore died before the check it exists to run. Signing is now enabled only when a key is actually present. The gate reads module metadata and POMs and never looks at a signature, so an unsigned local publish serves it perfectly well. A task-graph guard refuses any Maven Central upload without a key, so the relaxation cannot leak into a real release: it fails loudly at configuration time rather than uploading artifacts Central would reject, in a release that cannot be undone. scripts/publish.sh keeps loading signing credentials in every mode. Its comment said signing was required for the metadata gate, which is no longer true; the real reason is that a dry run must still exercise the step a release cannot survive failing, and --local exists to test the signed artifacts. Also syncs the toolchain bump: tasks.wrapper still pinned gradleVersion to 9.7.0, which would have re-downgraded the wrapper on the next `./gradlew wrapper`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…se CI The plan lived only as an artifact, where it drifted out of date and could not be reviewed alongside the code it describes. It moves to docs/maven-central-migration.md, updated to what has actually landed: the namespace is verified, the publishing configuration and publish script are in, and the only work left is cutting 1.9.0 and migrating the four in-house consumers. Deletes .github/workflows/release.yml. Releases are run manually and locally for now, so a tag-triggered publish job is not the chosen path — and with no Maven Central secrets in the repo it could never have published anyway. Left in place it would have reported a red release run for a release that succeeded on a developer's machine. CLAUDE.md said a version tag ran the same publish in CI, which is no longer true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Listing the vault item's field labels confirms what phase 1 left open: the signing fields are all present, but portal-username and portal-token were never added. publish.sh reads those two only in release mode, so --dry-run and --local work today and a real release fails immediately at the credential step, before any gate runs. That is the last thing standing between this branch and 1.9.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The portal credentials were read only in release mode, so a dry run rehearsed every gate except the two fields most likely to be wrong — a missing or misnamed vault field would first surface on the immutable run it exists to protect. Reading them in dry-run mode costs nothing and uploads nothing. --local still skips them; it needs no upload credentials at all. Also records that portal-username and portal-token are now in the vault, so the plan no longer lists the release as blocked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A dry run failed with a bare "Metaspace" error partway through publishing. Cause: publishing drives Dokka across all 26 modules in one daemon, which needs well over the 1 GiB metaspace a typical personal ~/.gradle/gradle.properties sets. User-level properties take precedence over the repo's, so the project cannot set this itself — meaning a release succeeded or failed depending on whose machine ran it. GRADLE_OPTS beats both files, so the script now pins its own. Also guards `./gradlew publishToMavenLocal` in the metadata gate, the only unchecked Gradle call in that section. set -e already aborted correctly; this just names what broke instead of leaving a raw Gradle failure. With both in, `--dry-run` passes end to end: all five vault fields read, gates green, 26 modules signed into ~/.m2, variant resolution clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Nothing resolves from jitpack.io any more. The one plausible candidate is com.github.skydoves:sandwich, whose group name suggests otherwise, but it publishes to Maven Central — so the repository entry was only widening the set of hosts the build trusts. Verified by resolving :app's releaseRuntimeClasspath with --refresh-dependencies after removal: 1199 components, nothing unresolved. jitpack.yml is likewise vestigial. JitPack builds a tag from that tag's own copy of the file, so removing it here cannot affect anyone still resolving 1.8.3. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jakeeilbeck
approved these changes
Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft: do not merge. The build side is complete and locally verified, but no artifact has ever been uploaded to Maven Central — see Blocking below. Nothing here is proven until a real publish succeeds.
Why
JitPack rewrites the Gradle Module Metadata it serves, and that single behaviour caused every publishing problem this library has had. Both available shapes were tried against real JitPack builds and both failed:
The two reachable states on JitPack are "no AAR source navigation" and "no AAR source navigation, plus a pile of doomed requests every sync". Maven Central serves exactly what is uploaded, so the workarounds are deleted rather than ported.
Breaking: coordinates change
com.github.appoly.AppolyDroid-Toolbox:BaseRepouk.co.appoly.droid:baserepocom.github.appoly.AppolyDroid-Toolbox:S3Uploader-Multipartuk.co.appoly.droid:s3uploader-multipartcom.github.appoly.AppolyDroid-Toolbox:AppolyDroid-Toolbox-bomuk.co.appoly.droid:bommaven { url = "https://jitpack.io" }Group now matches the package namespace, which has always been
uk.co.appoly.droid. Artifact IDs lowercased per Maven convention. Hence 1.9.0, not a patch. All four known consumers are in-house, so a clean cut is simpler than dual-publishing; 1.8.3 stays resolvable on JitPack indefinitely.What changed
com.vanniktech.maven.publish0.37.0 replaces 26 hand-writtenpublishing {}blocks. Shared POM metadata and coordinates live in the root build; a module declares only its name and description. Central rejects an incomplete POM — a missingdevelopersblock is a hard rejection — so the required fields sit centrally where a new module cannot omit them.-Pversionhandling. All three existed only to survive JitPack. The plugin produces sources and javadoc itself, so the AAR sources problem ceases to exist rather than being worked around.scripts/publish.sh— full release ceremony modelled on FlexiLogger's, with one deliberate difference: credential loading is in the script, reading the shared vault, rather than in a personal shell function. FlexiLogger's split makes releasing single-user; this way anyone with vault access can release..github/workflows/release.yml— tag-triggered, running the same gates as CI. Consequence worth stating: if CI is red, no artifact exists, where JitPack would have built one regardless.publishing-checkand all 26 READMEs move to the new coordinates. The README's JitPack branch-snapshot section is replaced with local installs and Central snapshots.Verified locally
Full publish with genuine signing from the 1Password vault:
uk.co.appoly.droid.ascalongside each pom, module, aar, sources and javadoc-sources.jarfile entry, which is precisely what JitPack strips3F4AD175B7176969, the last 16 of the Appoly fingerprintjava-platformhas no code, and Sonatype exempts POM-packaged artifactspublishing-checkpasses on the new coordinates./scripts/publish.sh --dry-runpasses every gate and stops cleanly before publish and taggingNot verified, and why this is a draft
No upload to Maven Central has been attempted. Everything above proves the artifacts are correct; none of it proves Central accepts them. Blocking:
uk.co.appolyDNS TXT record is not live. Verified independently from two sessions — the token is absent from the delegated Route 53 nameservers. Not propagation delay; the theory is a duplicate hosted zone, and a zone whose own NS set doesn't match the four delegated names is the wrong one.portal-username/portal-tokenon the existing 1Password item; the FlexiLogger session has agreed those names.The signing key itself is done, org-owned, and published to
keyserver.ubuntu.com.Review notes
javaDocReleaseGeneration(Dokka) has aborted a run twice and succeeded unchanged on retry. Not reproducible on demand, so reported as observed rather than diagnosed. It fails before any upload, so it costs a wasted run rather than a partial release.81d5aacbumps FlexiLogger to 2.1.4 (not mine). Confirmed resolvable and green — and it resolves toflexilogger-android, which is variant-aware resolution working as intended.🤖 Generated with Claude Code