From e2540e4fa8befd64dda124d17f456ee584f8e02e Mon Sep 17 00:00:00 2001
From: Joel Grunbaum <joelgrun@gmail.com>
Date: Fri, 20 Feb 2026 03:15:52 +0000
Subject: [PATCH] Another attempt at resolution

---
 src/archrepobuild/resolver.py |    8 +-
 src/archrepobuild/cli.py      |    4 
 src/archrepobuild/builder.py  |  123 +++++++++++++++++++++++++++++++++-------
 3 files changed, 106 insertions(+), 29 deletions(-)

diff --git a/src/archrepobuild/builder.py b/src/archrepobuild/builder.py
index dea86f9..4c0a9d0 100644
--- a/src/archrepobuild/builder.py
+++ b/src/archrepobuild/builder.py
@@ -418,8 +418,74 @@
 
         return list(results)
 
+    async def download_package(self, package: str) -> BuildResult:
+        """Download a package from a repository using pacman.
+
+        Args:
+            package: Package name
+
+        Returns:
+            BuildResult with status and artifact path
+        """
+        start_time = datetime.now()
+        logger.info(f"Downloading package from repositories: {package}")
+
+        dest_dir = self.config.repository.build_dir / "downloads"
+        dest_dir.mkdir(parents=True, exist_ok=True)
+
+        try:
+            # Use pacman -Sw to download to a specific directory is not directly possible
+            # But we can use pacman -Sp to get the URL and download it
+            result = subprocess.run(
+                ["pacman", "-Sp", "--noconfirm", package],
+                capture_output=True,
+                text=True,
+                check=True,
+            )
+            
+            urls = [line for line in result.stdout.strip().split("\n") if line.startswith("http") or line.startswith("ftp") or line.startswith("file")]
+            if not urls:
+                raise ValueError(f"Could not find download URL for package: {package}")
+
+            artifacts: list[Path] = []
+            import aiohttp
+            async with aiohttp.ClientSession() as session:
+                for url in urls:
+                    filename = url.split("/")[-1]
+                    dest_path = dest_dir / filename
+                    
+                    logger.debug(f"Downloading {url} to {dest_path}")
+                    async with session.get(url) as response:
+                        response.raise_for_status()
+                        with open(dest_path, "wb") as f:
+                            while True:
+                                chunk = await response.content.read(8192)
+                                if not chunk:
+                                    break
+                                f.write(chunk)
+                    artifacts.append(dest_path)
+
+            duration = (datetime.now() - start_time).total_seconds()
+            logger.info(f"Successfully downloaded {package} in {duration:.1f}s")
+            return BuildResult(
+                package=package,
+                status=BuildStatus.SUCCESS,
+                duration=duration,
+                artifacts=artifacts,
+            )
+
+        except Exception as e:
+            duration = (datetime.now() - start_time).total_seconds()
+            logger.error(f"Failed to download {package}: {e}")
+            return BuildResult(
+                package=package,
+                status=BuildStatus.FAILED,
+                duration=duration,
+                error=str(e),
+            )
+
     async def add_package(self, package: str) -> BuildResult:
-        """Add and build a new package with dependencies.
+        """Add and build (or download) a new package with dependencies.
 
         Args:
             package: Package name
@@ -432,31 +498,42 @@
         # Resolve dependencies
         build_order = await self.resolver.resolve([package])
 
-        if package not in build_order.packages:
-            logger.info(f"Package {package} does not need to be built")
-            return BuildResult(
-                package=package,
-                status=BuildStatus.SKIPPED,
-            )
+        # Filter build order: skip managed repo, download others, build AUR
+        final_results: list[BuildResult] = []
+        for pkg_name in build_order:
+            repo = self.resolver.is_in_repos(pkg_name)
+            
+            if repo == self.config.repository.name:
+                logger.info(f"Package {pkg_name} already in managed repository, skipping")
+                if pkg_name == package:
+                    return BuildResult(package=package, status=BuildStatus.SKIPPED)
+                continue
 
-        # Build dependencies first
-        results: list[BuildResult] = []
-        for dep in build_order:
-            if dep != package:
-                logger.info(f"Building dependency: {dep}")
-                result = await self.build_package(dep, force=True)
-                results.append(result)
+            if repo:
+                logger.info(f"Package {pkg_name} found in {repo}, downloading...")
+                result = await self.download_package(pkg_name)
+            else:
+                logger.info(f"Package {pkg_name} only in AUR, building...")
+                result = await self.build_package(pkg_name, force=True)
 
-                if result.status == BuildStatus.FAILED:
-                    logger.error(f"Dependency {dep} failed, aborting")
-                    return BuildResult(
-                        package=package,
-                        status=BuildStatus.FAILED,
-                        error=f"Dependency {dep} failed to build",
-                    )
+            final_results.append(result)
 
-        # Build main package
-        return await self.build_package(package, force=True)
+            if result.status == BuildStatus.FAILED:
+                logger.error(f"Failed to process {pkg_name}, aborting")
+                if pkg_name == package:
+                    return result
+                return BuildResult(
+                    package=package,
+                    status=BuildStatus.FAILED,
+                    error=f"Dependency {pkg_name} failed: {result.error}",
+                )
+
+        # Return result for the main package
+        for r in final_results:
+            if r.package == package:
+                return r
+        
+        return BuildResult(package=package, status=BuildStatus.SKIPPED)
 
     def remove_package(self, package: str) -> bool:
         """Remove a package from the build directory.
diff --git a/src/archrepobuild/cli.py b/src/archrepobuild/cli.py
index 34de7f3..e7ef98e 100644
--- a/src/archrepobuild/cli.py
+++ b/src/archrepobuild/cli.py
@@ -135,9 +135,9 @@
 
                     if result.status == BuildStatus.SUCCESS:
                         repo.add_packages(result)
-                        console.print(f"[green]✓[/] {package} added successfully")
+                        console.print(f"[green]✓[/] {package} processed successfully")
                     elif result.status == BuildStatus.SKIPPED:
-                        console.print(f"[yellow]⏭[/] {package} skipped (already in official repos or installed)")
+                        console.print(f"[yellow]⏭[/] {package} skipped (already in managed repository)")
                     else:
                         console.print(f"[red]✗[/] {package} failed: {result.error}")
 
diff --git a/src/archrepobuild/resolver.py b/src/archrepobuild/resolver.py
index 1fc9b2f..027e550 100644
--- a/src/archrepobuild/resolver.py
+++ b/src/archrepobuild/resolver.py
@@ -105,7 +105,7 @@
             logger.warning(f"Failed to get pacman package list: {e}")
             self._pacman_cache = {}
 
-    def is_in_repos(self, name: str, include_all: bool = True) -> bool:
+    def is_in_repos(self, name: str, include_all: bool = True) -> str | None:
         """Check if package is available in repositories.
 
         Args:
@@ -113,7 +113,7 @@
             include_all: If True, check all enabled repos. If False, only official ones.
 
         Returns:
-            True if available in repos
+            Name of repository where package was found, or None if not found
         """
         if not self._pacman_checked:
             self._refresh_pacman_cache()
@@ -125,8 +125,8 @@
             if not include_all and repo not in OFFICIAL_REPOS:
                 continue
             if base_name in pkgs:
-                return True
-        return False
+                return repo
+        return None
 
     def is_installed(self, name: str) -> bool:
         """Check if package is already installed.

--
Gitblit v1.10.0