From b12ef7531948fac0a665010013dd83e05a8da917 Mon Sep 17 00:00:00 2001 From: Pablo Caro Revuelta Date: Wed, 15 Jul 2026 15:10:08 +0200 Subject: [PATCH] fix(project): bump extension projects to the latest runner (LITE-33583) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `bump` selected the runner image by the Connect platform major, which is obsolete now that the runner follows its own semver line — it silently picked a stale image. Use the latest published runner instead, matching the bootstrap behavior. Bumping only the docker images would recreate the bootstrap dependency mismatch on existing projects (new runner, stale pyproject pins), so bump now also rewrites the `python` and `connect-eaas-core` entries in [tool.poetry.dependencies] with the specifiers derived from the runner's PyPI metadata. The rewrite is line-level to preserve the file's formatting, and dev-dependencies are left untouched. Drop the now-unused get_pypi_runner_version_by_connect_version(). --- .../cli/plugins/project/extension/helpers.py | 36 ++++++++++++- .../cli/plugins/project/extension/utils.py | 16 ------ .../plugins/project/test_extension_helpers.py | 53 +++++++++++++++---- 3 files changed, 76 insertions(+), 29 deletions(-) diff --git a/connect/cli/plugins/project/extension/helpers.py b/connect/cli/plugins/project/extension/helpers.py index 9bb9618..94246fe 100644 --- a/connect/cli/plugins/project/extension/helpers.py +++ b/connect/cli/plugins/project/extension/helpers.py @@ -14,7 +14,6 @@ get_event_definitions, get_pypi_runner_requirements, get_pypi_runner_version, - get_pypi_runner_version_by_connect_version, initialize_git_repository, ) from connect.cli.plugins.project.extension.wizard import ( @@ -246,11 +245,37 @@ def _update_docker_file_runner_from(dockerfile: str, latest_version: str): return to_be_replaced +def _update_pyproject_runner_deps(pyproject_file, latest_version): + connect_eaas_core_version, python_version = get_pypi_runner_requirements(latest_version) + new_values = {'python': python_version, 'connect-eaas-core': connect_eaas_core_version} + to_be_replaced = False + section = None + lines = [] + with open(pyproject_file, 'r') as f: + for line in f.readlines(): + stripped = line.strip() + if stripped.startswith('['): + section = stripped + elif section == '[tool.poetry.dependencies]': + # ponytail: handles the plain `name = "spec"` form bootstrap emits; a + # table form ({ version = ... }) gets rewritten to the plain form + key = stripped.split('=', 1)[0].strip() + new_value = new_values.get(key) + if new_value and stripped != f'{key} = "{new_value}"': + line = f'{key} = "{new_value}"\n' + to_be_replaced = True + lines.append(line) + if to_be_replaced: + with open(pyproject_file, 'w') as f: + f.writelines(lines) + return to_be_replaced + + def bump_runner_extension_project(project_dir: str): # noqa: CCR001 console.secho(f'Bumping runner version on project {project_dir}...\n', fg='blue') updated_files = set() - latest_version = get_pypi_runner_version_by_connect_version() + latest_version = get_pypi_runner_version() latest_runner_version = f'cloudblueconnect/connect-extension-runner:{latest_version}' docker_compose_file = os.path.join(project_dir, 'docker-compose.yml') if not os.path.isfile(docker_compose_file): @@ -294,6 +319,13 @@ def bump_runner_extension_project(project_dir: str): # noqa: CCR001 f'Error: {error}', ) + pyproject_file = os.path.join(project_dir, 'pyproject.toml') + if os.path.isfile(pyproject_file) and _update_pyproject_runner_deps( + pyproject_file, + latest_version, + ): + updated_files.add(pyproject_file) + if updated_files: console.secho( f'Runner version has been successfully updated to {latest_version}. The following ' diff --git a/connect/cli/plugins/project/extension/utils.py b/connect/cli/plugins/project/extension/utils.py index 93dd9a0..f3a592c 100644 --- a/connect/cli/plugins/project/extension/utils.py +++ b/connect/cli/plugins/project/extension/utils.py @@ -7,10 +7,6 @@ from connect.client import ClientError from packaging.requirements import Requirement -from connect.cli.core.utils import ( - get_connect_version, - get_last_version_by_major, -) from connect.cli.plugins.project.extension.constants import ( PRE_COMMIT_HOOK, PYPI_EXTENSION_RUNNER_RELEASE_URL, @@ -62,18 +58,6 @@ def get_pypi_runner_requirements(runner_version): ) -def get_pypi_runner_version_by_connect_version(): - connect_version = get_connect_version() - res = requests.get(PYPI_EXTENSION_RUNNER_URL) - if res.status_code != 200 or not connect_version: - raise ClickException( - f'We can not retrieve the current connect-extension-runner version from {PYPI_EXTENSION_RUNNER_URL}.', - ) - content = res.json() - version = get_last_version_by_major(content['releases'], connect_version.split('.', 1)[0]) - return version or content['info']['version'] - - def get_extension_types(config): if config.active.is_provider(): extension_types = [('hub', 'Hub integration')] diff --git a/tests/plugins/project/test_extension_helpers.py b/tests/plugins/project/test_extension_helpers.py index 2b925d0..e8ec698 100644 --- a/tests/plugins/project/test_extension_helpers.py +++ b/tests/plugins/project/test_extension_helpers.py @@ -11,8 +11,10 @@ from click import ClickException from flake8.api import legacy as flake8 -from connect.cli.core.constants import DEFAULT_ENDPOINT -from connect.cli.plugins.project.extension.constants import PYPI_EXTENSION_RUNNER_URL +from connect.cli.plugins.project.extension.constants import ( + PYPI_EXTENSION_RUNNER_RELEASE_URL, + PYPI_EXTENSION_RUNNER_URL, +) from connect.cli.plugins.project.extension.helpers import ( bootstrap_extension_project, bump_runner_extension_project, @@ -918,17 +920,28 @@ def test_bootstrap_extension_project_if_destination_exists(mocker): assert 'Answers cannot be saved' in str(cv.value) -def test_bump_runner_version(mocker, mocked_responses, capsys): +def test_bump_runner_version(mocked_responses, capsys): + """`bump` must move the project to the latest published runner and keep the + pyproject pins (connect-eaas-core, python) in lockstep with it. + + Bumping only the docker image would recreate the bootstrap mismatch: a new + runner with stale pyproject pins fails to resolve/run after `poetry update`. + Confidence: one bump leaves the whole project coherent with the runner. + """ mocked_responses.add( 'GET', - DEFAULT_ENDPOINT, - status=401, - headers={'Connect-Version': '1.0.1-abc'}, + PYPI_EXTENSION_RUNNER_URL, + json={'info': {'version': '1.0'}}, ) mocked_responses.add( 'GET', - PYPI_EXTENSION_RUNNER_URL, - json={'releases': {'1.0': ['release info']}}, + PYPI_EXTENSION_RUNNER_RELEASE_URL.format(version='1.0'), + json={ + 'info': { + 'requires_python': '<3.13,>=3.9', + 'requires_dist': ['connect-eaas-core<38,>=37.4'], + }, + }, ) with tempfile.TemporaryDirectory() as tmp_data: @@ -957,6 +970,18 @@ def test_bump_runner_version(mocker, mocked_responses, capsys): fp.write('FROM cloudblueconnect/connect-extension-runner:0.5') with open(f'{project_dir}/OtherDockerfile', 'w') as fp: fp.write('FROM cloudblueconnect/connect-extension-runner:0.5') + with open(f'{project_dir}/pyproject.toml', 'w') as fp: + fp.write( + '[tool.poetry]\n' + 'name = "myext"\n' + '\n' + '[tool.poetry.dependencies]\n' + 'python = ">=3.8,<4"\n' + 'connect-eaas-core = ">=30"\n' + '\n' + '[tool.poetry.dev-dependencies]\n' + 'pytest = ">=6.1.2,<8"\n', + ) bump_runner_extension_project(project_dir) captured = capsys.readouterr() captured_out = ''.join(captured.out.split('\n')) @@ -964,6 +989,13 @@ def test_bump_runner_version(mocker, mocked_responses, capsys): assert f'{os.path.join(project_dir, "docker-compose.yml")}' in captured_out assert f'{os.path.join(project_dir, "OtherDockerfile")}' in captured_out assert f'{os.path.join(project_dir, "Dockerfile")}' in captured_out + assert f'{os.path.join(project_dir, "pyproject.toml")}' in captured_out + + pyproject = open(f'{project_dir}/pyproject.toml').read() + assert 'python = "<3.13,>=3.9"' in pyproject + assert 'connect-eaas-core = "<38,>=37.4"' in pyproject + # dev-dependencies section stays untouched + assert 'pytest = ">=6.1.2,<8"' in pyproject def test_bump_runner_version_no_update_required(mocker, capsys): @@ -1047,8 +1079,7 @@ def test_bump_runner_docker_yaml_error(mocker): assert 'not properly formatted' in str(error.value) -def test_bump_runner_get_version_error(mocker, mocked_responses): - mocker.patch('connect.cli.plugins.project.extension.utils.get_connect_version') +def test_bump_runner_get_version_error(mocked_responses): mocked_responses.add('GET', PYPI_EXTENSION_RUNNER_URL, status=400) with pytest.raises(ClickException) as error: bump_runner_extension_project('project_dir') @@ -1057,7 +1088,7 @@ def test_bump_runner_get_version_error(mocker, mocked_responses): def _mock_pypi_version(mocker): mocker.patch( - 'connect.cli.plugins.project.extension.helpers.get_pypi_runner_version_by_connect_version', + 'connect.cli.plugins.project.extension.helpers.get_pypi_runner_version', return_value='1.0', )