Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  merged-pr-report.sh   Sprache: Shell

 

#!/usr/bin/env bash
#
# merged-prs-report
#
# Generates monthly reports of pull requests merged into the main, beta,
# and release branches of a GitHub repository.
#
# Usage:
#   ./merged-prs-report YEAR MONTH [TARGET_DIR] [--skip-excluded]
#
# Example:
#   ./merged-prs-report 2026 02
#   ./merged-prs-report 2026 02 ./reports
#   ./merged-prs-report 2026 02 . --skip-excluded
#
# Arguments:
#   YEAR           Four-digit year (e.g. 2026)
#   MONTH          Two-digit month (01-12)
#   TARGET_DIR     (Optional) Target directory for reports (default: current directory)
#   --skip-excluded (Optional) If set, PRs with "report: exclude" label are omitted from the report
#
# Output:
#   - Markdown report: merged-prs-YEAR-MONTH.md
#   - CSV report: merged-prs-YEAR-MONTH.csv
#
# For each PR, the reports include:
#   - branch       Target branch (main, beta, release)
#   - PR           PR number and link
#   - merged       Merge date (YYYY-MM-DD)
#   - title        PR title
#   - report       Status from labels (Highlight, Include, Exclude, Review)
#   - beta         First beta tag containing the merge commit (if any)
#   - release      First release tag containing the merge commit (if any)
#
# The CSV report also includes:
#   - author       PR author
#   - sha          Merge commit SHA
#   - url          Link to the PR
#   - comment      Empty column for manual notes
#
# Requirements:
#   - git: Installed and run from within the repository
#   - gh: GitHub CLI authenticated with access to the repository
#   - jq: JSON processor installed
#   - macOS/BSD date command (uses `date -j` for date arithmetic)
#
set -Eeuo pipefail

