Compile your AL project without docker

3 minute read

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.ps13 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-AppWithBcCompilerFolder2 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-RunPipeline4 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:

Search for retention policy via tell me

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:

Search for retention policy via tell me

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 🦦