【问题标题】:How should I use Nuget for internal enterprise development?我应该如何使用 Nuget 进行内部企业开发?
【发布时间】:2013-08-09 09:50:45
【问题描述】:

我们使用 Nuget 进行内部开发,以允许我们跨团队共享代码。但是,当一个人正在处理将同时部署在多个 nuget 包中的代码时,我们会遇到问题。比如

A 依赖于 B,而 B 又依赖于 C。

A、B 和 C 将他们的工件推送到 Nuget,这就是我们管理 A、B 和 C 之间依赖关系的方式。我们发现的问题是,如果开发人员想要在 C 中进行更改并快速看到这些更改反映在A,他们必须经过以下过程。

  1. 在 C 中进行更改。
  2. 将更改推送到 git
  3. CI 接受对 C 的更改并构建和部署新的 nuget 包。
  4. 进入 B 并使用 nuget update package 命令更新对 C 的引用。
  5. 将对 packages.config 文件的更改推送到 git
  6. CI 获取对 B 的更改并为 B 构建和部署新的 nuget 包
  7. 现在打开 A 并更改对 B 和 nuget 更新包的引用
  8. 在 A 中进行更改以与 B 中的更改(以及传递 C)一起进行

这似乎非常痛苦,并导致我们的一些开发人员质疑为我们内部开发的代码选择 Nuget。每个人仍然喜欢它消耗外部包。

在内部使用 Nuget 是否有更好的工作流程?

【问题讨论】:

  • 仍在使用,非常喜欢。对我们来说,当您构建代码时,脚本会将包直接发送到本地 NuGet 文件共享。这很好,因为其他项目可以通过 NuGet 更新在他们的项目中获取它。我认为你的有点强硬,不一定是因为 NuGet,而是因为 3 层依赖。如果您将 NuGet 排除在等式之外,您仍然需要执行过程中痛苦的部分。
  • 我认为您需要一个“胶水”包来添加更高级别的抽象。此包将引用正确的 A、B 和 C 包版本。然后项目只针对glue.1.0.0、glue.2.0.0等
  • 你可以考虑设计各种包之间的接口,升级C并不一定要换B,如果只是strong name/version issue,你可以看看@ 987654322@.

标签: nuget


【解决方案1】:

你有两个选择:

  1. 在您的组织内运行NuGet Gallery 的实例。这是运行 nuget.org 的代码
  2. 获取Artifactory Pro 的许可证,它具有内置的 Nuget 支持并充当 Nuget 存储库。

我都使用过,#1 是一个合理的开始选择,但 NuGet Galley 已针对 nuget.org 进行了优化和设计,而不是内部部署/企业使用,因此删除包之类的事情很痛苦(手动需要滚动 SQL)。

我会说您应该为 Artifactory Pro 支付(低)许可费 - 这是一款出色的产品,JFrog 团队非常热衷并投入使用。

您不应该将 nuget.org 用于内部/企业包; nuget.org 是为 3rd 方/开源库设计的,而不是内部构建依赖项。

编辑:就工作流程而言,为什么要将共享代码放入多个包中?如果代码需要共享,则需要放在自己单独的包中。

编辑 2:为了加快开发人员的代码更改工作流程,您可以使用 nuget.exe(命令行客户端)并使用命令行可访问的构建,因此您可以针对“开发人员”构建运行。然后在您的“开发人员”构建(而不是 CI 构建)中,当您想要将新更新的 B 作为依赖项并针对它构建时,您将 -Source 指定为本地路径(例如 nuget install B -Source C:\Code\B);同样适用于C 或其他本地新更新的软件包。然后当ABC 都构建良好时,您可以git push 所有它们(以相反的依赖顺序),让CI 做它的事情。

然而,如果您必须经常“跳舞”地进行此构建,您还应该质疑您的包分离是否真的合适,因为这表明所有代码都应该放在一个包中,或者可能在不同的包中沿着不同的线拆分。定义良好的包的一个关键特性是它不应该对其他包造成连锁反应,如果您有效地使用Semantic Versioning,则肯定不会。

编辑 3 marcelo-oliveira 要求的一些澄清:“命令行可访问的构建”是可以完全从命令行进行的构建,无需使用 Visual Studio,通常通过批处理文件。 “开发人员构建”是开发人员从她的工作站运行的构建,而不是在 CI 服务器上运行的 CI 构建(两个构建应该基本相同)。

【讨论】:

  • 我认为您关于正确使用语义版本控制的观点非常重要且容易被忽视。如果C 的新版本被设计为向后兼容 - 并考虑到这一点 - 那么你是对的,应该没有连锁反应,也没有级联更新。特别是,在发布A 的新版本之前,不必更新B。我有一些研究要围绕这个概念如何与组装强名称相互作用,但它值得深思!我也喜欢指定一个“开发者”包源以便快速周转的想法。
  • 你能详细说明一下Edit 2吗? “命令行可访问的构建”和“开发人员构建”是什么意思?
  • TeamCity 还具有充当 Nuget 存储库的能力。 developerfusion.com/article/144809/…
  • “你不应该使用 nuget.org” 他从未说过他在使用官方图库,从而使您的答案的主要部分完全无用。
  • @MatthewSkelton,没什么私人的,只是你的回答中有很大一部分说“使用私人包回购”,这通常是很好的建议,但完全不是被问到的。问题是关于需要对深度依赖进行更改时的工作流程。我的评论很有帮助,因为它提醒其他人不要再投票给一个不太有用的答案。
【解决方案2】:

在我们公司,我们通过以下设置解决了级联更新问题。首先,我们为 NuGet 存储库和构建服务器进行了以下设置。

  • 有一个内部 NuGet 存储库,其中包含公司所有已发布的包。此存储库只是我们其中一台服务器上的共享目录。
  • 每个开发人员都可以在自己的计算机上拥有(但不是必须拥有)一个或多个目录,用作本地 NuGet 包存储库。通过使用特定于用户的 NuGet configuration,开发人员可以控制 NuGet 在包存储库中搜索以查找包的顺序。

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <packageRestore>
        <add key="enabled" value="True" />
      </packageRestore>
      <packageSources>
        <add key="Dev" value="D:\dev\testpackages" />
        <add key="Company" value="<UNC_ADDRESS_COMPANY_REPOSITORY>" />
        <add key="NuGet official package source" value="https://nuget.org/api/v2/" />
      </packageSources>
      <disabledPackageSources />
      <activePackageSource>
        <add key="All" value="(Aggregate source)" />
      </activePackageSource>
    </configuration>
    
  • 所有解决方案都启用了自动包恢复功能,因此我们不必将包提交到我们的版本控制系统。

  • 开发人员只能控制 4 个版本号中的 3 个,例如如果版本是&lt;MAJOR&gt;.&lt;MINOR&gt;.&lt;BUILD&gt;.&lt;REVISION&gt;,那么开发人员只能更改主要、次要和内部版本号,修订号设置为 0,除非在构建服务器完成的构建中,它是构建的内部版本号。这很重要,因为这意味着对于由主要、次要和内部版本号组成的给定版本,构建服务器将始终生成更高的版本号。这再次意味着 NuGet 将更喜欢使用来自公司包存储库的包版本(它只通过构建服务器获取包)。

为了对基础库之一进行更改,有两个可能的过程正在使用。第一个过程是:

  1. 对基础库 (A) 进行更改。如果需要,更新 (A) 的版本。
  2. 运行 MsBuild 脚本构建二进制文件并创建 (A) 的 NuGet 包
  3. 将新的 NuGet 包复制到本地计算机上的包存储库中
  4. 在依赖项目 (B) 中升级到 (A) 的新包,这些包刚刚放置在本地计算机包存储库中(其版本应高于公司范围存储库或 NuGet 中可用的版本。组织)
  5. 对 (B) 进行更改。

如果需要对(A)进行更多更改,则重复步骤 1,2 和 3,然后从(B)的工作目录中删除(A)的包。下次运行构建时,NuGet 将寻找 (A) 的特定版本,在本地计算机存储库中找到它并将其拉回。请注意,NuGet cache 有时可能会阻止此过程,尽管它看起来像NuGet 可能不会缓存来自同一台机器的包(?)。

