feat: add update command to FastAnime CLI for version management

This commit is contained in:
Benexl
2025-07-24 20:41:17 +03:00
parent fd59d64b76
commit ae3a59d116
4 changed files with 154 additions and 8 deletions

View File

@@ -31,6 +31,7 @@ commands = {
"search": "search.search",
"anilist": "anilist.anilist",
"download": "download.download",
"update": "update.update",
}

View File

@@ -0,0 +1,144 @@
"""Update command for FastAnime CLI."""
import sys
from typing import TYPE_CHECKING
import click
from rich import print
from rich.console import Console
from rich.markdown import Markdown
from ..utils.update import check_for_updates, update_app
if TYPE_CHECKING:
from ...core.config import AppConfig
@click.command(
help="Update FastAnime to the latest version",
short_help="Update FastAnime",
epilog="""
\b
\b\bExamples:
# Check for updates and update if available
fastanime update
\b
# Force update even if already up to date
fastanime update --force
\b
# Only check for updates without updating
fastanime update --check-only
\b
# Show release notes for the latest version
fastanime update --release-notes
""",
)
@click.option(
"--force",
"-f",
is_flag=True,
help="Force update even if already up to date",
)
@click.option(
"--check-only",
"-c",
is_flag=True,
help="Only check for updates without updating",
)
@click.option(
"--release-notes",
"-r",
is_flag=True,
help="Show release notes for the latest version",
)
@click.pass_context
@click.pass_obj
def update(config: "AppConfig", ctx: click.Context, force: bool, check_only: bool, release_notes: bool) -> None:
"""
Update FastAnime to the latest version.
This command checks for available updates and optionally updates
the application to the latest version from the configured sources
(pip, uv, pipx, git, or nix depending on installation method).
Args:
config: The application configuration object
ctx: The click context containing CLI options
force: Whether to force update even if already up to date
check_only: Whether to only check for updates without updating
release_notes: Whether to show release notes for the latest version
"""
try:
if release_notes:
print("[cyan]Fetching latest release notes...[/]")
is_latest, release_json = check_for_updates()
if not release_json:
print("[yellow]Could not fetch release information. Please check your internet connection.[/]")
sys.exit(1)
version = release_json.get('tag_name', 'unknown')
release_name = release_json.get('name', version)
release_body = release_json.get('body', 'No release notes available.')
published_at = release_json.get('published_at', 'unknown')
console = Console()
print(f"[bold cyan]Release: {release_name}[/]")
print(f"[dim]Version: {version}[/]")
print(f"[dim]Published: {published_at}[/]")
print()
# Display release notes as markdown if available
if release_body.strip():
markdown = Markdown(release_body)
console.print(markdown)
else:
print("[dim]No release notes available for this version.[/]")
return
elif check_only:
print("[cyan]Checking for updates...[/]")
is_latest, release_json = check_for_updates()
if not release_json:
print("[yellow]Could not check for updates. Please check your internet connection.[/]")
sys.exit(1)
if is_latest:
print("[green]FastAnime is up to date![/]")
print(f"[dim]Current version: {release_json.get('tag_name', 'unknown')}[/]")
else:
latest_version = release_json.get('tag_name', 'unknown')
print(f"[yellow]Update available: {latest_version}[/]")
print(f"[dim]Run 'fastanime update' to update[/]")
sys.exit(1)
else:
print("[cyan]Checking for updates and updating if necessary...[/]")
success, release_json = update_app(force=force)
if not release_json:
print("[red]Could not check for updates. Please check your internet connection.[/]")
sys.exit(1)
if success:
latest_version = release_json.get('tag_name', 'unknown')
print(f"[green]Successfully updated to version {latest_version}![/]")
else:
if force:
print("[red]Update failed. Please check the error messages above.[/]")
sys.exit(1)
# If not forced and update failed, it might be because already up to date
# The update_app function already prints appropriate messages
except KeyboardInterrupt:
print("\n[yellow]Update cancelled by user.[/]")
sys.exit(1)
except Exception as e:
print(f"[red]An error occurred during update: {e}[/]")
# Get trace option from parent context
trace = ctx.parent.params.get('trace', False) if ctx.parent else False
if trace:
raise
sys.exit(1)

View File

@@ -9,13 +9,13 @@ import sys
import requests
from rich import print
from ... import APP_NAME, AUTHOR, GIT_REPO, __version__
from ...core.constants import AUTHOR, GIT_REPO, PROJECT_NAME_LOWER, __version__
API_URL = f"https://api.{GIT_REPO}/repos/{AUTHOR}/{APP_NAME}/releases/latest"
API_URL = f"https://api.{GIT_REPO}/repos/{AUTHOR}/{PROJECT_NAME_LOWER}/releases/latest"
def check_for_updates():
USER_AGENT = f"{APP_NAME} user"
USER_AGENT = f"{PROJECT_NAME_LOWER} user"
try:
request = requests.get(
API_URL,
@@ -96,9 +96,9 @@ def update_app(force=False):
return False, release_json
process = subprocess.run(
[NIX, "profile", "upgrade", APP_NAME.lower()], check=False
[NIX, "profile", "upgrade", PROJECT_NAME_LOWER], check=False
)
elif is_git_repo(AUTHOR, APP_NAME):
elif is_git_repo(AUTHOR, PROJECT_NAME_LOWER) :
GIT_EXECUTABLE = shutil.which("git")
args = [
GIT_EXECUTABLE,
@@ -117,9 +117,9 @@ def update_app(force=False):
)
elif UV := shutil.which("uv"):
process = subprocess.run([UV, "tool", "upgrade", APP_NAME], check=False)
process = subprocess.run([UV, "tool", "upgrade", PROJECT_NAME_LOWER], check=False)
elif PIPX := shutil.which("pipx"):
process = subprocess.run([PIPX, "upgrade", APP_NAME], check=False)
process = subprocess.run([PIPX, "upgrade", PROJECT_NAME_LOWER], check=False)
else:
PYTHON_EXECUTABLE = sys.executable
@@ -128,7 +128,7 @@ def update_app(force=False):
"-m",
"pip",
"install",
APP_NAME,
PROJECT_NAME_LOWER,
"-U",
"--no-warn-script-location",
]

View File

@@ -6,6 +6,7 @@ from pathlib import Path
PLATFORM = sys.platform
PROJECT_NAME = "FASTANIME"
PROJECT_NAME_LOWER = "fastanime"
APP_NAME = os.environ.get(f"{PROJECT_NAME}_APP_NAME", PROJECT_NAME.lower())
USER_NAME = os.environ.get("USERNAME", "User")