From 6e8ef859b9a0970971d50f7751464b36d318082b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 07:20:24 +0000 Subject: [PATCH] fix: cache github release data per bench (backport #26382) (#26387) * fix: cache github release data per bench (#26382) Reduce API calls to github as public calls are limited. (cherry picked from commit c2d5ef175c57078e1a192506d4b1d9de0629f608) # Conflicts: # frappe/utils/caching.py * chore: conflicts --------- Co-authored-by: Ankush Menat --- frappe/utils/caching.py | 13 ++++++------- frappe/utils/change_log.py | 39 +++++++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/frappe/utils/caching.py b/frappe/utils/caching.py index ad501072c9c..98402bf43c0 100644 --- a/frappe/utils/caching.py +++ b/frappe/utils/caching.py @@ -130,7 +130,7 @@ def site_cache_wrapper(*args, **kwargs): return time_cache_wrapper -def redis_cache(ttl: int | None = 3600, user: str | bool | None = None) -> Callable: +def redis_cache(ttl: int | None = 3600, user: str | bool | None = None, shared: bool = False) -> Callable: """Decorator to cache method calls and its return values in Redis args: @@ -151,12 +151,11 @@ def clear_cache(): def redis_cache_wrapper(*args, **kwargs): func_call_key = func_key + "::" + str(__generate_request_cache_key(args, kwargs)) if frappe.cache.exists(func_call_key): - return frappe.cache.get_value(func_call_key, user=user) - else: - val = func(*args, **kwargs) - ttl = getattr(func, "ttl", 3600) - frappe.cache.set_value(func_call_key, val, expires_in_sec=ttl, user=user) - return val + return frappe.cache.get_value(func_call_key, user=user, shared=shared) + val = func(*args, **kwargs) + ttl = getattr(func, "ttl", 3600) + frappe.cache.set_value(func_call_key, val, expires_in_sec=ttl, user=user, shared=shared) + return val return redis_cache_wrapper diff --git a/frappe/utils/change_log.py b/frappe/utils/change_log.py index d20ead24896..4fb2f2868c8 100644 --- a/frappe/utils/change_log.py +++ b/frappe/utils/change_log.py @@ -11,6 +11,7 @@ import frappe from frappe import _, safe_decode from frappe.utils import cstr +from frappe.utils.caching import redis_cache from frappe.utils.frappecloud import on_frappecloud @@ -250,22 +251,16 @@ def check_release_on_github( raise ValueError("Repo cannot be empty") # Get latest version from GitHub - r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/releases") - if r.ok: - latest_non_beta_release = parse_latest_non_beta_release(r.json(), current_version) - if latest_non_beta_release: - return Version(latest_non_beta_release), owner + releases = _get_latest_releases(owner, repo) + latest_non_beta_release = parse_latest_non_beta_release(releases, current_version) + if latest_non_beta_release: + return Version(latest_non_beta_release), owner return None, None def security_issues_count(owner: str, repo: str, current_version: Version, target_version: Version) -> int: - import requests - - r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/security-advisories") - if not r.ok: - return 0 - advisories = r.json() + advisories = _get_security_issues(owner, repo) def applicable(advisory) -> bool: # Current version is in vulnerable range @@ -285,6 +280,28 @@ def applicable(advisory) -> bool: return len([sa for sa in advisories if applicable(sa)]) +@redis_cache(ttl=6 * 24 * 60 * 60, shared=True) +def _get_latest_releases(owner, repo): + import requests + + r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/releases") + if not r.ok: + return [] + + return r.json() + + +@redis_cache(ttl=6 * 24 * 60 * 60, shared=True) +def _get_security_issues(owner, repo): + import requests + + r = requests.get(f"https://api.github.com/repos/{owner}/{repo}/security-advisories") + if not r.ok: + return [] + + return r.json() + + def parse_github_url(remote_url: str) -> tuple[str, str] | tuple[None, None]: """Parse the remote URL to get the owner and repo name.""" import re