Skip to content

Commit b2152f2

Browse files
committed
feat: Add self-update functionality to deepctl
- Add version checking module that queries PyPI API for latest version - Add installation detection for pip, pipx, uv, system, and development installs - Create 'deepctl update' command with --check-only, --force, and --yes flags - Store installation metadata in config for future updates - Add UpdateConfig to configuration schema for update preferences - Include comprehensive documentation and usage examples - Add unit tests for update command functionality
1 parent aee2fc9 commit b2152f2

17 files changed

Lines changed: 1643 additions & 0 deletions

File tree

docs/Home.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ Welcome to the deepctl documentation. This wiki contains architectural decisions
1616

1717
- **[MCP Server Command Architecture](MCP%20Server%20Command%20Architecture.md)** - Implementation of the Model Context Protocol server for Deepgram's Gnosis AI service
1818

19+
- **[Self Update Architecture](Self%20Update%20Architecture.md)** - Design and implementation of the self-update feature
20+
21+
- **[Self Update Demo](Self%20Update%20Demo.md)** - How to use the self-update feature with examples
22+
1923
## Security & Authentication
2024

2125
- **[Authentication and Security Architecture](Authentication%20and%20Security%20Architecture.md)** - Authentication methods, credential storage, and security features

docs/Self Update Architecture.md

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
# Self Update Architecture
2+
3+
## Overview
4+
5+
This document outlines the architecture for implementing a self-update feature in deepctl. The feature will:
6+
7+
1. Check for newer versions on PyPI
8+
2. Detect how deepctl was installed
9+
3. Remember installation method
10+
4. Provide appropriate update commands
11+
12+
## Key Components
13+
14+
### 1. Version Checking
15+
16+
#### PyPI API Integration
17+
18+
- Use the PyPI JSON API to check for the latest version
19+
- Compare with current version using semantic versioning
20+
- Cache version check results to avoid excessive API calls
21+
22+
```python
23+
# Example API endpoint
24+
https://pypi.org/pypi/deepctl/json
25+
26+
# Response includes:
27+
- info.version (latest stable version)
28+
- releases (all available versions)
29+
```
30+
31+
#### Version Check Strategy
32+
33+
- Check on startup (configurable frequency)
34+
- Provide explicit `deepctl version --check` command
35+
- Store last check timestamp in config
36+
- Respect user preference to disable checks
37+
38+
### 2. Installation Method Detection
39+
40+
#### Detection Methods
41+
42+
1. **pip/uv Detection**
43+
44+
- Check `sys.prefix` and package metadata
45+
- Look for `.dist-info` directory
46+
- Check if installed in virtual environment
47+
48+
2. **pipx Detection**
49+
50+
- Check for pipx metadata in installation path
51+
- Look for pipx-specific directory structure
52+
- Check environment variable markers
53+
54+
3. **System Package Manager**
55+
56+
- Check common system paths (/usr/bin, /usr/local/bin)
57+
- Look for package manager metadata (dpkg, rpm, etc.)
58+
59+
4. **Development Installation**
60+
- Check for editable installation markers
61+
- Look for git repository in parent directories
62+
63+
#### Installation Metadata Storage
64+
65+
Store in the configuration system:
66+
67+
```yaml
68+
installation:
69+
method: "pip" # pip, pipx, uv, system, development, unknown
70+
path: "/path/to/installation"
71+
version: "0.1.5"
72+
installed_at: "2024-01-20T10:00:00Z"
73+
virtual_env: true
74+
editable: false
75+
```
76+
77+
### 3. Update Commands
78+
79+
#### Update Strategies by Installation Method
80+
81+
1. **pip**
82+
83+
```bash
84+
pip install --upgrade deepctl
85+
```
86+
87+
2. **pipx**
88+
89+
```bash
90+
pipx upgrade deepctl
91+
```
92+
93+
3. **uv**
94+
95+
```bash
96+
uv pip install --upgrade deepctl
97+
```
98+
99+
4. **System Package Manager**
100+
101+
- Display message: "Please use your system package manager to update"
102+
- Provide specific commands for detected OS
103+
104+
5. **Development Installation**
105+
- Display message: "Development installation detected. Please pull latest changes."
106+
107+
### 4. Implementation Plan
108+
109+
#### Phase 1: Core Infrastructure
110+
111+
1. Create version checking module
112+
2. Implement PyPI API client
113+
3. Add version comparison logic
114+
4. Create installation detector
115+
116+
#### Phase 2: Command Implementation
117+
118+
1. Add `deepctl version` command with `--check` flag
119+
2. Add `deepctl update` command
120+
3. Implement update confirmation workflow
121+
122+
#### Phase 3: Configuration & Preferences
123+
124+
1. Add update preferences to config
125+
2. Implement check frequency control
126+
3. Add option to disable auto-checks
127+
128+
#### Phase 4: User Experience
129+
130+
1. Add progress indicators
131+
2. Implement rollback mechanism
132+
3. Add update notifications
133+
134+
## Detailed Design
135+
136+
### Version Checking Module (`deepctl_core/version_check.py`)
137+
138+
```python
139+
from typing import Optional, Dict, Any
140+
import httpx
141+
from packaging import version
142+
from datetime import datetime, timedelta
143+
from pydantic import BaseModel
144+
145+
class VersionInfo(BaseModel):
146+
"""Version information from PyPI."""
147+
latest_version: str
148+
current_version: str
149+
update_available: bool
150+
release_date: Optional[datetime]
151+
release_notes_url: Optional[str]
152+
153+
class VersionChecker:
154+
"""Handles version checking against PyPI."""
155+
156+
PYPI_API_URL = "https://pypi.org/pypi/deepctl/json"
157+
CACHE_DURATION = timedelta(hours=24)
158+
159+
def __init__(self, config: Config):
160+
self.config = config
161+
self.current_version = self._get_current_version()
162+
163+
async def check_version(self, force: bool = False) -> VersionInfo:
164+
"""Check for newer version on PyPI."""
165+
# Implementation details...
166+
167+
def should_check(self) -> bool:
168+
"""Determine if version check should run."""
169+
# Check user preferences and last check time
170+
```
171+
172+
### Installation Detector (`deepctl_core/installation.py`)
173+
174+
```python
175+
import sys
176+
import os
177+
from pathlib import Path
178+
from enum import Enum
179+
from typing import Optional
180+
181+
class InstallMethod(str, Enum):
182+
"""Supported installation methods."""
183+
PIP = "pip"
184+
PIPX = "pipx"
185+
UV = "uv"
186+
SYSTEM = "system"
187+
DEVELOPMENT = "development"
188+
UNKNOWN = "unknown"
189+
190+
class InstallationDetector:
191+
"""Detects how deepctl was installed."""
192+
193+
def detect(self) -> InstallMethod:
194+
"""Detect installation method."""
195+
# Check various markers and paths
196+
197+
def get_update_command(self, method: InstallMethod) -> Optional[str]:
198+
"""Get appropriate update command for installation method."""
199+
# Return command based on method
200+
```
201+
202+
### Update Command (`deepctl_cmd_update/`)
203+
204+
Create a new command package for updates:
205+
206+
```python
207+
# deepctl_cmd_update/command.py
208+
from deepctl_core import BaseCommand
209+
from deepctl_core.version_check import VersionChecker
210+
from deepctl_core.installation import InstallationDetector
211+
212+
class UpdateCommand(BaseCommand):
213+
"""Check for and install updates."""
214+
215+
name = "update"
216+
help = "Check for and install updates"
217+
218+
def add_arguments(self, parser):
219+
parser.add_argument(
220+
"--check-only",
221+
action="store_true",
222+
help="Only check for updates without installing"
223+
)
224+
parser.add_argument(
225+
"--force",
226+
action="store_true",
227+
help="Force update even if up to date"
228+
)
229+
230+
async def run(self, args):
231+
"""Run update command."""
232+
# Check version
233+
# Detect installation method
234+
# Prompt for confirmation
235+
# Execute update
236+
```
237+
238+
### Version Command Enhancement
239+
240+
Enhance existing version command:
241+
242+
```python
243+
@cli.command()
244+
@click.option("--check", is_flag=True, help="Check for updates")
245+
def version(check: bool):
246+
"""Show version information."""
247+
# Show current version
248+
# If --check, also check for updates
249+
```
250+
251+
## Configuration Schema Update
252+
253+
Add to `DeepgramConfig`:
254+
255+
```python
256+
class UpdateConfig(BaseModel):
257+
"""Update preferences."""
258+
check_enabled: bool = True
259+
check_frequency: str = "daily" # daily, weekly, never
260+
last_check: Optional[datetime] = None
261+
installation_method: Optional[str] = None
262+
installation_path: Optional[str] = None
263+
264+
class DeepgramConfig(BaseModel):
265+
# ... existing fields ...
266+
update: UpdateConfig = Field(default_factory=UpdateConfig)
267+
```
268+
269+
## Security Considerations
270+
271+
1. **HTTPS Only**: Always use HTTPS for PyPI API
272+
2. **Signature Verification**: Consider verifying package signatures
273+
3. **Rollback Support**: Keep previous version for rollback
274+
4. **User Confirmation**: Always require user confirmation for updates
275+
5. **Privilege Escalation**: Handle cases where admin rights needed
276+
277+
## User Experience
278+
279+
### Update Flow
280+
281+
1. **Automatic Check** (on startup, respecting frequency):
282+
283+
```
284+
╭─────────────────────────────────────╮
285+
│ Update available: deepctl 0.2.0 │
286+
│ Run 'deepctl update' to upgrade │
287+
╰─────────────────────────────────────╯
288+
```
289+
290+
2. **Manual Check**:
291+
292+
```bash
293+
$ deepctl version --check
294+
Current version: 0.1.5
295+
Latest version: 0.2.0
296+
Update available! Run 'deepctl update' to upgrade.
297+
```
298+
299+
3. **Update Command**:
300+
301+
```bash
302+
$ deepctl update
303+
Current version: 0.1.5
304+
Latest version: 0.2.0
305+
306+
Installation method: pip
307+
Update command: pip install --upgrade deepctl
308+
309+
Do you want to proceed? [y/N]: y
310+
Updating deepctl...
311+
✓ Successfully updated to version 0.2.0
312+
```
313+
314+
### Error Handling
315+
316+
1. **Network Errors**: Graceful fallback, don't block CLI usage
317+
2. **Permission Errors**: Provide clear instructions
318+
3. **Unknown Installation**: Provide manual update instructions
319+
320+
## Testing Strategy
321+
322+
1. **Unit Tests**:
323+
324+
- Version comparison logic
325+
- Installation detection
326+
- API response parsing
327+
328+
2. **Integration Tests**:
329+
330+
- Mock PyPI API responses
331+
- Test various installation scenarios
332+
- Test update workflows
333+
334+
3. **Manual Testing**:
335+
- Test on different platforms
336+
- Test with different installation methods
337+
- Test error scenarios
338+
339+
## Implementation Timeline
340+
341+
1. **Week 1**: Core version checking infrastructure
342+
2. **Week 2**: Installation detection logic
343+
3. **Week 3**: Update command implementation
344+
4. **Week 4**: Testing and documentation
345+
346+
## Future Enhancements
347+
348+
1. **Plugin Updates**: Check and update plugins
349+
2. **Batch Updates**: Update all deepctl packages together
350+
3. **Beta Channel**: Support pre-release versions
351+
4. **Offline Mode**: Work without internet connection
352+
5. **Update History**: Track update history
353+
6. **Automatic Updates**: Optional automatic updates (with user consent)
354+
355+
## Conclusion
356+
357+
The self-update feature is highly feasible and will significantly improve the user experience. By detecting installation methods and providing appropriate update commands, we can support various deployment scenarios while maintaining security and reliability.

0 commit comments

Comments
 (0)