👷 Add custom GitHub actions to preview docs

This commit is contained in:
Sebastián Ramírez 2021-08-24 16:05:55 +02:00
parent 343c326d59
commit 0a5e56cb06
6 changed files with 209 additions and 0 deletions

View File

@ -0,0 +1,7 @@
FROM python:3.7
RUN pip install httpx "pydantic==1.5.1" pygithub
COPY ./app /app
CMD ["python", "/app/main.py"]

View File

@ -0,0 +1,13 @@
name: Comment Docs Preview in PR
description: Comment with the docs URL preview in the PR
author: Sebastián Ramírez <tiangolo@gmail.com>
inputs:
token:
description: Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}
required: true
deploy_url:
description: The deployment URL to comment in the PR
required: true
runs:
using: docker
image: Dockerfile

View File

@ -0,0 +1,70 @@
import logging
import sys
from pathlib import Path
from typing import Optional
import httpx
from github import Github
from github.PullRequest import PullRequest
from pydantic import BaseModel, BaseSettings, SecretStr, ValidationError
github_api = "https://api.github.com"
class Settings(BaseSettings):
github_repository: str
github_event_path: Path
github_event_name: Optional[str] = None
input_token: SecretStr
input_deploy_url: str
class PartialGithubEventHeadCommit(BaseModel):
id: str
class PartialGithubEventWorkflowRun(BaseModel):
head_commit: PartialGithubEventHeadCommit
class PartialGithubEvent(BaseModel):
workflow_run: PartialGithubEventWorkflowRun
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
settings = Settings()
logging.info(f"Using config: {settings.json()}")
g = Github(settings.input_token.get_secret_value())
repo = g.get_repo(settings.github_repository)
try:
event = PartialGithubEvent.parse_file(settings.github_event_path)
except ValidationError as e:
logging.error(f"Error parsing event file: {e.errors()}")
sys.exit(0)
use_pr: Optional[PullRequest] = None
for pr in repo.get_pulls():
if pr.head.sha == event.workflow_run.head_commit.id:
use_pr = pr
break
if not use_pr:
logging.error(
f"No PR found for hash: {event.workflow_run.head_commit.id}"
)
sys.exit(0)
github_headers = {
"Authorization": f"token {settings.input_token.get_secret_value()}"
}
url = f"{github_api}/repos/{settings.github_repository}/issues/{use_pr.number}/comments"
logging.info(f"Using comments URL: {url}")
response = httpx.post(
url,
headers=github_headers,
json={
"body": f"📝 Docs preview for commit {use_pr.head.sha} at: {settings.input_deploy_url}"
},
)
if not (200 <= response.status_code <= 300):
logging.error(f"Error posting comment: {response.text}")
sys.exit(1)
logging.info("Finished")

View File

@ -0,0 +1,7 @@
FROM python:3.7
RUN pip install httpx PyGithub "pydantic==1.5.1"
COPY ./app /app
CMD ["python", "/app/main.py"]

View File

@ -0,0 +1,10 @@
name: Watch docs previews in PRs
description: Check PRs and trigger new docs deploys
author: "Sebastián Ramírez <tiangolo@gmail.com>"
inputs:
token:
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
required: true
runs:
using: docker
image: Dockerfile

View File

@ -0,0 +1,102 @@
import logging
from datetime import datetime
from pathlib import Path
from typing import List, Optional
import httpx
from github import Github
from github.NamedUser import NamedUser
from pydantic import BaseModel, BaseSettings, SecretStr
github_api = "https://api.github.com"
netlify_api = "https://api.netlify.com"
main_branch = "main"
class Settings(BaseSettings):
input_token: SecretStr
github_repository: str
github_event_path: Path
github_event_name: Optional[str] = None
class Artifact(BaseModel):
id: int
node_id: str
name: str
size_in_bytes: int
url: str
archive_download_url: str
expired: bool
created_at: datetime
updated_at: datetime
class ArtifactResponse(BaseModel):
total_count: int
artifacts: List[Artifact]
def get_message(commit: str) -> str:
return f"Docs preview for commit {commit} at"
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
settings = Settings()
logging.info(f"Using config: {settings.json()}")
g = Github(settings.input_token.get_secret_value())
repo = g.get_repo(settings.github_repository)
owner: NamedUser = repo.owner
headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
prs = list(repo.get_pulls(state="open"))
response = httpx.get(
f"{github_api}/repos/{settings.github_repository}/actions/artifacts",
headers=headers,
)
data = response.json()
artifacts_response = ArtifactResponse.parse_obj(data)
for pr in prs:
logging.info("-----")
logging.info(f"Processing PR #{pr.number}: {pr.title}")
pr_comments = list(pr.get_issue_comments())
pr_commits = list(pr.get_commits())
last_commit = pr_commits[0]
for pr_commit in pr_commits:
if pr_commit.commit.author.date > last_commit.commit.author.date:
last_commit = pr_commit
commit = last_commit.commit.sha
logging.info(f"Last commit: {commit}")
message = get_message(commit)
notified = False
for pr_comment in pr_comments:
if message in pr_comment.body:
notified = True
logging.info(f"Docs preview was notified: {notified}")
if not notified:
artifact_name = f"docs-zip-{commit}"
use_artifact: Optional[Artifact] = None
for artifact in artifacts_response.artifacts:
if artifact.name == artifact_name:
use_artifact = artifact
break
if not use_artifact:
logging.info("Artifact not available")
else:
logging.info(f"Existing artifact: {use_artifact.name}")
response = httpx.post(
f"{github_api}/repos/{settings.github_repository}/actions/workflows/preview-docs.yml/dispatches",
headers=headers,
json={
"ref": main_branch,
"inputs": {
"pr": f"{pr.number}",
"name": artifact_name,
"commit": commit,
},
},
)
logging.info(
f"Trigger sent, response status: {response.status_code} - content: {response.content}"
)
logging.info("Finished")