Skip to main content
Delivery Targets in AL-Go for GitHub enable you to automatically publish your Business Central applications to various package repositories and storage systems. This facilitates app distribution, dependency management, and partner collaboration.

Overview

AL-Go supports delivery to multiple target types:

GitHub Packages

Free NuGet feed provided by GitHub for each organization. Ideal for internal distribution and dependency management.

Custom NuGet Feeds

Azure DevOps Artifacts, private NuGet servers, or other custom NuGet v3 feeds.

Azure Storage

Publish artifacts to Azure Storage accounts for archival or custom distribution workflows.

Microsoft AppSource

Submit applications to Microsoft AppSource for public distribution.
NuGet and GitHub Packages delivery is currently experimental. While stable and used in production, the package structure and configuration may change in future versions.

Delivery Targets Concept

Each delivery target is configured through:
  1. Context Secret: A secret named <DeliveryTarget>Context containing connection information
  2. Delivery Script: An optional PowerShell script named DeliverTo<DeliveryTarget>.ps1 for custom logic
  3. Settings: Optional configuration in AL-Go settings files

Supported Targets

TargetPurposeContext SecretStatus
GitHubPackagesGitHub Packages NuGet feedGitHubPackagesContext✅ Experimental
NuGetCustom NuGet feedNuGetContext✅ Experimental
StorageAzure Storage AccountStorageContext✅ Stable
AppSourceMicrosoft AppSourceAppSourceContext✅ Stable

GitHub Packages Setup

GitHub Packages provides a free NuGet feed for each GitHub organization, making it the recommended approach for most Per-Tenant Extension (PTE) scenarios.
1

Create Personal Access Token

Generate a token with package permissions:
  1. Navigate to GitHub Personal Access Tokens
  2. Create a Classic Personal Access Token (fine-grained tokens don’t support packages yet)
  3. Select the following scopes:
    • write:packages - Required for publishing packages
    • read:packages - Required for consuming packages
    • repo - Required if your repositories are private
  4. Set an appropriate expiration date
  5. Generate and copy the token
Store the token securely. You won’t be able to see it again after leaving the page.
2

Create GitHubPackagesContext Secret

Create an organizational or repository secret:
  1. Navigate to SettingsSecrets and variablesActions
  2. Click New organization secret (or New repository secret)
  3. Name: GitHubPackagesContext
  4. Value: Compressed JSON with token and server URL
{"token":"ghp_<your_token>","serverUrl":"https://nuget.pkg.github.com/<your_org>/index.json"}
Replace:
  • <your_token>: Your personal access token
  • <your_org>: Your GitHub organization name
Use the BcContainerHelper function New-ALGoNuGetContext to generate correctly formatted JSON:
New-ALGoNuGetContext -token "ghp_..." -serverUrl "https://nuget.pkg.github.com/myorg/index.json"
3

Configure Repository Settings (Optional)

Control delivery behavior in your AL-Go settings file:
{
  "DeliverToGitHubPackages": {
    "ContinuousDelivery": true,
    "Branches": ["main", "release/*"]
  }
}
Configuration options:
  • ContinuousDelivery: Enable automatic delivery on successful builds
  • Branches: Array of branch patterns that trigger delivery
See DeliverTo settings for all options.
4

Verify Setup

After creating the secret:
  1. Push a commit to a configured branch
  2. Monitor the CI/CD workflow run
  3. Look for “Deliver to GitHubPackages” job
  4. Check your organization’s Packages tab for published packages
  5. Verify package metadata and version

Consuming GitHub Packages

GitHub Packages are automatically available for dependency resolution. When your apps reference dependencies published to your organization’s GitHub Packages feed, AL-Go will automatically resolve and download them. No additional configuration required - AL-Go uses the GitHubPackagesContext secret for both publishing and consuming packages.

Custom NuGet Feed Setup

For Azure DevOps Artifacts, private NuGet servers, or other custom feeds:
1

