Skip to content

Release Prep

Release Prep #8

Workflow file for this run

# Opens a release PR: bumps the version in Cargo.toml, regenerates Cargo.lock,
# and drafts a CHANGELOG entry from the PRs merged since the last release tag
# (via .github/scripts/generate-litep2p-changelog.sh). Triggered manually from
# the Actions tab.
#
# After this workflow runs:
# 1. Edit CHANGELOG.md on the release branch: sort the auto-generated entries
# into Added / Changed / Fixed and add a summary paragraph, then commit the
# result to the PR.
# 2. Merge the PR.
# 3. Run the Release Publish workflow with the same version to tag master and
# publish (see RELEASING.md).
#
# Re-running: this workflow only CREATES the release branch. If the branch
# already exists on origin it fails fast, because someone may have pushed
# manual edits to it (reviewer CHANGELOG fixes, etc.). To iterate on a pending
# release, edit that branch / its PR directly rather than re-running. To
# regenerate from master, delete the branch first
# (git push --delete origin release-vX.Y.Z) and re-run.
#
# Known limitation: GitHub does not trigger `pull_request` workflows on PRs
# opened by GITHUB_TOKEN. If you need CI to run on the release PR, push an
# empty commit to the branch after this workflow finishes, or replace
# GITHUB_TOKEN below with a PAT / GitHub App token stored as a secret.
name: Release Prep
on:
workflow_dispatch:
inputs:
version:
description: "New version (e.g. 0.15.0, no leading 'v')"
required: true
type: string
concurrency:
group: release-prep
cancel-in-progress: false
permissions:
contents: read
jobs:
prepare:
name: Prepare release PR
runs-on: ubuntu-latest
timeout-minutes: 20
container:
image: paritytech/ci-unified:bullseye-1.93.0-2026-01-27-v202605151311
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout master
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: master
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: Validate version input
id: validate
shell: bash
env:
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Version '$VERSION' is not a valid semver X.Y.Z"
exit 1
fi
CURRENT="$(grep -m1 -E '^version = "' Cargo.toml | sed -E 's/version = "(.*)"/\1/')"
echo "Current version: $CURRENT"
echo "New version: $VERSION"
if [ "$VERSION" = "$CURRENT" ]; then
echo "::error::New version equals current version ($CURRENT)"
exit 1
fi
higher=$(printf '%s\n%s\n' "$CURRENT" "$VERSION" | sort -V | tail -1)
if [ "$higher" != "$VERSION" ]; then
echo "::error::New version ($VERSION) is not greater than current ($CURRENT)"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "branch=release-v$VERSION" >> "$GITHUB_OUTPUT"
- name: Configure git identity
shell: bash
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Check release branch does not already exist
shell: bash
env:
BRANCH: ${{ steps.validate.outputs.branch }}
run: |
set -euo pipefail
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
echo "::error::Branch $BRANCH already exists on origin. To iterate on the pending release, push to that branch or edit its PR directly. To regenerate from master, delete the branch first: git push --delete origin $BRANCH"
exit 1
fi
echo "Branch $BRANCH does not exist on origin; it will be created."
- name: Bump version in Cargo.toml
env:
NEW_VERSION: ${{ steps.validate.outputs.version }}
shell: bash
run: |
set -euo pipefail
awk -v new="$NEW_VERSION" '
!done && /^version = "/ { sub(/"[^"]+"/, "\""new"\""); done=1 }
{ print }
' Cargo.toml > Cargo.toml.new
mv Cargo.toml.new Cargo.toml
echo "Diff of Cargo.toml after version bump:"
git --no-pager diff -- Cargo.toml
- name: Regenerate Cargo.lock
run: cargo generate-lockfile
- name: Draft CHANGELOG entry
env:
NEW_VERSION: ${{ steps.validate.outputs.version }}
shell: bash
run: |
set -euo pipefail
TODAY=$(date -u +%Y-%m-%d)
# Make sure origin/master and tags are present for the changelog script.
git fetch --tags --force origin master >/dev/null 2>&1 || true
# Generate the PR list with the shared script (strip its "[+]" log lines).
CHANGELOG_LIST=$(bash .github/scripts/generate-litep2p-changelog.sh 2>/dev/null | grep '^- ' || true)
if [ -z "$CHANGELOG_LIST" ]; then
CHANGELOG_LIST="_No merged PRs found since the last release tag; add entries manually._"
fi
# Build the new section (flat list; the reviewer sorts it into categories).
# Saved under $RUNNER_TEMP (outside the repo) so the PR-body step can
# reuse it without leaving an untracked file that would make
# `cargo publish --dry-run` see a dirty working tree.
{
echo "## [$NEW_VERSION] - $TODAY"
echo
echo "$CHANGELOG_LIST"
echo
} > "$RUNNER_TEMP/release-section.md"
# Prepend it before the first existing version heading in CHANGELOG.md.
FIRST_HEADING_LINE=$(grep -nE '^## \[' CHANGELOG.md | head -1 | cut -d: -f1)
if [ -z "$FIRST_HEADING_LINE" ]; then
echo "::error::No existing '## [...]' heading found in CHANGELOG.md; cannot determine insertion point."
exit 1
fi
head -n $((FIRST_HEADING_LINE - 1)) CHANGELOG.md > CHANGELOG.new
cat "$RUNNER_TEMP/release-section.md" >> CHANGELOG.new
tail -n +"$FIRST_HEADING_LINE" CHANGELOG.md >> CHANGELOG.new
mv CHANGELOG.new CHANGELOG.md
- name: Commit release changes
env:
BRANCH: ${{ steps.validate.outputs.branch }}
VERSION: ${{ steps.validate.outputs.version }}
shell: bash
run: |
set -euo pipefail
git checkout -b "$BRANCH"
git add Cargo.toml Cargo.lock CHANGELOG.md
git commit -m "chore: Release litep2p v$VERSION"
- name: Verify crate publishes cleanly (cargo publish --dry-run)
run: cargo publish --dry-run
- name: Push release branch (as a verified commit)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: ${{ steps.validate.outputs.branch }}
shell: bash
run: |
set -euo pipefail
REPO="$GITHUB_REPOSITORY"
# Push the locally-created (unsigned) commit first so its tree and
# blobs exist on the server, then replace it with an equivalent commit
# created through the GitHub API. API commits made with GITHUB_TOKEN
# are signed by GitHub's web-flow key and show as "Verified", which is
# what the release process requires -- this removes the manual
# re-sign-and-force-push step that was needed for the unsigned
# github-actions[bot] commit.
git push origin "$BRANCH"
UNSIGNED_SHA=$(git rev-parse HEAD)
MESSAGE=$(git log -1 --pretty=%B "$UNSIGNED_SHA")
TREE=$(gh api "repos/$REPO/git/commits/$UNSIGNED_SHA" -q '.tree.sha')
PARENT=$(gh api "repos/$REPO/git/commits/$UNSIGNED_SHA" -q '.parents[0].sha')
# Author/committer default to the token identity (github-actions[bot]);
# the commit is signed server-side.
SIGNED_SHA=$(gh api "repos/$REPO/git/commits" \
-f message="$MESSAGE" \
-f tree="$TREE" \
-f "parents[]=$PARENT" \
-q '.sha')
# Move the branch to the signed commit. This is not a fast-forward
# (same parent, different SHA), so force is required.
gh api -X PATCH "repos/$REPO/git/refs/heads/$BRANCH" \
-f sha="$SIGNED_SHA" \
-F force=true >/dev/null
echo "Branch $BRANCH now points at verified commit $SIGNED_SHA"
echo "Verification status:"
gh api "repos/$REPO/git/commits/$SIGNED_SHA" -q '.verification'
- name: Open release PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.validate.outputs.version }}
BRANCH: ${{ steps.validate.outputs.branch }}
shell: bash
run: |
set -euo pipefail
# Start the body with the generated changelog section, then append guidance.
# Kept under $RUNNER_TEMP (outside the repo) to avoid dirtying the tree.
cp "$RUNNER_TEMP/release-section.md" "$RUNNER_TEMP/pr-body.md"
cat >> "$RUNNER_TEMP/pr-body.md" <<EOF
---
> **Reviewer action required:** the changelog entries above are auto-generated from merged PRs and are **unsorted**. Please edit \`CHANGELOG.md\` on this branch to:
> - Group the entries under \`### Added\` / \`### Changed\` / \`### Fixed\`.
> - Add a short summary paragraph at the top of the \`## [$VERSION]\` section.
>
> Then **commit the adjusted \`CHANGELOG.md\` to this PR**.
**After merging:** run the **Release Publish** workflow from the Actions tab with version \`$VERSION\` to tag \`master\` and publish to crates.io. See \`RELEASING.md\`.
---
> _CI note: GitHub does not run \`pull_request\` workflows on PRs opened by \`GITHUB_TOKEN\`. If CI hasn't started on this PR, push an empty commit to trigger it:_
> \`\`\`bash
> git fetch origin && git checkout $BRANCH
> git commit --allow-empty -m "trigger ci" && git push
> \`\`\`
EOF
PR_URL=$(gh pr create \
--base master \
--head "$BRANCH" \
--title "chore: Release litep2p v$VERSION" \
--body-file "$RUNNER_TEMP/pr-body.md")
echo "Opened PR: $PR_URL"
echo "## Release PR opened" >> "$GITHUB_STEP_SUMMARY"
echo "$PR_URL" >> "$GITHUB_STEP_SUMMARY"