Compile your AL project without docker
Introduction
As I’ve setup a GitHub workflow for e2e testing BC container APIs via newman-cli (postman) last weekend, I used the cmdlet New-BcContainer
from the BcContainerHelper1 module to provide a BC container to test against. Nothing too special at this point.
I thought to myself: “Why do we need BC containers to compile our AL project?”. At least this is what I see in many build pipelines: creating a BC container to compile the application.
So let’s just compile without containers.
Getting started
Before we can use the Compile-AppWithBcCompilerFolder.ps1
cmdlet2 we have to make sure that we can provide it with a compilerFolder path.
Create a compiler folder
Create the compiler folder by executing the New-BcCompilerFolder.ps1
3 cmdlet with some additional parameters such as the artifactUrl.
New-BcCompilerFolder `
-artifactUrl (Get-BCArtifactUrl -type Sandbox -country w1 -version 20)
Adjust the Get-BCArtifactUrl
parameters to your project needs.
The compilerFolder consists of the following structure:
compilerFolder
├── compiler
| ├── \_rels
| ├── extension
| ├── package
| ├── \[Content\_Types].xml
| └── extension.vsixmanifest
├── dlls
| ├── Mock Assemblies
| ├── OpenXML
│ └── Service
└── symbols
├── Microsoft_Application_xx.x.xxxxx.xxxx.app
├── Microsoft_Application_xx.x.xxxxx.xxxx.json
├── Microsoft_Base Application_xx.x.xxxxx.xxxx.app
├── Microsoft_Base Application_xx.x.xxxxx.xxxx.app.json
├── Microsoft_..._xx.x.xxxxx.xxxx.app
├── Microsoft_..._xx.x.xxxxx.xxxx.app.json
├── System.app
└── System.app.json
The compilerFolder is created in the C:\ProgramData\BcContainerHelper\compiler directory by default. Obviously we don’t have to create the compiler folder every single time. Save the path to the compiler folder or cache the entire folder within your pipeline.
Compile the app with the compiler folder
We can now use the Compile-AppWithBcCompilerFolder
2 cmdlet to use the compiler folder instead of creating a docker container for app compilation.
Compile-AppWithBcCompilerFolder `
-compilerFolder $compilerFolder `
-appProjectFolder C:\code\AL\JustAnotterApp
The app file is created by default in the output folder in our app project folder after successful compilation.
If you are using AL-RunPipeline
4 in your pipelines, you might want to have a look at the useCompilerFolder
argument.
Breaking Changes
Although this section could be placed in another blog post, I decided it is somewhat related. Some build pipelines would not only compile but also publish and install the app to the container, which again is not primarily the purpose of a build pipeline.
So… AppSourceCop to the rescue!
I’ve prepared a small app which has a dependency to the Base App v20.0. It contains a table object with 2 fields and also a ruleset file to ignore some of the AppSourceCop rules.
Running the following script…
$compilerFolder = New-BcCompilerFolder -artifactUrl (Get-BCArtifactUrl -type Sandbox -country w1 -version 20)
$projectFolder = 'C:\code\AL\JustAnotterApp'
Compile-AppWithBcCompilerFolder `
-compilerFolder $compilerFolder `
-appProjectFolder $projectFolder `
-CopyAppToSymbolsFolder `
-EnableAppSourceCop `
-rulesetFile $projectFolder\al.ruleset.json `
-FailOn error
…leads to this output:
The app has been created successfully.
Set version in AppSourceCop.json
We shipped our latest release. Partners and customers would publish and install the app (v0.0.0.0). How does the AppSourceCop know what the previous app is?
We can configure the AppSourceCop with the AppSourceCop.json5 file. Provide a version to check against. This version should be the latest published version of our app.
{
"version": "0.0.0.0",
"mandatorySuffix": "CB"
}
Download the specific app file to the symbols folder. As we set the CopyAppToSymbolsFolder in the Compile-AppWithBcCompilerFolder our previous app file (*v0.0.0.0) is already copied to the symbols folder.
Make a breaking change
To check if the breaking change is detected, we:
- remove the non primary key field from our table object
- increase the version of our app in our app.json file.
Running the script again…results in the following output:
Compilation of our app version 0.1.0.0 is not successful because it detects a breaking change: AS0002: The field with the name ‘Enabled’ and ID ‘2’ was found in the previous version, but is missing in the current extension. This might break the upgrade of existing installations.
Conclusion
The scripts shown in this example allow us to compile our app without a docker BC container.
- Pros
- faster build speed
- save resources
- runs on Linux
- Cons
- not able to run tests this way
- not able to test install/upgrade logic
If you have any questions or suggestions, feel free to contact me.
Hope you learned something.
See you around 🦦
-
https://www.powershellgallery.com/packages/BcContainerHelper ↩
-
https://github.com/microsoft/navcontainerhelper/blob/master/CompilerFolderHandling/Compile-AppWithBcCompilerFolder.ps1 ↩ ↩2
-
https://github.com/microsoft/navcontainerhelper/blob/master/CompilerFolderHandling/New-BcCompilerFolder.ps1 ↩
-
https://github.com/microsoft/navcontainerhelper/blob/master/AppHandling/Run-AlPipeline.ps1 ↩
-
https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/appsourcecop#configuration ↩