#!/usr/bin/env bash # # pre-push — enforce Conventional Branches # https://conventional-branch.github.io/ # # Branch format: / # must be one of: feature, bugfix, hotfix, release, chore # # Protected branches (always allowed): # - main # - litellm_internal_staging # - dependabot/* # - gh-readonly-queue/* # # Tag pushes and branch deletions are skipped. # Bypass: git push --no-verify. set -eu ZERO_OID="0000000000000000000000000000000000000000" ZERO_OID_SHA256="0000000000000000000000000000000000000000000000000000000000000000" ALLOWED_TYPES="feature|bugfix|hotfix|release|chore" BRANCH_PATTERN="^(${ALLOWED_TYPES})/.+" PROTECTED_NAMES="main litellm_internal_staging" PROTECTED_PREFIXES="dependabot/ gh-readonly-queue/" is_protected() { branch="$1" for name in $PROTECTED_NAMES; do if [ "$branch" = "$name" ]; then return 0 fi done for prefix in $PROTECTED_PREFIXES; do case "$branch" in "$prefix"*) return 0 ;; esac done return 1 } invalid="" while read -r local_ref local_oid remote_ref remote_oid; do # Branch deletion (no local commit being pushed). if [ "$local_oid" = "$ZERO_OID" ] || [ "$local_oid" = "$ZERO_OID_SHA256" ]; then continue fi # Only validate branch pushes; ignore tags and other ref namespaces. case "$remote_ref" in refs/heads/*) ;; *) continue ;; esac branch="${remote_ref#refs/heads/}" if is_protected "$branch"; then continue fi if ! printf '%s' "$branch" | grep -Eq "$BRANCH_PATTERN"; then invalid="$invalid $branch" fi done if [ -n "$invalid" ]; then cat >&2 </ Allowed types: feature, bugfix, hotfix, release, chore Examples: feature/weighted-round-robin bugfix/streaming-empty-chunks chore/bump-deps hotfix/auth-bypass Protected (always allowed): main, litellm_internal_staging, dependabot/*, gh-readonly-queue/*. See https://conventional-branch.github.io/ Rename with: git branch -m To bypass (use sparingly): git push --no-verify EOF exit 1 fi exit 0