Obtain Feed Credentials

Get authentication credentials for your NuGet feed:Azure DevOps:
  1. Navigate to User SettingsPersonal Access Tokens
  2. Create new token with Packaging: Read & write scope
  3. Copy the token value
Private NuGet Server:
  • Use API key or credentials provided by your server administrator
2

Get Feed URL

Locate your NuGet v3 feed URL:Common formats:
  • Azure DevOps: https://pkgs.dev.azure.com/<org>/<project>/_packaging/<feedName>/nuget/v3/index.json
  • GitHub Packages: https://nuget.pkg.github.com/<org>/index.json
  • NuGet.org: https://api.nuget.org/v3/index.json
  • Private server: Contact your administrator
3

Create NuGetContext Secret

Create a repository or organization secret:Name: NuGetContextValue: Compressed JSON with credentials
{"token":"<YOUR_NUGET_TOKEN>","serverUrl":"https://pkgs.dev.azure.com/<org>/<project>/_packaging/<feed>/nuget/v3/index.json"}
Example for Azure DevOps:
{"token":"abc123xyz","serverUrl":"https://pkgs.dev.azure.com/contoso/BusinessCentral/_packaging/BC-Apps/nuget/v3/index.json"}
4

Configure Dependency Resolution (Optional)

Unlike GitHub Packages, custom NuGet feeds are not automatically used for dependency resolution. Add your feed to trustedNuGetFeeds:
{
  "trustedNuGetFeeds": [
    {
      "url": "https://pkgs.dev.azure.com/<org>/<project>/_packaging/<feed>/nuget/v3/index.json",
      "authTokenSecret": "NuGetContext",
      "patterns": ["*"]
    }
  ]
}
Parameters:
  • url: NuGet feed URL
  • authTokenSecret: Name of the secret containing credentials
  • patterns: Array of package name patterns (e.g., ["Contoso.*", "Fabrikam.*"])

Configuration Examples

Example 1: GitHub Packages for PTE Organization

Scenario: A company developing Per-Tenant Extensions wants to automatically publish apps to GitHub Packages for internal distribution. Organizational Secret: GitHubPackagesContext
{"token":"ghp_1234567890abcdef","serverUrl":"https://nuget.pkg.github.com/contoso/index.json"}
AL-Go-Settings.json:
{
  "DeliverToGitHubPackages": {
    "ContinuousDelivery": true,
    "Branches": ["main"]
  }
}
Result: Every successful build on the main branch automatically publishes packages to GitHub Packages.

Example 2: Azure DevOps Artifacts for PTE Development

Scenario: A partner with existing Azure DevOps infrastructure wants to deliver PTEs to Azure DevOps Artifacts. Repository Secret: NuGetContext
{"token":"<AZURE_DEVOPS_PAT>","serverUrl":"https://pkgs.dev.azure.com/contoso/BusinessCentral/_packaging/BC-Apps/nuget/v3/index.json"}
AL-Go-Settings.json:
{
  "DeliverToNuGet": {
    "ContinuousDelivery": true,
    "Branches": ["main", "release/*"]
  },
  "trustedNuGetFeeds": [
    {
      "url": "https://pkgs.dev.azure.com/contoso/BusinessCentral/_packaging/BC-Apps/nuget/v3/index.json",
      "authTokenSecret": "NuGetContext",
      "patterns": ["Contoso.*"]
    }
  ]
}
Result: Packages are published to Azure DevOps Artifacts and available for dependency resolution.

Example 3: Multi-Environment PTE Setup

Scenario: Publish development builds to GitHub Packages and production releases to a private NuGet feed. Secrets:
  • GitHubPackagesContext: For development builds
  • NuGetContext: For production releases
