Add reimport of package meta from latest release

Fixes #127
This commit is contained in:
rubenwardy 2020-07-12 02:22:35 +01:00
parent 868ced76a8
commit 284683e7e5
6 changed files with 123 additions and 4 deletions

View File

@ -21,7 +21,7 @@ import flask_menu as menu
from . import bp from . import bp
from app.models import * from app.models import *
from celery import uuid, group from celery import uuid, group
from app.tasks.importtasks import importRepoScreenshot, makeVCSRelease, checkZipRelease from app.tasks.importtasks import importRepoScreenshot, makeVCSRelease, checkZipRelease, updateMetaFromRelease
from app.tasks.forumtasks import importTopicList, checkAllForumAccounts from app.tasks.forumtasks import importTopicList, checkAllForumAccounts
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import * from wtforms import *
@ -47,6 +47,21 @@ def admin_page():
result = group(tasks).apply_async() result = group(tasks).apply_async()
while not result.ready():
import time
time.sleep(0.1)
return redirect(url_for("todo.view"))
elif action == "reimportpackages":
tasks = []
for package in Package.query.filter_by(approved=True, soft_deleted=False).all():
release = package.releases.first()
if release:
zippath = release.url.replace("/uploads/", app.config["UPLOAD_DIR"])
tasks.append(updateMetaFromRelease.s(release.id, zippath))
result = group(tasks).apply_async()
while not result.ready(): while not result.ready():
import time import time
time.sleep(0.1) time.sleep(0.1)

View File

@ -23,7 +23,7 @@ from . import bp
from app.models import * from app.models import *
from app.querybuilder import QueryBuilder from app.querybuilder import QueryBuilder
from app.tasks.importtasks import importRepoScreenshot from app.tasks.importtasks import importRepoScreenshot, updateMetaFromRelease
from app.utils import * from app.utils import *
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
@ -33,6 +33,8 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField, QuerySelectMultipleF
from sqlalchemy import or_, func from sqlalchemy import or_, func
from sqlalchemy.orm import joinedload, subqueryload from sqlalchemy.orm import joinedload, subqueryload
from celery import uuid
@menu.register_menu(bp, ".mods", "Mods", order=11, endpoint_arguments_constructor=lambda: { 'type': 'mod' }) @menu.register_menu(bp, ".mods", "Mods", order=11, endpoint_arguments_constructor=lambda: { 'type': 'mod' })
@menu.register_menu(bp, ".games", "Games", order=12, endpoint_arguments_constructor=lambda: { 'type': 'game' }) @menu.register_menu(bp, ".games", "Games", order=12, endpoint_arguments_constructor=lambda: { 'type': 'game' })
@ -466,3 +468,31 @@ def remove_self_maintainers(package):
db.session.commit() db.session.commit()
return redirect(package.getDetailsURL()) return redirect(package.getDetailsURL())
@bp.route("/packages/<author>/<name>/import-meta/", methods=["POST"])
@login_required
@is_package_page
def update_from_release(package):
if not package.checkPerm(current_user, Permission.REIMPORT_META):
flash("You don't have permission to reimport meta", "danger")
return redirect(package.getDetailsURL())
release = package.releases.first()
if not release:
flash("Release needed", "danger")
return redirect(package.getDetailsURL())
msg = "Updated meta from latest release"
addNotification(package.maintainers, current_user,
msg, package.getDetailsURL(), package)
severity = AuditSeverity.NORMAL if current_user in package.maintainers else AuditSeverity.EDITOR
addAuditLog(severity, current_user, msg, package.getDetailsURL(), package)
db.session.commit()
task_id = uuid()
zippath = release.url.replace("/uploads/", app.config["UPLOAD_DIR"])
updateMetaFromRelease.apply_async((release.id, zippath), task_id=task_id)
return redirect(url_for("tasks.check", id=task_id, r=package.getEditURL()))

View File

