Skip to content

Commit 0bce7cc

Browse files
committed
cygwin, #533: FIX daemon launching
+ Rework git-daemon launching with `with` resource-management. + cmd: add `is_cygwin` optional override kwd on `Git.polish_url()`. - Cygwin TCs failing: - PY2: err: 13, fail: 3 - PY3: err: 12, fail: 3
1 parent e6e23ed commit 0bce7cc

File tree

2 files changed

+122
-112
lines changed

2 files changed

+122
-112
lines changed

git/cmd.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,19 @@ def is_cygwin(cls):
196196
return is_cygwin_git(cls.GIT_PYTHON_GIT_EXECUTABLE)
197197

198198
@classmethod
199-
def polish_url(cls, url):
200-
if cls.is_cygwin():
199+
def polish_url(cls, url, is_cygwin=None):
200+
if is_cygwin is None:
201+
is_cygwin = cls.is_cygwin()
202+
203+
if is_cygwin:
204+
url = cygpath(url)
205+
else:
201206
"""Remove any backslahes from urls to be written in config files.
202207
203208
Windows might create config-files containing paths with backslashed,
204209
but git stops liking them as it will escape the backslashes.
205210
Hence we undo the escaping just to be sure.
206211
"""
207-
url = cygpath(url)
208-
else:
209212
url = url.replace("\\\\", "\\").replace("\\", "/")
210213

211214
return url

git/test/lib/helper.py

Lines changed: 115 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
66
from __future__ import print_function
77

8+
import contextlib
89
from functools import wraps
910
import io
1011
import logging
@@ -16,7 +17,7 @@
1617
import unittest
1718

1819
from git.compat import string_types, is_win, PY3
19-
from git.util import rmtree
20+
from git.util import rmtree, cwd
2021

2122
import os.path as osp
2223

@@ -151,32 +152,67 @@ def repo_creator(self):
151152
return argument_passer
152153

153154

154-
def launch_git_daemon(base_path, ip, port):
155-
from git import Git
156-
if is_win:
157-
## On MINGW-git, daemon exists in .\Git\mingw64\libexec\git-core\,
158-
# but if invoked as 'git daemon', it detaches from parent `git` cmd,
159-
# and then CANNOT DIE!
160-
# So, invoke it as a single command.
161-
## Cygwin-git has no daemon. But it can use MINGW's.
162-
#
163-
daemon_cmd = ['git-daemon',
164-
'--enable=receive-pack',
165-
'--listen=%s' % ip,
166-
'--port=%s' % port,
167-
'--base-path=%s' % base_path,
168-
base_path]
169-
gd = Git().execute(daemon_cmd, as_process=True)
170-
else:
171-
gd = Git().daemon(base_path,
172-
enable='receive-pack',
173-
listen=ip,
174-
port=port,
175-
base_path=base_path,
176-
as_process=True)
177-
# yes, I know ... fortunately, this is always going to work if sleep time is just large enough
178-
time.sleep(0.5)
179-
return gd
155+
@contextlib.contextmanager
156+
def git_daemon_launched(base_path, ip, port):
157+
from git import Git # Avoid circular deps.
158+
159+
gd = None
160+
try:
161+
if is_win:
162+
## On MINGW-git, daemon exists in .\Git\mingw64\libexec\git-core\,
163+
# but if invoked as 'git daemon', it detaches from parent `git` cmd,
164+
# and then CANNOT DIE!
165+
# So, invoke it as a single command.
166+
## Cygwin-git has no daemon. But it can use MINGW's.
167+
#
168+
daemon_cmd = ['git-daemon',
169+
'--enable=receive-pack',
170+
'--listen=%s' % ip,
171+
'--port=%s' % port,
172+
'--base-path=%s' % base_path,
173+
base_path]
174+
gd = Git().execute(daemon_cmd, as_process=True)
175+
else:
176+
gd = Git().daemon(base_path,
177+
enable='receive-pack',
178+
listen=ip,
179+
port=port,
180+
base_path=base_path,
181+
as_process=True)
182+
# yes, I know ... fortunately, this is always going to work if sleep time is just large enough
183+
time.sleep(0.5 * (1 + is_win))
184+
185+
yield
186+
187+
except Exception as ex:
188+
msg = textwrap.dedent("""
189+
Launching git-daemon failed due to: %s
190+
Probably test will fail subsequently.
191+
192+
BUT you may start *git-daemon* manually with this command:"
193+
git daemon --enable=receive-pack --listen=%s --port=%s --base-path=%s %s
194+
You may also run the daemon on a different port by passing --port=<port>"
195+
and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
196+
""")
197+
if is_win:
198+
msg += textwrap.dedent("""
199+
200+
On Windows,
201+
the `git-daemon.exe` must be in PATH.
202+
For MINGW, look into .\Git\mingw64\libexec\git-core\), but problems with paths might appear.
203+
CYGWIN has no daemon, but if one exists, it gets along fine (but has also paths problems).""")
204+
log.warning(msg, ex, ip, port, base_path, base_path, exc_info=1)
205+
206+
yield
207+
208+
finally:
209+
if gd:
210+
try:
211+
log.debug("Killing git-daemon...")
212+
gd.proc.kill()
213+
except Exception as ex:
214+
## Either it has died (and we're here), or it won't die, again here...
215+
log.debug("Hidden error while Killing git-daemon: %s", ex, exc_info=1)
180216