if [[ $# -lt 2 || $# -gt 4 ]]; then
  echo "Usage: $0 YEAR MONTH [TARGET_DIR] [--skip-excluded]"
  echo "Example: $0 2026 02"
  echo "Example: $0 2026 02 ./reports"
  echo "Example: $0 2026 02 . --skip-excluded"
  exit 1
fi

YEAR="$1"
MONTH="$2"
TARGET_DIR="."
SKIP_EXCLUDED=false

# Parse optional TARGET_DIR and --skip-excluded
if [[ $# -ge 3 ]]; then
  if [[ "$3" == "--skip-excluded" ]]; then
    SKIP_EXCLUDED=true
  else
    TARGET_DIR="$3"
  fi
fi
if [[ $# -eq 4 ]]; then
  if [[ "$4" == "--skip-excluded" ]]; then
    SKIP_EXCLUDED=true
  else
    :
  fi
fi

OWNER="thunderbird"
REPO="thunderbird-android"
BRANCHES=("main" "beta" "release")
DEFAULT_STATUS="Review"

# Validate input format
if [[ ! "$YEAR" =~ ^[0-9]{4}$ ]]; then
  echo "Error: YEAR must be a four-digit number (e.g. 2026)"
  exit 1
fi
if [[ ! "$MONTH" =~ ^(0[1-9]|1[0-2])$ ]]; then
  echo "Error: MONTH must be two digits (01-12)"
  exit 1
fi

START="${YEAR}-${MONTH}-01"
if ! END="$(date -j -v+1m -v-1d -f "%Y-%m-%d" "$START" +%Y-%m-%d 2>/dev/null)"; then
  echo "Error: Failed to calculate date range. Ensure you are on macOS or have BSD date installed."
  exit 1
fi

# Create target directory if it doesn't exist
mkdir -p "$TARGET_DIR"

MD_OUT="${TARGET_DIR}/merged-prs-${YEAR}-${MONTH}.md"
CSV_OUT="${TARGET_DIR}/merged-prs-${YEAR}-${MONTH}.csv"

# Temporary setup for git operations
TMP_REPO="$(mktemp -d)"
CACHE_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_REPO" "$CACHE_DIR"' EXIT

BETA_CACHE_FILE="$CACHE_DIR/beta_cache.txt"
RELEASE_CACHE_FILE="$CACHE_DIR/release_cache.txt"
touch "$BETA_CACHE_FILE" "$RELEASE_CACHE_FILE"

echo "Preparing temporary repo for tag analysis in $TMP_REPO..."
git init -q "$TMP_REPO"
git -C "$TMP_REPO" remote add origin "https://github.com/$OWNER/$REPO.git"

echo "Fetching tags and branch heads from origin..."
git -C "$TMP_REPO" fetch origin main beta release --tags --quiet

map_report_status() {
  local labels_json="$1"

  if jq -e '.[] | select(.name == "report: highlight")' >/dev/null <<< "$labels_json"; then
    echo "Highlight"
  elif jq -e '.[] | select(.name == "report: include")' >/dev/null <<< "$labels_json"; then
    echo "Include"
  elif jq -e '.[] | select(.name == "report: exclude")' >/dev/null <<< "$labels_json"; then
    echo "Exclude"
  else
    echo "$DEFAULT_STATUS"
  fi
}

map_version() {
  local sha="$1"
  local mode="$2"

  if [[ -z "$sha" ]]; then
    echo "-"
    return
  fi

  local target_branch
  if [[ "$mode" == "release" ]]; then
    target_branch="release"
  elif [[ "$mode" == "beta" ]]; then
    target_branch="beta"
  else
    echo "Error: Invalid mode '$mode' in map_version" >&2
    return 1
  fi

  # If commit is not in the target branch history, it's not applicable
  if ! git -C "$TMP_REPO" merge-base --is-ancestor "$sha" "origin/$target_branch" 2>/dev/null; then
    echo "-"
    return
  fi

  # Find the first tag (by version sorting) that contains this commit
  local first_tag
  if [[ "$mode" == "release" ]]; then
    # Exclude tags containing 'b' (betas)
    first_tag="$(git -C "$TMP_REPO" tag --list "THUNDERBIRD_*" --contains "$sha" --sort=version:refname | grep -v "b" | head -n 1)"
  else
    # Only tags containing 'b' (betas)
    first_tag="$(git -C "$TMP_REPO" tag --list "THUNDERBIRD_*b*" --contains "$sha" --sort=version:refname | head -n 1)"
  fi

  if [[ -n "$first_tag" ]]; then
    echo "$first_tag"
  else
    echo "Not released yet"
  fi
}

get_cached_value() {
  local cache_file="$1"
  local sha="$2"

  local result
  result="$(awk -F '\t' -v key="$sha" '$1 == key { print $2; exit }' "$cache_file")"

  if [[ -n "$result" ]]; then
    echo "$result"
    return 0
  fi

  return 1
}

set_cached_value() {
  local cache_file="$1"
  local sha="$2"
  local value="$3"

  printf '%s\t%s\n' "$sha" "$value" >> "$cache_file"
}

escape_md() {
  printf '%s' "$1" | sed 's/|/\\|/g'
}

escape_csv() {
  local value="$1"
  value="${value//\"/\"\"}"
  printf '"%s"' "$value"
}

{
  echo "# Merged PR Report (${YEAR}-${MONTH})"
  echo
  echo "**Repository:** $OWNER/$REPO  "
  echo "**Range:** $START -> $END"
  echo
} > "$MD_OUT"

echo "Branch,Number,Merged,Author,Title,Report,Beta,Release,SHA,URL,Comment" > "$CSV_OUT"

for BRANCH in "${BRANCHES[@]}"; do
  echo "Processing $BRANCH..."

  echo "## Branch: $BRANCH" >> "$MD_OUT"
  echo >> "$MD_OUT"
  echo "| PR | Merged | Title | Report | Feature Flag | Beta | Release |" >> "$MD_OUT"
  echo "|---|---|---|---|---|---|---|" >> "$MD_OUT"

  prs_json="$(gh pr list \
    --repo "$OWNER/$REPO" \
    --state merged \
    --base "$BRANCH" \
    --search "merged:$START..$END" \
    --json number,title,body,url,mergedAt,mergeCommit,labels,author \
    --limit 1000)"

  sorted_prs_json="$(jq 'sort_by(.mergedAt)' <<< "$prs_json")"

  if [[ "$(jq 'length' <<< "$sorted_prs_json")" -eq 0 ]]; then
    echo "| - | - | - | _No merged PRs in this range._ | - | - | - |" >> "$MD_OUT"
    echo >> "$MD_OUT"
    continue
  fi

  while IFS= read -r pr; do
    number="$(jq -r '.number' <<< "$pr")"
    title="$(jq -r '.title' <<< "$pr")"
    title_md="$(escape_md "$title")"
    url="$(jq -r '.url' <<< "$pr")"
    merged_at="$(jq -r '.mergedAt | split("T")[0]' <<< "$pr")"
    sha="$(jq -r '.mergeCommit.oid // empty' <<< "$pr")"
    author="$(jq -r '.author.login // "ghost"' <<< "$pr")"
    labels_json="$(jq -c '.labels // []' <<< "$pr")"
    status="$(map_report_status "$labels_json")"
    feature_flag="$(jq -r \
      'try (.body // "" | gsub("\r"; "") | capture("(?m)^feature-flag:\\s*`(?<flag>[^`]+)`$").flag) catch "" // "-"' \
      <<< "$pr")"

    if [[ "$SKIP_EXCLUDED" == "true" && "$status" == "Exclude" ]]; then
      continue
    fi

    if [[ -n "$sha" ]]; then
      # Beta tag analysis
      if ! beta_version="$(get_cached_value "$BETA_CACHE_FILE" "$sha" 2>/dev/null)"; then
        beta_version="$(map_version "$sha" "beta")"
        set_cached_value "$BETA_CACHE_FILE" "$sha" "$beta_version"
      fi

      # Release tag analysis
      if ! release_version="$(get_cached_value "$RELEASE_CACHE_FILE" "$sha" 2>/dev/null)"; then
        release_version="$(map_version "$sha" "release")"
        set_cached_value "$RELEASE_CACHE_FILE" "$sha" "$release_version"
      fi
    else
      beta_version="-"
      release_version="-"
    fi

    echo "| [#$number]($url) | $merged_at | $title_md | $status | $feature_flag | $beta_version | $release_version |" >> "$MD_OUT"

    printf '%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n' \
      "$(escape_csv "$BRANCH")" \
      "$(escape_csv "$number")" \
      "$(escape_csv "$merged_at")" \
      "$(escape_csv "$author")" \
      "$(escape_csv "$title")" \
      "$(escape_csv "$status")" \
      "$(escape_csv "$feature_flag")" \
      "$(escape_csv "$beta_version")" \
      "$(escape_csv "$release_version")" \
      "$(escape_csv "$sha")" \
      "$(escape_csv "$url")" \
      "$(escape_csv "")" \
      >> "$CSV_OUT"
  done < <(jq -c '.[]' <<< "$sorted_prs_json")

  echo >> "$MD_OUT"
done

echo "Wrote Markdown report to $MD_OUT"
echo "Wrote CSV report to $CSV_OUT"

Messung
CHZG
839810093

¤ Dauer der Verarbeitung: 0.3 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104838
#Domains=499075