銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Web発行先に応じたconfig変換をコマンドラインでのビルドで適用する ~既知のバグもあるよ~

One ASP.NET Advent Calendar 2013 - Adventar 15日目のエントリです。

弊社ではバージョン管理システムにGitを使っており、複数機能の開発を並行して行うために機能ごとにBranchを分けています。そして、Branchごとにテスト環境を用意しています。また、CIサーバーも用意し、コミットがあると自動でブランチごとのWebサイトに自動デプロイするようにしています。Branchごとに設定を分けないといけない項目があるため必要になるのが、BranchごとのWeb.configの切り替えになります(実際はわかりやすくするため、Branch1.config みたいにBranchごとに名前を分けて。

で注目したのが、 Web発行先に応じてWeb.configに変更を適用する - 新日々此何有哉 です。ビルド構成を追加せずにconfig変換を切り替えられるので非常に楽です。今回は、これをコマンドラインビルドから使おうというお話になります。

もともとはここの資料でも触れていたのですが、MSBuildのターゲットをpackageにしていったん成果物をzipで保存したあと、MSDeployコマンドでデプロイしていました。(自動デプロイする場合はCIでビルド後に即実行)

じゃあ、これを使ってMSBuildでpackage するときか、MSDeployするタイミングでWeb発行先のconfigをやろうと考えたのですが、標準のオプションには含まれていないようです...

参照: asp.net - How to Publish Web with msbuild? - Stack Overflow

これを解決するために、ビルドの拡張をしている方もいました。

richardszalay/msdeploy-package-publish · GitHub

これを使おうかとも思ったのですが、VisualStudio でのWeb発行そのもの(ビルドしてそのままデプロイする)をコマンドラインでやる方法が紹介されていました。

Command Line Deployment : The Official Microsoft ASP.NET Site

MSBuildで DeployOnBuild=true と指定すればコマンドラインからWeb発行ができます。コマンドラインからの場合、デプロイ先サーバーのパスワードは平文で次のように指定します。

msbuild.exe /m /p:Configuration=Debug,MvcBuildViews=true,DeployOnBuild=true,PublishProfile=Branch1.pubxml,Password='syoboipassword' /t:Clean,Build /v:Minimal

YATTA-

といいたいところですが、実際にこのコマンドをCIサーバーで実行するとこんなエラーメッセージが最後に出てコマンドの実行結果が失敗になってしまいました。

  発行に成功しました。
C:\workspace\Nazo-Branch1\Nazo.Web\obj\debug\csautoparameterize\original\web.config(20): error ASPCONFIG: アプリケーション レベルを超えて allowDefinition='MachineToApplication' として登録されているセクションを使うことはできません。このエラーは、仮想ディレクトリが IIS でアプリケーションとして構成されなかった場合に発生します。 [C:\workspace\Nazo-Branch1\Nazo.Web\Nazo.Web.csproj]
Build step 'Build a Visual Studio project or solution using MSBuild' marked build as failure

メッセージの通り発行自体には成功しているのですが、その後の処理でエラーになっています。発行自体できているのでOKとみなしてもいいのですが、それをやると本当に失敗したときとの区別がつかなくなって困ってしまいます。

で、これも検索していると Microsoft Connect にありました。

Error: allowDefinition='MachineToApplication' beyond application level | Microsoft Connect

どうやら既知のバグのようです...

Microsoft からWorkAround が提供されているので、じゃあ、ということで試したのですがエラーメッセージが変わっただけで、またエラーになってしまいました。

どうやら、そもそものエラーはconfig変換するときに一時フォルダにconfigファイルを保存するのですが、そのフォルダにあるconfigもデプロイしようとしてエラーになっている模様です。WorkAroundはひとまずこれらのフォルダを除外する、という処理を追加しているのですが、除外対象にすべきフォルダが増えていたようです(VS2013でそうなったのかもしれません)。

Connectの方にも投稿していますが、除外対象を追加したのが以下のXMLになります。csprojのの直前に挿入してください。

<PropertyGroup>
    <_EnableCleanOnBuildForMvcViews Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='' ">true</_EnableCleanOnBuildForMvcViews>
</PropertyGroup>
<Target Name="CleanupForBuildMvcViews" Condition=" '$(_EnableCleanOnBuildForMvcViews)'=='true' and '$(MVCBuildViews)'=='true' " BeforeTargets="MvcBuildViews">
    <ItemGroup>
     <_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\Package\**\*" />
     <_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\TransformWebConfig\**\*" />
     <!-- Add below line -->
     <_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\ProfileTransformWebConfig\**\*" />
     <_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\CSAutoParameterize\**\*" />
     <_TempWebConfigToDelete Include="$(BaseIntermediateOutputPath)**\TempPE\**\*" />
    </ItemGroup>

    <Delete Files="@(_TempWebConfigToDelete)"/>
</Target>

というわけで、コマンドラインからもWeb発行によるconfig変換使えるよ、というお話でした。