From bf867dfa6558d82ca3854d97c1244558a8e99f1d Mon Sep 17 00:00:00 2001 From: snw35 Date: Mon, 15 Dec 2025 20:05:49 +0000 Subject: [PATCH 1/2] Support variable substitution in URL and FILENAME --- dfupdate.py | 33 +++++++++++++++++++++++++++++++-- test_dfupdate.py | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/dfupdate.py b/dfupdate.py index 118d1c1..6b4b173 100644 --- a/dfupdate.py +++ b/dfupdate.py @@ -262,6 +262,30 @@ def _update_env_value(self, env_name: str, new_value: str) -> bool: ) return changed + def _substitute_version_tokens( + self, text: str | None, sw: str, new_ver: str, current_example: str + ) -> str: + """ + Replace common version placeholders with the new version. + Supports ${SW_VERSION}, $SW_VERSION, and the literal current version string. + """ + if not text: + return "" + sw_upper = sw.upper() + sw_lower = sw.lower() + placeholders = [ + f"${{{sw_upper}_VERSION}}", + f"${sw_upper}_VERSION", + f"${{{sw_lower}_VERSION}}", + f"${sw_lower}_VERSION", + ] + result = text + for placeholder in placeholders: + result = result.replace(placeholder, new_ver) + if current_example: + result = result.replace(current_example, new_ver) + return result + def _get_env_value( self, env_name: str, stage_index: int | None = None ) -> str | None: @@ -492,8 +516,13 @@ def update_software(self, sw: str, new_ver: str, current_versions: set[str]): df_sha = self._get_env_value(f"{sw}_SHA256", stage_hint) if df_url and df_filename and df_sha: logger.info("Found remote URL, fetching and calculating new shasum") - full_url = df_url + "/" + df_filename - full_url = full_url.replace(current_example, new_ver) + url_template = self._substitute_version_tokens( + df_url, sw, new_ver, current_example + ) + filename_template = self._substitute_version_tokens( + df_filename, sw, new_ver, current_example + ) + full_url = url_template.rstrip("/") + "/" + filename_template.lstrip("/") logger.info("Retrieving new SHA256 for %s from %s", sw, full_url) new_sha = get_remote_sha(full_url) if new_sha: diff --git a/test_dfupdate.py b/test_dfupdate.py index 6a80f58..404f9e8 100644 --- a/test_dfupdate.py +++ b/test_dfupdate.py @@ -130,7 +130,7 @@ def test_update_base_multi_stage(self): self.assertIn("FROM python:3.11 AS builder", content) self.assertIn("FROM alpine:3.19", content) - @mock.patch("dfupdate.get_remote_sha", return_value="deadbeef") + @mock.patch("dfupdate.get_remote_sha", return_value="jytncge6") def test_check_software_updates_across_stages(self, _msha): self._write_dockerfile( "FROM python:3.10 AS builder\n" @@ -148,8 +148,38 @@ def test_check_software_updates_across_stages(self, _msha): with open(self.dockerfile_path, "r", encoding="utf8") as fh: content = fh.read() self.assertEqual(content.count("FOO_VERSION=2.0"), 2) - self.assertIn("FOO_SHA256=deadbeef", content) - self.assertIn("FOO_SHA256 deadbeef", content) + self.assertIn("FOO_SHA256=jytncge6", content) + self.assertIn("FOO_SHA256 jytncge6", content) + + @mock.patch("dfupdate.get_remote_sha", return_value="jytncge6") + def test_update_replaces_version_placeholders_in_url_and_filename(self, msha): + self._write_dockerfile( + "FROM alpine\n" + "ENV UV_VERSION=0.9.15\n" + "ENV UV_URL=https://github.com/astral-sh/uv/releases/download/${UV_VERSION}\n" + "ENV UV_FILENAME=uv-$UV_VERSION.tar.gz\n" + "ENV UV_SHA256=old\n" + ) + with open(self.nvchecker_path, "w", encoding="utf8") as fh: + json.dump({"UV": {"version": "0.9.17"}}, fh) + self.updater.update() + used_url = msha.call_args.args[0] + self.assertNotIn("${UV_VERSION}", used_url) + self.assertNotIn("$UV_VERSION", used_url) + self.assertIn("0.9.17", used_url) + with open(self.dockerfile_path, "r", encoding="utf8") as fh: + content = fh.read() + self.assertIn("UV_VERSION=0.9.17", content) + self.assertIn("UV_SHA256=jytncge6", content) + + def test_update_detects_newer_version_and_writes(self): + self._write_dockerfile("FROM alpine\nENV UV_VERSION=0.9.15\n") + with open(self.nvchecker_path, "w", encoding="utf8") as fh: + json.dump({"UV": {"version": "0.9.17"}}, fh) + self.updater.update() + with open(self.dockerfile_path, "r", encoding="utf8") as fh: + content = fh.read() + self.assertIn("UV_VERSION=0.9.17", content) class TestParseArgs(unittest.TestCase): From 3e1fd5e0de416e9f0aa944e01f40ff853a6b1d8c Mon Sep 17 00:00:00 2001 From: snw35 Date: Mon, 15 Dec 2025 20:06:15 +0000 Subject: [PATCH 2/2] Bump version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e27220d..5f72656 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ COPY dfupdate.py /dfupdate.py COPY docker-entrypoint.sh /docker-entrypoint.sh -ENV DFUPDATE_VERSION 2.2.1 +ENV DFUPDATE_VERSION 2.2.2 ENV REQUESTS_VERSION 2.32.5 ENV TENACITY_VERSION 9.1.2 ENV DOCKERFILE_PARSE_VERSION 2.0.1