From ce77be2fb546f5bd67cc438c7dece3b10b8da056 Mon Sep 17 00:00:00 2001
From: Joel Grunbaum <joelgrun@gmail.com>
Date: Tue, 03 Mar 2026 00:09:03 +0000
Subject: [PATCH] Allow rebuilding existing repo packages

---
 src/archrepobuild/resolver.py |   13 ++++--
 src/archrepobuild/cli.py      |   10 ++++-
 tests/test_resolver.py        |   54 +++++++++++++++++++++++++--
 src/archrepobuild/builder.py  |    8 ++-
 4 files changed, 72 insertions(+), 13 deletions(-)

diff --git a/src/archrepobuild/builder.py b/src/archrepobuild/builder.py
index 84077ca..dd96578 100644
--- a/src/archrepobuild/builder.py
+++ b/src/archrepobuild/builder.py
@@ -494,11 +494,12 @@
                 error=str(e),
             )
 
-    async def add_package(self, package: str) -> BuildResult:
+    async def add_package(self, package: str, include_repo: bool = False) -> BuildResult:
         """Add and build (or download) a new package with dependencies.
 
         Args:
             package: Package name
+            include_repo: Whether to check the managed repository for existing packages
 
         Returns:
             BuildResult for the main package
@@ -506,14 +507,15 @@
         logger.info(f"Adding package: {package}")
 
         # Resolve dependencies
-        build_order = await self.resolver.resolve([package])
+        exclude_repo = None if include_repo else self.config.repository.name
+        build_order = await self.resolver.resolve([package], exclude_repo=exclude_repo)
 
         # 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:
+            if include_repo and 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)
diff --git a/src/archrepobuild/cli.py b/src/archrepobuild/cli.py
index ee62f69..1941d00 100644
--- a/src/archrepobuild/cli.py
+++ b/src/archrepobuild/cli.py
@@ -117,8 +117,14 @@
 
 @cli.command()
 @click.argument("packages", nargs=-1, required=True)
+@click.option(
+    "--include-repo",
+    is_flag=True,
+    default=False,
+    help="Check managed repository for existing packages (skip if present)",
+)
 @pass_context
-def add(ctx: Context, packages: tuple[str, ...]) -> None:
+def add(ctx: Context, packages: tuple[str, ...], include_repo: bool) -> None:
     """Add and build new packages from the AUR."""
     config = ctx.config
 
@@ -129,7 +135,7 @@
                 results = []
                 for package in packages:
                     console.print(f"[bold blue]Adding package:[/] {package}")
-                    result = await builder.add_package(package)
+                    result = await builder.add_package(package, include_repo=include_repo)
                     results.append(result)
 
                     if result.status == BuildStatus.SUCCESS:
diff --git a/src/archrepobuild/resolver.py b/src/archrepobuild/resolver.py
index fffbeb1..776eb1e 100644
--- a/src/archrepobuild/resolver.py
+++ b/src/archrepobuild/resolver.py
@@ -325,11 +325,12 @@
 
         return cycles
 
-    async def resolve(self, package_names: list[str]) -> BuildOrder:
+    async def resolve(self, package_names: list[str], exclude_repo: str | None = None) -> BuildOrder:
         """Resolve dependencies and determine build order.
 
         Args:
             package_names: List of packages to resolve
+            exclude_repo: Optional repository name to exclude from existence checks
 
         Returns:
             BuildOrder with packages in correct build order
@@ -340,9 +341,13 @@
         # Filter out packages already in repos or installed
         aur_package_names = []
         for name in package_names:
-            if self.is_in_repos(name):
-                logger.info(f"Package {name} found in repositories, skipping AUR lookup")
-                continue
+            repo = self.is_in_repos(name)
+            if repo:
+                if exclude_repo and repo == exclude_repo:
+                    logger.debug(f"Package {name} found in excluded repo {repo}, treating as not in repos")
+                else:
+                    logger.info(f"Package {name} found in {repo}, skipping AUR lookup")
+                    continue
             if self.is_installed(name):
                 logger.info(f"Package {name} is already installed, skipping AUR lookup")
                 continue
diff --git a/tests/test_resolver.py b/tests/test_resolver.py
index f676bc5..1d1a2af 100644
--- a/tests/test_resolver.py
+++ b/tests/test_resolver.py
@@ -145,6 +145,7 @@
         resolver = DependencyResolver(mock_aur_client)
         
         # Mock AUR response
+        from datetime import datetime
         pkg = Package(
             name="test-pkg",
             version="1.0",
@@ -154,8 +155,9 @@
             votes=0,
             popularity=0.0,
             out_of_date=None,
-            first_submitted=None,
-            last_modified=None,
+            first_submitted=datetime.now(),
+            last_modified=datetime.now(),
+            package_base="test-pkg",
             depends=[],
             makedepends=[],
             checkdepends=["check-dep"],
@@ -170,8 +172,9 @@
             votes=0,
             popularity=0.0,
             out_of_date=None,
-            first_submitted=None,
-            last_modified=None,
+            first_submitted=datetime.now(),
+            last_modified=datetime.now(),
+            package_base="check-dep",
             depends=[],
             makedepends=[],
             checkdepends=[],
@@ -188,3 +191,46 @@
             assert "check-dep" in build_order.packages
             assert "check-dep" in [d.name for d in build_order.aur_dependencies["test-pkg"]]
             assert any(d.dep_type == DependencyType.CHECK for d in build_order.aur_dependencies["test-pkg"])
+
+    @pytest.mark.asyncio
+    async def test_resolve_with_exclude_repo(self, mock_aur_client):
+        """Test that resolve correctly handles exclude_repo."""
+        from archrepobuild.aur import Package
+        
+        resolver = DependencyResolver(mock_aur_client)
+        
+        from datetime import datetime
+        # Mock AUR response
+        pkg = Package(
+            name="test-pkg",
+            version="1.0",
+            description="test",
+            url=None,
+            maintainer=None,
+            votes=0,
+            popularity=0.0,
+            out_of_date=None,
+            first_submitted=datetime.now(),
+            last_modified=datetime.now(),
+            package_base="test-pkg",
+            depends=[],
+            makedepends=[],
+            checkdepends=[],
+        )
+        
+        mock_aur_client.get_packages.return_value = [pkg]
+        
+        # Case 1: Package in repo, not excluded -> should be skipped
+        with patch.object(resolver, "is_in_repos", return_value="myrepo"):
+            build_order = await resolver.resolve(["test-pkg"])
+            assert "test-pkg" not in build_order.packages
+            
+        # Case 2: Package in repo, excluded -> should be included (AUR lookup)
+        with patch.object(resolver, "is_in_repos", return_value="myrepo"):
+            build_order = await resolver.resolve(["test-pkg"], exclude_repo="myrepo")
+            assert "test-pkg" in build_order.packages
+
+        # Case 3: Package in different repo, not excluded -> should be skipped
+        with patch.object(resolver, "is_in_repos", return_value="otherrepo"):
+            build_order = await resolver.resolve(["test-pkg"], exclude_repo="myrepo")
+            assert "test-pkg" not in build_order.packages

--
Gitblit v1.10.0