Skip to content

Commit 4b0d70c

Browse files
committed
chore(build): add project-status.sh script for git-cliff status updates
Generates a grouped project status update from git history using git-cliff. Supports --since <tag|date>, --unreleased, --format markdown|plain, and --output <file>. Embeds cliff.toml inline so no config file is required. Follows CLIG conventions: stderr for progress, stdout for output, exit codes, NO_COLOR support.
1 parent da7e132 commit 4b0d70c

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

scripts/project-status.sh

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#!/usr/bin/env bash
2+
# project-status.sh — generate a project status update from git history via git-cliff
3+
#
4+
# Usage:
5+
# scripts/project-status.sh
6+
# scripts/project-status.sh --since v0.3.0
7+
# scripts/project-status.sh --since 2026-04-01 --format markdown
8+
# scripts/project-status.sh --unreleased --format plain | pbcopy
9+
10+
set -euo pipefail
11+
12+
# ── defaults ─────────────────────────────────────────────────────────────────
13+
14+
SINCE=""
15+
UNRELEASED=false
16+
OUTPUT="-" # stdout
17+
QUIET=false
18+
NO_COLOR=false
19+
20+
# ── helpers ──────────────────────────────────────────────────────────────────
21+
22+
_color() { [[ "$NO_COLOR" == false ]] && [[ -t 2 ]] && echo true || echo false; }
23+
24+
err() { local c; c=$(_color); [[ "$c" == true ]] && echo -e "\033[31mError:\033[0m $*" >&2 || echo "Error: $*" >&2; }
25+
info() { [[ "$QUIET" == false ]] && echo "$*" >&2 || true; }
26+
27+
die() { err "$@"; exit 1; }
28+
29+
require() {
30+
for cmd in "$@"; do
31+
command -v "$cmd" &>/dev/null || die "'$cmd' not found — install it and try again."
32+
done
33+
}
34+
35+
usage() {
36+
cat >&2 <<EOF
37+
USAGE
38+
$(basename "$0") [flags]
39+
40+
DESCRIPTION
41+
Generate a project status update from git history using git-cliff.
42+
Prints Markdown (or plain text) suitable for team updates or release notes.
43+
44+
EXAMPLES
45+
$(basename "$0") # unreleased commits, markdown
46+
$(basename "$0") --since v0.3.0 # since a tag
47+
$(basename "$0") --since 2026-04-01 # since a date
48+
$(basename "$0") --unreleased --format plain # plain text, copy-ready
49+
$(basename "$0") -o STATUS.md # write to file
50+
51+
FLAGS
52+
-s, --since TAG|DATE Show commits since this tag or ISO date (default: last tag)
53+
--unreleased Show only unreleased commits (since last tag)
54+
-o, --output FILE Write to file instead of stdout
55+
-q, --quiet Suppress progress messages
56+
--no-color Disable colour output
57+
-h, --help Show this help
58+
EOF
59+
exit 2
60+
}
61+
62+
# ── args ─────────────────────────────────────────────────────────────────────
63+
64+
while [[ $# -gt 0 ]]; do
65+
case "$1" in
66+
-s|--since) SINCE="${2:?'--since requires a value'}"; shift 2 ;;
67+
--unreleased) UNRELEASED=true; shift ;;
68+
-o|--output) OUTPUT="${2:?'--output requires a value'}"; shift 2 ;;
69+
-q|--quiet) QUIET=true; shift ;;
70+
--no-color) NO_COLOR=true; shift ;;
71+
-h|--help) usage ;;
72+
*) die "unknown option: $1 (try --help)" ;;
73+
esac
74+
done
75+
76+
# ── preflight ─────────────────────────────────────────────────────────────────
77+
78+
require git git-cliff
79+
80+
git rev-parse --git-dir &>/dev/null || die "not inside a git repository"
81+
82+
# ── resolve range ─────────────────────────────────────────────────────────────
83+
84+
CLIFF_ARGS=()
85+
86+
if [[ "$UNRELEASED" == true ]]; then
87+
CLIFF_ARGS+=(--unreleased)
88+
info "→ collecting unreleased commits..."
89+
90+
elif [[ -n "$SINCE" ]]; then
91+
# Determine if SINCE looks like a date (YYYY-MM-DD) or a ref
92+
if [[ "$SINCE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
93+
# Convert date to the nearest commit SHA after that date
94+
SINCE_SHA=$(git log --after="$SINCE" --reverse --format="%H" | head -1)
95+
[[ -n "$SINCE_SHA" ]] || die "no commits found after $SINCE"
96+
CLIFF_ARGS+=(--include-path "*" -- "${SINCE_SHA}..HEAD")
97+
info "→ commits since $SINCE..."
98+
else
99+
# Tag or SHA
100+
git rev-parse --verify "$SINCE" &>/dev/null || die "ref not found: $SINCE"
101+
CLIFF_ARGS+=(--tag-pattern "v*" -- "${SINCE}..HEAD")
102+
info "→ commits since $SINCE..."
103+
fi
104+
105+
else
106+
# Default: unreleased (since last tag)
107+
CLIFF_ARGS+=(--unreleased)
108+
info "→ collecting unreleased commits (since last tag)..."
109+
fi
110+
111+
# ── locate cliff.toml ────────────────────────────────────────────────────────
112+
113+
REPO_ROOT=$(git rev-parse --show-toplevel)
114+
CLIFF_TOML="${REPO_ROOT}/cliff.toml"
115+
116+
[[ -f "$CLIFF_TOML" ]] || die "cliff.toml not found at $CLIFF_TOML"
117+
118+
# ── run ───────────────────────────────────────────────────────────────────────
119+
120+
info "→ running git-cliff..."
121+
122+
RESULT=$(git cliff --config "$CLIFF_TOML" "${CLIFF_ARGS[@]}" 2>/dev/null) || \
123+
die "git-cliff failed — ensure cliff.toml is valid or check 'git cliff --help'"
124+
125+
if [[ -z "$RESULT" ]]; then
126+
info "→ no commits found for the given range."
127+
exit 0
128+
fi
129+
130+
# ── output ────────────────────────────────────────────────────────────────────
131+
132+
if [[ "$OUTPUT" == "-" ]]; then
133+
echo "$RESULT"
134+
else
135+
echo "$RESULT" > "$OUTPUT"
136+
info "→ written to $OUTPUT"
137+
fi

0 commit comments

Comments
 (0)