Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 8 additions & 7 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,14 @@ jobs:
- name: Release script tests
shell: pwsh
run: |
$result = Invoke-Pester ./scripts/verify-release.Tests.ps1 -Output Detailed -PassThru
$result = Invoke-Pester ./scripts -Output Detailed -PassThru
if ($result.FailedCount -ne 0) { exit 1 }

# From pwsh, as build-release.ps1 calls it: mvnw.cmd 3.2.0 failed its checksum there on Windows.
- name: Maven wrapper
shell: pwsh
run: ./mvnw -v

# Runs the same probes twice, JIT compiled and published with Native AOT, so that a
# difference between the two is caused by AOT rather than by the platform. The project
# knows which probes are expected to fail in each mode and exits non-zero when reality
Expand All @@ -74,12 +79,10 @@ jobs:
env:
"log4net.AotEnvironmentProbe": from-environment
run: |
$PSNativeCommandUseErrorActionPreference = $true
dotnet run --project ./src/log4net.Tests.Aot/log4net.Tests.Aot.csproj -c Release
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
dotnet publish ./src/log4net.Tests.Aot/log4net.Tests.Aot.csproj -c Release -o ./aot-probes
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& "./aot-probes/log4net.Tests.Aot$($IsWindows ? '.exe' : '')"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

# A process that hosts the runtime natively has no entry assembly, so the configuration
# system cannot work out where the config file is (issue #162). The check owns the process
Expand All @@ -93,6 +96,4 @@ jobs:
# makes it a green that cannot fail for the reason the check exists.
- name: AOT probes, no entry assembly
shell: pwsh
run: |
dotnet run --project ./src/log4net.Tests.Aot/log4net.Tests.Aot.csproj -c Release -- --no-entry-assembly
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
run: dotnet run --project ./src/log4net.Tests.Aot/log4net.Tests.Aot.csproj -c Release -- --no-entry-assembly
88 changes: 88 additions & 0 deletions scripts/FakeCommands.TestHelper.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#Requires -Version 7.4

<#
.SYNOPSIS
Runs a build script in a scratch tree against fake native commands, for the Pester tests.

.DESCRIPTION
The fakes log every call and create the files the scripts go on to read, so a test sees which
commands ran without building, signing, tagging or pushing anything.
#>

# Logs its call, fails when named in FAKE_FAIL, and otherwise writes the files the real command
# would, where it is told to, so a wrong output path fails as it would for real.
$script:FakeCommand = @'
param([string]$Name)
Add-Content -Path $env:FAKE_LOG -Value "$Name $($args -join ' ')"
if ($env:FAKE_FAIL -eq $Name) { exit 1 }
$outputs = switch ($Name)
{
'dotnet'
{
$version = ($args -join ' ') -replace '.*PackageVersion=(\S+).*', '$1'
$build = "$(Split-Path $PSScriptRoot)/build"
"$build/artifacts/log4net.$version.nupkg", "$build/artifacts/log4net.Ext.Mail.$version.nupkg", "$build/Release/net462/log4net.dll"
}
'git' { if ($args[0] -eq 'archive') { $args[$args.IndexOf('--output') + 1] } }
'zip' { $args[1] }
}
$outputs | ForEach-Object { New-Item -ItemType File -Force -Path $_ | Out-Null }
'@

