Skip to content

Update README What's New #33

Update README What's New

Update README What's New #33

name: Update README What's New
on:
workflow_run:
workflows: ["Daily Change Summary"]
types: [completed]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update-readme:
if: >-
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Check for commits
id: check
run: |
git checkout main
git log --oneline -1
SINCE=$(date -u -d "24 hours ago" +%Y-%m-%dT%H:%M:%S)
echo "SINCE=$SINCE"
COUNT=$(git log --since="$SINCE" --oneline --no-merges | wc -l)
echo "commit_count=$COUNT" >> "$GITHUB_OUTPUT"
echo "since=$SINCE" >> "$GITHUB_OUTPUT"
if [ "$COUNT" -eq "0" ]; then
echo "No commits in the last 24 hours, skipping."
else
echo "Found $COUNT commits since $SINCE"
fi
- name: Setup Node.js
if: steps.check.outputs.commit_count != '0'
uses: actions/setup-node@v6
with:
node-version: '25'
- name: Install Copilot CLI
if: steps.check.outputs.commit_count != '0'
run: npm install -g @github/copilot
- name: Detect significant features
if: steps.check.outputs.commit_count != '0'
id: detect
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_PAT }}
run: |
SINCE="${{ steps.check.outputs.since }}"
GIT_LOG=$(git log main --since="$SINCE" --pretty=format:"%h %s (%an)" --no-merges)
OLDEST=$(git log main --since="$SINCE" --format="%H" --no-merges | tail -1)
DIFF_STAT=$(git diff --stat "${OLDEST}^..main" 2>/dev/null || echo "N/A")
DIFF_NAMES=$(git diff --name-only "${OLDEST}^..main" 2>/dev/null || echo "N/A")
PROMPT=$(cat <<'PROMPT_EOF'
You are a release-notes analyst for a Next.js + TypeScript AI research assistant project.
Analyze the following git commits and file changes. Determine if there are any SIGNIFICANT NEW FEATURES worth highlighting in a "What's New" section of the README.
CLASSIFICATION RULES:
- SIGNIFICANT = new user-facing capability, new major integration, new UI panel/page, new API endpoint category, new workflow/system
- NOT SIGNIFICANT = bug fixes, refactoring, dependency updates, CI/workflow changes, documentation-only changes, minor UI tweaks, code style changes, test additions
OUTPUT FORMAT:
If there ARE significant features, output EXACTLY in this format (no extra text before or after):
---BEGIN_FEATURES_EN---
- **Feature Name**: One-line English description
---END_FEATURES_EN---
---BEGIN_FEATURES_ZH---
- **功能名称**: 一行中文描述
---END_FEATURES_ZH---
If there are NO significant features, output EXACTLY:
---NO_FEATURES---
IMPORTANT:
- Output TWO separate blocks: one English, one Chinese
- Feature names and descriptions must correspond 1:1 between the two blocks
- Keep descriptions concise (under 120 chars per line)
- Group related commits into a single feature bullet
- Maximum 3 feature bullets per day
PROMPT_EOF
)
RESULT=$(copilot -p "${PROMPT}
## Git Commits:
${GIT_LOG}
## Changed Files:
${DIFF_NAMES}
## File Change Stats:
${DIFF_STAT}")
echo "$RESULT"
echo "$RESULT" > /tmp/feature_detection.txt
if echo "$RESULT" | grep -q "BEGIN_FEATURES_EN"; then
echo "has_features=true" >> "$GITHUB_OUTPUT"
# Extract English features
sed -n '/---BEGIN_FEATURES_EN---/,/---END_FEATURES_EN---/p' \
/tmp/feature_detection.txt \
| grep -vF -- '---' > /tmp/new_features_en.txt
# Extract Chinese features
sed -n '/---BEGIN_FEATURES_ZH---/,/---END_FEATURES_ZH---/p' \
/tmp/feature_detection.txt \
| grep -vF -- '---' > /tmp/new_features_zh.txt
echo "Detected English features:"
cat /tmp/new_features_en.txt
echo "Detected Chinese features:"
cat /tmp/new_features_zh.txt
else
echo "has_features=false" >> "$GITHUB_OUTPUT"
echo "No significant features detected."
fi
- name: Translate features for JA/FR/DE
if: steps.detect.outputs.has_features == 'true'
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_PAT }}
run: |
NEW_FEATURES_EN=$(cat /tmp/new_features_en.txt)
NEW_FEATURES_ZH=$(cat /tmp/new_features_zh.txt)
PROMPT=$(cat <<'PROMPT_EOF'
You translate concise README "What's New" bullets for a software project.
Convert the provided feature bullets into THREE aligned output blocks in this exact format:
---BEGIN_FEATURES_JA---
- **Feature Name**: Japanese description
---END_FEATURES_JA---
---BEGIN_FEATURES_FR---
- **Nom de fonctionnalite**: Description francaise en ASCII uniquement
---END_FEATURES_FR---
---BEGIN_FEATURES_DE---
- **Funktionsname**: Deutsche Beschreibung in ASCII nur
---END_FEATURES_DE---
RULES:
- Keep bullet count and order exactly aligned with the English source.
- Preserve the markdown bullet and bold feature-name structure.
- Keep each line concise and skimmable.
- Japanese may use natural Japanese.
- French must use ASCII only, with no accented characters.
- German must use ASCII only; use ae, oe, ue, and ss when needed.
- Do not add commentary before or after the blocks.
PROMPT_EOF
)
RESULT=$(copilot -p "${PROMPT}
## English features:
${NEW_FEATURES_EN}
## Chinese features:
${NEW_FEATURES_ZH}")
echo "$RESULT"
echo "$RESULT" > /tmp/feature_translations.txt
if echo "$RESULT" | grep -q "BEGIN_FEATURES_JA"; then
sed -n '/---BEGIN_FEATURES_JA---/,/---END_FEATURES_JA---/p' \
/tmp/feature_translations.txt \
| grep -vF -- '---' > /tmp/new_features_ja.txt
sed -n '/---BEGIN_FEATURES_FR---/,/---END_FEATURES_FR---/p' \
/tmp/feature_translations.txt \
| grep -vF -- '---' > /tmp/new_features_fr.txt
sed -n '/---BEGIN_FEATURES_DE---/,/---END_FEATURES_DE---/p' \
/tmp/feature_translations.txt \
| grep -vF -- '---' > /tmp/new_features_de.txt
echo "Detected Japanese features:"
cat /tmp/new_features_ja.txt
echo "Detected French features:"
cat /tmp/new_features_fr.txt
echo "Detected German features:"
cat /tmp/new_features_de.txt
else
echo "Translation blocks not detected. Falling back to English bullets for JA/FR/DE."
cp /tmp/new_features_en.txt /tmp/new_features_ja.txt
cp /tmp/new_features_en.txt /tmp/new_features_fr.txt
cp /tmp/new_features_en.txt /tmp/new_features_de.txt
fi
- name: Update READMEs
if: steps.detect.outputs.has_features == 'true'
run: |
TODAY=$(date +%Y-%m-%d)
MARKER_START="<!-- whats-new-start -->"
# Update README.md (English homepage)
NEW_FEATURES_EN=$(cat /tmp/new_features_en.txt)
ENTRY_EN=$(printf '#### %s\n%s\n' "$TODAY" "$NEW_FEATURES_EN")
awk -v marker="$MARKER_START" -v entry="$ENTRY_EN" '
{ print }
index($0, marker) { printf "\n%s\n", entry }
' README.md > /tmp/README_updated.md
mv /tmp/README_updated.md README.md
echo "README.md (EN) updated with new entry for $TODAY"
# Update docs/README_CN.md (Chinese translation)
NEW_FEATURES_ZH=$(cat /tmp/new_features_zh.txt)
ENTRY_ZH=$(printf '#### %s\n%s\n' "$TODAY" "$NEW_FEATURES_ZH")
awk -v marker="$MARKER_START" -v entry="$ENTRY_ZH" '
{ print }
index($0, marker) { printf "\n%s\n", entry }
' docs/README_CN.md > /tmp/README_CN_updated.md
mv /tmp/README_CN_updated.md docs/README_CN.md
echo "docs/README_CN.md (ZH) updated with new entry for $TODAY"
# Update docs/README_JA.md (Japanese translation)
NEW_FEATURES_JA=$(cat /tmp/new_features_ja.txt)
ENTRY_JA=$(printf '#### %s\n%s\n' "$TODAY" "$NEW_FEATURES_JA")
awk -v marker="$MARKER_START" -v entry="$ENTRY_JA" '
{ print }
index($0, marker) { printf "\n%s\n", entry }
' docs/README_JA.md > /tmp/README_JA_updated.md
mv /tmp/README_JA_updated.md docs/README_JA.md
echo "docs/README_JA.md (JA) updated with new entry for $TODAY"
# Update docs/README_FR.md (French translation)
NEW_FEATURES_FR=$(cat /tmp/new_features_fr.txt)
ENTRY_FR=$(printf '#### %s\n%s\n' "$TODAY" "$NEW_FEATURES_FR")
awk -v marker="$MARKER_START" -v entry="$ENTRY_FR" '
{ print }
index($0, marker) { printf "\n%s\n", entry }
' docs/README_FR.md > /tmp/README_FR_updated.md
mv /tmp/README_FR_updated.md docs/README_FR.md
echo "docs/README_FR.md (FR) updated with new entry for $TODAY"
# Update docs/README_DE.md (German translation)
NEW_FEATURES_DE=$(cat /tmp/new_features_de.txt)
ENTRY_DE=$(printf '#### %s\n%s\n' "$TODAY" "$NEW_FEATURES_DE")
awk -v marker="$MARKER_START" -v entry="$ENTRY_DE" '
{ print }
index($0, marker) { printf "\n%s\n", entry }
' docs/README_DE.md > /tmp/README_DE_updated.md
mv /tmp/README_DE_updated.md docs/README_DE.md
echo "docs/README_DE.md (DE) updated with new entry for $TODAY"
- name: Trim old entries and collapse
if: steps.detect.outputs.has_features == 'true'
run: |
python3 <<'PYEOF'
import re
MAX_ENTRIES = 10
VISIBLE_DATES = 2 # Number of most-recent unique dates shown expanded
LANG_LABELS = {
"README.md": "Show earlier updates",
"docs/README_CN.md": "显示更早的更新",
"docs/README_JA.md": "以前の更新を表示",
"docs/README_FR.md": "Afficher les mises a jour precedentes",
"docs/README_DE.md": "Aeltere Updates anzeigen",
}
for readme_file in [
"README.md",
"docs/README_CN.md",
"docs/README_JA.md",
"docs/README_FR.md",
"docs/README_DE.md",
]:
with open(readme_file, "r", encoding="utf-8") as f:
content = f.read()
start_marker = "<!-- whats-new-start -->"
end_marker = "<!-- whats-new-end -->"
start_idx = content.find(start_marker)
end_idx = content.find(end_marker)
if start_idx == -1 or end_idx == -1:
print(f"{readme_file}: Markers not found, skipping trim.")
continue
before = content[:start_idx + len(start_marker)]
section = content[start_idx + len(start_marker):end_idx]
after = content[end_idx:]
# Strip any existing <details> wrapper from previous runs
section = re.sub(
r'<details>\s*<summary>.*?</summary>\s*(.*?)\s*</details>',
r'\1',
section,
flags=re.DOTALL,
)
# Split into date-headed entries
entries = re.split(r'(?=\n#### \d{4}-\d{2}-\d{2})', section)
entries = [e for e in entries if e.strip() and '####' in e]
if len(entries) > MAX_ENTRIES:
entries = entries[:MAX_ENTRIES]
print(f"{readme_file}: Trimmed to {MAX_ENTRIES} entries.")
else:
print(f"{readme_file}: Have {len(entries)} entries (max {MAX_ENTRIES}), no trimming needed.")
# Determine split point: first VISIBLE_DATES unique dates stay expanded
seen_dates = []
split_idx = len(entries)
for i, entry in enumerate(entries):
m = re.search(r'#### (\d{4}-\d{2}-\d{2})', entry)
if m and m.group(1) not in seen_dates:
seen_dates.append(m.group(1))
if len(seen_dates) > VISIBLE_DATES:
split_idx = i
break
visible = entries[:split_idx]
collapsed = entries[split_idx:]
result = "\n".join(visible)
if collapsed:
label = LANG_LABELS.get(readme_file, "Show earlier updates")
result += "\n\n<details>\n<summary>" + label + "</summary>\n"
result += "\n".join(collapsed)
result += "\n\n</details>\n"
result += "\n\n"
new_content = before + "\n" + result + after
with open(readme_file, "w", encoding="utf-8") as f:
f.write(new_content)
print(f"{readme_file}: {len(visible)} entries visible, {len(collapsed)} collapsed.")
PYEOF
- name: Create branch, PR and merge
if: steps.detect.outputs.has_features == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TODAY=$(date +%Y-%m-%d)
BRANCH="auto/whats-new-${TODAY}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Create or reset branch from current state (READMEs already modified)
git checkout -B "${BRANCH}"
git add README.md docs/README_CN.md docs/README_JA.md docs/README_FR.md docs/README_DE.md
git commit -m "docs: update What's New section (${TODAY})" || {
echo "No changes to commit."
exit 0
}
git push origin "${BRANCH}" --force-with-lease
# Check if PR already exists
EXISTING_PR=$(gh pr list --head "${BRANCH}" --json number --jq '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
echo "PR #${EXISTING_PR} already exists. Branch updated."
PR_NUMBER="$EXISTING_PR"
else
# Build PR body in a file to avoid heredoc quoting issues with backticks
NEW_FEATURES_EN=$(cat /tmp/new_features_en.txt)
NEW_FEATURES_ZH=$(cat /tmp/new_features_zh.txt)
cat > /tmp/pr_body.md <<'BODY_EOF'
## Summary
Auto-detected significant new features from today's commits and updated the **What's New** section in `README.md` (English homepage), `docs/README_CN.md` (Chinese translation), and lightweight localized summaries in `docs/README_JA.md`, `docs/README_FR.md`, and `docs/README_DE.md`.
BODY_EOF
printf '\n### New entries (English):\n\n%s\n' "$NEW_FEATURES_EN" >> /tmp/pr_body.md
printf '\n### New entries (Chinese):\n\n%s\n' "$NEW_FEATURES_ZH" >> /tmp/pr_body.md
printf '\n---\n*Auto-generated by the `readme-whats-new` workflow.*\n' >> /tmp/pr_body.md
PR_URL=$(gh pr create \
--title "docs: What's New - ${TODAY}" \
--body-file /tmp/pr_body.md \
--label "documentation" \
--base main \
--head "${BRANCH}")
echo "Created PR: ${PR_URL}"
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
fi
# Auto-merge the PR
if [ -n "$PR_NUMBER" ]; then
echo "Merging PR #${PR_NUMBER} ..."
gh pr merge "$PR_NUMBER" --squash --delete-branch
echo "PR #${PR_NUMBER} merged successfully."
fi