AL/Invoke-CompileApp.ps1

<#
 .Synopsis
  Compiles the app
 .Description
  Compiles the app for the current project and optionally updates the build number of the app and installs it
 .Parameter ContainerName
  Token to access Azure DevOps. Can be provided in settings.json
 .Parameter pfxFile
  Path to the code sign certificate
 .Parameter pfxPassword
  Password for the code sign certificate
 .Parameter SourcePath
  Path to the current project. If not defined, the current folder will be used
 .Parameter OutputPath
  Path where the compiled app will be saved
 .Parameter BuildNumber
  Defines the new build number that should be used to update the app. If not specified, the build numbers revision will be updated (major.minor.revision.build)
 .Parameter DoNotUpdateVersion
  Add this switch to not update the build number of the app
 .Parameter DoNotInstallApp
  Add this switch to skip the install of the compiled app
 .Example
  Invoke-CompileApp -pfxFile "C:\pfx.pfx" -pfxPassword (ConvertTo-SecureString "P@ssword1" -AsPlainText -Force) -OutputPath "C:\Output" -DoNotUpdateVersion
#>

function Invoke-CompileApp {
    Param(
        [Parameter(Mandatory=$false)]
        [string] $ContainerName,
        [Parameter(Mandatory=$false)]
        [string] $pfxCertificate = $null,
        [Parameter(Mandatory=$false)]
        [securestring] $pfxFile = $null,
        [Parameter(Mandatory=$false)]
        [securestring] $pfxPassword = $null,
        [Parameter(Mandatory=$false)]
        [string] $SourcePath = (Get-Location),
        [Parameter(Mandatory=$true)]
        [string] $OutputPath,
        [Parameter(Mandatory=$false)]
        [string] $BuildNumber = "",
        [switch] $DoNotUpdateVersion,
        [switch] $DoNotInstallApp
    )

    $ContainerName = Get-NewContainerName -SourcePath $SourcePath -ContainerName $ContainerName

    if (!(Test-Path $OutputPath)) {
        New-EmptyDirectory $OutputPath
    }
    if (!(Test-Path (Join-Path $OutputPath "app"))) {
        New-EmptyDirectory (Join-Path $OutputPath "app")
    }
    if (!(Test-Path (Join-Path $OutputPath "runtimeapp"))) {
        New-EmptyDirectory (Join-Path $OutputPath "runtimeapp")
    }
    if (!(Test-Path (Join-Path $OutputPath "testapp"))) {
        New-EmptyDirectory (Join-Path $OutputPath "testapp")
    }
    if (!(Test-Path (Join-Path $OutputPath "tests"))) {
        New-EmptyDirectory (Join-Path $OutputPath "tests")
    }

    Write-Output @'

     _____ _ _ _ _____ _ _
    | __ \ | | | (_) / ____| | | | |
    | | | | _____ ___ __ | | ___ __ _ __| |_ _ __ __ _ | (___ _ _ _ __ ___ | |__ ___ | |___
    | | | |/ _ \ \ /\ / / '_ \| |/ _ \ / _` |/ _` | | '_ \ / _` | \___ \| | | | '_ ` _ \| '_ \ / _ \| / __|
    | |__| | (_) \ V V /| | | | | (_) | (_| | (_| | | | | | (_| | ____) | |_| | | | | | | |_) | (_) | \__ \
    |_____/ \___/ \_/\_/ |_| |_|_|\___/ \__,_|\__,_|_|_| |_|\__, | |_____/ \__, |_| |_| |_|_.__/ \___/|_|___/
                                                             __/ | __/ |
                                                            |___/ |___/

'@


    [version]$platform = Get-AppKeyValue -SourcePath $SourcePath -KeyName 'platform'
    if ($platform.Major -ge 15) {
        Wait-ForTenantReady -containerName $ContainerName -tenant "default"
    }

    if ($null -eq $pfxCertificate -or $pfxCertificate -eq "") {
        if ($null -eq $pfxFile -or $pfxFile -eq "") {
            $pfxCertificate = (Get-PfxFile -publisher (Get-AppKeyValue -SourcePath $SourcePath -KeyName 'publisher'))
        }
        else {
            $pfxCertificate = ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pfxFile)))
        }
    }
    if ($null -eq $pfxPassword) {
        $pfxPassword = (Get-PfxPassword -publisher (Get-AppKeyValue -SourcePath $sourcePath -KeyName 'publisher'))
    }

    $CompilerPath = Get-CompilerFromContainer -ContainerName $ContainerName
    Get-AllSymbol -SourcePath $SourcePath -ContainerName $ContainerName

    #update the app version with the build number (actually store it in the Revision - the last element of the version no)
    if (!$DoNotUpdateVersion.IsPresent) {
        if ($BuildNumber -eq "") {
            throw "BuildNumber missing"
        }
        $AppVersion = [System.Version]::new((Get-AppKeyValue -SourcePath $SourcePath -KeyName 'version'))
        Set-AppKeyValue -SourcePath $SourcePath -KeyName 'version' -KeyValue ('{0}.{1}.{2}.{3}' -f $AppVersion.Major, $AppVersion.Minor, $AppVersion.Build, $BuildNumber)
    }

    try {
        Remove-Item (Join-Path $SourcePath '.altemplates') -Recurse -Force
        Remove-Item (Join-Path $SourcePath '.altestrunner') -Recurse -Force
    }
    catch {
        Write-Output "Did not remove folders"
    }

    Write-Output @'

     _____ _ _ _
    / ____| (_) (_) /\
   | | ___ _ __ ___ _ __ _| |_ _ __ __ _ / \ _ __ _ __
   | | / _ \| '_ ` _ \| '_ \| | | | '_ \ / _` | / /\ \ | '_ \| '_ \
   | |___| (_) | | | | | | |_) | | | | | | | (_| | / ____ \| |_) | |_) |
    \_____\___/|_| |_| |_| .__/|_|_|_|_| |_|\__, | /_/ \_\ .__/| .__/
                         | | __/ | | | | |
                         |_| |___/ |_| |_|

