amber-api 2.1.0

Rust client for Amber Electric's API
Documentation
#!/bin/bash
set -euo pipefail

################################################################################
## Prepare Release
##
## Prepares a release by:
## - Determining the next version (or using provided version)
## - Updating Cargo.toml with new version
## - Generating/updating CHANGELOG.md with git-cliff
## - Committing the changes
## - Pushing release branch to origin (in CI)
##
## This script is designed to be called by CI but can also be run locally.
##
## Usage:
##   ./prepare-release [--dry-run] [VERSION]
##
## Arguments:
##   --dry-run    Update files but don't commit/push (easily reverted with git)
##   VERSION      Override automatic version detection (optional)
##
## Outputs (GITHUB_OUTPUT in CI):
##   skip         true if release should be skipped, empty otherwise
##   reason       Human-readable reason for skipping (if skipped)
##   version      The version being prepared (if not skipped)
##
## Exit Codes:
##   0 - Success (prepared or skipped)
##   255 - Command failure
################################################################################

# Source CI utilities
# shellcheck source=scripts/ci/lib.sh
source "$(dirname "$0")/lib.sh"
# shellcheck source=scripts/version.sh
source "${CI_REPO_ROOT}/scripts/version.sh"

################################################################################
## Configuration
################################################################################

DRY_RUN=false
OVERRIDE_VERSION=""

# Parse arguments
while [ $# -gt 0 ]; do
  case "$1" in
  --dry-run)
    DRY_RUN=true
    info "Running in dry-run mode"
    shift
    ;;
  *)
    OVERRIDE_VERSION="$1"
    shift
    ;;
  esac
done

################################################################################
## Validate Environment
################################################################################

info "Validating environment"

# Assert required commands
assert_cmd git-cliff
assert_cmd cargo
assert_cmd git
assert_cmd sed

# Check git status
if [ -n "$(git status --porcelain)" ]; then
  warn "Working directory has uncommitted changes"
  if [ "$DRY_RUN" = false ]; then
    err "Cannot prepare release with uncommitted changes"
  fi
fi

################################################################################
## Check if HEAD is a release commit
################################################################################

# If the current HEAD commit is a release commit, skip creating a new release
HEAD_COMMIT_MSG=$(git log -1 --pretty=format:"%s" HEAD)
if [[ $HEAD_COMMIT_MSG =~ ^chore\(release\): ]]; then
  info "HEAD commit is a release commit: $HEAD_COMMIT_MSG"
  ci_append_output "skip=true" "reason=HEAD is already a release commit"
  exit 0
fi

################################################################################
## Determine Version
################################################################################

CURRENT_VERSION=$(get_current_version)
info "Current version: $CURRENT_VERSION"

if [ -n "$OVERRIDE_VERSION" ]; then
  info "Using override version: $OVERRIDE_VERSION"
  NEXT_VERSION="$OVERRIDE_VERSION"

  if ! validate_semver "$NEXT_VERSION"; then
    err "Invalid version format: $NEXT_VERSION (expected X.Y.Z)"
  fi
else
  info "Determining next version from conventional commits"
  NEXT_VERSION=$(determine_next_version)
fi

# Check if version has changed
if [ "$NEXT_VERSION" = "$CURRENT_VERSION" ]; then
  info "No version change needed (current: $CURRENT_VERSION)"
  ci_append_output "skip=true" "reason=No version change needed"
  exit 0
fi

# Check if version is already tagged
if is_version_tagged "$NEXT_VERSION"; then
  warn "Version $NEXT_VERSION is already tagged"
  ci_append_output "skip=true" "reason=Version already tagged"
  exit 0
fi

info "Next version: $NEXT_VERSION"

################################################################################
## Update Cargo.toml
################################################################################

info "Updating Cargo.toml version"

# Update version in Cargo.toml
# We use sed to replace the version line, preserving whitespace
# Use [[:space:]]* for BSD sed compatibility (macOS)
sed -i.bak "s/^\([[:space:]]*version[[:space:]]*=[[:space:]]*\)\".*\"/\1\"$NEXT_VERSION\"/" Cargo.toml
rm -f Cargo.toml.bak

# Verify the change
UPDATED_VERSION=$(get_current_version)
if [ "$UPDATED_VERSION" != "$NEXT_VERSION" ]; then
  err "Failed to update Cargo.toml version (got $UPDATED_VERSION, expected $NEXT_VERSION)"
fi

info "Updated Cargo.toml: $CURRENT_VERSION$NEXT_VERSION"

################################################################################
## Generate Changelog
################################################################################

info "Generating changelog for v$NEXT_VERSION"

# Generate only the new version's changelog and prepend to existing file
# This preserves any manual edits to previous versions
TAG_NAME=$(format_tag_name "$NEXT_VERSION")

ensure git-cliff --unreleased --tag "$TAG_NAME" --prepend CHANGELOG.md --output CHANGELOG.release.md
echo "<!-- generated by git-cliff on $(date +%Y-%m-%d) -->" >>CHANGELOG.md

info "Prepended v$NEXT_VERSION to CHANGELOG.md"

# Check if CHANGELOG.md was actually modified
# git status --porcelain returns empty if no changes
CHANGELOG_STATUS=$(git status --porcelain CHANGELOG.md)

if [ -z "$CHANGELOG_STATUS" ]; then
  info "No substantive changes to release (CHANGELOG.md unchanged)"
  # Revert Cargo.toml change
  git checkout -- Cargo.toml
  ci_append_output "skip=true" "reason=No substantive changes to release"
  exit 0
else
  info "CHANGELOG.md has substantive changes"
fi

################################################################################
## Generate PR Body
################################################################################

info "Generating PR body"

# Generate PR body file for use with peter-evans/create-pull-request
cat >.github/release-pr-body.md <<EOF
# Release v${NEXT_VERSION}

This PR prepares the release for version ${NEXT_VERSION}.

### Checklist

- [ ] Changelog is accurate and complete
- [ ] Version bump is correct
- [ ] All CI checks pass

> [!WARNING]
>
> This PR is automatically maintained by the release automation. Any manual changes to files will be overwritten when the workflow runs again.
> To make changes, modify the source and push to main, which will trigger an update to this PR.

---

$(cat CHANGELOG.release.md)

---
_This PR is automatically maintained by the release automation._
EOF

info "Generated PR body at .github/release-pr-body.md"

# Output version for CI
ci_append_output "version=$NEXT_VERSION"

info "Files prepared for release v${NEXT_VERSION}"