diff --git a/app/blueprints/packages/releases.py b/app/blueprints/packages/releases.py index 6fa673e..68a726a 100644 --- a/app/blueprints/packages/releases.py +++ b/app/blueprints/packages/releases.py @@ -24,7 +24,7 @@ from wtforms.ext.sqlalchemy.fields import QuerySelectField from wtforms.validators import * from app.rediscache import has_key, set_key, make_download_key -from app.tasks.importtasks import makeVCSRelease, checkZipRelease +from app.tasks.importtasks import makeVCSRelease, checkZipRelease, check_update_config from app.utils import * from . import bp @@ -278,6 +278,7 @@ def update_config(package): flash("Deleted update configuration", "success") if package.update_config: db.session.delete(package.update_config) + db.session.commit() else: if package.update_config is None: package.update_config = PackageUpdateConfig() @@ -287,7 +288,15 @@ def update_config(package): package.update_config.ref = nonEmptyOrNone(form.ref.data) package.update_config.make_release = form.action.data == "make_release" - db.session.commit() + if package.update_config.last_commit is None: + last_release = package.releases.first() + if last_release and last_release.commit_hash: + package.update_config.last_commit = last_release.commit_hash + + db.session.commit() + + if package.update_config.last_commit is None: + check_update_config.delay(package.id) if not form.disable.data and package.releases.count() == 0: flash("Now, please create an initial release", "success") diff --git a/app/blueprints/todo/__init__.py b/app/blueprints/todo/__init__.py index 23d85e7..486b799 100644 --- a/app/blueprints/todo/__init__.py +++ b/app/blueprints/todo/__init__.py @@ -75,7 +75,7 @@ def view_editor(): outdated_packages = Package.query \ .filter(Package.state == PackageState.APPROVED, - Package.update_config.has(outdated=True)).count() + Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))).count() return render_template("todo/editor.html", current_tab="editor", packages=packages, wip_packages=wip_packages, releases=releases, screenshots=screenshots, @@ -165,7 +165,7 @@ def view_user(username=None): outdated_packages = user.maintained_packages \ .filter(Package.state != PackageState.DELETED, - Package.update_config.has(outdated=True)) \ + Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \ .order_by(db.asc(Package.title)).all() topics_to_add = ForumTopic.query \ @@ -184,6 +184,7 @@ def view_user(username=None): def outdated(): outdated_packages = Package.query \ .filter(Package.state == PackageState.APPROVED, - Package.update_config.has(outdated=True)).all() + Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None)))\ + .order_by(db.desc(PackageUpdateConfig.outdated_at)).all() return render_template("todo/outdated.html", current_tab="outdated", outdated_packages=outdated_packages) diff --git a/app/models/packages.py b/app/models/packages.py index c6e4072..f5b4c61 100644 --- a/app/models/packages.py +++ b/app/models/packages.py @@ -861,7 +861,7 @@ class PackageRelease(db.Model): self.approved = True if self.package.update_config: - self.package.update_config.outdated = False + self.package.update_config.outdated_at = None self.package.update_config.last_commit = self.commit_hash return True @@ -955,11 +955,15 @@ class PackageUpdateConfig(db.Model): package = db.relationship("Package", back_populates="update_config", foreign_keys=[package_id]) last_commit = db.Column(db.String(41), nullable=True, default=None) + last_tag = db.Column(db.String(41), nullable=True, default=None) - # Set to true when an outdated notification is sent. Set to false when a release is created - outdated = db.Column(db.Boolean, nullable=False, default=False) + # Set to now when an outdated notification is sent. Set to None when a release is created + outdated_at = db.Column(db.DateTime, nullable=True, default=None) trigger = db.Column(db.Enum(PackageUpdateTrigger), nullable=False, default=PackageUpdateTrigger.COMMIT) ref = db.Column(db.String(41), nullable=True, default=None) make_release = db.Column(db.Boolean, nullable=False, default=False) + + def set_outdated(self): + self.outdated_at = datetime.datetime.utcnow() diff --git a/app/tasks/importtasks.py b/app/tasks/importtasks.py index 1040318..fa5b452 100644 --- a/app/tasks/importtasks.py +++ b/app/tasks/importtasks.py @@ -106,6 +106,24 @@ def get_commit_hash(git_url, ref_name=None): return remote_refs.get(ref_name) +def get_latest_tag(git_url): + with get_temp_dir() as git_dir: + repo = git.Repo.init(git_dir) + origin = repo.create_remote("origin", url=git_url) + origin.fetch() + + refs = repo.git.ls_remote(tags=True, sort="creatordate").split('\n') + if len(refs) == 0: + return None + + last_ref = refs[-1] + hash_ref_list = last_ref.split('\t') + + tag = hash_ref_list[1].replace("refs/tags/", "") + commit_hash = repo.git.rev_parse(tag + "^{}") + return tag, commit_hash + + @celery.task() def getMeta(urlstr, author): with clone_repo(urlstr, recursive=True) as repo: @@ -297,6 +315,67 @@ def importForeignDownloads(self, id): db.session.commit() +def check_update_config_impl(package): + config = package.update_config + + if config.trigger == PackageUpdateTrigger.COMMIT: + tag = None + commit = get_commit_hash(package.repo, package.update_config.ref) + elif config.trigger == PackageUpdateTrigger.TAG: + tag, commit = get_latest_tag(package.repo) + else: + raise TaskError("Unknown update trigger") + + if config.last_commit == commit: + if tag and config.last_tag != tag: + config.last_tag = tag + db.session.commit() + return + + if not config.last_commit: + config.last_commit = commit + config.last_tag = tag + db.session.commit() + return + + if config.make_release: + rel = PackageRelease() + rel.package = package + rel.title = tag if tag else commit[0:5] + rel.url = "" + rel.task_id = uuid() + db.session.add(rel) + db.session.commit() + + makeVCSRelease.apply_async((rel.id, commit), task_id=rel.task_id) + + elif config.outdated_at is None: + config.set_outdated() + + if config.trigger == PackageUpdateTrigger.COMMIT: + msg_last = "" + if config.last_commit: + msg_last = " The last commit was {}".format(config.last_commit[0:5]) + + msg = "New commit {} found on the Git repo, is the package outdated?{}" \ + .format(commit[0:5], msg_last) + else: + msg_last = "" + if config.last_tag: + msg_last = " The last tag was {}".format(config.last_tag) + + msg = "New tag {} found on the Git repo.{}" \ + .format(tag, msg_last) + + for user in package.maintainers: + addSystemNotification(user, NotificationType.BOT, + msg, url_for("todo.view_user", username=user.username, _external=False), package) + + config.last_commit = commit + config.last_tag = tag + db.session.commit() + + @celery.task(bind=True) def check_update_config(self, package_id): package: Package = Package.query.get(package_id) @@ -305,14 +384,9 @@ def check_update_config(self, package_id): elif package.update_config is None: raise TaskError("No update config attached to package") - config = package.update_config - - if config.trigger != PackageUpdateTrigger.COMMIT: - return - err = None try: - hash = get_commit_hash(package.repo, package.update_config.ref) + check_update_config_impl(package) except IndexError as e: err = "Unable to find the reference.\n" + str(e) except GitCommandError as e: @@ -320,6 +394,8 @@ def check_update_config(self, package_id): err = e.stderr except gitdb.exc.BadName as e: err = "Unable to find the reference " + (package.update_config.ref or "?") + "\n" + e.stderr + except TaskError as e: + err = e.value if err: err = err.replace("stderr: ", "") \ @@ -327,49 +403,13 @@ def check_update_config(self, package_id): .strip() msg = "Error: {}.\n\nTask ID: {}\n\n[Change update configuration]({})" \ - .format(err, self.request.id, package.getUpdateConfigURL()) + .format(err, self.request.id, package.getUpdateConfigURL()) post_bot_message(package, "Failed to check git repository", msg) db.session.commit() return - if config.last_commit == hash: - return - - if not config.last_commit: - config.last_commit = hash - db.session.commit() - return - - if config.make_release: - rel = PackageRelease() - rel.package = package - rel.title = hash[0:5] - rel.url = "" - rel.task_id = uuid() - db.session.add(rel) - db.session.commit() - - makeVCSRelease.apply_async((rel.id, package.update_config.ref), task_id=rel.task_id) - - elif not config.outdated: - config.outdated = True - - msg_last = "" - if config.last_commit: - msg_last = " The last commit was {}".format(config.last_commit[0:5]) - - msg = "New commit {} found on the Git repo, is the package outdated?{}" \ - .format(hash[0:5], msg_last) - - for user in package.maintainers: - addSystemNotification(user, NotificationType.BOT, - msg, url_for("todo.view_user", username=user.username), package) - - config.last_commit = hash - db.session.commit() - @celery.task def check_for_updates(): diff --git a/app/templates/macros/todo.html b/app/templates/macros/todo.html index 02676d9..72e415b 100644 --- a/app/templates/macros/todo.html +++ b/app/templates/macros/todo.html @@ -17,7 +17,11 @@ {% endif %}
- {{ _("Git repo has commit %(ref)s", ref=package.update_config.last_commit[0:5]) }} + {% if package.update_config.trigger == package.update_config.trigger.TAG and package.update_config.last_tag %} + {{ _("New tag: %(tag)s", tag=package.update_config.last_tag) }} + {% else %} + {{ _("Git repo has commit %(ref)s", ref=package.update_config.last_commit[0:5]) }} + {% endif %}
diff --git a/app/templates/packages/release_new.html b/app/templates/packages/release_new.html index 2f67ec4..dc70354 100644 --- a/app/templates/packages/release_new.html +++ b/app/templates/packages/release_new.html @@ -10,7 +10,12 @@ {% if package.update_config %}

