cargo-upkeep 0.3.2

Unified Rust project maintenance CLI (cargo subcommand)
set shell := ["bash", "-eu", "-o", "pipefail", "-c"]

default:
  @just --list

help:
  @just --list

# Build the project
build:
  cargo build

# Build release binary
build-release:
  cargo build --release

# Run the CLI with arguments
run *args:
  cargo run -- {{args}}

# Run tests
test:
  cargo test

# Run tests with output
test-verbose:
  cargo test -- --nocapture

# Run tests and watch for changes
test-watch:
  cargo watch -x test

# Run clippy linter
lint:
  cargo clippy --locked --all-targets -- -D warnings

# Run clippy and fix issues
lint-fix:
  cargo clippy --all-targets --fix --allow-dirty --allow-staged

# Format code
format:
  cargo fmt

# Check formatting without changing files
format-check:
  cargo fmt --check

# Run all checks (lint, format, test)
check:
  just format-check
  just lint
  just test

# Full CI pipeline
ci:
  just check
  just build-release

# Clean build artifacts
clean:
  cargo clean

# Generate changelog
changelog:
  git cliff -o CHANGELOG.md

# Preview unreleased changes
changelog-preview:
  git cliff --unreleased

# Bump patch version (0.0.x)
bump-patch:
  just bump-version patch

# Bump minor version (0.x.0)
bump-minor:
  just bump-version minor

# Bump major version (x.0.0)
bump-major:
  just bump-version major

# Write an explicit version to every place it appears
set-version version:
  #!/usr/bin/env bash
  set -euo pipefail
  CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
  NEW="{{version}}"
  # `sed -i.bak` is the portable spelling. Bare `-i ''` is macOS-only and bare `-i`
  # is GNU-only; this recipe runs both locally and on ubuntu in CI.
  sed -i.bak "s/^version = \"$CURRENT\"/version = \"$NEW\"/" Cargo.toml && rm -f Cargo.toml.bak
  # Update only this crate's own lockfile entry. `cargo generate-lockfile` re-resolves
  # the whole graph, which can pull newer transitive deps into a release commit — and
  # resolver v2 is not MSRV-aware, so that can silently break the declared rust-version.
  cargo update --workspace
  # Update skill versions
  for skill in skills/upkeep-rs-*/SKILL.md; do
    sed -i.bak "s/^version: .*/version: $NEW/" "$skill" && rm -f "$skill.bak"
  done
  echo "Set version: $CURRENT -> $NEW"

# Print the next version from conventional commits, capped at minor (stdout = version only)
next-version:
  #!/usr/bin/env bash
  set -euo pipefail
  CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
  NEXT=$(git cliff --bumped-version 2>/dev/null | tail -1 | sed 's/^v//')
  if [ -z "$NEXT" ]; then
    echo "could not compute a next version from git cliff" >&2
    exit 1
  fi
  IFS='.' read -r cmaj cmin _ <<< "$CURRENT"
  IFS='.' read -r nmaj _ _ <<< "$NEXT"
  # Never emit a major bump automatically. While the crate is 0.x this is also
  # semantically right, because cargo already treats a 0.x minor bump as breaking.
  if [ "$nmaj" -gt "$cmaj" ]; then
    NEXT="$cmaj.$((cmin + 1)).0"
    echo "warning: commits imply a major bump; capping at minor -> $NEXT" >&2
    if [ "$cmaj" -ne 0 ]; then
      echo "warning: the crate is past 1.0, so this cap now understates a breaking change" >&2
    fi
  fi
  echo "$NEXT"

# Bump the version from conventional commits, capped at minor
bump-auto:
  #!/usr/bin/env bash
  set -euo pipefail
  CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
  NEXT=$(just next-version)
  if [ "$NEXT" = "$CURRENT" ]; then
    echo "No releasable commits since v$CURRENT; nothing to bump."
    exit 0
  fi
  just set-version "$NEXT"

# Bump version by type (patch, minor, major)
bump-version bump:
  #!/usr/bin/env bash
  set -euo pipefail
  CURRENT=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
  IFS='.' read -r major minor patch <<< "$CURRENT"
  case "{{bump}}" in
    patch) patch=$((patch + 1)) ;;
    minor) minor=$((minor + 1)); patch=0 ;;
    major) major=$((major + 1)); minor=0; patch=0 ;;
    *) echo "Invalid bump type: {{bump}}"; exit 1 ;;
  esac
  just set-version "$major.$minor.$patch"

# Commit version bump and create tag
commit-version:
  #!/usr/bin/env bash
  set -euo pipefail
  VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
  git add Cargo.toml Cargo.lock skills/upkeep-rs-*/SKILL.md
  git commit -m "chore(release): bump version to v$VERSION"
  git tag "v$VERSION"
  echo "Created tag v$VERSION"
  echo "Push with: git push origin main --tags"

# Show current version
show-version:
  @grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/'

# Install the CLI locally
install:
  cargo install --path .

# Uninstall the CLI
uninstall:
  cargo uninstall cargo-upkeep

# Run security audit
audit:
  cargo upkeep audit

# Check outdated dependencies
deps:
  cargo upkeep deps

# Run quality check
quality:
  cargo upkeep quality