Skip to content

Commit 5fa8781

Browse files
committed
Fix update service to better detect installation method and refactor update command to be a group of commands
1 parent 508a5d5 commit 5fa8781

9 files changed

Lines changed: 1363 additions & 125 deletions

File tree

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import typer
1+
"""Update command module."""
22

3-
from .command import update
3+
from .check import check_update_command
4+
from .command import update_app
5+
from .info import info_command
6+
from .perform import perform_update_command
47

5-
update_app = typer.Typer(
6-
name="update",
7-
help="Update KiLM itself to the latest version",
8-
rich_markup_mode="rich",
9-
callback=update,
10-
invoke_without_command=True,
11-
)
12-
13-
__all__ = ["update", "update_app"]
8+
__all__ = [
9+
"update_app",
10+
"check_update_command",
11+
"info_command",
12+
"perform_update_command",
13+
]
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Update check command implementation."""
2+
3+
import importlib.metadata
4+
from typing import Annotated
5+
6+
import typer
7+
from rich.console import Console
8+
9+
from ...services.update_service import UpdateService
10+
11+
console = Console()
12+
13+
14+
def check_update_command(
15+
force: Annotated[
16+
bool, typer.Option(help="Force fresh check, bypass cache")
17+
] = False,
18+
quiet: Annotated[
19+
bool,
20+
typer.Option("--quiet", "-q", help="Only show output if update is available"),
21+
] = False,
22+
) -> None:
23+
"""Check for available updates without installing."""
24+
25+
version = importlib.metadata.version("kilm")
26+
update_service = UpdateService(version)
27+
28+
# Use force flag to bypass cache
29+
update_info = update_service.check_for_updates(use_cache=not force)
30+
31+
if update_info["has_update"]:
32+
current = update_info["current_version"]
33+
latest = update_info["latest_version"]
34+
method = update_info["method"]
35+
can_auto_update = update_info["supports_auto_update"]
36+
37+
console.print("[yellow]Update available![/yellow]")
38+
console.print(f"Current version: [blue]{current}[/blue]")
39+
console.print(f"Latest version: [green]{latest}[/green]")
40+
console.print(f"Installation method: [cyan]{method}[/cyan]")
41+
42+
if can_auto_update:
43+
console.print("\nRun [bold cyan]kilm update perform[/bold cyan] to update")
44+
else:
45+
update_cmd = update_service.get_update_instructions()
46+
console.print(f"\nManual update required: [cyan]{update_cmd}[/cyan]")
47+
48+
# Exit with code 1 to indicate update available
49+
raise typer.Exit(1)
50+
else:
51+
if not quiet:
52+
console.print(
53+
f"[green]✓[/green] You are using the latest version ([bold cyan]v{update_info['current_version']}[/bold cyan])"
54+
)
55+
# Exit with code 0 for no update needed
56+
raise typer.Exit(0)
Lines changed: 13 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,20 @@
11
"""
2-
Update command implementation for KiCad Library Manager (Typer version).
3-
Updates KiLM itself to the latest version.
2+
Update command group for KiCad Library Manager.
3+
Contains subcommands for checking, showing info, and performing updates.
44
"""
55

6-
import importlib.metadata
7-
from typing import Annotated
8-
96
import typer
10-
from rich.console import Console
11-
from rich.panel import Panel
12-
13-
from ...services.update_service import UpdateManager
14-
15-
console = Console()
16-
17-
18-
def update(
19-
check: Annotated[
20-
bool, typer.Option(help="Check for updates without installing")
21-
] = False,
22-
force: Annotated[
23-
bool, typer.Option(help="Force update even if already up to date")
24-
] = False,
25-
) -> None:
26-
"""Update KiLM to the latest version.
27-
28-
This command updates KiLM itself by downloading and installing the latest
29-
version from PyPI. The update method depends on how KiLM was installed
30-
(pip, pipx, conda, etc.).
31-
32-
⚠️ DEPRECATION NOTICE:
33-
In KiLM 0.4.0, the 'update' command now updates KiLM itself.
34-
To update library content, use 'kilm sync' instead.
35-
This banner will be removed in a future version.
36-
37-
Use --check to see if updates are available without installing.
38-
"""
39-
40-
deprecation_notice = (
41-
"[bold yellow]⚠️ BREAKING CHANGE NOTICE (KiLM 0.4.0)[/bold yellow]\n\n"
42-
"The [bold]kilm update[/bold] command now updates KiLM itself.\n"
43-
"To update library content, use [bold cyan]kilm sync[/bold cyan] instead.\n"
44-
"This notice will be removed in a future version."
45-
)
46-
console.print(Panel(deprecation_notice, expand=False, border_style="yellow"))
47-
48-
version = importlib.metadata.version("kilm")
49-
50-
update_manager = UpdateManager(version)
51-
52-
console.print(
53-
f"[blue]Current KiLM version:[/blue] [bold cyan]v{version}[/bold cyan]"
54-
)
55-
console.print(
56-
f"[blue]Installation method:[/blue] {update_manager.installation_method}"
57-
)
58-
console.print("\n[bold cyan]Checking for updates...[/bold cyan]")
59-
60-
latest_version = update_manager.check_latest_version()
61-
62-
if latest_version is None:
63-
console.print("[red]Could not check for updates. Please try again later.[/red]")
64-
return
65-
66-
if not update_manager.is_newer_version_available(latest_version):
67-
if not force:
68-
console.print(
69-
f"[green]KiLM is up to date[/green] [bold green]v{version}[/bold green]"
70-
)
71-
return
72-
else:
73-
console.print(
74-
f"[yellow]Forcing update to v{latest_version}[/yellow] (current: v{version})"
75-
)
76-
else:
77-
console.print(
78-
f"[green]New version available:[/green] [bold green]v{latest_version}[/bold green]"
79-
)
807

81-
if check:
82-
if update_manager.is_newer_version_available(latest_version):
83-
console.print(
84-
f"\n[green]Update available:[/green] [bold green]v{latest_version}[/bold green]"
85-
)
86-
console.print(
87-
f"[blue]To update, run:[/blue] [cyan]{update_manager.get_update_instruction()}[/cyan]"
88-
)
89-
else:
90-
console.print("[green]No updates available[/green]")
91-
return
8+
from .check import check_update_command
9+
from .info import info_command
10+
from .perform import perform_update_command
9211

93-
# Perform the update
94-
if update_manager.can_auto_update():
95-
console.print(
96-
f"\n[bold cyan]Updating KiLM to version {latest_version}...[/bold cyan]"
97-
)
98-
success, message = update_manager.perform_update()
12+
# Create the update command group
13+
update_app = typer.Typer(
14+
name="update", help="Update KiLM CLI to the latest version", no_args_is_help=True
15+
)
9916

100-
if success:
101-
console.print(f"[bold green]✅ {message}[/bold green]")
102-
console.print(
103-
f"[green]KiLM has been updated to version {latest_version}[/green]"
104-
)
105-
else:
106-
console.print(f"[bold red]❌ {message}[/bold red]")
107-
else:
108-
instruction = update_manager.get_update_instruction()
109-
console.print(
110-
f"\n[yellow]Manual update required for {update_manager.installation_method} installation.[/yellow]"
111-
)
112-
console.print(f"[blue]Please run:[/blue] [cyan]{instruction}[/cyan]")
17+
# Register subcommands
18+
update_app.command("check")(check_update_command)
19+
update_app.command("info")(info_command)
20+
update_app.command("perform")(perform_update_command)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Update info command implementation."""
2+
3+
import importlib.metadata
4+
from typing import Annotated
5+
6+
import typer
7+
from rich.console import Console
8+
from rich.panel import Panel
9+
10+
from ...services.update_service import UpdateService
11+
12+
console = Console()
13+
14+
15+
def info_command(
16+
force_check: Annotated[
17+
bool, typer.Option(help="Force fresh version check, bypass cache")
18+
] = False,
19+
) -> None:
20+
"""Show detailed installation and update information."""
21+
22+
version = importlib.metadata.version("kilm")
23+
update_service = UpdateService(version)
24+
25+
# Get installation and update information
26+
update_info = update_service.check_for_updates(use_cache=not force_check)
27+
method = update_service.get_installation_method()
28+
can_auto_update = update_service.can_auto_update()
29+
update_cmd = update_service.get_update_instructions()
30+
31+
# Current version status
32+
version_status = f"[green]{update_info['current_version']}[/green]"
33+
if update_info["has_update"]:
34+
version_status += (
35+
f" → [yellow]{update_info['latest_version']} available[/yellow]"
36+
)
37+
else:
38+
version_status += " [dim](latest)[/dim]"
39+
40+
# Installation details
41+
install_details = [
42+
f"Method: [cyan]{method}[/cyan]",
43+
f"Auto-update: [{'green' if can_auto_update else 'red'}]{can_auto_update}[/]",
44+
f"Update command: [cyan]{update_cmd}[/cyan]",
45+
]
46+
47+
# Display information
48+
console.print(
49+
Panel(
50+
f"""[bold]Version Information[/bold]
51+
Current version: {version_status}
52+
53+
[bold]Installation Details[/bold]
54+
{chr(10).join(install_details)}
55+
56+
[bold]Available Commands[/bold]
57+
[cyan]kilm update check[/cyan] - Check for updates
58+
[cyan]kilm update perform[/cyan] - Install updates
59+
[cyan]kilm update info[/cyan] - Show this information""",
60+
title="[bold blue]KiLM Installation Info[/bold blue]",
61+
border_style="blue",
62+
)
63+
)
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"""Update perform command implementation."""
2+
3+
import importlib.metadata
4+
from typing import Annotated, Optional
5+
6+
import typer
7+
from rich.console import Console
8+
from rich.panel import Panel
9+
10+
from ...services.update_service import UpdateService
11+
12+
console = Console()
13+
14+
15+
def perform_update_command(
16+
target_version: Annotated[
17+
Optional[str],
18+
typer.Option(
19+
"--target-version",
20+
"-t",
21+
help="Specific version to install (default: latest)",
22+
),
23+
] = None,
24+
force: Annotated[
25+
bool,
26+
typer.Option("--force", "-f", help="Force update even if already up to date"),
27+
] = False,
28+
dry_run: Annotated[
29+
bool,
30+
typer.Option("--dry-run", help="Show what would be updated without doing it"),
31+
] = False,
32+
no_cache: Annotated[
33+
bool,
34+
typer.Option("--no-cache", help="Skip cache and force fresh version check"),
35+
] = False,
36+
) -> None:
37+
"""Perform KiLM update installation.
38+
39+
This command updates KiLM itself by downloading and installing the latest
40+
version from PyPI. The update method depends on how KiLM was installed
41+
(pip, pipx, uv tool, etc.).
42+
43+
⚠️ DEPRECATION NOTICE:
44+
In KiLM 0.4.0, the 'update' command now updates KiLM itself.
45+
To update library content, use 'kilm sync' instead.
46+
This banner will be removed in a future version.
47+
"""
48+
49+
# Show deprecation notice
50+
deprecation_notice = (
51+
"[bold yellow]⚠️ BREAKING CHANGE NOTICE (KiLM 0.4.0)[/bold yellow]\n\n"
52+
"The [bold]kilm update[/bold] command now updates KiLM itself.\n"
53+
"To update library content, use [bold cyan]kilm sync[/bold cyan] instead.\n"
54+
"This notice will be removed in a future version."
55+
)
56+
console.print(Panel(deprecation_notice, expand=False, border_style="yellow"))
57+
58+
version = importlib.metadata.version("kilm")
59+
update_service = UpdateService(version)
60+
61+
if dry_run:
62+
console.print("[yellow]Dry run mode - showing what would be done[/yellow]")
63+
console.print()
64+
65+
# Check if we can auto-update (skip during dry run)
66+
if not dry_run and not update_service.can_auto_update():
67+
console.print(
68+
"[yellow]⚠[/yellow] Automatic update not supported for your installation method."
69+
)
70+
method = update_service.get_installation_method()
71+
update_cmd = update_service.get_update_instructions()
72+
73+
console.print(f"Installation method: [bold]{method}[/bold]")
74+
console.print(f"Please run manually: [bold cyan]{update_cmd}[/bold cyan]")
75+
raise typer.Exit(1)
76+
77+
# Perform update check first (unless forced)
78+
if not force and not dry_run:
79+
update_info = update_service.check_for_updates(use_cache=not no_cache)
80+
if not update_info["has_update"]:
81+
console.print(
82+
f"[green]✓[/green] Already using the latest version ([bold cyan]v{update_info['current_version']}[/bold cyan])"
83+
)
84+
console.print("\nUse [cyan]--force[/cyan] to reinstall the current version")
85+
raise typer.Exit(0)
86+
87+
current = update_info["current_version"]
88+
latest = update_info["latest_version"]
89+
target = target_version or latest
90+
91+
if not dry_run:
92+
console.print(
93+
f"Updating from [blue]{current}[/blue] to [green]{target}[/green]"
94+
)
95+
console.print()
96+
97+
# Perform the update
98+
success, message = update_service.perform_update(
99+
target_version=target_version, force=force, dry_run=dry_run
100+
)
101+
102+
if dry_run:
103+
if success:
104+
console.print(
105+
"\n[green]✓[/green] Dry run completed - automatic update is supported"
106+
)
107+
else:
108+
console.print("\n[red]✗[/red] Automatic update not available")
109+
console.print("Manual update will be required")
110+
raise typer.Exit(0)
111+
112+
if success:
113+
console.print("\n[green]✓[/green] Update completed successfully!")
114+
console.print(
115+
"You may need to restart your shell or run [cyan]hash -r[/cyan] to use the new version"
116+
)
117+
else:
118+
console.print(f"\n[red]✗[/red] Update failed: {message}")
119+
console.print("Please try updating manually or check the error messages above")
120+
raise typer.Exit(1)

0 commit comments

Comments
 (0)