【问题标题】:Using git hooks post commit to serve a subdirectory folder (dist/public) to a testing server使用 git hooks post commit 将子目录文件夹(dist/public)提供给测试服务器
【发布时间】:2018-07-21 14:28:58
【问题描述】:

所以我有一个这样的目录结构:

- Source
  - site a 
  - site b
  - site c
  - localhost(home directory for local test server)
    -  site a test
    -  site b test
    -  site c test

我目前的设置方式是源文件夹中的站点是工作版本。 当我很高兴测试一项功能时,我会设置一个 git hook post-commit 以将它们提供到相应站点名称下的 localhost 文件夹中。

   #!/bin/bash
   unset GIT_INDEX_FILE
   git --work-tree=/Users/kevin/Source/local-host/www.kevvin.me/ --git-dir=/Users/kevin/Source/www.kevvin.me/.git checkout -f

所以我提交了我所做的更改,本地 linux 服务器反映了这些更改。我还在生产服务器上设置了一个 git 挂钩,它可以在推送时提取最新更改(当我完成测试时)。

问题是这仅适用于没有像 /dist/ 文件夹这样的子目录结构的静态站点/站点。

我找不到推送整个分支的方法,而只使用 git hooks 为我的服务器提供 /dist/ 文件夹

我使用 git subtree 找到了一些接近的答案,但这些都涉及使用单独的目录文件夹并将其链接到具有子树的工作文件夹。 我需要使用 git 挂钩来选择一个已经存在的子文件夹 /dist/ 并将其发送到本地服务器。

