# Interactive (default - asks for pull and confirmation)
./scripts/release.sh
make release
# Fully automated (no prompts at all)
./scripts/release.sh --auto
./scripts/release.sh release --auto
make release-auto
# Skip only confirmation
./scripts/release.sh --no-confirm
# Skip only pull
./scripts/release.sh --no-pull
# Kombinationen
./scripts/release.sh release --no-pull --no-confirm
# Mit custom CalVer
CALVER_PATTERN=%Y.%m ./scripts/release.sh --auto
# Über Makefile
make release-auto CALVER_PATTERN=%Y.%m
Created
September 29, 2025 07:26
-
-
Save dazz/ce1d95b2c3d9ae6640f62c6d0bde2ee7 to your computer and use it in GitHub Desktop.
Git release tag
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Configuration | |
| RELEASE_BRANCH ?= main | |
| CALVER_PATTERN ?= %Y.%m.%d | |
| .PHONY: release release-auto release-next-tag release-check | |
| # Interactive release (prompts for pull and confirmation) | |
| release: | |
| @RELEASE_BRANCH=$(RELEASE_BRANCH) CALVER_PATTERN=$(CALVER_PATTERN) ./scripts/release.sh release | |
| # Automated release (no prompts, pull + create + push) | |
| release-auto: | |
| @RELEASE_BRANCH=$(RELEASE_BRANCH) CALVER_PATTERN=$(CALVER_PATTERN) ./scripts/release.sh release --auto | |
| # Show next tag | |
| release-next-tag: | |
| @RELEASE_BRANCH=$(RELEASE_BRANCH) CALVER_PATTERN=$(CALVER_PATTERN) ./scripts/release.sh next-tag | |
| # Check for changes | |
| release-check: | |
| @RELEASE_BRANCH=$(RELEASE_BRANCH) CALVER_PATTERN=$(CALVER_PATTERN) ./scripts/release.sh check-changes |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| set -e | |
| # Configuration | |
| RELEASE_BRANCH="${RELEASE_BRANCH:-main}" | |
| CALVER_PATTERN="${CALVER_PATTERN:-%Y.%m.%d}" | |
| # Colors for output | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| NC='\033[0m' # No Color | |
| # Functions | |
| show_help() { | |
| cat << EOF | |
| Usage: $(basename "$0") [COMMAND] [OPTIONS] | |
| Commands: | |
| release Create a new release (default) | |
| next-tag Show what the next tag would be | |
| check-branch Check if current branch is correct | |
| check-changes Check if there are new merges since last tag | |
| help Show this help message | |
| Release Options: | |
| --no-pull Skip git pull | |
| --no-confirm Skip confirmation prompt | |
| --auto Equivalent to --no-confirm (pull, create, push without prompts) | |
| Environment Variables: | |
| RELEASE_BRANCH Branch from which releases are created (default: main) | |
| CALVER_PATTERN Date format for CalVer tags (default: %Y.%m.%d) | |
| Examples: | |
| %Y.%m.%d -> 2025.09.29 (default) | |
| %Y.%m -> 2025.09 | |
| %Y.%W -> 2025.39 (week number) | |
| v%Y.%m.%d -> v2025.09.29 | |
| Examples: | |
| $(basename "$0") # Interactive release process | |
| $(basename "$0") release --auto # Automated release (no prompts) | |
| $(basename "$0") release --no-confirm # Pull interactively, skip confirmation | |
| $(basename "$0") release --no-pull # Skip pull, confirm before push | |
| $(basename "$0") next-tag # See what tag would be created | |
| CALVER_PATTERN=%Y.%m $(basename "$0") --auto # Automated with custom pattern | |
| EOF | |
| } | |
| check_branch() { | |
| local current_branch=$(git rev-parse --abbrev-ref HEAD) | |
| if [ "$current_branch" != "$RELEASE_BRANCH" ]; then | |
| echo -e "${RED}Error: Releases can only be created from the '$RELEASE_BRANCH' branch.${NC}" | |
| echo "Current branch: $current_branch" | |
| return 1 | |
| fi | |
| echo -e "${GREEN}✓ On $RELEASE_BRANCH branch${NC}" | |
| return 0 | |
| } | |
| get_last_tag() { | |
| git describe --tags --abbrev=0 2>/dev/null || echo "" | |
| } | |
| calculate_next_tag() { | |
| local base_tag=$(date +"$CALVER_PATTERN") | |
| local existing_tags=$(git tag -l "$base_tag*" | sort -V) | |
| if [ -z "$existing_tags" ]; then | |
| echo "$base_tag" | |
| else | |
| local last_today=$(echo "$existing_tags" | tail -n 1) | |
| if [ "$last_today" = "$base_tag" ]; then | |
| echo "$base_tag.1" | |
| else | |
| local suffix=$(echo "$last_today" | sed "s/$(echo "$base_tag" | sed 's/\./\\./g')\.//") | |
| local next_suffix=$((suffix + 1)) | |
| echo "$base_tag.$next_suffix" | |
| fi | |
| fi | |
| } | |
| show_next_tag() { | |
| local last_tag=$(get_last_tag) | |
| local next_tag=$(calculate_next_tag) | |
| echo "Release branch: $RELEASE_BRANCH" | |
| echo "CalVer pattern: $CALVER_PATTERN" | |
| echo "Last tag: ${last_tag:-none}" | |
| echo "Next tag: $next_tag" | |
| } | |
| get_changelog() { | |
| local last_tag="$1" | |
| if [ -z "$last_tag" ]; then | |
| echo -e "${YELLOW}Warning: No previous tag found, showing all merge commits${NC}" >&2 | |
| git log --merges HEAD \ | |
| --pretty=format:"- %s" \ | |
| | sed -E 's/^Merge (pull request #[0-9]+ from [^ ]+ |branch .+ )?//' \ | |
| | head -n 20 | |
| else | |
| git log --merges "$last_tag"..HEAD \ | |
| --pretty=format:"- %s" \ | |
| | sed -E 's/^Merge (pull request #[0-9]+ from [^ ]+ |branch .+ )?//' | |
| fi | |
| } | |
| check_changes() { | |
| local last_tag=$(get_last_tag) | |
| local msg=$(get_changelog "$last_tag") | |
| echo "Release branch: $RELEASE_BRANCH" | |
| echo "CalVer pattern: $CALVER_PATTERN" | |
| echo "Last tag: ${last_tag:-none}" | |
| echo "" | |
| if [ -z "$msg" ]; then | |
| echo -e "${YELLOW}No new merges since $last_tag${NC}" | |
| return 1 | |
| else | |
| echo -e "${GREEN}Found new merges:${NC}" | |
| echo "$msg" | |
| return 0 | |
| fi | |
| } | |
| update_branch() { | |
| local skip_pull="$1" | |
| if [ "$skip_pull" = "true" ]; then | |
| echo "Skipping git pull (--no-pull)" | |
| return 0 | |
| fi | |
| read -p "Update $RELEASE_BRANCH branch (git pull)? [Y/n] " update_answer | |
| case $update_answer in | |
| [nN]*) | |
| echo "Branch will not be updated." | |
| ;; | |
| *) | |
| echo "Updating $RELEASE_BRANCH branch..." | |
| git pull | |
| ;; | |
| esac | |
| } | |
| create_release() { | |
| local skip_pull=false | |
| local skip_confirm=false | |
| # Parse arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| --no-pull) | |
| skip_pull=true | |
| shift | |
| ;; | |
| --no-confirm|--auto) | |
| skip_confirm=true | |
| shift | |
| ;; | |
| *) | |
| echo -e "${RED}Error: Unknown option '$1'${NC}" | |
| show_help | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| # Check branch | |
| check_branch || exit 1 | |
| # Update branch | |
| if [ "$skip_pull" = "false" ]; then | |
| if [ "$skip_confirm" = "true" ]; then | |
| echo "Updating $RELEASE_BRANCH branch..." | |
| git pull | |
| else | |
| update_branch "$skip_pull" | |
| fi | |
| else | |
| echo "Skipping git pull" | |
| fi | |
| # Get tags | |
| local last_tag=$(get_last_tag) | |
| local new_tag=$(calculate_next_tag) | |
| echo "" | |
| echo "CalVer pattern: $CALVER_PATTERN" | |
| echo "Last tag: ${last_tag:-none}" | |
| echo "New tag: $new_tag" | |
| # Generate changelog | |
| local msg=$(get_changelog "$last_tag") | |
| if [ -z "$msg" ]; then | |
| echo -e "${RED}No new merges since $last_tag${NC}" | |
| exit 1 | |
| fi | |
| # Preview | |
| echo "" | |
| echo "Tag message preview:" | |
| echo "----------------------" | |
| echo "Release $new_tag" | |
| echo "" | |
| echo "$msg" | |
| echo "----------------------" | |
| echo "" | |
| # Confirmation | |
| if [ "$skip_confirm" = "true" ]; then | |
| echo "Creating and pushing tag automatically (--no-confirm)" | |
| git tag -a "$new_tag" -m "Release $new_tag"$'\n\n'"$msg" | |
| git push origin "$new_tag" | |
| echo -e "${GREEN}✓ Release $new_tag successfully created and pushed${NC}" | |
| else | |
| read -p "Really create and push tag? [y/N] " answer | |
| case $answer in | |
| [yY]*) | |
| git tag -a "$new_tag" -m "Release $new_tag"$'\n\n'"$msg" | |
| git push origin "$new_tag" | |
| echo -e "${GREEN}✓ Release $new_tag successfully created and pushed${NC}" | |
| ;; | |
| *) | |
| echo "Aborted." | |
| exit 1 | |
| ;; | |
| esac | |
| fi | |
| } | |
| # Main script logic | |
| COMMAND="${1:-release}" | |
| case $COMMAND in | |
| release) | |
| shift # Remove 'release' from arguments | |
| create_release "$@" | |
| ;; | |
| next-tag) | |
| show_next_tag | |
| ;; | |
| check-branch) | |
| check_branch | |
| ;; | |
| check-changes) | |
| check_changes | |
| ;; | |
| help|--help|-h) | |
| show_help | |
| ;; | |
| --*) | |
| # If first argument is an option, assume 'release' command | |
| create_release "$@" | |
| ;; | |
| *) | |
| echo -e "${RED}Error: Unknown command '$COMMAND'${NC}" | |
| echo "" | |
| show_help | |
| exit 1 | |
| ;; | |
| esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment