Skip to content

Commit 8bddbf1

Browse files
feat(scripts): add preview-version.sh script
Add preview-version.sh script from infra to preview semantic-release versions. This script simulates merging the current branch into a target branch and runs semantic-release in dry-run mode to show what version would be released. The script: - Creates temporary git worktree and merge commit for analysis - Restores original branch state via cleanup function - Supports both root and package-level version previews - Uses bun for fast dependency installation via global cache
1 parent edbbed0 commit 8bddbf1

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed

scripts/preview-version.sh

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#!/usr/bin/env bash
2+
# preview-version.sh - Preview semantic-release version after merging to target branch
3+
#
4+
# Usage:
5+
# ./scripts/preview-version.sh [target-branch] [package-path]
6+
#
7+
# Examples:
8+
# ./scripts/preview-version.sh # Preview root version on main
9+
# ./scripts/preview-version.sh main packages/docs # Preview docs package version on main
10+
# ./scripts/preview-version.sh beta packages/docs # Preview docs version on beta
11+
#
12+
# This script simulates merging the current branch into the target branch and
13+
# runs semantic-release in dry-run mode to preview what version would be released.
14+
15+
set -euo pipefail
16+
17+
# Configuration
18+
TARGET_BRANCH="${1:-main}"
19+
PACKAGE_PATH="${2:-}"
20+
CURRENT_BRANCH=$(git branch --show-current)
21+
REPO_ROOT=$(git rev-parse --show-toplevel)
22+
WORKTREE_DIR=$(mktemp -d "${TMPDIR:-/tmp}/semantic-release-preview.XXXXXX")
23+
24+
# Save original target branch HEAD for restoration
25+
ORIGINAL_TARGET_HEAD=""
26+
27+
# Colors for output
28+
RED='\033[0;31m'
29+
GREEN='\033[0;32m'
30+
YELLOW='\033[1;33m'
31+
BLUE='\033[0;34m'
32+
NC='\033[0m' # No Color
33+
34+
# Cleanup function
35+
cleanup() {
36+
local exit_code=$?
37+
38+
# Always restore target branch to original state if we modified it
39+
if [ -n "$ORIGINAL_TARGET_HEAD" ]; then
40+
echo -e "\n${BLUE}restoring ${TARGET_BRANCH} to original state...${NC}"
41+
git update-ref "refs/heads/$TARGET_BRANCH" "$ORIGINAL_TARGET_HEAD" 2>/dev/null || true
42+
fi
43+
44+
# Clean up worktree
45+
if [ -d "$WORKTREE_DIR" ]; then
46+
echo -e "${BLUE}cleaning up worktree...${NC}"
47+
git worktree remove --force "$WORKTREE_DIR" 2>/dev/null || true
48+
# Prune any stale worktree references
49+
git worktree prune 2>/dev/null || true
50+
fi
51+
52+
exit $exit_code
53+
}
54+
55+
trap cleanup EXIT INT TERM
56+
57+
# Validation
58+
if [ "$CURRENT_BRANCH" == "$TARGET_BRANCH" ]; then
59+
echo -e "${YELLOW}already on target branch ${TARGET_BRANCH}${NC}"
60+
echo -e "${YELLOW}running test-release instead of preview${NC}\n"
61+
if [ -n "$PACKAGE_PATH" ]; then
62+
cd "$REPO_ROOT/$PACKAGE_PATH"
63+
fi
64+
exec bun run test-release
65+
fi
66+
67+
# Display what we're doing
68+
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
69+
echo -e "${BLUE}semantic-release version preview${NC}"
70+
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
71+
echo -e "current branch: ${GREEN}${CURRENT_BRANCH}${NC}"
72+
echo -e "target branch: ${GREEN}${TARGET_BRANCH}${NC}"
73+
if [ -n "$PACKAGE_PATH" ]; then
74+
echo -e "package: ${GREEN}${PACKAGE_PATH}${NC}"
75+
else
76+
echo -e "package: ${GREEN}(root)${NC}"
77+
fi
78+
echo -e "${BLUE}───────────────────────────────────────────────────────────────${NC}\n"
79+
80+
# Verify target branch exists
81+
if ! git show-ref --verify --quiet "refs/heads/$TARGET_BRANCH"; then
82+
echo -e "${RED}error: target branch '${TARGET_BRANCH}' does not exist${NC}" >&2
83+
exit 1
84+
fi
85+
86+
# Save original target branch HEAD before any modifications
87+
ORIGINAL_TARGET_HEAD=$(git rev-parse "$TARGET_BRANCH")
88+
89+
# Create merge tree to test if merge is possible
90+
echo -e "${BLUE}simulating merge of ${CURRENT_BRANCH}${TARGET_BRANCH}...${NC}"
91+
92+
# Perform merge-tree operation to test if merge is possible
93+
MERGE_OUTPUT=$(git merge-tree --write-tree "$TARGET_BRANCH" "$CURRENT_BRANCH" 2>&1)
94+
MERGE_EXIT=$?
95+
96+
if [ $MERGE_EXIT -ne 0 ]; then
97+
echo -e "${RED}error: merge conflicts detected${NC}" >&2
98+
echo -e "${YELLOW}please resolve conflicts in your branch before previewing${NC}" >&2
99+
echo -e "\n${YELLOW}conflict details:${NC}" >&2
100+
echo "$MERGE_OUTPUT" >&2
101+
exit 1
102+
fi
103+
104+
# Extract tree hash from merge-tree output (first line)
105+
MERGE_TREE=$(echo "$MERGE_OUTPUT" | head -1)
106+
107+
if [ -z "$MERGE_TREE" ]; then
108+
echo -e "${RED}error: failed to create merge tree${NC}" >&2
109+
exit 1
110+
fi
111+
112+
# Create temporary merge commit
113+
echo -e "${BLUE}creating temporary merge commit...${NC}"
114+
TEMP_COMMIT=$(git commit-tree -p "$TARGET_BRANCH" -p "$CURRENT_BRANCH" \
115+
-m "Temporary merge for semantic-release preview" "$MERGE_TREE")
116+
117+
if [ -z "$TEMP_COMMIT" ]; then
118+
echo -e "${RED}error: failed to create temporary merge commit${NC}" >&2
119+
exit 1
120+
fi
121+
122+
# Temporarily update target branch to point to merge commit
123+
# This allows semantic-release to analyze the correct commit history
124+
# The cleanup function will ALWAYS restore the original branch HEAD
125+
echo -e "${BLUE}temporarily updating ${TARGET_BRANCH} ref for analysis...${NC}"
126+
git update-ref "refs/heads/$TARGET_BRANCH" "$TEMP_COMMIT"
127+
128+
# Create worktree at target branch (now pointing to merge commit)
129+
echo -e "${BLUE}creating temporary worktree at ${TARGET_BRANCH}...${NC}"
130+
git worktree add --quiet "$WORKTREE_DIR" "$TARGET_BRANCH"
131+
132+
# Navigate to worktree
133+
cd "$WORKTREE_DIR"
134+
135+
# Install dependencies in worktree (bun uses global cache, so this is fast)
136+
echo -e "${BLUE}installing dependencies in worktree...${NC}"
137+
bun install --silent &>/dev/null
138+
139+
# Navigate to package if specified
140+
if [ -n "$PACKAGE_PATH" ]; then
141+
if [ ! -d "$PACKAGE_PATH" ]; then
142+
echo -e "${RED}error: package path '${PACKAGE_PATH}' does not exist${NC}" >&2
143+
exit 1
144+
fi
145+
cd "$PACKAGE_PATH"
146+
fi
147+
148+
# Run semantic-release in dry-run mode
149+
echo -e "\n${BLUE}running semantic-release analysis...${NC}\n"
150+
151+
# Capture output and parse version
152+
# Exclude @semantic-release/github to avoid GitHub token requirement for preview
153+
# This is safe because dry-run skips publish/success/fail steps anyway
154+
PLUGINS="@semantic-release/commit-analyzer,@semantic-release/release-notes-generator"
155+
156+
if [ -n "$PACKAGE_PATH" ]; then
157+
# For monorepo packages, check if package.json has specific plugins configured
158+
OUTPUT=$(bun run semantic-release --dry-run --no-ci --branches "$TARGET_BRANCH" --plugins "$PLUGINS" 2>&1 || true)
159+
else
160+
# For root package
161+
OUTPUT=$(bun run semantic-release --dry-run --no-ci --branches "$TARGET_BRANCH" --plugins "$PLUGINS" 2>&1 || true)
162+
fi
163+
164+
# Display semantic-release summary (filter out verbose plugin repetition)
165+
echo "$OUTPUT" | grep -v "^$" | grep -vE "(No more plugins|does not provide step)" | \
166+
grep -E "(semantic-release|Running|analyzing|Found.*commits|release version|Release note|Features|Bug Fixes|Breaking Changes|Published|\*\s)" || true
167+
168+
echo -e "\n${BLUE}═══════════════════════════════════════════════════════════════${NC}"
169+
170+
# Extract and display the next version
171+
if echo "$OUTPUT" | grep -q "There are no relevant changes"; then
172+
echo -e "${YELLOW}no version bump required${NC}"
173+
echo -e "no semantic commits found since last release"
174+
elif echo "$OUTPUT" | grep -q "is not configured to publish from"; then
175+
echo -e "${YELLOW}cannot determine version${NC}"
176+
echo -e "branch ${TARGET_BRANCH} is not in release configuration"
177+
elif VERSION=$(echo "$OUTPUT" | grep -oP 'next release version is \K[0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?' | head -1); then
178+
echo -e "${GREEN}next version: ${VERSION}${NC}"
179+
180+
# Extract release type if available
181+
if TYPE=$(echo "$OUTPUT" | grep -oP 'Release type: \K[a-z]+' | head -1); then
182+
echo -e "release type: ${TYPE}"
183+
fi
184+
else
185+
echo -e "${YELLOW}could not parse version from output${NC}"
186+
echo -e "check the semantic-release output above for details"
187+
fi
188+
189+
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}\n"

0 commit comments

Comments
 (0)