-
Notifications
You must be signed in to change notification settings - Fork 0
/
run.py
155 lines (136 loc) · 5.68 KB
/
run.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import base64
import json
import os
import pathlib
from datetime import datetime
from typing import Tuple
import git
import requests
import monitor
from models.constants import LOGGER, env, static
def get_index_file() -> bytes:
"""Reads the index file and returns the data as bytes."""
try:
with open(static.INDEX_FILE, "rb") as file:
return base64.b64encode(file.read())
except FileNotFoundError as error:
LOGGER.critical(error)
class GitHub:
"""GitHub's operations including GitPython and GH API.
>>> GitHub
"""
def __init__(self):
"""Instantiates a session with GitHub API and authenticates GitPython."""
self.session = requests.Session()
self.session.headers = {
"Authorization": "token " + env.git_token,
"Content-Type": "application/json",
}
local_repo = pathlib.Path(os.getcwd())
self.repository = git.Repo(local_repo)
self.origin = self.repository.remote(name="origin")
self.origin.config_writer.set(
"url",
f"https://{env.git_user}:{env.git_token}@github.com/{env.git_owner}/{local_repo.name}.git",
)
self.origin.config_writer.release()
def head_branch(self) -> None:
"""Check and create docs branch if not available."""
self.repository.remotes.origin.fetch(prune=True)
remote_branches = [ref.name for ref in self.repository.remote("origin").refs]
if f"origin/{static.DOCS_BRANCH}" in remote_branches:
LOGGER.debug("Branch '%s' already exists remotely.", static.DOCS_BRANCH)
return
if static.DOCS_BRANCH in self.repository.heads:
LOGGER.info(
"Branch '%s' exists locally, but not on remote. Deleting local branch.",
static.DOCS_BRANCH,
)
self.repository.delete_head(static.DOCS_BRANCH, force=True)
base_branch = self.repository.heads[self.repository.active_branch.name]
new_branch = self.repository.create_head(
static.DOCS_BRANCH, base_branch.commit.hexsha
)
self.origin.push(new_branch.name)
LOGGER.info("Branch '%s' created and pushed to remote.", static.DOCS_BRANCH)
def git_push(self, sha: str, content: str) -> requests.Response:
"""Performs git push and returns the response object.
Notes:
GH API is used to perform git push, since there is no way to push changes to a branch without checking out.
"""
LOGGER.info("Pushing changes to GitHub")
payload = {
"message": static.COMMIT_MESSAGE,
"branch": static.DOCS_BRANCH,
"content": content,
}
if sha:
payload["sha"] = sha
return self.session.put(static.INDEX_URL, data=json.dumps(payload))
def get_origin_file(self) -> Tuple[bytes, str]:
"""Gets the commit object for the remote branch, and retrieves the file content from commit tree.
Returns:
Tuple[bytes, str]:
Returns a tuple of file content as bytes, and the commit SHA.
"""
commit = self.repository.commit(f"origin/{static.DOCS_BRANCH}")
# The / operator is overloaded in GitPython to allow easy traversal of the tree structure
# The / expression navigates through the tree to find the file located at docs/index.html
target_file = commit.tree / "docs/index.html"
file_content = target_file.data_stream.read()
file_sha = target_file.hexsha
return base64.b64encode(file_content), file_sha
def push_to_github(self):
"""Commit and push to GitHub."""
if local_content := get_index_file():
self.head_branch()
else:
return
try:
remote_content, sha = self.get_origin_file()
# push only when there are changes
if env.check_existing:
push = local_content != remote_content
else:
push = True
if datetime.now().minute not in env.override_check:
LOGGER.warning(
"Check existing is set to False, this will push to origin regardless of changes!"
)
except KeyError as error:
# File is missing in docs branch, perhaps newly created head branch
LOGGER.warning(error)
LOGGER.warning("Creating a new file in %s branch", static.DOCS_BRANCH)
sha = None
push = True
except git.BadName as warning:
LOGGER.critical(warning)
LOGGER.critical(
"Branch '%s' doesn't exist, but it should have been covered by '%s'",
static.DOCS_BRANCH,
self.head_branch.__name__,
)
push = True
sha = None
if push:
push_response = self.git_push(sha, local_content.decode("utf-8"))
json_response = push_response.json()
if push_response.ok:
LOGGER.info("Updated %s branch with changes", static.DOCS_BRANCH)
LOGGER.debug(push_response.json())
else:
LOGGER.critical("%s - %s", push_response.status_code, json_response)
else:
LOGGER.info("Nothing to push")
# Delete the file since there is no branch checkout happening
os.remove(static.INDEX_FILE)
def entrypoint():
"""Entrypoint for the monitor."""
if env.skip_schedule == datetime.now().strftime(static.SKIPPER_FORMAT):
LOGGER.info("Schedule ignored at '%s'", env.skip_schedule)
else:
monitor.main()
github = GitHub()
github.push_to_github()
if __name__ == "__main__":
entrypoint()