#15 WIP: better support for different release bumping use cases
Closed 2 years ago by nphilipp. Opened 4 years ago by nphilipp.
fedora-infra/ nphilipp/rpmautospec master--hh-use-cases  into  main

@@ -1,4 +1,3 @@ 

- #!/usr/bin/python3

  import collections

  import datetime

  import logging

file modified
+3 -3
@@ -27,7 +27,7 @@ 

      match = evr_re.match(evr_str)

  

      if not match:

-         raise ValueError(str)

+         raise ValueError(evr_str)

  

      epoch = match.group("epoch") or 0

      epoch = int(epoch)
@@ -40,7 +40,7 @@ 

      match = release_re.match(tag)

      if match:

          pkgrel = int(match.group("pkgrel"))

-         middle = match.group("middle")

+         middle = match.group("middle") or None

          try:

              minorbump = int(match.group("minorbump"))

          except TypeError:
@@ -57,7 +57,7 @@ 

      return _kojiclient

  

  

- def get_package_builds(pkgname: str, extras: bool = False) -> List[dict]:

+ def get_package_builds(pkgname: str) -> List[dict]:

      assert _kojiclient

  

      pkgid = _kojiclient.getPackageID(pkgname)

file modified
+7 -13
@@ -1,9 +1,7 @@ 

- #!/usr/bin/python3

  from collections import defaultdict

  from itertools import chain

  import logging

  import re

