当需要构建安装项目时,如何在 Subversion 中管理维护/错误修复分支?
我们有一套用 VB6 编写的相关产品,以及一些 C# 和 VB.NET 项目,所有源代码都保存在一个 Subversion 存储库中。
我们还没有在 Subversion 中使用分支(尽管我们现在使用标签发布),只是在主干中进行所有开发,当主干足够稳定时创建新版本。当我们发布新版本,发现问题,并且我们已经开始开发新功能或对主干进行重大更改时,这会导致无尽的悲伤。过去,我们会通过以下两种方式之一解决这个问题,具体取决于问题的严重程度以及我们认为主干的稳定性:
尽快稳定主干,解决问题,然后发布基于维护的更新在 HEAD 版本上,但这对修复错误的版本产生了副作用,但由于主干中的半完成功能或错误修复而引入了新问题。
让客户等到下一个正式版本,这通常是几个月。
我们希望改变我们的政策以更好地应对这种情况。每当我标记正式版本时,我都会考虑在 Subversion 中创建一个“维护分支”。然后,新的开发将在主干中继续,我可以定期将主干中的特定修复程序合并到维护分支中,并在积累了足够的修复程序时创建维护版本,同时我们继续并行进行下一个主要更新。我知道我们也可以有一个更稳定的主干,并为新的更新创建一个分支,但将当前的开发保留在主干中对我来说似乎更简单。
主要问题是,虽然我们可以轻松地从发布标签分支源代码并重新编译它以获取该版本的二进制文件,但我不确定如何处理设置和安装程序项目。我们使用QSetup来创建我们所有的安装程序,现在当我们需要修改一个安装项目,我们只需就地编辑项目文件(所有安装项目和我们自己不编译的任何依赖项都存储在单独的服务器上,并且我们确保始终仅在该计算机上编译安装项目)。但是,由于我们可能会在代码更改时向安装程序添加或删除文件,因此无法保证今天的安装项目能够与昨天的源代码一起使用。
我本来打算将所有 QSetup 项目放入 Subversion 中来处理这个问题,但我发现这种方法存在一些问题。
我希望安装程序的创建尽可能自动化,至少,我想要一个单独的构建机器,我可以在其中构建我想要的版本(首先从 Subversion 获取代码),获取该项目的安装项目从 Subversion 发布,重新编译安装程序,然后将安装程序复制到网络上的另一个位置以进行 QA 测试并最终发布给客户。
但是,当有人需要更改安装项目(添加 trunk 现在需要的新依赖项或进行其他更改)时,就会出现问题。如果他们将其视为源文件并在自己的计算机上检出并对其进行编辑,则他们将无法将文件添加到项目中,除非他们首先将需要添加到构建计算机的文件复制(因此他们是可供其他开发人员使用),然后将所有其他依赖项从构建计算机复制到他们的计算机,确保与文件夹结构完全匹配。这里的问题是 QSetup 对添加到安装项目的任何文件使用绝对路径。然而,这意味着在开发机器上安装一堆安装依赖项,这看起来很混乱(如果有人不小心在他们的机器上运行安装项目,这可能会破坏开发环境的稳定性)。
另外,我们如何管理第三方依赖项?例如,如果当前维护分支使用 MSXML 3.0 并且主干现在需要 MSXML 4.0,如果我们已经用最新版本替换了构建计算机上的 MSXML 库(假设两个版本),我们就无法返回并创建维护版本具有相同的文件名)。我能想到的唯一解决方案是将所有第三方依赖项与源代码一起放入 Subversion 中,或者确保我们将不同的库版本放在单独的文件夹中(即 C:\Setup\Dependency\MSXML\ v3.0
和 C:\Setup\Dependencies\MSXML\v4.0
)。一种方式比另一种方式“更好”或更常见吗?
是否有处理这种情况的最佳实践?基本上,如果我们发布软件的 v2.0,我们希望在处理 v2.1 时能够发布 v2.0.1、v2.0.2 和 v.2.0.3,但整个设置/安装项目和设置依赖性问题使得这个问题比典型的“只需在 Subversion 中创建一个分支并根据需要重新编译”的答案更加复杂。
We have a suite of related products written in VB6, with some C# and VB.NET projects, and all the source is kept in a single Subversion repository.
We haven't been using branches in Subversion (although we do tag releases now), and simply do all development in trunk, creating new releases when the trunk is stable enough. This causes no end of grief when we release a new version, issues are found with it, and we have already begun working on new features or major changes to the trunk. In the past, we would address this in one of two ways, depending on the severity of the issues and how stable we thought the trunk was:
Hurry to stabilize the trunk, fix the issues, and then release a maintenance update based on the HEAD revision, but this had the side effect of releases that fixed the bugs but introduced new issues because of half-finished features or bugfixes that were in trunk.
Make customers wait until the next official release, which is usually a few months.
We want to change our policies to better deal with this situation. I was considering creating a "maintenance branch" in Subversion whenever I tag an official release. Then, new development would continue in trunk, and I can periodically merge specific fixes from trunk into the maintenance branch, and create a maintenance release when enough fixes are accumulated, while we continue to work on the next major update in parallel. I know we could also have a more stable trunk and create a branch for new updates instead, but keeping current development in trunk seems simpler to me.
The major problem is that while we can easily branch the source code from a release tag and recompile it to get the binaries for that release, I'm not sure how to handle the setup and installer projects. We use QSetup to create all of our setup programs, and right now when we need to modify a setup project, we just edit the project file in-place (all the setup projects and any dependencies that we don't compile ourselves are stored on a separate server, and we make sure to always compile the setup projects on that machine only). However, since we may add or remove files to the setup as our code changes, there is no guarantee that today's setup projects will work with yesterday's source code.
I was going to put all the QSetup projects in Subversion to deal with this, but I see some problems with this approach.
I want the creation of setup programs to be as automated as possible, and at the very least, I want a separate build machine where I can build the release that I want (grabbing the code from Subversion first), grab the setup project for that release from Subversion, recompile the setup, and then copy the setup to another place on the network for QA testing and eventual release to customers.
However, when someone needs to change a setup project (to add a new dependency that trunk now requires or to make other changes), there is a problem. If they treat it like a source file and check it out on their own machine to edit it, they won't be able to add files to the project unless they first copy the files they need to add to the build machine (so they are available to other developers), then copy all the other dependencies from the build machine to their machine, making sure to match the folder structure exactly. The issue here is that QSetup uses absolute paths for any files added to a setup project. However, this means installing a bunch of setup dependencies onto development machines, which seems messy (and which could destabilize the development environment if someone accidentally runs the setup project on their machine).
Also, how do we manage third-party dependencies? For example, if the current maintenance branch used MSXML 3.0 and the trunk now requires MSXML 4.0, we can't go back and create a maintenance release if we have already replaced the MSXML library on the build machine with the latest version (assuming both versions have the same filename). The only solution I can think is to either put all the third-party dependencies in Subversion along with the source code, or to make sure we put different library versions in separate folders (i.e. C:\Setup\Dependencies\MSXML\v3.0
and C:\Setup\Dependencies\MSXML\v4.0
). Is one way "better" or more common than the other?
Are there any best practices for dealing with this situation? Basically, if we release v2.0 of our software, we want to be able to release v2.0.1, v2.0.2, and v.2.0.3 while we work on v2.1, but the whole setup/installation project and setup dependency issue is making this more complicated than the typical "just create a branch in Subversion and recompile as needed" answer.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
据我了解,关键是摆脱绝对路径和“众所周知”的网络位置。没有它们。曾经。
在我工作的地方,我们将所有设置文件、项目、依赖项 - 所有这些 - 保存在主代码所在的同一代码存储库中。这样,当我们创建分支时,所有设置内容都会随之分支,并以与分支时完全相同的形式保留。
我在这里看到的唯一问题是,正如您提到的,QSetup 使用绝对路径。虽然我觉得这完全荒谬,但我知道您可能不会考虑转向更专业的东西(顺便说一句,我们使用 NSIS)。
然而,在我看来,可以应用相对简单的解决方案。我在这里假设 QSetup 将其项目保留为某种文本、人类可以理解的格式(否则,它就太荒谬了,无法理解)。因此,您可以创建 QSetup 项目文件的副本,并将所有路径替换为相对路径,并在开头添加某种标记,如下所示:
%ROOT%\Dependency\SomeDep.dll
然后,您可以让构建脚本在调用 QSetup 之前运行一个简单的实用程序,这将通过将
%ROOT%
替换为存储库所在的适当路径来创建“真正的”项目文件待检查。实现此操作后,您可以在任何机器上创建构建 - 无论是指定的构建机器、开发机器还是其他机器。
To my understanding, the key is to get rid of absolute paths and "well-known" network locations. Don't have them. Ever.
Where I work, we keep all the setup files, projects, dependencies - ALL of it - in the very same code repository where the main code is. This way, when we create a branch, all the setup stuff gets branched along with it, and gets preserved in the exact same form as it was at the moment of branching.
The only problem that I see here is that, as you mentioned, QSetup uses absolute paths. While I find this completely ridiculous, I understand that you probably will not consider switching to something more professional (we use NSIS, by the way).
However, it seems to me that a relatively simple solution may be applied. I assume here that QSetup keeps its projects in some kind of text, human-understandable format (otherwise, it is just ridiculous beyond comprehension). As such, you can create a copy of your QSetup project file(s) and replace all your paths with relative ones, with some kind of a marker at the beginning, like so:
%ROOT%\Dependencies\SomeDep.dll
Then you can have your build script run a simple utility just before calling QSetup, which will create the "real" project file by replacing
%ROOT%
with the appropriate path, where the repository happens to be checked out.After implementing this, you may create a build on any machine - be it a designated build machine, or a development one, or something else.