mirror of
https://github.com/zebbern/claude-code-guide.git
synced 2026-08-29 02:57:20 +08:00
f73c6d0a76
- Rename 'Guide On CLAUDE.md' folder to 'guides/' - Move CHANGELOG.md to root (was in Official Claude Releases/) - Delete 'Official Claude Releases' folder - Add agents/ directory with 100+ subagent definitions - Add new guide sections: testing/, typescript/, security/ - Update zebbern CLAUDE.md with 2026 best practices - Update sync workflow to target root CHANGELOG.md
286 lines
9.5 KiB
YAML
286 lines
9.5 KiB
YAML
name: Sync Anthropic Release Notes
|
||
|
||
on:
|
||
schedule:
|
||
# Runs once every 24h at 00:15 UTC
|
||
- cron: "15 0 * * *"
|
||
workflow_dispatch:
|
||
inputs:
|
||
force_update:
|
||
description: 'Force update all files'
|
||
required: false
|
||
type: boolean
|
||
default: false
|
||
|
||
permissions:
|
||
contents: write
|
||
|
||
concurrency:
|
||
group: sync-release-notes
|
||
cancel-in-progress: false
|
||
|
||
jobs:
|
||
sync:
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 10
|
||
|
||
steps:
|
||
- name: Check out repository
|
||
uses: actions/checkout@v4
|
||
with:
|
||
persist-credentials: true
|
||
fetch-depth: 0
|
||
|
||
- name: Download and process release notes
|
||
id: sync_files
|
||
env:
|
||
FORCE_UPDATE: ${{ github.event.inputs.force_update || 'false' }}
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
# Configuration - Now syncing directly to root
|
||
readonly MAX_RETRIES=3
|
||
readonly RETRY_DELAY=5
|
||
|
||
# Color output for better readability in logs
|
||
readonly RED='\033[0;31m'
|
||
readonly GREEN='\033[0;32m'
|
||
readonly YELLOW='\033[1;33m'
|
||
readonly NC='\033[0m' # No Color
|
||
|
||
# Source URLs mapped to destination filenames (synced to root)
|
||
# Only raw markdown URLs are supported (HTML pages cannot be fetched as markdown)
|
||
declare -A FILES=(
|
||
["https://raw.githubusercontent.com/anthropics/claude-code/main/CHANGELOG.md"]="CHANGELOG.md"
|
||
)
|
||
|
||
# Function to download with retries
|
||
download_with_retry() {
|
||
local url="$1"
|
||
local output="$2"
|
||
local attempt=1
|
||
|
||
while [[ $attempt -le $MAX_RETRIES ]]; do
|
||
if curl -fsSL \
|
||
-H "Accept: text/markdown, text/plain;q=0.9, text/x-markdown;q=0.9, */*;q=0.1" \
|
||
-H "User-Agent: GitHub-Actions-Release-Notes-Sync/1.0" \
|
||
--connect-timeout 10 \
|
||
--max-time 30 \
|
||
"$url" -o "$output"; then
|
||
return 0
|
||
fi
|
||
|
||
echo -e "${YELLOW}Attempt $attempt/$MAX_RETRIES failed for $url${NC}" >&2
|
||
|
||
if [[ $attempt -lt $MAX_RETRIES ]]; then
|
||
sleep $RETRY_DELAY
|
||
fi
|
||
|
||
((attempt++))
|
||
done
|
||
|
||
echo -e "${RED}Failed to download $url after $MAX_RETRIES attempts${NC}" >&2
|
||
return 1
|
||
}
|
||
|
||
# Validate downloaded content
|
||
validate_content() {
|
||
local file="$1"
|
||
local min_size=10
|
||
|
||
# Check file exists and has content
|
||
if [[ ! -f "$file" ]] || [[ ! -s "$file" ]]; then
|
||
echo -e "${RED}File $file is empty or doesn't exist${NC}" >&2
|
||
return 1
|
||
fi
|
||
|
||
# Check minimum size
|
||
local size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
|
||
if [[ $size -lt $min_size ]]; then
|
||
echo -e "${RED}File $file is too small ($size bytes)${NC}" >&2
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# Arrays to track changes
|
||
declare -a CHANGED_FILES=()
|
||
COMMIT_BODY=""
|
||
README_LIST=""
|
||
TOTAL_ADDED=0
|
||
TOTAL_DELETED=0
|
||
|
||
# Process each file
|
||
for url in "${!FILES[@]}"; do
|
||
name="${FILES[$url]}"
|
||
out="$name"
|
||
tmp="$out.tmp"
|
||
|
||
echo -e "${GREEN}Processing: $name${NC}"
|
||
|
||
# Download file
|
||
if ! download_with_retry "$url" "$tmp"; then
|
||
echo -e "${RED}Skipping $name due to download failure${NC}"
|
||
rm -f "$tmp"
|
||
continue
|
||
fi
|
||
|
||
# Validate downloaded content
|
||
if ! validate_content "$tmp"; then
|
||
echo -e "${RED}Skipping $name due to validation failure${NC}"
|
||
rm -f "$tmp"
|
||
continue
|
||
fi
|
||
|
||
# Check if file needs updating
|
||
needs_update=false
|
||
|
||
if [[ "$FORCE_UPDATE" == "true" ]]; then
|
||
needs_update=true
|
||
echo "Force update enabled"
|
||
elif [[ ! -f "$out" ]]; then
|
||
needs_update=true
|
||
echo "New file detected"
|
||
elif ! git --no-pager diff --no-index --ignore-all-space --quiet -- "$out" "$tmp" 2>/dev/null; then
|
||
needs_update=true
|
||
echo "Changes detected"
|
||
fi
|
||
|
||
if [[ "$needs_update" == "false" ]]; then
|
||
echo "No changes for $name"
|
||
rm -f "$tmp"
|
||
continue
|
||
fi
|
||
|
||
# Calculate statistics
|
||
base_file="$out"
|
||
[[ -f "$out" ]] || base_file="/dev/null"
|
||
|
||
numstat=$(git --no-pager diff --no-index --numstat -- "$base_file" "$tmp" 2>/dev/null || echo "0 0 -")
|
||
read -r added deleted _ <<< "$numstat"
|
||
|
||
# Extract highlights from changes
|
||
highlights=$(
|
||
git --no-pager diff --no-index -U0 -- "$base_file" "$tmp" 2>/dev/null \
|
||
| sed -n 's/^+[^+]//p' \
|
||
| grep -E '^(#{1,3} |- |\* |[0-9]+\. )' \
|
||
| head -n 5 || true
|
||
)
|
||
|
||
# Fallback to first few lines if no structured content
|
||
if [[ -z "$highlights" ]]; then
|
||
highlights=$(
|
||
git --no-pager diff --no-index -U0 -- "$base_file" "$tmp" 2>/dev/null \
|
||
| sed -n 's/^+[^+]//p' \
|
||
| grep -v '^$' \
|
||
| head -n 3 || true
|
||
)
|
||
fi
|
||
|
||
# Update file and track changes
|
||
mv -f "$tmp" "$out"
|
||
CHANGED_FILES+=("$out")
|
||
|
||
# Build commit message body
|
||
COMMIT_BODY+="- **${name}** (+${added:-0} / -${deleted:-0})"$'\n'
|
||
if [[ -n "$highlights" ]]; then
|
||
while IFS= read -r line; do
|
||
# Truncate long lines
|
||
line="${line:0:100}"
|
||
COMMIT_BODY+=" • ${line}"$'\n'
|
||
done <<< "$highlights"
|
||
fi
|
||
|
||
# Build README summary
|
||
README_LIST+="- **${name}**: +${added:-0} / -${deleted:-0}"$'\n'
|
||
|
||
# Track totals
|
||
((TOTAL_ADDED += added)) || true
|
||
((TOTAL_DELETED += deleted)) || true
|
||
|
||
echo -e "${GREEN}✓ Updated $name (+$added / -$deleted)${NC}"
|
||
done
|
||
|
||
# Stage changed files
|
||
if [[ ${#CHANGED_FILES[@]} -gt 0 ]]; then
|
||
git add "${CHANGED_FILES[@]}"
|
||
echo "files_changed=true" >> "$GITHUB_OUTPUT"
|
||
else
|
||
echo "files_changed=false" >> "$GITHUB_OUTPUT"
|
||
fi
|
||
|
||
# Save data for next steps
|
||
{
|
||
printf '%b' "$COMMIT_BODY"
|
||
} > /tmp/commit_body.txt
|
||
|
||
{
|
||
printf '%b' "$README_LIST"
|
||
} > /tmp/readme_list.txt
|
||
|
||
echo "total_added=$TOTAL_ADDED" >> "$GITHUB_OUTPUT"
|
||
echo "total_deleted=$TOTAL_DELETED" >> "$GITHUB_OUTPUT"
|
||
echo "changed_count=${#CHANGED_FILES[@]}" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Commit and push changes
|
||
if: always()
|
||
run: |
|
||
set -euo pipefail
|
||
|
||
# Configure git
|
||
git config user.name "github-actions[bot]"
|
||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||
|
||
# Check if there are changes to commit
|
||
if ! git diff --cached --quiet; then
|
||
date_str=$(date -u +%Y-%m-%d)
|
||
|
||
if [[ "${{ steps.sync_files.outputs.files_changed }}" == "true" ]]; then
|
||
# Content changes detected
|
||
title="Sync release notes ($date_str)"
|
||
body=$(cat /tmp/commit_body.txt 2>/dev/null || echo "Updated release notes")
|
||
|
||
git commit -m "$title" -m "$body"
|
||
echo "✅ Committed content changes"
|
||
else
|
||
# Only README status update
|
||
title="Update sync status ($date_str)"
|
||
body="No release note content changes. Updated status in README."
|
||
|
||
git commit -m "$title" -m "$body"
|
||
echo "✅ Committed status update"
|
||
fi
|
||
|
||
# Push changes
|
||
git push
|
||
echo "✅ Pushed changes to repository"
|
||
else
|
||
echo "ℹ️ No changes to commit"
|
||
fi
|
||
|
||
- name: Create summary
|
||
if: always()
|
||
run: |
|
||
{
|
||
echo "## Sync Summary"
|
||
echo
|
||
|
||
if [[ "${{ steps.sync_files.outputs.files_changed }}" == "true" ]]; then
|
||
echo "### ✅ Success"
|
||
echo
|
||
echo "**Files updated**: ${{ steps.sync_files.outputs.changed_count }}"
|
||
echo "**Lines added**: +${{ steps.sync_files.outputs.total_added }}"
|
||
echo "**Lines deleted**: -${{ steps.sync_files.outputs.total_deleted }}"
|
||
echo
|
||
echo "### 📝 Changes"
|
||
echo
|
||
echo '```'
|
||
cat /tmp/commit_body.txt 2>/dev/null || echo "No details available"
|
||
echo '```'
|
||
else
|
||
echo "### ℹ️ No Changes"
|
||
echo
|
||
echo "All tracked files are up to date. Status README was updated."
|
||
fi
|
||
} >> "$GITHUB_STEP_SUMMARY" |