Continous integration and Continues deployment are common practice for some years now. Most of the pipelines using docker environments, this is common practice for automated tests, but the start of containers is consuming some time. The container is used to get the symbols for Microsoft Apps in the right version, and mostly to get the compiler. At least for the compiler we had also the possibilty to get it by downloading the AL VS Code extension and extract the files from there.
But shouldn’t there be an easier solution to just get the compiler and symbols?
Well there is (at least in beta state for now) a new way how to get the ressources. Microsoft is working on a NuGet strategy for Business Central, which includes symbols for Microsoft Apps and AppSource Apps. And what I found some days ago make this even better. Microsoft released the Business Central compiler as beta on a public Nuget feed
https://www.nuget.org/packages/Microsoft.Dynamics.BusinessCentral.Development.Tools/
The basics about NuGet
NuGet is a widely-used package manager for the .NET ecosystem, streamlining the process of managing external libraries, tools, and dependencies. It allows developers to search for, download, and integrate third-party packages into their projects easily. Hosted on repositories like nuget.org, it provides access to thousands of pre-built libraries and tools. NuGet packages come with metadata that include versioning, license information, and, most importantly, dependencies.
One of the core features of NuGet is its dependency management system. When you install a NuGet package, it automatically resolves and installs all necessary dependencies required for that package to function. Each NuGet package can declare other packages as dependencies, and NuGet will ensure that all those packages, and their respective versions, are installed to avoid compatibility issues. If multiple packages share the same dependency but require different versions, NuGet handles this by using the most appropriate version or creating different project dependency sets to maintain stability. This feature is crucial for maintaining a reliable build environment, especially in large, complex solutions.
BCNuGet Strategy
When I think about NuGet in Business Central two people came to my mind Freddy Kristiansen (Microsoft Employee) and Kamil Sacek (Microsoft MVP), the both have shown very much details about the strategy on different events. There also some recordings online like from BCTechDays.
But here a short summary, basically the idea is to make ressource for compiling Business Central easier. So Microsoft decided to have 3 public NuGet feeds
Microsoft Apps
The feed include the full version of Microsoft apps and can also be used to install apps.
Microsoft Symbols
This feed includes only the symbols from Microsoft apps which could be used for developing and compiling
AppSource Symbols
This feed incluedes symbols of every AppSource app and makes the live of developers easier, but if you want to execute tests you have to either use SaaS sandbox or you have to ask the publisher of the app for the full version of it (Maybe they have also a nuget feed)
The project setup
For my scenario I created an app using VSCode
Here is the app.json for it
{
"id": "6c8920e8-bc4d-4d3e-b455-c193bfddb861",
"name": "BCNuget",
"publisher": "Default Publisher",
"version": "1.0.0.0",
"brief": "",
"description": "",
"privacyStatement": "",
"EULA": "",
"help": "",
"url": "",
"logo": "",
"dependencies": [
{
"id": "1b26c6f6-4689-4dc3-af55-f39e95a781f8",
"name": "COSMO Advanced Manufacturing Pack",
"publisher": "Cosmo Consult",
"version": "9.2.240755.0"
}
],
"screenshots": [],
"application": "25.0.23364.25340",
"idRanges": [
{
"from": 50100,
"to": 50149
}
],
"resourceExposurePolicy": {
"allowDebugging": true,
"allowDownloadingSource": true,
"includeSourceInSymbolFile": true
},
"runtime": "14.0",
"features": [
"NoImplicitWith"
]
}
Creating a pipeline on Azure DevOps
First we need to install the developer tools from NuGet, this work normally via the NuGet CLI with the following command
nuget.exe install Microsoft.Dynamics.BusinessCentral.Development.Tools -outputDirectory . -PreRelease
But at the moment this results in an error about missing dependency “Microsoft.Dynamics.Nav.Xrm.Common”, therefore I already created a GitHub Issue https://github.com/microsoft/AL/issues/7865
But you can also download the tools directly via nuget.org, the file is an archive with the ending nupkg. You can just extract files from there
Invoke-WebRequest https://www.nuget.org/api/v2/package/Microsoft.Dynamics.BusinessCentral.Development.Tools/15.0.17.43318-beta -OutFile microsoft.dynamics.businesscentral.development.tools.15.0.17.43318-beta.nupkg
Expand-Archive .\microsoft.dynamics.businesscentral.development.tools.15.0.17.43318-beta.nupkg
Lets start the yaml file
1) Get the tools
- task: NuGetToolInstaller@1
displayName: Install Nuget
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
Invoke-WebRequest https://www.nuget.org/api/v2/package/Microsoft.Dynamics.BusinessCentral.Development.Tools/15.0.17.43318-beta -OutFile .\alc.zip
Expand-Archive .\alc.zip
workingDirectory: '$(Agent.BuildDirectory)'
displayName: Get ALC Tools
2) Add NuGet Sources
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
nuget source add -Name MSSymbols -source "https://dynamicssmb2.pkgs.visualstudio.com/DynamicsBCPublicFeeds/_packaging/MSSymbols/nuget/v3/index.json"
nuget source add -Name AppSourceSymbols -source "https://dynamicssmb2.pkgs.visualstudio.com/DynamicsBCPublicFeeds/_packaging/AppSourceSymbols/nuget/v3/index.json"
workingDirectory: '$(Agent.BuildDirectory)'
displayName: Add Nuget Sources
3) Get NuGet packages
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
$ApplicationPackage="Microsoft.Application.symbols"
$ManifestObject = Get-Content $(Build.SourcesDirectory)\app\app.json -Encoding UTF8 | ConvertFrom-Json
$applicationVersion = $ManifestObject.Application
$packagecachepath="$(Agent.BuildDirectory)\alpackages"
mkdir $packagecachepath
nuget install $ApplicationPackage -version $applicationVersion -outputDirectory $packagecachepath
foreach ($Dependency in $ManifestObject.dependencies) {
$PackageName = ("{0}.{1}.symbols.{2}" -f $Dependency.publisher, $Dependency.name, $Dependency.id ) -replace ' ', ''
Write-Host "Get $PackageName"
nuget install $PackageName -version $Dependency.version -outputDirectory $packagecachepath
}
workingDirectory: '$(Agent.BuildDirectory)'
displayName: Get Nuget Dependencies
4) Compile
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
$ManifestObject = Get-Content $(Build.SourcesDirectory)\app\app.json -Encoding UTF8 | ConvertFrom-Json
$packagecachepath="$(Agent.BuildDirectory)\alpackages"
$ParametersList = @()
$AppFileName = (("{0}_{1}_{2}.app" -f $ManifestObject.publisher, $ManifestObject.name, $ManifestObject.version).Split([System.IO.Path]::GetInvalidFileNameChars()) -join '')
$ParametersList += @(("/project:`"$(Build.SourcesDirectory)\app`" "))
$ParametersList += @(("/packagecachepath:$packagecachepath"))
$ParametersList += @(("/out:`"{0}`"" -f "$(Build.ArtifactStagingDirectory)\$AppFileName"))
$ParametersList += @(("/loglevel:Warning"))
Write-Host "Using parameters:"
foreach ($Parameter in $ParametersList) {
Write-Host " $($Parameter)"
}
Push-Location
Set-Location .\alc\Tools\net8.0\any
.\alc.exe $ParametersList
Pop-Location
workingDirectory: '$(Agent.BuildDirectory)'
displayName: Compile
5) Run Pipeline
You can find the whole example here
https://dev.azure.com/BCNuGetPipelineExample/_git/BCNugetExample?path=/.devops/azure-pipelines.yml
Conclusion
This is a fully working proof of concept, but I would not recommend it to use it as it is, because it does not include running tests. To run tests you need to have a running environment.
And of course if you have many projects I would suggest you to use a managed CI/CD Solution. There are couple of tools like COSMO Alpaca, AL Ops or AL:Go for GitHub