]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
♻️ Refactor and move `scripts/notify_translations.py`, no need for a custom GitHub...
authorSebastián Ramírez <tiangolo@gmail.com>
Tue, 28 Jan 2025 21:47:33 +0000 (21:47 +0000)
committerGitHub <noreply@github.com>
Tue, 28 Jan 2025 21:47:33 +0000 (21:47 +0000)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
.github/actions/notify-translations/Dockerfile [deleted file]
.github/actions/notify-translations/action.yml [deleted file]
.github/workflows/notify-translations.yml
scripts/notify_translations.py [moved from .github/actions/notify-translations/app/main.py with 89% similarity]

diff --git a/.github/actions/notify-translations/Dockerfile b/.github/actions/notify-translations/Dockerfile
deleted file mode 100644 (file)
index b68b4bb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-FROM python:3.9
-
-RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,<6.0.0"
-
-COPY ./app /app
-
-CMD ["python", "/app/main.py"]
diff --git a/.github/actions/notify-translations/action.yml b/.github/actions/notify-translations/action.yml
deleted file mode 100644 (file)
index c357997..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-name: "Notify Translations"
-description: "Notify in the issue for a translation when there's a new PR available"
-author: "Sebastián Ramírez <tiangolo@gmail.com>"
-inputs:
-  token:
-    description: 'Token, to read the GitHub API. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
-    required: true
-runs:
-  using: 'docker'
-  image: 'Dockerfile'
index 187322bcaddb32cab66efe081cb42eab19c53dac..c969926897d6e7f8248f55ade96094386b395260 100644 (file)
@@ -15,15 +15,14 @@ on:
         required: false
         default: 'false'
 
-permissions:
-  discussions: write
-
 env:
   UV_SYSTEM_PYTHON: 1
 
 jobs:
-  notify-translations:
+  job:
     runs-on: ubuntu-latest
+    permissions:
+      discussions: write
     steps:
       - name: Dump GitHub context
         env:
@@ -42,12 +41,19 @@ jobs:
           cache-dependency-glob: |
             requirements**.txt
             pyproject.toml
+      - name: Install Dependencies
+        run: uv pip install -r requirements-github-actions.txt
       # Allow debugging with tmate
       - name: Setup tmate session
         uses: mxschmitt/action-tmate@v3
         if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
         with:
           limit-access-to-actor: true
-      - uses: ./.github/actions/notify-translations
-        with:
-          token: ${{ secrets.GITHUB_TOKEN }}
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      - name: Notify Translations
+        run: python ./scripts/notify_translations.py
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          NUMBER: ${{ github.event.inputs.number || null }}
+          DEBUG: ${{ github.event.inputs.debug_enabled || 'false' }}
similarity index 89%
rename from .github/actions/notify-translations/app/main.py
rename to scripts/notify_translations.py
index 716232d49a605dd620161fae57d233e5a1874508..7a43019a6e5a6c5f5cc864cb809d51e679489d31 100644 (file)
@@ -7,12 +7,13 @@ from typing import Any, Dict, List, Union, cast
 
 import httpx
 from github import Github
-from pydantic import BaseModel, BaseSettings, SecretStr
+from pydantic import BaseModel, SecretStr
+from pydantic_settings import BaseSettings
 
 awaiting_label = "awaiting-review"
 lang_all_label = "lang-all"
 approved_label = "approved-1"
-translations_path = Path(__file__).parent / "translations.yml"
+
 
 github_graphql_url = "https://api.github.com/graphql"
 questions_translations_category_id = "DIC_kwDOCZduT84CT5P9"
@@ -176,19 +177,20 @@ class AllDiscussionsResponse(BaseModel):
 
 class Settings(BaseSettings):
     github_repository: str
-    input_token: SecretStr
+    github_token: SecretStr
     github_event_path: Path
     github_event_name: Union[str, None] = None
     httpx_timeout: int = 30
-    input_debug: Union[bool, None] = False
+    debug: Union[bool, None] = False
+    number: int | None = None
 
 
 class PartialGitHubEventIssue(BaseModel):
-    number: int
+    number: int | None = None
 
 
 class PartialGitHubEvent(BaseModel):
