From 223bda4a25f0efbe8d8425f71ecb8b34ebb3d02e Mon Sep 17 00:00:00 2001 From: stanislas Date: Wed, 25 Sep 2024 19:27:34 +0200 Subject: [PATCH] can update a list of software to a desired version from github --- src/losoup/cvs/github.py | 20 +++++-- src/losoup/cvs/github_settings.py | 2 +- src/losoup/file_operations/local_files.py | 19 +++++-- src/losoup/main.py | 66 +++++++++++++++++++++-- src/losoup/models.py | 13 +++-- test/losoup/software.yaml | 10 ++-- 6 files changed, 105 insertions(+), 25 deletions(-) diff --git a/src/losoup/cvs/github.py b/src/losoup/cvs/github.py index 6cb9ce2..0013a22 100644 --- a/src/losoup/cvs/github.py +++ b/src/losoup/cvs/github.py @@ -1,7 +1,7 @@ import requests -from losoup.models import Asset, GithubSoftware -from losoup.cvs.github_settings import GithubSettings +from models import Asset, GithubSoftware +from cvs.github_settings import GithubSettings class GithubConnector: @@ -21,11 +21,21 @@ class GithubConnector: return assets def get_release_asset(self, software: GithubSoftware) -> Asset: - for asset in self.get_release_assets_list(software.release_url): + release_url = self.get_release_url(software) + for asset in self.get_release_assets_list(release_url): if asset.name == software.filename: return asset - else: - continue + + def get_release_url(self, software: GithubSoftware) -> str: + headers = { + "Accept": "application/vnd.github+json", + "Authorization": f"token {self.settings.token}", + "X-Github-Api-Version": "2022-11-28", + } + response = requests.get(software.releases_url, headers=headers) + for release in response.json(): + if software.version in release.get("name"): + return release.get("url") def download_release_asset(self, asset: Asset) -> bytes: headers = { diff --git a/src/losoup/cvs/github_settings.py b/src/losoup/cvs/github_settings.py index d70d72f..ee9fbc7 100644 --- a/src/losoup/cvs/github_settings.py +++ b/src/losoup/cvs/github_settings.py @@ -2,7 +2,7 @@ from pydantic_settings import BaseSettings, SettingsConfigDict class GithubSettings(BaseSettings): - model_config = SettingsConfigDict(env_prefix="GITHUB_") + model_config = SettingsConfigDict(env_prefix="GITHUB_", env_file=".env") token: str url: str = "https://api.github.com/repos" diff --git a/src/losoup/file_operations/local_files.py b/src/losoup/file_operations/local_files.py index 9b612c8..bb093ed 100644 --- a/src/losoup/file_operations/local_files.py +++ b/src/losoup/file_operations/local_files.py @@ -1,6 +1,6 @@ from pathlib import Path -from losoup.models import Software, Asset +from models import Software class LocalFile: @@ -17,8 +17,21 @@ class LocalFile: def is_file_absent(self): return not self.is_file_present() - def write_file(self, file: Asset) -> None: - self.software.absolute_path.write_bytes(file.content) + def write_file(self, file: bytes) -> None: + self.software.absolute_path.write_bytes(file) + + def chmod(self, mode: oct) -> None: + self.software.absolute_path.chmod(mode) + + def chmod_other_versions(self, mode: oct) -> None: + others = Path(self.software.folder).glob(self.software.filename_pattern) + print(list(others)) + for other in others: + print(other.name) + print(self.software.filename) + if other.name != self.software.name: + print(f"chmod {other.name}") + other.chmod(mode) def delete_file(self): self.software.absolute_path.unlink() diff --git a/src/losoup/main.py b/src/losoup/main.py index 566c852..8509337 100644 --- a/src/losoup/main.py +++ b/src/losoup/main.py @@ -1,9 +1,30 @@ from pathlib import Path +from typing import Annotated import yaml -from losoup.file_operations import local_files -from losoup.models import Software, GitlabSoftware, GithubSoftware +from file_operations.local_files import LocalFile +from cvs.github import GithubConnector +from cvs.github_settings import GithubSettings +from models import Software, GitlabSoftware, GithubSoftware + +import typer + +from rich.progress import Progress, SpinnerColumn, TextColumn +from rich.console import Console +from rich.theme import Theme + +log_theme = Theme( + { + "info": "cyan", + "warning": "yellow", + "error": "bold red", + } +) +console = Console(theme=log_theme) + +github_settings = GithubSettings() +github = GithubConnector(github_settings) def load_software_list(software_file: Path) -> list[Software]: @@ -21,8 +42,45 @@ def load_software_list(software_file: Path) -> list[Software]: def is_software_to_update(software: Software) -> bool: - return local_files.LocalFile(software).is_file_absent() + return LocalFile(software).is_file_absent() + + +def update(software: Software) -> None: + if isinstance(software, GithubSoftware): + download = github.download_software(software) + local_file = LocalFile(software) + local_file.write_file(download) + local_file.chmod(0o544) + local_file.chmod_other_versions(0o444) + + +def main( + file_path: Annotated[ + str, + typer.Option( + "--file", + "-f", + help="Configuration file that contains the definition of the software to get and their version", + ), + ] = "./software_definition.yml" +): + path = Path(file_path).resolve() + software_list = load_software_list(path) + for software in software_list: + if is_software_to_update(software): + with Progress( + SpinnerColumn(), + TextColumn("[progress.desciption]{task.description}"), + transient=True, + ) as progress: + progress.add_task(description=f"Updating {software.name}...") + update(software) + else: + console.print( + f"[bold]{software.name}[/bold] is up to date.", + style="info", + ) if __name__ == "__main__": - pass + typer.run(main) diff --git a/src/losoup/models.py b/src/losoup/models.py index c755e84..ce42945 100644 --- a/src/losoup/models.py +++ b/src/losoup/models.py @@ -24,18 +24,17 @@ class Software(BaseModel): def absolute_path(self) -> Path: return Path(self.folder, self.filename) + @property + def filename_pattern(self) -> str: + return self.filename_format.replace("{{version}}", "*") + class GithubSoftware(Software): base_url: str = "https://api.github.com" @property - def release_url(self): - if self.version == "latest": - release_path = self.version - else: - release_path = f"tags/{self.version}" - - return f"{self.base_url}/repos/{self.owner}/{self.repo}/releases/{release_path}" + def releases_url(self): + return f"{self.base_url}/repos/{self.owner}/{self.repo}/releases" class GitlabSoftware(Software): diff --git a/test/losoup/software.yaml b/test/losoup/software.yaml index 73cd9ff..6f143f9 100644 --- a/test/losoup/software.yaml +++ b/test/losoup/software.yaml @@ -1,16 +1,16 @@ software: - - name: NextCloud + - name: Nextcloud cvs: github owner: nextcloud-releases repo: desktop - version: latest + version: 3.14.0 folder: /home/stan/Softwares - filenameFormat: KeePassXC-{{version}}-x86_64.AppImage + filenameFormat: Nextcloud-{{version}}-x86_64.AppImage - name: KeePassXC cvs: giTHub owner: keepassxreboot repo: keepassxc - version: latest + version: 2.7.9 folder: /home/stan/Softwares - filenameFormat: Nextcloud-{{version}}-x86_64.AppImage + filenameFormat: KeePassXC-{{version}}-x86_64.AppImage