|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# Decide whether the given component needs a new semver tag. |
| 4 | +# |
| 5 | +# Reads: |
| 6 | +# COMPONENT - component name (e.g. reusable-workflow-1) |
| 7 | +# INCLUDE_PATH - path filter passed to `git log -- <path>` |
| 8 | +# |
| 9 | +# Writes (when GITHUB_OUTPUT is set, i.e. running under GitHub Actions): |
| 10 | +# release=true|false |
| 11 | +# tag=<component>/v<new-version> (only when release=true) |
| 12 | +# version=<new-version> (only when release=true) |
| 13 | + |
| 14 | +set -euo pipefail |
| 15 | + |
| 16 | +: "${COMPONENT:?COMPONENT must be set}" |
| 17 | +: "${INCLUDE_PATH:?INCLUDE_PATH must be set}" |
| 18 | + |
| 19 | +if [ ! -e "$INCLUDE_PATH" ]; then |
| 20 | + echo "INCLUDE_PATH '$INCLUDE_PATH' does not exist" >&2 |
| 21 | + exit 1 |
| 22 | +fi |
| 23 | + |
| 24 | +emit() { |
| 25 | + if [ -n "${GITHUB_OUTPUT:-}" ]; then |
| 26 | + echo "$1" >> "$GITHUB_OUTPUT" |
| 27 | + fi |
| 28 | +} |
| 29 | + |
| 30 | +LATEST_TAG=$(git tag -l "${COMPONENT}/v[0-9]*.[0-9]*.[0-9]*" --sort=-v:refname | head -n1 || true) |
| 31 | +if [ -z "$LATEST_TAG" ]; then |
| 32 | + CURRENT_VER="0.0.0" |
| 33 | + RANGE="HEAD" |
| 34 | + BUMP="minor" |
| 35 | + echo "No previous tag found for ${COMPONENT}, treating as new component (initial release will be at least 0.1.0)" |
| 36 | +else |
| 37 | + CURRENT_VER="${LATEST_TAG#"${COMPONENT}"/v}" |
| 38 | + RANGE="${LATEST_TAG}..HEAD" |
| 39 | + echo "Current version: ${CURRENT_VER} (from tag ${LATEST_TAG})" |
| 40 | +fi |
| 41 | + |
| 42 | +mapfile -t SUBJECTS < <(git log "$RANGE" --first-parent --format='%s' -- "$INCLUDE_PATH") |
| 43 | +echo "Found ${#SUBJECTS[@]} commits affecting ${INCLUDE_PATH} since ${LATEST_TAG:-<initial>}" |
| 44 | + |
| 45 | +BUMP="${BUMP:-}" |
| 46 | +for subject in "${SUBJECTS[@]}"; do |
| 47 | + [ -z "$subject" ] && continue |
| 48 | + echo " - ${subject}" |
| 49 | + if echo "$subject" | grep -qE '^[a-zA-Z]+(\([^)]+\))?!:'; then |
| 50 | + echo " → BREAKING CHANGE found in: ${subject}" |
| 51 | + echo " → Major bump triggered by: ${subject}" |
| 52 | + BUMP="major" |
| 53 | + break |
| 54 | + fi |
| 55 | + TYPE=$(echo "$subject" | sed -E 's/^([a-zA-Z]+).*/\1/') |
| 56 | + case "$TYPE" in |
| 57 | + feat) |
| 58 | + if [ "$BUMP" != "minor" ]; then |
| 59 | + echo " → Minor bump triggered by: ${subject}" |
| 60 | + BUMP="minor" |
| 61 | + fi |
| 62 | + ;; |
| 63 | + fix|perf|chore|docs|refactor|revert) |
| 64 | + [ -z "$BUMP" ] && BUMP="patch" |
| 65 | + ;; |
| 66 | + esac |
| 67 | +done |
| 68 | + |
| 69 | +if [ -z "$BUMP" ]; then |
| 70 | + echo "No release needed for ${COMPONENT} (latest=${LATEST_TAG})" |
| 71 | + emit "release=false" |
| 72 | + exit 0 |
| 73 | +fi |
| 74 | + |
| 75 | +IFS='.' read -r MAJ MIN PAT <<< "$CURRENT_VER" |
| 76 | +case "$BUMP" in |
| 77 | + major) NEW_VER="$((MAJ+1)).0.0" ;; |
| 78 | + minor) NEW_VER="${MAJ}.$((MIN+1)).0" ;; |
| 79 | + patch) NEW_VER="${MAJ}.${MIN}.$((PAT+1))" ;; |
| 80 | +esac |
| 81 | +NEW_TAG="${COMPONENT}/v${NEW_VER}" |
| 82 | +echo "Will release ${NEW_TAG} from ${LATEST_TAG:-<initial>} (bump=${BUMP})" |
| 83 | +emit "release=true" |
| 84 | +emit "tag=${NEW_TAG}" |
| 85 | +emit "version=${NEW_VER}" |
0 commit comments