The recommended way to release is the /release skill, available in both Copilot CLI and Claude Code. It automates
the full process: pre-flight checks, artifact publishing, GitHub release creation, and documentation upload.
/release
The skill validates credentials, verifies you are on main with no pending changes, and walks through each step with
clear progress reporting. If any step fails, it stops immediately with an error.
For manual publishing or troubleshooting, see the sections below.
All Gradle properties go in your user-level gradle.properties (never commit real values to the repo):
- Windows:
%USERPROFILE%\.gradle\gradle.properties - macOS/Linux:
~/.gradle/gradle.properties
| Property | Description | Where to get it |
|---|---|---|
ossrhUsername |
Sonatype OSSRH username | Register at central.sonatype.com |
ossrhPassword |
Sonatype OSSRH password | Same account |
signingKey |
GPG private key (armored, newlines replaced with \n) |
gpg --gen-key, then gpg --armor --export-secret-keys KEY_ID |
signingPassword |
GPG key passphrase | Chosen during key generation |
Publish your public key to a key server so Maven Central can verify signatures:
gpg --keyserver keyserver.ubuntu.com --send-keys KEY_ID| Property | Description | Where to get it |
|---|---|---|
nuget-api-key |
NuGet API key | nuget.org → API Keys → Create (scope: robocode.tankroyale.*) |
Create an API token at pypi.org → Account Settings → API Tokens (scope: robocode-tankroyale-botapi).
Configure via one of these (checked in order):
- Gradle property:
pypiToken=pypi-AgEIcH...in~/.gradle/gradle.properties - Environment variable:
PYPI_TOKEN=pypi-AgEIcH... - ~/.pypirc file:
[distutils] index-servers = pypi [pypi] repository: https://upload.pypi.org/legacy/ username: __token__ password: pypi-AgEIcH...
Create an API token at npmjs.com → Access Tokens → Generate New Token
(type: Automation; scope: @robocode.dev/tank-royale-bot-api).
| Property | Description | Where to get it |
|---|---|---|
npmjs-api-key |
npmjs Automation token | npmjs.com → Access Tokens |
Authenticate with gh auth login. If gh is not installed, the release skill provides a manual fallback URL.
For the local create-release Gradle task, a Personal Access Token is needed instead:
| Property | Description | Where to get it |
|---|---|---|
tankRoyaleGitHubToken |
GitHub PAT | github.com/settings/tokens (scopes: repo, workflow) |
# Maven Central (Sonatype)
ossrhUsername=your-sonatype-username
ossrhPassword=your-sonatype-password
# GPG signing
signingKey=-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nlQWGBGBq5...\n-----END PGP PRIVATE KEY BLOCK-----
signingPassword=your-gpg-passphrase
# NuGet
nuget-api-key=oy2abc123...
# PyPI
pypiToken=pypi-AgEIcH...
# npmjs
npmjs-api-key=npm_abc123...
# GitHub (for local create-release Gradle task)
tankRoyaleGitHubToken=ghp_abc123...Publish to staging, close, and release:
./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepositoryOr publish to staging only (for manual review before release):
./gradlew publishToSonatypeDry run (shows what would be executed):
.\scripts\release\publish-nuget.ps1Publish (prompts for confirmation):
.\scripts\release\publish-nuget.ps1 -ExecutePublish without confirmation (for automated workflows):
.\scripts\release\publish-nuget.ps1 -Execute -ForceManual alternative:
cd bot-api\dotnet\api\bin\Release
dotnet nuget push robocode.tankroyale.botapi.«version».nupkg --api-key «key» --source https://api.nuget.org/v3/index.jsonBuild the wheel, then upload:
./gradlew :bot-api:python:upload-testpypi # test first
./gradlew :bot-api:python:upload-pypi # productionBuild and publish:
./gradlew :bot-api:typescript:npmPublishAfter publishing, a verify-publish workflow checks all four registries automatically.
It runs as four parallel jobs in the Actions UI — each shows a green ✅ or red ❌ badge
as soon as the version becomes available (or times out).
No credentials are needed — all checks use public registry HTTP APIs.
| Job | Registry checked | Retry strategy |
|---|---|---|
| check-nuget | api.nuget.org |
Every 30s, up to 3 min |
| check-pypi | pypi.org |
Every 30s, up to 3 min |
| check-npm | registry.npmjs.org |
Every 30s, up to 3 min |
| check-maven | repo1.maven.org |
Every 5 min, up to 120 min |
The release skill triggers this automatically after publishing. To run manually:
gh workflow run verify-publish.yml --ref main -R robocode-dev/tank-royale -f version=0.39.0Or via GitHub → Actions → Verify Publish → Run workflow.
The create-release workflow creates a draft pre-release on GitHub with tag v<version>, builds the project artifacts
and sample bots, then dispatches a packaging workflow that builds native installers via jpackage for Windows, Linux, and
macOS and attaches them to the draft release.
The release skill triggers this automatically. To run manually:
From GitHub Actions (recommended):
./gradlew create-release -PtankRoyaleGitHubToken=${{ github.token }}Locally:
./gradlew create-release -PtankRoyaleGitHubToken=$YOUR_PATTo also trigger native installer packaging:
./gradlew create-release \
-PtankRoyaleGitHubToken=$YOUR_PAT \
-PtriggerPackageReleaseWorkflow=trueThe release remains a draft until you manually review and publish it on GitHub:
- Open the draft release on GitHub.
- Verify that JARs, sample bot zips, and all platform installers are attached.
- Update the notes if needed.
- Click Publish release.
- If packaging fails on a platform, check
build/ci-gradle.logattached as a workflow artifact. - Ensure the packaging workflow has permissions:
contents: writeandactions: read. - Ensure you passed
-PtriggerPackageReleaseWorkflow=truewhen you want installers created.