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., ), use that as the target version
- If no argument is provided, auto-detect the next patch version
Step 1: Determine Current State
Run these commands to gather release context:
bash
# 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.,
->
).
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:
bash
# 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:
bash
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:
bash
git worktree remove ../release-X.Y.Z
git branch -d release-work
Step 4: Run the Release
Execute the release script:
bash
./scripts/release.sh --version <VERSION> [--skip-tests] [--deploy-local]
The script handles the entire pipeline:
- Version bump - Updates and
- 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 then to crates.io
- GitHub Release - Creates tag, generates release notes, creates release
- Cross-compile - Triggered automatically by the tag push
- Wait for binaries - Waits for cross-compile to attach binaries to the release
- Announcements - Matrix and River notifications (if tools available)
Important Options
- - Skip local pre-release tests (CI still runs on the PR)
- - Show what would be done without executing
- - Deploy to local gateway after release
- - Deploy to remote gateways after release
Resumability
The release script is
resumable. If it fails partway through, re-running with the same
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
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.
bash
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)
- 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
workflow triggers on tag push and takes ~15-20 min to build binaries for all platforms. Gateway auto-update depends on these binaries.
bash
# 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:
Matrix (#freenet-locutus channel):
bash
# Use matrix-send wrapper if available (handles E2E encryption, retries, timeouts)
matrix-send -r '!ygHfYcXtXmivTbOwjX:matrix.org' -m "Freenet vX.Y.Z released - [summary]. https://github.com/freenet/freenet-core/releases/tag/vX.Y.Z"
River (Freenet Official room):
bash
riverctl message send 69Ht4YjZsT884MndR2uWhQYe1wb9b2x77HRq7Dgq7wYE "Freenet vX.Y.Z released - [summary]. https://github.com/freenet/freenet-core/releases/tag/vX.Y.Z"
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:
bash
# 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: - patch incremented each release
- fdev: - 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 may leave stale binary at shadowing ; 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
+ , 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
Gateway Auto-Update
Gateways poll GitHub every 10 minutes for new releases and auto-update. This is pull-based (no inbound webhooks needed) and architecture-aware (x86_64/aarch64).
Peers also self-update: when a peer detects a version mismatch with the gateway (exit code 42), systemd triggers
automatically.
Manual trigger (if you have gateway access):
gateway-auto-update.sh --force
Success Criteria
Release is complete when:
- ✓ PR merged to main
- ✓ Published to crates.io
- ✓ GitHub release created with tag and binaries attached
- ✓ Matrix announcement sent
- ✓ River announcement sent
- ✓ Network verified healthy post-release (logs clean, telemetry normal)