]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
👷‍♀️ Add script for GitHub Topic Repositories and update External Links (#13135)
authorAlejandra <90076947+alejsdev@users.noreply.github.com>
Thu, 2 Jan 2025 20:44:50 +0000 (20:44 +0000)
committerGitHub <noreply@github.com>
Thu, 2 Jan 2025 20:44:50 +0000 (20:44 +0000)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
.github/workflows/topic-repos.yml [new file with mode: 0644]
docs/en/data/topic_repos.yml [new file with mode: 0644]
docs/en/docs/external-links.md
docs/en/docs/js/custom.js
docs/en/mkdocs.yml
scripts/topic_repos.py [new file with mode: 0644]

diff --git a/.github/workflows/topic-repos.yml b/.github/workflows/topic-repos.yml
new file mode 100644 (file)
index 0000000..3c5c881
--- /dev/null
@@ -0,0 +1,40 @@
+name: Update Topic Repos
+
+on:
+  schedule:
+    - cron: "0 12 1 * *"
+  workflow_dispatch:
+
+env:
+  UV_SYSTEM_PYTHON: 1
+
+jobs:
+  topic-repos:
+    if: github.repository_owner == 'fastapi'
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+    steps:
+      - name: Dump GitHub context
+        env:
+          GITHUB_CONTEXT: ${{ toJson(github) }}
+        run: echo "$GITHUB_CONTEXT"
+      - uses: actions/checkout@v4
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.11"
+      - name: Setup uv
+        uses: astral-sh/setup-uv@v5
+        with:
+          version: "0.4.15"
+          enable-cache: true
+          cache-dependency-glob: |
+            requirements**.txt
+            pyproject.toml
+      - name: Install GitHub Actions dependencies
+        run: uv pip install -r requirements-github-actions.txt
+      - name: Update Topic Repos
+        run: python ./scripts/topic_repos.py
+        env:
+          GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }}
diff --git a/docs/en/data/topic_repos.yml b/docs/en/data/topic_repos.yml
new file mode 100644 (file)
index 0000000..e69de29
index 5a3b8ee33d2fd152977ba2e176da8a0b0e1b7445..3ed04e5c551d781fe08be67a5e21aa7aa5b3d14a 100644 (file)
@@ -28,9 +28,12 @@ If you have an article, project, tool, or anything related to **FastAPI** that i
 {% endfor %}
 {% endfor %}
 
-## Projects
+## GitHub Repositories
 
-Latest GitHub projects with the topic `fastapi`:
+Most starred GitHub repositories with the topic `fastapi`:
 
