#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" WORKSPACE_ROOT="${1:-$(cd "$SCRIPT_DIR/../../../.." && pwd)}" SINCE="${2:-7 days ago}" UNTIL="${3:-}" EXCLUDES_FILE="$WORKSPACE_ROOT/.workspace-ops/config/target-excludes.txt" if [[ "$WORKSPACE_ROOT" != "/" ]]; then WORKSPACE_ROOT="${WORKSPACE_ROOT%/}" fi normalized_excludes() { local exclude [[ -f "$EXCLUDES_FILE" ]] || return 0 while IFS= read -r exclude || [[ -n "$exclude" ]]; do exclude="${exclude%%#*}" exclude="${exclude#"${exclude%%[![:space:]]*}"}" exclude="${exclude%"${exclude##*[![:space:]]}"}" exclude="${exclude#./}" exclude="${exclude#/}" exclude="${exclude%/}" [[ -z "$exclude" ]] && continue printf '%s\n' "$exclude" done < "$EXCLUDES_FILE" } is_excluded_repo() { local rel="$1" local exclude while IFS= read -r exclude; do if [[ "$rel" == "$exclude" || "$rel" == "$exclude/"* ]]; then return 0 fi done < <(normalized_excludes) return 1 } repo_paths() { local -a find_args=( "$WORKSPACE_ROOT" -path "$WORKSPACE_ROOT/.git" -prune -o -path "$WORKSPACE_ROOT/.workspace-ops" -prune -o -path "$WORKSPACE_ROOT/.tmp" -prune -o -path "$WORKSPACE_ROOT/.antigravitycli" -prune -o -path "*/.agent-cache" -prune -o -path "*/node_modules" -prune -o -path "*/build" -prune -o -path "*/dist" -prune -o -path "*/.dart_tool" -prune -o -path "*/coverage" -prune -o ) local exclude local gitdir local rel local repo while IFS= read -r exclude; do find_args+=(-path "$WORKSPACE_ROOT/$exclude" -prune -o) done < <(normalized_excludes) find_args+=(-type d -name .git -print) find "${find_args[@]}" | sort | while IFS= read -r gitdir; do repo="$(dirname "$gitdir")" rel="${repo#$WORKSPACE_ROOT/}" if git -C "$WORKSPACE_ROOT" check-ignore -q -- "$rel" && ! is_excluded_repo "$rel"; then printf '%s\n' "$repo" fi done } log_args=(--branches --remotes --since="$SINCE") if [[ -n "$UNTIL" ]]; then log_args+=(--until="$UNTIL") fi while IFS= read -r repo; do rel="${repo#$WORKSPACE_ROOT/}" echo "## $rel" printf 'branch: ' git -C "$repo" branch --show-current printf 'commits: ' git -C "$repo" log "${log_args[@]}" --format='%H' | sort -u | wc -l authors="$(git -C "$repo" log "${log_args[@]}" --format='%aN <%aE>' | sort -u)" author_count="$(printf '%s\n' "$authors" | awk 'NF {c++} END {print c + 0}')" echo "author_count: $author_count" echo "authors:" if [[ -n "$authors" ]]; then printf '%s\n' "$authors" | sed 's/^/- /' fi echo "subjects:" git -C "$repo" log "${log_args[@]}" --date=short --pretty=format:'%h | %ad | %s' echo echo "top paths:" git -C "$repo" log "${log_args[@]}" --name-only --pretty=format:'' | awk ' NF { n = split($0, a, "/") if (n == 1) { p = a[1] } else if (a[1] == "agent-task" || a[1] == "agent-roadmap" || a[1] == "agent-ops") { p = a[1] "/" a[2] } else { p = a[1] "/" a[2] } c[p]++ } END { for (p in c) print c[p], p } ' | sort -nr | head -n 12 echo "shortstat:" git -C "$repo" log "${log_args[@]}" --shortstat --pretty=tformat: | awk ' /files? changed/ { files += $1 for (i = 1; i <= NF; i++) { if ($i == "insertion(+)" || $i == "insertions(+)") ins += $(i - 1) if ($i == "deletion(-)" || $i == "deletions(-)") del += $(i - 1) } } END { printf "files_changed_sum=%d insertions=%d deletions=%d\n", files, ins, del } ' echo done < <(repo_paths)