Release

SkillDev tools

Release Tank Royale artifacts to Maven Central, NuGet, PyPI, and npmjs, then trigger the GitHub Actions create-release workflow. Use when the user runs /release to publish a new version.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Release skill

What this skill tells your AI

The instructions your AI receives, as published by robocode-dev/tank-royale in .agents/skills/release/SKILL.md and read by ahel’s review.

You are executing the Tank Royale release workflow. Follow these phases exactly, in order. If ANY step fails (non-zero exit code), STOP immediately with an ERROR — never continue to the next step, and never downgrade errors to warnings.

Announce each step clearly before executing it.

Phase 1 — Pre-flight Checks

1.1 — Verify branch

Run git branch --show-current and confirm the output is exactly main.

  • If the branch is not main: print "❌ ERROR: Release must be run from the 'main' branch. Current branch: <branch>" and STOP.
  • If on main: print "✅ Branch: main" and continue.

1.2 — Verify clean working tree and no unpushed commits

Run git status --porcelain and check the output.

  • If there is any output (uncommitted or untracked changes): print "❌ ERROR: Working tree is not clean. Please commit or stash all changes before releasing." followed by the output, and STOP.
  • If the output is empty: print "✅ Working tree: clean".

Then run git fetch origin main --quiet followed by git rev-list HEAD..origin/main --count and git rev-list origin/main..HEAD --count.

  • If the local branch is behind remote (first count > 0): print "❌ ERROR: Local 'main' is behind 'origin/main'. Please pull before releasing." and STOP.
  • If the local branch is ahead of remote (second count > 0): print "❌ ERROR: Local 'main' has unpushed commits. Please push before releasing." and STOP.
  • If both counts are 0: print "✅ Branch is up to date with origin/main".

1.3 — Verify repository root

Confirm that build.gradle.kts, gradle.properties, and VERSION exist in the current working directory.

  • If any is missing: print "❌ ERROR: Not at the repository root. Please cd to the Tank Royale repository root." and STOP.

1.4 — Read and display version

Read VERSION and extract the X.Y.Z value. VERSION is the authoritative version source used by the Gradle build. Parse the major, minor, and patch components.

Print:

📋 Release version: X.Y.Z
   Major: X | Minor: Y | Patch: Z

Determine the release type:

  • If patch is 0 → this is a major/minor release (documentation will be uploaded)
  • If patch > 0 → this is a patch release (documentation upload will be skipped)

Print the release type:

  • "📋 Release type: Major/Minor release (documentation will be uploaded)" — or —
  • "📋 Release type: Patch release (documentation upload will be skipped)"

1.5 — Verify the dated CHANGELOG.md release entry

