假设您有一个典型的 Web 应用程序和一个文件 configuration.whatever。每个从事该项目的开发人员都将有一个用于他们的开发盒的版本,将有一个开发、生产和阶段版本。您如何在源代码管理中处理这个问题?根本不签入此文件,使用不同的名称进行检查或完全做一些花哨的事情?
19 回答
我过去所做的是有一个默认的配置文件,它被签入到源代码管理中。然后,每个开发人员都有自己的覆盖配置文件,该文件被排除在源代码管理之外。应用程序首先加载默认文件,然后如果存在覆盖文件,则加载该文件并使用覆盖文件中的任何设置优先于默认文件。
一般来说,覆盖文件越小越好,但对于一个非常不标准的环境的开发人员来说,它总是可以包含更多的设置。
配置就是代码,您应该对其进行版本控制。我们的配置文件基于用户名;在 UNIX/Mac 和 Windows 中,您都可以访问用户的登录名,只要这些对项目是唯一的,就可以了。您甚至可以在环境中覆盖它,但您应该对所有内容进行版本控制。
这还允许您检查其他人的配置,这有助于诊断构建和平台问题。
不要版本该文件。版本模板或其他东西。
我的团队为每个环境(web.config.dev、web.config.test、web.config.prod)保留了不同版本的配置文件。我们的部署脚本复制出正确的版本,将其重命名为 web.config。这样,我们对每个环境的配置文件都有完整的版本控制,可以轻松地执行差异等。
目前我有一个带有扩展名的“模板”配置文件,例如:
web.config.rename
但是,如果关键更改发生了变化,我会发现此方法存在问题。
+1 关于模板方法。
但是由于这个问题带有 Git 标记,因此我想到了分布式替代方案,其中自定义项保存在私有测试分支上:
A---B---C---D--- <- mainline (public)
\ \
B'------D'--- <- testing (private)
在这个方案中,主线包含一个通用的“模板”配置文件,需要最少的调整才能发挥作用。
现在,开发人员/测试人员可以根据自己的意愿调整配置文件,并且只在一个私有测试分支上本地提交这些更改(例如 B' = B + 定制)。每次主线推进时,他们都会毫不费力地将其合并到测试中,这会导致合并提交,例如 D'(= D + B 自定义的合并版本)。
当“模板”配置文件更新时,这种方案真的很出色:双方的更改被合并,如果它们不兼容,极有可能导致冲突(或测试失败)!
我们使用的解决方案是只有一个配置文件 (web.config/app.config),但我们在文件中添加了一个特殊部分,其中包含所有环境的设置。
在我们的配置文件中有一个LOCAL、DEV、QA、PRODUCTION部分,每个部分都包含与该环境相关的配置键。
使这一切工作的是一个名为xxx.Environment的程序集,它在我们所有的应用程序(winforms 和 webforms)中都被引用,它告诉应用程序它在哪个环境中运行。
xxx.Environment 程序集从给定机器的machine.config中读取一行信息,告诉它它在 DEV、QA 等设备上。这个条目存在于我们所有的工作站和服务器上。
希望这可以帮助。
我一直将所有版本的配置文件保存在源代码管理中,与 web.config 文件位于同一文件夹中。
例如
web.config
web.qa.config
web.staging.config
web.production.config
我更喜欢这种命名约定(而不是 web.config.production 或 production.web.config),因为
- 当您按文件名排序时,它将文件保存在一起
- 当您按文件扩展名排序时,它将文件保存在一起
- 如果文件意外推送到生产环境,您将无法通过 http 看到内容,因为 IIS 将阻止提供 *.config 文件
应配置默认配置文件,以便您可以在自己的机器上本地运行应用程序。
最重要的是,这些文件在各个方面都应该几乎 100% 相同,甚至是格式。您不应该在一个版本中使用制表符而在另一个版本中使用空格进行缩进。您应该能够对文件运行差异工具,以准确查看它们之间的不同之处。我更喜欢使用 WinMerge 来区分文件。
当您的构建过程创建二进制文件时,应该有一个任务使用适合该环境的配置文件覆盖 web.config。如果文件被压缩,那么应该从该版本中删除不相关的文件。
我以前使用过模板,即 web.dev.config、web.prod.config 等,但现在更喜欢“覆盖文件”技术。web.config 文件包含大部分设置,但外部文件包含特定于环境的值,例如 db 连接。保罗威尔逊博客上的很好解释。
我认为这减少了配置文件之间的重复数量,这在添加新值/属性时会引起痛苦。
@Grant 是对的。
我在一个有近 100 名其他开发人员的团队中,我们的配置文件没有被检入源代码管理。我们在每次签出时都会提取存储库中的文件版本,但它们不会更改。
对我们来说效果很好。
签入的普通版本的 app/web.config 应该足够通用,可以在所有开发人员机器上工作,并随时了解任何新的设置更改等。如果您需要一组特定的 dev 设置/test/production 设置,使用这些设置检查单独的文件,正如 GateKiller 所说,使用某种命名约定,尽管我通常使用“web.prod.config”,因为不更改文件扩展名。
我们使用一个模板配置文件签入到版本控制中,然后在我们的自动构建中使用特定于环境的设置替换模板文件中的特定条目。特定于环境的设置存储在一个单独的 XML 文件中,该文件也受版本控制。
我们在自动构建中使用 MSBuild,因此我们使用来自MSBuild 社区任务的 XmlUpdate 任务来更新值。
很长一段时间以来,我一直在做 bcwood 所做的事情。我将 web.dev.config、web.test.config、web.prod.config 等的副本保留在源代码控制之下,然后我的构建/部署系统会在部署到各种环境时自动重命名它们。您会在文件之间获得一定程度的冗余(尤其是其中的所有 asp.net 内容),但通常它工作得很好。您还必须确保团队中的每个人都记得在进行更改时更新所有文件。
顺便说一句,我喜欢在最后保留“.config”作为扩展名,这样文件关联就不会被破坏。
至于配置文件的本地开发者版本,我总是尽力鼓励人们尽可能使用相同的本地设置,这样就不需要拥有自己的版本。它并不总是对每个人都有效,在这种情况下,人们通常只是根据需要在本地更换它并从那里开始。这不是太痛苦或任何事情。
我对它进行版本控制,但从不将其推送到其他服务器。如果生产服务器需要更改,我直接对配置文件进行更改。
它可能不漂亮,但它工作得很好。
在我们的项目中,我们将配置存储在带有前缀的文件中,然后我们的构建系统根据当前系统的主机名提取适当的配置。这对我们相对较小的团队来说效果很好,允许我们在添加新配置项时将配置更改应用到其他人的文件。显然,这绝对不能扩展到拥有无限数量的开发人员的开源项目。
我们这里有两个问题。
首先,我们必须控制软件附带的配置文件。
如果开发人员在 devolvement 环境中使用相同的文件,那么开发人员很容易将不需要的更改签入到主配置文件中。
另一方面,如果您有安装程序包含的单独配置文件,很容易忘记向其中添加新设置,或者让其中的注释与 devolvement 中的注释不同步配置文件。
然后我们遇到的问题是,当其他开发人员添加新的配置设置时,开发人员必须保持配置文件的副本是最新的。但是,对于每个开发人员来说,数据库连接字符串等一些设置是不同的。
问题/答案未涵盖第三个问题。 当您安装新版本的软件时,如何合并客户对您的配置文件所做的更改?
我还没有看到在所有情况下都能很好地工作的好的解决方案,但是我已经看到了一些部分解决方案 (可以根据需要组合成不同的组合),从而大大减少了问题。
首先减少主配置文件中的配置项数量。
如果您不需要让您的客户更改您的映射,请使用 Fluent NHibernate(或其他方式)将配置移动到代码中。
对于依赖注入设置也是如此。
尽可能拆分配置文件,例如使用单独的文件来配置 Log4Net 记录的内容。
不要在大量配置文件之间重复项目,例如,如果您有 4 个 Web 应用程序都安装在同一台机器上,那么每个应用程序中的 web.config 文件都指向一个整体配置文件。
(默认使用相对路径,因此很少需要更改 web.config 文件)
处理开发配置文件以获取发货配置文件。
可以通过在 Xml 注释中设置默认值来完成,然后在构建完成时在配置文件中设置这些值。或者在创建安装程序的过程中删除部分。
每个开发人员都拥有一个,而不是只有一个数据库连接字符串。
例如,在运行时首先在配置文件中查找“database_ianr”(其中ianr是我的用户名或机器名),如果没有找到,则查找“database”
有一个第二级“例如 -oracle 或 -sqlserver”使开发人员可以更快地访问这两个数据库系统。
这当然也可以对任何其他配置值进行。
然后可以在发送配置文件之前删除所有以“_userName”结尾的值。
然而,最终你是一个“配置文件的所有者”,负责管理如上所述或其他方式的配置文件。他/她还应该在每次发货前对面向客户的配置文件进行比较。
你不能消除对一个有爱心的人的需要一些这样的短问题。
我不认为有一个单一的解决方案适用于所有情况,因为它可能取决于配置文件中数据的敏感性、您使用的编程语言以及许多其他因素。但我认为将所有环境的配置文件保持在源代码控制下很重要,这样您就可以随时知道它何时以及由谁更改,更重要的是,如果出现问题,能够恢复它。他们会的。
所以这就是我的做法。这通常适用于 NodeJS 项目,但我认为它也适用于其他框架和语言。
我所做的是configs
在项目的根目录下创建一个目录,并在该目录下为所有环境保留多个文件(有时也为每个开发人员的环境提供单独的文件),这些文件都在源代码管理中进行跟踪。并且代码使用的实际文件命名config
在项目的根目录中。这是唯一未跟踪的文件。所以看起来像这样
root
|
|- config (not tracked)
|
|- configs/ (all tracked)
|- development
|- staging
|- live
|- James
当有人签出项目时,他复制了他想在未跟踪config
文件中使用的配置文件,并且他可以随意编辑它,但也有责任在根据需要提交到其他环境文件之前复制这些更改。
在服务器上,未跟踪文件可以只是对应于该环境的已跟踪文件的副本(或引用)。在 JS 中,您可以简单地使用 1 行来要求该文件。
这个流程一开始可能有点复杂,但它有很大的优势:
- 您永远不必担心在没有备份的情况下在服务器上删除或修改配置文件
- 如果开发人员在他的机器上有一些自定义配置并且他的机器由于任何原因停止工作,情况也是如此
- 在进行任何部署之前,您可以比较配置文件,
development
例如staging
,查看是否有任何丢失或损坏。
我们只是保持生产配置文件签入。开发人员有责任在将文件从源代码中安全地拉出以进行暂存或开发时更改文件。这在过去让我们很伤心,所以我不建议这样做。
我遇到了同样的问题,我找到了解决方案。我首先将所有文件添加到中央存储库(也是开发人员的)。
因此,如果开发人员从存储库中获取文件,则开发人员配置也在那里。对此文件进行更改时,Git 不应该知道这些更改。这样,更改就不能被推送/提交到存储库,而是留在本地。
我通过使用 git 命令解决了这个问题:update-index --assume-unchanged
. 我制作了一个 bat 文件,该文件在项目的预构建中执行,其中包含一个文件,其更改应该被 Git 忽略。这是我放在bat文件中的代码:
IF NOT EXIST %2%\.git GOTO NOGIT
set fileName=%1
set fileName=%fileName:\=/%
for /f "useback tokens=*" %%a in ('%fileName%') do set fileName=%%~a
set "gitUpdate=git update-index --assume-unchanged"
set parameter= "%gitUpdate% %fileName%"
echo %parameter% as parameter for git
"C:\Program Files (x86)\Git\bin\sh.exe" --login -i -c %parameter%
echo Make FIleBehaveLikeUnchangedForGit Done.
GOTO END
:NOGIT
echo no git here.
echo %2%
:END
在我的预构建中,我会调用 bat 文件,例如:
call "$(ProjectDir)\..\..\MakeFileBehaveLikeUnchangedForGit.bat" "$(ProjectDir)Web.config.developer" "$(SolutionDir)"
我在 SO 上找到了一个将正确的配置文件复制到 web.config/app.config 的 bat 文件。我也在预编译中调用了这个 bat 文件。这个bat文件的代码是:
@echo off
echo Comparing two files: %1 with %2
if not exist %1 goto File1NotFound
if not exist %2 goto File2NotFound
fc %1 %2
if %ERRORLEVEL%==0 GOTO NoCopy
echo Files are not the same. Copying %1 over %2
copy %1 %2 /y & goto END
:NoCopy
echo Files are the same. Did nothing
goto END
:File1NotFound
echo %1 not found.
goto END
:File2NotFound
copy %1 %2 /y
goto END
:END
echo Done.
在我的预构建中,我会调用 bat 文件,例如:
call "$(ProjectDir)\..\..\copyifnewer.bat" "$(ProjectDir)web.config.$(ConfigurationName)" "$(ProjectDir)web.config