Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package openSUSE-release-tools for openSUSE:Factory checked in at 2021-08-05 20:48:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openSUSE-release-tools (Old) and /work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "openSUSE-release-tools" Thu Aug 5 20:48:07 2021 rev:313 rq:910295 version:20210805.5b68d530 Changes: -------- --- /work/SRC/openSUSE:Factory/openSUSE-release-tools/openSUSE-release-tools.changes 2021-07-29 21:33:06.940685973 +0200 +++ /work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.1899/openSUSE-release-tools.changes 2021-08-05 20:48:40.643893365 +0200 @@ -1,0 +2,15 @@ +Thu Aug 05 10:57:53 UTC 2021 - opensuse-releaset...@opensuse.org + +- Update to version 20210805.5b68d530: + * OBSLocal: top-level classes documentation to clarify the scope + * Point to osc.core to compare the APIs + * More improvements in the OBSLocal.py documentation + * In OBSLocal.StagingWorkflow.submit_package, package may not be None + * Some convenience extensions to OBSLocal + * OBSLocal: improved management of meta + * Tiny fix in check_source_test.py + * Improvements in the OBSLocal.py documentation + * Convert OBSLocal documentation to reStructured Text + * document OBSLocal.py + +------------------------------------------------------------------- Old: ---- openSUSE-release-tools-20210729.455dc99c.obscpio New: ---- openSUSE-release-tools-20210805.5b68d530.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openSUSE-release-tools.spec ++++++ --- /var/tmp/diff_new_pack.zGG9cL/_old 2021-08-05 20:48:41.287892642 +0200 +++ /var/tmp/diff_new_pack.zGG9cL/_new 2021-08-05 20:48:41.291892638 +0200 @@ -20,7 +20,7 @@ %define source_dir openSUSE-release-tools %define announcer_filename factory-package-news Name: openSUSE-release-tools -Version: 20210729.455dc99c +Version: 20210805.5b68d530 Release: 0 Summary: Tools to aid in staging and release work for openSUSE/SUSE License: GPL-2.0-or-later AND MIT ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.zGG9cL/_old 2021-08-05 20:48:41.359892562 +0200 +++ /var/tmp/diff_new_pack.zGG9cL/_new 2021-08-05 20:48:41.359892562 +0200 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/openSUSE/openSUSE-release-tools.git</param> - <param name="changesrevision">c838245a377d88baed3e6e8b132d0db6a367b9b8</param> + <param name="changesrevision">b188340266cdd7d9621ddd5085f600fc03186ad6</param> </service> </servicedata> ++++++ openSUSE-release-tools-20210729.455dc99c.obscpio -> openSUSE-release-tools-20210805.5b68d530.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openSUSE-release-tools-20210729.455dc99c/tests/OBSLocal.py new/openSUSE-release-tools-20210805.5b68d530/tests/OBSLocal.py --- old/openSUSE-release-tools-20210729.455dc99c/tests/OBSLocal.py 2021-07-29 12:48:27.000000000 +0200 +++ new/openSUSE-release-tools-20210805.5b68d530/tests/OBSLocal.py 2021-08-05 12:55:11.000000000 +0200 @@ -166,9 +166,18 @@ class StagingWorkflow(object): + """This class is intended to setup and manipulate the environment (projects, users, etc.) in + the local OBS instance used to tests the release tools. It makes easy to setup scenarios similar + to the ones used during the real (open)SUSE development, with staging projects, rings, etc. + """ def __init__(self, project=PROJECT): - """ - Initialize the configuration + """Initializes the configuration + + Note this constructor calls :func:`create_target`, which implies several projects and users + are created right away. + + :param project: default target project + :type project: str """ THIS_DIR = os.path.dirname(os.path.abspath(__file__)) oscrc = os.path.join(THIS_DIR, 'test.oscrc') @@ -205,6 +214,12 @@ self.api = StagingAPI(APIURL, project) def load_config(self, project=None): + """Loads the corresponding :class:`osclib.Config` object into the attribute ``config`` + + :param project: target project name + :type project: str + """ + if project is None: project = self.project self.config = Config(APIURL, project) @@ -248,7 +263,15 @@ attribute_value_save(APIURL, self.project, 'Config', '\n'.join(config_lines)) def create_group(self, name, users=[]): + """Creates a group and assigns users to it. + + If the group already exists then it just updates it users. + :param name: name of group + :type name: str + :param users: list of users to be in group + :type users: list(str) + """ meta = """ <group> <title>{}</title> @@ -268,6 +291,17 @@ osc.core.http_PUT(url, data=meta) def create_user(self, name): + """Creates a user and their home project. + + Do nothing if the user already exists. + Password is always "opensuse". + + The home project is not really created in the OBS instance, but :func:`Project.update_meta` + can be used to create it. + + :param name: name of the user + :type name: str + """ if name in self.users: return meta = """ <person> @@ -285,6 +319,17 @@ self.projects[home_project] = Project(home_project, create=False) def create_target(self): + """Creates + + - target project + - "staging-bot" user + - "factory-staging" group + + setup staging and also ``*:Staging:A`` and ``*:Staging:B`` projects. + + After the execution, the target project is indexed in the projects dictionary twice, + by its name and as 'target'. + """ if self.projects.get('target'): return self.create_user('staging-bot') self.create_group('factory-staging', users=['staging-bot']) @@ -299,11 +344,23 @@ self.projects['staging:A'] = Project(self.project + ':Staging:A', create=False) self.projects['staging:B'] = Project(self.project + ':Staging:B', create=False) - def setup_rings(self): + def setup_rings(self, devel_project=None): + """Creates a typical Factory setup with rings. + + It creates three projects: 'ring0', 'ring1' and the target (see :func:`create_target`). + It also creates a 'wine' package in the target project and a link from it to ring1. + It sets the devel project for the package if ``devel_project`` is given. + + :param devel_project: name of devel project. It must exist and contain a 'wine' package, + otherwise OBS returns an error code. + :type devel_project: str or None + """ self.create_target() self.projects['ring0'] = Project(name=self.project + ':Rings:0-Bootstrap') self.projects['ring1'] = Project(name=self.project + ':Rings:1-MinimalX') - target_wine = Package(name='wine', project=self.projects['target']) + target_wine = Package( + name='wine', project=self.projects['target'], devel_project=devel_project + ) target_wine.create_commit() self.create_link(target_wine, self.projects['ring1']) @@ -321,6 +378,13 @@ return target_package def create_project(self, name, reviewer={}, maintainer={}, project_links=[]): + """Creates project if it does not already exist. + + For params see the constructor of :class:`Project` + + :return: the project instance representing the given project + :rtype: Project + """ if isinstance(name, Project): return name if name in self.projects: @@ -330,7 +394,18 @@ project_links=project_links) return self.projects[name] - def submit_package(self, package=None, project=None): + def submit_package(self, package, project=None): + """Creates submit request from package to target project. + + Both have to exist (Use :func:`create_submit_request` otherwise). + + :param package: package to submit + :type package: Package + :param project: project where to send submit request, None means use the default. + :type project: Project or str or None + :return: created request. + :rtype: Request + """ if not project: project = self.project request = Request(source_package=package, target_project=project) @@ -345,6 +420,21 @@ return request def create_submit_request(self, project, package, text=None): + """Creates submit request from package in specified project to default project. + + It creates project if not exist and also package. + Package is commited with optional text. + Note different parameters than submit_package. + + :param project: project where package will live + :type project: Project or str + :param package: package name to create + :type package: str + :param text: commit message for initial package creation + :type text: str + :return: created request. + :rtype: Request + """ project = self.create_project(project) package = Package(name=package, project=project) package.create_commit(text=text) @@ -404,7 +494,36 @@ self.api._invalidate_all() class Project(object): + """This class represents a project in the testing environment of the release tools. It usually + corresponds to a project in the local OBS instance that is used by the tests. + + The class offers methods to setup and configure such projects to simulate the different testing + scenarios. + + Not to be confused with the class Project in osc.core_, aimed to allow osc to manage projects + from real OBS instances + + .. _osc.core: https://github.com/openSUSE/osc/blob/master/osc/core.py + + """ def __init__(self, name, reviewer={}, maintainer={}, project_links=[], create=True, with_repo=False): + """Initializes a new Project object. + + If ``create`` is False, an object is created but the project is not registered in the OBS + instance. If ``create`` is True, the project is created in the OBS instance with the given + meta information (by passing that information directly to :func:`update_meta`). + + TODO: a class should be introduced to represent the meta information. See :func:`get_meta`. + + :param name: project name + :type name: str + :param reviewer: see the corresponding parameter at :func:`update_meta` + :param maintainer: see :func:`update_meta` + :param project_links: see :func:`update_meta` + :param create: whether the project should be registed in the OBS instance + :type create: bool + :param with_repo: see :func:`update_meta` + """ self.name = name self.packages = [] @@ -414,6 +533,21 @@ self.update_meta(reviewer, maintainer, project_links, with_repo=with_repo) def update_meta(self, reviewer={}, maintainer={}, project_links=[], with_repo=False): + """Sets the meta information for the project in the OBS instance + + If the project does not exist in the OBS instance, calling this method will register it. + + TODO: a class should be introduced to represent the meta. See :func:`get_meta`. + + :param reviewer: see the ``'reviewer'`` key of the meta dictionary at :func:`get_meta` + :type reviewer: dict[str, list(str)] + :param maintainer: see the ``'maintainer'`` key of the meta dictionary at :func:`get_meta` + :type maintainer: dict[str, list(str)] + :param project_links: names of linked project from which it inherits + :type project_links: list(str) + :param with_repo: whether a repository should be created as part of the meta + :type with_repo: bool + """ meta = """ <project name="{0}"> <title></title> @@ -440,6 +574,65 @@ self.custom_meta(ET.tostring(root)) + def get_meta(self): + """Data from the meta section of the project in the OBS instance + + TODO: a class should be introduced to represent the meta, a set of nested dictionaries + is definitely not the way to go for the long term. The structure of the dictionary has + to be managed at several places and the corresponding keys pollute the signature of the + ``Project`` constructor and also other methods like :func:`update_meta`. + + Currently, the meta information is represented by a dictionary with the following keys + and values: + + * ``'reviewer'``: contains a dictionary with two keys 'groups' and 'users', each of them + containing a list of strings with names of the corresponding reviewers of the project + * ``'maintainer'``: same structure as 'reviewer', but with lists of maintainer names + * ``'project_links'``: list of names of linked projects + * ``'with_repo'``: boolean indicating whether the meta includes some repository + + :return: the meta dictionary, see description above + :rtype: dict[str, dict or list(str) or bool] + """ + meta = { + 'reviewer': { 'groups': [], 'users': [] }, + 'maintainer': { 'groups': [], 'users': [] }, + 'project_links': [], + 'with_repo': False + } + url = osc.core.make_meta_url('prj', self.name, APIURL) + data = ET.parse(osc.core.http_GET(url)) + for child in data.getroot(): + if child.tag == 'repository': + meta['with_repo'] = True + elif child.tag == 'link': + meta['project_links'].append(child.attrib['project']) + elif child.tag == 'group': + role = child.attrib['role'] + if role not in ['reviewer', 'maintainer']: + continue + meta[role]['groups'].append(child.attrib['groupid']) + elif child.tag == 'person': + role = child.attrib['role'] + if role not in ['reviewer', 'maintainer']: + continue + meta[role]['users'].append(child.attrib['userid']) + + return meta + + def add_reviewers(self, users = [], groups = []): + """Adds the given reviewers to the meta information of the project + + :param users: usernames to add to the current list of reviewers + :type users: list(str) + :param groups: groups to add to the current list of reviewers + :type groups: list(str) + """ + meta = self.get_meta() + meta['reviewer']['users'] = list(set(meta['reviewer']['users'] + users)) + meta['reviewer']['groups'] = list(set(meta['reviewer']['groups'] + groups)) + self.update_meta(**meta) + def add_package(self, package): self.packages.append(package) @@ -466,7 +659,27 @@ self.remove() class Package(object): + """This class represents a package in the local OBS instance used to test the release tools and + offers methods to create and modify such packages in order to simulate the different testing + scenarios. + + Not to be confused with the class Package in osc.core_, aimed to allow osc to manage packages + from real OBS instances + + .. _osc.core: https://github.com/openSUSE/osc/blob/master/osc/core.py + """ def __init__(self, name, project, devel_project=None): + """Creates a package in the OBS instance and instantiates an object to represent it + + :param name: Package name + :type name: str + :param project: project where package lives + :type project: Project + :param devel_project: name of devel project. Package has to already exists there, + otherwise OBS returns 400. + :type devel_project: str + """ + self.name = name self.project = project @@ -512,8 +725,29 @@ text = ''.join([random.choice(string.ascii_letters) for i in range(40)]) osc.core.http_PUT(url, data=text) + def commit_files(self, path): + """Commits to the package the files in the given directory + + Useful to load fixtures. + + :param path: path to a directory containing the files that must be added to the package + """ + for filename in os.listdir(path): + with open(os.path.join(path, filename)) as f: + self.create_commit(filename=filename, text=f.read()) + class Request(object): + """This class represents a request in the local OBS instance used to test the release tools and + offers methods to create and modify such requests in order to simulate the different testing + scenarios. + + Not to be confused with the class Request in osc.core_, aimed to allow osc to create and + manage requests on real OBS instances + + .. _osc.core: https://github.com/openSUSE/osc/blob/master/osc/core.py + """ def __init__(self, source_package=None, target_project=None, target_package=None, type='submit'): + """Creates a request in the OBS instance and instantiates an object to represent it""" self.revoked = True if type == 'submit': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openSUSE-release-tools-20210729.455dc99c/tests/check_source_tests.py new/openSUSE-release-tools-20210805.5b68d530/tests/check_source_tests.py --- old/openSUSE-release-tools-20210729.455dc99c/tests/check_source_tests.py 2021-07-29 12:48:27.000000000 +0200 +++ new/openSUSE-release-tools-20210805.5b68d530/tests/check_source_tests.py 2021-08-05 12:55:11.000000000 +0200 @@ -25,6 +25,7 @@ # Using OBSLocal.StagingWorkflow makes it easier to setup testing scenarios self.wf = OBSLocal.StagingWorkflow(PROJECT) + self.project = self.wf.projects[PROJECT] # Set up the reviewers team self.wf.create_group(REVIEW_TEAM) @@ -35,9 +36,8 @@ self.bot_user = 'factory-auto' self.wf.create_user(self.bot_user) - self.project = self.wf.create_project(PROJECT) # When creating a review, set the by_user to bot_user - self.project.update_meta(reviewer={'users': [self.bot_user]}) + self.project.add_reviewers(users = [self.bot_user]) # Ensure different test runs operate in unique namespace. self.bot_name = '::'.join([type(self).__name__, str(random.getrandbits(8))]) @@ -164,13 +164,7 @@ devel_project = self.wf.create_project(SRC_PROJECT, maintainer=maintainer) devel_package = OBSLocal.Package('blowfish', project=devel_project) - blowfish_spec = os.path.join(FIXTURES, 'packages', 'blowfish', 'blowfish.spec') - with open(blowfish_spec) as f: - devel_package.create_commit(filename='blowfish.spec', text=f.read()) - - blowfish_changes = os.path.join(FIXTURES, 'packages', 'blowfish', 'blowfish.changes') - with open(blowfish_changes) as f: - devel_package.create_commit(filename='blowfish.changes', text=f.read()) + fixtures_path = os.path.join(FIXTURES, 'packages', 'blowfish') + devel_package.commit_files(fixtures_path) - devel_package.create_file(filename='blowfish-1.tar.gz') - target_package = OBSLocal.Package('blowfish', self.wf.projects['target'], devel_project=SRC_PROJECT) + OBSLocal.Package('blowfish', self.project, devel_project=SRC_PROJECT) ++++++ openSUSE-release-tools.obsinfo ++++++ --- /var/tmp/diff_new_pack.zGG9cL/_old 2021-08-05 20:48:41.963891884 +0200 +++ /var/tmp/diff_new_pack.zGG9cL/_new 2021-08-05 20:48:41.967891880 +0200 @@ -1,5 +1,5 @@ name: openSUSE-release-tools -version: 20210729.455dc99c -mtime: 1627555707 -commit: 455dc99c10beb8e109a8439d52c4c500da6fc26d +version: 20210805.5b68d530 +mtime: 1628160911 +commit: 5b68d530e89562e130939079d2314ba6e6f808fa