Every module is published from one macOS host; the MinGW C bridge is cross-compiled with clang and the msys2 sysroot that Kotlin/Native downloads, so no Windows machine is needed.
~/.gradle/gradle.properties:
sonatypeUsername=<Central Portal user token>
sonatypePassword=<Central Portal user token password>
signingInMemoryKey=<base64 PGP secret key>
signingInMemoryKeyPassword=<key passphrase>
The signing public key must be on a keyserver (keyserver.ubuntu.com or keys.openpgp.org).
Before release, disable source substitution and run the same tests against the exact
published dependencies: -Pnetonstream.local=false -Phyper4k.local=false.
Publishing tasks reject source substitution, including Maven Local and staging output.
Do not substitute a sibling checkout to hide an unavailable dependency version.
Development CI checks out revisions from tools/ci/protocol-sources.lock using
bash tools/ci/checkout-protocols.sh. The script never resets existing checkouts.
Run source integration tests with -Pnetonstream.local=true -Pnetonstream.local.openssl=true -PnativeTargets=all; declaring all OpenSSL target
variants does not require compiling all targets when only host test tasks are requested.
Source-stack test success is not evidence that a Central-only consumer can build.
Use the Central Portal bundle upload. Do not use the OSSRH staging endpoint
(publishAllPublicationsToSonatypeCentralRepository): that API keys its implicit staging
repository by client IP, and an exit-IP change during the seven-minute upload (a TUN-mode
proxy failing over, for example) silently splits the release across two staging repositories.
The 1.0.0-beta1 release was published in two halves for exactly this reason. The bundle
upload is a single request and cannot split.
# 1. Build, sign and lay out every publication under build/staging-repo
./gradlew -Pnetonstream.local=false -Phyper4k.local=false publishAllPublicationsToStagingLocalRepository
# 2. Bundle it (repository-level metadata is not part of a bundle)
cd build/staging-repo
find com -name 'maven-metadata.xml*' -delete
zip -qr ../neton-<version>.zip com
cd -
# 3. Upload with USER_MANAGED so validation can be inspected before anything goes live
TOKEN=$(printf '%s:%s' "$SONATYPE_USER" "$SONATYPE_PASS" | base64)
curl -X POST -H "Authorization: Bearer $TOKEN" \
-F "bundle=@build/neton-<version>.zip" \
"https://central.sonatype.com/api/v1/publisher/upload?name=neton-<version>&publishingType=USER_MANAGED"
# → prints a deployment id
# 4. Poll until VALIDATED, then publish
curl -X POST -H "Authorization: Bearer $TOKEN" "https://central.sonatype.com/api/v1/publisher/status?id=<deployment id>"
curl -X POST -H "Authorization: Bearer $TOKEN" "https://central.sonatype.com/api/v1/publisher/deployment/<deployment id>"
# 5. Confirm every coordinate
curl -H "Authorization: Bearer $TOKEN" \
"https://central.sonatype.com/api/v1/publisher/published?namespace=com.netonstream&name=neton-core&version=<version>"Publishing to Central is irreversible. Check the validation result before step 4.
- Tag:
git tag -a v<version> -m "Neton <version>" && git push origin v<version> - Verify from a clean project that resolves only from
mavenCentral():implementation("com.netonstream:neton:<version>")plus one versionless module.