@@ -49,58 +49,59 @@ def _validate_plugin_dir_name(plugin_name: str, source_path: Path) -> str:
4949 return plugin_name
5050
5151
52- def get_git_repo (url : str , target_path : Path , proxy : str | None = None ) -> None :
53- """Download code from a Git repository and extract to the specified path"""
52+ def download_repository (
53+ url : str ,
54+ target_path : Path ,
55+ proxy : str | None = None ,
56+ ) -> None :
57+ """Download repository source without requiring a local Git executable.
58+
59+ Args:
60+ url: Supported repository URL.
61+ target_path: Directory that will receive the repository source.
62+ proxy: Optional URL-prefix mirror for the archive request.
63+
64+ Raises:
65+ ValueError: If the repository URL is unsupported or invalid.
66+ httpx.HTTPError: If repository metadata or the archive cannot be downloaded.
67+ """
68+ from astrbot .core .repository import GitHubRepository
69+
5470 temp_dir = Path (tempfile .mkdtemp ())
5571 try :
56- # Parse repository info
57- repo_namespace = url .split ("/" )[- 2 :]
58- author = repo_namespace [0 ]
59- repo = repo_namespace [1 ]
60-
61- # Try to get the latest release
62- release_url = f"https://api.github.com/repos/{ author } /{ repo } /releases"
63- try :
64- with httpx .Client (
65- proxy = proxy if proxy else None ,
66- follow_redirects = True ,
67- ) as client :
68- resp = client .get (release_url )
69- resp .raise_for_status ()
70- releases = resp .json ()
71-
72- if releases :
73- # Use the latest release
74- download_url = releases [0 ]["zipball_url" ]
75- else :
76- # No release found, use default branch
77- click .echo (f"Downloading { author } /{ repo } from default branch" )
78- download_url = f"https://github.com/{ author } /{ repo } /archive/refs/heads/master.zip"
79- except Exception as e :
80- click .echo (f"Failed to get release info: { e } . Using provided URL directly" )
81- download_url = url
72+ repository = GitHubRepository .parse (url )
73+ if not repository .branch :
74+ try :
75+ with httpx .Client (follow_redirects = True , trust_env = True ) as client :
76+ response = client .get (repository .default_branch_api_url )
77+ response .raise_for_status ()
78+ default_branch = str (
79+ response .json ().get ("default_branch" ) or ""
80+ ).strip ()
81+ except httpx .HTTPError as exc :
82+ default_branch = ""
83+ click .echo (
84+ f"Failed to resolve the default GitHub branch: { exc } . Trying main."
85+ )
86+ branch = default_branch or "main"
87+ repository = GitHubRepository (
88+ repository .owner ,
89+ repository .name ,
90+ branch ,
91+ )
8292
83- # Apply proxy
93+ click .echo (
94+ f"Downloading { repository .owner } /{ repository .name } "
95+ f"from GitHub branch { repository .branch } "
96+ )
97+ download_url = repository .archive_url
8498 if proxy :
85- download_url = f"{ proxy } /{ download_url } "
86-
87- # Download and extract
88- with httpx .Client (
89- proxy = proxy if proxy else None ,
90- follow_redirects = True ,
91- ) as client :
92- resp = client .get (download_url )
93- if (
94- resp .status_code == 404
95- and "archive/refs/heads/master.zip" in download_url
96- ):
97- alt_url = download_url .replace ("master.zip" , "main.zip" )
98- click .echo ("Branch 'master' not found, trying 'main' branch" )
99- resp = client .get (alt_url )
100- resp .raise_for_status ()
101- else :
102- resp .raise_for_status ()
103- zip_content = BytesIO (resp .content )
99+ download_url = f"{ proxy .rstrip ('/' )} /{ download_url } "
100+
101+ with httpx .Client (follow_redirects = True , trust_env = True ) as client :
102+ response = client .get (download_url )
103+ response .raise_for_status ()
104+ zip_content = BytesIO (response .content )
104105 with ZipFile (zip_content ) as z :
105106 z .extractall (temp_dir )
106107 namelist = z .namelist ()
@@ -328,7 +329,7 @@ def manage_plugin(
328329 click .echo (
329330 f"{ 'Updating' if is_update else 'Downloading' } plugin { plugin_name } from { repo_url } ..." ,
330331 )
331- get_git_repo (repo_url , target_path , proxy )
332+ download_repository (repo_url , target_path , proxy )
332333
333334 # Update succeeded, delete backup
334335 if is_update and backup_path is not None and backup_path .exists ():
0 commit comments