# Justfile for serde-structprop
# Install just: https://just.systems
# Default: list available recipes
default:
@just --list
# Run the full test suite
test:
cargo test
# Check formatting and lints (mirrors CI)
check:
cargo fmt --check
cargo clippy --all-targets -- -W clippy::pedantic
# Auto-format the source
fmt:
cargo fmt
# Validate conventional commit history
commits:
cog check
# ---------------------------------------------------------------------------
# Release
# ---------------------------------------------------------------------------
#
# Two-step process to work with branch protection on main:
#
# Step 1 — just release
# Runs pre-flight checks, calls `cog bump --auto` on a local release
# branch, opens a PR. Review and merge the PR normally.
#
# Step 2 — just push-tag
# After the PR is merged, pulls main, tags the merge commit with the
# version from Cargo.toml, and pushes the tag.
# The tag push triggers release.yml which publishes to crates.io.
#
# Prerequisites:
# - `cocogitto` installed: cargo install cocogitto
# - `gh` CLI installed and authenticated: https://cli.github.com
# ---------------------------------------------------------------------------
# Step 1: open a release PR.
#
# Runs pre-flight checks, bumps the version on a release branch,
# generates CHANGELOG.md, and opens a pull request against main.
# After the PR is merged, run `just push-tag` to trigger the publish.
release:
#!/usr/bin/env bash
set -euo pipefail
# Guard: must be on main.
branch=$(git rev-parse --abbrev-ref HEAD)
if [[ "$branch" != "main" ]]; then
echo "error: releases must be cut from main (currently on '$branch')" >&2
exit 1
fi
# Guard: working tree must be clean.
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "error: working tree has uncommitted changes; commit or stash them first" >&2
exit 1
fi
# Guard: local main must not be behind origin.
git fetch --quiet origin main
if [[ $(git rev-list --count HEAD..origin/main) -gt 0 ]]; then
echo "error: local main is behind origin/main; run 'git pull' first" >&2
exit 1
fi
# Run the full test suite before touching anything.
echo "==> Running tests..."
cargo test
# Check formatting and lints.
echo "==> Checking formatting and lints..."
cargo fmt --check
cargo clippy --all-targets -- -W clippy::pedantic
# Determine the next version without making any changes yet.
# cog bump --auto --dry-run prints e.g. "v0.1.0" to stdout.
echo "==> Determining next version..."
next_version=$(cog bump --auto --dry-run)
echo " Next version: ${next_version}"
# Create and switch to a release branch.
release_branch="release/${next_version}"
git checkout -b "${release_branch}"
# Bump version, generate CHANGELOG.md, commit, and create the local tag.
# cog bump --auto:
# - Updates the version field in Cargo.toml
# - Writes CHANGELOG.md
# - Creates a commit "chore(version): bump to vX.Y.Z"
# - Creates an annotated tag vX.Y.Z (local only until push-tag)
echo "==> Bumping version with cog..."
cog bump --auto
# Push the release branch (not the tag — that comes after PR merge).
echo "==> Pushing release branch..."
git push -u origin "${release_branch}"
# Open the pull request.
echo "==> Opening pull request..."
gh pr create \
--title "chore(release): ${next_version}" \
--body "$(cat CHANGELOG.md)" \
--base main \
--head "${release_branch}"
echo ""
echo "==> Release PR opened."
echo " Review and merge the PR, then run:"
echo ""
echo " just push-tag"
echo ""
echo " to push the tag and trigger the crates.io publish."
# Step 2: tag main HEAD and push the tag.
#
# Run this after the release PR has been merged into main.
# Pulls the latest main, reads the version from Cargo.toml, creates an
# annotated tag on the current HEAD, and pushes it to trigger release.yml.
push-tag:
#!/usr/bin/env bash
set -euo pipefail
# Must be on main.
branch=$(git rev-parse --abbrev-ref HEAD)
if [[ "$branch" != "main" ]]; then
echo "error: push-tag must be run from main (currently on '$branch')" >&2
exit 1
fi
# Pull so we are at the merge commit.
echo "==> Pulling latest main..."
git pull --ff-only origin main
# Read the version from Cargo.toml.
version=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)".*/\1/')
tag="v${version}"
echo "==> Tagging HEAD as ${tag}..."
# Guard: tag must not already exist on origin.
if git ls-remote --tags origin "${tag}" | grep -q "refs/tags/${tag}$"; then
echo "error: tag ${tag} already exists on origin" >&2
exit 1
fi
# Delete stale local tag if present (leftover from the release branch).
git tag -d "${tag}" 2>/dev/null || true
# Create a fresh annotated tag on the current (merged) HEAD.
git tag -a "${tag}" -m "chore(release): ${tag}"
echo "==> Pushing tag ${tag}..."
git push origin "${tag}"
echo "==> Done. Monitor the release workflow at:"
echo " https://github.com/anthonyoteri/serde-structprop/actions"