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-09-20 23:33:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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" Mon Sep 20 23:33:27 2021 rev:328 rq:920284 version:20210920.e1eef928 Changes: -------- --- /work/SRC/openSUSE:Factory/openSUSE-release-tools/openSUSE-release-tools.changes 2021-09-17 23:26:19.833256458 +0200 +++ /work/SRC/openSUSE:Factory/.openSUSE-release-tools.new.1899/openSUSE-release-tools.changes 2021-09-20 23:36:17.895406704 +0200 @@ -1,0 +2,7 @@ +Mon Sep 20 09:42:05 UTC 2021 - opensuse-releaset...@opensuse.org + +- Update to version 20210920.e1eef928: + * Link the new testcase from processes.md + * Test to showcase a submit request to SLE + +------------------------------------------------------------------- Old: ---- openSUSE-release-tools-20210916.1cb39a18.obscpio New: ---- openSUSE-release-tools-20210920.e1eef928.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openSUSE-release-tools.spec ++++++ --- /var/tmp/diff_new_pack.vMmuXW/_old 2021-09-20 23:36:18.507407460 +0200 +++ /var/tmp/diff_new_pack.vMmuXW/_new 2021-09-20 23:36:18.511407465 +0200 @@ -20,7 +20,7 @@ %define source_dir openSUSE-release-tools %define announcer_filename factory-package-news Name: openSUSE-release-tools -Version: 20210916.1cb39a18 +Version: 20210920.e1eef928 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.vMmuXW/_old 2021-09-20 23:36:18.547407509 +0200 +++ /var/tmp/diff_new_pack.vMmuXW/_new 2021-09-20 23:36:18.547407509 +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">1cb39a18350f6ade62c4f02e8914c6f0f1b72c56</param> + <param name="changesrevision">e1eef928bd70a6c7d84a4c49060c89b10653bff0</param> </service> </servicedata> ++++++ openSUSE-release-tools-20210916.1cb39a18.obscpio -> openSUSE-release-tools-20210920.e1eef928.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openSUSE-release-tools-20210916.1cb39a18/docs/processes.md new/openSUSE-release-tools-20210920.e1eef928/docs/processes.md --- old/openSUSE-release-tools-20210916.1cb39a18/docs/processes.md 2021-09-16 16:29:36.000000000 +0200 +++ new/openSUSE-release-tools-20210920.e1eef928/docs/processes.md 2021-09-20 11:40:45.000000000 +0200 @@ -38,7 +38,7 @@ workflows](https://github.com/openSUSE/open-build-service/wiki/Staging-Workflow), extending and adapting them to the (open)SUSE use case. -This [testcase](../tests/factory_submit_request_test.py) showcases the whole submission process +This [testcase](../tests/factory_submit_request_tests.py) showcases the whole submission process explaining how the different reviews are created and processed by OBS and by the involved bots and release tools. @@ -114,7 +114,9 @@ like the [Origin Manager](./origin-manager.md) to verify aspects that are not relevant for Tumbleweed. -The following [SUSE-internal +This [testcase](../tests/sle_submit_request_tests.py) showcases the whole submission process +explaining how the different reviews are created and processed by IBS and by the involved bots and +release tools. Additionally, the following [SUSE-internal document](https://confluence.suse.com/display/projectmanagement/Product+Handbook) offers all kind of details about the processes involved in the development of SLE and all its associated products. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openSUSE-release-tools-20210916.1cb39a18/tests/OBSLocal.py new/openSUSE-release-tools-20210920.e1eef928/tests/OBSLocal.py --- old/openSUSE-release-tools-20210916.1cb39a18/tests/OBSLocal.py 2021-09-16 16:29:36.000000000 +0200 +++ new/openSUSE-release-tools-20210920.e1eef928/tests/OBSLocal.py 2021-09-20 11:40:45.000000000 +0200 @@ -30,6 +30,7 @@ from urllib.error import HTTPError, URLError from abc import ABC, abstractmethod +import re # pointing to other docker container APIURL = 'http://api:3000' @@ -257,6 +258,21 @@ """Used to ensure different test runs operate in unique namespace.""" return '::'.join([type(self).__name__, user, str(random.getrandbits(8))]) + def assertReviewBot(self, request_id, user, before, after, comment=None): + """Asserts the review bot associated to the given user produces the expected change in the + reviews of a request. + + This is very similar to :func:`assertReviewScript`, but it executes the corresponding review + bot instead of the script pointed by the ``script`` attribute. + """ + self.assertReview(request_id, by_user=(user, before)) + + self.execute_review_bot([request_id], user) + + review = self.assertReview(request_id, by_user=(user, after)) + if comment: + self.assertEqual(review.comment, comment) + class StagingWorkflow(ABC): """This abstract base class is intended to setup and manipulate the environment (projects, users, etc.) in the local OBS instance used to tests the release tools. Thus, the derivative @@ -694,6 +710,71 @@ return staging +class SLEWorkflow(StagingWorkflow): + """A class that makes easy to setup scenarios similar to the one used during the real + SLE development, with projects that inherit some packages from previous service packs, etc. + """ + def staging_group_name(self): + return 'sle-staging-managers' + + def initial_config(self): + return { + 'staging-group': self.staging_group_name() + } + + def create_target_project(self): + """Creates the main target project (see :func:`create_target`) + + If the name of the target project follows the SLE naming convention of using "SP" to + indicate a service pack and a prefix "GA" or "Update", this also creates all the linked + projects needed to implement package inheritance. For example, if the target name is + "SLE-15-SP1:Update", the method will create that project and also the projects + "SLE-15-SP1:GA", "SLE-15:Update", "SLE-15:GA", linking each project to the corresponding one + in the inheritance chain. + """ + if not re.search(r'.+:(GA|Update)$', self.project): + super().create_target_project() + return + + suffixes = ["GA", "Update"] + basename, number, suffix = self._prj_name_components(self.project) + last = number * 2 + suffixes.index(suffix) + + previous = None + for num in range(0, last + 1): + name = self._sp_name(basename, int(num / 2)) + suffix = suffixes[num % 2] + name = name + ":" + suffix + + if previous: + p = Project(name, project_links=[previous]) + else: + p = Project(name) + + self.projects[name] = p + previous = name + + self.projects['target'] = self.projects[self.project] + + def _prj_name_components(self, prj_name): + """Internal function to break a SLE-like name into pieces""" + distro, suffix = prj_name.rsplit(":", 1) + match = re.search(r'(.*)-SP(\d+)$', distro) + if match: + number = int(match.group(2)) + basename = match.group(1) + else: + number = 0 + basename = distro + return [basename, number, suffix] + + def _sp_name(self, basename, number): + """Internal function to build a SLE-like name""" + if number > 0: + return f'{basename}-SP{number}' + else: + return basename + 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. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openSUSE-release-tools-20210916.1cb39a18/tests/sle_submit_request_tests.py new/openSUSE-release-tools-20210920.e1eef928/tests/sle_submit_request_tests.py --- old/openSUSE-release-tools-20210916.1cb39a18/tests/sle_submit_request_tests.py 1970-01-01 01:00:00.000000000 +0100 +++ new/openSUSE-release-tools-20210920.e1eef928/tests/sle_submit_request_tests.py 2021-09-20 11:40:45.000000000 +0200 @@ -0,0 +1,205 @@ +import logging +from . import OBSLocal +import random +import os + +# Needed to configure OriginManager +import yaml +from osclib.core import attribute_value_save + +# Needed to mock LegalAuto +from osc.core import change_review_state +from mock import MagicMock + +# Import the involved staging commands +from osclib.freeze_command import FreezeCommand +from osclib.select_command import SelectCommand +from osclib.accept_command import AcceptCommand + +# Import the involved bots +from check_source import CheckSource +from check_tags_in_requests import TagChecker +legal_auto = __import__("legal-auto") # Needed because of the dash in the filename +LegalAuto = legal_auto.LegalAuto +origin_manager = __import__("origin-manager") # Same than above, dash in the filename +OriginManager = origin_manager.OriginManager + +PROJECT = 'SUSE:SLE-15-SP3:GA' +DEVEL_PROJECT = 'devel:drinking' +STAGING_PROJECT_NAME = 'SUSE:SLE-15-SP3:GA:Staging:A' +HUMAN_REVIEWER = 'release-manager' +FIXTURES = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'fixtures') + +class TestSLESubmitRequest(OBSLocal.TestCase): + """Tests for the whole lifecycle of submit requests in SLE + + Similar in purpose and philosophy to TestFactorySubmitRequest, check + factory_submit_request_tests.py for more details + """ + + def setUp(self): + super(TestSLESubmitRequest, self).setUp() + + # Setup the basic scenario, with manual reviewers, staging projects[...] + self.wf = OBSLocal.SLEWorkflow(PROJECT) + self.__setup_review_teams() + self.__setup_devel_package('wine') + self.__config_origin_manager() + + # Setup the different bots typically used for Factory + self.setup_review_bot(self.wf, PROJECT, 'factory-auto', CheckSource) + self.setup_review_bot(self.wf, PROJECT, 'licensedigger', LegalAuto) + self.setup_review_bot(self.wf, PROJECT, 'sle-changelog-checker', TagChecker) + self.setup_review_bot(self.wf, PROJECT, 'origin-manager', OriginManager) + + # Simulating the environment to please some of the bots while keeping this test readable + # may be a bit more tricky than it seems. Check the descriptions of __mock_licendigger and + # __mock_changelog_checker for more rationale + self.__mock_licensedigger() + self.__mock_changelog_checker() + + # The staging project must be frozen in order to move packages into it + FreezeCommand(self.wf.api).perform(STAGING_PROJECT_NAME) + + # Create the submit request + self.request = self.wf.create_submit_request(DEVEL_PROJECT, 'wine') + + def tearDown(self): + super().tearDown() + del self.wf + + def project(self): + return self.wf.projects[PROJECT] + + def test_happy_path(self): + """Tests the ideal case in which all bots are happy and the request successfully goes + through staging""" + + reqid = self.request.reqid + + # Initial state: reviews have been created for... + # ...three human reviewers... + self.assertReview(reqid, by_group=('sle-release-managers', 'new')) + self.assertReview(reqid, by_group=('autobuild-team', 'new')) + self.assertReview(reqid, by_group=('origin-reviewers', 'new')) + # ...for the staging workflow... + self.assertReview(reqid, by_group=('sle-staging-managers', 'new')) + + # ...and for the bots. + # So let's first execute the bots and verify their results + self.assertReviewBot(reqid, 'factory-auto', 'new', 'accepted') + self.assertReviewBot(reqid, 'licensedigger', 'new', 'accepted') + self.assertReviewBot(reqid, 'origin-manager', 'new', 'accepted') + self.assertReviewBot(reqid, 'sle-changelog-checker', 'new', 'accepted') + + # So now that bots are happy, let's accept the manual reviews + self.osc_user(HUMAN_REVIEWER) + change_review_state(self.wf.apiurl, reqid, 'accepted', by_group='sle-release-managers') + change_review_state(self.wf.apiurl, reqid, 'accepted', by_group='autobuild-team') + change_review_state(self.wf.apiurl, reqid, 'accepted', by_group='origin-reviewers') + self.osc_user_pop() + + # Now only the staging workflow is pending + self.assertReview(reqid, by_group=('sle-release-managers', 'accepted')) + self.assertReview(reqid, by_group=('autobuild-team', 'accepted')) + self.assertReview(reqid, by_group=('origin-reviewers', 'accepted')) + self.assertReview(reqid, by_group=('sle-staging-managers', 'new')) + + # Before using the staging plugin, we need to force a reload of the configuration + # because assertReviewBot temporarily switches the user and that causes problems + self.wf.load_config() + + # One staging manager puts the request into the staging project + SelectCommand(self.wf.api, STAGING_PROJECT_NAME).perform(['wine']) + + # The sle-staging-managers review is now accepted and a new review associated to + # the staging project has been created + self.assertReview(reqid, by_group=('sle-staging-managers', 'accepted')) + self.assertReview(reqid, by_project=(STAGING_PROJECT_NAME, 'new')) + + # Let's say everything looks good in the staging project, so the staging manager can + # accept that staging + AcceptCommand(self.wf.api).accept_all([STAGING_PROJECT_NAME], True) + + # Finally, all the reviews are accepted: + # ...one for each bot, + self.assertReview(reqid, by_user=('factory-auto', 'accepted')) + self.assertReview(reqid, by_user=('licensedigger', 'accepted')) + self.assertReview(reqid, by_user=('sle-changelog-checker', 'accepted')) + self.assertReview(reqid, by_user=('origin-manager', 'accepted')) + # ...one for each manual review + self.assertReview(reqid, by_group=('sle-release-managers', 'accepted')) + self.assertReview(reqid, by_group=('autobuild-team', 'accepted')) + self.assertReview(reqid, by_group=('origin-reviewers', 'accepted')) + # ...and two for the staging project (one as a consequence of selecting the package into a + # staging project and the other as a consequence of accepting the staging) + self.assertReview(reqid, by_group=('sle-staging-managers', 'accepted')) + self.assertReview(reqid, by_project=(STAGING_PROJECT_NAME, 'accepted')) + + # So it's time to accept the request + self.request.change_state('accepted') + self.assertRequestState(reqid, name='accepted') + + def __setup_devel_package(self, pkg_name): + pkg = self.wf.create_package(DEVEL_PROJECT, pkg_name) + pkg.commit_files(os.path.join(FIXTURES, 'packages', pkg_name)) + + target_pkg = OBSLocal.Package(pkg_name, project=self.project(), devel_project=DEVEL_PROJECT) + target_pkg.create_commit() + + def __setup_review_teams(self): + """Creates the different review teams for manual reviews. + + For simplicity, this uses a common user for all groups. + + This also sets those groups as reviewers of the target project, to ensure new reviews are + created for them as soon as the request is created. Note the difference with the Factory + workflow, in which the groups of human reviewers are not initially set as reviewers in the + target project configuration. Instead, in Factory the review targeting the human reviewers + is created by the factory-auto (CheckSource) bot. + """ + self.wf.create_user(HUMAN_REVIEWER) + groups = ['sle-release-managers', 'origin-reviewers', 'autobuild-team'] + for group in groups: + self.wf.create_group(group, users=[HUMAN_REVIEWER]) + self.project().add_reviewers(groups = [group]) + + def __config_origin_manager(self): + """Creates the very minimal configuration needed by origin-manager to work""" + self.wf.create_attribute_type('OSRT', 'OriginConfig', 1) + self.wf.remote_config_set({'originmanager-request-age-min': 0}) + config = { + 'origins': [{'<devel>': {}}], + 'review-user': 'origin-manager', + 'fallback-group': 'origin-reviewers' + } + config = yaml.dump(config, default_flow_style=False) + attribute_value_save(self.wf.apiurl, PROJECT, 'OriginConfig', config) + + def __mock_changelog_checker(self): + """Mocks the verification done by the TagChecker. + + Normally, the bot checks whether the request references an entry in any of the known + issue trackers or whether the same request has been sent to Factory. Although simulating one + of those scenarios looks easy (and probably is), the whole check is mocked to return True + for simplicity (the rest of the execution of the bot still takes place normally). + """ + bot = self.review_bots['sle-changelog-checker'] + bot.checkTagInRequest = MagicMock(return_value = True) + + def __mock_licensedigger(self): + """Mocks the execution of the LegalAuto bot, so it always succeeds and accepts the review + + Unfortunatelly, LegalAuto is not written to be testable and it's very dependant on external + components. So just mocking its whole execution looks like the simplest solution for the + time being. Hopefully this whole mock could be removed in the future. + """ + bot = self.review_bots['licensedigger'] + bot.check_requests = MagicMock(side_effect=self.__accept_license) + + def __accept_license(self): + """See :func:`__mock_licensedigger`""" + change_review_state( + apiurl = self.wf.apiurl, reqid = self.request.reqid, + newstate = 'accepted', by_user='licensedigger' + ) ++++++ openSUSE-release-tools.obsinfo ++++++ --- /var/tmp/diff_new_pack.vMmuXW/_old 2021-09-20 23:36:19.155408260 +0200 +++ /var/tmp/diff_new_pack.vMmuXW/_new 2021-09-20 23:36:19.155408260 +0200 @@ -1,5 +1,5 @@ name: openSUSE-release-tools -version: 20210916.1cb39a18 -mtime: 1631802576 -commit: 1cb39a18350f6ade62c4f02e8914c6f0f1b72c56 +version: 20210920.e1eef928 +mtime: 1632130845 +commit: e1eef928bd70a6c7d84a4c49060c89b10653bff0