Package Types for Business Central Artifacts – CI / CD 7

Package Types for Business Central Artifacts – CI / CD 7

In my last CI / CD post I promised to describe: “… why I use Universal Packages for my artifacts”.

To explain this, I must answer the following question: What kind of artifacts do I have in my Microsoft Dynamics 365 Business Central projects?

Well, my answer is simple: I usually have to manage files in my CD process… 😮

Simple Files

My CI build pipelines compile and create my Apps-Files. In case of Hybrid-Development, my Artifacts are also FOB-Files. During my build process I store such Artifacts automatically in my Azure DevOps Artifact Feeds:

Modern products are also provided as Apps and for older BC Versions delivered as FOB’s. Normally, I upload this Artifacts manually to my Artifact Feed.

You also know that Business Central provide the data migration to import customer data. In addition, it is also common to set up a company by using the Rapid Start Service.

In some special cases we need also to integrate Database backup files in our CI / CD process. Consequently, all this files are Artifacts and I store them (mostly manually) in my Feed.

In short, I need to upload the following files to my Artifact Feed:

  • Generated Files (Apps + FOB’s) from CI Pipelines
  • External Artifacts like Products (Apps + FOB’s)
  • Data in the form of RapidStart packages
  • Database backup files (bak / bacpac)

Most importantly, the manual upload and download must be simple and easy.

Packages

Azure DevOps provide a lot of package formats. In my opinion, the following package types work best for Microsoft Dynamics 365 Business Central Artifacts (Apps files, FOB files, and RapidStart packages):

  • Universal Packages
  • NuGet

Universal Packages

My favorit are Universal Packages because they are extremely simple. This package type is like a zip file. Ok, it’s simplicity is also it’s disadvantage. I can’t define dependencies to other packages.

On the other hand, my manual upload or download of my Artifacts with Azure DevOps CLI is very easy. Azure DevOps provides the needed commands (“Artifacts” > “Connect to feed”) and this is a clear advantage to me.

I use these commands to upload external Apps and RapidStart packages. As a result, this Artifacts are available for my CI / CD pipelines and I can deploy such Artifacts automatically to the target environment.

Sometimes in the development process, I need to install manually a special App version into my Docker Container. Then, I use the available download command via copy & paste for the command line:

Note, there are some prerequisites:
Before you can use this commands, you must first install some tools. Let me share my PowerShell script with you to install these prerequisite tools to upload / download Universal Packages by Command Line:

# Install Choco
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
# Install UPack
choco install -y upack
choco upgrade -y upack
# Install Azure CLI
choco install -y azure-cli 
choco upgrade -y azure-cli
# Reload the PATH variable
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine")
# Install Azure CLI Extension for Azure-DevOps
az extension add --name azure-devops

NuGet Packages

Another popular package type are NuGet Packages. In my opinion more powerful but also a bit more complex. I need a .nuspec file to define my content and specify dependencies to other packages. Finally, I use the tool NuGet.exe to create my package based on the definition.

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <!-- Required elements-->
        <id>al-project-template</id>
        <version>$version$</version>
        <description>Contract First Design App for SCM</description>
        <authors>Michael Megel</authors>
    </metadata>
    <files>
        <file src="app\**\*.app" target="content"/>
    </files>
</package>

Azure Artifacts also allow the manual upload and download NuGet Packages. In this case I use to tool NuGet.exe instead of Azure CLI with Universal Packages.

The biggest advantage is the dependency definition with nuspec-files. Here I can define dependant packages – in my case Apps – as in the app.json file:

<dependencies>
    <dependency id="contract" version="1.0.30" />
    <dependency id="fin" version="1.0.2" />
</dependencies>

Package Name

Package names have some limitations like MSDyn365BC Application names in Azure DevOps. You may not be able to use the full name of the app for a package.

In my case I use Universal Packages [Manifest Spec.]. Therefor, my package name must have one to fifty characters, consisting of:

  • numbers (0-9)
  • upper- and lower-case letters (a-z)
  • dashes (-)
  • periods (.)
  • underscores (_)

Note (Azure CLI): “… Universal package names must be one or more lowercase alphanumeric segments separated by a dash, dot or underscore. The package name must be under 256 characters.”

To clarify, my package name should be similar to my App-Name from app.json. But, this is not possible due to restrictions. Therefor, I have decided to replace non matching characters with an regular expression in PowerShell as follows:

$appJson     = ((Get-Item "$($env:PATH_TO_APP_JSON)" -ErrorAction SilentlyContinue) | Get-Content -ErrorAction SilentlyContinue | ConvertFrom-Json -ErrorAction SilentlyContinue)
$packageName = [Regex]::Replace($appJson.Name, "[^0-9a-zA-Z]+", "_").ToLower()

Note: “PATH_TO_APP_JSON” is a variable in my CI pipeline.

For instance, my App “SCM” have a manifest as follows:

{
    "id":  "4c6fd911-e3f5-48db-a5ad-9e66ff96d1f8",
    "name":  "SCM",
    "publisher":  "My Company",
    "version":  "1.0.0.0",
    "brief":  "Contract-First-Design [Contract] App"
}

Therefor, the corresponding package name for my Universal Package is “scm”.

I don’t include the publisher name, because of limited length of the package name.

Package Dependencies

As you know, I prefer Universal Packages for their simplicity. I can’t define dependencies with packages. As result, I resolve dependent packages from app.json dependencies by name manually or automatically in CI pipelines.

Here is an example of my manual dependency definition for one of my CI pipelines:

# ... Prepare the Container ...

# Import the App 'Contract'
- template:         'template.steps.importBC15.yaml'
  parameters:
    PackageName:    contact
    View:           QA

# Import the App 'FIN'
- template:         'template.steps.importBC15.yaml'
  parameters:
    PackageName:    fin
    View:           QA

# ... Compile the App ...

NuGet Packages instead provide this feature and you can download all dependencies automatically from Artifact Feed. I know, that Kamil Sacek set up it’s DevOps Projects in this way and might have shared some examples.

Conclusion

To summarize, Microsoft Dynamics 365 Business Central Artifacts are usually files. Azure DevOps provide at least 2 package types (Universal & NuGet Packages) to store such files in an Artifact Feed.

Further, dependency management for Apps is important for automated processes. Therefor, it is necessary to have a clear naming concept for your Artifacts.

In addition, the used package type must provide a simple way to upload and download Artifacts manually. This is important during my development, to manage products, and data like RapidStart packages.

Finally, my preferred package type is Universal Packages even though NuGet Packages offer automatic dependency download. I like the simplicity of Universal Packages like a “Zip-Archive” to store files.

What’s Next?

In my next post I’ll dive into Artifact Feeds and Views, and describe how I realize the staging of my Artifacts.

In the meanwhile, I want your feedback! Which package format do you prefer for your Artifacts and How do you organize your packages?

… Happy Sharing and don’t forget #NeverStopLearning

Share

3 thoughts on “Package Types for Business Central Artifacts – CI / CD 7

Leave a Reply

Your email address will not be published. Required fields are marked *