{{ _("Settings") }} - {{ _("You have automatic releases enabled") }} + {% if package.update_config.make_release %} + {{ _("You have automatic releases enabled.") }} + {% else %} + {{ _("You have Git update notifications enabled.") }} + {{ _("You can enable automatic updates in the update detection settings.") }} + {% endif %}

{% else %}

diff --git a/app/templates/packages/update_config.html b/app/templates/packages/update_config.html index bf9761e..6d97a5c 100644 --- a/app/templates/packages/update_config.html +++ b/app/templates/packages/update_config.html @@ -31,19 +31,18 @@ {{ render_field(form.trigger, class_="mt-5") }} -

- - + {{ render_field(form.ref, placeholder="Leave blank to use default branch", + pattern="[A-Za-z0-9/._-]+") }} - {{ _("The new tag trigger isn't supported yet.") }} - {{ _("If you choose it, it won't trigger until it is supported.") }} - {{ _("In the meantime, you can use webhooks to create releases on tag events.") }} +

+ + + + {{ _("Currently, the branch name field is only used by the New Commit trigger.") }}

- {{ render_field(form.ref, placeholder="Leave blank to use default branch", - pattern="[A-Za-z0-9/._-]+") }} {{ render_field(form.action) }}

diff --git a/migrations/versions/a337bcc165c0_.py b/migrations/versions/a337bcc165c0_.py new file mode 100644 index 0000000..261a79d --- /dev/null +++ b/migrations/versions/a337bcc165c0_.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: a337bcc165c0 +Revises: f565dde93553 +Create Date: 2021-01-29 21:30:37.277197 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'a337bcc165c0' +down_revision = 'f565dde93553' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('package_update_config', sa.Column('outdated_at', sa.DateTime(), nullable=True)) + op.add_column('package_update_config', sa.Column('last_tag', sa.String(length=41), nullable=True)) + op.drop_column('package_update_config', 'outdated') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('package_update_config', sa.Column('outdated', sa.BOOLEAN(), server_default=sa.text('false'), autoincrement=False, nullable=False)) + op.drop_column('package_update_config', 'outdated_at') + op.drop_column('package_update_config', 'last_tag') + # ### end Alembic commands ###