- import sys

  import typing

  

  from .misc import (
@@ -41,11 +39,11 @@ 

      return subcmd_name

  

  

- def main_sequential_builds_algo(args):

+ def main_sequential_builds_algo(package: str, dist: str):

      n_builds = 1

      last_build = last_version = None

-     for build in get_package_builds(args.package):

-         if args.dist in build["release"]:

+     for build in get_package_builds(package):

+         if dist in build["release"]:

              if n_builds == 1:

                  last_build = build

                  last_version = build["version"]
@@ -62,7 +60,7 @@ 

          n_builds = max([pkgrel + 1, n_builds])

      except TypeError:

          pass

-     print(f"Next build: {last_build['name']}-{last_build['version']}-{n_builds}.{args.dist}")

+     print(f"Next build: {last_build['name']}-{last_build['version']}-{n_builds}.{dist}")

  

  

  def holistic_heuristic_calculate_release(
@@ -71,7 +69,6 @@ 

      lower_bound: dict,

      higher_bound: typing.Optional[dict],

  ):

- 

      # So what package EVR are we going for again? Default to "same as lower bound".

      try:

          epoch, version, release = evr
@@ -129,10 +126,7 @@ 

  def holistic_heuristic_algo(package: str, dist: str, evr: typing.Tuple[int, str, str]):

      match = disttag_re.match(dist)

      if not match:

-         print(

-             f"Dist tag {dist!r} has wrong format (should be e.g. 'fc31', 'epel7')", file=sys.stderr,

-         )

-         sys.exit(1)

+         raise ValueError(f"Dist tag {dist!r} has wrong format (should be e.g. 'fc31', 'epel7')")

  

      distcode = match.group("distcode")

      pkgdistver = int(match.group("distver"))
@@ -212,7 +206,7 @@ 

      else:

          new_evr_str = f"{new_evr['version']}-{new_evr['release']}"

  

-     print(f"Calculated new release, EVR: {new_evr['release']}, {new_evr_str}")

+     print(f"Calculated new release: {new_evr['release']}, EVR: {new_evr_str}")

  

      return new_evr["release"]

  
@@ -222,6 +216,6 @@ 

      koji_init(args.koji_url)

  

      if args.algorithm == "sequential_builds":

-         main_sequential_builds_algo(args)

+         main_sequential_builds_algo(args.package, args.dist)

      elif args.algorithm == "holistic_heuristic":

          holistic_heuristic_algo(args.package, args.dist, args.evr)

@@ -1,5 +1,3 @@ 

- #!/usr/bin/python3

- 

  import logging

  import os

  import subprocess

@@ -0,0 +1,60 @@ 

+ import sys

+ from unittest import mock

+ 

+ import pytest

+ 

+ from rpmautospec import cli

+ 

+ 

+ class TestCli:

+     """Test the rpmautospec.cli module"""

+ 

+     @mock.patch.dict("rpmautospec.cli.subcmd_modules_by_name")

+     @mock.patch("argparse.ArgumentParser.exit")

+     def test_get_cli_args(self, argparse_exit, capsys):

+         """Test normal function of get_cli_args()"""

+         args = cli.get_cli_args(["--koji-url=https://192.0.2.1"])

+ 

+         # Verify the global argument passed in has an effect

+         assert args.koji_url == "https://192.0.2.1"

+ 

+         # Verify that the sub-command modules are registered

+         assert len(cli.subcmd_modules_by_name)

+ 

+         # We didn't pass a sub-command, it should print help then exit

+         stdout, stderr = capsys.readouterr()

+         assert not stdout

+         assert stderr

+         argparse_exit.assert_called_once()

+ 

+     @mock.patch.dict("rpmautospec.cli.subcmd_modules_by_name")

+     def test_get_cli_args_duplicate_subcmd(self):

+         """Test that sub-commands can't register under the same name"""

+         cli.subcmd_modules_by_name["generate-changelog"] = "dummy"

+ 

+         with pytest.raises(RuntimeError):

+             cli.get_cli_args([])

+ 

+     @mock.patch.object(sys, "argv")

+     @mock.patch("rpmautospec.cli.get_cli_args")

+     @mock.patch("argparse.ArgumentParser.exit")

+     @mock.patch.dict("rpmautospec.cli.subcmd_modules_by_name")

+     @pytest.mark.parametrize("has_subcmd", (True, False))

+     def test_main(self, argparse_exit, get_cli_args, sys_argv, has_subcmd):

+         """Test normal function of main()"""

+         sys_argv = ["boo"]  # noqa: F841

+         cli.subcmd_modules_by_name["a-subcommand"] = subcmd_module = mock.MagicMock()

+ 

+         args = mock.MagicMock()

+         if has_subcmd:

+             args.subcommand = "a-subcommand"

+         else:

+             args.subcommand = None

+         get_cli_args.return_value = args

+ 

+         cli.main()

+ 

+         if has_subcmd:

+             subcmd_module.main.assert_called_once_with(args)

+         else:

+             subcmd_module.main.assert_not_called()

@@ -0,0 +1,102 @@ 

+ import random

+ from unittest import mock

+ 

+ import pytest

+ 

+ from rpmautospec import misc

+ 

+ 

+ class TestMisc:

+     """Test the rpmautospec.misc module"""

+ 

+     builds_lower_higher = (

+         {"epoch": None, "version": "1.0", "release": "1"},

+         {"epoch": None, "version": "1.0", "release": "2"},

+         {"epoch": None, "version": "1.0", "release": "3"},

+         {"epoch": 1, "version": "0.1", "release": "1"},

+     )

+ 

+     evr_str_expected = {

+         "2:2.10.18-1.fc31": (2, "2.10.18", "1.fc31"),

+         "1.0-1.fc32": (0, "1.0", "1.fc32"),

+         "not:an:evr": ValueError,

+     }

+ 

+     tag_expected = {

+         "1": (1, None, None),

+         "1.fc31": (1, ".fc31", None),

+         "2.fc30.1": (2, ".fc30", 1),

+         "3.fc32.a": (3, ".fc32.a", None),

+         "not a valid release": (None, None, None),

+     }

+ 

+     def test_rpmvercmp_key(self):

+         """Test sorting with rpmvercmp_key"""

+         # Ensure we have a build that counts as "same".

+         builds_in_order = list(self.builds_lower_higher[:3] + self.builds_lower_higher[2:])

+ 

+         # Shuffle.

+         builds = random.sample(builds_in_order, len(builds_in_order))

+ 

+         assert sorted(builds, key=misc.rpmvercmp_key) == builds_in_order

+ 

+     @pytest.mark.parametrize("evr_str", evr_str_expected.keys())

+     def test_parse_evr(self, evr_str):

+         """Test the parse_evr() function"""

+         expected = self.evr_str_expected[evr_str]

+         if isinstance(expected, type) and issubclass(expected, Exception):

+             with pytest.raises(expected) as excinfo:

+                 misc.parse_evr(evr_str)

+             assert str(excinfo.value) == evr_str

+         else:

+             assert misc.parse_evr(evr_str) == expected

+ 

+     @pytest.mark.parametrize("tag", tag_expected.keys())

+     def test_parse_release_tag(self, tag):

+         """Test the parse_release_tag() function"""

+         expected = self.tag_expected[tag]

+         assert misc.parse_release_tag(tag) == expected

+ 

+     @mock.patch.object(misc, "_kojiclient", new=mock.MagicMock())

+     @mock.patch("rpmautospec.misc.koji.ClientSession")

+     def test_koji_init(self, client_session):

+         """Test the koji_init() function"""

+         client_session.return_value = "Boo!"

+         assert misc.koji_init("https://192.0.2.1") == "Boo!"

+         client_session.assert_called_once_with("https://192.0.2.1")

+         assert misc._kojiclient == "Boo!"

+ 

+     @pytest.mark.parametrize("phenomenon", ("normal", "kojiclient unset", "unknown package"))

+     @mock.patch("rpmautospec.misc._kojiclient")

+     def test_get_package_builds(self, kojiclient_global, phenomenon):

+         """Test the get_package_builds() function"""

+         getPackageID = kojiclient_global.getPackageID

+         listBuilds = kojiclient_global.listBuilds

+ 

+         if phenomenon == "kojiclient unset":

+             misc._kojiclient = None

+         elif phenomenon == "unknown package":

+             getPackageID.return_value = None

+         else:  # normal

+             getPackageID.return_value = 12345

+             listBuilds.return_value = expected = ["foo-1.0-1.fc31", "foo-0.9-1.fc30"]

+ 

+         if phenomenon == "kojiclient unset":

+             with pytest.raises(AssertionError):

+                 misc.get_package_builds("foo")

+         elif phenomenon == "unknown package":

+             with pytest.raises(ValueError):

+                 misc.get_package_builds("foo")

+         else:

+             result = misc.get_package_builds("foo")

+ 

+         if phenomenon == "kojiclient unset":

+             getPackageID.assert_not_called()

+         else:

+             getPackageID.assert_called_once_with("foo")

+ 

+         if phenomenon == "normal":

+             listBuilds.assert_called_once_with(12345, type="rpm", queryOpts={"order": "-nvr"})

+             assert result == expected

+         else:

+             listBuilds.assert_not_called()

WIP: Not finished yet and needs PR#13 to be merged first (first commit).

Build succeeded.

rebased onto 1619354

4 years ago

Build succeeded.

rebased onto a77c90553d5f39463decbace8f0690e451e88b9e

4 years ago

Build succeeded.

Build succeeded.

rebased onto d1b1b948e85ac594411f99542accb45b63ca7e62

4 years ago

Build succeeded.

rebased onto d0ca35f

4 years ago

Looks like this conflicts now

With the changed requirements for the release bumping algorithm, these changes are moot. Closing.

Pull-Request has been closed by nphilipp

2 years ago