1

我正在使用 Visual Studio C# 库项目来包含部署工件所需的静态资源。(在我的例子中,结合 RoundhouseE 和 Octopus deploy 运行的 SQL 文件)。按照惯例,项目中的所有文件都必须设置其属性,以便“构建操作”为“内容”,“复制到输出目录”为“始终复制”。

如果团队中的某个人添加了文件但忘记设置这些属性,我们会看到部署错误。这通常是在内部环境中发现的,但我希望找到一种方法在 CI 构建中强制执行这一点。

那么有没有办法在构建过程中使用 MS Build 任务使构建失败或更好地覆盖这些属性?我以错误的方式解决这个问题吗?欢迎任何建议。

4

3 回答 3

3

您将不得不解析项目文件并检查Content没有CopyToOutputDirectory设置为Always,我怀疑还有另一种方法。

这可以使用您想要的任何脚本语言来完成,或者您甚至可以编写一个使用 Microsoft.Build.Evaluation 命名空间中的类的小型 C# 工具。这是一个可能的 PowerShell 实现 - 最难的部分是正确使用正则表达式。第一个检查没有任何元数据的内容,第二个检查 CopyToOutputDirectory 不以“A”开头的内容(我认为应该是“始终”,不知道如何匹配整个单词)。

FindBadContentNodes.ps1:

param([String]$inputDir)

Function FindBadContent()
{
  $lines = Get-Content $input
  $text = [string]::Join( "`n", $lines )
  if( $text -match "<Content Include.*/>" -Or
      $text -match "<Content Include.*`n\s*<CopyToOutputDirectory>[^A]\w*<.*" )
  {
    "Found file with bad content node"
    exit 1
  }
}

Get-ChildItem -Recurse -Include *.csproj -Path $inputDir | FindBadContent

从 MsBuild 调用它:

<Target Name="FindBadContentNodes">
  <Exec Command="Powershell FindBadContentNodes.ps1 -inputDir path\to\sourceDir"/>
</Target>

请注意,您在构建期间提到或最好仍然覆盖这些属性。我会远离这样的解决方案:您只是在掩埋问题并依靠 CI 生成正确的构建,因此仅使用 VS 的本地构建将不一样。Imo 使构建失败更好,特别是因为大多数 CI 系统都有一种通知开发人员的方法,无论如何都要负责,因此应该快速应用修复程序。

另一种可能性是让 CI 应用修复,然后提交更改,这样至少每个人都有正确的版本。

于 2014-03-10T09:49:10.033 回答
2

IIRC 在 Visual Studio 中有一种方法可以设置文件扩展名以默认执行某些操作,就像 .config 文件将始终设置为内容并复制到输出目录一样。

因此,可以对 .sql 文件(以及他们希望以这种方式设置的其他文件)执行相同的操作。快速搜索让我想到了这一点: http: //blog.andreloker.de/post/2010/07/02/Visual-Studio-default-build-action-for-non-default-file-types.aspx

相关部分:

可以在注册表中配置文件类型的默认构建操作。但是,我们没有手动破解注册表,而是使用了一种更好的方法:pkgdef 文件(一篇关于 pkgdef 文件的好文章)。本质上,pkdef 是类似于 .reg 文件的配置文件,它定义了注册表项和值,这些注册表项和值会自动合并到真实注册表中的正确位置。如果删除 pkgfile,更改将自动撤消。因此,您可以安全地修改注册表,而不会有破坏任何东西的危险——或者至少,很容易消除损坏。

最后,这是一个如何更改文件类型的默认构建操作的示例:

