import re import requests from erminig.config import Config from erminig.controllers.evezh.abstract import UpstreamSource from erminig.system.retry import retry_on_failure class GitHubSource(UpstreamSource): @retry_on_failure() def get_latest(self): repo = self.config["github"] file_template = self.config.get("file") latest = self._get_latest_release(repo) if not latest: latest = self._get_latest_tag(repo) if not latest: print(f"[{self.name}] Aucune version détectée.") return None version = self.normalize_version(latest["tag"]) url = latest.get("url") if not url and file_template: filename = file_template.replace("${version}", version) url = f"https://github.com/{repo}/releases/download/{latest['tag']}/{filename}" print(f"[{self.name}] Fallback URL : {url}") print(url) return {"name": self.name, "version": version, "url": url or ""} def _github_headers(self): headers = {} if Config.GITHUB_TOKEN: headers["Authorization"] = f"token {Config.GITHUB_TOKEN}" return headers def _get_latest_release(self, repo): r = requests.get( f"https://api.github.com/repos/{repo}/releases", headers=self._github_headers(), ) r.raise_for_status() data = r.json() if not data: return None release = data[0] for asset in release.get("assets", []): if asset["browser_download_url"].endswith(".tar.gz"): return { "tag": release["tag_name"], "url": asset["browser_download_url"], } return {"tag": release["tag_name"]} def _get_latest_tag(self, repo): r = requests.get( f"https://api.github.com/repos/{repo}/tags", headers=self._github_headers() ) r.raise_for_status() tags = r.json() if not tags: return None url = f"https://github.com/{repo}/archive/refs/tags/{tags[0]["name"]}.tar.gz" return {"tag": tags[0]["name"], "url": url} def normalize_version(self, tag): # Exemples : v2.7.1 → 2.7.1, R_2_7_1 → 2.7.1, expat-2.7.1 → 2.7.1 tag = tag.strip() if tag.lower().startswith(("v", "r_", "r")): tag = re.sub(r"^[vVrR_]+", "", tag) tag = tag.replace("_", ".") match = re.search(r"(\d+\.\d+(?:\.\d+)?)", tag) return match.group(1) if match else tag