diff --git a/setup.py b/setup.py index f404150..b41b6f9 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,7 @@ setup( 'GitPython', 'python-gitlab', 'pycurl', + 'pyyaml', 'rpkg>=1.65', ], scripts=['src/bin/centpkg', 'src/bin/centpkg-sig'], diff --git a/src/centpkg/cli.py b/src/centpkg/cli.py index 5225c29..dfbca2c 100755 --- a/src/centpkg/cli.py +++ b/src/centpkg/cli.py @@ -166,6 +166,14 @@ class centpkgClient(cliClient): pp_api_url = config_get_safely(cfg, "centpkg.internal", "pp_api_url") rhel_dist_git = config_get_safely(cfg, "centpkg.internal", "rhel_dist_git") + try: + distrobaker_config = config_get_safely( + cfg, "centpkg.internal", "distrobaker_config" + ) + except rpkgError as e: + # Fall back to the default + distrobaker_config = centpkg.utils.default_distrobaker_config + # Find out divergent branch and stabilization self.log.info("Checking current state...") stream_version = self.cmd.target.split("-")[0] @@ -178,6 +186,7 @@ class centpkgClient(cliClient): repo_name=self.cmd.repo_name, cs_branch=stream_version, pp_api_url=pp_api_url, + distrobaker_config=distrobaker_config, ) except centpkg.utils.RHELError as e: self.log.error("Could not determine RHEL state of this package") @@ -272,6 +281,13 @@ class centpkgClient(cliClient): rhel_dist_git = config_get_safely( cfg, "centpkg.internal", "rhel_dist_git" ) + try: + distrobaker_config = config_get_safely( + cfg, "centpkg.internal", "distrobaker_config" + ) + except rpkgError as e: + # Fall back to the default file + distrobaker_config = centpkg.utils.default_distrobaker_config # Find out divergent branch and stabalization self.log.info("Checking rhel-target information:") @@ -285,6 +301,7 @@ class centpkgClient(cliClient): repo_name=self.cmd.repo_name, cs_branch=stream_version, pp_api_url=pp_api_url, + distrobaker_config=distrobaker_config, ) except centpkg.utils.RHELError as e: self.log.error("Could not determine RHEL state of this package") diff --git a/src/centpkg/utils.py b/src/centpkg/utils.py index 8ef80bc..64cf59f 100644 --- a/src/centpkg/utils.py +++ b/src/centpkg/utils.py @@ -24,6 +24,7 @@ from pyrpkg import rpkgError from requests.exceptions import ConnectionError, HTTPError from configparser import NoOptionError, NoSectionError from urllib.parse import quote_plus, urlparse +import yaml import git as gitpython @@ -43,6 +44,9 @@ pp_phase_name_lookup[pp_phase_stabilization] = "Stabilization" pp_phase_maintenance = 600 pp_phase_name_lookup[pp_phase_maintenance] = "Maintenance" +# Default lookup location for unsynced packages +default_distrobaker_config = "https://gitlab.cee.redhat.com/osci/distrobaker_config/-/raw/rhel9/distrobaker.yaml?ref_type=heads" + rhel_state_nt = namedtuple( "RHELState", [ @@ -52,6 +56,7 @@ rhel_state_nt = namedtuple( "phase", "rhel_target_default", "enforcing", + "synced", ], ) @@ -385,6 +390,15 @@ def _datesplit(isodate): return [int(x) for x in date_string_tuple] +# Certain packages are not synced to RHEL, and will always use the 'cXs' branch +# rules. This list is maintained in the distrobaker configuration: +def get_unsynced_projects(distrobaker_config, namespace): + res = requests.get(distrobaker_config, timeout=60) + res.raise_for_status() + payload = yaml.safe_load(res.content.decode("utf-8")) + return payload["configuration"]["control"]["exclude"][namespace] + + def parse_rhel_shortname(shortname): # The shortname is in the form rhel-9-1.0 or rhel-10.0[.beta] m = re.match( @@ -450,7 +464,9 @@ def format_branch(x_version, y_version, is_beta): return branch -def determine_rhel_state(rhel_dist_git, namespace, repo_name, cs_branch, pp_api_url): +def determine_rhel_state( + rhel_dist_git, namespace, repo_name, cs_branch, pp_api_url, distrobaker_config +): """ Arguments: * rhel_dist_git: an https URL to the RHEL dist-git. Used for determining @@ -465,6 +481,8 @@ def determine_rhel_state(rhel_dist_git, namespace, repo_name, cs_branch, pp_api_ RHEL major release. * pp_api_url: The URL to the RHEL Product Pages API. Used for determining the current development phase. + * distrobaker_config: The URL to the DistroBaker configuration. Used for + identifying packages that are not synced to RHEL. Returns: a namedtuple containing key information about the RHEL release associated with this CentOS Stream branch. It has the following members: @@ -476,11 +494,33 @@ def determine_rhel_state(rhel_dist_git, namespace, repo_name, cs_branch, pp_api_ and its format is not guaranteed. * rule_branch: The branch to be used for check-tickets rules (str) * rhel_target_default: The default `--rhel-target` (str) or - None (NoneType). The possible values if not None are "latest" or - "zstream". + None (NoneType). The possible values if not None are "latest", + "zstream" or "none" (distinctive from NoneType) * enforcing: Whether ticket approvals should be enforced. (bool) + * synced: Whether this package is synced to RHEL. False means it is a + CentOS Stream-only package. (bool) """ + # First, check if this package has a RHEL counterpart or is CentOS Stream + # only. + try: + if repo_name in get_unsynced_projects(distrobaker_config, namespace): + # We don't need to do any looking up, because it will always use the + # stream version and never enforce tickets. It will return + # rhel_target_default="none" to instruct distrobaker not to attempt + # to build on RHEL. + return rhel_state_nt( + latest_version=cs_branch, + target_version=cs_branch, + rule_branch=cs_branch, + phase=pp_phase_devtestdoc, + rhel_target_default="none", + enforcing=False, + synced=False, + ) + except (ConnectionError, HTTPError) as e: + raise RHELError("Could not retrieve distrobaker config. Are you on the VPN?") + x_version, rhel_version = stream_mapping(cs_branch) # Query the "package pages" API for the current active Y-stream release @@ -559,6 +599,7 @@ def determine_rhel_state(rhel_dist_git, namespace, repo_name, cs_branch, pp_api_ phase=pp_phase_devtestdoc, rhel_target_default="latest", enforcing=False, + synced=True, ) # First, check if this is the special case of Y=0 @@ -622,6 +663,7 @@ def determine_rhel_state(rhel_dist_git, namespace, repo_name, cs_branch, pp_api_ phase=phase, rhel_target_default=rhel_target_default, enforcing=enforcing, + synced=True, ) @@ -632,6 +674,9 @@ def format_current_state_message(rhel_state): the check-tickets function in merge requests """ + if not rhel_state.synced: + return f"This component is not synced to RHEL" + message = ( f"Current RHEL status:\n" f"\tThe latest active Y-stream release is RHEL {rhel_state.latest_version}\n"