181217

182218
def with_rw_and_rw_remote_repo(working_tree_ref):
@@ -193,10 +229,10 @@ def with_rw_and_rw_remote_repo(working_tree_ref):
193229
directories in it.
194230
195231
The following scetch demonstrates this::
196-
rorepo ---<bare clone>---> rw_remote_repo ---<clone>---> rw_repo
232+
rorepo ---<bare clone>---> rw_daemon_repo ---<clone>---> rw_repo
197233
198234
The test case needs to support the following signature::
199-
def case(self, rw_repo, rw_remote_repo)
235+
def case(self, rw_repo, rw_daemon_repo)
200236
201237
This setup allows you to test push and pull scenarios and hooks nicely.
202238
@@ -211,94 +247,65 @@ def argument_passer(func):
211247

212248
@wraps(func)
213249
def remote_repo_creator(self):
214-
remote_repo_dir = tempfile.mktemp("remote_repo_%s" % func.__name__)
215-
repo_dir = tempfile.mktemp("remote_clone_non_bare_repo")
250+
rw_daemon_repo_dir = tempfile.mktemp(prefix="daemon_repo-%s-" % func.__name__)
251+
rw_repo_dir = tempfile.mktemp("daemon_cloned_repo-%s-" % func.__name__)
216252

217-
rw_remote_repo = self.rorepo.clone(remote_repo_dir, shared=True, bare=True)
253+
rw_daemon_repo = self.rorepo.clone(rw_daemon_repo_dir, shared=True, bare=True)
218254
# recursive alternates info ?
219-
rw_repo = rw_remote_repo.clone(repo_dir, shared=True, bare=False, n=True)
220-
rw_repo.head.commit = working_tree_ref
221-
rw_repo.head.reference.checkout()
222-
223-
# prepare for git-daemon
224-
rw_remote_repo.daemon_export = True
225-
226-
# this thing is just annoying !
227-
with rw_remote_repo.config_writer() as crw:
228-
section = "daemon"
229-
try:
230-
crw.add_section(section)
231-
except Exception:
232-
pass
233-
crw.set(section, "receivepack", True)
234-
235-
# Initialize the remote - first do it as local remote and pull, then
236-
# we change the url to point to the daemon.
237-
d_remote = Remote.create(rw_repo, "daemon_origin", remote_repo_dir)
238-
d_remote.fetch()
239-
240-
base_path, rel_repo_dir = osp.split(remote_repo_dir)
241-
242-
remote_repo_url = Git.polish_url("git://localhost:%s/%s" % (GIT_DAEMON_PORT, rel_repo_dir))
243-
with d_remote.config_writer as cw:
244-
cw.set('url', remote_repo_url)
245-
255+
rw_repo = rw_daemon_repo.clone(rw_repo_dir, shared=True, bare=False, n=True)
246256
try:
247-
gd = launch_git_daemon(Git.polish_url(base_path), '127.0.0.1', GIT_DAEMON_PORT)
248-
except Exception as ex:
249-
if is_win:
250-
msg = textwrap.dedent("""
251-
The `git-daemon.exe` must be in PATH.
252-
For MINGW, look into .\Git\mingw64\libexec\git-core\), but problems with paths might appear.
253-
CYGWIN has no daemon, but if one exists, it gets along fine (has also paths problems)
254-
Anyhow, alternatively try starting `git-daemon` manually:""")
255-
else:
256-
msg = "Please try starting `git-daemon` manually:"
257-
msg += textwrap.dedent("""
258-
git daemon --enable=receive-pack --base-path=%s %s
259-
You can also run the daemon on a different port by passing --port=<port>"
260-
and setting the environment variable GIT_PYTHON_TEST_GIT_DAEMON_PORT to <port>
261-
""" % (base_path, base_path))
262-
raise AssertionError(ex, msg)
263-
# END make assertion
264-
else:
265-
# Try listing remotes, to diagnose whether the daemon is up.
266-
rw_repo.git.ls_remote(d_remote)
267-
268-
# adjust working dir
269-
prev_cwd = os.getcwd()
270-
os.chdir(rw_repo.working_dir)
257+
rw_repo.head.commit = working_tree_ref
258+
rw_repo.head.reference.checkout()
271259

