miso_soup3 Blog

主に ASP.NET 関連について書いています。

Custom Deployment for Azure Web Apps using CAKE

GitHub のソースから Azure WebApps にデプロイするときの Kudu カスタムデプロイ機能にて、
CAKE(C#) を使ったビルドを試してみました。

CAKE とは


http://cakebuild.net/

f:id:miso_soup3:20161228190401p:plain


C# のスクリプトでビルドのタスクを書くプロジェクトです。Roslyn と Mono で動くので、クロスプラットフォームなビルドシステムになります。
オープンソースです。
ググるときは、CakePHP がひっかかってしまうで、"CAKE build" "cakebuild" がいいです。(C# Make が由来っぽいけど…。)

↓このような感じで C# でビルドタスクを記述します。

f:id:miso_soup3:20161228181430p:plain

今回は、デプロイに加え、Web.config の置換をアドインでやってみました。ソースは、ASP.NET なプロジェクトを前提とします。

Sample : GitHub - hhyyg/CAKEWebAppCustomDeploy

試してみた手順

GitHub と Azure WebApps の連携

まず、GitHub のリポジトリ・Azure WebApps・ASP.NET なプロジェクトを用意します。
GitHub には、ASP.NET のプロジェクトを push しておきます。
次に、ポータルの Azure WebApps にて、「デプロイ オプション」を選択し、WebApps と GitHub を紐づけます。

f:id:miso_soup3:20161228181132p:plain

項目について:

似た項目が2つ「デプロイ オプション」と「継続的配信(プレビュー)」とありますが、
前者は Kudu が「MSBuild.exe」でビルドしますが、後者は、Visual Studio Team Service のビルドエージェントにてビルドを行います。
後者は Visual Studio Team Service にてビルドされますが、ソースは GitHub に置くことも可能です。

「継続的配信(プレビュー)」については、次の2つのドキュメントが参考になりました。

Visual Studio Team Servicesのサーバービルド機能を使う(8日目) - kkamegawa's weblog
Continuous delivery to Azure App Service from Release Management

Kudu のカスタムデプロイ

上記のように、GitHub と Azure WebApps を連携すると、すぐにデプロイが開始されます。
この仕組みについては、Azure Web App のカスタムデプロイを使って特定のディレクトリをGithubと同期する - tech.guitarrapc.cóm が参考になりました。

デプロイは、WebApps の「D:\home\site\deployments\tools」配下にある「deploy.cmd」が実行されます。

f:id:miso_soup3:20161228183153p:plain
(↑ https://{webappsの名前}.scm.azurewebsites.net/DebugConsole でアクセスできる Kudu の CMD 画面。)

カスタムデプロイを試す一歩として、GitHub リポジトリの root フォルダに「.deployment」ファイルを作成し、次のように記述します。

.deployment:

[config]
command = deploy.cmd

deploy.cmd は、先ほどの「D:\home\site\deployments\tools」配下からコピーし、GitHub リポジトリの root フォルダに配置します。

f:id:miso_soup3:20161228183512p:plain
(↑ GitHub リポジトリのフォルダ)

この状態で GitHub に push すると、deploy.cmd が実行されデプロイが行われます。
この「.deployment」ファイルや「deploy.cmd」等をカスタマイズすることが、いわゆる Kudu の カスタムデプロイと言われます。

カスタマイズ方法については:
Customizing deployments · projectkudu/kudu Wiki · GitHub
Configurable settings · projectkudu/kudu Wiki · GitHub

CAKE でビルドする

Kudu のカスタムデプロイ時に、CAKE でビルドを行うようにします。
Cake - Cake Kudu - Azure Web Deployment Addin こちらのサイトが参考になりました。が、そのままコピペするとエラーになります。
(余計なが入っていたり、\が/になっているため。)

先ほど作成した deploy.cmd ファイルを、次のように変更します。
deploy.cmd:

@echo off
IF NOT EXIST "Tools" (md "Tools")
IF NOT EXIST "Tools\Addins" (md "Tools\Addins")
nuget install Cake -ExcludeVersion -OutputDirectory "Tools"
Tools\Cake\Cake.exe deploy.cake -verbosity=Verbose

次に、同じく GitHub リポジトリの root フォルダに「deploy.cake」ファイルを作成し、次のように記述します。
deploy.cake:

#tool "KuduSync.NET" "https://www.nuget.org/api/v2/"
#addin "Cake.Kudu" "https://www.nuget.org/api/v2/"

///////////////////////////////////////////////////////////////////////////////
// ARGUMENTS
///////////////////////////////////////////////////////////////////////////////

var target          = Argument<string>("target", "Default");
var configuration   = Argument<string>("configuration", "Release");

///////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
///////////////////////////////////////////////////////////////////////////////

var websitePath     = MakeAbsolute(Directory("./MisoDep"));
var solutionPath    = MakeAbsolute(File("./MisoDep.sln"));

if (!Kudu.IsRunningOnKudu)
{
    throw new Exception("Not running on Kudu");
}

var deploymentPath = Kudu.Deployment.Target;
if (!DirectoryExists(deploymentPath))
{
    throw new DirectoryNotFoundException(
        string.Format(
            "Deployment target directory not found {0}",
            deploymentPath
            )
        );
}

///////////////////////////////////////////////////////////////////////////////
// TASK DEFINITIONS
///////////////////////////////////////////////////////////////////////////////

Task("Clean")
    .Does(() =>
{
    //Clean up any binaries
    Information("Cleaning {0}", websitePath);
    CleanDirectories(websitePath + "/bin");
});

Task("Restore")
    .Does(() =>
{
    // Restore all NuGet packages.
    Information("Restoring {0}...", solutionPath);
    NuGetRestore(solutionPath);
});

Task("Build")
    .IsDependentOn("Clean")
    .IsDependentOn("Restore")
    .Does(() =>
{
    // Build all solutions.
    Information("Building {0}", solutionPath);
    MSBuild(solutionPath, settings =>
        settings.SetPlatformTarget(PlatformTarget.MSIL)
            .WithProperty("TreatWarningsAsErrors","true")
            .WithTarget("Build")
            .SetConfiguration(configuration));
});

Task("Publish")
    .IsDependentOn("Build")
    .Does(() =>
{
    Information("Deploying web from {0} to {1}", websitePath, deploymentPath);
    Kudu.Sync(websitePath);
});

Task("Default")
    .IsDependentOn("Publish");

///////////////////////////////////////////////////////////////////////////////
// EXECUTION
///////////////////////////////////////////////////////////////////////////////

RunTarget(target);

ここで、次の行を環境に合わせて編集します。

configuration の第二引数には、ビルドのモードを設定します。(Release など)
edit deploy.cake:

var target          = Argument<string>("target", "Default");
var configuration   = Argument<string>("configuration", "vsbuildrelease");

websitePath には、GitHub リポジトリ内の、発行対象となるフォルダを、
websitePath には、.sln ファイルパスを記述します。
edit deploy.cake:

var websitePath     = MakeAbsolute(Directory("./MisoDep"));
var solutionPath    = MakeAbsolute(File("./MisoDep.sln"));

以上のように編集した後、push を行うと CAKE によるビルドが走ると思います。
確認するには、Azure WebApps のポータルの「デプロイオプション」にてログを表示します。

f:id:miso_soup3:20161228184652p:plain

Web.config を変換する

このままでは、Web.config が適した Web.**.config へ変換されません。変換するには、GitHub - nengberg/cake-envxmltransform: A plugin for Cake for environment-based configuration transformations こちらのアドインを追加します。

ASP.NET のプロジェクトに第三のソリューション構成を作成して試してみます。

ビルド>構成マネージャー を開き、「vsbuildrelease」という名前で追加してみました。
f:id:miso_soup3:20161228184950p:plain
f:id:miso_soup3:20161228185050p:plain

Web.config が置換されたかどうか確認するために、構成ごとの appSettings の値をサイトに表示する、という手段をとります。

「Web.config」ファイルを右クリックし、「Config 変換を追加」をクリックします。「Web.vsbuildrelease.config」が追加されます。

「Web.config」ファイルにて、appSettings を追加します。

  <appSettings>
    <add key="Environment" value="Dev"/>

「Web.vsbuildrelease.config」には、次のように記述します。

  <appSettings>
    <add key="Environment" value="vsbuildrelease"
         xdt:Transform="SetAttributes" xdt:Locator="Match(key)"/>


ASP.NET のプロジェクト内にて、適当なページに次のように記述します。(例は Home.cshtml ファイルにて)

<div>
    Environment: @System.Configuration.ConfigurationManager.AppSettings["Environment"]
</div>

CALE ビルドを設定します。
3行目あたりに「#addin Cake.EnvXmlTransform」を追加します。
deploy.cake:

#tool "KuduSync.NET" "https://www.nuget.org/api/v2/"
#addin "Cake.Kudu" "https://www.nuget.org/api/v2/"
#addin Cake.EnvXmlTransform

deploy.cake L55 あたりに追加:

Task("Apply-Config-Transformations")
  .Does(() => {

    Information("Apply-Config-Transformations");
    var configFileFolder = "./*/*.config";
    var environment = configuration;
    ConfigTransform.ApplyTransformations(configFileFolder, environment);
});


64行目あたりで、次のように「.IsDependentOn("Apply-Config-Transformations")」を追加します。
deploy.cake L64:

Task("Build")
    .IsDependentOn("Clean")
    .IsDependentOn("Restore")
    .IsDependentOn("Apply-Config-Transformations")
    .Does(() =>
{
    // Build all solutions.
    Information("Building {0}", solutionPath);
    MSBuild(solutionPath, settings =>
        settings.SetPlatformTarget(PlatformTarget.MSIL)
            .WithProperty("TreatWarningsAsErrors","true")
            .WithTarget("Build")
            .SetConfiguration(configuration));
});

これでデプロイすると、Web.config が置換されてデプロイされるはずです。

ログを見ると次のように表示されました。
f:id:miso_soup3:20161228185704p:plain

CAKE のパッケージやアドインは、「D:\home\site\repository\Tools」配下にあります。

f:id:miso_soup3:20161228185831p:plain

その後、CAKE.Kudu 0.4.0 が更新され .NET Core に移行したみたいです。

また、こちら