Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 18 additions & 86 deletions .github/workflows/BuildMissingImages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,85 +113,29 @@ jobs:

- name: Build Image
env:
Tag: ${{ matrix.tag }}
PushToProd: ${{ github.event.inputs.PushToProd }}
GenericTag: ${{ needs.AnalyzeImages.outputs.genericTag }}
run: |
$erroractionpreference = "STOP"
Set-StrictMode -version 2.0
try {
$pushRegistry = "mcrbusinesscentral.azurecr.io"
az acr login --name $pushRegistry
Set-Location "generic"
$rootPath = Get-Location
$genericTag = $env:GenericTag
$pushToProd = $true
if ($env:GITHUB_EVENT_NAME -eq "workflow_dispatch") {
$filesonly = ($env:Tag -like '*-filesonly|*')
$only24 = ($env:Tag -like '*-24|*' -or $env:Tag -like '*-24-filesonly|*')
$baseImage = $env:Tag.split('|')[1]
$ltscTag = $env:Tag.split('|')[2]

$pushToProd = $true
if ($env:GITHUB_EVENT_NAME -eq "workflow_dispatch") {
$pushToProd = $env:PushToProd -eq 'True'
}
$osversion = '${{ matrix.tag }}'.split('|')[0].split('-')[0]
$filesonly = ('${{ matrix.tag }}' -like '*-filesonly|*')
$only24 = ('${{ matrix.tag }}' -like '*-24|*' -or '${{ matrix.tag }}' -like '*-24-filesonly|*')
$baseImage = '${{ matrix.tag }}'.split('|')[1]
$ltscTag = '${{ matrix.tag }}'.split('|')[2]
$setupUrlsFile = Join-Path $rootPath "Run/SetupUrls.ps1"
Get-Content -Path $setupUrlsFile | Out-Host
$dockerfile = Join-Path $rootPath "DOCKERFILE"
$strFilesOnly = ''
$str24 = ''
if ($only24) {
$str24 = "-24"
}
if ($filesOnly) {
$strFilesOnly = "-filesonly"
$dockerfile += '-filesonly'
}
$image = "my:$osversion-$genericTag$str24$strFilesOnly"
$newtags = @(
"$pushRegistry/public/businesscentral:$osversion$str24$strFilesonly-dev"
"$pushRegistry/public/businesscentral:$ltscTag$str24$strFilesonly-dev"
)
if ($pushToProd) {
$newtags += @(
"$pushRegistry/public/businesscentral:$osversion$str24$strFilesonly"
"$pushRegistry/public/businesscentral:$osversion-$genericTag$str24$strFilesonly"
"$pushRegistry/public/businesscentral:$ltscTag$str24$strFilesonly"
)
}
$newTags | out-host
$created = [DateTime]::Now.ToUniversalTime().ToString("yyyyMMddHHmm")
docker pull $baseimage
$inspect = docker inspect $baseimage | ConvertFrom-Json
$success = $false
docker build --build-arg baseimage=$baseimage `
--build-arg created=$created `
--build-arg tag="$genericTag" `
--build-arg osversion="$osversion" `
--build-arg filesonly="$filesonly" `
--build-arg only24="$only24" `
--isolation=hyperv `
--memory 8G `
--tag $image `
--file $dockerfile `
$RootPath | % {
$_ | Out-Host
if ($_ -like "Successfully built*") {
$success = $true
}
}
if (!$success) {
throw "Error building image"
}
$newtags | ForEach-Object {
Write-Host "Push $_"
docker tag $image $_
docker push $_
}
}
catch {
Write-Host "::Error::Error building images. Error was $($_.Exception.Message)"
$host.SetShouldExit(1)
}

. ${{ github.workspace }}/build/build.ps1 `
-BaseImage $baseImage `
-LtscTag $ltscTag `
-FilesOnly $filesonly `
-Only24 $only24 `
-GenericTag $env:GenericTag `
-PushToDev `
-PushToProd:$pushToProd

MarkOldImagesStale:
runs-on: [ Windows-Latest ]
needs: [ AnalyzeImages, BuildImages ]
Expand All @@ -215,16 +159,4 @@ jobs:
run: |
$erroractionpreference = "STOP"
$digests = $env:digestsJson | ConvertFrom-Json
$version = "1.2.0"
$filename = Join-Path $env:TEMP "oras_$($version)_windows_amd64.zip"
Invoke-RestMethod -Method GET -UseBasicParsing -Uri "https://github.com/oras-project/oras/releases/download/v$($version)/oras_$($version)_windows_amd64.zip" -OutFile $filename
Expand-Archive -Path $filename -DestinationPath temp
$pushRegistry = "mcrbusinesscentral.azurecr.io"
$staleDate = [System.DateTime]::Today.AddDays(-1).ToString('yyyy-MM-dd')
az acr login --name $pushRegistry
$digests | ForEach-Object {
$image = "$pushRegistry/public/businesscentral@$_"
Write-Host "Stale $image on $staleDate"
./temp/oras.exe attach --artifact-type application/vnd.microsoft.artifact.lifecycle --annotation "vnd.microsoft.artifact.lifecycle.end-of-life.date=$staleDate" $image
}

. ${{ github.workspace }}/build/markstale.ps1 -Digests $digests
14 changes: 1 addition & 13 deletions .github/workflows/MarkStale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,4 @@ jobs:
run: |
$erroractionpreference = "STOP"
$digests = "$env:digests".Split(",").Trim() | Select-Object -Unique
$version = "1.2.0"
$filename = Join-Path $env:TEMP "oras_$($version)_windows_amd64.zip"
Invoke-RestMethod -Method GET -UseBasicParsing -Uri "https://github.com/oras-project/oras/releases/download/v$($version)/oras_$($version)_windows_amd64.zip" -OutFile $filename
Expand-Archive -Path $filename -DestinationPath temp
$pushRegistry = "mcrbusinesscentral.azurecr.io"
$staleDate = [System.DateTime]::Today.AddDays(-1).ToString('yyyy-MM-dd')
az acr login --name $pushRegistry
$digests | ForEach-Object {
$image = "$pushRegistry/public/businesscentral@$_"
Write-Host "Stale $image on $staleDate"
./temp/oras.exe attach --artifact-type application/vnd.microsoft.artifact.lifecycle --annotation "vnd.microsoft.artifact.lifecycle.end-of-life.date=$staleDate" $image
}

. ${{ github.workspace }}/build/markstale.ps1 -Digests $digests
111 changes: 111 additions & 0 deletions .github/workflows/PRBuild.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: CI

on:
pull_request:
branches: [ master ]

permissions:
contents: read

defaults:
run:
shell: PowerShell

jobs:
AnalyzeImages:
runs-on: [ windows-latest ]
outputs:
genericTag: ${{ steps.Analyze.outputs.genericTag }}
buildImagesJson: ${{ steps.Analyze.outputs.buildImagesJson }}
digestsJson: ${{ steps.Analyze.outputs.digestsJson }}
steps:
- uses: actions/checkout@v4

- name: Analyze
id: Analyze
run: |
$erroractionpreference = "STOP"
try {
$bctags = @('ltsc2016','ltsc2019','ltsc2022','ltsc2025')
$servercoretags = @{
"ltsc2016" = "4.8-windowsservercore-ltsc2016"
"ltsc2019" = "4.8-windowsservercore-ltsc2019"
"ltsc2022" = "4.8.1-windowsservercore-ltsc2022"
"ltsc2025" = "4.8.1-windowsservercore-ltsc2025"
}
$tags = @($bctags | ForEach-Object { "$_-dev"; "$_-filesonly-dev" })
if ($prod) {
$tags += @($bctags | ForEach-Object { "$_"; "$_-filesonly" })
}
$digests = $tags | ForEach-Object {
Write-Host -NoNewline "$_ : "
$manifest = docker manifest inspect mcr.microsoft.com/businesscentral:$_ -v | ConvertFrom-Json
$manifest.Descriptor.digest
} | Select-Object -Unique
Set-Location "generic"
$rootPath = Get-Location
$genericTag = (Get-Content -Raw -Path (Join-Path $RootPath 'tag.txt')).Trim(@(13,10,32))
$tagver = [System.Version]$genericTag
$revision = [int]($ENV:GITHUB_RUN_NUMBER)
$genericTag = "$($tagver.Major).$($tagver.Minor).$($tagver.Build).$revision"
Write-Host "Using generic Tag $genericTag"
$webclient = New-Object System.Net.WebClient
$webclient.Headers.Add('Accept', "application/json")
$neededBcTags = $bctags | ForEach-Object {
$osVersion = [System.Version](($webclient.DownloadString("https://mcr.microsoft.com/v2/dotnet/framework/runtime/manifests/$($serverCoreTags."$_")") | ConvertFrom-Json).history[0].v1Compatibility | ConvertFrom-Json)."os.version"
"$osVersion-$genericTag|mcr.microsoft.com/dotnet/framework/runtime:$($serverCoreTags."$_")|$_"
"$osVersion-$genericTag-filesonly|mcr.microsoft.com/dotnet/framework/runtime:$($serverCoreTags."$_")|$_"
}
Write-Host "Needed Tags ($($neededBcTags.Count))"
$neededBcTags | ForEach-Object { Write-Host "- $_" }
$alltags = (($webclient.DownloadString("https://mcr.microsoft.com/v2/businesscentral/tags/list") | ConvertFrom-Json)).tags
$imagesBcTags = @($neededBcTags | Where-Object { $alltags -notcontains $_ })
Write-Host "Image Tags ($($imagesBcTags.Count))"
if ($imagesBcTags) {
$imagesBcTags | ForEach-Object { Write-Host "- $_" }
}
else {
Write-Host '- none'
}
$buildImagesJson = ConvertTo-Json -InputObject $imagesBcTags -Compress
$digestsJson = ConvertTo-Json -InputObject $digests -Compress
Add-Content -encoding utf8 -Path $ENV:GITHUB_OUTPUT -Value "digestsJson=$digestsJson"
Write-Host "digestsJson=$digestsJson"
Add-Content -encoding utf8 -Path $ENV:GITHUB_OUTPUT -Value "genericTag=$genericTag"
Write-Host "genericTag=$genericTag"
Add-Content -encoding utf8 -Path $ENV:GITHUB_OUTPUT -Value "buildImagesJson=$buildImagesJson"
Write-Host "buildImagesJson=$buildImagesJson"
}
catch {
Write-Host "::Error::Error analyzing images. Error was $($_.Exception.Message)"
$host.SetShouldExit(1)
}

BuildImages:
runs-on: [ Windows-2025 ]
needs: [ AnalyzeImages ]
strategy:
matrix:
tag: ${{ fromJson(needs.AnalyzeImages.outputs.buildImagesJson) }}
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Build Image
env:
Tag: ${{ matrix.tag }}
GenericTag: ${{ needs.AnalyzeImages.outputs.genericTag }}
run: |
$osversion = $env:Tag.split('|')[0].split('-')[0]
$filesonly = ($env:Tag -like '*-filesonly|*')
$only24 = ($env:Tag -like '*-24|*' -or $env:Tag -like '*-24-filesonly|*')
$baseImage = $env:Tag.split('|')[1]
$ltscTag = $env:Tag.split('|')[2]

. ${{ github.workspace }}/build/build.ps1 `
-BaseImage $baseImage `
-LtscTag $ltscTag `
-FilesOnly $filesonly `
-Only24 $only24 `
-GenericTag $env:GenericTag
28 changes: 28 additions & 0 deletions LOCAL_DEV_ENV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# How to build and test images locally

## Prerequisites
- Install and run [Docker Desktop](https://docs.docker.com/desktop/install/windows-install/). Make sure it is running Windows containers.
- Install [BcContainerHelper PS module](https://www.powershellgallery.com/packages/BcContainerHelper) (latest available version).
`Install-Module BCContainerHelper -AllowPrerelease` would do.

## Build a new image locally

If you want to build a new docker image locally you can use the build.ps1 script. The following will produce a new docker image based on Windows Server Core 2025.
```powershell
$GenericTag = "1.2.3.4"
$baseImage = "mcr.microsoft.com/dotnet/framework/runtime:4.8.1-windowsservercore-ltsc2025"
./build/build.ps1 -BaseImage $baseImage -LtscTag 'ltsc2025' -FilesOnly $false -Only24 $false -GenericTag $GenericTag
```

If you'd rather use a different base image, you can also use one of the following:
* mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2016
* mcr.microsoft.com/dotnet/framework/runtime:4.8-windowsservercore-ltsc2019
* mcr.microsoft.com/dotnet/framework/runtime:4.8.1-windowsservercore-ltsc2022
* mcr.microsoft.com/dotnet/framework/runtime:4.8.1-windowsservercore-ltsc2025

## Create a docker container with the image

Once you have a local image you can use New-BCContainer to spin up a Business Central container that uses your image.
```powershell
New-BcContainer -accept_eula -accept_insiderEula -containerName "MyTestContainer" -artifactUrl (Get-BCArtifactUrl -select NextMajor -accept_insiderEula -country W1) -useGenericImage "my:$GenericTag"
```
Loading