Repository: flyingcircusio/batou

Issue:
Better reporting of invalid revision range when using git-bundle
```
('fatal: Refusing to create empty bundle.\n', 'host00')
('fatal: Refusing to create empty bundle.\n', 'host33')
('fatal: Refusing to create empty bundle.\n', 'host21')
('fatal: Refusing to create empty bundle.\n', 'host03')
('fatal: Refusing to create empty bundle.\n', 'host01')
('fatal: Refusing to create empty bundle.\n', 'host46')
('fatal: Refusing to create empty bundle.\n', 'host47')
('fatal: Invalid revision range 7b7aeb0c5d0ef8480d60469205c39bb4ecc5d2fb..testing\n', 'clxstagc60')
('fatal: Refusing to create empty bundle.\n', 'host61')
ERROR: git fetch batou-bundle
Return code: 128
STDOUT

STDERR
fatal: invalid gitfile format: batou-bundle.git
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

============================================================================================== Waiting for remaining connections ... ==============================================================================================

ERROR: Unexpected exception
	Traceback (most recent call last):
	File ".appenv/80112659/lib/python3.8/site-packages/batou/deploy.py", line 252, in main
    getattr(deployment, step)()
	File ".appenv/80112659/lib/python3.8/site-packages/batou/deploy.py", line 182, in deploy
    [c.join() for c in self.connections]
	File ".appenv/80112659/lib/python3.8/site-packages/batou/deploy.py", line 182, in <listcomp>
    [c.join() for c in self.connections]
	File ".appenv/80112659/lib/python3.8/site-packages/batou/deploy.py", line 50, in join
    raise exc_value.with_traceback(exc_tb)
	File ".appenv/80112659/lib/python3.8/site-packages/batou/deploy.py", line 42, in run
    self.host.start()
	File ".appenv/80112659/lib/python3.8/site-packages/batou/host.py", line 287, in start
    env.repository.update(self)
	File ".appenv/80112659/lib/python3.8/site-packages/batou/repository.py", line 246, in update
    self._ship(host)
	File ".appenv/80112659/lib/python3.8/site-packages/batou/repository.py", line 332, in _ship
    host.rpc.git_unbundle_code()
	File ".appenv/80112659/lib/python3.8/site-packages/batou/host.py", line 99, in call
    raise RuntimeError(
	RuntimeError: host.fcio.net: Remote exception encountered.
================================================================================================ DEPLOYMENT FAILED (during deploy) ================================================================================================
```

Comments:

Patch:
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 9869218e..7263cd44 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -15,5 +15,5 @@ six==1.15.0
 toml==0.10.2
 tox==3.20.1
 virtualenv==20.1.0
-sphinx==4.0.2
+sphinx==4.2.0
 recommonmark==0.7.1
diff --git a/src/batou/repository.py b/src/batou/repository.py
index a8761418..2467cd11 100644
--- a/src/batou/repository.py
+++ b/src/batou/repository.py
@@ -395,14 +395,21 @@ class GitBundleRepository(GitRepository):
             )
         fd, bundle_file = tempfile.mkstemp()
         os.close(fd)
-        out, err = cmd(
-            "git bundle create {file} {range}".format(
-                file=bundle_file, range=bundle_range
-            ),
-            acceptable_returncodes=[0, 128],
-        )
-        if "create empty bundle" in err:
-            return
+        try:
+            out, err = cmd(
+                "git bundle create {file} {range}".format(
+                    file=bundle_file, range=bundle_range
+                ),
+                acceptable_returncodes=[0],
+            )
+        except CmdExecutionError as e:
+            if (
+                e.returncode == 128
+                and "fatal: Refusing to create empty bundle." in e.stderr
+            ):
+                return
+            raise
+
         change_size = os.stat(bundle_file).st_size
         if change_size == 0:
             output.error("Created invalid bundle (0 bytes):")


Test patch:
diff --git a/src/batou/tests/test_remote.py b/src/batou/tests/test_remote.py
index d03317d8..0274826b 100644
--- a/src/batou/tests/test_remote.py
+++ b/src/batou/tests/test_remote.py
@@ -1,10 +1,12 @@
+import os
+
 import mock
 import pytest
 
 from batou.deploy import Deployment
 from batou.environment import Environment
 from batou.host import RemoteHost
-from batou.utils import cmd
+from batou.utils import CmdExecutionError, cmd
 
 
 @pytest.mark.slow
@@ -36,6 +38,30 @@ def test_remote_bundle_breaks_on_missing_head(sample_service):
     )
 
 
+def test_git_remote_bundle_fails_if_needed(tmp_path):
+    env = mock.Mock()
+    env.base_dir = tmp_path
+    env.branch = "master"
+    host = mock.Mock()
+    host.rpc.git_current_head.return_value.decode.return_value = "HEAD"
+    from batou.repository import GitBundleRepository
+
+    os.chdir(tmp_path)
+    cmd("git init")
+    cmd("touch foo")
+    cmd("git add foo")
+    cmd("git commit -m 'initial commit'")
+    repository = GitBundleRepository(env)
+    assert repository._ship(host) is None
+
+    # now destroy the repository state
+    head_commit = cmd("git rev-parse HEAD")[0].strip()
+    cmd(f"rm -rf .git/objects/{head_commit[:2]}/{head_commit[2:]}")
+    with pytest.raises(CmdExecutionError) as e:
+        repository._ship(host)
+    assert "Invalid revision range" in str(e.value)
+
+
 def test_remotehost_start(sample_service):
     env = Environment("test-with-env-config")
     env.load()