function New-ScratchTree
{
param ([Parameter(Mandatory)][string]$Script)

$root = New-Item -ItemType Directory -Path (Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid()))
$scripts = New-Item -ItemType Directory -Path (Join-Path $root 'scripts')
$bin = New-Item -ItemType Directory -Path (Join-Path $root 'fakebin')
Copy-Item (Join-Path $PSScriptRoot $Script) $scripts
Copy-Item (Join-Path $PSScriptRoot 'verify-release.*') $scripts
'license' | Set-Content (Join-Path $root 'LICENSE')
'notice' | Set-Content (Join-Path $root 'NOTICE')
Set-Content -Path (Join-Path $bin 'fake.ps1') -Value $script:FakeCommand
foreach ($name in 'dotnet', 'git', 'gpg', 'zip', 'mvnw')
{
# mvnw is called by path from the root, the others through PATH.
$directory = $name -eq 'mvnw' ? $root : $bin
if ($IsWindows)
{
Set-Content -Path (Join-Path $directory "$name.cmd") -Value "@pwsh -NoProfile -File `"$bin\fake.ps1`" $name %*`r`n@exit /b %ERRORLEVEL%"
}
else
{
$path = Join-Path $directory $name
Set-Content -Path $path -Value "#!/bin/sh`nexec pwsh -NoProfile -File '$bin/fake.ps1' $name `"`$@`""
chmod +x $path
}
}
return $root.FullName
}

function Invoke-InScratchTree
{
param ([Parameter(Mandatory)][string]$Root, [Parameter(Mandatory)][string]$Script, [string]$Fail)

$log = Join-Path $Root 'calls.log'
New-Item -ItemType File -Path $log -Force | Out-Null
$path = $env:PATH
try
{
$env:PATH = (Join-Path $Root 'fakebin') + [System.IO.Path]::PathSeparator + $path
$env:FAKE_LOG = $log
$env:FAKE_FAIL = $Fail
# NonInteractive makes the confirmation pause throw, standing in for a release manager who says no.
$output = pwsh -NoProfile -NonInteractive -File (Join-Path $Root 'scripts' $Script) 2>&1
$exitCode = $LASTEXITCODE
}
finally
{
$env:PATH = $path
Remove-Item Env:FAKE_LOG, Env:FAKE_FAIL -ErrorAction SilentlyContinue
}
return [pscustomobject]@{
ExitCode = $exitCode
Calls = @(Get-Content $log | ForEach-Object { ($_ -split ' ')[0..1] -join ' ' })
Output = $output -join [Environment]::NewLine
}
}
48 changes: 48 additions & 0 deletions scripts/build-preview.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#Requires -Version 7.4
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' }

<#
.SYNOPSIS
Checks that build-preview.ps1 stops at the first failure and tags nothing unconfirmed.

.DESCRIPTION
Runs the script against fake native commands, so nothing is built, signed, tagged or pushed.

Run with: Invoke-Pester ./scripts/build-preview.Tests.ps1
#>

BeforeAll {
. (Join-Path $PSScriptRoot 'FakeCommands.TestHelper.ps1')
}

Describe 'build-preview.ps1' {
BeforeEach {
$script:Root = New-ScratchTree -Script 'build-preview.ps1'
}

AfterEach {
Remove-Item $script:Root -Recurse -Force -ErrorAction SilentlyContinue
}

It 'signs nothing when the build fails' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-preview.ps1' -Fail 'dotnet'

$result.ExitCode | Should -Not -Be 0
$result.Calls | Should -Be @('dotnet build')
}

It 'tags nothing when signing fails' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-preview.ps1' -Fail 'gpg'

$result.ExitCode | Should -Not -Be 0
$result.Calls | Should -Be @('dotnet build', 'gpg --armor')
}

It 'signs both packages and tags nothing without confirmation' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-preview.ps1'

$result.ExitCode | Should -Not -Be 0
$result.Output | Should -BeLike '*NonInteractive*'
$result.Calls | Should -Be @('dotnet build', 'gpg --armor', 'gpg --armor')
}
}
65 changes: 65 additions & 0 deletions scripts/build-release.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#Requires -Version 7.4
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0' }

<#
.SYNOPSIS
Checks that build-release.ps1 stops at the first failure and tags nothing unconfirmed.

.DESCRIPTION
Runs the script against fake native commands, so nothing is built, signed, tagged or pushed.

Run with: Invoke-Pester ./scripts/build-release.Tests.ps1
#>

BeforeAll {
. (Join-Path $PSScriptRoot 'FakeCommands.TestHelper.ps1')
}

Describe 'build-release.ps1' {
BeforeEach {
$script:Root = New-ScratchTree -Script 'build-release.ps1'
}

AfterEach {
Remove-Item $script:Root -Recurse -Force -ErrorAction SilentlyContinue
}

It 'signs nothing when the build fails' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-release.ps1' -Fail 'dotnet'

$result.ExitCode | Should -Not -Be 0
$result.Calls | Should -Be @('dotnet test')
}

It 'builds no site when signing fails' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-release.ps1' -Fail 'gpg'

$result.ExitCode | Should -Not -Be 0
$result.Calls | Should -Be @('dotnet test', 'git archive', 'zip -r', 'gpg --armor')
}

It 'asks for no tag when the site build fails' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-release.ps1' -Fail 'mvnw'

$result.ExitCode | Should -Not -Be 0
$result.Output | Should -Not -BeLike '*NonInteractive*'
$result.Calls[-1] | Should -Be 'mvnw site'
}

It 'signs all six artifacts and tags nothing without confirmation' {
$result = Invoke-InScratchTree -Root $script:Root -Script 'build-release.ps1'

$result.ExitCode | Should -Not -Be 0
$result.Output | Should -BeLike '*NonInteractive*'
$result.Calls | Should -Be @('dotnet test', 'git archive', 'zip -r',
'gpg --armor', 'gpg --armor', 'gpg --armor', 'gpg --armor', 'gpg --armor', 'gpg --armor', 'mvnw site')
}

It 'ships no artifact without a hash' {
Invoke-InScratchTree -Root $script:Root -Script 'build-release.ps1' | Out-Null

$artifacts = Get-ChildItem (Join-Path $script:Root 'build' 'artifacts') -Exclude '*.sha512', '*.asc'
$artifacts.Name | Should -HaveCount 6
$artifacts | Where-Object { !(Test-Path "$($_.FullName).sha512") } | Should -BeNullOrEmpty
}
}
2 changes: 1 addition & 1 deletion scripts/build-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pushd $PSScriptRoot/..
git archive --format=zip --output $PSScriptRoot/../build/artifacts/apache-log4net-source-$Version.zip master
popd
'compressing binaries ...'
Copy-Item $PSScriptRoot/verify-release.* $PSScriptRoot/../build/artifacts/
Copy-Item $PSScriptRoot/verify-release.ps1, $PSScriptRoot/verify-release.sh $PSScriptRoot/../build/artifacts/
Copy-Item $PSScriptRoot/../LICENSE $PSScriptRoot/../build/Release/
Copy-Item $PSScriptRoot/../NOTICE $PSScriptRoot/../build/Release/
pushd $PSScriptRoot/../build/Release
Expand Down
2 changes: 1 addition & 1 deletion src/changelog/3.5.0/.release.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
<release xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://logging.apache.org/xml/ns"
xsi:schemaLocation="https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-changelog-0.xsd"
date="2026-11-01"
date="2026-09-28"
version="3.5.0"/>
Loading