git post-receive 挂钩不在后台 运行

git post-receive hook not running in background

根据 git documentation,post-receive 挂钩实质上会阻止回购,直到它完成:

... the client doesn’t disconnect until it has completed, so be careful if you try to do anything that may take a long time.

如果您需要钩子来启动一个构建作业,然后在启动另一个作业(比如部署)之前轮询它是否完成,这会导致问题。例如,当所述脚本为 运行.

时,构建服务器无法从存储库中获取

我们还假设您完全没有能力将脚本放在 git 服务器上作为 shell 命令执行,整个 nohup /usr/bin/env python /path/to/post_receive.py 2>&1 > /dev/null & 方法类似于 this question.

我们还假设您已经尝试了与 this 类似的整个双重 os.fork()'ing 守护进程和一些其他问题(non-working 下面的示例代码),发现 git 在完成挂钩之前仍然等待 long-running child 完成。

pid = os.fork()
if pid == 0:
    os.setsid()
    pid = os.fork()
    if pid == 0:
        long_running_post_receive_function()
    else:
        os._exit(0)
else:
    for fd in range(0, 3):
        os.close(fd)
    os._exit(0)

因此,在这些限制条件下,有没有人成功地使用长 运行 python post-receive 挂钩实际在后台运行而不会阻塞回购协议?

编辑

工作 没有异常处理的最小结构...感谢@torek 和@jthill

pid = os.fork()
if pid == 0:
    os.setsid()
    pid = os.fork()
    if pid == 0:
        for fd in range(0, 3):
            os.close(fd)
        long_running_post_receive_function()
    else:
        os._exit(0)
else:
    sys.exit()

您需要关闭所有描述符访问,以便 ssh 知道它永远不会再获得任何数据。换句话说,在描述符 0 到 2 上调用 os.close。实际上你需要那些 open,所以最好打开 os.devnullos.dup2 0、1 和 2 上的结果描述符(对于真正强大的软件,请确保 os.open 还没有 return 一个值 0 <= fd <= 2,当然,如果是,那没关系,只需将其保持在原位,同时复制其余部分)。

(你仍然需要通常的双叉技巧,放弃会话 ID 等可能是明智的。在一些 Unix 派生的系统中,有一个库例程称为 daemon,它可能在 libc 或 libutil 中,它会为你做这一切。一些细节不可避免地 OS 相关,例如放弃控制终端的方式(如果有的话)。但是,链接 [=23] 中缺少的主要内容=]-特定答案是 stdin/stdout/stderr 描述符的替换。)