1

背景

我们在工作中使用 Gradle 来构建我们的软件 (Java/C++/MATLAB),为每个客户打包配置文件的变体,并组装所有东西的最终聚合安装。

“客户变体”的一部分是具有一组复制/分层规则的常规目录结构,因此我们可以在变体之间共享尽可能多的配置。

我们今天通过一个类似于我在这里重新创建的虚构仓库的 copyConfiguration 任务来完成此任务。每个变体都被认为是整个“配置”项目的一个单独的子项目。我们按顺序 (1, 2, project.name) 复制,其中 project.name 中的文件覆盖来自 1 或 2 的同名文件(同样适用于 2 over 1)。我们构建一个目标目录,然后将其压缩以进行部署。我已经在示例中编造了名称,但考虑到有十几个“组件”子目录(aaa、bbb 等)和 10-15 个子项目(最大“orderToCopy”长度约为 5)。文件名在不同的组件或子项目中不是全局唯一的(所以你可以'

痛点

每周,我们都会根据哪些客户将收到下一个版本重新安装几个变体。现在,安装过程是痛苦的手动和缓慢的(对于几百台服务器通常是一夜之间),所以没有灾难性的东西,我们避免重新滚动我们的配置的整个发布版本,直到下一次安装。我们通过使用内部“补丁应用程序”系统来解决问题,该系统允许我们覆盖更改而无需重新安装所有内容。这些补丁是根据“需要”修复的内容手工构建的。当同一个文件被不同的人多次修补或有人将同名文件复制到错误的目的地时,这有时会很痛苦(例如,我们有多个 log4j.properties,但在给定目录中只有一个是正确的)。

我们还经常遇到一个客户的变体(旧客户)需要与另一个客户的新变体(新客户)进行通信的情况。当我们交付新客户端时,这通常通过在最后交付的旧客户端配置之上构建与上述类似的补丁来处理。这更加痛苦和紧张,因为有时这些补丁可以相互叠加,并且所有补丁都是手工构建的(例如,在此处复制此文件,重命名此文件等)。在我们拥有支持分支的 VCS 之前,情况要糟糕得多……

问题/期望的目标?

几个月来我一直在思考这个问题,但我还没有找到一个好的解决方案。我想做的是依靠来自 VCS 的信息(在我们的例子中是 git)或一些以前的信息(工件、构建区域等)来为给定参考点的每个变体构建一个 delta zip 文件。在我的示例 repo 中,如果 src/main/dist/aaa/2/fileA 发生更改,在理想情况下,subproject1 的 delta 补丁 zip 将为空,并且仅包含 subproject2 的更改文件。

想法

我尝试进行完整构建,下载以前的构建(基于 Gradle 命令行属性),提取它,然后区分两个输出目录。 我想我可以完成这项工作,但它看起来很脏并且在 Gradle 之外工作。 在向我指出 Mozilla 使用的 delta-binary 补丁后,我找到了一个用于 Java 的 xdelta 二进制差异库,并使用它进行了概念验证。

还有其他想法吗?有没有更好的声明性或惯用的 Gradle 方式来做这样的事情?如果有某种方法可以修改 copyConfiguration 以使其更容易,我愿意接受想法。

放弃的想法

  1. 我已经能够使用 git whatchanged 获取已更改文件的列表,然后在 eachFile{} 闭包中排除()不在我的列表中的文件。只要修改了“叶子”文件,这就会起作用。如果 src/main/dist/aaa/2/fileA 发生变化,我最终会排除 aaa/1/fileA 和 aaa/subproject1/fileA 并为我的补丁构建获取错误的内容。这也不处理现在应该使用 1 或 2 中的文件的删除(因为较高优先级的文件已被删除)。还有一种情况是删除所有出现的文件,这样我们就不应该安装任何东西,但我认为这是一个暂时可以忽略的边缘情况。在所有极端情况下,这似乎很难做到。

  2. 我也试过只取所有更改文件的基本名称,但这会过度选择常见的文件名并使补丁包含太多。例如,如果 aaa/2/fileA 发生变化,我们最终会为 subproject1 构建一个包含 aaa/data/fileA 和 bbb/data/fileA 的补丁(当没有文件是“正确”答案时)。我不喜欢这个,因为补丁 zip 包含未更改的文件并且不处理删除/移动。

  3. 我也想过,但还没有尝试过,做类似上面的事情。签出上一个提交,构建,签出提交以构建补丁,然后挂钩到 Gradle 的增量构建功能。我们仍在使用 Gradle 1.1,因此我还无法使用该功能。 这很痛苦,需要一个脚本来执行 Gradle 两次。

谢谢阅读!

4

0 回答 0