272-
try:
273-
return func(self, rw_repo, rw_remote_repo)
274-
except:
275-
log.info("Keeping repos after failure: repo_dir = %s, remote_repo_dir = %s",
276-
repo_dir, remote_repo_dir)
277-
repo_dir = remote_repo_dir = None
278-
raise
279-
finally:
280-
os.chdir(prev_cwd)
260+
# prepare for git-daemon
261+
rw_daemon_repo.daemon_export = True
262+
263+
# this thing is just annoying !
264+
with rw_daemon_repo.config_writer() as crw:
265+
section = "daemon"
266+
try:
267+
crw.add_section(section)
268+
except Exception:
269+
pass
270+
crw.set(section, "receivepack", True)
271+
272+
# Initialize the remote - first do it as local remote and pull, then
273+
# we change the url to point to the daemon.
274+
d_remote = Remote.create(rw_repo, "daemon_origin", rw_daemon_repo_dir)
275+
d_remote.fetch()
276+
277+
base_daemon_path, rel_repo_dir = osp.split(rw_daemon_repo_dir)
278+
279+
remote_repo_url = Git.polish_url("git://localhost:%s/%s" % (GIT_DAEMON_PORT, rel_repo_dir))
280+
with d_remote.config_writer as cw:
281+
cw.set('url', remote_repo_url)
282+
283+
with git_daemon_launched(Git.polish_url(base_daemon_path, is_cygwin=False), # No daemon in Cygwin.
284+
'127.0.0.1',
285+
GIT_DAEMON_PORT):
286+
# Try listing remotes, to diagnose whether the daemon is up.
287+
rw_repo.git.ls_remote(d_remote)
288+
289+
with cwd(rw_repo.working_dir):
290+
try:
291+
return func(self, rw_repo, rw_daemon_repo)
292+
except:
293+
log.info("Keeping repos after failure: \n rw_repo_dir: %s \n rw_daemon_repo_dir: %s",
294+
rw_repo_dir, rw_daemon_repo_dir)
295+
rw_repo_dir = rw_daemon_repo_dir = None
296+
raise
281297

282298
finally:
283-
try:
284-
log.debug("Killing git-daemon...")
285-
gd.proc.kill()
286-
except:
287-
## Either it has died (and we're here), or it won't die, again here...
288-
pass
289-
290299
rw_repo.git.clear_cache()
291-
rw_remote_repo.git.clear_cache()
292-
rw_repo = rw_remote_repo = None
300+
rw_daemon_repo.git.clear_cache()
301+
del rw_repo
302+
del rw_daemon_repo
293303
import gc
294304
gc.collect()
295-
if repo_dir:
296-
rmtree(repo_dir)
297-
if remote_repo_dir:
298-
rmtree(remote_repo_dir)
299-
300-
if gd is not None:
301-
gd.proc.wait()
305+
if rw_repo_dir:
306+
rmtree(rw_repo_dir)
307+
if rw_daemon_repo_dir:
308+
rmtree(rw_daemon_repo_dir)
302309
# END cleanup
303310
# END bare repo creator
304311
return remote_repo_creator

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy