Search code examples
angulardeploymentbuildcakebuild

cake build angular application to deploy to azure


I have created a angular application using command ng new APPNAME. I want to use cake build to deploy this application. Is it possible to use cake build? If so how? My end target is to deploy it to azure, but i need to do it with Cake build. I have uploaded all the source code to git repository.


Solution

  • So building a plain vanilla app with the Angular CLI would look something like this

    string          target      = Argument("target", "Build");
    FilePath        ngPath      = Context.Tools.Resolve("ng.cmd");
    FilePath        npmPath     = Context.Tools.Resolve("npm.cmd");
    DirectoryPath   outputPath  = MakeAbsolute(Directory("./output"));
    
    Action<FilePath, ProcessArgumentBuilder> Cmd => (path, args) => {
        var result = StartProcess(
            path,
            new ProcessSettings {
                Arguments = args
            });
    
        if(0 != result)
        {
            throw new Exception($"Failed to execute tool {path.GetFilename()} ({result})");
        }
    };
    
    Task("Install-AngularCLI")
        .Does(() => {
        if (ngPath != null && FileExists(ngPath))
        {
            Information("Found Angular CLI at {0}.", ngPath);
            return;
        }
    
        DirectoryPath ngDirectoryPath = MakeAbsolute(Directory("./Tools/ng"));
    
        EnsureDirectoryExists(ngDirectoryPath);
    
        Cmd(npmPath,
            new ProcessArgumentBuilder()
                    .Append("install")
                    .Append("--prefix")
                    .AppendQuoted(ngDirectoryPath.FullPath)
                    .Append("@angular/cli")
        );
        ngPath = Context.Tools.Resolve("ng.cmd");
    });
    
    Task("Clean")
        .Does( ()=> {
            CleanDirectory(outputPath);
    });
    
    Task("Install")
        .IsDependentOn("Clean")
        .Does( ()=> {
        Cmd(npmPath,
            new ProcessArgumentBuilder()
                .Append("install")
        );
    });
    
    Task("Build")
        .IsDependentOn("Install-AngularCLI")
        .IsDependentOn("Install")
        .Does( ()=> {
        Cmd(ngPath,
            new ProcessArgumentBuilder()
                .Append("build")
                .Append("--output-path")
                .AppendQuoted(outputPath.FullPath)
        );
    });
    
    RunTarget(target);
    

    Which basically does

    1. Install Angular CLI If not found
    2. Install node modules
    3. Build Angular app

    If you want to run the build and publish on kudu you can use the Cake.Kudu addin and KuduSync tool, dependencies are declared by adding tool and addin pre-processor directives like this:

    #tool nuget:?package=KuduSync.NET&version=1.3.1
    #addin nuget:?package=Cake.Kudu&version=0.6.0
    

    And the publish task would look something like this

    Task("Publish")
        .IsDependentOn("Build")
        .Does( ()=> {
          Kudu.Sync(outputPath);
    });
    

    For kudu to know it should use a custom deployment script you need to add a .deployment file telling it do so, which could look something like this:

    [config]
    command = deploy.cmd
    

    And a custom boostrapper to install Cake i the kudu environment which could look something like this:

    @ECHO OFF
    REM SET Cake
    SET CAKE_VERSION=0.23.0
    SET CAKE_FOLDER=Cake.%CAKE_VERSION%
    SET PATH=%~dp0\Tools;%PATH%
    
    REM Cleanup any old Cake versions
    FOR /f "delims=" %%c IN ('dir /AD /B "Tools\Cake*"') DO (
            IF NOT "%%c" == "%CAKE_FOLDER%" (RD /S /Q "Tools\%%c")
    )
    
    REM Install Dependencies
    IF NOT EXIST "Tools" (md "Tools")
    IF NOT EXIST "Tools\Addins" (md "Tools\Addins")
    IF NOT EXIST "Tools\%CAKE_FOLDER%\Cake.exe" (
        echo Downloading Cake %CAKE_VERSION%
        nuget install Cake -Version %CAKE_VERSION% -OutputDirectory "Tools" -Source https://api.nuget.org/v3/index.json
        )
    
    REM Execute deploy
    Tools\%CAKE_FOLDER%\Cake.exe -version
    Tools\%CAKE_FOLDER%\Cake.exe build.cake --Target="Publish"
    

    Which basically just cleans up any old Cake versions and fetches 0.23.0 if not already installed.

    The complete Cake script would look something like below

    #tool nuget:?package=KuduSync.NET&version=1.3.1
    #addin nuget:?package=Cake.Kudu&version=0.6.0
        string          target      = Argument("target", "Build");
        FilePath        ngPath      = Context.Tools.Resolve("ng.cmd");
        FilePath        npmPath     = Context.Tools.Resolve("npm.cmd");
        DirectoryPath   outputPath  = MakeAbsolute(Directory("./output"));
    
        Action<FilePath, ProcessArgumentBuilder> Cmd => (path, args) => {
            var result = StartProcess(
                path,
                new ProcessSettings {
                    Arguments = args
                });
    
            if(0 != result)
            {
                throw new Exception($"Failed to execute tool {path.GetFilename()} ({result})");
            }
        };
    
        Task("Install-AngularCLI")
            .Does(() => {
            if (ngPath != null && FileExists(ngPath))
            {
                Information("Found Angular CLI at {0}.", ngPath);
                return;
            }
    
            DirectoryPath ngDirectoryPath = MakeAbsolute(Directory("./Tools/ng"));
    
            EnsureDirectoryExists(ngDirectoryPath);
    
            Cmd(npmPath,
                new ProcessArgumentBuilder()
                        .Append("install")
                        .Append("--prefix")
                        .AppendQuoted(ngDirectoryPath.FullPath)
                        .Append("@angular/cli")
            );
            ngPath = Context.Tools.Resolve("ng.cmd");
        });
    
        Task("Clean")
            .Does( ()=> {
                CleanDirectory(outputPath);
        });
    
        Task("Install")
            .IsDependentOn("Clean")
            .Does( ()=> {
            Cmd(npmPath,
                new ProcessArgumentBuilder()
                    .Append("install")
            );
        });
    
        Task("Build")
            .IsDependentOn("Install-AngularCLI")
            .IsDependentOn("Install")
            .Does( ()=> {
            Cmd(ngPath,
                new ProcessArgumentBuilder()
                    .Append("build")
                    .Append("--output-path")
                    .AppendQuoted(outputPath.FullPath)
            );
        });
    
    Task("Publish")
        .IsDependentOn("Build")
        .Does( ()=> {
        Kudu.Sync(outputPath);
    });
    
    RunTarget(target);
    

    And output on the Kudu build running on your Azure web site would look something like this

                 +##   #;;'
                 #;;#  .+;;;;+,
                 '+;;#;,+';;;;;'#.
                 ++'''';;;;;;;;;;# ;#;
                ##';;;;++'+#;;;;;'.   `#:
             ;#   '+'';;;;;;;;;'#`       #.
          `#,        .'++;;;;;':..........#
        '+      `.........';;;;':.........#
       #..................+;;;;;':........#
       #..................#';;;;;'+''''''.#
       #.......,:;''''''''##';;;;;'+'''''#,
       #''''''''''''''''''###';;;;;;+''''#
       #''''''''''''''''''####';;;;;;#'''#
       #''''''''''''''''''#####';;;;;;#''#
       #''''''''''''''''''######';;;;;;#'#
       #''''''''''''''''''#######';;;;;;##
       #''''''''''''''''''########';;;;;;#
       #''''''''''''++####+;#######';;;;;;#
       #+####':,`             ,#####';;;;;;'
                                  +##'''''+.
       ___      _          ___       _ _     _
      / __\__ _| | _____  / __\_   _(_) | __| |
     / /  / _` | |/ / _ \/__\// | | | | |/ _` |
    / /___ (_| |   <  __/ \/  \ |_| | | | (_| |
    \____/\__,_|_|\_\___\_____/\__,_|_|_|\__,_|
    
                                 Version 0.23.0+Branch.main.Sha.67afe72f1c21a8a3cfd96d3969fb2591d62f37ff
    
    
    ========================================
    Install-AngularCLI
    ========================================
    Found Angular CLI at D:/home/site/repository/tools/ng/ng.cmd.
    
    ========================================
    Clean
    ========================================
    
    ========================================
    Install
    ========================================
    npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^1.0.0 (node_modules\chokidar\node_modules\fsevents):
    npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"ia32"})
    npm WARN codelyzer@3.2.2 requires a peer of @angular/compiler@^2.3.1 || >=4.0.0-beta <5.0.0 but none was installed.
    npm WARN codelyzer@3.2.2 requires a peer of @angular/core@^2.3.1 || >=4.0.0-beta <5.0.0 but none was installed.
    
    ========================================
    Build
    ========================================
    Date: 2017-11-17T10:36:45.847Z
    Hash: 3b11c732f8aa65f3a08c
    Time: 8815ms
    chunk {inline} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
    chunk {main} main.bundle.js, main.bundle.js.map (main) 7.79 kB [initial] [rendered]
    chunk {polyfills} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 200 kB [initial] [rendered]
    chunk {styles} styles.bundle.js, styles.bundle.js.map (styles) 11.3 kB [initial] [rendered]
    chunk {vendor} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.29 MB [initial] [rendered]
    
    ========================================
    Publish
    ========================================
    KuduSync.NET from: 'D:\home\site\repository\output' to: 'D:\home\site\wwwroot'
    Copying file: 'favicon.ico'
    Copying file: 'index.html'
    Copying file: 'inline.bundle.js'
    Copying file: 'inline.bundle.js.map'
    Copying file: 'main.bundle.js'
    Copying file: 'main.bundle.js.map'
    Copying file: 'polyfills.bundle.js'
    Copying file: 'polyfills.bundle.js.map'
    Copying file: 'styles.bundle.js'
    Copying file: 'styles.bundle.js.map'
    Copying file: 'vendor.bundle.js'
    Copying file: 'vendor.bundle.js.map'
    Time 444
    
    Task                          Duration            
    --------------------------------------------------
    Install-AngularCLI            00:00:00.0491433    
    Clean                         00:00:00.0782836    
    Install                       00:00:35.4828120    
    Build                         00:01:12.5709830    
    Publish                       00:00:00.8032134    
    --------------------------------------------------
    Total:                        00:01:48.9844353    
    

    In the last comment you write that you want to deploy using Octopus deploy, that would basically mean adding 2 new tasks to just like the kudu publish execute post build.

    1. Package up the artifact
    2. Push to Octopus server

    For this Cake has the OctoPack and OctoPush aliases.

    These aliases require the octo.exe tool, which can be fetch using the tool directive like this

    #tool nuget:?package=OctopusTools&version=4.25.0
    

    A pack task could look something like this

        DirectoryPath   nugetPath= MakeAbsolute(Directory("./nuget"));
    Task("Pack")
        .IsDependentOn("Build")
        .Does( ()=> {
        OctoPack(
            "PoCApp",
            new OctopusPackSettings {
                BasePath = outputPath,
                OutFolder = nugetPath,
                Format = OctopusPackFormat.Zip
            }
        );
    });
    

    Once you packaged your app and pushed package to our octopus server, then you can use their built-in deploy to Azure web app functionality, there's a good guide for that at https://octopus.com/docs/deploying-applications/deploying-to-azure/deploying-a-package-to-an-azure-web-app

    Some of the above code is available in the below repo https://github.com/azurevoodoo/NGAppDeployedWithKuduPoC