'@


    # first compile the app only (remove tests)
    $TestPath = New-TempDirectory
    Copy-Item "$SourcePath/*" $TestPath -Recurse

    # update dependencies
    Remove-ALTestDependencyList -sourcePath $SourcePath

    #set the target, if applicable
    if ((Get-EnvironmentKeyValue -SourcePath $SourcePath -KeyName target) -ne "") {
        Set-AppKeyValue -SourcePath $TestPath -KeyName target -KeyValue (Get-EnvironmentKeyValue -SourcePath $SourcePath -KeyName target)
    }

    Remove-Item (Join-Path $SourcePath 'Tests') -Recurse -Force

    if (!(Invoke-BuildALAppPackage -SourcePath $SourcePath -ContainerName $ContainerName -CompilerPath $CompilerPath -SignApp -DoNotDownloadSymbols -Install:(!$DoNotInstallApp.IsPresent) -pfxCertificate $pfxCertificate -pfxPassword $pfxPassword)){
        throw "App could not be built"
    }
    if (Test-Path "$SourcePath/Translations/*.g.xlf" -PathType Leaf) {
        Copy-Item "$SourcePath/Translations/*.g.xlf" -Destination "$TestPath/Translations/" -Force | Out-Null
    }
    Copy-Item "$SourcePath/*.app" "$OutputPath/app"
    Copy-Item "$SourcePath/*.app" "$TestPath/.alpackages"

    Write-Output @'

     _____ _ _ _ _______ _
    / ____| (_) (_) |__ __| | | /\
   | | ___ _ __ ___ _ __ _| |_ _ __ __ _ | | ___ ___| |_ / \ _ __ _ __
   | | / _ \| '_ ` _ \| '_ \| | | | '_ \ / _` | | |/ _ \/ __| __| / /\ \ | '_ \| '_ \
   | |___| (_) | | | | | | |_) | | | | | | | (_| | | | __/\__ \ |_ / ____ \| |_) | |_) |
    \_____\___/|_| |_| |_| .__/|_|_|_|_| |_|\__, | |_|\___||___/\__| /_/ \_\ .__/| .__/
                         | | __/ | | | | |
                         |_| |___/ |_| |_|

