Freenet Release Skill
Overview
This skill orchestrates a complete Freenet release. It determines the next version, shows what's changed since the last release, confirms with the user, and runs the automated release pipeline.
Arguments
- If an argument is provided (e.g.,
/release 0.1.133), use that as the target version - If no argument is provided, auto-detect the next patch version
Step 1: Determine Current State
First, pull the latest changes from origin. Without this, you may see zero commits since the last tag and incorrectly conclude there's nothing to release.
# Pull latest — MUST do this before anything else
git pull origin main
# Get current version from Cargo.toml
grep "^version" crates/core/Cargo.toml | cut -d'"' -f2
# Get the last release tag
git describe --tags --abbrev=0
# Get commits since last release
git log --oneline $(git describe --tags --abbrev=0)..HEAD
Auto-version logic: If no version argument was provided, increment the patch version of the current version (e.g., 0.1.132 -> 0.1.133).
Step 2: Show Changelog and Confirm
Present the user with:
- Current version and target version
- Commits since last release (categorized by conventional commit type)
- fdev version that will be auto-incremented
Then ask the user to confirm before proceeding.
Step 3: Pre-flight Checks
Before running the release script, verify:
# Must be on main branch (or a release worktree)
git branch --show-current
# Must have clean working directory
git status --porcelain # Should be empty
# Must be up to date with origin
git fetch origin main
git rev-parse HEAD # Compare with:
git rev-parse origin/main
If any check fails, inform the user and stop.
Optional: Create a Release Worktree
To avoid disrupting the main worktree:
cd ~/code/freenet/freenet-core/main # or wherever main worktree is
git worktree add -b release-work ../release-X.Y.Z
cd ../release-X.Y.Z
Clean up after release:
git worktree remove ../release-X.Y.Z
git branch -d release-work
Step 4: Run the Release
CRITICAL: You MUST run release.sh as a single command. Do NOT manually execute individual release steps (gh release create, cargo publish, etc.). The script handles draft releases, binary waits, and publish ordering that prevent users from seeing a version before its binaries exist. Doing steps manually caused a user-facing 404 during v0.1.177.
Execute the release script:
./scripts/release.sh --version <VERSION> [--skip-tests]
The script handles the entire pipeline:
- Version bump - Updates
crates/core/Cargo.tomlandcrates/fdev/Cargo.toml - Release PR - Creates a branch, commits, pushes, opens PR with auto-merge
- Wait for CI - Monitors GitHub CI on the release PR (up to 30 min)
- Publish crates - Publishes
freenetthenfdevto crates.io - GitHub Release - Creates tag, generates release notes, creates draft release
- Cross-compile - Triggered automatically by the tag push
- Wait for binaries - Waits for cross-compile to attach binaries to the release
- Publish draft - Publishes the draft release only after binaries are attached
- Gateway updates - SSHes into all gateways and triggers immediate update
- Announcements - Matrix and River notifications (if tools available)
Important Options
--skip-tests- Skip local pre-release tests (CI still runs on the PR)--dry-run- Show what would be done without executing
Resumability
The release script is resumable. If it fails partway through, re-running with the same --version will auto-detect completed steps and skip them. State is saved to /tmp/release-<VERSION>.state.
You can also resume explicitly: ./scripts/release.sh --resume /tmp/release-<VERSION>.state
Branch Safety
The script automatically restores the original git branch on exit (success or failure). If the script is interrupted, your working directory won't be left on the release/v* branch.
CI Wait Behavior
- Main CI check: If main branch CI is still running when the script starts, it polls every 30s (up to 10 min) instead of exiting immediately
- PR merge wait: Polls every 30s (up to 30 min) for the PR to pass CI and auto-merge
- Merge queue optimization: Release PRs skip expensive tests (Unit & Integration, Simulation, NAT Validation) in the merge queue since main CI already validated the code. This reduces merge queue time from ~6 min to ~1 min.
Step 5: Handle Common Issues
PR title / Conventional Commits check fails: Release PRs must use "build:" prefix (not "chore:"). The commit-msg hook only allows: feat, fix, docs, style, refactor, perf, test, build, ci.
gh api repos/freenet/freenet-core/pulls/XXXX --method PATCH -f title="build: release X.Y.Z"
Auto-merge not triggering:
- GitHub auto-merge can take 5-10 minutes after checks pass
- The script waits up to 30 minutes (showing progress every 30s)
- Release PRs skip expensive tests in the merge queue (commit message detection), so merge queue should complete in ~1 min
- You can manually merge the PR — the script detects manual merges and continues
Test failures: Check CI logs. Either fix the issue or inform the user and ask how to proceed.
Step 6: Verify Release Artifacts
CRITICAL: Do NOT announce until cross-compile binaries are available. The Build and Cross-Compile workflow triggers on tag push and takes ~15-20 min to build binaries for all platforms. Gateway auto-update depends on these binaries.
# Check crates.io publication
cargo search freenet --limit 1
# Check GitHub release exists
gh release view v<VERSION>
# Verify binaries are attached
gh release view v<VERSION> --json assets --jq '.assets[].name'
# Should show: freenet-x86_64-unknown-linux-musl.tar.gz, freenet-aarch64-unknown-linux-musl.tar.gz, etc.
# Monitor cross-compile workflow if binaries not yet available
gh run list --workflow=cross-compile.yml --limit 3
Step 7: Announcements
Only after binaries are confirmed available. Use the matrix-comms and river-official-room skills for detailed instructions on each platform.
Announcement content: Write a 1-3 sentence Markdown summary of the key changes in this release, followed by a link to the GitHub release for full details. Both Matrix and River support Markdown formatting.
Example format:
**Freenet v0.1.177 released.** Transient WebSocket errors no longer kill the client slot permanently. See [release notes](https://github.com/freenet/freenet-core/releases/tag/v0.1.177) for details.
Matrix (#freenet-locutus channel) — use the matrix-comms skill:
# -z flag is REQUIRED for Markdown rendering (bold, links, etc.)
timeout 30 matrix-commander -z -r "!ygHfYcXtXmivTbOwjX:matrix.org" -m "announcement text"
River (Freenet Official room) — use the river-official-room skill:
# Ensure Room Owner identity is restored first (see river-official-room skill)
cd /home/ian/code/freenet/river/main
cargo run -p riverctl -- message send 69Ht4YjZsT884MndR2uWhQYe1wb9b2x77HRq7Dgq7wYE "announcement text"
Step 8: Post-Release Verification
A release is NOT complete until the network is verified healthy.
Wait 10-15 minutes for gateways to auto-update, then verify:
- Gateway versions updated — Check that gateways are running the new version
- Gateway logs clean — No new errors, warnings, panics, or log spam
- Network health — Peers connecting, contracts propagating, subscriptions working
If you have access to gateway machines, check logs directly. If you have access to telemetry, monitor network-wide health. The specific verification steps depend on your access level — see your local environment's release skill for machine-specific commands.
What to look for:
- Log spam — Same message repeating hundreds of times (can fill disks within hours)
- Rapid log growth — Normal is ~1MB/hour; much faster indicates a problem
- New error patterns — Errors not present before the release
- Connection failures — "connection refused", "timeout", "handshake failed"
- Resource issues — "out of memory", "no space left", "too many open files"
If critical issues found: Roll back immediately, create GitHub issue, announce rollback.
Rollback
If a release needs to be rolled back:
# Rollback (keeps crates.io versions)
./scripts/release-rollback.sh --version <VERSION>
# Rollback and yank from crates.io (irreversible!)
./scripts/release-rollback.sh --version <VERSION> --yank-crates
# Dry run
./scripts/release-rollback.sh --version <VERSION> --dry-run
Version Scheme
- freenet:
0.1.X- patch incremented each release - fdev:
0.3.X- patch auto-incremented by release script (independent versioning)
Incident Learnings
These are real issues from past releases that the release process has been hardened against:
- "Text file busy" during deployment — Deploy script now disables systemd auto-restart, waits for binary release, re-enables after
- PR title must use "build:" prefix — Changed from "chore:" to comply with commit-msg hook
- Matrix announcements can hang — matrix-send wrapper has 20s timeout and 3 retries; matrix-commander handles E2E encryption that raw curl cannot
- PATH shadowing — Old
cargo install freenetmay leave stale binary at~/.cargo/bin/freenetshadowing/usr/local/bin/freenet; always use absolute paths when verifying versions - Binary vs running process mismatch — Deploying a new binary doesn't mean the service is running it; verify via
systemctl show -p MainPID+/proc/PID/exe, not just binary on disk - Don't announce before binaries exist — Cross-compile takes 15-20 min; gateway auto-update (especially aarch64) depends on release binaries being attached
- Log spam can fill disks — Always review logs 10-15 min after release; previous releases introduced logging that consumed disk space within hours
- Merge queue ran full CI for release PRs —
github.head_refis a queue branch in merge_group events, not the PR branch. Fixed by detecting release PRs via commit message (build: release*) and skipping expensive test steps - Script left user on release branch — Added EXIT trap to restore original branch on any exit
Gateway Updates
The release script automatically SSHes into all known gateways and triggers gateway-auto-update.sh --force immediately after cross-compile binaries are available. This eliminates the 10-minute polling delay that previously caused version mismatch issues (users installing the new version before gateways updated).
Gateways also have a 10-minute polling timer as a fallback. Peers self-update when they detect a version mismatch with the gateway (exit code 42), which triggers freenet update automatically.
Success Criteria
Release is complete when:
- ✓ PR merged to main
- ✓ Published to crates.io
- ✓ GitHub release created with tag and binaries attached
- ✓ Gateways updated to new version
- ✓ Matrix announcement sent
- ✓ River announcement sent
- ✓ Network verified healthy post-release (logs clean, telemetry normal)