mirror of
https://github.com/esphome/esphome.git
synced 2025-11-10 11:28:52 +00:00
cache github downloads
This commit is contained in:
195
script/github_download_cache.py
Executable file
195
script/github_download_cache.py
Executable file
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GitHub Download Cache CLI
|
||||
|
||||
This script provides a command-line interface to the GitHub download cache.
|
||||
The actual caching logic is in esphome/github_cache.py.
|
||||
|
||||
Usage:
|
||||
python3 script/github_download_cache.py download URL
|
||||
python3 script/github_download_cache.py list
|
||||
python3 script/github_download_cache.py stats
|
||||
python3 script/github_download_cache.py clear
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
# Add parent directory to path to import esphome modules
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from esphome.github_cache import GitHubCache
|
||||
|
||||
|
||||
def download_with_progress(
|
||||
cache: GitHubCache, url: str, force: bool = False, check_updates: bool = True
|
||||
) -> Path:
|
||||
"""Download a URL with progress indicator and caching.
|
||||
|
||||
Args:
|
||||
cache: GitHubCache instance
|
||||
url: URL to download
|
||||
force: Force re-download even if cached
|
||||
check_updates: Check for updates using HTTP 304
|
||||
|
||||
Returns:
|
||||
Path to cached file
|
||||
"""
|
||||
# If force, skip cache check
|
||||
if not force:
|
||||
cached_path = cache.get_cached_path(url, check_updates=check_updates)
|
||||
if cached_path:
|
||||
print(f"Using cached file for {url}")
|
||||
print(f" Cache: {cached_path}")
|
||||
return cached_path
|
||||
|
||||
# Need to download
|
||||
print(f"Downloading {url}")
|
||||
cache_path = cache._get_cache_path(url)
|
||||
print(f" Cache: {cache_path}")
|
||||
|
||||
# Download with progress
|
||||
temp_path = cache_path.with_suffix(cache_path.suffix + ".tmp")
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(url) as response:
|
||||
total_size = int(response.headers.get("Content-Length", 0))
|
||||
downloaded = 0
|
||||
|
||||
with open(temp_path, "wb") as f:
|
||||
while True:
|
||||
chunk = response.read(8192)
|
||||
if not chunk:
|
||||
break
|
||||
f.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
|
||||
if total_size > 0:
|
||||
percent = (downloaded / total_size) * 100
|
||||
print(f"\r Progress: {percent:.1f}%", end="", flush=True)
|
||||
|
||||
print() # New line after progress
|
||||
|
||||
# Move to final location
|
||||
temp_path.replace(cache_path)
|
||||
|
||||
# Let cache handle metadata
|
||||
cache.save_to_cache(url, cache_path)
|
||||
|
||||
return cache_path
|
||||
|
||||
except Exception as e:
|
||||
if temp_path.exists():
|
||||
temp_path.unlink()
|
||||
raise RuntimeError(f"Failed to download {url}: {e}") from e
|
||||
|
||||
|
||||
def main():
|
||||
"""CLI entry point."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="GitHub Download Cache Manager",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Download and cache a URL
|
||||
%(prog)s download https://github.com/pioarduino/registry/releases/download/0.0.1/esptoolpy-v5.1.0.zip
|
||||
|
||||
# List cached files
|
||||
%(prog)s list
|
||||
|
||||
# Show cache statistics
|
||||
%(prog)s stats
|
||||
|
||||
# Clear cache
|
||||
%(prog)s clear
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--cache-dir",
|
||||
type=Path,
|
||||
help="Cache directory (default: ~/.platformio/esphome_download_cache)",
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest="command", help="Command to execute")
|
||||
|
||||
# Download command
|
||||
download_parser = subparsers.add_parser("download", help="Download and cache a URL")
|
||||
download_parser.add_argument("url", help="URL to download")
|
||||
download_parser.add_argument(
|
||||
"--force", action="store_true", help="Force re-download even if cached"
|
||||
)
|
||||
download_parser.add_argument(
|
||||
"--no-check-updates",
|
||||
action="store_true",
|
||||
help="Skip checking for updates (don't use HTTP 304)",
|
||||
)
|
||||
|
||||
# List command
|
||||
subparsers.add_parser("list", help="List cached files")
|
||||
|
||||
# Stats command
|
||||
subparsers.add_parser("stats", help="Show cache statistics")
|
||||
|
||||
# Clear command
|
||||
subparsers.add_parser("clear", help="Clear all cached files")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.command:
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
# Use PlatformIO cache directory by default
|
||||
if args.cache_dir is None:
|
||||
args.cache_dir = Path.home() / ".platformio" / "esphome_download_cache"
|
||||
|
||||
cache = GitHubCache(args.cache_dir)
|
||||
|
||||
if args.command == "download":
|
||||
try:
|
||||
check_updates = not args.no_check_updates
|
||||
cache_path = download_with_progress(
|
||||
cache, args.url, force=args.force, check_updates=check_updates
|
||||
)
|
||||
print(f"\nCached at: {cache_path}")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
elif args.command == "list":
|
||||
cached = cache.list_cached()
|
||||
if not cached:
|
||||
print("No cached files")
|
||||
return 0
|
||||
|
||||
print(f"Cached files ({len(cached)}):")
|
||||
for item in cached:
|
||||
size_mb = item["size"] / (1024 * 1024)
|
||||
print(f" {item['url']}")
|
||||
print(f" Size: {size_mb:.2f} MB")
|
||||
print(f" Path: {item['path']}")
|
||||
return 0
|
||||
|
||||
elif args.command == "stats":
|
||||
total_size = cache.cache_size()
|
||||
cached_count = len(cache.list_cached())
|
||||
size_mb = total_size / (1024 * 1024)
|
||||
|
||||
print(f"Cache directory: {cache.cache_dir}")
|
||||
print(f"Cached files: {cached_count}")
|
||||
print(f"Total size: {size_mb:.2f} MB")
|
||||
return 0
|
||||
|
||||
elif args.command == "clear":
|
||||
cache.clear_cache()
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user