AL-Go-Settings.json:
{
  "DeliverToGitHubPackages": {
    "ContinuousDelivery": true,
    "Branches": ["develop", "feature/*"]
  },
  "DeliverToNuGet": {
    "ContinuousDelivery": true,
    "Branches": ["main"]
  },
  "trustedNuGetFeeds": [
    {
      "url": "https://pkgs.dev.azure.com/contoso/BC/_packaging/Production/nuget/v3/index.json",
      "authTokenSecret": "NuGetContext",
      "patterns": ["Contoso.*"]
    }
  ]
}
Result:
  • Development branches → GitHub Packages
  • Main branch → Private NuGet feed

Example 4: Multiple Feed Configuration

Scenario: Use multiple NuGet feeds for different package sources. AL-Go-Settings.json:
{
  "trustedNuGetFeeds": [
    {
      "url": "https://nuget.pkg.github.com/contoso/index.json",
      "authTokenSecret": "GitHubPackagesContext",
      "patterns": ["Contoso.*"]
    },
    {
      "url": "https://pkgs.dev.azure.com/contoso/BC/_packaging/External/nuget/v3/index.json",
      "authTokenSecret": "AzureDevOpsContext",
      "patterns": ["External.*", "Partner.*"]
    },
    {
      "url": "https://api.nuget.org/v3/index.json",
      "patterns": ["Newtonsoft.*"]
    }
  ]
}
Result: AL-Go resolves dependencies from multiple feeds based on package name patterns.

Custom Delivery Scripts

For advanced scenarios, create custom delivery scripts to implement specialized delivery logic.
1

Create Delivery Script

Create a PowerShell script in your .github folder:File: .github/DeliverToCustomFeed.ps1
Param(
    [Parameter(Mandatory = $true)]
    [HashTable] $parameters
)

# Extract parameters
$project = $parameters.project
$projectName = $parameters.projectName
$type = $parameters.type
$appsFolder = $parameters.appsFolder

# Custom delivery logic
Write-Host "Delivering project '$projectName' (type: $type) to custom feed"

if ($appsFolder) {
    Get-ChildItem -Path $appsFolder -Filter "*.app" | ForEach-Object {
        Write-Host "Processing app: $($_.Name)"
        # Your custom delivery logic here
    }
}
2

Create Context Secret

Create a secret named CustomFeedContext with your custom configuration:
{"customField1":"value1","customField2":"value2"}
Access the secret in your script:
$context = $parameters.context | ConvertFrom-Json
$customValue = $context.customField1
3

Configure AL-Go Settings

Enable continuous delivery to your custom target:
{
  "DeliverToCustomFeed": {
    "ContinuousDelivery": true,
    "Branches": ["main"]
  }
}

Script Parameters

Your delivery script receives a hashtable with the following parameters:
ParameterTypeDescription
projectstringProject path (escaped for artifact naming)
projectNamestringProject name (sanitized for paths)
typestringDelivery type: “CD” or “Release”
appsFolderstringPath to folder containing .app files
testAppsFolderstringPath to test app files (if available)
dependenciesFolderstringPath to dependency files (if available)
appsFoldersstring[]Array of all app folders from different build modes
testAppsFoldersstring[]Array of all test app folders
dependenciesFoldersstring[]Array of all dependency folders
Folder parameters may be $null if no artifacts of that type were found. Use null checks before accessing folders.

Branch-Specific Delivery

Configure different delivery targets for different branches:
{
  "DeliverToGitHubPackages": {
    "ContinuousDelivery": true,
    "Branches": ["develop", "feature/*"]
  },
  "DeliverToNuGet": {
    "ContinuousDelivery": true,
    "Branches": ["main", "release/*"]
  },
  "DeliverToStorage": {
    "ContinuousDelivery": true,
    "Branches": ["main"]
  }
}
Use cases:
  • Feature branches: Deliver to development feed for testing
  • Main branch: Deliver to production feed
  • Release branches: Deliver to staging feed for validation

Troubleshooting

Missing Context Secret

