Pipelines/DevOps-Pipeline.ps1

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"
}

. (Join-Path $PSScriptRoot "Read-Settings.ps1") -environment $environment -version $version -settingsFile $settingsFile
. (Join-Path $PSScriptRoot "Install-BcContainerHelper.ps1") -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 <<

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 `
    -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"
}