Read CHANGELOG.md and find the first version heading (the topmost line matching ## [X.Y.Z]). It must use this exact format:

## [X.Y.Z] - YYYY-MM-DD - Short release title
  • If the version in that heading does not match the version from VERSION, its date is not a valid YYYY-MM-DD calendar date, its title is empty, or it contains Unreleased: print "❌ ERROR: CHANGELOG.md must start with a dated release entry for X.Y.Z: ## [X.Y.Z] - YYYY-MM-DD - Short release title. Replace Unreleased before publishing." and STOP.
  • If it meets these requirements: print "✅ CHANGELOG.md has a dated release entry for X.Y.Z".

1.6 — Detect platform

Determine whether you are running on Windows or Unix/macOS. This affects the Gradle wrapper command:

  • Windows: use .\gradlew.bat
  • Unix/macOS: use ./gradlew

Print: "📋 Platform: Windows" or "📋 Platform: Unix/macOS"


Phase 2 — Validate Credentials

Before publishing anything, verify that all required credentials are present. This avoids long builds that fail due to missing secrets. Check all credentials and report results together — do not stop at the first failure.

Determine the user Gradle properties file path:

  • Windows: %USERPROFILE%\.gradle\gradle.properties
  • Unix/macOS: ~/.gradle/gradle.properties

Read both the project gradle.properties (in repo root) and the user file (above). User-level values override project-level values.

2.1 — Maven Central credentials

Look for these four properties (in user gradle.properties, falling back to project gradle.properties):

  • ossrhUsername — must be present and not empty
  • ossrhPassword — must be present and not empty
  • signingKey — must be present, not empty, and not the dummy value -----BEGIN PGP PRIVATE KEY BLOCK-----...
  • signingPassword — must be present and not dummy

2.2 — NuGet credentials

NuGet publishing uses Trusted Publishing (OIDC) via the publish-nuget.yml GitHub Actions workflow — there is no local credential to check. Readiness depends only on gh CLI authentication (checked in 2.5) and the publish-nuget.yml workflow existing on main.

2.3 — PyPI credentials

PyPI credentials can come from any one of three sources. Check in order:

  1. Gradle property pypiToken (would be passed via -PpypiToken=... or in gradle.properties)
  2. Environment variable PYPI_TOKEN
  3. A ~/.pypirc file containing a [pypi] section

At least one of these must be available.

2.4 — npmjs credentials

Look for npmjs-api-key in the user gradle.properties (falling back to project gradle.properties).

  • Must be present, not empty, and not dummy

2.5 — GitHub CLI authentication

Run gh auth status and check the exit code.

  • If it succeeds (exit code 0): the user is authenticated.
  • If gh is not installed: note it (the release can still proceed with manual fallback in Phase 3).
  • If gh is installed but not authenticated: flag as missing.

2.6 — Report results

Print a credential summary:

🔑 Credential check:
  ✅ Maven Central  — ossrhUsername, ossrhPassword, signingKey, signingPassword
  ✅ NuGet          — publish-nuget.yml workflow (Trusted Publishing, no local credential)
  ✅ PyPI           — pypiToken (or PYPI_TOKEN env var, or ~/.pypirc)
  ✅ npmjs          — npmjs-api-key
  ✅ GitHub CLI     — authenticated as <username>

If any credential is missing or invalid, show ❌ for that line with a description of what's missing, then print: "❌ ERROR: Missing credentials. See release.md → Credentials setup for details." and STOP.


Phase 3 — Publish Artifacts

Step 1 of 4 — Publish Java artifacts to Maven Central

Print: "📦 Step 1/4: Publishing Java artifacts to Maven Central..."

Run the Gradle command (use the platform-appropriate wrapper):

./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
  • If the command succeeds: print "✅ Step 1/4: Java artifacts published to Maven Central".
  • If the command fails: print "❌ ERROR: Step 1/4 failed — Java publish to Maven Central failed" and STOP.

Step 2 of 4 — Publish .NET package to NuGet

Print: "📦 Step 2/4: Publishing .NET package to NuGet..."

NuGet publishing runs via Trusted Publishing (OIDC) inside the publish-nuget.yml GitHub Actions workflow — it cannot be pushed from a local machine. Trigger the workflow and wait for it to complete (unlike the fire-and-forget trigger in Phase 3.5, this step must block and check the result, since a NuGet failure must STOP the release):

gh workflow run publish-nuget.yml --ref main -R robocode-dev/tank-royale -f version=X.Y.Z

Then poll for the run this dispatch created and wait for it to finish, e.g.:

gh run list --workflow=publish-nuget.yml -R robocode-dev/tank-royale --limit 1 --json databaseId --jq ".[0].databaseId"
gh run watch <databaseId> -R robocode-dev/tank-royale --exit-status

Note: the workflow requires manual approval via its nuget-publish GitHub Environment — gh run watch will show the run waiting; the maintainer must approve it in the GitHub Actions UI before it proceeds.

  • If gh run watch --exit-status succeeds: print "✅ Step 2/4: .NET package published to NuGet".
  • If it fails (non-zero exit): print "❌ ERROR: Step 2/4 failed — .NET publish to NuGet failed" and STOP.

Step 3 of 4 — Publish Python package to PyPI

Print: "📦 Step 3/4: Publishing Python package to PyPI..."

Run the Gradle command (use the platform-appropriate wrapper):

./gradlew :bot-api:python:upload-pypi
  • If the command succeeds: print "✅ Step 3/4: Python package published to PyPI".
  • If the command fails: print "❌ ERROR: Step 3/4 failed — Python publish to PyPI failed" and STOP.

Step 4 of 4 — Publish TypeScript package to npmjs

Print: "📦 Step 4/4: Publishing TypeScript package to npmjs..."

Run the Gradle command (use the platform-appropriate wrapper):

./gradlew :bot-api:typescript:npmPublish
  • If the command succeeds: print "✅ Step 4/4: TypeScript package published to npmjs".
  • If the command fails: print "❌ ERROR: Step 4/4 failed — TypeScript publish to npmjs failed" and STOP.

Phase 3.5 — Trigger Publish Verification

Print: "🔍 Step 3.5: Triggering verify-publish workflow to monitor artifact availability..."

Read the version from VERSION file (same value used in Phase 1).

If gh is available, run:

gh workflow run verify-publish.yml --ref main -R robocode-dev/tank-royale -f version=X.Y.Z
  • If the command succeeds: print:
    ✅ Step 3.5: verify-publish workflow triggered — monitor registry availability at:
       https://github.com/robocode-dev/tank-royale/actions/workflows/verify-publish.yml
    
  • If the command fails: print a warning but do not stop — this step is informational only:
    ⚠️ Step 3.5: Could not trigger verify-publish workflow. You can trigger it manually at:
       https://github.com/robocode-dev/tank-royale/actions/workflows/verify-publish.yml
    

If gh is not available: print the manual URL as a warning and continue.

Note: The workflow runs in the background on GitHub — you do not wait for it here. It shows four parallel jobs (NuGet, PyPI, npmjs, Maven Central) as green/red badges in the Actions UI.


Phase 4 — Create GitHub Release

Print: "🚀 Step 4: Triggering create-release GitHub Actions workflow on main..."

First check if the gh CLI is available by running gh --version.

If gh is available, run:

gh workflow run create-release.yml --ref main -R robocode-dev/tank-royale
  • If the command succeeds: print "✅ Step 4: create-release workflow triggered successfully".
  • If the command fails: print "❌ ERROR: Step 4 failed — could not trigger create-release workflow" and STOP.

If gh is not available:

  • Print "⚠️ GitHub CLI (gh) is not installed. Please trigger the workflow manually:"
  • Print " https://github.com/robocode-dev/tank-royale/actions/workflows/create-release.yml"
  • Print " Click 'Run workflow' → select 'main' branch → click 'Run workflow'"
  • Continue to the next step (this is the only step that allows a manual fallback).

Phase 5 — Generate Documentation (Conditional)

Check the patch version from Phase 1.

If patch is 0 (major/minor release):

Print: "📚 Step 5: Generating documentation for verification (major/minor release — patch is 0)..."

Run the Gradle command (use the platform-appropriate wrapper):

./gradlew upload-docs
  • If the command fails: print "❌ ERROR: Step 5 failed — documentation generation failed" and STOP.

upload-docs creates a local Pages artifact for verification. The generated site is intentionally not tracked; .github/workflows/deploy-docs.yml builds and publishes it from accepted main.

  • Do not stage, commit, or push generated documentation from this workflow.
  • If the command succeeds: report that documentation was generated successfully and that deploy-docs will build and publish it from main.

If patch > 0 (patch release):

Print: "ℹ️ Step 5: Skipping documentation upload (patch release — patch version is Z, not 0)"


Phase 6 — Release Summary

Print a summary of the release:

========================================
🎉 Release X.Y.Z — Complete!
========================================

Published artifacts:
  ✅ Maven Central  — Java artifacts
  ✅ NuGet          — .NET package
  ✅ PyPI           — Python package
  ✅ npmjs          — TypeScript package

GitHub release:
  ✅ create-release workflow triggered (or manual fallback)

Publish verification:
  ✅ verify-publish workflow running — https://github.com/robocode-dev/tank-royale/actions/workflows/verify-publish.yml
     (NuGet, PyPI, and npmjs: ~seconds to minutes | Maven Central: up to 2 hours)

Documentation:
  ✅ Generated locally; deploy-docs publishes from main (or ℹ️ Skipped for patch release)

Next steps:
  1. Monitor the create-release workflow: https://github.com/robocode-dev/tank-royale/actions/workflows/create-release.yml
  2. Once the workflow completes, review the draft release on GitHub
  3. Verify platform installers are attached (Windows, Linux, macOS)
  4. Publish the release when ready
========================================

Error Handling — Rules

These rules are non-negotiable and override any other behavior:

  1. Every command executed must have its exit code checked
  2. Non-zero exit code = ERROR — print the error and STOP immediately
  3. Never continue to the next step after a failure
  4. Never downgrade an error to a warning
  5. Always print which step failed with its step number and description
  6. Partial releases are expected — if step 2 fails, step 1 artifacts are already published. This is normal and acceptable. Do not attempt rollback.

Signals

GitHub stars
267
Forks
57
Last commit
Sep 2026
Hacker News mentions
20
Advanced
Catalog kind
skill
Gateway key
release-robocode-dev
Source
github.com/robocode-dev/tank-royale