Pipelines/DevOps-Pipeline.ps1

function Invoke-DevOpsPipeline {
    Param(
        [Parameter(Mandatory=$false)]
        [ValidateSet('AzureDevOps','GithubActions','GitLab')]
        [string] $environment = 'AzureDevOps',
        [Parameter(Mandatory=$true)]
        [string] $version,
        [Parameter(Mandatory=$false)]
        [int] $appBuild = 0,
        [Parameter(Mandatory=$false)]
        [int] $appRevision = 0
    )

    if ($environment -eq "AzureDevOps") {
        $buildArtifactFolder = $ENV:BUILD_ARTIFACTSTAGINGDIRECTORY
    }
    elseif ($environment -eq "GitHubActions") {
        $buildArtifactFolder = Join-Path $ENV:GITHUB_WORKSPACE "output"
        New-Item $buildArtifactFolder -ItemType Directory | Out-Null
    }

    if ($env:APPFOLDER) {
        $baseFolder = Join-Path $env:SYSTEM_DEFAULTWORKINGDIRECTORY $env:APPFOLDER
    } else {
        $baseFolder = $env:SYSTEM_DEFAULTWORKINGDIRECTORY
    }

    if ($env:SETTINGSFILE) {
        $settingsFile = Join-Path $baseFolder $env:SETTINGSFILE
    } else {
        $settingsFile = Join-Path $baseFolder "settings.json"
    }

    Invoke-ReadSettings -environment $environment -version $version -settingsFile $settingsFile
    Install-BcContainerHelper -bcContainerHelperVersion $settings.bcContainerHelperVersion -genericImageName $settings.genericImageName

    $authContext = $null
    $refreshToken = "$($ENV:BcSaasRefreshToken)"
    $environmentName = "$($ENV:EnvironmentName)"
    if ($refreshToken -and $environmentName) {
        $authContext = New-BcAuthContext -refreshToken $refreshToken
        if (Get-BcEnvironments -bcAuthContext $authContext | Where-Object { $_.Name -eq $environmentName -and  $_.type -eq "Sandbox" }) {
            Remove-BcEnvironment -bcAuthContext $authContext -environment $environmentName
        }
        $countryCode = $artifact.Split('/')[3]
        New-BcEnvironment -bcAuthContext $authContext -environment $environmentName -countryCode $countrycode -environmentType "Sandbox" | Out-Null
        do {
            Start-Sleep -Seconds 10
            $baseApp = Get-BcPublishedApps -bcAuthContext $authContext -environment $environmentName | Where-Object { $_.Name -eq "Base Application" }
        } while (!($baseApp))
        $baseapp | Out-Host

        $artifact = Get-BCArtifactUrl `
            -country $countryCode `
            -version $baseApp.Version `
            -select Closest

        if ($artifact) {
            Write-Host "Using Artifacts: $artifact"
        }
        else {
            throw "No artifacts available"
        }
    }

    $params = @{}
    $licenseFile = "$ENV:licenseFile"

    # $codeSigncertPfxFile = "$ENV:CodeSignCertPfxFile"
    # if (!$settings.doNotSignApps -and $codeSigncertPfxFile) {
    # if ("$ENV:CodeSignCertPfxPassword" -ne "") {
    # $codeSignCertPfxPassword = try { "$ENV:CodeSignCertPfxPassword" | ConvertTo-SecureString } catch { ConvertTo-SecureString -String "$ENV:CodeSignCertPfxPassword" -AsPlainText -Force }
    # $params = @{
    # "codeSignCertPfxFile" = $codeSignCertPfxFile
    # "codeSignCertPfxPassword" = $codeSignCertPfxPassword
    # }
    # }
    # else {
    # $codeSignCertPfxPassword = $null
    # }
    # }

    $allTestResults = "testresults*.xml"
    $testResultsFile = Join-Path $baseFolder "TestResults.xml"
    $testResultsFiles = Join-Path $baseFolder $allTestResults
    if (Test-Path $testResultsFiles) {
        Remove-Item $testResultsFiles -Force
    }

    if($settings.copyLibraries){
        $splitString = $artifact -split '/'
        $paramNames = @("storageAccount", "type", "version", "country", "select")
        $parameters = @{}

        for ($i=0; $i -lt $splitString.Length; $i++) {
            if ($splitString[$i]) {
                $parameters[$paramNames[$i]] = $splitString[$i]
            }
        }
        $url = Get-BCArtifactUrl @parameters
        $versionPart = ($url -split '/')[4]
        $version = ($versionPart -split '\.')[0]
        $NewBcContainerScript = { Param([Hashtable]$parameters)
            New-BcContainer @parameters;
            Invoke-ScriptInBcContainer $parameters.ContainerName -scriptblock { $progressPreference = 'SilentlyContinue' };
            foreach($library in $settings.copyLibraries){
                $localPath = Join-Path $baseFolder "$library"
                $fileName = Split-Path "$library" -Leaf
                $containerPath = "C:\Program Files\Microsoft Dynamics NAV\$($version)0\Service\Add-Ins\$fileName" # TODO just find the single folder instead of searching version, idea: https://github.com/microsoft/navcontainerhelper/blob/86a6e14b5329e151d73d3b6594d0465e949cdcdd/ObjectHandling/Import-ObjectsToNavContainer.ps1#L49
                Copy-FileToNavContainer -containerName $parameters.ContainerName -localPath "$localPath" -containerPath "$containerPath"
            }

            Restart-BcContainer $parameters.ContainerName;
        }
        $params += @{ "NewBcContainer" = $NewBcContainerScript }
    }

    # Get packages from NuGet >>
    if ($settings.additionalNuGetFeeds) {
        $bcContainerHelperConfig.TrustedNuGetFeeds = @()
        $settings.additionalNuGetFeeds | ForEach-Object {
            if (($_ -eq "latest") -and ($settings.nugetFeedUrlForLatest)) {
                $bcContainerHelperConfig.TrustedNuGetFeeds += [PSCustomObject]@{ "Url" = $settings.nugetFeedUrlForLatest; "Token" = $ENV:SystemAccessToken }
            } elseif (($_ -eq "release") -and ($settings.nugetFeedUrlForRelease)) {
                $bcContainerHelperConfig.TrustedNuGetFeeds += [PSCustomObject]@{ "Url" = $settings.nugetFeedUrlForRelease; "Token" = $ENV:SystemAccessToken }
            } else {
                $bcContainerHelperConfig.TrustedNuGetFeeds += [PSCustomObject]@{ "Url" = $_.Trim(); "Token" = $ENV:SystemAccessToken }
            }
        }
    }

    $params += @{
        "InstallMissingDependencies" = {
            Param([Hashtable]$parameters)
            $parameters.missingDependencies | ForEach-Object {
                $appid = $_.Split(':')[0]
                $appName = $_.Split(':')[1]
                $version = $appName.SubString($appName.LastIndexOf('_')+1)
                $version = [System.Version]$version.SubString(0,$version.Length-4)
                $publishParams = @{
                    "packageName" = $appId
                    "version" = $version
                }
                if ($parameters.ContainsKey('CopyInstalledAppsToFolder')) {
                    $publishParams += @{
                        "CopyInstalledAppsToFolder" = $parameters.CopyInstalledAppsToFolder
                    }
                }
                if ($parameters.ContainsKey('containerName')) {
                    Publish-BcNuGetPackageToContainer -containerName $parameters.containerName -tenant $parameters.tenant -skipVerification -appSymbolsFolder $parameters.appSymbolsFolder @publishParams -ErrorAction SilentlyContinue
                }
                else {
                    Download-BcNuGetPackageToFolder -folder $parameters.appSymbolsFolder @publishParams | Out-Null
                }
            }
        }
    }
    # Get packages from NuGet <<

    $params += @{
        "GetBcContainerAppRuntimePackage" = {
            Param([Hashtable]$parameters)
            if ($settings.IncludeSourceInRuntimePackage) {
                $parameters["IncludeSourceInPackageFile"] = $true
            }
            Get-BcContainerAppRuntimePackage @parameters
        }
    }

    if($settings.enableLinterCop) {
        $params += @{ "CustomCodeCops" = "https://github.com/StefanMaron/BusinessCentral.LinterCop/releases/latest/download/BusinessCentral.LinterCop.dll" }
    }

    Run-AlPipeline @params `
        -pipelinename $pipelineName `
        -containerName $containerName `
        -imageName $imageName `
        -bcAuthContext $authContext `
        -environment $environmentName `
        -artifact $artifact `
        -accept_insiderEula `
        -memoryLimit $settings.memoryLimit `
        -baseFolder $baseFolder `
        -licenseFile $LicenseFile `
        -installApps $installApps `
        -previousApps $previousApps `
        -appFolders $settings.appFolders `
        -testFolders $settings.testFolders `
        -installTestApps $settings.installTestApps `
        -bcptTestFolders $settings.bcptTestFolders `
        -useCompilerFolder:$settings.useCompilerFolder `
        -doNotPublishApps:$settings.doNotPublishApps `
        -doNotRunTests:$settings.doNotRunTests `
        -doNotRunBcptTests:$settings.doNotRunBcptTests `
        -testResultsFile $testResultsFile `
        -testResultsFormat 'JUnit' `
        -installTestRunner:$settings.installTestRunner `
        -installTestFramework:$settings.installTestFramework `
        -installTestLibraries:$settings.installTestLibraries `
        -installPerformanceToolkit:$settings.installPerformanceToolkit `
        -companyName $settings.companyNameForTests `
        -enableCodeCop:$settings.enableCodeCop `
        -enableAppSourceCop:$settings.enableAppSourceCop `
        -enablePerTenantExtensionCop:$settings.enablePerTenantExtensionCop `
        -enableUICop:$settings.enableUICop `
        -useDefaultAppSourceRuleSet:$settings.useDefaultAppSourceRuleSet `
        -rulesetFile:$settings.rulesetFile `
        -enableExternalRulesets `
        -azureDevOps `
        -failOn 'error' `
        -AppSourceCopMandatoryAffixes $settings.appSourceCopMandatoryAffixes `
        -AppSourceCopSupportedCountries $settings.appSourceCopSupportedCountries `
        -additionalCountries $settings.additionalCountries `
        -buildArtifactFolder $buildArtifactFolder `
        -CreateRuntimePackages:$settings.CreateRuntimePackages `
        -appBuild $appBuild -appRevision $appRevision `
        -applicationInsightsConnectionString "$($ENV:applicationInsightsConnectionString)"`
        -SourceRepositoryUrl $env:BUILD_REPOSITORY_URI `
        -SourceCommit $env:BUILD_SOURCEVERSION `
        -BuildBy 'SMART-BcBuildHelper and BcContainerHelper' `
        -BuildUrl ($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + $env:SYSTEM_TEAMPROJECT + "/_build/results?buildId=" + $env:BUILD_BUILDID)


    if ($environment -eq 'AzureDevOps') {
        Write-Host "##vso[task.setvariable variable=TestResults]$allTestResults"
    }
}