Skip to content

Weekly README Sync Check #41

Weekly README Sync Check

Weekly README Sync Check #41

name: Weekly README Sync Check
on:
schedule:
- cron: '0 6 * * 0' # Every Sunday 06:00 UTC (09:00 Riyadh)
workflow_dispatch:
jobs:
check-readme-sync:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
- uses: actions/checkout@v6
- name: Count files and compare with README
id: sync_check
run: |
python3 - <<'PYEOF'
import os, re, json, sys
from pathlib import Path
# ── 1. Actual file counts ─────────────────────────────────────────────
def count_files(directory, extensions):
d = Path(directory)
if not d.exists():
return 0
return sum(
1 for f in d.iterdir()
if f.is_file() and f.suffix in extensions
)
actual = {
"skills": count_files("skills", {".md"}),
"sources": count_files("sources", {".md"}),
"datasets": count_files("datasets", {".md", ".csv"}),
"examples": count_files("examples", {".md"}),
"prompts": count_files("prompts", {".md"}),
"docs": count_files("docs", {".md"}),
"scripts": count_files("scripts", {".py", ".sql"}),
}
# ── 2. Extract counts mentioned in README ─────────────────────────────
readme = Path("README.md").read_text(encoding="utf-8")
# Patterns: "skills/ 7 ملفات", "7 skill files", "skills/ 7 files",
# "(skills/ 8 files)", "8 skill", etc.
# Strategy: for each dir name search for the closest integer neighbour.
def extract_readme_count(text, dir_name):
# Look for dir_name followed shortly by a number, or number before dir_name
patterns = [
rf"{re.escape(dir_name)}[^\n]{{0,40}}?(\d+)\s*(?:ملفات?|files?)",
rf"(\d+)\s*(?:ملفات?|files?)[^\n]{{0,40}}?{re.escape(dir_name)}",
rf"{re.escape(dir_name)}[/\s]{{0,5}}(\d+)",
rf"\({re.escape(dir_name)}[/\s]{{0,5}}(\d+)",
]
for pat in patterns:
m = re.search(pat, text, re.IGNORECASE)
if m:
return int(m.group(1))
return None
mentioned = {k: extract_readme_count(readme, k) for k in actual}
# ── 3. Build comparison table ─────────────────────────────────────────
rows = []
any_mismatch = False
for key in actual:
act = actual[key]
exp = mentioned[key]
if exp is None:
status = "❓ غير مذكور"
elif act == exp:
status = "✅ متطابق"
else:
status = "⚠️ يحتاج تحديث"
any_mismatch = True
rows.append((key + "/", act, exp if exp is not None else "—", status))
# ── 4. Write outputs ──────────────────────────────────────────────────
table_lines = []
for dir_, act, exp, status in rows:
table_lines.append(f"| `{dir_}` | {act} | {exp} | {status} |")
table_md = "\n".join(table_lines)
counts_summary = ", ".join(
f"{k}={actual[k]}" for k in actual
)
# ── 5. Write GITHUB_OUTPUT ────────────────────────────────────────────
with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
fh.write(f"any_mismatch={'true' if any_mismatch else 'false'}\n")
# ── 6. Write issue body to file (avoids shell interpolation of ──
# Unicode, pipe chars, and backticks in the table rows) ──
# Note: code_fence built at runtime so no unindented ``` lines appear
# in the YAML literal block scalar (which would terminate it early).
import datetime
date_str = datetime.datetime.utcnow().strftime("%Y-%m-%d")
code_fence = "`" * 3
body = (
f"## README Sync Check — {date_str}\n\n"
"الملفات الفعلية مقارنة بما هو مذكور في README:\n\n"
"| المجلد | الملفات الفعلية | ما يقوله README | الحالة |\n"
"|--------|----------------|-----------------|--------|\n"
f"{table_md}\n\n"
"### الإجراء المطلوب\n\n"
"لتحديث README، افتح Claude Code في مجلد الريبو واكتب:\n\n"
f"{code_fence}\n"
"Read the current file counts and update README.md to reflect:\n"
f"{counts_summary}\n"
f"{code_fence}\n\n"
"---\n"
"*Generated by Weekly README Sync Check workflow*"
)
with open("/tmp/issue_body.md", "w", encoding="utf-8") as f:
f.write(body)
# Also write date for use in shell steps
with open(os.environ["GITHUB_OUTPUT"], "a") as fh:
fh.write(f"date_str={date_str}\n")
# Print to log regardless
print("=== README Sync Check ===")
print(f"{'Directory':<12} {'Actual':>8} {'README':>8} Status")
print("-" * 50)
for dir_, act, exp, status in rows:
print(f"{dir_:<12} {act:>8} {str(exp):>8} {status}")
print()
if any_mismatch:
print("RESULT: README is OUT OF SYNC — issue will be created.")
else:
print("RESULT: README is in sync — no issue needed.")
PYEOF
- name: README is in sync
if: steps.sync_check.outputs.any_mismatch == 'false'
run: echo "README is in sync with the repository. No issue created."
- name: Ensure 'documentation' label exists
if: steps.sync_check.outputs.any_mismatch == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh label create "documentation" \
--description "Documentation updates" \
--color "0075ca" \
--repo "${{ github.repository }}" 2>/dev/null || true
- name: Check for existing open sync issue
if: steps.sync_check.outputs.any_mismatch == 'true'
id: existing_issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
existing=$(gh issue list \
--repo "${{ github.repository }}" \
--state open \
--label documentation \
--search "تحديث README الأسبوعي in:title" \
--json number \
--jq '.[0].number // ""')
echo "issue_number=${existing}" >> "$GITHUB_OUTPUT"
- name: Create sync issue
if: >
steps.sync_check.outputs.any_mismatch == 'true' &&
steps.existing_issue.outputs.issue_number == ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DATE_STR: ${{ steps.sync_check.outputs.date_str }}
run: |
TITLE="📋 تحديث README الأسبوعي — ${DATE_STR}"
gh issue create \
--repo "${{ github.repository }}" \
--title "${TITLE}" \
--body-file /tmp/issue_body.md \
--label "documentation" \
--assignee "Samix2026"
- name: Log skipped (duplicate issue exists)
if: >
steps.sync_check.outputs.any_mismatch == 'true' &&
steps.existing_issue.outputs.issue_number != ''
env:
EXISTING_ISSUE: ${{ steps.existing_issue.outputs.issue_number }}
run: |
echo "README is out of sync but issue #${EXISTING_ISSUE} is already open. Skipping duplicate."