【问题讨论】:

    标签: linux git apache githooks


    【解决方案1】:

    TL;DR

    您可能想使用git archive --format=tar ... | tar -C <path> -x ...。 (我不清楚您是否要剥离 dist/ 从提取的路径名中,但--strip-components=1 会这样做;请参阅the tar documentation。)有很多棘手的小细节,尽管。其中只有一个是 ... 部分中的内容。

    我需要使用 git 钩子选择一个已经存在的子文件夹 dist/ 并将其发送到本地服务器。

    可以做到这一点。您只是不能轻松做到这一点(嗯,这有多容易取决于您可用的工具)。幸运的是,你已经用linux 交叉标记了它,所以我们可以假设你有 Linux 工具。

    让我们先看看你现有的 post-receive hook 并注意一些缺陷:

    #!/bin/bash
    

    (到目前为止一切顺利)

    unset GIT_INDEX_FILE
    

    这条线没有任何用处(但无害)。不过有趣的是:在未设置索引文件的情况下,Git 将使用什么索引?答案当然是默认索引,$GIT_DIR/index

    我将下一行分成几部分:

    git --work-tree=/Users/kevin/Source/local-host/www.kevvin.me/ \
    

    这里的--work-tree 选项很重要:服务器存储库可能是一个裸存储库。这会覆盖裸露并选择不同的工作树。然后索引需要匹配这个工作树,并且通常会。

    如果存储库不是裸露的,则可能存在许多问题,包括使用主索引来索引此备用工作树。将索引文件指向其他地方可能更明智,甚至使用添加的工作树 (git worktree add),尽管当我进行实验时,这在接收存储库时效果很差。 (特别是现代 Git 允许您将 receive.denyCurrentBranch 设置为 updateInstead,但如果您使用添加的工作树这样做,Git 将无法更新添加的工作树。)

    --git-dir=/Users/kevin/Source/www.kevvin.me/.git
    

    由于这是一个接收后挂钩,它应该已经将$GIT_DIR 设置为正确的目录。用--git-dir 覆盖$GIT_DIR 似乎是一件很奇怪的事情。此外,这里的路径通常是用于非裸存储库的路径,因此我们处于“未设置的 GIT_INDEX_FILE 似乎可疑”领域。

    checkout -f
    

    这将运行git checkout -f,并按照两个选项的指示设置$GIT_DIR$GIT_WORK_TREEgit checkout -f 会签出哪个分支?嗯,那是在the git checkout documentation,部分内容是:

    您可以省略 ,在这种情况下,命令会退化为“检查 当前分支”...

    (强调我的)。那么 current 分支是哪个分支呢?嗯,这是在$GIT_DIR 中设置的当前分支。

    请注意,您将检查当前分支无论哪些分支和/或标签和/或注释实际上由git push 更新。因此,这个特殊的接收后脚本虽然可以使用,但不是很好。我们绝对可以做得更好。

    更好的部署脚本

    首先,我们应该阅读git push更新的名称:

    while read old new ref; do
        ...
    done
    

    在循环内部,我们可以检查 $old$new$ref 以查看:

    • 成功更改了哪个引用? ($ref)
    • 在更改之前该引用名称的哈希 ID 是什么? ($old)
    • 现在该引用名称的哈希 ID 是什么? ($new)

    我们还可以检查引用是创建$old 是全零)还是删除$new 是全零)。不过,大概您只想在一个特定的分支名称refs/heads/<em>name</em> 已更新时进行部署;并且大概该名称存在并且永远不会被删除。因此,我们可以只检查名称,如果要部署的分支名为 deployme,则该名称必须与 refs/heads/deployme 完全匹配:

    while read old new ref; do
        case $ref in
        refs/heads/deployme) deploy $new;;  # target branch updated: deploy it
        *) continue;;  # something else happened: ignore
        esac
    done
    

    这就是我们要运行的循环;现在我们需要编写deploy 函数。

    请注意,如果您想将不同分支部署到不同位置以进行测试,您可以扩展循环和/或设计deploy函数。现在,我们将坚持一个简单的。

    部署功能

    既然您要做的是从提交的快照中提取 dist/ 子目录,让我们首先编写我们的 deploy 函数来归档 dist/ 目录:

    # called with one argument, which is the hash ID of the
    # commit we are to deploy
    deploy() {
        git archive --format=tar $1 dist/ | ...
    }
    

    git archive 命令(这里不需要任何--git-dir--work-tree 参数)将把dist 目录的内容作为tar 归档写入存储在由hash ID 标识的提交中$1。所以tar,当我们运行它时,只会得到以dist/ 开头的路径。这意味着我们不必大惊小怪地限制我们提取的内容。我们可以-x 一切。

    我们需要的tar 命令是:

    tar -C /Users/kevin/Source/local-host/www.kevvin.me/
    

    如果您想放弃dist/ 部分,则与--strip-components=1 相同。但是这里有一个风险:如果 old 提交//Users/kevin/Source/local-host/www.kevvin.me/ 的内容有一个名为 junk 的文件,而 new 提交中的 dist/junk 不再存在,该怎么办? ?这个tar -C 命令不会删除 junk 文件。

    如果这是您想要的,我们就完成了;但如果没有,我们需要一些方法来清除我们提取的所有文件。一种很好的方法是提取文件,不是直接提取到/Users/kevin/Source/local-host/www.kevvin.me/,而是提取到同一文件系统上的临时目录。然后,我们可以重命名 www.kevvin.me 目录并重命名 新的临时目录,这样可以最大限度地减少中断时间——整个过程只需要两次重命名——然后清理重命名后的www.kevvin.me 目录。

    请注意,如果您这样做,您应该考虑到第二个 git push 启动 new 部署一个 new 替换的可能性,而旧的替换是仍在部署或清理部署。让这个工作正常由你决定(或者只是忍受它并假设它不会发生)。基于 pid 的锁定系统在这里不是一个坏主意,确实是万无一失的,但是我在这里写起来太复杂了。

    最后,干净地部署有一种相当肮脏/丑陋的方法,即首先删除所有内容,然后提取 tar 文件。我将首先展示一个,因为它是最简单的方法,但也是最糟糕的方法:

    deploy() {
        local target=/Users/kevin/Source/local-host/www.kevvin.me
    
        rm -rf $target && mkdir $target
        git archive --format=tar $1 -- dist/ | tar -C $target -x --strip-components=1
    }
    

    这是一个使用临时目录,没有锁定:

    deploy() {
        local target=/Users/kevin/Source/local-host/www.kevvin.me
        local tmpdir=$(mktemp -d $(dirname $target)/.newXXXXXXXX)
        local olddir=$(mktemp -d $(dirname $target)/.oldXXXXXXXX)
    
        git archive --format=tar $1 -- dist/ | tar -C $tmpdir -x --strip-components=1
        mv $target $olddir && mv $tmpdir $target && rm -rf $olddir
    }
    

    当然,您可以对此进行任何数量的额外调整。您还可以编写一个工具来进行就地更新,读取 tar 存档并将其内容与现有树的现有内容进行比较。但是,如果此过程很慢,请注意您将有一段时间部署的树将混合新旧内容。这就是目录交换技巧如此出色的原因。请注意,它假定 $(dirname $target)/.newXXXXXXXX 生成的路径与目标位于同一挂载点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-11
      • 2020-05-19
      • 2011-11-29
      • 2019-09-08
      • 2017-12-16
      • 2023-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多