microsoft / AL-Go

The plug-and-play DevOps solution for Business Central app development on GitHub
MIT License
293 stars 125 forks source link

Support NuGet packages for external dependencies #261

Open freddydk opened 2 years ago

freddydk commented 2 years ago

[!NOTE] Changes to the implementation made on December 18th 2023

Summary

Why NuGet?

Creating a Business Central specific package format, a package manager, integrating this in GitHub Packages and Azure Artifacts and potentially building a public feed for BC packages seems like overkill. Cheapest and best approach would be to find a suitable package format.

Azure Artifacts supports NuGet, Maven, npm, Gradle and Universal Packages. GitHub packages supports NuGet, Maven, npm, pip and RubyGems Nuget.org obviously supports NuGet

Even though Universal packages (from a technology standpoint) seems like the right choice, we should not settle on a format, which isn't natively supported by GitHub packages. Maven is mostly used for Java and doesn't seem like the right format.

npm is for node.js modules and NuGet is for dotnet packages, both formats could be used, but NuGet seems to be the easiest to work with wrt simplicity (just a .zip file) and access (public API for NuGet feed servers)

NuGet package format

The NuGet package format is described here: https://learn.microsoft.com/en-us/nuget/reference/nuspec

BcNuGet package format

A BcNuGet package contains exactly one .app file (or a runtime package)

A normal BcNuGet package is formattet like:

manifest.nuspec
filename.app

Sample manifest.nuspec:

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>FreddyKristiansen.BingMapsPTE.165d73c1-39a4-4fb6-85a5-925edc1684fb</id>
        <version>4.4.3.0</version>
        <title>BingMaps.PTE</title>
        <description>BingMaps Integration App with geocode functionality and map control</description>
        <authors>Freddy Kristiansen</authors>
        <repository type="git" url="https://github.com/microsoft/bcsamples-bingmaps.pte" />
        <dependencies>
            <dependency id="Microsoft.Application" version="20.1.39764.40432" />
            <dependency id="Microsoft.Platform" version="20.0.39668.40349" />
        </dependencies>
    </metadata>
    <files>
        <file src="Freddy%20Kristiansen_BingMaps.PTE_4.4.3.0.app" target="Freddy%20Kristiansen_BingMaps.PTE_4.4.3.0.app" />
    </files>
</package>

Must contain id, version, title, description and authors. For GitHub, repository should point to the owning repository of the package.

The default naming of nuget packages is Publisher.Name.AppId (where publisher and name are normalized - i.e. spaces, dots and special characters are removed, dashes are kept.)

Dependencies includes all dependencies (including Application dependency and Platform dependency)

Dependency resolution is done on the AppId only (if the package/dependency name ends with a GUID - considered to be the appid) - if the name does not end with an AppId, then the full name is used for dependency resolution)

Referencing a BcNuGet Package in your project

In app.json you include a reference to the app on which you have a dependency, like:

{
    "id":  "165d73c1-39a4-4fb6-85a5-925edc1684fb",
    "name":  "BingMaps.PTE",
    "publisher":  "Freddy Kristiansen",
    "version":  "1.0.0.0"
},

In your AL-Go repository settings you specify a list of trusted NuGet feeds like:

"TrustedNuGetFeeds": [
    {
        "Url": "https://api.nuget.org/v3/index.json",
        "Patterns": [ "Microsoft.*" ]
    },
    {
        "Url": "https://api.nuget.org/v3/index.json",
        "Fingerprints": [ "DFE254D1477DF4133CB9C69EEF6AE0E5DC334C85A2901D6ADA3D18495D74B260" ]
    },
    {
        "Url": "https://nuget.pkg.github.com/businesscentralapps/index.json",
        "AuthTokenSecret": "GhTokenWorkflow"
    },
    {
        "Url": "https://pkgs.dev.azure.com/freddydk/apps/_packaging/MyApps/nuget/v3/index.json"
    }
]

Patterns specifies which package patterns are considered in the corresponding feed. This can be the full name of a package, a registered prefix (like microsoft.*), but please note that anybody can publish malicious packages to f.ex. nuget.org, so please only trust registered prefixes or only signed packages.

Fingerprints specifies that you only want to consider packages, which are signed with any of the fingerprints in the array. Note that NuGet.org signs all packages with a repository signature. That one should not be trusted - you should trust only author signatures.

If no AuthTokenSecret specified, the nuget feed is considered public.

If a downloaded package contains dependencies to other NuGet packages, these are downloaded from the same nuget feed or other trusted nuget feeds.

NOTE that All trusted feeds are searched for a dependency to be resolved. If

Versioning

NuGet package versioning will have the same version as the app contained. NuGet package versioning and dependency versioning follows this schema: https://learn.microsoft.com/en-us/nuget/concepts/package-versioning

BcContainerHelper does support pre-release packages - this support will be added to AL-Go for GitHub as well.

A Public feed for Microsoft apps

Microsoft is planning on creating a public NuGet feed for all Microsoft apps. This NuGet feed will automatically be trusted by BcContainerHelper. A Proof-Of-Concept implementation of this feed can be found here: https://pkgs.dev.azure.com/freddydk/apps/_packaging/MyApps/nuget/v3/index.json

A Public feed for AppSource app symbols

Microsoft is also planning on creating a public NuGet feed containing NuGet packages for symbols for AppSource apps., This NuGet feed will automatically be trusted by BcContainerHelper.

Generation of NuGet packages

AL-Go for GitHub will have support for generating, delivering and updating NuGet packages with the content of a repository. Main part of the functionality will reside in BcContainerHelper and can be used in other DevOps solutions as well.

Downloading NuGet packages

BcContainerHelper will have functionality for downloading BcNuGet packages, which will be used by AL-Go. This function will among other things have a parameter specifying which apps are already installed / existing. Apps in this collection will NOT be downloaded.

Try it out

Run Update AL-Go System Files with freddydk/AL-Go@nuget to try out the latest version of this.

danielgorski-bynd365 commented 1 year ago

Yeah, looking forward! 🚀🚀🚀

ronnykwon commented 6 months ago

Can't wait to try this as it will unlock a big constraint in our CI/CD