feat(repo): Add GitHub About sync command
Expose an explicit sync:github-about workflow that updates the remote GitHub About description, homepage, and topics from the repository metadata source of truth. Add regression coverage for the generated gh commands and document the new maintainer path in the changelog and walkthrough.
This commit is contained in:
@@ -3,6 +3,7 @@ import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -10,6 +11,29 @@ from update_readme import configure_utf8_output, find_repo_root, load_metadata,
|
||||
|
||||
|
||||
ABOUT_DESCRIPTION_RE = re.compile(r'"description"\s*:\s*"([^"]*)"')
|
||||
GITHUB_HOMEPAGE_URL = "https://sickn33.github.io/antigravity-awesome-skills/"
|
||||
RECOMMENDED_TOPICS = [
|
||||
"agentic-skills",
|
||||
"agent-skills",
|
||||
"ai-agents",
|
||||
"ai-agent-skills",
|
||||
"awesome-list",
|
||||
"awesome-lists",
|
||||
"antigravity",
|
||||
"antigravity-skills",
|
||||
"autonomous-coding",
|
||||
"claude-code",
|
||||
"claude-code-skills",
|
||||
"codex-cli",
|
||||
"codex-skills",
|
||||
"cursor-skills",
|
||||
"developer-tools",
|
||||
"gemini-cli",
|
||||
"gemini-skills",
|
||||
"mcp",
|
||||
"ai-workflows",
|
||||
"skill-library",
|
||||
]
|
||||
README_TAGLINE_RE = re.compile(
|
||||
r"^> \*\*Installable GitHub library of \d[\d,]*\+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants\.\*\*$",
|
||||
re.MULTILINE,
|
||||
@@ -44,6 +68,55 @@ def build_about_description(metadata: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
def build_about_topics() -> list[str]:
|
||||
return list(RECOMMENDED_TOPICS)
|
||||
|
||||
|
||||
def run_cli_command(args: list[str], dry_run: bool = False) -> None:
|
||||
if dry_run:
|
||||
print(f"[dry-run] {' '.join(args)}")
|
||||
return
|
||||
|
||||
subprocess.run(args, check=True)
|
||||
|
||||
|
||||
def sync_github_about(
|
||||
metadata: dict,
|
||||
dry_run: bool,
|
||||
runner=run_cli_command,
|
||||
) -> None:
|
||||
description = build_about_description(metadata)
|
||||
repo = metadata["repo"]
|
||||
|
||||
runner(
|
||||
[
|
||||
"gh",
|
||||
"repo",
|
||||
"edit",
|
||||
repo,
|
||||
"--description",
|
||||
description,
|
||||
"--homepage",
|
||||
GITHUB_HOMEPAGE_URL,
|
||||
],
|
||||
dry_run=dry_run,
|
||||
)
|
||||
|
||||
topic_command = [
|
||||
"gh",
|
||||
"api",
|
||||
f"repos/{repo}/topics",
|
||||
"--method",
|
||||
"PUT",
|
||||
]
|
||||
for topic in build_about_topics():
|
||||
topic_command.extend(["-f", f"names[]={topic}"])
|
||||
runner(topic_command, dry_run=dry_run)
|
||||
|
||||
if not dry_run:
|
||||
print(f"✅ Synced GitHub About settings for {repo}")
|
||||
|
||||
|
||||
def replace_if_present(content: str, pattern: re.Pattern[str], replacement: str) -> tuple[str, bool]:
|
||||
updated_content, count = pattern.subn(replacement, content, count=1)
|
||||
return updated_content, count > 0
|
||||
@@ -271,7 +344,8 @@ def print_manual_github_about(metadata: dict) -> None:
|
||||
description = build_about_description(metadata)
|
||||
print("\nManual GitHub repo settings update:")
|
||||
print(f"- About description: {description}")
|
||||
print("- Suggested topics: claude-code, cursor, gemini-cli, codex-cli, github-copilot, antigravity")
|
||||
print(f"- Homepage: {GITHUB_HOMEPAGE_URL}")
|
||||
print(f"- Suggested topics: {', '.join(build_about_topics())}")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
@@ -284,6 +358,11 @@ def parse_args() -> argparse.Namespace:
|
||||
action="store_true",
|
||||
help="Refresh live star count and updated_at when syncing README metadata.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--apply-github-about",
|
||||
action="store_true",
|
||||
help="Apply the GitHub About description, homepage, and topics to the remote repository via gh CLI.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -300,6 +379,8 @@ def main() -> int:
|
||||
)
|
||||
package_updated = update_package_description(base_dir, metadata, args.dry_run)
|
||||
docs_updated = sync_curated_docs(base_dir, metadata, args.dry_run)
|
||||
if args.apply_github_about:
|
||||
sync_github_about(metadata, dry_run=args.dry_run)
|
||||
print_manual_github_about(readme_metadata)
|
||||
|
||||
if args.dry_run and not package_updated:
|
||||
|
||||
@@ -120,6 +120,37 @@ If you want a faster answer than "browse all 1,273+ skills", start with a tool-s
|
||||
self.assertIn("1,304+ agentic skills", description)
|
||||
self.assertIn("installer CLI", description)
|
||||
|
||||
def test_sync_github_about_builds_expected_commands(self):
|
||||
calls = []
|
||||
|
||||
def fake_runner(args, dry_run=False):
|
||||
calls.append((args, dry_run))
|
||||
|
||||
sync_repo_metadata.sync_github_about(
|
||||
{
|
||||
"repo": "sickn33/antigravity-awesome-skills",
|
||||
"total_skills_label": "1,304+",
|
||||
},
|
||||
dry_run=True,
|
||||
runner=fake_runner,
|
||||
)
|
||||
|
||||
self.assertEqual(len(calls), 2)
|
||||
repo_edit_args, repo_edit_dry_run = calls[0]
|
||||
topics_args, topics_dry_run = calls[1]
|
||||
|
||||
self.assertTrue(repo_edit_dry_run)
|
||||
self.assertTrue(topics_dry_run)
|
||||
self.assertEqual(repo_edit_args[:4], ["gh", "repo", "edit", "sickn33/antigravity-awesome-skills"])
|
||||
self.assertIn("--description", repo_edit_args)
|
||||
self.assertIn("--homepage", repo_edit_args)
|
||||
self.assertIn("https://sickn33.github.io/antigravity-awesome-skills/", repo_edit_args)
|
||||
|
||||
self.assertEqual(topics_args[:4], ["gh", "api", "repos/sickn33/antigravity-awesome-skills/topics", "--method"])
|
||||
self.assertIn("PUT", topics_args)
|
||||
self.assertIn("names[]=claude-code", topics_args)
|
||||
self.assertIn("names[]=skill-library", topics_args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user