← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/turnip:dynamic-git-capability-advertisement into turnip:master

 

Colin Watson has proposed merging ~cjwatson/turnip:dynamic-git-capability-advertisement into turnip:master.

Commit message:
Dynamically generate git v2 capability advertisement

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/turnip/+git/turnip/+merge/417597

`turnip.pack.helpers.get_capabilities_advertisement` hardcoded our protocol 2 capability advertisement quite tightly, which caused tests to fail when upgrading to a newer version of git.  Generate this dynamically instead based on git's own advertisement.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/turnip:dynamic-git-capability-advertisement into turnip:master.
diff --git a/turnip/pack/helpers.py b/turnip/pack/helpers.py
index 9cfcfcf..7ab61e4 100644
--- a/turnip/pack/helpers.py
+++ b/turnip/pack/helpers.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2022 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 from __future__ import (
@@ -9,13 +9,16 @@ from __future__ import (
 
 from collections import OrderedDict
 import enum
+from functools import lru_cache
 import hashlib
 import os.path
 import re
+import shutil
 import stat
 import subprocess
 import sys
 from tempfile import (
+    mkdtemp,
     mktemp,
     NamedTemporaryFile,
     )
@@ -248,6 +251,28 @@ def translate_xmlrpc_fault(code):
     return result
 
 
+@lru_cache(maxsize=1)
+def _get_git_v2_capabilities_advertisement():
+    """Returns git's own v2 capability advertisement, parsed into packets."""
+    git_dir = mkdtemp(prefix="git-advertisement-")
+    try:
+        subprocess.check_call(
+            ["git", "init", git_dir], stdout=subprocess.DEVNULL)
+        env = dict(os.environ)
+        env["GIT_PROTOCOL"] = "version=2"
+        advertisement = subprocess.check_output(
+            ["git", "upload-pack", git_dir], input=b"", env=env)
+    finally:
+        shutil.rmtree(git_dir, ignore_errors=True)
+    packets = []
+    while advertisement:
+        packet, advertisement = decode_packet(advertisement)
+        if packet is None:
+            break
+        packets.append(packet)
+    return packets
+
+
 def get_capabilities_advertisement(version=b'1'):
     """Returns the capability advertisement binary string to be sent to
     clients for a given protocol version requested.
@@ -257,11 +282,10 @@ def get_capabilities_advertisement(version=b'1'):
     if version != b'2':
         return b""
     turnip_version = six.ensure_binary(version_info.get("revision_id", '-1'))
-    return (
-        encode_packet(b"version 2\n") +
-        encode_packet(b"agent=git/2.25.1@turnip/%s\n" % turnip_version) +
-        encode_packet(b"ls-refs\n") +
-        encode_packet(b"fetch=shallow\n") +
-        encode_packet(b"server-option\n") +
-        b'0000'
-    )
+    advertisement = bytearray()
+    for packet in _get_git_v2_capabilities_advertisement():
+        if packet.startswith(b"agent="):
+            packet = b"%s@turnip/%s\n" % (packet.rstrip(b"\n"), turnip_version)
+        advertisement += encode_packet(packet)
+    advertisement += b"0000"
+    return bytes(advertisement)
diff --git a/turnip/pack/tests/test_helpers.py b/turnip/pack/tests/test_helpers.py
index 815497f..d652bf8 100644
--- a/turnip/pack/tests/test_helpers.py
+++ b/turnip/pack/tests/test_helpers.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2022 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 from __future__ import (
@@ -347,7 +347,8 @@ class TestCapabilityAdvertisement(TestCase):
 
         turnip_capabilities = get_capabilities_advertisement(version=b'2')
         version = six.ensure_binary(version_info["revision_id"])
-        turnip_agent = encode_packet(b"agent=git/2.25.1@turnip/%s\n" % version)
+        turnip_agent = encode_packet(
+            b"agent=git/%s@turnip/%s\n" % (git_version_num, version))
 
         self.assertEqual(
             turnip_capabilities,
diff --git a/turnip/pack/tests/test_http.py b/turnip/pack/tests/test_http.py
index b1158d1..2b01f62 100644
--- a/turnip/pack/tests/test_http.py
+++ b/turnip/pack/tests/test_http.py
@@ -1,4 +1,4 @@
-# Copyright 2015 Canonical Ltd.  This software is licensed under the
+# Copyright 2015-2022 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 from __future__ import (
@@ -15,7 +15,6 @@ from unittest import mock
 from fixtures import TempDir
 from openid.consumer import consumer
 from paste.auth.cookie import encode as encode_cookie
-import six
 from testtools import TestCase
 from testtools.deferredruntest import AsynchronousDeferredRunTest
 from twisted.internet import (
@@ -34,13 +33,12 @@ from turnip.pack import (
     helpers,
     http,
     )
-from turnip.pack.helpers import encode_packet
+from turnip.pack.helpers import get_capabilities_advertisement
 from turnip.pack.http import (
     get_protocol_version_from_request,
     HTTPAuthLoginResource,
     )
 from turnip.pack.tests.fake_servers import FakeVirtInfoService
-from turnip.version_info import version_info
 
 
 class LessDummyRequest(requesthelper.DummyRequest):
@@ -261,15 +259,9 @@ class TestSmartHTTPRefsResource(ErrorTestMixin, TestCase):
             b'turnip-request-id': mock.ANY,
             b'turnip-stateless-rpc': b'yes'})
 
-        ver = six.ensure_binary(version_info["revision_id"])
-        capabilities = (
-            encode_packet(b'version 2\n') +
-            encode_packet(b'agent=git/2.25.1@turnip/%s\n' % ver) +
-            encode_packet(b'ls-refs\n') +
-            encode_packet(b'fetch=shallow\n') +
-            encode_packet(b'server-option\n') +
-            b'0000'
-            )
+        # Details tested separately; see
+        # turnip.pack.tests.test_helpers.TestCapabilityAdvertisement.
+        capabilities = get_capabilities_advertisement(version=b"2")
         self.assertEqual(
             capabilities +
             b'001e# service=git-upload-pack\n'