From: Michael Tremer Date: Tue, 21 Jan 2025 10:48:34 +0000 (+0000) Subject: releases: Split them off into their own file X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=170aacdcc591bf38e7097f324e2bdb9fa5b5d76a;p=pbs.git releases: Split them off into their own file Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index a57d9a39..6aa4cae5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -103,6 +103,7 @@ buildservice_PYTHON = \ src/buildservice/misc.py \ src/buildservice/packages.py \ src/buildservice/ratelimiter.py \ + src/buildservice/releases.py \ src/buildservice/releasemonitoring.py \ src/buildservice/repository.py \ src/buildservice/sessions.py \ diff --git a/src/buildservice/distribution.py b/src/buildservice/distribution.py index 591f03e4..86151aea 100644 --- a/src/buildservice/distribution.py +++ b/src/buildservice/distribution.py @@ -1,147 +1,39 @@ -#!/usr/bin/python +############################################################################### +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2025 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### -import datetime import logging import sqlalchemy from sqlalchemy import Column, Computed, ForeignKey -from sqlalchemy import ARRAY, Boolean, DateTime, Integer, Text +from sqlalchemy import ARRAY, DateTime, Integer, Text from . import base from . import database from . import misc +from . import releases from . import repository from . import sources -from .decorators import * # Setup logging log = logging.getLogger("pbs.distros") -class Release(database.Base, database.BackendMixin, database.SoftDeleteMixin): - __tablename__ = "releases" - - def __str__(self): - return self.name - - def has_perm(self, *args, **kwargs): - # Inherit all permissions from the distribution - return self.distro.has_perm(*args, **kwargs) - - # ID - - id = Column(Integer, primary_key=True) - - # Distro - - distro_id = Column(Integer, ForeignKey("distributions.id"), nullable=False) - - distro = sqlalchemy.orm.relationship("Distro", back_populates="releases", lazy="selectin") - - # Name - - name = Column(Text, nullable=False) - - # Slug - - slug = Column(Text, unique=True, nullable=False) - - # Created At - - created_at = Column( - DateTime(timezone=False), nullable=False, server_default=sqlalchemy.func.current_timestamp(), - ) - - # Created By ID - - created_by_id = Column(Integer, ForeignKey("users.id"), nullable=False) - - # Created By - - created_by = sqlalchemy.orm.relationship( - "User", foreign_keys=[created_by_id], lazy="selectin", - ) - - # Deleted By ID - - deleted_by_id = Column(Integer, ForeignKey("users.id")) - - # Deleted By - - deleted_by = sqlalchemy.orm.relationship( - "User", foreign_keys=[deleted_by_id], lazy="selectin", - ) - - # Stable? - - stable = Column(Boolean, nullable=False) - - # Announcement - - announcement = Column(Text, nullable=False) - - # URL - - @property - def url(self): - return "/distros/%s/releases/%s" % (self.distro.slug, self.slug) - - # Publish - - def is_published(self): - if self.published_at and self.published_at <= datetime.datetime.utcnow(): - return True - - return False - - # Published At - - published_at = Column(DateTime) - - async def publish(self, when=None): - """ - Called to publish the release - """ - self.published_at = when or sqlalchemy.func.current_timestamp() - - # XXX TODO - - # Delete - - async def delete(self, user=None): - """ - Deletes this release - """ - self._set_attribute_now("deleted_at") - if user: - self._set_attribute("deleted_by", user) - - # XXX TODO delete images - - # Images - - #images = sqlalchemy.orm.relationship("Image", back_populates="release") - - @lazy_property - def XXXimages(self): - images = self.backend.distros.releases.images._get_images(""" - SELECT - * - FROM - release_images - WHERE - release_id = %s - AND - deleted_at IS NULL - """, self.id, - - # Populate cache - release=self, - ) - - # Return grouped by architecture - return misc.group(images, lambda image: image.arch) - - class Distributions(base.Object): def __aiter__(self): stmt = ( @@ -423,15 +315,15 @@ class Distro(database.Base, database.BackendMixin, database.SoftDeleteMixin): stmt = ( sqlalchemy .select( - Release, + releases.Release, ) .where( - Release.deleted_at == None, - Release.distro == self, - Release.published_at <= sqlalchemy.func.current_timestamp(), + releases.Release.deleted_at == None, + releases.Release.distro == self, + releases.Release.published_at <= sqlalchemy.func.current_timestamp(), ) .order_by( - Release.published_at.desc(), + releases.Release.published_at.desc(), ) .limit(1) ) @@ -464,7 +356,7 @@ class Distro(database.Base, database.BackendMixin, database.SoftDeleteMixin): # Create a new Release release = await self.db.insert( - Release, + releases.Release, distro = self, name = name, slug = slug, diff --git a/src/buildservice/events.py b/src/buildservice/events.py index 4a811628..4d02dfc3 100644 --- a/src/buildservice/events.py +++ b/src/buildservice/events.py @@ -32,6 +32,7 @@ from . import database from . import distribution as distros from . import jobs from . import mirrors +from . import releases from . import releasemonitoring as monitorings from . import repository as repos @@ -661,18 +662,18 @@ class Events(base.Object): TYPE("release-created"), # Timestamp - TIMESTAMP(distros.Release.created_at), + TIMESTAMP(releases.Release.created_at), # Priority PRIORITY(1), # Release ID - distros.Release.id.label("release_id"), + releases.Release.id.label("release_id"), # By User ID - distros.Release.created_by_id.label("by_user_id"), + releases.Release.created_by_id.label("by_user_id"), ) - .select_from(distros.Release) + .select_from(releases.Release) )) # Releases Deleted @@ -683,20 +684,20 @@ class Events(base.Object): TYPE("release-deleted"), # Timestamp - TIMESTAMP(distros.Release.deleted_at), + TIMESTAMP(releases.Release.deleted_at), # Priority PRIORITY(1), # Release ID - distros.Release.id.label("release_id"), + releases.Release.id.label("release_id"), # By User ID - distros.Release.deleted_by_id.label("by_user_id"), + releases.Release.deleted_by_id.label("by_user_id"), ) - .select_from(distros.Release) + .select_from(releases.Release) .where( - distros.Release.deleted_at != None, + releases.Release.deleted_at != None, ) )) @@ -708,18 +709,18 @@ class Events(base.Object): TYPE("release-published"), # Timestamp - TIMESTAMP(distros.Release.published_at), + TIMESTAMP(releases.Release.published_at), # Priority PRIORITY(5), # Release ID - distros.Release.id.label("release_id"), + releases.Release.id.label("release_id"), ) - .select_from(distros.Release) + .select_from(releases.Release) .where( - distros.Release.published_at != None, - distros.Release.published_at <= sqlalchemy.func.current_timestamp(), + releases.Release.published_at != None, + releases.Release.published_at <= sqlalchemy.func.current_timestamp(), ) )) diff --git a/src/buildservice/releases.py b/src/buildservice/releases.py new file mode 100644 index 00000000..1e410a77 --- /dev/null +++ b/src/buildservice/releases.py @@ -0,0 +1,156 @@ +############################################################################### +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2025 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +import datetime +import logging + +import sqlalchemy +from sqlalchemy import Column, Computed, ForeignKey +from sqlalchemy import ARRAY, Boolean, DateTime, Integer, Text + +from . import database + +# Setup logging +log = logging.getLogger("pbs.releases") + +class Release(database.Base, database.BackendMixin, database.SoftDeleteMixin): + __tablename__ = "releases" + + def __str__(self): + return self.name + + def has_perm(self, *args, **kwargs): + # Inherit all permissions from the distribution + return self.distro.has_perm(*args, **kwargs) + + # ID + + id = Column(Integer, primary_key=True) + + # Distro ID + + distro_id = Column(Integer, ForeignKey("distributions.id"), nullable=False) + + # Distro + + distro = sqlalchemy.orm.relationship("Distro", back_populates="releases", lazy="selectin") + + # Name + + name = Column(Text, nullable=False) + + # Slug + + slug = Column(Text, unique=True, nullable=False) + + # Created At + + created_at = Column(DateTime(timezone=False), nullable=False, + server_default=sqlalchemy.func.current_timestamp()) + + # Created By ID + + created_by_id = Column(Integer, ForeignKey("users.id"), nullable=False) + + # Created By + + created_by = sqlalchemy.orm.relationship( + "User", foreign_keys=[created_by_id], lazy="selectin", + ) + + # Deleted By ID + + deleted_by_id = Column(Integer, ForeignKey("users.id")) + + # Deleted By + + deleted_by = sqlalchemy.orm.relationship( + "User", foreign_keys=[deleted_by_id], lazy="selectin", + ) + + # Stable? + + stable = Column(Boolean, nullable=False) + + # Announcement + + announcement = Column(Text, nullable=False) + + # URL + + @property + def url(self): + return "/distros/%s/releases/%s" % (self.distro.slug, self.slug) + + # Publish + + def is_published(self): + if self.published_at and self.published_at <= datetime.datetime.utcnow(): + return True + + return False + + # Published At + + published_at = Column(DateTime) + + async def publish(self, when=None): + """ + Called to publish the release + """ + self.published_at = when or sqlalchemy.func.current_timestamp() + + # XXX TODO + + # Delete + + async def delete(self, user=None): + """ + Deletes this release + """ + self._set_attribute_now("deleted_at") + if user: + self._set_attribute("deleted_by", user) + + # XXX TODO delete images + + # Images + + #images = sqlalchemy.orm.relationship("Image", back_populates="release") + + @property + def XXXimages(self): + images = self.backend.distros.releases.images._get_images(""" + SELECT + * + FROM + release_images + WHERE + release_id = %s + AND + deleted_at IS NULL + """, self.id, + + # Populate cache + release=self, + ) + + # Return grouped by architecture + return misc.group(images, lambda image: image.arch)