-<div class="github-topic-projects">
-</div>
+{% for repo in topic_repos %}
+
+<a href={{repo.html_url}} target="_blank">★ {{repo.stars}} - {{repo.name}}</a> by <a href={{repo.owner_html_url}} target="_blank">@{{repo.owner_login}}</a>.
+
+{% endfor %}
index ff17710e298ab19802270747b7152f10e2b58210..4c0ada312e00a68fc31870d35bad29660f296ddb 100644 (file)
@@ -1,25 +1,3 @@
-const div = document.querySelector('.github-topic-projects')
-
-async function getDataBatch(page) {
-    const response = await fetch(`https://api.github.com/search/repositories?q=topic:fastapi&per_page=100&page=${page}`, { headers: { Accept: 'application/vnd.github.mercy-preview+json' } })
-    const data = await response.json()
-    return data
-}
-
-async function getData() {
-    let page = 1
-    let data = []
-    let dataBatch = await getDataBatch(page)
-    data = data.concat(dataBatch.items)
-    const totalCount = dataBatch.total_count
-    while (data.length < totalCount) {
-        page += 1
-        dataBatch = await getDataBatch(page)
-        data = data.concat(dataBatch.items)
-    }
-    return data
-}
-
 function setupTermynal() {
     document.querySelectorAll(".use-termynal").forEach(node => {
         node.style.display = "block";
@@ -158,20 +136,6 @@ async function showRandomAnnouncement(groupId, timeInterval) {
 }
 
 async function main() {
-    if (div) {
-        data = await getData()
-        div.innerHTML = '<ul></ul>'
-        const ul = document.querySelector('.github-topic-projects ul')
-        data.forEach(v => {
-            if (v.full_name === 'fastapi/fastapi') {
-                return
-            }
-            const li = document.createElement('li')
-            li.innerHTML = `<a href="${v.html_url}" target="_blank">★ ${v.stargazers_count} - ${v.full_name}</a> by <a href="${v.owner.html_url}" target="_blank">@${v.owner.login}</a>`
-            ul.append(li)
-        })
-    }
-
     setupTermynal();
     showRandomAnnouncement('announce-left', 5000)
     showRandomAnnouncement('announce-right', 10000)
index f2abf7f6b966140e18e9734f254e1a08cd726e33..e9a639d0b24afbd205b0e3d0f1e190bff0ee2a17 100644 (file)
@@ -72,6 +72,7 @@ plugins:
     - members: ../en/data/members.yml
     - sponsors_badge: ../en/data/sponsors_badge.yml
     - sponsors: ../en/data/sponsors.yml
+    - topic_repos: ../en/data/topic_repos.yml
   redirects:
     redirect_maps:
       deployment/deta.md: deployment/cloud.md
diff --git a/scripts/topic_repos.py b/scripts/topic_repos.py
new file mode 100644 (file)
index 0000000..bc14977
--- /dev/null
@@ -0,0 +1,80 @@
+import logging
+import secrets
+import subprocess
+from pathlib import Path
+
+import yaml
+from github import Github
+from pydantic import BaseModel, SecretStr
+from pydantic_settings import BaseSettings
+
+
+class Settings(BaseSettings):
+    github_repository: str
+    github_token: SecretStr
+
+
+class Repo(BaseModel):
+    name: str
+    html_url: str
+    stars: int
+    owner_login: str
+    owner_html_url: str
+
+
+def main() -> None:
+    logging.basicConfig(level=logging.INFO)
+    settings = Settings()
+
+    logging.info(f"Using config: {settings.model_dump_json()}")
+    g = Github(settings.github_token.get_secret_value(), per_page=100)
+    r = g.get_repo(settings.github_repository)
+    repos = g.search_repositories(query="topic:fastapi")
+    repos_list = list(repos)
+    final_repos: list[Repo] = []
+    for repo in repos_list[:100]:
+        if repo.full_name == settings.github_repository:
+            continue
+        final_repos.append(
+            Repo(
+                name=repo.name,
+                html_url=repo.html_url,
+                stars=repo.stargazers_count,
+                owner_login=repo.owner.login,
+                owner_html_url=repo.owner.html_url,
+            )
+        )
+    data = [repo.model_dump() for repo in final_repos]
+
+    # Local development
+    # repos_path = Path("../docs/en/data/topic_repos.yml")
+    repos_path = Path("./docs/en/data/topic_repos.yml")
+    repos_old_content = repos_path.read_text(encoding="utf-8")
+    new_repos_content = yaml.dump(data, sort_keys=False, width=200, allow_unicode=True)
+    if repos_old_content == new_repos_content:
+        logging.info("The data hasn't changed. Finishing.")
+        return
+    repos_path.write_text(new_repos_content, encoding="utf-8")
+    logging.info("Setting up GitHub Actions git user")
+    subprocess.run(["git", "config", "user.name", "github-actions"], check=True)
+    subprocess.run(
+        ["git", "config", "user.email", "github-actions@github.com"], check=True
+    )
+    branch_name = f"fastapi-topic-repos-{secrets.token_hex(4)}"
+    logging.info(f"Creating a new branch {branch_name}")
+    subprocess.run(["git", "checkout", "-b", branch_name], check=True)
+    logging.info("Adding updated file")
+    subprocess.run(["git", "add", str(repos_path)], check=True)
+    logging.info("Committing updated file")
+    message = "👥 Update FastAPI GitHub topic repositories"
+    subprocess.run(["git", "commit", "-m", message], check=True)
+    logging.info("Pushing branch")
+    subprocess.run(["git", "push", "origin", branch_name], check=True)
+    logging.info("Creating PR")
+    pr = r.create_pull(title=message, body=message, base="master", head=branch_name)
+    logging.info(f"Created PR: {pr.number}")
+    logging.info("Finished")
+
+
+if __name__ == "__main__":
+    main()