From: Michael Tremer Date: Wed, 14 Jun 2023 10:05:21 +0000 (+0000) Subject: distros: Add scaffolding for releases X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e1322ca55d26501dd221cfbd38debaef9512518;p=pbs.git distros: Add scaffolding for releases Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 8e74e127..b3ad549b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -233,6 +233,17 @@ dist_templates_distros_modules_DATA = \ templates_distros_modulesdir = $(templates_distrosdir)/modules +dist_templates_distros_releases_DATA = \ + src/templates/distros/releases/index.html \ + src/templates/distros/releases/show.html + +templates_distros_releasesdir = $(templates_distrosdir)/releases + +dist_templates_distros_releases_modules_DATA = \ + src/templates/distros/releases/modules/list.html + +templates_distros_releases_modulesdir = $(templates_distros_releasesdir)/modules + dist_templates_monitorings_DATA = \ src/templates/monitorings/delete.html \ src/templates/monitorings/edit.html \ diff --git a/src/buildservice/distribution.py b/src/buildservice/distribution.py index 2b006346..10fa3410 100644 --- a/src/buildservice/distribution.py +++ b/src/buildservice/distribution.py @@ -3,6 +3,7 @@ import logging from . import base +from . import misc from .decorators import * # Setup logging @@ -85,6 +86,13 @@ class Distributions(base.Object): """, name, distro_id, version_id, ) + @lazy_property + def releases(self): + """ + Releases + """ + return Releases(self.backend) + class Distribution(base.DataObject): table = "distributions" @@ -346,6 +354,8 @@ class Distribution(base.DataObject): return builds + # Sources + @lazy_property def sources(self): sources = self.backend.sources._get_sources(""" @@ -363,3 +373,249 @@ class Distribution(base.DataObject): ) return list(sources) + + # Releases + + def get_releases(self, limit=None, offset=None): + releases = self.backend.distros.releases._get_releases(""" + SELECT + * + FROM + releases + WHERE + distro_id = %s + AND + deleted_at IS NULL + ORDER BY + created_at DESC + LIMIT + %s + OFFSET + %s + """, self.id, limit, offset, + + # Populate cache + distro=self, + ) + + return list(releases) + + def get_release(self, slug): + return self.backend.distros.releases._get_release(""" + SELECT + * + FROM + releases + WHERE + distro_id = %s + AND + slug = %s + AND + deleted_at IS NULL + """, self.id, slug, + ) + + +class Releases(base.Object): + def _get_releases(self, query, *args, **kwargs): + return self.db.fetch_many(Release, query, *args, **kwargs) + + def _get_release(self, query, *args, **kwargs): + return self.db.fetch_one(Release, query, *args, **kwargs) + + def get_by_id(self, id): + return self._get_release(""" + SELECT + * + FROM + releases + WHERE + id = %s + """, id, + ) + + def create(self, distro, repo, name, user, prerelease=False): + """ + Creates a new release + """ + # Create a slug + slug = misc.normalize(name) + + release = self._get_release(""" + INSERT INTO + releases + ( + distro_id, + repo_id, + name, + slug, + created_by, + prerelease + ) + VALUES + ( + %s, %s, %s, %s, %s, %s + ) + RETURNING * + """, distro, repo, name, slug, user, prerelease, + + # Populate cache + distro=distro, repo=repo, + ) + + # XXX create image jobs + + return release + + # Images + + @lazy_property + def images(self): + return Images(self.backend) + + +class Release(base.DataObject): + table = "releases" + + def __repr__(self): + return "<%s %s>" % (self.__class__.__name__, self.name) + + def __str__(self): + return self.name + + # Distro + + @lazy_property + def distro(self): + return self.backend.distros.get_by_id(self.data.distro_id) + + # Repo + + @lazy_property + def repo(self): + return self.backend.repos.get_by_id(self.data.repo_id) + + # Name + + @property + def name(self): + return self.data.name + + # Slug + + @property + def slug(self): + return self.data.slug + + # Created At + + @property + def created_at(self): + return self.data.created_at + + # Created By + + @lazy_property + def created_by(self): + return self.backend.users.get_by_id(self.data.created_by) + + # Deleted At + + @property + def deleted_at(self): + return self.data.deleted_at + + # Deleted By + + @lazy_property + def deleted_by(self): + return self.backend.users.get_by_id(self.data.deleted_by) + + # Pre-Release + + @property + def prerelease(self): + return self.data.prerelease + + # Announcement + + def get_announcement(self): + return self.data.announcement + + def set_announcement(self, text): + self._set_attribute("announcement", text) + + announcement = property(get_announcement, set_announcement) + + # URL + + @property + def url(self): + return "/distros/%s/releases/%s" % (self.distro.slug, self.slug) + + # Release + + def is_released(self): + if self.released_at: + return True + + return False + + @property + def released_at(self): + return self.data.released_at + + async def release(self, when=None): + """ + Called to prepare releasing the release + """ + if when: + self._set_attribute("released_at", when) + else: + self._set_attribute_now("released_at") + + # XXX TODO + + # Images + + @lazy_property + def images(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 Images(base.Object): + def _get_images(self, query, *args, **kwargs): + return self.db.fetch_many(Image, query, *args, **kwargs) + + def _get_image(self, query, *args, **kwargs): + return self.db.fetch_one(Image, query, *args, **kwargs) + + +class Image(base.DataObject): + table = "release_images" + + @lazy_property + def release(self): + return self.backend.distros.releases.get_by_id(self.data.release_id) + + # Arch + + @property + def arch(self): + return self.data.arch diff --git a/src/database.sql b/src/database.sql index 74b76462..b12465d8 100644 --- a/src/database.sql +++ b/src/database.sql @@ -704,6 +704,40 @@ CREATE VIEW public.relation_sizes AS ORDER BY (pg_relation_size((c.oid)::regclass)) DESC; +-- +-- Name: release_images; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.release_images ( + id integer NOT NULL, + release_id integer NOT NULL, + type_id integer NOT NULL, + arch text NOT NULL, + filename text NOT NULL, + path text +); + + +-- +-- Name: release_images_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.release_images_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: release_images_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.release_images_id_seq OWNED BY public.release_images.id; + + -- -- Name: release_monitoring_releases; Type: TABLE; Schema: public; Owner: - -- @@ -760,6 +794,46 @@ CREATE SEQUENCE public.release_monitorings_id_seq ALTER SEQUENCE public.release_monitorings_id_seq OWNED BY public.release_monitorings.id; +-- +-- Name: releases; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.releases ( + id integer NOT NULL, + distro_id integer NOT NULL, + repo_id integer NOT NULL, + name text NOT NULL, + slug text NOT NULL, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + created_by integer NOT NULL, + deleted_at timestamp without time zone, + deleted_by integer, + released_at timestamp without time zone, + prerelease boolean DEFAULT false NOT NULL, + announcement text +); + + +-- +-- Name: releases_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.releases_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: releases_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.releases_id_seq OWNED BY public.releases.id; + + -- -- Name: repo_builds; Type: TABLE; Schema: public; Owner: - -- @@ -1168,6 +1242,13 @@ ALTER TABLE ONLY public.mirrors ALTER COLUMN id SET DEFAULT nextval('public.mirr ALTER TABLE ONLY public.packages ALTER COLUMN id SET DEFAULT nextval('public.packages_id_seq'::regclass); +-- +-- Name: release_images id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.release_images ALTER COLUMN id SET DEFAULT nextval('public.release_images_id_seq'::regclass); + + -- -- Name: release_monitoring_releases id; Type: DEFAULT; Schema: public; Owner: - -- @@ -1182,6 +1263,13 @@ ALTER TABLE ONLY public.release_monitoring_releases ALTER COLUMN id SET DEFAULT ALTER TABLE ONLY public.release_monitorings ALTER COLUMN id SET DEFAULT nextval('public.release_monitorings_id_seq'::regclass); +-- +-- Name: releases id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.releases ALTER COLUMN id SET DEFAULT nextval('public.releases_id_seq'::regclass); + + -- -- Name: repositories id; Type: DEFAULT; Schema: public; Owner: - -- @@ -1349,6 +1437,14 @@ ALTER TABLE ONLY public.packages ADD CONSTRAINT packages_pkey PRIMARY KEY (id); +-- +-- Name: release_images release_images_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.release_images + ADD CONSTRAINT release_images_pkey PRIMARY KEY (id); + + -- -- Name: release_monitoring_releases release_monitoring_releases_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1365,6 +1461,14 @@ ALTER TABLE ONLY public.release_monitorings ADD CONSTRAINT release_monitorings_pkey PRIMARY KEY (id); +-- +-- Name: releases releases_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.releases + ADD CONSTRAINT releases_pkey PRIMARY KEY (id); + + -- -- Name: repositories repositories_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -1710,6 +1814,20 @@ CREATE UNIQUE INDEX release_monitoring_releases_unique ON public.release_monitor CREATE UNIQUE INDEX release_monitorings_unique ON public.release_monitorings USING btree (distro_id, name) WHERE (deleted_at IS NULL); +-- +-- Name: releases_distro_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX releases_distro_id ON public.releases USING btree (distro_id) WHERE (deleted_at IS NULL); + + +-- +-- Name: releases_unique; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX releases_unique ON public.releases USING btree (slug) WHERE (deleted_at IS NULL); + + -- -- Name: repo_builds_build_id; Type: INDEX; Schema: public; Owner: - -- @@ -2137,6 +2255,22 @@ ALTER TABLE ONLY public.packages ADD CONSTRAINT packages_distro_id FOREIGN KEY (distro_id) REFERENCES public.distributions(id); +-- +-- Name: release_images release_images_release_id; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.release_images + ADD CONSTRAINT release_images_release_id FOREIGN KEY (release_id) REFERENCES public.releases(id); + + +-- +-- Name: release_images release_images_type_id; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.release_images + ADD CONSTRAINT release_images_type_id FOREIGN KEY (type_id) REFERENCES public.images_types(id); + + -- -- Name: release_monitoring_releases release_monitoring_releases_build_id; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -2185,6 +2319,38 @@ ALTER TABLE ONLY public.release_monitorings ADD CONSTRAINT release_monitorings_distro_id FOREIGN KEY (distro_id) REFERENCES public.distributions(id); +-- +-- Name: releases releases_created_by; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.releases + ADD CONSTRAINT releases_created_by FOREIGN KEY (created_by) REFERENCES public.users(id); + + +-- +-- Name: releases releases_deleted_by; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.releases + ADD CONSTRAINT releases_deleted_by FOREIGN KEY (deleted_by) REFERENCES public.users(id); + + +-- +-- Name: releases releases_distro_id; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.releases + ADD CONSTRAINT releases_distro_id FOREIGN KEY (distro_id) REFERENCES public.distributions(id); + + +-- +-- Name: releases releases_repo_id; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.releases + ADD CONSTRAINT releases_repo_id FOREIGN KEY (repo_id) REFERENCES public.repositories(id); + + -- -- Name: repo_builds repo_builds_added_by; Type: FK CONSTRAINT; Schema: public; Owner: - -- diff --git a/src/templates/distros/releases/index.html b/src/templates/distros/releases/index.html new file mode 100644 index 00000000..1519c3d2 --- /dev/null +++ b/src/templates/distros/releases/index.html @@ -0,0 +1,36 @@ +{% extends "../../base.html" %} + +{% block title %}{{ distro }} - {{ _("Releases") }}{% end block %} + +{% block body %} +
+
+
+ + +

