Modules/businessdev.ALbuild.RuntimePackages/Public/New-BcIndirectNuGetPackage.ps1

function New-BcIndirectNuGetPackage {
    <#
    .SYNOPSIS
        Creates an "indirect" NuGet package that maps platform versions to runtime packages.
 
    .DESCRIPTION
        Produces a dependency-only NuGet package whose dependencies are the per-platform-version
        runtime packages. A consumer resolving against a specific Business Central build pulls the
        matching runtime package through this single, stable package id.
 
    .PARAMETER PackageId
        The indirect package id.
 
    .PARAMETER Version
        The indirect package version (typically the app version).
 
    .PARAMETER RuntimePackage
        The runtime package dependencies, each @{ id = ...; version = ... } (version may be a range).
 
    .PARAMETER Authors
        Package authors. Default '365 business development'.
 
    .PARAMETER Description
        Package description.
 
    .PARAMETER OutputFolder
        Output folder. Default: current directory.
 
    .OUTPUTS
        System.String - the path to the created .nupkg.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)] [string] $PackageId,
        [Parameter(Mandatory)] [string] $Version,
        [Parameter(Mandatory)] [object[]] $RuntimePackage,
        [string] $Authors = '365 business development',
        [string] $Description,
        [string] $OutputFolder = (Get-Location).Path
    )

    if (-not $Description) { $Description = "Indirect runtime package index for $PackageId." }

    $depXml = ''
    foreach ($dep in $RuntimePackage) {
        $depXml += " <dependency id=`"$($dep.id)`" version=`"$($dep.version)`" />`n"
    }

    $nuspec = @"
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
  <metadata>
    <id>$PackageId</id>
    <version>$Version</version>
    <authors>$Authors</authors>
    <owners>$Authors</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>$Description</description>
    <dependencies>
$depXml </dependencies>
  </metadata>
</package>
"@


    $contentTypes = @'
<?xml version="1.0" encoding="utf-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default Extension="nuspec" ContentType="application/octet" />
  <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />
</Types>
'@


    $rels = @"
<?xml version="1.0" encoding="utf-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Type="http://schemas.microsoft.com/packaging/2010/07/manifest" Target="/$PackageId.nuspec" Id="R$([Guid]::NewGuid().ToString('N').Substring(0,16))" />
</Relationships>
"@


    $nupkgPath = Join-Path $OutputFolder "$PackageId.$Version.nupkg"
    if (-not $PSCmdlet.ShouldProcess($nupkgPath, 'Create indirect NuGet package')) { return }
    if (-not (Test-Path -LiteralPath $OutputFolder)) { New-Item -Path $OutputFolder -ItemType Directory -Force | Out-Null }
    if (Test-Path -LiteralPath $nupkgPath) { Remove-Item -LiteralPath $nupkgPath -Force }

    Add-Type -AssemblyName System.IO.Compression -ErrorAction SilentlyContinue
    Add-Type -AssemblyName System.IO.Compression.FileSystem -ErrorAction SilentlyContinue
    $utf8 = [System.Text.UTF8Encoding]::new($false)
    $zip = [System.IO.Compression.ZipFile]::Open($nupkgPath, [System.IO.Compression.ZipArchiveMode]::Create)
    try {
        $addText = {
            param($entryName, $text)
            $entry = $zip.CreateEntry($entryName); $stream = $entry.Open()
            try { $bytes = $utf8.GetBytes($text); $stream.Write($bytes, 0, $bytes.Length) } finally { $stream.Dispose() }
        }
        & $addText '[Content_Types].xml' $contentTypes
        & $addText '_rels/.rels' $rels
        & $addText "$PackageId.nuspec" $nuspec
    }
    finally { $zip.Dispose() }

    Write-ALbuildLog -Level Success "Created indirect NuGet package '$nupkgPath'."
    return $nupkgPath
}