1:[$RootKey$\Projects{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\FileExtensions.spark]

2: "DefaultBuildAction"="Content" 键中的 Guid 是指项目类型。在这种情况下,“{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}”表示“C#项目”。可以在此处找到相当全面的项目类型指南列表。尽管没有明确涵盖 Visual Studio 2010,但这些指南也适用于当前版本。顺便说一句,我们可以在这里使用 C# 作为项目类型,因为基于 C# 的 MVC 项目实际上是 C# 项目(和 Web 应用程序项目)。对于 Visual Basic,您可以改用“{F184B08F-C81C-45F6-A57F-5ABD9991F28F}”。

$RootKey$ 是 Visual Studio 存储配置的真实注册表项的抽象:HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0_Config(注意:不要尝试手动编辑此项下的任何内容,因为它可以随时被覆盖通过 Visual Studio)。

其余的应该是不言自明的:此选项将 .spark 文件的默认构建操作设置为“内容”,因此这些文件包含在发布过程中。

您现在需要做的就是将这段文本放入扩展名为 pkgdef 的文件中,将其放在 %PROGRAMFILES(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Extensions 下(在 64 位系统上)或%PROGRAMFILES(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Extensions(在 32 位系统上)和 Visual Studio 将在下次启动时自动加载和应用设置。要撤消更改,只需删除文件。

最后,我附上了一堆在生产中使用的 pkgdef 文件,它们分别为 .spark、.brail、.brailjs 和 .less 文件定义了 C# 和 VB 项目的“内容”默认构建操作。下载它们,将它们保存在 Extensions 文件夹中的某个位置,您就可以开始使用了。

作者还说他构建了一个实用程序来帮助您完成所有这些工作:

http://tools.andreloker.de/dbag

于 2014-03-10T15:55:20.663 回答
0

扩展@stijn 答案,而不是使用正则表达式,使用本机 xml 解析要容易得多。

这是我建议的文件,它还支持通过仅在文件名上使用正则表达式来自定义评估哪些文件的能力。

param([String]$Path, [string]$IncludeMatch, [switch]$AllowPreserve)

Function Test-BadContentExists
{
    param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [Alias("FullName")]
        [string[]]$Path,
        [string]$IncludeMatch,
        [switch]$AllowPreserve
        )
  [xml]$proj = Get-Content -Path $Path
  $ContentNodes = ($proj | Select-Xml "//Content|//n:Content" -Namespace @{n='http://schemas.microsoft.com/developer/msbuild/2003'}).Node
  if (![string]::IsNullOrEmpty($IncludeMatch)) {
    $ContentNodes = $ContentNodes | Where-Object -Property Include -Match $IncludeMatch
  }
  #remove the always nodes
  $ContentNodes = $ContentNodes | Where-Object -Property CopyToOutputDirectory -ne 'Always'
  #optionally remove the preserve nodes
  if ($AllowPreserve) {
    $ContentNodes = $ContentNodes | Where-Object -Property CopyToOutputDirectory -ne 'PreserveNewest'
  }
  if($ContentNodes)
  {
    write-output "Found file with bad content node:"
    write-output ($ContentNodes | Select-Object Include,CopyToOutputDirectory | sort Include | Out-String)

    exit 1
  }
}

[hashtable]$Options = $PSBoundParameters
[void]$Options.Remove("Path")
Get-ChildItem -Recurse -Include *.csproj -Path $Path | Test-BadContentExists @Options

并使用参数调用它:

<Target Name="FindBadContentNodes">
  <Exec Command="Powershell FindBadContentNodes.ps1 -inputDir path\to\sourceDir -IncludeMatch '^Upgrade.*\.(sql|xml)$'"/>
</Target>

我最终改用了预构建事件,并将这个 ps1 文件放在我的解决方案目录中,这样我就可以将它用于多个项目。

echo "Build Dir: %cd%"
echo "Sol Dir: $(SolutionDir)"
echo "Proj Dir: '$(ProjectDir)"
echo.
Powershell -NoProfile -Command "& '$(SolutionDir)\FindBadContentNodes.ps1' -Path '$(ProjectDir)' -IncludeMatch '^Upgrade.*\.(sql|xml)$'"

示例构建输出:

1>  "Build Dir: C:\Source\RPS\MRM BI\MRMBI-Setup\MRMBI-Schema\bin\Debug"
1>  "Sol Dir: C:\Source\RPS\MRM BI\MRMBI-Setup\"
1>  "Proj Dir: 'C:\Source\RPS\MRM BI\MRMBI-Setup\MRMBI-Schema\"
1>  
1>  Found file with bad content node:
1>  
1>  Include                          CopyToOutputDirectory
1>  -------                          ----------------------
1>  Upgrades\V17.09\myfile1.sql                          
1>  Upgrades\V20.05\myfile2.sql      PreserveNewest       
1>  
于 2020-06-12T18:02:51.027 回答