forked from Gitlink/gitlink-cli
303 lines
13 KiB
Bash
303 lines
13 KiB
Bash
#!/usr/bin/env bash
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
# Scenario 1: Community Operations Automation
|
||
# Flow: Issue auto-classify → Assign responsible → Weekly report → Release notes
|
||
#
|
||
# Commands/Skills chained:
|
||
# 1. issue +list -- fetch open issues
|
||
# 2. issue +view -- read issue details
|
||
# 3. issue +batch-label -- add classification labels
|
||
# 4. issue +batch-assign -- assign responsible person
|
||
# 5. pr +list -- collect merged PRs for weekly report
|
||
# 6. wiki +create -- publish community weekly report
|
||
# 7. release +create -- publish release notes
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
source "$SCRIPT_DIR/lib/common.sh"
|
||
|
||
usage() {
|
||
echo "Usage: $0 --owner OWNER --repo REPO [--week WEEKS_AGO] [--dry-run]"
|
||
echo ""
|
||
echo " --owner OWNER Repository owner (org or user)"
|
||
echo " --repo REPO Repository name"
|
||
echo " --week N Generate report for N weeks ago (default: 0 = this week)"
|
||
echo " --dry-run Preview actions without executing"
|
||
exit 1
|
||
}
|
||
|
||
WEEKS_AGO=0
|
||
DRY_RUN=false
|
||
OWNER=""
|
||
REPO=""
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--owner) OWNER="$2"; shift 2 ;;
|
||
--repo) REPO="$2"; shift 2 ;;
|
||
--week) WEEKS_AGO="$2"; shift 2 ;;
|
||
--dry-run) DRY_RUN="true"; shift ;;
|
||
--help|-h) usage ;;
|
||
*) log_err "Unknown arg: $1"; usage ;;
|
||
esac
|
||
done
|
||
|
||
check_auth
|
||
require_owner_repo
|
||
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
log_title "Phase 1: Issue Auto-Classification"
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
|
||
log_step "Fetching open issues..."
|
||
ISSUES_JSON=$(gl_check issue +list --owner "$OWNER" --repo "$REPO" --state open --limit 100)
|
||
ISSUE_COUNT=$(echo "$ISSUES_JSON" | jq '.data.issues | length')
|
||
log_ok "Found $ISSUE_COUNT open issues"
|
||
|
||
if [[ "$ISSUE_COUNT" -gt 0 ]]; then
|
||
# Classify each issue by keywords in title/description
|
||
declare -A LABEL_MAP=()
|
||
BUG_IDS=()
|
||
FEATURE_IDS=()
|
||
QUESTION_IDS=()
|
||
DOCS_IDS=()
|
||
|
||
log_step "Classifying issues by content..."
|
||
|
||
for i in $(seq 0 $((ISSUE_COUNT - 1))); do
|
||
ISSUE_ID=$(echo "$ISSUES_JSON" | jq -r ".data.issues[$i].id")
|
||
ISSUE_TITLE=$(echo "$ISSUES_JSON" | jq -r ".data.issues[$i].subject // .data.issues[$i].title // \"\"")
|
||
ISSUE_DESC=$(echo "$ISSUES_JSON" | jq -r ".data.issues[$i].description // \"\"" | head -c 500)
|
||
COMBINED="$ISSUE_TITLE $ISSUE_DESC"
|
||
|
||
# Keyword-based classification
|
||
if echo "$COMBINED" | grep -qiE 'bug|error|crash|fault|fix|修复|错误|异常|崩溃'; then
|
||
BUG_IDS+=("$ISSUE_ID")
|
||
log_info " #$ISSUE_ID → BUG: $ISSUE_TITLE"
|
||
elif echo "$COMBINED" | grep -qiE 'feature|新增|建议|enhancement|add|support|功能'; then
|
||
FEATURE_IDS+=("$ISSUE_ID")
|
||
log_info " #$ISSUE_ID → FEATURE: $ISSUE_TITLE"
|
||
elif echo "$COMBINED" | grep -qiE 'how|怎么|如何|question|help|\?|?'; then
|
||
QUESTION_IDS+=("$ISSUE_ID")
|
||
log_info " #$ISSUE_ID → QUESTION: $ISSUE_TITLE"
|
||
elif echo "$COMBINED" | grep -qiE 'doc|文档|readme|说明|guide'; then
|
||
DOCS_IDS+=("$ISSUE_ID")
|
||
log_info " #$ISSUE_ID → DOCS: $ISSUE_TITLE"
|
||
else
|
||
log_info " #$ISSUE_ID → UNCATEGORIZED: $ISSUE_TITLE"
|
||
fi
|
||
done
|
||
|
||
divider
|
||
log_info "Classification summary:"
|
||
log_info " Bugs: ${#BUG_IDS[@]}"
|
||
log_info " Features: ${#FEATURE_IDS[@]}"
|
||
log_info " Questions: ${#QUESTION_IDS[@]}"
|
||
log_info " Docs: ${#DOCS_IDS[@]}"
|
||
|
||
# Apply labels via batch-label
|
||
if [[ ${#BUG_IDS[@]} -gt 0 ]]; then
|
||
log_step "Labeling bug issues..."
|
||
for bid in "${BUG_IDS[@]}"; do
|
||
gl_run issue +label-add --owner "$OWNER" --repo "$REPO" --number "$bid" --labels "bug" > /dev/null 2>&1 || true
|
||
done
|
||
log_ok "Labeled ${#BUG_IDS[@]} bug issues"
|
||
fi
|
||
|
||
if [[ ${#FEATURE_IDS[@]} -gt 0 ]]; then
|
||
log_step "Labeling feature issues..."
|
||
for fid in "${FEATURE_IDS[@]}"; do
|
||
gl_run issue +label-add --owner "$OWNER" --repo "$REPO" --number "$fid" --labels "feature" > /dev/null 2>&1 || true
|
||
done
|
||
log_ok "Labeled ${#FEATURE_IDS[@]} feature issues"
|
||
fi
|
||
|
||
if [[ ${#QUESTION_IDS[@]} -gt 0 ]]; then
|
||
log_step "Labeling question issues..."
|
||
for qid in "${QUESTION_IDS[@]}"; do
|
||
gl_run issue +label-add --owner "$OWNER" --repo "$REPO" --number "$qid" --labels "question" > /dev/null 2>&1 || true
|
||
done
|
||
log_ok "Labeled ${#QUESTION_IDS[@]} question issues"
|
||
fi
|
||
|
||
if [[ ${#DOCS_IDS[@]} -gt 0 ]]; then
|
||
log_step "Labeling docs issues..."
|
||
for did in "${DOCS_IDS[@]}"; do
|
||
gl_run issue +label-add --owner "$OWNER" --repo "$REPO" --number "$did" --labels "documentation" > /dev/null 2>&1 || true
|
||
done
|
||
log_ok "Labeled ${#DOCS_IDS[@]} docs issues"
|
||
fi
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
log_title "Phase 2: Assign Responsible Persons"
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
|
||
log_step "Fetching repo members for assignment..."
|
||
MEMBERS_JSON=$(gl_run repo +members --owner "$OWNER" --repo "$REPO" --limit 50)
|
||
MEMBER_COUNT=$(echo "$MEMBERS_JSON" | jq '(.data.members // .data | if type == "array" then . else [] end) | length' 2>/dev/null || echo "0")
|
||
|
||
if [[ "$MEMBER_COUNT" -gt 0 ]]; then
|
||
# Assign bugs to first member, features to second, etc. (round-robin)
|
||
MEMBER_LOGINS=()
|
||
MEMBERS_DATA_PATH='(.data.members // .data | if type == "array" then . else [] end)'
|
||
for i in $(seq 0 $((MEMBER_COUNT - 1))); do
|
||
LOGIN=$(echo "$MEMBERS_JSON" | jq -r "$MEMBERS_DATA_PATH[$i].login // $MEMBERS_DATA_PATH[$i].username // empty")
|
||
[[ -n "$LOGIN" ]] && MEMBER_LOGINS+=("$LOGIN")
|
||
done
|
||
|
||
if [[ ${#MEMBER_LOGINS[@]} -gt 0 ]]; then
|
||
assign_issues() {
|
||
local label="$1"
|
||
shift
|
||
local ids=("$@")
|
||
local member_idx=0
|
||
for id in "${ids[@]}"; do
|
||
local assignee="${MEMBER_LOGINS[$((member_idx % ${#MEMBER_LOGINS[@]}))]}"
|
||
# issue +update doesn't support --assignee, use raw API
|
||
gl_run api PATCH "/v1/$OWNER/$REPO/issues/$id" \
|
||
--body "{\"assigned_to_id\": \"$assignee\"}" > /dev/null 2>&1 || true
|
||
log_info " Assigned #$id → @$assignee"
|
||
((member_idx++))
|
||
done
|
||
}
|
||
|
||
log_step "Assigning bug issues..."
|
||
assign_issues "bug" "${BUG_IDS[@]}" 2>/dev/null || true
|
||
log_step "Assigning feature issues..."
|
||
assign_issues "feature" "${FEATURE_IDS[@]}" 2>/dev/null || true
|
||
log_ok "Assignment complete"
|
||
fi
|
||
else
|
||
log_warn "No repo members found, skipping assignment"
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
log_title "Phase 3: Generate Community Weekly Report"
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
|
||
WEEK_START=$(date -d "$((WEEKS_AGO * 7)) days ago" +%Y-%m-%d 2>/dev/null || date_today)
|
||
WEEK_END=$(date_today)
|
||
|
||
log_step "Collecting weekly data (week of $WEEK_START)..."
|
||
|
||
# Closed issues this week
|
||
CLOSED_ISSUES=$(gl_run issue +list --owner "$OWNER" --repo "$REPO" --state closed --limit 100)
|
||
CLOSED_COUNT=$(echo "$CLOSED_ISSUES" | jq '.data.issues | length' 2>/dev/null || echo "0")
|
||
|
||
# Merged PRs this week
|
||
MERGED_PRS=$(gl_run pr +list --owner "$OWNER" --repo "$REPO" --state merged --limit 100)
|
||
# PR list may return .data[], .data.pulls[], or .data.issues[]
|
||
MERGED_COUNT=$(echo "$MERGED_PRS" | jq '
|
||
(.data.issues // .data.pulls // .data | if type == "array" then . else [] end) | length
|
||
' 2>/dev/null || echo "0")
|
||
|
||
# New issues
|
||
NEW_ISSUES_COUNT=$ISSUE_COUNT
|
||
|
||
# Build weekly report
|
||
REPORT_TITLE="Community Weekly Report: $WEEK_START ~ $WEEK_END"
|
||
REPORT_BODY="# $REPORT_TITLE
|
||
|
||
## Summary
|
||
- New Issues: **$NEW_ISSUES_COUNT**
|
||
- Closed Issues: **$CLOSED_COUNT**
|
||
- Merged PRs: **$MERGED_COUNT**
|
||
|
||
## Issue Classification
|
||
| Type | Count |
|
||
|------|-------|
|
||
| Bug | ${#BUG_IDS[@]} |
|
||
| Feature | ${#FEATURE_IDS[@]} |
|
||
| Question | ${#QUESTION_IDS[@]} |
|
||
| Docs | ${#DOCS_IDS[@]} |
|
||
|
||
## Highlights
|
||
- Auto-classified and labeled $(( ${#BUG_IDS[@]} + ${#FEATURE_IDS[@]} + ${#QUESTION_IDS[@]} + ${#DOCS_IDS[@]} )) issues
|
||
- Assigned responsible persons for bug and feature issues
|
||
|
||
---
|
||
*Auto-generated by gitlink-cli community-ops workflow*"
|
||
|
||
log_ok "Weekly report generated"
|
||
echo ""
|
||
echo "$REPORT_BODY"
|
||
|
||
# Publish to Wiki
|
||
log_step "Publishing weekly report to Wiki..."
|
||
WIKI_RESULT=$(gl_run wiki +create --owner "$OWNER" --repo "$REPO" \
|
||
--title "$REPORT_TITLE" \
|
||
--content "$REPORT_BODY" 2>&1) || true
|
||
|
||
if [[ "$(json_ok "$WIKI_RESULT")" == "true" ]]; then
|
||
log_ok "Weekly report published to Wiki"
|
||
else
|
||
log_warn "Wiki publish may have failed (wiki module might not be enabled)"
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
log_title "Phase 4: Auto-Publish Release Notes"
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
|
||
log_step "Collecting recent changes for release notes..."
|
||
|
||
# Get recent commits via compare API
|
||
TAG_NAME="weekly-$(date +%Y%m%d)"
|
||
RELEASE_NAME="Weekly Release $(date +%Y-%m-%d)"
|
||
|
||
# Build release notes from closed issues and merged PRs
|
||
RELEASE_BODY="# Release Notes - $(date +%Y-%m-%d)
|
||
|
||
## Merged PRs ($MERGED_COUNT)"
|
||
|
||
if [[ "$MERGED_COUNT" -gt 0 ]]; then
|
||
PR_DATA_PATH='(.data.issues // .data.pulls // .data | if type == "array" then . else [] end)'
|
||
for i in $(seq 0 $((MERGED_COUNT > 10 ? 9 : MERGED_COUNT - 1))); do
|
||
PR_TITLE=$(echo "$MERGED_PRS" | jq -r "$PR_DATA_PATH[$i].subject // $PR_DATA_PATH[$i].title // \"\"")
|
||
PR_NUM=$(echo "$MERGED_PRS" | jq -r "$PR_DATA_PATH[$i].id // $PR_DATA_PATH[$i].number // \"\"")
|
||
RELEASE_BODY+=$'\n'"- #$PR_NUM $PR_TITLE"
|
||
done
|
||
fi
|
||
|
||
RELEASE_BODY+="
|
||
|
||
## Closed Issues ($CLOSED_COUNT)"
|
||
|
||
if [[ "$CLOSED_COUNT" -gt 0 ]]; then
|
||
for i in $(seq 0 $((CLOSED_COUNT > 10 ? 9 : CLOSED_COUNT - 1))); do
|
||
ISSUE_TITLE=$(echo "$CLOSED_ISSUES" | jq -r ".data.issues[$i].subject // .data.issues[$i].title // empty")
|
||
ISSUE_NUM=$(echo "$CLOSED_ISSUES" | jq -r ".data.issues[$i].number // .data.issues[$i].id // empty")
|
||
[[ -n "$ISSUE_TITLE" ]] && RELEASE_BODY+=$'\n'"- #${ISSUE_NUM:-?} $ISSUE_TITLE"
|
||
done
|
||
fi
|
||
|
||
RELEASE_BODY+="
|
||
|
||
---
|
||
*Auto-generated by gitlink-cli community-ops workflow*"
|
||
|
||
log_step "Creating release: $TAG_NAME..."
|
||
RELEASE_RESULT=$(gl_run release +create --owner "$OWNER" --repo "$REPO" \
|
||
--tag "$TAG_NAME" \
|
||
--name "$RELEASE_NAME" \
|
||
--body "$RELEASE_BODY" 2>&1) || true
|
||
|
||
if [[ "$(json_ok "$RELEASE_RESULT")" == "true" ]]; then
|
||
log_ok "Release $TAG_NAME created successfully"
|
||
else
|
||
log_warn "Release creation may have failed (tag might already exist)"
|
||
log_info "You can manually create with: gitlink-cli release +create --owner $OWNER --repo $REPO --tag $TAG_NAME --name '$RELEASE_NAME'"
|
||
fi
|
||
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
log_title "Community Operations Complete"
|
||
# ─────────────────────────────────────────────────────────────────────
|
||
|
||
echo -e "${GREEN}Summary:${NC}"
|
||
echo " Issues classified: $(( ${#BUG_IDS[@]} + ${#FEATURE_IDS[@]} + ${#QUESTION_IDS[@]} + ${#DOCS_IDS[@]} ))"
|
||
echo " Closed this week: $CLOSED_COUNT"
|
||
echo " Merged PRs: $MERGED_COUNT"
|
||
echo " Weekly report: Published to Wiki"
|
||
echo " Release notes: $TAG_NAME"
|
||
echo ""
|