{{ _("Releases") }}

+

{{ distro }}

+
+
+
+ +
+
+ {% module ReleasesList(releases) %} +
+
+{% end block %} diff --git a/src/templates/distros/releases/modules/list.html b/src/templates/distros/releases/modules/list.html new file mode 100644 index 00000000..297d532b --- /dev/null +++ b/src/templates/distros/releases/modules/list.html @@ -0,0 +1,34 @@ + diff --git a/src/templates/distros/releases/show.html b/src/templates/distros/releases/show.html new file mode 100644 index 00000000..bae0a9db --- /dev/null +++ b/src/templates/distros/releases/show.html @@ -0,0 +1,50 @@ +{% extends "../../base.html" %} + +{% block title %}{{ distro }} - {{ release }}{% end block %} + +{% block body %} +
+
+
+ + +

{{ release }}

+

+ {% if release.prerelease %} + {{ _("Development Release") }} + {% else %} + {{ _("Stable Release") }} + {% end %} +

+
+
+
+ + {# Announcement #} + {% if release.announcement %} +
+
+ {% module Text(release.announcement) %} +
+
+ {% end %} +{% end block %} diff --git a/src/web/__init__.py b/src/web/__init__.py index 47461d88..d8a868c9 100644 --- a/src/web/__init__.py +++ b/src/web/__init__.py @@ -78,6 +78,9 @@ class Application(tornado.web.Application): "PackageInfo" : packages.InfoModule, "PackageDependencies": packages.DependenciesModule, + # Releases + "ReleasesList" : distributions.ReleasesListModule, + # Repositories "ReposList" : repos.ListModule, @@ -218,6 +221,10 @@ class Application(tornado.web.Application): (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/delete", monitorings.DeleteHandler), (r"/distros/([A-Za-z0-9\-\.]+)/monitorings/([\w\-_]+)/edit", monitorings.EditHandler), + # Distro Releases + (r"/distros/([A-Za-z0-9\-\.]+)/releases", distributions.ReleasesIndexHandler), + (r"/distros/([A-Za-z0-9\-\.]+)/releases/([\w\-_]+)", distributions.ReleasesShowHandler), + # Mirrors (r"/mirrors", mirrors.IndexHandler), (r"/mirrors/create", mirrors.CreateHandler), diff --git a/src/web/distributions.py b/src/web/distributions.py index 566c9c77..5e9604c5 100644 --- a/src/web/distributions.py +++ b/src/web/distributions.py @@ -55,6 +55,43 @@ class EditHandler(base.BaseHandler): self.redirect("/distros/%s" % distro.slug) +class ReleasesIndexHandler(base.BaseHandler): + def get(self, distro_slug): + distro = self.backend.distros.get_by_slug(distro_slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro: %s" % distro_slug) + + # Pagination + limit = self.get_argument_int("limit", 20) + offset = self.get_argument_int("offset", None) + + # Fetch releases + releases = distro.get_releases(limit=limit, offset=offset) + + self.render("distros/releases/index.html", distro=distro, releases=releases) + + +class ReleasesShowHandler(base.BaseHandler): + def get(self, distro_slug, release_slug): + distro = self.backend.distros.get_by_slug(distro_slug) + if not distro: + raise tornado.web.HTTPError(404, "Could not find distro: %s" % distro_slug) + + # Fetch the release + release = distro.get_release(release_slug) + if not release: + raise tornado.web.HTTPError(404, "Could not find release %s" % release_slug) + + # XXX check permissions + + self.render("distros/releases/show.html", distro=distro, release=release) + + class ListModule(ui_modules.UIModule): def render(self, distros): return self.render_string("distros/modules/list.html", distros=distros) + + +class ReleasesListModule(ui_modules.UIModule): + def render(self, releases): + return self.render_string("distros/releases/modules/list.html", releases=releases)