2013-12-09 46 views
1

我知道有cgi脚本可以为多个mercurial仓库提供服务,但是我正在尝试创建一个django web应用程序来执行此操作。例如,请考虑bitbucket或任何其他存储库托管站点。构建一个webapp以服务多个HG库

要做到这一点,最好的方法是什么(或无论如何,真的)?

基本上,我需要做的是在我的网站上为特定于项目的URL提供Merucrial HttpCommandProtocol的实现,以便mercurial客户端可以将其回购站与网站上存储的回购站同步。我想我可以使用hglib来做到这一点,但CommandServer命令和HttpCommandProtocol命令之间似乎没有任何直接的映射。我可以自己实现HttpCommandProtocol:我已经成功完成了其中的一些,但大多数命令都没有记录,我无法弄清楚它们应该做什么。

另一种选择是为每个存储库运行hg serve,并且实质上从客户端传递到hg服务器,但可以有任何数量的回购站,并且每个服务器都需要位于不同的端口上,除非有一些方法可以通过管道或文件描述符与HttpServer通信,而不是TCP端口。你可以用hg serve --commandserver pipe这样做,但是CommandServer协议并不是HTTP所需要的。

回答

2

如果你对GPL的条款没问题,最简单的方法就是从你的django视图中调用hgweb。 hgweb是一款全功能,多版本库的python wsgi应用程序,django喜欢使用它。

这是完全有可能你的整个Django的看法可能是:

from mercurial.hgweb import hgweb 

def hg_view(request): 
    """ relay a WSGI Request to HG """ 
    hgweb(config).run_wsgi(request) 

和你的路线,与一个普通的Django视图。您可能需要修改请求以删除前导路径元素,但更重要的一点是,如果目标是让一个wsgi应用程序(该django)调用另一个wsgi应用程序(该网站是hgweb),那么避免使用TCP插座和本地管道,而是把所有东西都放在家里。

+0

这真棒,谢谢!只要我可以测试它并验证它的工作,我会接受这个答案。 – brianmearns

+0

它不会像那样工作,但它不会太远。请记住,这是与GPL代码的“链接”,因此您可以使用它,并且可以将其作为Web服务提供,然后将其作为GPL发布,但如果不受“GPL”条款的约束,则无法“分发”它GPL。 –

+0

谢谢,我很喜欢GPL =)。我不清楚如何正确地将一个单独的WSGI应用程序(hgweb)集成到django中:我不认为Django的请求对象(它传递给视图函数)实际上是一个WSGI请求,它可以传递给'run_wsgi' 。有任何想法吗? – brianmearns

0

这是我根据Ry4an接受的答案,实际使用的解决方案是。 django HttpRequest对象可以作为wsgirequest完全可以直接传递给hgweb_mod.run_wsgi,但我无法弄清楚返回的内容。以下为我工作,它当然不处理所有的边缘情况,但我能够推动和拉我的回购这种方式:

#!/usr/bin/python 

class WsgiWrapper(object): 
    """ 
    A wrapper object that works reasonably well (at least in the cases in which it is currently being used) 
    to turn a django request into a Wsgi thingy. 
    """ 
    def __init__(self, request): 
     self.request = request 
     self.status = None 
     self.headers = None 
     self.exc_info = None 

    def __call__(self, status=None, response_headers=None, exc_info=None): 
     if status is not None: 
      self.status = status 
     self.headers = response_headers 
     self.exc_info = exc_info 

     self.outputbuffer = cStringIO.StringIO() 
     return self.outputbuffer.write 


def serve_hg(request, repopath): 
    """ 
    Delegates to mercurial's built-in hgweb WSGI application to serve up access 
    to mercurial HTTPCommand Protocol. 
    """ 

    #Put together a wsgi request from the django request. 
    wrapper = WsgiWrapper(request) 
    env = {} 
    env.update(request.META) 
    env['SCRIPT_NAME'] = '/' 
    env['PATH_INFO'] = '/' 
    req = hgweb.request.wsgirequest(env, wrapper) 

    #Invoke hgweb as a wsgi application. 
    hgw = hgweb.hgweb(repopath) 
    hgw.repo.baseui.setconfig('web', 'allow_push', '*') 
    hgw.repo.baseui.setconfig('web', 'push_ssl', 'false') 
    gen = hgw.run_wsgi(req) 
    data = wrapper.outputbuffer.getvalue() 
    if len(data): 
     gen = [data] 
    wrapper.outputbuffer.close() 

    #Now build up the django response object from that. 
    resp = HttpResponse("".join(gen)) 
    stat = wrapper.status 
    if stat is None: 
     resp.status_code = 200 
    else: 
     pair = stat.split(' ', 1) 
     resp.status_code = pair[0] 
     if len(pair) > 1: 
      resp.reason_phrase = pair[1] 

    hopbyhop = (
     'Connection', 
     'Keep-Alive', 
     'Proxy-Authenticate', 
     'Proxy-Authorization', 
     'TE', 
     'Trailers', 
     'Transfer-Encoding', 
     'Upgrade', 
    ) 
    if wrapper.headers is not None: 
     for k, v in wrapper.headers: 
      if k not in hopbyhop: 
       resp[k] = v 

    return resp