-    pull_request: PartialGitHubEventIssue
+    pull_request: PartialGitHubEventIssue | None = None
 
 
 def get_graphql_response(
@@ -202,9 +204,7 @@ def get_graphql_response(
     comment_id: Union[str, None] = None,
     body: Union[str, None] = None,
 ) -> Dict[str, Any]:
-    headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
-    # some fields are only used by one query, but GraphQL allows unused variables, so
-    # keep them here for simplicity
+    headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"}
     variables = {
         "after": after,
         "category_id": category_id,
@@ -228,37 +228,40 @@ def get_graphql_response(
     data = response.json()
     if "errors" in data:
         logging.error(f"Errors in response, after: {after}, category_id: {category_id}")
+        logging.error(data["errors"])
         logging.error(response.text)
         raise RuntimeError(response.text)
     return cast(Dict[str, Any], data)
 
 
-def get_graphql_translation_discussions(*, settings: Settings):
+def get_graphql_translation_discussions(
+    *, settings: Settings
+) -> List[AllDiscussionsDiscussionNode]:
     data = get_graphql_response(
         settings=settings,
         query=all_discussions_query,
         category_id=questions_translations_category_id,
     )
-    graphql_response = AllDiscussionsResponse.parse_obj(data)
+    graphql_response = AllDiscussionsResponse.model_validate(data)
     return graphql_response.data.repository.discussions.nodes
 
 
 def get_graphql_translation_discussion_comments_edges(
     *, settings: Settings, discussion_number: int, after: Union[str, None] = None
-):
+) -> List[CommentsEdge]:
     data = get_graphql_response(
         settings=settings,
         query=translation_discussion_query,
         discussion_number=discussion_number,
         after=after,
     )
-    graphql_response = CommentsResponse.parse_obj(data)
+    graphql_response = CommentsResponse.model_validate(data)
     return graphql_response.data.repository.discussion.comments.edges
 
 
 def get_graphql_translation_discussion_comments(
     *, settings: Settings, discussion_number: int
-):
+) -> list[Comment]:
     comment_nodes: List[Comment] = []
     discussion_edges = get_graphql_translation_discussion_comments_edges(
         settings=settings, discussion_number=discussion_number
@@ -276,43 +279,49 @@ def get_graphql_translation_discussion_comments(
     return comment_nodes
 
 
-def create_comment(*, settings: Settings, discussion_id: str, body: str):
+def create_comment(*, settings: Settings, discussion_id: str, body: str) -> Comment:
     data = get_graphql_response(
         settings=settings,
         query=add_comment_mutation,
         discussion_id=discussion_id,
         body=body,
     )
-    response = AddCommentResponse.parse_obj(data)
+    response = AddCommentResponse.model_validate(data)
     return response.data.addDiscussionComment.comment
 
 
-def update_comment(*, settings: Settings, comment_id: str, body: str):
+def update_comment(*, settings: Settings, comment_id: str, body: str) -> Comment:
     data = get_graphql_response(
         settings=settings,
         query=update_comment_mutation,
         comment_id=comment_id,
         body=body,
     )
-    response = UpdateCommentResponse.parse_obj(data)
+    response = UpdateCommentResponse.model_validate(data)
     return response.data.updateDiscussionComment.comment
 
 
-if __name__ == "__main__":
+def main() -> None:
     settings = Settings()
-    if settings.input_debug:
+    if settings.debug:
         logging.basicConfig(level=logging.DEBUG)
     else:
         logging.basicConfig(level=logging.INFO)
-    logging.debug(f"Using config: {settings.json()}")
-    g = Github(settings.input_token.get_secret_value())
+    logging.debug(f"Using config: {settings.model_dump_json()}")
+    g = Github(settings.github_token.get_secret_value())
     repo = g.get_repo(settings.github_repository)
     if not settings.github_event_path.is_file():
         raise RuntimeError(
             f"No github event file available at: {settings.github_event_path}"
         )
     contents = settings.github_event_path.read_text()
-    github_event = PartialGitHubEvent.parse_raw(contents)
+    github_event = PartialGitHubEvent.model_validate_json(contents)
+    logging.info(f"Using GitHub event: {github_event}")
+    number = (
+        github_event.pull_request and github_event.pull_request.number
+    ) or settings.number
+    if number is None:
+        raise RuntimeError("No PR number available")
 
     # Avoid race conditions with multiple labels
     sleep_time = random.random() * 10  # random number between 0 and 10 seconds
@@ -323,8 +332,8 @@ if __name__ == "__main__":
     time.sleep(sleep_time)
 
     # Get PR
-    logging.debug(f"Processing PR: #{github_event.pull_request.number}")
-    pr = repo.get_pull(github_event.pull_request.number)
+    logging.debug(f"Processing PR: #{number}")
+    pr = repo.get_pull(number)
     label_strs = {label.name for label in pr.get_labels()}
     langs = []
     for label in label_strs:
@@ -415,3 +424,7 @@ if __name__ == "__main__":
                 f"There doesn't seem to be anything to be done about PR #{pr.number}"
             )
     logging.info("Finished")
+
+
+if __name__ == "__main__":
+    main()