Repository: packit-service/packit
Pull request #872 (2020-06-19 09:21:03)

[packit] Copr enhancements
- Be able to set listed_on_hp,preserve_project,additional_repos in CLI for copr-build.
- Allow setting/editing of unlisted_on_hp,temporary,additional_repos for copr projects.
- Add new fields for metadata about copr-builds.
- Fixes: https://github.com/packit-service/packit/issues/774
- Users can set also `additional_packages` in config but it's not used yet. (Needs to be set per-chroot and will take more time to make it properly.)
- [x] PR in packit-service with proper tests will follow.

softwarefactory-project-zuul[bot]: Build failed.

- [packit-pre-commit ](https://softwarefactory-project.io/zuul/t/packit-service/build/b880219c51e84e0298d4078d05fe502e) : SUCCESS in 2m 48s
- [packit-tests-rpm ](https://softwarefactory-project.io/zuul/t/packit-service/build/2758f1e25b3b4ef38319b858dbb99a1d) : FAILURE in 16m 18s
- [packit-tests-pip-deps ](https://softwarefactory-project.io/zuul/t/packit-service/build/b85b513230174a24b1107008e9cb1b05) : TIMED_OUT in 30m 29s
- [packit-tests-git-master ](https://softwarefactory-project.io/zuul/t/packit-service/build/9f844264718f43d5b0f39fb29b666be8) : TIMED_OUT in 30m 32s
- [packit-tests-rpm-sess-rec ](https://softwarefactory-project.io/zuul/t/packit-service/build/5d991aa45ef942218aaf8a83877f1499) : SUCCESS in 2m 20s
- [packit-tests-pip-deps-sess-rec ](https://softwarefactory-project.io/zuul/t/packit-service/build/7a125ea21266440b95e7ecfec6475af1) : SUCCESS in 2m 47s
- [packit-tests-git-master-sess-rec ](https://softwarefactory-project.io/zuul/t/packit-service/build/6518f934cef54cf7b138dcf1136e8619) : SUCCESS in 3m 04s
- [reverse-dep-packit-service-tests ](https://softwarefactory-project.io/zuul/t/packit-service/build/80e066a8378548e2b6a49226c6883652) : SUCCESS in 4m 43s


Patch:
diff --git a/packit/api.py b/packit/api.py
index 8d26d0ca..c904ec6f 100644
--- a/packit/api.py
+++ b/packit/api.py
@@ -33,9 +33,9 @@ from datetime import datetime
 from pathlib import Path
 from typing import Sequence, Callable, List, Tuple, Dict, Iterable, Optional
 
-from ogr.abstract import PullRequest
 from tabulate import tabulate
 
+from ogr.abstract import PullRequest
 from packit import utils
 from packit.actions import ActionName
 from packit.config import Config
@@ -650,6 +650,10 @@ class PackitAPI:
         description: str = None,
         instructions: str = None,
         upstream_ref: str = None,
+        list_on_homepage: bool = False,
+        preserve_project: bool = False,
+        additional_packages: List[str] = None,
+        additional_repos: List[str] = None,
     ) -> Tuple[int, str]:
         """
         Submit a build to copr build system using an SRPM using the current checkout.
@@ -661,6 +665,10 @@ class PackitAPI:
         :param description: description of the project
         :param instructions: installation instructions for the project
         :param upstream_ref: git ref to upstream commit
+        :param list_on_homepage: if set, created copr project will be visible on copr's home-page
+        :param preserve_project: if set, project will not be created as temporary
+        :param list additional_packages: buildroot packages for the chroot [DOES NOT WORK YET]
+        :param list additional_repos: buildroot additional additional_repos
         :return: id of the created build and url to the build web page
         """
         srpm_path = self.create_srpm(
@@ -679,6 +687,10 @@ class PackitAPI:
             owner=owner,
             description=description,
             instructions=instructions,
+            list_on_homepage=list_on_homepage,
+            preserve_project=preserve_project,
+            additional_packages=additional_packages,
+            additional_repos=additional_repos,
         )
         logger.debug(
             f"Submitting a build to copr build system,"
diff --git a/packit/cli/copr_build.py b/packit/cli/copr_build.py
index 185f2611..a9dfa0f1 100644
--- a/packit/cli/copr_build.py
+++ b/packit/cli/copr_build.py
@@ -21,6 +21,7 @@
 # SOFTWARE.
 import logging
 import os
+from typing import Optional, List
 
 import click
 
@@ -57,6 +58,27 @@ logger = logging.getLogger(__name__)
     help="Installation instructions for the project to build in.",
     default=None,
 )
+@click.option(
+    "--list-on-homepage",
+    help="Created copr project will be visible on copr's home-page.",
+    default=False,
+    is_flag=True,
+)
+@click.option(
+    "--preserve-project",
+    help="Created copr project will not be removed after 60 days.",
+    default=False,
+    is_flag=True,
+)
+@click.option(
+    "--additional-repos",
+    help="URLs to additional yum repos, which can be used during build. "
+    "Comma separated. "
+    "This should be baseurl from .repo file. "
+    "E.g.: http://copr-be.cloud.fedoraproject.org/"
+    "results/rhughes/f20-gnome-3-12/fedora-$releasever-$basearch/",
+    default=None,
+)
 @click.option(
     "--upstream-ref",
     default=None,
@@ -75,7 +97,10 @@ def copr_build(
     targets,
     description,
     instructions,
+    list_on_homepage,
+    preserve_project,
     upstream_ref,
+    additional_repos,
     path_or_url,
 ):
     """
@@ -96,6 +121,10 @@ def copr_build(
         *targets.split(","), default="fedora-rawhide-x86_64"
     )
 
+    additional_repos_list: Optional[List[str]] = additional_repos.split(
+        ","
+    ) if additional_repos else None
+
     build_id, repo_url = api.run_copr_build(
         project=project,
         chroots=list(targets_to_build),
@@ -103,6 +132,9 @@ def copr_build(
         description=description,
         instructions=instructions,
         upstream_ref=upstream_ref,
+        list_on_homepage=list_on_homepage,
+        preserve_project=preserve_project,
+        additional_repos=additional_repos_list,
     )
     click.echo(f"Build id: {build_id}, repo url: {repo_url}")
     if not nowait:
diff --git a/packit/config/job_config.py b/packit/config/job_config.py
index d41effc2..26cac176 100644
--- a/packit/config/job_config.py
+++ b/packit/config/job_config.py
@@ -66,6 +66,10 @@ class JobMetadataConfig:
         dist_git_branches: List[str] = None,
         branch: str = None,
         scratch: bool = False,
+        list_on_homepage: bool = False,
+        preserve_project: bool = False,
+        additional_packages: List[str] = None,
+        additional_repos: List[str] = None,
     ):
         """
         :param targets: copr_build job, mock chroots where to build
@@ -75,6 +79,10 @@ class JobMetadataConfig:
         :param dist_git_branches: propose_downstream, branches in dist-git where packit should work
         :param branch: for `commit` trigger to specify the branch name
         :param scratch: if we want to run scratch build in koji
+        :param list_on_homepage: if set, created copr project will be visible on copr's home-page
+        :param preserve_project: if set, project will not be created as temporary
+        :param list additional_packages: buildroot packages for the chroot [DOES NOT WORK YET]
+        :param list additional_repos: buildroot additional additional_repos
         """
         self.targets: Set[str] = set(targets) if targets else set()
         self.timeout: int = timeout
@@ -85,6 +93,10 @@ class JobMetadataConfig:
         ) if dist_git_branches else set()
         self.branch: str = branch
         self.scratch: bool = scratch
+        self.list_on_homepage: bool = list_on_homepage
+        self.preserve_project: bool = preserve_project
+        self.additional_packages: List[str] = additional_packages or []
+        self.additional_repos: List[str] = additional_repos or []
 
     def __repr__(self):
         return (
@@ -93,9 +105,13 @@ class JobMetadataConfig:
             f"timeout={self.timeout}, "
             f"owner={self.owner}, "
             f"project={self.project}, "
-            f"dist_git_branches={self.dist_git_branches},"
-            f"branch={self.branch},"
-            f"scratch={self.scratch})"
+            f"dist_git_branches={self.dist_git_branches}, "
+            f"branch={self.branch}, "
+            f"scratch={self.scratch}, "
+            f"list_on_homepage={self.list_on_homepage}, "
+            f"preserve_project={self.preserve_project}, "
+            f"additional_packages={self.additional_packages}, "
+            f"additional_repos={self.additional_repos})"
         )
 
     def __eq__(self, other: object):
@@ -111,6 +127,10 @@ class JobMetadataConfig:
             and self.dist_git_branches == other.dist_git_branches
             and self.branch == other.branch
             and self.scratch == other.scratch
+            and self.list_on_homepage == other.list_on_homepage
+            and self.preserve_project == other.preserve_project
+            and self.additional_packages == other.additional_packages
+            and self.additional_repos == other.additional_repos
         )
 
 
diff --git a/packit/copr_helper.py b/packit/copr_helper.py
index 7d35331a..e84ecbf9 100644
--- a/packit/copr_helper.py
+++ b/packit/copr_helper.py
@@ -1,8 +1,8 @@
 import logging
+import time
 from datetime import datetime, timedelta
 from typing import Callable, List, Optional
 
-import time
 from copr.v3 import Client as CoprClient
 from copr.v3.exceptions import CoprNoResultException, CoprException
 from munch import Munch
@@ -45,6 +45,11 @@ class CoprHelper:
         owner: str = None,
         description: str = None,
         instructions: str = None,
+        list_on_homepage: bool = False,
+        preserve_project: bool = False,
+        additional_packages: List[str] = None,
+        additional_repos: List[str] = None,
+        update_additional_values: bool = True,
     ) -> None:
         """
         Create a project in copr if it does not exists.
@@ -56,17 +61,32 @@ class CoprHelper:
                 ownername=owner, projectname=project
             )
             # make sure or project has chroots set correctly
+            # we can also update other settings
             if set(copr_proj.chroot_repos.keys()) != set(chroots):
-                logger.info(f"Updating targets on project '{owner}/{project}'")
-                logger.debug(f"old = {set(copr_proj.chroot_repos.keys())}")
-                logger.debug(f"new = {set(chroots)}")
+                logger.info(f"Updating copr project '{owner}/{project}'")
+                logger.debug(f"old targets = {set(copr_proj.chroot_repos.keys())}")
+                logger.debug(f"new targets = {set(chroots)}")
+
+                if not update_additional_values:
+                    delete_after_days = None
+                elif preserve_project:
+                    delete_after_days = -1
+                else:
+                    delete_after_days = 60
+
                 self.copr_client.project_proxy.edit(
-                    owner,
-                    project,
+                    ownername=owner,
+                    projectname=project,
                     chroots=chroots,
                     description=description,
                     instructions=instructions,
+                    unlisted_on_hp=not list_on_homepage
+                    if update_additional_values
+                    else None,
+                    additional_repos=additional_repos,
+                    delete_after_days=delete_after_days,
                 )
+                # TODO: additional_packages
         except CoprNoResultException as ex:
             if owner != self.configured_owner:
                 raise PackitCoprProjectException(
@@ -74,7 +94,17 @@ class CoprHelper:
                 ) from ex
 
             logger.info(f"Copr project '{owner}/{project}' not found. Creating new.")
-            self.create_copr_project(chroots, description, instructions, owner, project)
+            self.create_copr_project(
+                chroots=chroots,
+                description=description,
+                instructions=instructions,
+                owner=owner,
+                project=project,
+                list_on_homepage=list_on_homepage,
+                preserve_project=preserve_project,
+                additional_packages=additional_packages,
+                additional_repos=additional_repos,
+            )
 
     def create_copr_project(
         self,
@@ -83,6 +113,10 @@ class CoprHelper:
         instructions: str,
         owner: str,
         project: str,
+        list_on_homepage: bool = False,
+        preserve_project: bool = False,
+        additional_packages: List[str] = None,
+        additional_repos: List[str] = None,
     ) -> None:
 
         try:
@@ -96,16 +130,18 @@ class CoprHelper:
                     "For more info check out https://packit.dev/"
                 ),
                 contact="https://github.com/packit-service/packit/issues",
-                # don't show project on Copr homepage
-                unlisted_on_hp=True,
+                # don't show project on Copr homepage by default
+                unlisted_on_hp=not list_on_homepage,
                 # delete project after the specified period of time
-                delete_after_days=60,
+                delete_after_days=60 if not preserve_project else None,
+                additional_repos=additional_repos,
                 instructions=instructions
                 or "You can check out the upstream project "
                 f"{self.upstream_local_project.git_url} to find out how to consume these builds. "
                 f"This copr project is created and handled by the packit project "
                 "(https://packit.dev/).",
             )
+            # TODO: additional_packages
         except CoprException as ex:
             error = (
                 f"Cannot create a new Copr project "
diff --git a/packit/schema.py b/packit/schema.py
index fd4d6f58..c2cc3bc2 100644
--- a/packit/schema.py
+++ b/packit/schema.py
@@ -228,6 +228,10 @@ class JobMetadataSchema(MM23Schema):
     dist_git_branches = fields.List(fields.String())
     branch = fields.String()
     scratch = fields.Boolean()
+    list_on_homepage = fields.Boolean()
+    preserve_project = fields.Boolean()
+    additional_packages = fields.List(fields.String())
+    additional_repos = fields.List(fields.String())
 
     @pre_load
     def ordered_preprocess(self, data, **_):


Test patch:
diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py
index 99b9a2f0..3825972a 100644
--- a/tests/unit/test_config.py
+++ b/tests/unit/test_config.py
@@ -38,8 +38,10 @@ from packit.config.job_config import JobMetadataConfig
 from packit.schema import JobConfigSchema
 
 
-def get_job_config_dict_simple():
-    return {"job": "build", "trigger": "release"}
+def get_job_config_dict_simple(**update):
+    result = {"job": "build", "trigger": "release"}
+    result.update(update)
+    return result
 
 
 def get_job_config_simple(**kwargs):
@@ -151,6 +153,18 @@ def test_job_config_validate(raw, is_valid):
     [
         (get_job_config_dict_simple(), get_job_config_simple()),
         (get_job_config_dict_full(), get_job_config_full()),
+        (
+            get_job_config_dict_simple(),
+            get_job_config_simple(metadata=JobMetadataConfig(preserve_project=False)),
+        ),
+        (
+            get_job_config_dict_simple(metadata={"preserve_project": False}),
+            get_job_config_simple(metadata=JobMetadataConfig(preserve_project=False)),
+        ),
+        (
+            get_job_config_dict_simple(metadata={"preserve_project": True}),
+            get_job_config_simple(metadata=JobMetadataConfig(preserve_project=True)),
+        ),
     ],
 )
 def test_job_config_parse(raw, expected_config):