Skip to content

Commit 00bf8ae

Browse files
committed
feat(hooks): Implement auto-push capability in pre-push hook to handle automated version bumps gracefully
1 parent c797e8d commit 00bf8ae

1 file changed

Lines changed: 127 additions & 102 deletions

File tree

.github/hooks/pre-push

Lines changed: 127 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -75,127 +75,152 @@ read_config_bool() {
7575
echo "$value"
7676
}
7777

78-
# Check if there are commits since last tag
79-
has_commits_to_release() {
80-
local last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
78+
# Read all refs from stdin to pass to auto-push if needed
79+
# Format: <local_ref> <local_oid> <remote_ref> <remote_oid>
80+
declare -a PUSH_REFS
81+
while read -r line; do
82+
PUSH_REFS+=("$line")
83+
done
84+
85+
# Check if there are commits to release
86+
if ! has_commits_to_release; then
87+
log_info "No commits to release, skipping semantic versioning"
88+
# Continue to verification
89+
else
90+
# Semantic Versioning Logic
91+
# -------------------------
92+
93+
# Check if semantic versioning is enabled
94+
SEMVER_ENABLED=$(read_config_bool "semver.enabled" "true")
8195

82-
if [[ -z "$last_tag" ]]; then
83-
# No tags yet, check if there are any commits
84-
[[ -n "$(git rev-list HEAD 2>/dev/null)" ]]
85-
else
86-
# Check if there are commits since last tag
87-
[[ -n "$(git log "${last_tag}..HEAD" 2>/dev/null)" ]]
96+
if [[ "$SEMVER_ENABLED" == "true" && -f "$SEMVER_SCRIPT" ]]; then
97+
log_info "Analyzing commits for semantic versioning..."
98+
99+
# Analyze commits
100+
if ! ANALYSIS=$("$SEMVER_SCRIPT" --analyze 2>/dev/null); then
101+
log_error "Failed to analyze commits"
102+
exit 1
103+
fi
104+
105+
# Parse JSON response
106+
BUMP_TYPE=$(echo "$ANALYSIS" | sed -n 's/.*"bump_type"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
107+
BREAKING_COUNT=$(echo "$ANALYSIS" | sed -n 's/.*"breaking"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
108+
FEATURE_COUNT=$(echo "$ANALYSIS" | sed -n 's/.*"features"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
109+
110+
log_info "Found: $FEATURE_COUNT features, $BREAKING_COUNT breaking changes"
111+
log_success "Suggested version bump: ${YELLOW}${BUMP_TYPE}${NC}"
112+
113+
# Breaking change confirmation
114+
REQUIRE_BREAKING_CONFIRM=$(read_config_bool "semver.require_breaking_change_confirmation" "true")
115+
if [[ $BREAKING_COUNT -gt 0 && "$REQUIRE_BREAKING_CONFIRM" == "true" ]]; then
116+
log_warn "Breaking changes detected!"
117+
echo ""
118+
while true; do
119+
read -p "$(echo -e ${BLUE}Confirm breaking changes? [yes/no]${NC}: )" confirm < /dev/tty
120+
confirm=$(echo "$confirm" | tr '[:upper:]' '[:lower:]')
121+
if [[ "$confirm" == "yes" ]]; then break;
122+
elif [[ "$confirm" == "no" ]]; then log_error "Push cancelled"; exit 1;
123+
fi
124+
done
125+
fi
126+
127+
# Store bump info for later (after verification)
128+
SHOULD_BUMP=true
88129
fi
89-
}
90-
91-
################################################################################
92-
# Main Hook Logic
93-
################################################################################
94-
95-
# Check if semantic versioning is enabled
96-
SEMVER_ENABLED=$(read_config_bool "semver.enabled" "true")
97-
AUTO_UPDATE=$(read_config_bool "semver.auto_update_version" "true")
98-
REQUIRE_BREAKING_CONFIRM=$(read_config_bool "semver.require_breaking_change_confirmation" "true")
99-
100-
if [[ "$SEMVER_ENABLED" != "true" ]]; then
101-
# Semantic versioning is disabled, skip
102-
exit 0
103130
fi
104131

105-
# Check if scripts exist
106-
if [[ ! -f "$SEMVER_SCRIPT" ]]; then
107-
log_warn "semantic-version.sh not found, skipping semantic versioning"
108-
exit 0
109-
fi
132+
# -------------------------
133+
# 1. Run Verification First
134+
# -------------------------
135+
# We verify BEFORE bumping to ensure we don't tag broken code
110136

111-
# Check if there are commits to analyze
112-
if ! has_commits_to_release; then
113-
log_info "No commits to release, skipping semantic versioning"
114-
exit 0
137+
echo "🔍 Running Universal CI verification..."
138+
139+
if [ -f "${REPO_ROOT}/run-ci.sh" ]; then
140+
"${REPO_ROOT}/run-ci.sh"
141+
elif command -v curl >/dev/null 2>&1; then
142+
curl -sL https://raw.githubusercontent.com/orchestrate-solutions/universal-ci/main/run-ci.sh | sh
143+
else
144+
echo "⚠️ run-ci.sh not found and curl not available"
145+
exit 0
115146
fi
116147

117-
log_info "Analyzing commits for semantic versioning..."
148+
exit_code=$?
118149

119-
# Analyze commits
120-
if ! ANALYSIS=$("$SEMVER_SCRIPT" --analyze 2>/dev/null); then
121-
log_error "Failed to analyze commits"
122-
exit 1
150+
if [ $exit_code -ne 0 ]; then
151+
echo ""
152+
echo "❌ Verification failed. Push blocked."
153+
echo " Fix the issues above and try again."
154+
exit 1
123155
fi
124156

125-
# Parse JSON response (portable sed-based parsing)
126-
BUMP_TYPE=$(echo "$ANALYSIS" | sed -n 's/.*"bump_type"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
127-
BREAKING_COUNT=$(echo "$ANALYSIS" | sed -n 's/.*"breaking"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
128-
FEATURE_COUNT=$(echo "$ANALYSIS" | sed -n 's/.*"features"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
129-
FIX_COUNT=$(echo "$ANALYSIS" | sed -n 's/.*"fixes"[[:space:]]*:[[:space:]]*\([0-9]*\).*/\1/p')
157+
echo "✅ Verification passed."
130158

131-
log_info "Found: $FEATURE_COUNT features, $FIX_COUNT fixes, $BREAKING_COUNT breaking changes"
132-
log_success "Suggested version bump: ${YELLOW}${BUMP_TYPE}${NC}"
159+
# -------------------------
160+
# 2. Bump & Auto-Push
161+
# -------------------------
133162

134-
# If breaking changes detected, require user confirmation
135-
if [[ $BREAKING_COUNT -gt 0 && "$REQUIRE_BREAKING_CONFIRM" == "true" ]]; then
136-
log_warn "Breaking changes detected!"
137-
echo ""
163+
if [[ "$SHOULD_BUMP" == "true" ]]; then
164+
AUTO_UPDATE=$(read_config_bool "semver.auto_update_version" "true")
138165

139-
# Interactive breaking change confirmation
140-
while true; do
141-
read -p "$(echo -e ${BLUE}Confirm breaking changes? [yes/no]${NC}: )" confirm
142-
confirm=$(echo "$confirm" | tr '[:upper:]' '[:lower:]')
143-
144-
if [[ "$confirm" == "yes" ]]; then
145-
log_success "Breaking change confirmed"
146-
break
147-
elif [[ "$confirm" == "no" ]]; then
148-
log_error "Push cancelled - breaking changes not confirmed"
149-
exit 1
150-
else
151-
log_error "Please respond with 'yes' or 'no'"
152-
fi
153-
done
154-
fi
155-
156-
# Auto-update version if enabled
157-
if [[ "$AUTO_UPDATE" == "true" ]]; then
158-
if [[ -f "$VERSION_FILE" && -f "$BUMP_SCRIPT" ]]; then
159-
160-
# Check if we've already bumped the version in the latest commit to avoid infinite loop
161-
# If the last commit message starts with "chore: release", we assume it's our auto-bump
166+
if [[ "$AUTO_UPDATE" == "true" && -f "$VERSION_FILE" && -f "$BUMP_SCRIPT" ]]; then
167+
168+
# Check if last commit was already a release (avoid loop)
162169
last_msg=$(git log -1 --pretty=%B)
163170
if [[ "$last_msg" == chore:\ release\ v* ]]; then
164-
log_info "Version bump already committed. Proceeding with push."
165-
else
166-
log_info "Auto-updating VERSION to $BUMP_TYPE bump..."
171+
log_info "Version bump already committed. Proceeding with push..."
172+
exit 0
173+
fi
174+
175+
log_info "Auto-updating VERSION to $BUMP_TYPE bump..."
176+
177+
# Run bump
178+
BUMP_OUTPUT=$("$BUMP_SCRIPT" "$BUMP_TYPE")
179+
if [[ $? -eq 0 ]]; then
180+
NEW_VERSION=$(head -n 1 "$VERSION_FILE")
181+
log_success "Updated VERSION to v${NEW_VERSION}"
182+
183+
# Commit
184+
git add "$VERSION_FILE" "$CHANGELOG_FILE" package.json 2>/dev/null || true
185+
git commit -m "chore: release v${NEW_VERSION} [skip ci]" --no-verify >/dev/null 2>&1
186+
187+
log_success "Committed release v${NEW_VERSION}"
188+
189+
# Auto-Push
190+
# We construct the push command based on what the user originally requested
191+
# $1 is remote name, $2 is remote URL
192+
REMOTE_NAME="$1"
167193

168-
# Run bump-version with detected bump type
169-
# Capture output to get the new version number
170-
BUMP_OUTPUT=$("$BUMP_SCRIPT" "$BUMP_TYPE")
171-
EXIT_CODE=$?
194+
if [[ -z "$REMOTE_NAME" ]]; then REMOTE_NAME="origin"; fi
172195

173-
if [[ $EXIT_CODE -eq 0 ]]; then
174-
# Extract new version (simple grep from the output or read file)
175-
NEW_VERSION=$(head -n 1 "$VERSION_FILE")
176-
177-
log_success "Updated VERSION to v${NEW_VERSION}"
178-
179-
# Commit the version bump
180-
git add "$VERSION_FILE" "$CHANGELOG_FILE" package.json 2>/dev/null || true
181-
git commit -m "chore: release v${NEW_VERSION} [skip ci]" --no-verify >/dev/null 2>&1
182-
183-
log_warn "---------------------------------------------------------"
184-
log_warn "🚀 Version bumped and committed: v${NEW_VERSION}"
185-
log_warn "---------------------------------------------------------"
186-
log_info "The push was intercepted to include the version update."
187-
log_info "Please run 'git push' again used to publish this release."
188-
log_warn "---------------------------------------------------------"
189-
190-
exit 1 # Intentionally fail push to let user re-push with new commit
191-
else
192-
log_error "Failed to update VERSION"
193-
exit 1
194-
fi
196+
echo "🚀 Auto-pushing to $REMOTE_NAME..."
197+
198+
# Push each ref requested
199+
for ref_line in "${PUSH_REFS[@]}"; do
200+
# line is: local_ref local_sha remote_ref remote_sha
201+
# We want to push HEAD to remote_ref
202+
parts=($ref_line)
203+
remote_ref="${parts[2]}"
204+
205+
if [[ -n "$remote_ref" ]]; then
206+
log_info "Pushing HEAD -> $remote_ref"
207+
git push --no-verify "$REMOTE_NAME" "HEAD:$remote_ref"
208+
fi
209+
done
210+
211+
echo ""
212+
log_warn "---------------------------------------------------------------"
213+
log_warn "✅ AUTOMATION COMPLETE: Version bumped & pushed."
214+
log_warn "ℹ️ The original push command will now 'fail' because it was"
215+
log_warn " superseded by the auto-push. This is normal."
216+
log_warn "---------------------------------------------------------------"
217+
218+
exit 1 # Block original push (it's stale now)
219+
else
220+
log_error "Failed to update version block"
221+
exit 1
195222
fi
196223
fi
197224
fi
198225

199-
echo ""
200-
log_success "Pre-push verification complete - proceeding with push"
201226
exit 0

0 commit comments

Comments
 (0)