Error: Secret 'GitHubPackagesContext' not foundSolutions:
  • Verify secret exists at organization or repository level
  • Check secret name matches exactly (case-sensitive)
  • Ensure repository has access to organization secrets
  • Confirm secret is not expired (for PATs)

Authentication Failed

Error: 401 Unauthorized when publishing packagesSolutions:
  • Verify PAT has correct scopes (write:packages, read:packages)
  • Check if token has expired
  • Ensure token has access to target organization
  • Confirm server URL is correct
  • Validate JSON format (must be compressed)

Package Not Found

Error: Unable to find package during dependency resolutionSolutions:
  • Verify package was published successfully
  • Check trustedNuGetFeeds configuration
  • Ensure package name matches app.json dependencies
  • Confirm package version exists
  • Verify feed URL is accessible

Curly Brackets Masked

Error: Seeing *** instead of JSON in logsSolutions:
  • Ensure JSON is compressed (single line)
  • Remove all newlines and extra whitespace
  • Use New-ALGoNuGetContext to generate correct format
  • Validate JSON syntax before creating secret

Debugging Steps

1

Check Workflow Logs

Review the CI/CD workflow for delivery job:
  1. Navigate to Actions → Select workflow run
  2. Look for “Deliver to [Target]” job
  3. Review step outputs for errors
  4. Check for authentication failures
2

Verify Package Publication

Confirm packages were published:
  • GitHub Packages: Organization → Packages tab
  • Azure DevOps: Artifacts → Feed → Packages
  • Custom feed: Use feed’s web interface
3

Test Dependency Resolution

Verify dependency resolution works:
  1. Check workflow logs for “Resolving Dependencies”
  2. Look for “Installing app dependencies” messages
  3. Verify correct package versions are downloaded
  4. Check for authentication errors
4

Validate Secret Format

Use PowerShell to validate JSON:
# Test JSON parsing
$json = '{"token":"test","serverUrl":"https://example.com"}'
$json | ConvertFrom-Json

# Generate correct format
New-ALGoNuGetContext -token "ghp_..." -serverUrl "https://..."

Security Best Practices

Token Scopes

Grant minimum permissions:
  • GitHub Packages: write:packages, read:packages only
  • Azure DevOps: Packaging (Read & Write) scope
  • Avoid granting repo scope unless necessary
  • Use fine-grained tokens when available

Secret Storage

Use appropriate secret levels:
  • Organization secrets for shared feeds
  • Repository secrets for project-specific feeds
  • Environment secrets for environment-specific delivery
  • Azure Key Vault for sensitive credentials

Token Rotation

Regular credential updates:
  • Rotate PATs every 90 days
  • Set expiration dates on all tokens
  • Monitor token usage
  • Revoke unused tokens

Access Control

Limit feed access:
  • Use dedicated service accounts
  • Implement feed permissions
  • Audit package access
  • Monitor download patterns

Limitations and Considerations

Known Limitations:
  • Fine-grained tokens: GitHub Packages doesn’t support fine-grained PATs yet (use classic tokens)
  • Package visibility: GitHub Packages inherit repository visibility
  • Retention policies: Consider feed retention policies and storage costs
  • Version conflicts: Duplicate versions may cause resolution issues
  • Feed limits: Some feeds have package count or size limits

Best Practices

  1. Use semantic versioning: Follow semver (e.g., 1.2.3) for all packages
  2. Test in isolation: Validate delivery configuration in test repositories first
  3. Monitor package sizes: Be aware of feed storage limits
  4. Document dependencies: Maintain clear dependency documentation
  5. Implement cleanup: Set up package retention and cleanup policies
  6. Version consistency: Ensure app.json versions match package versions
  7. Feed organization: Use separate feeds for dev, test, and production

Next Steps

Continuous Deployment

Learn about automated deployment strategies

Sandbox Environment

Set up sandbox environments for testing

AL-Go Settings

Explore all configuration options

AL-Go Secrets

Learn about secret management

Build docs developers (and LLMs) love