From 727fff6b5378cfeb6ca5cb79263df8137ff26388 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Tue, 23 Jan 2024 09:43:28 -0500 Subject: [PATCH] fix(relpro): set 'version' parameter Tasks like `mark-as-shipped` expect a version parameter to exist. This is supposed to be set by the release-promotion action, but I neglected to add this. Typically we just assume that there's a file called `version.txt` at the root of the repo, but this PR implements a new `version-parser` key in config.yml. This defaults to a parser that reads `version.txt`, but can be used to point to a custom function in case we want to support storing the version in other locations. This function takes the parameters as input so that it can make decisions around what version to return. For example, in `mozilla-vpn-client`, there are two distinct products that can be shipped (client and addons). This will allow them to determine a version based on things like the `shipping-phase`. --- .../actions/release_promotion.py | 7 ++++ src/mozilla_taskgraph/config.py | 14 +++++++ src/mozilla_taskgraph/version.py | 12 ++++++ test/actions/test_release_promotion.py | 40 +++++++++++++++++++ test/conftest.py | 21 ++++++++-- test/data/testver.py | 2 + test/test_version.py | 11 +++++ 7 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/mozilla_taskgraph/version.py create mode 100644 test/data/testver.py create mode 100644 test/test_version.py diff --git a/src/mozilla_taskgraph/actions/release_promotion.py b/src/mozilla_taskgraph/actions/release_promotion.py index fbdb22b..9cda060 100644 --- a/src/mozilla_taskgraph/actions/release_promotion.py +++ b/src/mozilla_taskgraph/actions/release_promotion.py @@ -1,6 +1,7 @@ from taskgraph.decision import taskgraph_decision from taskgraph.parameters import Parameters from taskgraph.taskgraph import TaskGraph +from taskgraph.util.python_path import find_object from taskgraph.util.taskcluster import get_artifact from taskgraph.util.taskgraph import ( find_decision_task, @@ -126,6 +127,12 @@ def release_promotion_action(parameters, graph_config, input, task_group_id, tas parameters["shipping_phase"] = input["release_promotion_flavor"] parameters["tasks_for"] = "action" + version_parser_objpath = "mozilla_taskgraph.version:default_parser" + if "version-parser" in graph_config: + version_parser_objpath = graph_config["version-parser"] + version_func = find_object(version_parser_objpath) + parameters["version"] = version_func(parameters) + # make parameters read-only parameters = Parameters(**parameters) diff --git a/src/mozilla_taskgraph/config.py b/src/mozilla_taskgraph/config.py index ef99224..a02b524 100644 --- a/src/mozilla_taskgraph/config.py +++ b/src/mozilla_taskgraph/config.py @@ -1,3 +1,5 @@ +from textwrap import dedent + from taskgraph import config as tg from voluptuous import Optional @@ -8,5 +10,17 @@ Optional("release-format"): str, Optional("scope-prefix"): str, }, + Optional( + "version-parser", + description=dedent( + """ + Python path of the form ``:`` pointing to a + function that takes a set of parameters as input and returns + the version string to use for release tasks. + + Defaults to ``mozilla_taskgraph.version:default_parser``. + """.lstrip() + ), + ): str, } ) diff --git a/src/mozilla_taskgraph/version.py b/src/mozilla_taskgraph/version.py new file mode 100644 index 0000000..7a5469c --- /dev/null +++ b/src/mozilla_taskgraph/version.py @@ -0,0 +1,12 @@ +import os + +from taskgraph.util.vcs import get_repository + +here = os.path.abspath(os.path.dirname(__file__)) + + +def default_parser(params): + repo_root = get_repository(here).path + + with open(os.path.join(repo_root, "version.txt")) as f: + return f.read().strip() diff --git a/test/actions/test_release_promotion.py b/test/actions/test_release_promotion.py index e7a65d8..43c28ff 100644 --- a/test/actions/test_release_promotion.py +++ b/test/actions/test_release_promotion.py @@ -1,3 +1,4 @@ +import sys from itertools import count import pytest @@ -14,6 +15,12 @@ def enable(): enable_action("release-promotion") +@pytest.fixture(scope="session", autouse=True) +def mock_version(session_mocker): + m = session_mocker.patch("mozilla_taskgraph.version.default_parser") + m.return_value = "1.0.0" + + @pytest.fixture def setup(responses, parameters): tc_url = liburl.test_root_url() @@ -85,6 +92,7 @@ def test_release_promotion(parameters, setup, run_action, datadir): "shipping_phase": "promote", "target_tasks_method": "target_promote", "tasks_for": "action", + "version": "1.0.0", } ) @@ -93,6 +101,36 @@ def test_release_promotion(parameters, setup, run_action, datadir): assert_call(datadir, mock, expected_params) +def test_release_promotion_custom_version_parser( + parameters, setup, run_action, datadir, make_graph_config +): + setup() + expected_params = parameters.copy() + expected_params.update( + { + "build_number": 2, + "do_not_optimize": [], + "existing_tasks": {"a": 0, "b": 1}, + "optimize_target_tasks": True, + "shipping_phase": "promote", + "target_tasks_method": "target_promote", + "tasks_for": "action", + "version": "99", + } + ) + + graph_config = make_graph_config( + extra_config={"version-parser": "testver:fake_version"} + ) + input = {"build_number": "2", "release_promotion_flavor": "promote"} + try: + sys.path.insert(0, str(datadir)) + mock = run_action("release-promotion", parameters, input, graph_config) + finally: + sys.path.pop(0) + assert_call(datadir, mock, expected_params) + + def test_release_promotion_combine_previous_graphs( parameters, setup, run_action, datadir ): @@ -126,6 +164,7 @@ def test_release_promotion_combine_previous_graphs( "shipping_phase": "ship", "target_tasks_method": "target_ship", "tasks_for": "action", + "version": "1.0.0", } ) @@ -157,6 +196,7 @@ def test_release_promotion_rebuild_kinds(parameters, setup, run_action, datadir) "shipping_phase": "promote", "target_tasks_method": "target_promote", "tasks_for": "action", + "version": "1.0.0", } ) diff --git a/test/conftest.py b/test/conftest.py index e6a1bf6..a89690e 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -35,6 +35,11 @@ def datadir(): return here / "data" +@pytest.fixture(scope="session") +def repo_root(): + return here.parent + + @pytest.fixture(scope="session") def make_graph_config(datadir): def inner(root_dir=None, extra_config=None): @@ -86,7 +91,7 @@ def parameters(): "target_tasks_method": "test_method", "tasks_for": "hg-push", "try_mode": None, - "version": "1.0.0", + "version": "", } ) @@ -133,20 +138,30 @@ def run_action(mocker, monkeypatch, graph_config): # cause failures in other tests. monkeypatch.setattr(create, "testing", True) monkeypatch.setattr(tc_util, "testing", True) + root_dir = graph_config.root_dir - def inner(name, parameters, input): + def inner(name, parameters, input, graph_config=None): m = mocker.patch.object(release_promotion, "taskgraph_decision") m.return_value = lambda *args, **kwargs: (args, kwargs) + gc_mock = None + if graph_config: + gc_mock = mocker.patch("taskgraph.actions.registry.load_graph_config") + gc_mock.return_value = graph_config + trigger_action_callback( task_group_id="group-id", task_id=None, input=input, callback=name, parameters=parameters, - root=graph_config.root_dir, + root=root_dir, test=True, ) + + if gc_mock: + gc_mock.reset() + return m return inner diff --git a/test/data/testver.py b/test/data/testver.py new file mode 100644 index 0000000..7773364 --- /dev/null +++ b/test/data/testver.py @@ -0,0 +1,2 @@ +def fake_version(params): + return "99" diff --git a/test/test_version.py b/test/test_version.py new file mode 100644 index 0000000..fdf7c72 --- /dev/null +++ b/test/test_version.py @@ -0,0 +1,11 @@ +from unittest.mock import mock_open, patch + +from mozilla_taskgraph.version import default_parser + + +def test_default_parser(repo_root): + version = "1.0.0" + + with patch("mozilla_taskgraph.version.open", mock_open(read_data=version)) as m: + assert default_parser({}) == version + m.assert_called_with(str(repo_root / "version.txt"))