@ -80,6 +80,7 @@ class Permission(enum.Enum):
MAKE_RELEASE = "MAKE_RELEASE" MAKE_RELEASE = "MAKE_RELEASE"
DELETE_RELEASE = "DELETE_RELEASE" DELETE_RELEASE = "DELETE_RELEASE"
ADD_SCREENSHOTS = "ADD_SCREENSHOTS" ADD_SCREENSHOTS = "ADD_SCREENSHOTS"
REIMPORT_META = "REIMPORT_META"
APPROVE_SCREENSHOT = "APPROVE_SCREENSHOT" APPROVE_SCREENSHOT = "APPROVE_SCREENSHOT"
APPROVE_RELEASE = "APPROVE_RELEASE" APPROVE_RELEASE = "APPROVE_RELEASE"
APPROVE_NEW = "APPROVE_NEW" APPROVE_NEW = "APPROVE_NEW"
@ -358,11 +359,12 @@ class Dependency(db.Model):
optional = db.Column(db.Boolean, nullable=False, default=False) optional = db.Column(db.Boolean, nullable=False, default=False)
__table_args__ = (db.UniqueConstraint("depender_id", "package_id", "meta_package_id", name="_dependency_uc"), ) __table_args__ = (db.UniqueConstraint("depender_id", "package_id", "meta_package_id", name="_dependency_uc"), )
def __init__(self, depender=None, package=None, meta=None): def __init__(self, depender=None, package=None, meta=None, optional=False):
if depender is None: if depender is None:
return return
self.depender = depender self.depender = depender
self.optional = optional
packageProvided = package is not None packageProvided = package is not None
metaProvided = meta is not None metaProvided = meta is not None
@ -673,6 +675,10 @@ class Package(db.Model):
return url_for("packages.remove_self_maintainers", return url_for("packages.remove_self_maintainers",
author=self.author.username, name=self.name) author=self.author.username, name=self.name)
def getUpdateFromReleaseURL(self):
return url_for("packages.update_from_release",
author=self.author.username, name=self.name)
def getReviewURL(self): def getReviewURL(self):
return url_for('packages.review', return url_for('packages.review',
author=self.author.username, name=self.name) author=self.author.username, name=self.name)
@ -705,7 +711,8 @@ class Package(db.Model):
elif perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS: elif perm == Permission.MAKE_RELEASE or perm == Permission.ADD_SCREENSHOTS:
return isMaintainer return isMaintainer
elif perm == Permission.EDIT_PACKAGE or perm == Permission.APPROVE_CHANGES or perm == Permission.APPROVE_RELEASE: elif perm == Permission.EDIT_PACKAGE or perm == Permission.REIMPORT_META or \
perm == Permission.APPROVE_CHANGES or perm == Permission.APPROVE_RELEASE:
return isMaintainer and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER) return isMaintainer and user.rank.atLeast(UserRank.MEMBER if self.approved else UserRank.NEW_MEMBER)
# Anyone can change the package name when not approved, but only editors when approved # Anyone can change the package name when not approved, but only editors when approved

View File

@ -139,6 +139,60 @@ def cloneRepo(urlstr, ref=None, recursive=False):
.replace("Cloning into '" + gitDir + "'...", "") \ .replace("Cloning into '" + gitDir + "'...", "") \
.strip()) .strip())
@celery.task(bind=True)
def updateMetaFromRelease(self, id, path):
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")
temp = getTempDir()
try:
with ZipFile(path, 'r') as zip_ref:
zip_ref.extractall(temp)
try:
tree = build_tree(temp, expected_type=ContentType[release.package.type.name], \
author=release.package.author.username, name=release.package.name)
cache = {}
def getMetaPackages(names):
return [ MetaPackage.GetOrCreate(x, cache) for x in names ]
provides = getMetaPackages(tree.fold("name"))
package = release.package
package.provides.clear()
package.provides.extend(provides)
for dep in package.dependencies:
if dep.meta_package:
db.session.delete(dep)
for meta in getMetaPackages(tree.fold("meta", "depends")):
db.session.add(Dependency(package, meta=meta, optional=False))
for meta in getMetaPackages(tree.fold("meta", "optional_depends")):
db.session.add(Dependency(package, meta=meta, optional=True))
db.session.commit()
except MinetestCheckError as err:
if "Fails validation" not in release.title:
release.title += " (Fails validation)"
release.task_id = self.request.id
release.approved = False
db.session.commit()
raise TaskError(str(err))
finally:
shutil.rmtree(temp)
@celery.task() @celery.task()
def getMeta(urlstr, author): def getMeta(urlstr, author):
gitDir, _ = cloneRepo(urlstr, recursive=True) gitDir, _ = cloneRepo(urlstr, recursive=True)
@ -249,6 +303,7 @@ def makeVCSRelease(id, branch):
finally: finally:
shutil.rmtree(gitDir) shutil.rmtree(gitDir)
@celery.task() @celery.task()
def importRepoScreenshot(id): def importRepoScreenshot(id):
package = Package.query.get(id) package = Package.query.get(id)

View File

@ -21,6 +21,7 @@
<select name="action"> <select name="action">
<option value="delstuckreleases" selected>Delete stuck releases</option> <option value="delstuckreleases" selected>Delete stuck releases</option>
<option value="checkreleases">Validate all Zip releases</option> <option value="checkreleases">Validate all Zip releases</option>
<option value="reimportpackages">Reimport meta</option>
<option value="importmodlist">Import forum topics</option> <option value="importmodlist">Import forum topics</option>
<option value="recalcscores">Recalculate package scores</option> <option value="recalcscores">Recalculate package scores</option>
<option value="checkusers">Check forum users</option> <option value="checkusers">Check forum users</option>

View File

@ -28,6 +28,17 @@
{{ _("Have you read the Package Inclusion Policy and Guidance yet?") }} {{ _("Have you read the Package Inclusion Policy and Guidance yet?") }}
</div> </div>
{% if package and package.checkPerm(current_user, "REIMPORT_META") and package.releases.first() %}
<form class="alert alert-secondary mb-5" method="post" action="{{ package.getUpdateFromReleaseURL() }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input class="btn btn-sm btn-warning float-right" type="submit" value="{{ _('Import') }}" />
<b>{{ _("Reimport meta from latest release.") }}</b>
{{ _("This will override 'provides', 'dependencies', and 'optional_dependencies'.") }}
<div style="clear:both;"></div>
</form>
{% endif %}
<noscript> <noscript>
<div class="alert alert-warning"> <div class="alert alert-warning">
{{ _("Javascript is needed to improve the user interface, and is needed for features {{ _("Javascript is needed to improve the user interface, and is needed for features