From 2d3478ea5dfea8cf2665f171f92093af45b998e0 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Thu, 28 May 2026 10:13:50 +0200 Subject: [PATCH 1/3] Add missing requirements for eol-watcher Signed-off-by: Petr "Stone" Hracek --- .gitignore | 1 + .pre-commit-config.yaml | 5 +-- eol-checker/eol_checker/checker.py | 68 +++++++----------------------- eol-checker/eol_checker/utils.py | 26 ++++++------ eol-checker/tests/test_checker.py | 40 ++++++------------ pyproject.toml | 5 +++ requirements-dev.txt | 2 + requirements.txt | 2 + 8 files changed, 53 insertions(+), 96 deletions(-) create mode 100644 pyproject.toml create mode 100644 requirements-dev.txt diff --git a/.gitignore b/.gitignore index db18d27..26b0ca8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .mypy_cache .idea +.venv .vscode __pycache__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 93e89e3..7794a31 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,11 +3,10 @@ # pre-commit install repos: -- repo: https://github.com/ambv/black - rev: 19.3b0 +- repo: https://github.com/psf/black + rev: 24.10.0 hooks: - id: black - language_version: python3.6 - repo: https://github.com/pre-commit/pre-commit-hooks rev: v2.2.3 hooks: diff --git a/eol-checker/eol_checker/checker.py b/eol-checker/eol_checker/checker.py index 7d69a21..773e62a 100755 --- a/eol-checker/eol_checker/checker.py +++ b/eol-checker/eol_checker/checker.py @@ -109,14 +109,10 @@ def check_enddate(self, lifecycle: Dict[str, Any]) -> None: self.eol_images[self.os_name][self.container_to_analyze] = container_struct if is_eol == 2: eol_msg = f"Deprecation of image stream {application_stream_name} is approaching next month should be scheduled: enddate is {enddate}" - self.approaching_eol_images[self.os_name][ - self.container_to_analyze - ] = container_struct + self.approaching_eol_images[self.os_name][self.container_to_analyze] = container_struct if is_eol == 3: eol_msg = f"Deprecation of image stream {application_stream_name} already approached one month ago should be scheduled: enddate is {enddate}" - self.already_eol_images[self.os_name][ - self.container_to_analyze - ] = container_struct + self.already_eol_images[self.os_name][self.container_to_analyze] = container_struct if is_eol != 0: logger.info(eol_msg) @@ -144,16 +140,9 @@ def _get_jira_msg(self, report_type: str, enddate: str) -> str: if self.jira_fetcher.jira is None else "Jira ticket is not filled. Use Jira issue template:" ) - return ( - self.bold_line - + f"{report_type} in {enddate}" - + self.bold_line_end - + f". {jira_msg}" - ) + return self.bold_line + f"{report_type} in {enddate}" + self.bold_line_end + f". {jira_msg}" - def summary_for_images( - self, images: dict, os_name: str, eol_type: bool = True - ) -> str: + def summary_for_images(self, images: dict, os_name: str, eol_type: bool = True) -> str: """ Generate a summary report for the images. Args: @@ -168,17 +157,12 @@ def summary_for_images( report_type = "reached EOL" if eol_type else "approaching EOL" report = "\n" report += ( - self.bold_line - + f"Summary report for {os_name}:" - + self.bold_line_end - + self.end_line + self.bold_line + f"Summary report for {os_name}:" + self.bold_line_end + self.end_line ) report += self.end_line + "\n" logger.debug("EOL images: '%s'", images) for container_name, values in images[os_name].items(): - logger.info( - "Processing container: '%s' with values: '%s'", container_name, values - ) + logger.info("Processing container: '%s' with values: '%s'", container_name, values) stream_name = values["name"] if self.send_email: for mail in self.eol_sme_mails[container_name]: @@ -186,36 +170,20 @@ def summary_for_images( self.default_mails.append(mail) if self.jira_fetcher.jira is None: logger.error("Connection to Jira failed") - jira_msg = self._get_jira_msg( - report_type=report_type, enddate=values["enddate"] - ) + jira_msg = self._get_jira_msg(report_type=report_type, enddate=values["enddate"]) jira_id = self.jira_fetcher.jira_deprecation_ticket jira_url = get_jira_ticket_url(jira_issue_id=jira_id) - url = ( - f"{jira_url}" - if self.send_email - else jira_url - ) + url = f"{jira_url}" if self.send_email else jira_url report += f"{stream_name} for {os_name} {jira_msg} {url}{self.end_line}" continue - jira_msg = self._get_jira_msg( - report_type=report_type, enddate=values["enddate"] - ) + jira_msg = self._get_jira_msg(report_type=report_type, enddate=values["enddate"]) jira_msg += "Jira ticket is already filed:" - jira_id = self.jira_fetcher.is_jira_filled_for_container( - stream_name=stream_name - ) + jira_id = self.jira_fetcher.is_jira_filled_for_container(stream_name=stream_name) if jira_id == "": - jira_msg = self._get_jira_msg( - report_type=report_type, enddate=values["enddate"] - ) + jira_msg = self._get_jira_msg(report_type=report_type, enddate=values["enddate"]) jira_id = self.jira_fetcher.jira_deprecation_ticket jira_url = get_jira_ticket_url(jira_issue_id=jira_id) - url = ( - f"{jira_url}" - if self.send_email - else jira_url - ) + url = f"{jira_url}" if self.send_email else jira_url report += f"{stream_name} for {os_name} {jira_msg} {url}{self.end_line}" report += "\n" @@ -230,13 +198,9 @@ def summary_report(self) -> str: report = "\n" for os_name in OS_NAMES: if len(self.already_eol_images[os_name]) != 0: - report += self.summary_for_images( - images=self.already_eol_images, os_name=os_name - ) + report += self.summary_for_images(images=self.already_eol_images, os_name=os_name) if len(self.eol_images[os_name]) != 0: - report += self.summary_for_images( - images=self.eol_images, os_name=os_name - ) + report += self.summary_for_images(images=self.eol_images, os_name=os_name) if len(self.approaching_eol_images[os_name]) != 0: report += self.summary_for_images( images=self.approaching_eol_images, os_name=os_name, eol_type=False @@ -266,9 +230,7 @@ def analyze_containers(self): continue self.lifecycle_data = YamlLoader.download_yaml(yaml_url) if self.lifecycle_data is None: - logger.error( - "Failed to download lifecycle YAML file from '%s'", yaml_url - ) + logger.error("Failed to download lifecycle YAML file from '%s'", yaml_url) continue self.analyze_lifecycle_yaml(self.lifecycle_data) logger.info("Analyzing OS %s completed", self.os_name) diff --git a/eol-checker/eol_checker/utils.py b/eol-checker/eol_checker/utils.py index 583cc56..e6a8811 100644 --- a/eol-checker/eol_checker/utils.py +++ b/eol-checker/eol_checker/utils.py @@ -91,17 +91,17 @@ def load_mails_from_environment(): Load email addresses from environment variables. """ sclorg_mails = {} - sclorg_mails["mariadb"] = get_env_variable("DB_SME").split(",") - sclorg_mails["mysql"] = get_env_variable("DB_SME").split(",") - sclorg_mails["postgresql"] = get_env_variable("DB_SME").split(",") - sclorg_mails["ruby"] = get_env_variable("RUBY_SME").split(",") - sclorg_mails["python"] = get_env_variable("PYTHON_SME").split(",") - sclorg_mails["nodejs"] = get_env_variable("NODEJS_SME").split(",") - sclorg_mails["perl"] = get_env_variable("PERL_SME").split(",") - sclorg_mails["php"] = get_env_variable("PHP_SME").split(",") - sclorg_mails["redis"] = get_env_variable("REDIS_SME").split(",") - sclorg_mails["varnish"] = get_env_variable("VARNISH_SME").split(",") - sclorg_mails["valkey"] = get_env_variable("VALKEY_SME").split(",") - sclorg_mails["httpd"] = get_env_variable("HTTPD_SME").split(",") - sclorg_mails["nginx"] = get_env_variable("NGINX_SME").split(",") + sclorg_mails["mariadb"] = get_env_variable("DB_MAILS").split(",") + sclorg_mails["mysql"] = get_env_variable("DB_MAILS").split(",") + sclorg_mails["postgresql"] = get_env_variable("DB_MAILS").split(",") + sclorg_mails["ruby"] = get_env_variable("RUBY_MAILS").split(",") + sclorg_mails["python"] = get_env_variable("PYTHON_MAILS").split(",") + sclorg_mails["nodejs"] = get_env_variable("NODEJS_MAILS").split(",") + sclorg_mails["perl"] = get_env_variable("PERL_MAILS").split(",") + sclorg_mails["php"] = get_env_variable("PHP_MAILS").split(",") + sclorg_mails["redis"] = get_env_variable("REDIS_MAILS").split(",") + sclorg_mails["varnish"] = get_env_variable("VARNISH_MAILS").split(",") + sclorg_mails["valkey"] = get_env_variable("VALKEY_MAILS").split(",") + sclorg_mails["httpd"] = get_env_variable("HTTPD_MAILS").split(",") + sclorg_mails["nginx"] = get_env_variable("NGINX_MAILS").split(",") return sclorg_mails diff --git a/eol-checker/tests/test_checker.py b/eol-checker/tests/test_checker.py index c3c52df..e13f73b 100644 --- a/eol-checker/tests/test_checker.py +++ b/eol-checker/tests/test_checker.py @@ -167,9 +167,9 @@ def test_summary_for_images_when_jira_unavailable(checker): def test_summary_for_images_without_jira_ticket(checker): checker.eol_images["RHEL9"] = {"nodejs": _container_struct("nodejs-18", "20250501")} flexmock(checker.jira_fetcher).should_receive("jira").and_return(flexmock()) - flexmock(checker.jira_fetcher).should_receive( - "is_jira_filled_for_container" - ).with_args(stream_name="nodejs-18").and_return("") + flexmock(checker.jira_fetcher).should_receive("is_jira_filled_for_container").with_args( + stream_name="nodejs-18" + ).and_return("") report = checker.summary_for_images(checker.eol_images, "RHEL9") @@ -199,13 +199,9 @@ def test_summary_report_includes_all_eol_categories(checker): checker.eol_images[os_name] = {} checker.approaching_eol_images[os_name] = {} checker.already_eol_images[os_name] = {} - checker.already_eol_images["RHEL8"] = { - "nodejs": _container_struct("nodejs-16", "20250401") - } + checker.already_eol_images["RHEL8"] = {"nodejs": _container_struct("nodejs-16", "20250401")} checker.eol_images["RHEL9"] = {"nodejs": _container_struct("nodejs-18", "20250501")} - checker.approaching_eol_images["RHEL10"] = { - "httpd": _container_struct("httpd-26", "20250601") - } + checker.approaching_eol_images["RHEL10"] = {"httpd": _container_struct("httpd-26", "20250601")} flexmock(checker.jira_fetcher).should_receive("jira").and_return(None) report = checker.summary_report() @@ -257,9 +253,7 @@ def test_analyze_containers_analyzes_downloaded_yaml(checker): flexmock(checker_module.YamlLoader).should_receive("get_yaml_url").and_return( "http://test/yaml" ) - flexmock(checker_module.YamlLoader).should_receive("download_yaml").and_return( - lifecycle_data - ) + flexmock(checker_module.YamlLoader).should_receive("download_yaml").and_return(lifecycle_data) flexmock(checker).should_receive("analyze_lifecycle_yaml").with_args( lifecycle_data ).at_least().once() @@ -274,16 +268,12 @@ def test_analyze_containers_populates_eol_from_yaml(checker): flexmock(checker_module.YamlLoader).should_receive("get_yaml_url").and_return( "http://test/yaml" ) - flexmock(checker_module.YamlLoader).should_receive("download_yaml").and_return( - lifecycle_data - ) + flexmock(checker_module.YamlLoader).should_receive("download_yaml").and_return(lifecycle_data) checker.analyze_containers() for os_name in OS_NAMES: - assert checker.eol_images[os_name]["nodejs"] == _container_struct( - "nodejs-18", "20250501" - ) + assert checker.eol_images[os_name]["nodejs"] == _container_struct("nodejs-18", "20250501") def _mock_send_email_env(): @@ -307,9 +297,9 @@ def test_send_emails_sends_html_message(checker): mock_smtp.should_receive("sendmail").once() mock_smtp.should_receive("close").once() _mock_send_email_env() - flexmock(checker_module).should_receive("SMTP").with_args( - "smtp.test", 2525 - ).and_return(mock_smtp) + flexmock(checker_module).should_receive("SMTP").with_args("smtp.test", 2525).and_return( + mock_smtp + ) checker.send_emails() @@ -325,9 +315,7 @@ def test_send_emails_logs_smtp_exception(checker, caplog): checker.body = "report" mock_smtp = flexmock() mock_smtp.should_receive("set_debuglevel").and_return(None) - mock_smtp.should_receive("sendmail").and_raise( - smtplib.SMTPException("smtp failure") - ) + mock_smtp.should_receive("sendmail").and_raise(smtplib.SMTPException("smtp failure")) mock_smtp.should_receive("close").once() _mock_send_email_env() flexmock(checker_module).should_receive("SMTP").and_return(mock_smtp) @@ -340,9 +328,7 @@ def test_send_emails_logs_smtp_exception(checker, caplog): def test_run_skips_jira_when_connection_unavailable(checker): flexmock(checker.jira_fetcher).should_receive("jira").and_return(None) - flexmock(checker.jira_fetcher).should_receive( - "get_jira_deprecation_details" - ).never() + flexmock(checker.jira_fetcher).should_receive("get_jira_deprecation_details").never() flexmock(checker).should_receive("analyze_containers").once() flexmock(checker).should_receive("summary_report").and_return("\nreport\n") flexmock(checker).should_receive("send_emails").never() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f933397 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[tool.black] +line-length = 100 + +[tool.ruff] +line-length = 100 diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..d121e11 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +black>=24.0.0 +ruff>=0.8.0 diff --git a/requirements.txt b/requirements.txt index e66fbf4..c3b06df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,5 @@ urllib3 GitPython slack_sdk xmltodict +colorama +atlassian-python-api From 791d76ba053b6a2ff7cbd7550c5480922ece4c45 Mon Sep 17 00:00:00 2001 From: "Petr \"Stone\" Hracek" Date: Thu, 28 May 2026 10:16:11 +0200 Subject: [PATCH 2/3] Add missing tag for new version 0.10.1 Signed-off-by: Petr "Stone" Hracek --- .github/workflows/build-and-push.yml | 2 +- Dockerfile.daily-tests | 2 +- Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-push.yml b/.github/workflows/build-and-push.yml index fb94e2d..8b4f249 100644 --- a/.github/workflows/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -19,6 +19,6 @@ jobs: registry_username: ${{ secrets.QUAY_IMAGE_SCLORG_BUILDER_USERNAME }} registry_token: ${{ secrets.QUAY_IMAGE_SCLORG_BUILDER_TOKEN }} dockerfile: Dockerfile.daily-tests - tag: "0.10.0" + tag: "0.10.1" image_name: "upstream-daily-tests" quay_application_token: ${{ secrets.QUAY_IMAGE_SCLORG_UPDATE_DESC }} diff --git a/Dockerfile.daily-tests b/Dockerfile.daily-tests index 2a70a4e..2ca117c 100644 --- a/Dockerfile.daily-tests +++ b/Dockerfile.daily-tests @@ -2,7 +2,7 @@ FROM quay.io/fedora/fedora:42 ENV SHARED_DIR="/var/ci-scripts" \ VERSION="42" \ - RELEASE_UPSTREAM="0.10.0" \ + RELEASE_UPSTREAM="0.10.1" \ UPSTREAM_TMT_REPO="https://github.com/sclorg/sclorg-testing-farm" \ UPSTREAM_TMT_DIR="sclorg-testing-farm" \ HOME="/home/nightly" \ diff --git a/Makefile b/Makefile index c6674d0..fc23f06 100644 --- a/Makefile +++ b/Makefile @@ -7,4 +7,4 @@ shellcheck: ./run-shellcheck.sh `git ls-files *.sh` build_images: - podman build -t quay.io/sclorg/upstream-daily-tests:0.10.0 -f Dockerfile.daily-tests . + podman build -t quay.io/sclorg/upstream-daily-tests:0.10.1 -f Dockerfile.daily-tests . From c11d0bcf8aba120391ec4bfc642152a02c062d5e Mon Sep 17 00:00:00 2001 From: Petr Hracek Date: Thu, 28 May 2026 10:24:35 +0200 Subject: [PATCH 3/3] Do not duplicate code Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- eol-checker/eol_checker/utils.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/eol-checker/eol_checker/utils.py b/eol-checker/eol_checker/utils.py index e6a8811..aa590c5 100644 --- a/eol-checker/eol_checker/utils.py +++ b/eol-checker/eol_checker/utils.py @@ -95,7 +95,13 @@ def load_mails_from_environment(): sclorg_mails["mysql"] = get_env_variable("DB_MAILS").split(",") sclorg_mails["postgresql"] = get_env_variable("DB_MAILS").split(",") sclorg_mails["ruby"] = get_env_variable("RUBY_MAILS").split(",") - sclorg_mails["python"] = get_env_variable("PYTHON_MAILS").split(",") + python_mails = get_env_variable("PYTHON_MAILS").split(",") + sclorg_mails["python"] = python_mails + sclorg_mails["python36"] = python_mails + sclorg_mails["python38"] = python_mails + sclorg_mails["python39"] = python_mails + sclorg_mails["python311"] = python_mails + sclorg_mails["python312"] = python_mails sclorg_mails["nodejs"] = get_env_variable("NODEJS_MAILS").split(",") sclorg_mails["perl"] = get_env_variable("PERL_MAILS").split(",") sclorg_mails["php"] = get_env_variable("PHP_MAILS").split(",")