]> git.ipfire.org Git - pbs.git/commitdiff
repositories: Add option to create personal repositories
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 Jun 2022 16:10:07 +0000 (16:10 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 Jun 2022 16:10:07 +0000 (16:10 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/buildservice/misc.py
src/buildservice/repository.py
src/buildservice/users.py
src/database.sql

index c491da69a1b6932e63cba9bf038f8b651467e2a9..0db857c674b058c9e60c1a65afe405e008d87d7a 100644 (file)
@@ -6,6 +6,7 @@ import random
 import re
 import string
 import tarfile
+import unicodedata
 
 from .constants import *
 
@@ -18,6 +19,34 @@ def generate_random_string(length=16):
        """
        return "".join([random.choice(random_chars) for i in range(length)])
 
+def infinity():
+        i = 0
+
+        while True:
+                i += 1
+                yield i
+
+def normalize(*args, iteration=1):
+       s = "-".join((e for e in args if e))
+
+       # Append iteration when larger than one
+       if iteration > 1:
+               s += "-%s" % iteration
+
+       # Remove any non-ASCII characters
+       try:
+               s = unicodedata.normalize("NFKD", s)
+       except TypeError:
+               pass
+
+       # Encode into ASCII and back again
+       s = s.encode("ascii", errors="ignore").decode()
+
+       # Remove excessive whitespace
+       s = re.sub(r"[^\w]+", " ", s)
+
+       return "-".join(s.split())
+
 def format_size(s):
        units = ("B", "k", "M", "G", "T")
 
index ef2dadcb1298d9b4ff13588839951ae293c19839..2b3e79aa1c4bfbc0dfbd5a1c2c4544a7cb77d21b 100644 (file)
@@ -10,6 +10,7 @@ log.propagate = 1
 
 from . import base
 from . import logs
+from . import misc
 
 from .constants import *
 from .decorators import *
@@ -33,9 +34,44 @@ class Repositories(base.Object):
 
                return iter(repositories)
 
-       def create(self, distro, name, description):
-               return self._get_repository("INSERT INTO repositories(distro_id, name, description) \
-                       VALUES(%s, %s, %s) RETURNING *", distro.id, name, description)
+       def create(self, distro, name, owner=None):
+               """
+                       Creates a new repository
+               """
+               # Generate a slug
+               slug = self._make_slug(name, owner=owner)
+
+               return self._get_repository("""
+                       INSERT INTO
+                               repositories
+                       (
+                               distro_id,
+                               owner_id,
+                               name,
+                               slug
+                       )
+                       VALUES
+                       (
+                               %s,
+                               %s,
+                               %s,
+                               %s
+                       )
+                       RETURNING *""",
+                       distro,
+                       owner,
+                       name,
+                       slug,
+               )
+
+       def _make_slug(self, name, owner=None):
+               for i in misc.infinity():
+                       slug = misc.normalize(name)
+
+                       exists = self.db.get("SELECT 1 FROM repositories \
+                               WHERE deleted IS FALSE AND owner_id = %s AND slug = %s", owner, slug)
+                       if not exists:
+                               return slug
 
        def get_by_id(self, repo_id):
                return self._get_repository("SELECT * FROM repositories \
@@ -132,14 +168,16 @@ class Repository(base.DataObject):
 
        priority = property(lambda s: s.data.priority, set_priority)
 
-       def get_user(self):
-               if self.data.user_id:
-                       return self.backend.users.get_by_id(self.data.user_id)
+       # Owner
 
-       def set_user(self, user):
-               self._set_attribute("user_id", user.id)
+       def get_owner(self):
+               if self.data.owner_id:
+                       return self.backend.users.get_by_id(self.data.owner_id)
 
-       user = property(get_user, set_user)
+       def set_owner(self, owner):
+               self._set_attribute("owner_id", owner)
+
+       owner = property(get_owner, set_owner)
 
        @property
        def info(self):
@@ -150,31 +188,37 @@ class Repository(base.DataObject):
                        "arches" : self.arches,
                }
 
-       @property
-       def basepath(self):
-               return os.path.join(
-                       self.distro.identifier,
-                       self.identifier,
+       @lazy_property
+       def path(self):
+               parts = []
+
+               # Add owner
+               if self.owner:
+                       parts.append("~%s" % self.owner.name)
+
+               # Add distro & slug
+               parts += (
+                       self.distro.sname,
+                       self.slug,
                )
 
-       @property
-       def path(self):
-               return os.path.join(REPOS_DIR, self.basepath)
+               # Join everything together
+               return os.path.join(*parts)
 
        @property
        def url(self):
                return "/".join((
                        self.settings.get("baseurl", "https://pakfire.ipfire.org"),
                        "repositories",
-                       self.basepath,
+                       self.path,
                ))
 
        @property
        def mirrorlist(self):
                return "/".join((
                        self.settings.get("baseurl", "https://pakfire.ipfire.org"),
-                       "distro", self.distro.identifier,
-                       "repo", self.identifier,
+                       "distro", self.distro.sname,
+                       "repo", self.slug,
                        "mirrorlist?arch=%{arch}"
                ))
 
@@ -198,14 +242,6 @@ class Repository(base.DataObject):
        def name(self):
                return self.data.name
 
-       @property
-       def identifier(self):
-               return self.name.lower()
-
-       @property
-       def type(self):
-               return self.data.type
-
        @property
        def summary(self):
                lines = self.description.splitlines()
index 63a603d0b35e40f1f1ba002dc912f1548ce73096..00af8a1fe5323f7bc4748e4febeacc4d530efbe9 100644 (file)
@@ -369,6 +369,29 @@ class User(base.DataObject):
                return self.backend.sessions._get_sessions("SELECT * FROM sessions \
                        WHERE user_id = %s AND valid_until >= NOW() ORDER BY created_at")
 
+       # Custom repositories
+
+       @property
+       def repos(self):
+               """
+                       Returns all custom repositories
+               """
+               repos = self.backend.repositories._get_repos("""
+                       SELECT
+                               *
+                       FROM
+                               repositories
+                       WHERE
+                               deleted IS FALSE
+                       AND
+                               owner_id = %s
+                       ORDER BY
+                               name""",
+                       self.id,
+               )
+
+               return list(repos)
+
 
 class UserEmail(base.DataObject):
        table = "users_emails"
index 9248b03097eccd78ef68cc4c6c753f20447b2945..68aaf7572ec8a70776a4532882d96095abf6a8c5 100644 (file)
@@ -1086,7 +1086,7 @@ ALTER TABLE public.relation_sizes OWNER TO pakfire;
 CREATE TABLE public.repositories (
     id integer NOT NULL,
     name text NOT NULL,
-    type text DEFAULT 'testing'::text NOT NULL,
+    slug text,
     description text NOT NULL,
     distro_id integer NOT NULL,
     parent_id integer,
@@ -1099,8 +1099,10 @@ CREATE TABLE public.repositories (
     time_max integer DEFAULT 0 NOT NULL,
     deleted boolean DEFAULT false NOT NULL,
     priority integer,
-    user_id integer,
-    update_forced boolean DEFAULT false NOT NULL
+    owner_id integer,
+    update_forced boolean DEFAULT false NOT NULL,
+    created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
+    listed boolean DEFAULT true NOT NULL
 );
 
 
@@ -1883,14 +1885,6 @@ ALTER TABLE ONLY public.queue_delete
     ADD CONSTRAINT idx_2198155_primary PRIMARY KEY (id);
 
 
---
--- Name: repositories idx_2198164_primary; Type: CONSTRAINT; Schema: public; Owner: pakfire
---
-
-ALTER TABLE ONLY public.repositories
-    ADD CONSTRAINT idx_2198164_primary PRIMARY KEY (id);
-
-
 --
 -- Name: repositories_aux idx_2198179_primary; Type: CONSTRAINT; Schema: public; Owner: pakfire
 --
@@ -1987,6 +1981,14 @@ ALTER TABLE ONLY public.repositories_builds
     ADD CONSTRAINT repositories_builds_unique UNIQUE (repo_id, build_id);
 
 
+--
+-- Name: repositories repositories_pkey; Type: CONSTRAINT; Schema: public; Owner: pakfire
+--
+
+ALTER TABLE ONLY public.repositories
+    ADD CONSTRAINT repositories_pkey PRIMARY KEY (id);
+
+
 --
 -- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: pakfire
 --
@@ -2305,6 +2307,13 @@ CREATE INDEX packages_src_created_at ON public.packages USING btree (created_at
 CREATE INDEX repositories_builds_repo_id ON public.repositories_builds USING btree (repo_id);
 
 
+--
+-- Name: repositories_unique; Type: INDEX; Schema: public; Owner: pakfire
+--
+
+CREATE UNIQUE INDEX repositories_unique ON public.repositories USING btree (owner_id, distro_id, name) WHERE (deleted IS FALSE);
+
+
 --
 -- Name: uploads_uuid; Type: INDEX; Schema: public; Owner: pakfire
 --
@@ -2656,19 +2665,19 @@ ALTER TABLE ONLY public.repositories
 
 
 --
--- Name: repositories repositories_parent_id; Type: FK CONSTRAINT; Schema: public; Owner: pakfire
+-- Name: repositories repositories_owner_id; Type: FK CONSTRAINT; Schema: public; Owner: pakfire
 --
 
 ALTER TABLE ONLY public.repositories
-    ADD CONSTRAINT repositories_parent_id FOREIGN KEY (parent_id) REFERENCES public.repositories(id);
+    ADD CONSTRAINT repositories_owner_id FOREIGN KEY (owner_id) REFERENCES public.users(id);
 
 
 --
--- Name: repositories repositories_user_id; Type: FK CONSTRAINT; Schema: public; Owner: pakfire
+-- Name: repositories repositories_parent_id; Type: FK CONSTRAINT; Schema: public; Owner: pakfire
 --
 
 ALTER TABLE ONLY public.repositories
-    ADD CONSTRAINT repositories_user_id FOREIGN KEY (user_id) REFERENCES public.users(id);
+    ADD CONSTRAINT repositories_parent_id FOREIGN KEY (parent_id) REFERENCES public.repositories(id);
 
 
 --