clean up the code a fair bit
This commit is contained in:
parent
6e8a193f8f
commit
4804488943
|
@ -4,7 +4,9 @@ from flask import redirect
|
||||||
from flask import abort
|
from flask import abort
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import base64
|
|
||||||
|
from .webgit import Gitea, Github
|
||||||
|
from .utils import issue_sentinel, generate_sentinel
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.from_envvar('GIT_BRIDGE_SETTINGS')
|
app.config.from_envvar('GIT_BRIDGE_SETTINGS')
|
||||||
|
@ -15,6 +17,19 @@ def index():
|
||||||
|
|
||||||
@app.route("/bridge/endpoints/gitea/repo", methods=["POST"])
|
@app.route("/bridge/endpoints/gitea/repo", methods=["POST"])
|
||||||
def gitea_handle_repo_action():
|
def gitea_handle_repo_action():
|
||||||
|
"""
|
||||||
|
Our plan of action for handling these events:
|
||||||
|
- ignore deleting repositories -- this is a potentially destructive operation
|
||||||
|
that *only* *the* *user* *should* *do*
|
||||||
|
- create a cooresponding repo on github
|
||||||
|
- create a push mirror to github so pushes to gitea get to github
|
||||||
|
- create webhooks for issues (and in the future, pull requests) on both
|
||||||
|
Gitea and Github ends
|
||||||
|
"""
|
||||||
|
|
||||||
|
gitea = Gitea(app.config["GITEA_ACCESS_TOKEN"])
|
||||||
|
github = Github(app.config["GITHUB_ACCESS_TOKEN"])
|
||||||
|
|
||||||
data = request.json
|
data = request.json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -29,20 +44,10 @@ def gitea_handle_repo_action():
|
||||||
except KeyError:
|
except KeyError:
|
||||||
abort(400) # the data isn't formatted correctly
|
abort(400) # the data isn't formatted correctly
|
||||||
|
|
||||||
"""
|
|
||||||
Our plan of action for handling these events:
|
|
||||||
- ignore deleting repositories -- this is a potentially destructive operation
|
|
||||||
that *only* *the* *user* *should* *do*
|
|
||||||
- create a cooresponding repo on github
|
|
||||||
- create a push mirror to github so pushes to gitea get to github
|
|
||||||
- create webhooks for issues (and in the future, pull requests) on both
|
|
||||||
Gitea and Github ends
|
|
||||||
"""
|
|
||||||
|
|
||||||
if not repo_action == "created":
|
if not repo_action == "created":
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
github_created_repo_result = requests.post(
|
github_created_repo_result = github.post(
|
||||||
"https://api.github.com/user/repos",
|
"https://api.github.com/user/repos",
|
||||||
json={
|
json={
|
||||||
"name": repo_name,
|
"name": repo_name,
|
||||||
|
@ -53,21 +58,11 @@ def gitea_handle_repo_action():
|
||||||
repo_name,
|
repo_name,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Accept": "application/vnd.github+json",
|
|
||||||
"Authorization": "token " + app.config["GITHUB_ACCESS_TOKEN"],
|
|
||||||
"X-GitHub-Api-Version": "2022-11-28",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
|
||||||
github_created_repo_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
new_github_repo_url = github_created_repo_result.json()["html_url"]
|
new_github_repo_url = github_created_repo_result.json()["html_url"]
|
||||||
|
|
||||||
gitea_add_github_repo_url_result = requests.patch(
|
gitea_add_github_repo_url_result = gitea.patch(
|
||||||
"https://{}/api/v1/repos/{}/{}".format(
|
"https://{}/api/v1/repos/{}/{}".format(
|
||||||
app.config["GITEA_INSTANCE_DOMAIN"],
|
app.config["GITEA_INSTANCE_DOMAIN"],
|
||||||
repo_owner,
|
repo_owner,
|
||||||
|
@ -76,17 +71,9 @@ def gitea_handle_repo_action():
|
||||||
json={
|
json={
|
||||||
"website": new_github_repo_url,
|
"website": new_github_repo_url,
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Authorization": "token " + app.config["GITEA_ACCESS_TOKEN"],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
gitea_push_target_result = gitea.post(
|
||||||
gitea_add_github_repo_url_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
gitea_push_target_result = requests.post(
|
|
||||||
"https://{}/api/v1/repos/{}/{}/push_mirrors".format(
|
"https://{}/api/v1/repos/{}/{}/push_mirrors".format(
|
||||||
app.config["GITEA_INSTANCE_DOMAIN"],
|
app.config["GITEA_INSTANCE_DOMAIN"],
|
||||||
repo_owner,
|
repo_owner,
|
||||||
|
@ -99,33 +86,17 @@ def gitea_handle_repo_action():
|
||||||
"remote_username": repo_owner,
|
"remote_username": repo_owner,
|
||||||
"sync_on_commit": True,
|
"sync_on_commit": True,
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Authorization": "token " + app.config["GITEA_ACCESS_TOKEN"],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
gitea_force_target_push = gitea.post(
|
||||||
gitea_push_target_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
gitea_force_target_push = requests.post(
|
|
||||||
"https://{}/api/v1/repos/{}/{}/push_mirrors-sync".format(
|
"https://{}/api/v1/repos/{}/{}/push_mirrors-sync".format(
|
||||||
app.config["GITEA_INSTANCE_DOMAIN"],
|
app.config["GITEA_INSTANCE_DOMAIN"],
|
||||||
repo_owner,
|
repo_owner,
|
||||||
repo_name
|
repo_name
|
||||||
),
|
),
|
||||||
headers={
|
|
||||||
"Authorization": "token " + app.config["GITEA_ACCESS_TOKEN"],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
github_create_webhook_result = github.post(
|
||||||
gitea_force_target_push.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
github_create_webhook_result = requests.post(
|
|
||||||
"https://api.github.com/repos/{}/{}/hooks".format(
|
"https://api.github.com/repos/{}/{}/hooks".format(
|
||||||
repo_owner,
|
repo_owner,
|
||||||
repo_name,
|
repo_name,
|
||||||
|
@ -142,19 +113,9 @@ def gitea_handle_repo_action():
|
||||||
"issues", "issue_comment",
|
"issues", "issue_comment",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Accept": "application/vnd.github+json",
|
|
||||||
"Authorization": "token " + app.config["GITHUB_ACCESS_TOKEN"],
|
|
||||||
"X-GitHub-Api-Version": "2022-11-28",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
gitea_create_webhook_result = gitea.post(
|
||||||
github_create_webhook_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
gitea_create_webhook_result = requests.post(
|
|
||||||
"https://{}/api/v1/repos/{}/{}/hooks".format(
|
"https://{}/api/v1/repos/{}/{}/hooks".format(
|
||||||
app.config["GITEA_INSTANCE_DOMAIN"],
|
app.config["GITEA_INSTANCE_DOMAIN"],
|
||||||
repo_owner,
|
repo_owner,
|
||||||
|
@ -174,20 +135,31 @@ def gitea_handle_repo_action():
|
||||||
"issues", "issue_comment",
|
"issues", "issue_comment",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Authorization": "token " + app.config["GITEA_ACCESS_TOKEN"],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
|
||||||
gitea_create_webhook_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
@app.route("/bridge/endpoints/gitea/issue", methods=["POST"])
|
@app.route("/bridge/endpoints/gitea/issue", methods=["POST"])
|
||||||
def gitea_handle_issue_action():
|
def gitea_handle_issue_action():
|
||||||
|
"""
|
||||||
|
firstly, check if the sentinal is in the issue body
|
||||||
|
- if it is, then stop processing the event
|
||||||
|
(we don't want infinite loops)
|
||||||
|
if we've opened an issue:
|
||||||
|
- create a new one on the Github side
|
||||||
|
- make it relate to the Gitea one
|
||||||
|
- make the Gitea one related to the Github one
|
||||||
|
|
||||||
|
if we've commented:
|
||||||
|
- add a comment to the cooresponding Github issue
|
||||||
|
|
||||||
|
if we've closed:
|
||||||
|
- add a cooresponding comment and close the Github issue
|
||||||
|
"""
|
||||||
|
|
||||||
|
gitea = Gitea(app.config["GITEA_ACCESS_TOKEN"])
|
||||||
|
github = Github(app.config["GITHUB_ACCESS_TOKEN"])
|
||||||
|
|
||||||
data = request.json
|
data = request.json
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -218,23 +190,7 @@ def gitea_handle_issue_action():
|
||||||
print(e, type(e))
|
print(e, type(e))
|
||||||
abort(400) # the data isn't formatted correctly
|
abort(400) # the data isn't formatted correctly
|
||||||
|
|
||||||
"""
|
if issue_sentinel in event_body:
|
||||||
firstly, check if the sentinal is in the issue body
|
|
||||||
- if it is, then stop processing the event
|
|
||||||
(we don't want infinite loops)
|
|
||||||
if we've opened an issue:
|
|
||||||
- create a new one on the Github side
|
|
||||||
- make it relate to the Gitea one
|
|
||||||
- make the Gitea one related to the Github one
|
|
||||||
|
|
||||||
if we've commented:
|
|
||||||
- add a comment to the cooresponding Github issue
|
|
||||||
|
|
||||||
if we've closed:
|
|
||||||
- add a cooresponding comment and close the Github issue
|
|
||||||
"""
|
|
||||||
|
|
||||||
if "GITEA_GITHUB_ISSUE_SYNC_SENTINAL" in event_body:
|
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
if event_type == "opened":
|
if event_type == "opened":
|
||||||
|
@ -248,9 +204,9 @@ def gitea_handle_issue_action():
|
||||||
<details>
|
<details>
|
||||||
<summary>Internal issue metadata</summary>
|
<summary>Internal issue metadata</summary>
|
||||||
|
|
||||||
GITEA_GITHUB_ISSUE_SYNC_SENTINAL {}
|
{}
|
||||||
</details>
|
</details>
|
||||||
""".format(base64.b64encode(event_url.encode("utf-8")).decode("utf-8"))
|
""".format(generate_sentinel(event_url))
|
||||||
|
|
||||||
issue_body = "\n\n".join([
|
issue_body = "\n\n".join([
|
||||||
issue_header,
|
issue_header,
|
||||||
|
@ -258,7 +214,7 @@ def gitea_handle_issue_action():
|
||||||
issue_footer
|
issue_footer
|
||||||
])
|
])
|
||||||
|
|
||||||
github_create_issue_result = requests.post(
|
github_create_issue_result = github.post(
|
||||||
"https://api.github.com/repos/{}/{}/issues".format(
|
"https://api.github.com/repos/{}/{}/issues".format(
|
||||||
repo_owner,
|
repo_owner,
|
||||||
repo_name,
|
repo_name,
|
||||||
|
@ -267,18 +223,8 @@ def gitea_handle_issue_action():
|
||||||
"title": event_title,
|
"title": event_title,
|
||||||
"body": issue_body,
|
"body": issue_body,
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Accept": "application/vnd.github+json",
|
|
||||||
"Authorization": "token " + app.config["GITHUB_ACCESS_TOKEN"],
|
|
||||||
"X-GitHub-Api-Version": "2022-11-28",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
|
||||||
github_create_issue_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
returned_data = github_create_issue_result.json()
|
returned_data = github_create_issue_result.json()
|
||||||
issue_comment_body = """
|
issue_comment_body = """
|
||||||
*This issue is being mirrored on Github [here]({}).*
|
*This issue is being mirrored on Github [here]({}).*
|
||||||
|
@ -286,14 +232,14 @@ def gitea_handle_issue_action():
|
||||||
<details>
|
<details>
|
||||||
<summary>Internal issue metadata</summary>
|
<summary>Internal issue metadata</summary>
|
||||||
|
|
||||||
GITEA_GITHUB_ISSUE_SYNC_SENTINEL {}
|
{}
|
||||||
</details>
|
</details>
|
||||||
""".format(
|
""".format(
|
||||||
returned_data["html_url"],
|
returned_data["html_url"],
|
||||||
base64.b64encode(returned_data["url"].encode("utf-8")).decode("utf-8")
|
generate_sentinel(returned_data["url"])
|
||||||
)
|
)
|
||||||
|
|
||||||
gitea_issue_comment_result = requests.post(
|
gitea_issue_comment_result = gitea.post(
|
||||||
"https://{}/api/v1/repos/{}/{}/issues/{}/comments".format(
|
"https://{}/api/v1/repos/{}/{}/issues/{}/comments".format(
|
||||||
app.config["GITEA_INSTANCE_DOMAIN"],
|
app.config["GITEA_INSTANCE_DOMAIN"],
|
||||||
repo_owner,
|
repo_owner,
|
||||||
|
@ -303,14 +249,6 @@ def gitea_handle_issue_action():
|
||||||
json={
|
json={
|
||||||
"body": issue_comment_body,
|
"body": issue_comment_body,
|
||||||
},
|
},
|
||||||
headers={
|
|
||||||
"Authorization": "token " + app.config["GITEA_ACCESS_TOKEN"],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
return ''
|
||||||
gitea_issue_comment_result.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError:
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
return ''
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import base64
|
||||||
|
|
||||||
|
def to_base64(s: str) -> str:
|
||||||
|
return base64.b64encode(
|
||||||
|
s.encode("utf-8")
|
||||||
|
).decode("utf-8")
|
||||||
|
|
||||||
|
def from_base64(s: str) -> str:
|
||||||
|
return base64.b64decode(
|
||||||
|
s.encode("utf-8")
|
||||||
|
).decode("utf-8")
|
||||||
|
|
||||||
|
issue_sentinel = "GITEA_GITHUB_ISSUE_SYNC_SENTINEL"
|
||||||
|
|
||||||
|
def generate_sentinel(url: str) -> str:
|
||||||
|
return ' '.join([sentinel, to_base64(url)])
|
|
@ -0,0 +1,65 @@
|
||||||
|
from typing import Any
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests import Request, Session
|
||||||
|
from requests.exceptions import HTTPError
|
||||||
|
|
||||||
|
from flask import abort
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WebgitClient:
|
||||||
|
"""
|
||||||
|
A quite thin wrapper around various code forges' REST APIs.
|
||||||
|
Designed to be subclassed.
|
||||||
|
"""
|
||||||
|
api_token: str
|
||||||
|
headers: Any = field(init=False)
|
||||||
|
|
||||||
|
def _post_request(self, request_obj):
|
||||||
|
try:
|
||||||
|
request_obj.raise_for_status()
|
||||||
|
except HTTPError as e:
|
||||||
|
print("An exception occured: {}({})".format(
|
||||||
|
type(e).__name__, e
|
||||||
|
))
|
||||||
|
abort(500)
|
||||||
|
|
||||||
|
def _request_wrapper(self, method, *args, **kwargs):
|
||||||
|
s = Session()
|
||||||
|
r = Request(method, *args, **kwargs, headers=self.headers)
|
||||||
|
prepped = s.prepare_request(r)
|
||||||
|
settings = s.merge_environment_settings(prepped.url, {}, None, None, None)
|
||||||
|
r = s.send(prepped, **settings)
|
||||||
|
|
||||||
|
self._post_request(r)
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def post(self, *args, **kwargs):
|
||||||
|
return self._request_wrapper("POST", *args, **kwargs)
|
||||||
|
|
||||||
|
def patch(self, *args, **kwargs):
|
||||||
|
return self._request_wrapper("PATCH", *args, **kwargs)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Github(WebgitClient):
|
||||||
|
"""
|
||||||
|
A quite thin wrapper around Github's REST API.
|
||||||
|
"""
|
||||||
|
def __post_init__(self):
|
||||||
|
self.headers = {
|
||||||
|
"Accept": "application/vnd.github+json",
|
||||||
|
"Authorization": "token " + self.api_token,
|
||||||
|
"X-GitHub-Api-Version": "2022-11-28",
|
||||||
|
}
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Gitea(WebgitClient):
|
||||||
|
"""
|
||||||
|
A quite thin wrapper around Gitea's REST API.
|
||||||
|
"""
|
||||||
|
def __post_init__(self):
|
||||||
|
self.headers = {
|
||||||
|
"Authorization": "token " + self.api_token,
|
||||||
|
}
|
Loading…
Reference in New Issue