Search code examples
c#azurecontinuous-integrationcakebuild

Can Cake (of http://cakebuild.net) be used for deploying Azure WebApps


Have been checking out Cake (at http://cakebuild.net) and am wondering if it can be used for deploying webapps and/or accessing virtual servers fro deploying of release packages.

I like the idea of cake being a deployment framework in C# so being in the same language as core development.

Are there any examples of azure deployments that I could get access?


Solution

  • There's a few ways you can deploy Azure using Cake, either through prebuilding site using some CI service like VSTS/AppVeyor and then publishing the artifacts using web deploy, git or ftp (there's a few Cake addins that can help with that Cake.WebDeploy, Cake.Git or Cake.FTP or using Azure built-in deployment engine Kudu and a custom deployment script using Cake.

    To assist with the Kudu deploy/build environment you can use the Cake.Kudu addin.

    The first step is to tell Kudu that you've got an custom deployment script, you do this by adding a ".deployment" file to the root of your repository with the content of

    [config]
    command = deploy.cmd
    

    The deploy.cmd could look something like this to install & launch Cake

    @echo off
    
    IF NOT EXIST "Tools" (md "Tools")
    
    IF NOT EXIST "Tools\Addins" (MD "Tools\Addins")
    
    nuget install Cake -ExcludeVersion -OutputDirectory "Tools" -Source https://www.nuget.org/api/v2/
    
    Tools\Cake\Cake.exe deploy.cake -verbosity=Verbose
    

    And deploy.cake could look something like this:

    #tool "nuget:https://www.nuget.org/api/v2/?package=xunit.runner.console"
    
    #tool "nuget:https://www.nuget.org/api/v2/?package=KuduSync.NET"
    
    #addin "nuget:https://www.nuget.org/api/v2/?package=Cake.Kudu"
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // ARGUMENTS
    
    ///////////////////////////////////////////////////////////////////////////////
    
    
    var target = Argument<string>("target", "Default");
    
    var configuration = Argument<string>("configuration", "Release");
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // GLOBAL VARIABLES
    
    ///////////////////////////////////////////////////////////////////////////////
    
    var webRole = (EnvironmentVariable("web_role") ?? string.Empty).ToLower();
    
    var solutionPath = MakeAbsolute(File("./src/MultipleWebSites.sln"));
    
    string outputPath = MakeAbsolute(Directory("./output")).ToString();
    
    string testsOutputPath = MakeAbsolute(Directory("./testsOutputPath")).ToString();
    
    
    DirectoryPath websitePath,
    
                    websitePublishPath,
    
                    testsPath;
    
    
    FilePath projectPath,
    
                testsProjectPath;
    
    switch(webRole)
    
    {
    
        case "api":
    
            {
    
                websitePath = MakeAbsolute(Directory("./src/Api"));
    
                projectPath = MakeAbsolute(File("./src/Api/Api.csproj"));
    
                testsPath = MakeAbsolute(Directory("./src/Api.Tests"));
    
                testsProjectPath = MakeAbsolute(File("./src/Api.Tests/Api.Tests.csproj"));
    
                websitePublishPath = MakeAbsolute(Directory("./output/_PublishedWebsites/Api"));
    
                break;
    
            }
    
        case "frontend":
    
            {
    
                websitePath = MakeAbsolute(Directory("./src/Frontend"));
    
                projectPath = MakeAbsolute(File("./src/Frontend/Frontend.csproj"));
    
                testsPath = MakeAbsolute(Directory("./src/Frontend.Tests"));
    
                testsProjectPath = MakeAbsolute(File("./src/Frontend.Tests/Frontend.Tests.csproj"));
    
                websitePublishPath = MakeAbsolute(Directory("./output/_PublishedWebsites/Frontend"));
    
                break;
    
            }
    
        case "backoffice":
    
            {
    
                websitePath = MakeAbsolute(Directory("./src/Backoffice"));
    
                projectPath = MakeAbsolute(File("./src/Backoffice/Backoffice.csproj"));
    
                testsPath = MakeAbsolute(Directory("./src/Backoffice.Tests"));
    
                testsProjectPath = MakeAbsolute(File("./src/Backoffice.Tests/Backoffice.Tests.csproj"));
    
                websitePublishPath = MakeAbsolute(Directory("./output/_PublishedWebsites/Backoffice"));
    
                break;
    
            }
    
        default:
    
            {
    
                throw new Exception(
    
                string.Format(
    
                        "Unknown web role {0}!",
    
                        webRole
    
                    )
    
                );
    
            }
    
    }
    
    
    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
    
                )
    
            );
    
    }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // SETUP / TEARDOWN
    
    ///////////////////////////////////////////////////////////////////////////////
    
    
    Setup(() =>
    
    {
    
        // Executed BEFORE the first task.
    
        Information("Running tasks...");
    
    });
    
    
    Teardown(() =>
    
    {
    
        // Executed AFTER the last task.
    
        Information("Finished running tasks.");
    
    });
    
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // TASK DEFINITIONS
    
    ///////////////////////////////////////////////////////////////////////////////
    
    
    Task("Clean")
    
        .Does(() =>
    
    {
    
        //Clean up any binaries
    
        Information("Cleaning {0}", outputPath);
    
        CleanDirectories(outputPath);
    
    
        Information("Cleaning {0}", testsOutputPath);
    
        CleanDirectories(testsOutputPath);
    
    
        var cleanWebGlobber = websitePath + "/**/" + configuration + "/bin";
    
        Information("Cleaning {0}", cleanWebGlobber);
    
        CleanDirectories(cleanWebGlobber);
    
    
        var cleanTestsGlobber = testsPath + "/**/" + configuration + "/bin";
    
        Information("Cleaning {0}", cleanTestsGlobber);
    
        CleanDirectories(cleanTestsGlobber);
    
    });
    
    
    Task("Restore")
    
        .Does(() =>
    
    {
    
        // Restore all NuGet packages.
    
        Information("Restoring {0}...", solutionPath);
    
        NuGetRestore(solutionPath);
    
    });
    
    
    Task("Build")
    
        .IsDependentOn("Clean")
    
        .IsDependentOn("Restore")
    
        .Does(() =>
    
    {
    
        // Build target web & tests.
    
        Information("Building web {0}", projectPath);
    
        MSBuild(projectPath, settings =>
    
            settings.SetPlatformTarget(PlatformTarget.MSIL)
    
                .WithProperty("TreatWarningsAsErrors","true")
    
                .WithProperty("OutputPath", outputPath)
    
                .WithTarget("Build")
    
                .SetConfiguration(configuration));
    
    
        Information("Building tests {0}", testsProjectPath);
    
        MSBuild(testsProjectPath, settings =>
    
            settings.SetPlatformTarget(PlatformTarget.MSIL)
    
                .WithProperty("TreatWarningsAsErrors","true")
    
                .WithProperty("ReferencePath", outputPath)
    
                .WithProperty("OutputPath", testsOutputPath)
    
                .WithTarget("Build")
    
                .SetConfiguration(configuration));
    
    });
    
    
    Task("Run-Unit-Tests")
    
        .IsDependentOn("Build")
    
        .Does(() =>
    
    {
    
        XUnit2(testsOutputPath + "/**/*.Tests.dll", new XUnit2Settings {
    
            NoAppDomain = true
    
            });
    
    });
    
    
    Task("Publish")
    
        .IsDependentOn("Run-Unit-Tests")
    
        .Does(() =>
    
    {
    
        Information("Deploying web from {0} to {1}", websitePublishPath, deploymentPath);
    
        Kudu.Sync(websitePublishPath);
    
    });
    
    
    
    Task("Default")
    
        .IsDependentOn("Publish");
    
    
    
    ///////////////////////////////////////////////////////////////////////////////
    
    // EXECUTION
    
    ///////////////////////////////////////////////////////////////////////////////
    
    
    RunTarget(target);
    

    In the above scenario it supports a solution with 3 different websites and the one that is published is based on an appsetting.

    For .NET Core web apps the flow is similar, basically something like below:

    1. DotNetCoreRestore
    2. DotNetCoreBuild
    3. DotNetCorePublish
    4. Kudu.Sync

    There's a couple of good blog posts on deploying to Azure with Cake: