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 flask import jsonify, abort, make_response, url_for, current_app
from app.logic.packages import do_edit_package 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.logic.screenshots import do_create_screenshot, do_order_screenshots
from app.models import APIToken, Package, MinetestRelease, PackageScreenshot 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, 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): min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason="API", commit_hash:str=None):
if not token.canOperateOnPackage(package): if not token.canOperateOnPackage(package):

View File

@ -24,7 +24,7 @@ from sqlalchemy import func, or_, and_
from app import github, csrf from app import github, csrf
from app.models import db, User, APIToken, Package, Permission, AuditSeverity from app.models import db, User, APIToken, Package, Permission, AuditSeverity
from app.utils import abs_url_for, addAuditLog, login_user_set_active 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 import hmac, requests
@bp.route("/github/start/") @bp.route("/github/start/")
@ -149,4 +149,7 @@ def webhook():
if package.releases.filter_by(commit_hash=ref).count() > 0: if package.releases.filter_by(commit_hash=ref).count() > 0:
return 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 import csrf
from app.models import Package, APIToken, Permission 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(): def webhook_impl():
@ -66,7 +66,10 @@ def webhook_impl():
if package.releases.filter_by(commit_hash=ref).count() > 0: if package.releases.filter_by(commit_hash=ref).count() > 0:
return 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"]) @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.LogicError import LogicError
from app.logic.uploads import upload_file from app.logic.uploads import upload_file
from app.models import PackageRelease, db, Permission, User, Package, MinetestRelease 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 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 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, def do_create_zip_release(user: User, package: Package, title: str, file,
min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None, min_v: MinetestRelease = None, max_v: MinetestRelease = None, reason: str = None,
commit_hash: str = None): commit_hash: str = None):

View File

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

View File

@ -22,8 +22,8 @@ from kombu import uuid
from app.models import * from app.models import *
from app.tasks import celery, TaskError from app.tasks import celery, TaskError
from app.utils import randomString, post_bot_message, addSystemNotification, addSystemAuditLog, get_system_user 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 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 .minetestcheck import build_tree, MinetestCheckError, ContentType
from ..logic.LogicError import LogicError from ..logic.LogicError import LogicError
from ..logic.packages import do_edit_package, ALIASES from ..logic.packages import do_edit_package, ALIASES
@ -155,14 +155,14 @@ def checkZipRelease(self, id, path):
@celery.task(bind=True) @celery.task(bind=True)
def makeVCSRelease(self, id, branch): def makeVCSRelease(self, id, ref):
release = PackageRelease.query.get(id) release = PackageRelease.query.get(id)
if release is None: if release is None:
raise TaskError("No such release!") raise TaskError("No such release!")
elif release.package is None: elif release.package is None:
raise TaskError("No package attached to release") 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) postReleaseCheckUpdate(self, release, repo.working_tree_dir)
filename = randomString(10) + ".zip" filename = randomString(10) + ".zip"
@ -182,6 +182,38 @@ def makeVCSRelease(self, id, branch):
return release.url 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() @celery.task()
def importRepoScreenshot(id): def importRepoScreenshot(id):
package = Package.query.get(id) package = Package.query.get(id)

View File

@ -79,6 +79,32 @@ def clone_repo(urlstr, ref=None, recursive=False):
.strip()) .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): def get_latest_commit(git_url, ref_name=None):
git_url = generateGitURL(git_url) git_url = generateGitURL(git_url)