更改完成后,我们:

  1. 将更改提交到 (A)。构建服务器将运行集成构建以验证一切正常。
  2. 告诉构建服务器运行发布构建,它会构建二进制文件并将 NuGet 包推送到公司范围的 NuGet 存储库。
  3. 在 (B) 中,升级到 (A) 的最新版本(版本号应高于测试包,因为测试包的版本应为 abc0,而公司范围存储库中的新构建版本应为be abc where > 0
  4. 将更改提交到 (B)。等待构建服务器完成集成测试
  5. 告诉构建服务器为 (B) 运行发布构建。

进行开发工作的另一种方法是采取以下步骤

  1. 对基础库 (A) 进行更改。如果需要,更新 (A) 的版本。
  2. 构建二进制文件
  3. 将二进制文件复制到 NuGet 为项目 (B) 解压 (A) 包的位置(例如 c:\mysource\projectB\packages\ProjectA.1.2.3.4
  4. 对项目 (B) 进行必要的更改

提交过程还是一样,项目(A)需要先提交,项目(B)中的NuGet引用(A)需要升级。

第一种方法稍微简洁一些,因为此过程还会警告 (A) 的 NuGet 包中是否存在错误(例如忘记添加新程序集),而在第二个过程中,开发人员直到包之后才知道for (A) 已发布。

【讨论】:

  • 查看我上面编辑的答案:共享代码需要进入自己的包,而不是分布在多个包中。这是一个包管理基础。
  • @MatthewSkelton 我不确定您所说的“共享代码需要放入自己的包中”是什么意思。在我的回答中,我假设每个产品 A、B 和 C 都有单独的包。显然,任何产品都可能有多个包,但这不会改变答案。你能详细说明一下吗?
  • 嗨 Petrik,请参阅上面对我的答案的新编辑。基本上,如果包边界合适,对一个包的更改不需要更改另一个包。马修
  • 第三种开发方式可能是使用nugetreferenceswitcher.codeplex.com VS 扩展,它会自动将 NuGet 引用切换到项目引用并返回...
  • @JeffB 您声明“对一个包的更改总是需要对使用它的项目/包进行更改。如果您不更新消费者,他们将无法获得增强和/或bug修复。”第二个陈述是正确的,但这并不能使您的第一个陈述正确。包管理的部分意义在于它将消费者与包更改分离。
【解决方案3】:

如果 A、B 和 C 在同一个解决方案下,您可以在单个构建中为它们创建 NuGet 包。

只需确保使用包具有它所依赖的包的新版本号(假设您的构建不会随机更改它)。

如果 A、B 和 C 故意处于不同的解决方案下,例如A 属于基础架构解决方案,B 属于产品解决方案,那么我对您的唯一建议是定义您的 CI 构建在签入时运行,而不是定期运行。


编辑:

另一种选择是在本地构建期间创建一个推送预发布包(例如版本 1.0.0-alpha)到您的 NuGet 服务器,这样开发人员可以在创建之前对包进行试验发布版本。

【讨论】:

  • 有多种工具可以在构建过程中自动创建包,包括我构建的一个。
【解决方案4】:

Nuget 专为共享第三方库而设计。我们计划在内部对所有可以在项目之间共享的通用代码使用 Nuget。诸如通用数据层、字符串和其他正则表达式函数、电子邮件组件和其他类似工件之类的东西。

我认为 Nuget 有帮助的唯一方法是,您在团队/项目之间共享的组件、代码或工件是稳定的,并且有自己的发布周期,不同于您的项目的发布周期。对于您的需求,Nuget 是一种矫枉过正。在同一个解决方案中链接所有此类项目可能会有更高的生产力。您的解决方案结构应包括三个项目 A、B 和 C,作为在需要时在内部引用的依赖项目。

【讨论】:

  • “单一解决方案”方法简化了很多,但不能很好地扩展。 Microsoft 建议在通过 50 个项目(中型应用程序)时拆分您的解决方案。如果没有检查二进制文件,Nuget 似乎是次优,即使很痛苦。
  • 如果我们谈论的是库,您如何看待以子存储库的形式(使用 Git/Mercurial)而不是通过 NuGet 编译的 DLL 共享源代码?
  • @user20358 完全同意...如果软件包频繁且没有自己的发布周期,则不值得花费时间和精力。我正在试验 GitSubmodules,会看到
  • @Gokulnath 子模块的效果如何?我正在处理同样的问题/痛苦
  • @A.K 子模块运行良好。我们一直使用它,直到包足够稳定,然后切换到内部 NuGet。当所有依赖的应用程序都应该引用相同的版本时,GitSubmodules 工作得很好,而我们在不同的应用程序中引用不同的版本(或分支)时遇到了困难。我们目前正在试验 NuKeeper hanselman.com/blog/…
猜你喜欢
  • 1970-01-01
  • 2014-11-09
  • 2015-09-18
  • 1970-01-01
  • 2017-08-04
  • 1970-01-01
  • 2012-10-20
  • 1970-01-01
  • 2019-02-21
相关资源
最近更新 更多