|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Exit immediately if a command exits with a non-zero status. |
| 4 | +set -e |
| 5 | + |
| 6 | +# Initialize variables |
| 7 | +TAG="" |
| 8 | +DRY_RUN=false |
| 9 | + |
| 10 | +# Parse arguments |
| 11 | +forargin"$@";do |
| 12 | +case$argin |
| 13 | + --dry-run) |
| 14 | + DRY_RUN=true |
| 15 | + ;; |
| 16 | +*) |
| 17 | +# The first non-flag argument is the tag |
| 18 | +if [[!$arg== --* ]];then |
| 19 | +if [-z"$TAG" ];then |
| 20 | + TAG=$arg |
| 21 | +fi |
| 22 | +fi |
| 23 | + ;; |
| 24 | +esac |
| 25 | +done |
| 26 | + |
| 27 | +if ["$DRY_RUN"=true ];then |
| 28 | +echo"DRY RUN: No changes will be pushed to the remote repository." |
| 29 | +echo |
| 30 | +fi |
| 31 | + |
| 32 | +# 1. Validate input |
| 33 | +if [-z"$TAG" ];then |
| 34 | +echo"Error: No tag specified." |
| 35 | +echo"Usage: ./script/tag-release vX.Y.Z [--dry-run]" |
| 36 | +exit 1 |
| 37 | +fi |
| 38 | + |
| 39 | +# Regular expression for semantic versioning (vX.Y.Z or vX.Y.Z-suffix) |
| 40 | +if [[!$TAG=~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$ ]];then |
| 41 | +echo"Error: Tag must be in format vX.Y.Z or vX.Y.Z-suffix (e.g., v1.0.0 or v1.0.0-rc1)" |
| 42 | +exit 1 |
| 43 | +fi |
| 44 | + |
| 45 | +# 2. Check current branch |
| 46 | +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) |
| 47 | +if ["$CURRENT_BRANCH"!="main" ];then |
| 48 | +echo"Error: You must be on the 'main' branch to create a release." |
| 49 | +echo"Current branch is '$CURRENT_BRANCH'." |
| 50 | +exit 1 |
| 51 | +fi |
| 52 | + |
| 53 | +# 3. Fetch latest from origin |
| 54 | +echo"Fetching latest changes from origin..." |
| 55 | +git fetch origin |
| 56 | + |
| 57 | +# 4. Check if the working directory is clean |
| 58 | +if! git diff-index --quiet HEAD --;then |
| 59 | +echo"Error: Working directory is not clean. Please commit or stash your changes." |
| 60 | +exit 1 |
| 61 | +fi |
| 62 | + |
| 63 | +# 5. Check if main is up-to-date with origin/main |
| 64 | +LOCAL_SHA=$(git rev-parse @) |
| 65 | +REMOTE_SHA=$(git rev-parse @{u}) |
| 66 | + |
| 67 | +if ["$LOCAL_SHA"!="$REMOTE_SHA" ];then |
| 68 | +echo"Error: Your local 'main' branch is not up-to-date with 'origin/main'. Please pull the latest changes." |
| 69 | +exit 1 |
| 70 | +fi |
| 71 | +echo"✅ Local 'main' branch is up-to-date with 'origin/main'." |
| 72 | + |
| 73 | +# 6. Check if tag already exists |
| 74 | +if git tag -l| grep -q"^${TAG}$";then |
| 75 | +echo"Error: Tag${TAG} already exists locally." |
| 76 | +exit 1 |
| 77 | +fi |
| 78 | +if git ls-remote --tags origin| grep -q"refs/tags/${TAG}$";then |
| 79 | +echo"Error: Tag${TAG} already exists on remote 'origin'." |
| 80 | +exit 1 |
| 81 | +fi |
| 82 | + |
| 83 | +# 7. Confirm release with user |
| 84 | +echo |
| 85 | +LATEST_TAG=$(git tag --sort=-version:refname| head -n 1) |
| 86 | +if [-n"$LATEST_TAG" ];then |
| 87 | +echo"Current latest release:$LATEST_TAG" |
| 88 | +fi |
| 89 | +echo"Proposed new release:$TAG" |
| 90 | +echo |
| 91 | +read -p"Do you want to proceed with the release? (y/n)" -n 1 -r |
| 92 | +echo# Move to a new line |
| 93 | +if [[!$REPLY=~ ^[Yy]$ ]];then |
| 94 | +echo"Release cancelled." |
| 95 | +exit 1 |
| 96 | +fi |
| 97 | +echo |
| 98 | + |
| 99 | +# 8. Create the new release tag |
| 100 | +if ["$DRY_RUN"=true ];then |
| 101 | +echo"DRY RUN: Skipping creation of tag$TAG." |
| 102 | +else |
| 103 | +echo"Creating new release tag:$TAG" |
| 104 | + git tag -a"$TAG" -m"Release$TAG" |
| 105 | +fi |
| 106 | + |
| 107 | +# 9. Push the new tag to the remote repository |
| 108 | +if ["$DRY_RUN"=true ];then |
| 109 | +echo"DRY RUN: Skipping push of tag$TAG to origin." |
| 110 | +else |
| 111 | +echo"Pushing tag$TAG to origin..." |
| 112 | + git push origin"$TAG" |
| 113 | +fi |
| 114 | + |
| 115 | +# 10. Update and push the 'latest-release' tag |
| 116 | +if ["$DRY_RUN"=true ];then |
| 117 | +echo"DRY RUN: Skipping update and push of 'latest-release' tag." |
| 118 | +else |
| 119 | +echo"Updating 'latest-release' tag to point to$TAG..." |
| 120 | + git tag -f latest-release"$TAG" |
| 121 | +echo"Pushing 'latest-release' tag to origin..." |
| 122 | + git push origin latest-release --force |
| 123 | +fi |
| 124 | + |
| 125 | +if ["$DRY_RUN"=true ];then |
| 126 | +echo"✅ DRY RUN complete. No tags were created or pushed." |
| 127 | +else |
| 128 | +echo"✅ Successfully tagged and pushed release$TAG." |
| 129 | +echo"✅ 'latest-release' tag has been updated." |
| 130 | +fi |
| 131 | + |
| 132 | +# 11. Post-release instructions |
| 133 | +REPO_URL=$(git remote get-url origin) |
| 134 | +REPO_SLUG=$(echo"$REPO_URL"| sed -e's/.*github.com[:\/]//' -e's/\.git$//') |
| 135 | + |
| 136 | +cat<<EOF |
| 137 | +
|
| 138 | +## 🎉 Release$TAG has been initiated! |
| 139 | +
|
| 140 | +### Next steps: |
| 141 | +1. 📋 Check https://github.com/$REPO_SLUG/releases and wait for the draft release to show up (after the goreleaser workflow completes) |
| 142 | +2. ✏️ Edit the new release, delete the existing notes and click the auto-generate button GitHub provides |
| 143 | +3. ✨ Add a section at the top calling out the main features |
| 144 | +4. 🚀 Publish the release |
| 145 | +. 📢 Post message in #gh-mcp-releases channel in Slack and then share to the other mcp channels |
| 146 | +
|
| 147 | +### Resources: |
| 148 | +- 📦 Draft Release: https://github.com/$REPO_SLUG/releases/tag/$TAG |
| 149 | +
|
| 150 | +The release process is now ready for your review and completion! |
| 151 | +EOF |