#!/bin/bash set -euo pipefail declare -r TAGBRANCH=main declare CURRENTBRANCH="" declare ORIGBRANCH="" declare -r ARG1="${1:-}" declare -r ARG2="${2:-}" declare is_dry_run=false declare release_input="" showHelp() { cat <<'EOF' Usage: makerelease.sh VERSION makerelease.sh +0.0.1 makerelease.sh +0.1 makerelease.sh +1 makerelease.sh --dry-run VERSION makerelease.sh --dry-run +0.0.1 makerelease.sh --dry-run +0.1 makerelease.sh --dry-run +1 Creates an annotated Git tag from the current dev branch. If VERSION starts with +, it is treated as an increment: - +0.0.1 increments patch - +0.1 increments minor - +1 increments major Use --dry-run to show the computed release version without running Git actions. Requirements: - run from a clean Git working tree - current branch must be dev for real releases - main branch must exist locally EOF } cleanup() { if [ -n "${ORIGBRANCH}" ] && [ "${CURRENTBRANCH}" != "${ORIGBRANCH}" ]; then git checkout "${ORIGBRANCH}" >/dev/null 2>&1 || true fi } trap cleanup EXIT if [ "${ARG1}" = "--dry-run" ] || [ "${ARG1}" = "-n" ]; then is_dry_run=true release_input="${ARG2:-}" else release_input="${ARG1}" fi if [ -z "${release_input}" ]; then showHelp echo "" echo "no version provided!" exit 1 fi if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "You are not inside a Git repository!" exit 1 fi if [ -n "$(git status --porcelain)" ]; then echo "Working tree is not clean!" echo "Commit or stash your changes before creating a release." exit 1 fi CURRENTBRANCH=$(git rev-parse --abbrev-ref HEAD) ORIGBRANCH="${CURRENTBRANCH}" if [ "${is_dry_run}" = false ] && [ "${CURRENTBRANCH}" != "dev" ]; then echo "You are not in dev branch!" echo "Use dev branch to make a release!" exit 1 fi release_tag="${release_input}" if [ "${release_input}" != "${release_input#+}" ]; then current_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)" if [ -z "${current_tag}" ]; then echo "No existing tag found to increment from!" exit 1 fi case "${release_input}" in +1) release_tag="$(printf '%s' "${current_tag}" | awk -F. 'BEGIN{OFS="."} { $1+=1; $2=0; $3=0; print }')" ;; +0.1) release_tag="$(printf '%s' "${current_tag}" | awk -F. 'BEGIN{OFS="."} { $2+=1; $3=0; print }')" ;; +0.0.1) release_tag="$(printf '%s' "${current_tag}" | awk -F. 'BEGIN{OFS="."} { $3+=1; print }')" ;; *) echo "Unsupported increment syntax: ${release_input}" exit 1 ;; esac fi if git rev-parse -q --verify "refs/tags/${release_tag}" >/dev/null; then echo "Tag ${release_tag} already exists!" exit 1 fi if [ "${is_dry_run}" = true ]; then echo "Dry run: computed release tag ${release_tag}" exit 0 fi echo "Release tag selected: ${release_tag}" read -r -p "Proceed with release? [y/N] " confirm case "${confirm}" in y|Y) ;; *) echo "Release cancelled." exit 1 ;; esac git checkout "${TAGBRANCH}" CURRENTBRANCH="${TAGBRANCH}" git merge "${ORIGBRANCH}" git push git tag -a "${release_tag}" -m "Release ${release_tag}" git push --tags echo "Created release tag ${release_tag}"