'@


    #build the test app
    $TestAppId = Get-EnvironmentKeyValue -SourcePath $SourcePath -KeyName 'testappid'
    if($null -eq $TestAppId) {
        $TestAppId = ([Guid]::NewGuid().Guid)
    }

    Set-AppKeyValue -SourcePath $TestPath -KeyName id -KeyValue $TestAppId
    Set-AppKeyValue -SourcePath $TestPath -KeyName name -KeyValue ('{0} Tests' -f (Get-AppKeyValue -SourcePath $TestPath -KeyName name))
    Set-AppKeyValue -SourcePath $TestPath -KeyName screenshots ''

    #add dependency on test symbols for test app
    [version]$BCFallPlatform = '15.0.0.0'
    [version]$platform = Get-AppKeyValue -SourcePath $TestPath -KeyName 'platform'
    if ($platform -ge $BCFallPlatform){
        Add-TestAppsToAppJson -SourcePath $TestPath
        Get-AllSymbol -SourcePath $TestPath -ContainerName $ContainerName
    }
    else{
        # for 2018 (and BC 14), it doesn't properly work to have "test" set, since it updates the symbols when importing the objects (due to possible base object compiles)
        if (Get-UseStandardTest -MajorVersion $platform.Major -SourcePath $TestPath) {
            Set-AppKeyValue -SourcePath $TestPath -KeyName 'test' -KeyValue (Get-AppKeyValue -KeyName 'application')
            Get-AllSymbol -SourcePath $TestPath -ContainerName $ContainerName -includeTestSymbols
        }
        else {
            if (($platform.Major -eq 14) -or ($platform.Major -eq 11)) {
                Set-AppKeyValue -SourcePath $TestPath -KeyName 'test' -KeyValue (Get-AppKeyValue -KeyName 'application')
            }
            else {
                Set-AppKeyValue -SourcePath $TestPath -KeyName 'test' -KeyValue ''
            }
            Get-AllSymbol -SourcePath $TestPath -ContainerName $ContainerName -includeTestSymbols
        }
    }

    New-ALAppDependency -SourcePath $TestPath `
                        -Id (Get-AppKeyValue -SourcePath $SourcePath -KeyName id) `
                        -Name (Get-AppKeyValue -SourcePath $SourcePath -KeyName name) `
                        -Publisher (Get-AppKeyValue -SourcePath $SourcePath -KeyName publisher) `
                        -Version (Get-AppKeyValue -SourcePath $SourcePath -KeyName version)

    #remove all directories from the test app apart from those beginning with . and the Logo and Tests folders
    Get-ChildItem $TestPath -Directory | Where-Object Name -NotLike '.*' | Where-Object Name -NotLike Tests | Where-Object Name -NotLike *Logo* | ForEach-Object {Remove-Item $_.FullName -Force -Recurse}

    #remove dotnet.al file for test app
    Get-ChildItem $TestPath | Where-Object Name -Like 'dotnet.al' | ForEach-Object {Remove-Item $_.FullName -Force -Recurse}

    if ((Get-EnvironmentKeyValue -KeyName 'tests') -eq 'skip') {
        if (!(Invoke-BuildALAppPackage -SourcePath $TestPath -ContainerName $ContainerName -CompilerPath $CompilerPath -DoNotDownloadSymbols -pfxFile $pfxFile -pfxPassword $pfxPassword)) {
            throw "test app could not be built"
        }
    }
    else {
        if (!(Invoke-BuildALAppPackage -SourcePath $TestPath -ContainerName $ContainerName -CompilerPath $CompilerPath -DoNotDownloadSymbols -Install:(!$DoNotInstallApp.IsPresent) -pfxFile $pfxFile -pfxPassword $pfxPassword)) {
            throw "test app could not be built"
        }
    }

    Write-Output @'

     _____ _
    / ____| |
   | | | | ___ __ _ _ __ _ _ _ __
   | | | |/ _ \/ _` | '_ \| | | | '_ \
   | |____| | __/ (_| | | | | |_| | |_) |
    \_____|_|\___|\__,_|_| |_|\__,_| .__/
                                   | |
                                   |_|

'@


    Copy-Item "$TestPath/*.app" (Join-Path $OutputPath "testapp")
    Remove-Item $TestPath -Recurse

    if ($CompilerPath -ne "") {
        Remove-Item (Split-Path (Split-Path (Split-Path (Split-path $CompilerPath -Parent) -Parent) -Parent) -Parent) -Recurse -Force -ErrorAction SilentlyContinue
    }
}
Export-ModuleMember Invoke-CompileApp