Fix commit webhooks on non-default branches

Fixes #258
This commit is contained in:
rubenwardy 2021-02-28 21:53:08 +00:00
parent 032d8bf67b
commit e3aeeb4132
7 changed files with 100 additions and 11 deletions

View File

@ -18,7 +18,7 @@
from flask import jsonify, abort, make_response, url_for, current_app
from app.logic.packages import do_edit_package
from app.logic.releases import LogicError, do_create_vcs_release, do_create_zip_release
from app.logic.releases import LogicError, do_create_vcs_release, do_create_zip_release, do_handle_webhook_push
from app.logic.screenshots import do_create_screenshot, do_order_screenshots
from app.models import APIToken, Package, MinetestRelease, PackageScreenshot
@ -53,6 +53,23 @@ def api_create_vcs_release(token: APIToken, package: Package, title: str, ref: s
})
def api_handle_webhook_push(token: APIToken, package: Package, title: str, ref: str, branch: str):
if not token.canOperateOnPackage(package):
error(403, "API token does not have access to the package")
reason = "Webhook, token=" + token.name
if branch:
branch = branch.replace("refs/heads/", "")
task_id = guard(do_handle_webhook_push)(token.owner, package, title, ref, branch, reason)
return jsonify({
"success": True,
"task": url_for("tasks.check", id=task_id),
})
def api_create_zip_release(token: APIToken, package: Package, title: str, file,
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason="API", commit_hash:str=None):
if not token.canOperateOnPackage(package):

View File

@ -24,7 +24,7 @@ from sqlalchemy import func, or_, and_
from app import github, csrf
from app.models import db, User, APIToken, Package, Permission, AuditSeverity
from app.utils import abs_url_for, addAuditLog, login_user_set_active
from app.blueprints.api.support import error, api_create_vcs_release
from app.blueprints.api.support import error, api_create_vcs_release, api_handle_webhook_push
import hmac, requests
@bp.route("/github/start/")
@ -149,4 +149,7 @@ def webhook():
if package.releases.filter_by(commit_hash=ref).count() > 0:
return
return api_create_vcs_release(actual_token, package, title, ref, reason="Webhook")
if event == "push":
return api_handle_webhook_push(actual_token, package, title, ref, json.get("ref"))
else:
return api_create_vcs_release(actual_token, package, title, ref, reason="Webhook")

View File

@ -20,7 +20,7 @@ bp = Blueprint("gitlab", __name__)
from app import csrf
from app.models import Package, APIToken, Permission
from app.blueprints.api.support import error, api_create_vcs_release
from app.blueprints.api.support import error, api_create_vcs_release, api_handle_webhook_push
def webhook_impl():
@ -66,7 +66,10 @@ def webhook_impl():
if package.releases.filter_by(commit_hash=ref).count() > 0:
return
return api_create_vcs_release(token, package, title, ref, reason="Webhook")
if event == "push":
return api_handle_webhook_push(token, package, title, ref, json.get("ref"))
else:
return api_create_vcs_release(token, package, title, ref, reason="Webhook")
@bp.route("/gitlab/webhook/", methods=["POST"])

View File

@ -22,7 +22,7 @@ from celery import uuid
from app.logic.LogicError import LogicError
from app.logic.uploads import upload_file
from app.models import PackageRelease, db, Permission, User, Package, MinetestRelease
from app.tasks.importtasks import makeVCSRelease, checkZipRelease
from app.tasks.importtasks import makeVCSRelease, makeVCSReleaseCheckBranch, checkZipRelease
from app.utils import AuditSeverity, addAuditLog, nonEmptyOrNone
@ -62,6 +62,14 @@ def do_create_vcs_release(user: User, package: Package, title: str, ref: str,
return rel
def do_handle_webhook_push(user: User, package: Package, title: str, commit: str, branch: str,
reason: str = None):
check_can_create_release(user, package)
task = makeVCSReleaseCheckBranch.delay((user.id, package.id, title, nonEmptyOrNone(commit), branch, reason))
return task.id
def do_create_zip_release(user: User, package: Package, title: str, file,
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None,
commit_hash: str = None):

View File

@ -458,7 +458,7 @@ class Package(db.Model):
author=self.author.username, name=self.name)
else:
return url_for("packages.view",
author=self.author.username, name=self.name)
author=self.author.username, name=self.name, _external=False)
def getShieldURL(self, type):
from app.utils import abs_url_for

View File

@ -22,8 +22,8 @@ from kombu import uuid
from app.models import *
from app.tasks import celery, TaskError
from app.utils import randomString, post_bot_message, addSystemNotification, addSystemAuditLog, get_system_user
from app.utils.git import clone_repo, get_latest_tag, get_latest_commit, get_temp_dir
from app.utils import randomString, post_bot_message, addSystemNotification, addSystemAuditLog, get_system_user, addAuditLog
from app.utils.git import clone_repo, get_latest_tag, get_latest_commit, get_temp_dir, get_default_branch
from .minetestcheck import build_tree, MinetestCheckError, ContentType
from ..logic.LogicError import LogicError
from ..logic.packages import do_edit_package, ALIASES
@ -155,14 +155,14 @@ def checkZipRelease(self, id, path):
@celery.task(bind=True)
def makeVCSRelease(self, id, branch):
def makeVCSRelease(self, id, ref):
release = PackageRelease.query.get(id)
if release is None:
raise TaskError("No such release!")
elif release.package is None:
raise TaskError("No package attached to release")
with clone_repo(release.package.repo, ref=branch, recursive=True) as repo:
with clone_repo(release.package.repo, ref=ref, recursive=True) as repo:
postReleaseCheckUpdate(self, release, repo.working_tree_dir)
filename = randomString(10) + ".zip"
@ -182,6 +182,38 @@ def makeVCSRelease(self, id, branch):
return release.url
@celery.task(bind=True)
def makeVCSReleaseCheckBranch(self, user_id: int, package_id: int, title: str, ref: str, branch: str, reason: str):
package = Package.query.get(package_id)
if package is None:
raise TaskError("No such package!")
user = User.query.get(user_id)
if user is None:
raise TaskError("No such user!")
default_branch = get_default_branch(package.repo)
if branch != default_branch:
return
rel = PackageRelease()
rel.package = package
rel.title = title
rel.url = ""
rel.task_id = uuid()
db.session.add(rel)
if reason is None:
msg = "Created release {}".format(rel.title)
else:
msg = "Created release {} ({})".format(rel.title, reason)
addAuditLog(AuditSeverity.NORMAL, user, msg, package.getDetailsURL(), package)
db.session.commit()
makeVCSRelease(self, rel.id, ref)
@celery.task()
def importRepoScreenshot(id):
package = Package.query.get(id)

View File

@ -79,6 +79,32 @@ def clone_repo(urlstr, ref=None, recursive=False):
.strip())
def get_default_branch(git_url):
git_url = generateGitURL(git_url)
g = git.cmd.Git()
remote_refs = {}
for ref in g.ls_remote(git_url).split('\n'):
hash_ref_list = ref.split('\t')
remote_refs[hash_ref_list[1]] = hash_ref_list[0]
hash = remote_refs.get("HEAD")
matching = []
for ref, value in remote_refs.items():
if value == hash and ref != "HEAD":
matching.append(ref)
if len(matching) == 1:
return matching[0].replace("refs/heads/", "")
elif len(matching) == 0 or "master" in matching:
return "master"
elif "main" in matching:
return "main"
else:
return matching[0].replace("refs/heads/", "")
def get_latest_commit(git_url, ref_name=None):
git_url = generateGitURL(git_url)