|
| 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 | +for arg in "$@"; do |
| 12 | + case $arg in |
| 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 |
0 commit comments