Author SHA1 Message Date
James MaesandClaude Opus 5.5 4fbb457846 ci: first run on ci-workflows v1 (native workflows)
node / node (push) Successful in 20s
build / scripts (push) Successful in 20s
windows / windows (push) Successful in 2m19s
build / windows (push) Successful in 2m19s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 08:26:33 -06:00
james.maes 611cad4881 Merge pull request 'ci: build on the shared ci-workflows Windows workflow' (#9) from ci/shared-windows into main 2026-11-08 13:51:44 +00:00
James MaesandClaude Opus 5.5 579dac6df8 ci: pin the shared workflow to v1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:51:25 -06:00
James MaesandClaude Opus 5.5 b4a2734d9e ci: call the shared workflow from ci-workflows main
node / node (push) Successful in 17s
build / scripts (push) Successful in 17s
windows / windows (push) Successful in 2m18s
build / windows (push) Successful in 2m18s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:47:22 -06:00
James MaesandClaude Opus 5.5 fe1883d4a4 ci: let the Pester suites see child exit codes
node / node (push) Successful in 22s
build / scripts (push) Successful in 22s
windows / windows (push) Successful in 2m7s
build / windows (push) Successful in 2m7s
They assert on failing child processes themselves; the shared workflow's
native-exit-code preference turned those expected failures into errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:42:17 -06:00
James MaesandClaude Opus 5.5 f9cad0128d ci: create the module directory before saving the pinned modules
node / node (push) Successful in 22s
build / scripts (push) Successful in 22s
windows / windows (push) Canceled after 1m13s
build / windows (push) Canceled after 1m13s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:38:25 -06:00
James MaesandClaude Opus 5.5 49147f6c28 ci: run the coverage gate as a separate pwsh process
node / node (push) Successful in 24s
build / scripts (push) Successful in 24s
windows / windows (push) Canceled after 1m27s
build / windows (push) Canceled after 1m27s
Assert-Coverage.ps1 reports a shortfall with exit 1, which only stops the
build when the script runs as a native process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:35:32 -06:00
James MaesandClaude Opus 5.5 61f6d1c1a3 ci: build on the shared ci-workflows Windows workflow
node / node (push) Successful in 21s
build / scripts (push) Successful in 21s
windows / windows (push) Canceled after 1m27s
build / windows (push) Canceled after 1m27s
Locked restore, warnings-as-errors build, dotnet format, core and CLI tests
with the 100% coverage gate, the template pack/instantiate smoke test and the
PowerShell analyzer/Pester checks run on the KOF Windows host runner; the
review-gate scripts lint and test on node.yaml. Pins SDK 10.0.403 (the
runner's SDK) and refreshes the lock files for its ILLink 10.0.14 reference.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 07:32:52 -06:00
forge-recovery 60f8c6856a build: pin SDK 10.0.401, the version the committed lock files require (#8)
KofTwentyTwo/ci-policy: ci / fork-guard (push) Skipped
KofTwentyTwo/ci-policy: ci / go-0 (push) Skipped
KofTwentyTwo/ci-policy: ci / go-nix-1 (push) Skipped
KofTwentyTwo/ci-policy: ci / gradle-2 (push) Skipped
KofTwentyTwo/ci-policy: ci / gradle-3 (push) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-4 (push) Skipped
KofTwentyTwo/ci-policy: ci / java-maven-5 (push) Skipped
KofTwentyTwo/ci-policy: ci / manifests-6 (push) Skipped
KofTwentyTwo/ci-policy: ci / nix-7 (push) Skipped
KofTwentyTwo/ci-policy: ci / node-8 (push) Skipped
KofTwentyTwo/ci-policy: ci / python-9 (push) Skipped
KofTwentyTwo/ci-policy: ci / rust-10 (push) Skipped
KofTwentyTwo/ci-policy: ci / rust-12 (push) Skipped
KofTwentyTwo/ci-policy: ci / rust-11 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-0 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-1 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-2 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-3 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-4 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-5 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-6 (push) Skipped
KofTwentyTwo/ci-policy: ci / image-build-7 (push) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (push) Skipped
KofTwentyTwo/ci-policy: native / fork-deny (push) Skipped
KofTwentyTwo/ci-policy: native / dotnet-1 (push) Skipped
KofTwentyTwo/ci-policy: native / dotnet-2 (push) Skipped
KofTwentyTwo/ci-policy: native / dotnet-3 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-4 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-5 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-6 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-7 (push) Skipped
KofTwentyTwo/ci-policy: native / swift-8 (push) Skipped
KofTwentyTwo/ci-policy: Shared source and config CI / verify (push) Successful in 22s
KofTwentyTwo/ci-policy: Shared source and config CI / secrets (push) Successful in 22s
KofTwentyTwo/ci-policy: Shared .NET CI / secrets (push) Successful in 23s
KofTwentyTwo/ci-policy: Shared source and config CI / gate (push) Successful in 12s
KofTwentyTwo/ci-policy: ci / source-config-13 (push) Successful in 40s
KofTwentyTwo/ci-policy: ci / ci-result (push) Successful in 8s
KofTwentyTwo/ci-policy: Shared .NET CI / verify (push) Successful in 1m1s
KofTwentyTwo/ci-policy: Shared .NET CI / gate (push) Successful in 15s
KofTwentyTwo/ci-policy: native / dotnet-0 (push) Successful in 1m18s
KofTwentyTwo/ci-policy: native / native-result (push) Successful in 5s
2026-11-01 23:27:44 +00:00
15 changed files with 78 additions and 118 deletions

No files matched your search

+66
View File
@@ -0,0 +1,66 @@
name: build
on:
push:
branches: ['**']
tags: ['v*']
jobs:
windows:
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/windows.yaml@v1
secrets: inherit
with:
commands: |
dotnet restore Kof22App.slnx -p:Platform=x64 --locked-mode
dotnet build Kof22App.slnx -p:Platform=x64 -warnaserror --no-restore
# The WinUI app project is built directly so the format workspace can load its generated XAML code.
dotnet build src/Kof22App/Kof22App.csproj -p:Platform=x64 -warnaserror --no-restore
$env:Platform = 'x64'
dotnet format Kof22App.slnx --verify-no-changes --severity warn --no-restore
Remove-Item Env:Platform
dotnet test tests/Kof22App.Core.Tests --no-build --settings coverage.runsettings --collect:"XPlat Code Coverage" --logger trx --results-directory TestResults
dotnet test tests/Kof22App.Cli.Tests --no-build --settings coverage.runsettings --collect:"XPlat Code Coverage" --logger trx --results-directory TestResults
pwsh -NoProfile -File ./build/Assert-Coverage.ps1 -ResultsDirectory TestResults -Packages Kof22App.Core,kof22-app
# Template: pack, install into a private hive, instantiate with and without the CLI, then build, format, test and gate coverage.
$temp = $env:RUNNER_TEMP ?? [IO.Path]::GetTempPath()
$hive = Join-Path $temp "template-hive-$env:GITHUB_RUN_ID"
dotnet pack templates/KofTwentyTwo.Templates.WindowsApp.csproj -c Release -p:Version=0.1.0 -p:RestoreLockedMode=true -o artifacts/template
dotnet new install artifacts/template/KofTwentyTwo.Templates.WindowsApp.0.1.0.nupkg --debug:custom-hive $hive
foreach ($cli in 'true', 'false') {
$directory = "artifacts/template-probe-$cli"
$name = if ($cli -eq 'true') { 'TemplateProbe.WithCli' } else { 'TemplateProbe.NoCli' }
dotnet new k22-winapp -n $name --appId "probe-$cli" --displayName "Probe $cli" --cli $cli -o $directory --debug:custom-hive $hive
dotnet restore "$directory/$name.slnx" -p:Platform=x64 --locked-mode
dotnet build "$directory/$name.slnx" -p:Platform=x64 -warnaserror --no-restore
dotnet format "$directory/$name.slnx" --verify-no-changes --severity warn --no-restore
dotnet test "$directory/tests/$name.Core.Tests" --no-build --settings "$directory/coverage.runsettings" --collect:'XPlat Code Coverage' --results-directory "$directory/TestResults"
if ($cli -eq 'true') {
dotnet test "$directory/tests/$name.Cli.Tests" --no-build --settings "$directory/coverage.runsettings" --collect:'XPlat Code Coverage' --results-directory "$directory/TestResults"
}
pwsh -NoProfile -File "$directory/build/Assert-Coverage.ps1" -ResultsDirectory "$directory/TestResults"
}
# PowerShell build scripts: analyzer and Pester boundary tests with pinned modules saved to the job's temp directory.
$modules = Join-Path $temp "psmodules-$env:GITHUB_RUN_ID"
New-Item -ItemType Directory -Force -Path $modules | Out-Null
Save-PSResource -Name PSScriptAnalyzer -Version 1.25.0 -Repository PSGallery -TrustRepository -Path $modules
Save-PSResource -Name Pester -Version 5.7.1 -Repository PSGallery -TrustRepository -Path $modules
Import-Module (Join-Path $modules PSScriptAnalyzer/1.25.0/PSScriptAnalyzer.psd1)
Import-Module (Join-Path $modules Pester/5.7.1/Pester.psd1)
foreach ($file in Get-ChildItem -LiteralPath build -Recurse -File | Where-Object Extension -In '.ps1', '.psm1', '.psd1') {
# One file at a time: recursive analysis can crash inside 1.25.0.
$findings = @(Invoke-ScriptAnalyzer -Path $file.FullName -Settings ./PSScriptAnalyzerSettings.psd1)
if ($findings.Count -gt 0) { $findings | Format-Table; throw "Script analysis failed: $($file.Name)" }
}
# The Pester suites assert on child processes' exit codes themselves.
$PSNativeCommandUseErrorActionPreference = $false
$pester = New-PesterConfiguration
$pester.Run.Path = @('build/Assert-Coverage.Tests.ps1', 'build/GitHubRepository.Tests.ps1', 'build/Distribution.Tests.ps1', 'build/InstalledUpdates.Tests.ps1', 'build/PackageBytes.Tests.ps1', 'build/ReleaseSigning.Tests.ps1')
$pester.Run.PassThru = $true
if ((Invoke-Pester -Configuration $pester).Result -ne 'Passed') { throw 'Pester tests failed' }
scripts:
uses: self:KofTwentyTwo/ci-workflows/.gitea/workflows/node.yaml@v1
with:
node-version: '24'
install: npm ci --ignore-scripts
commands: |
npm run lint
npm test
npm audit signatures
+1 -6
View File
@@ -54,12 +54,7 @@ jobs:
# Analyze each file separately: recursive analysis can crash inside 1.25.0.
foreach($script in Get-ChildItem -LiteralPath build -Recurse -File | Where-Object Extension -In '.ps1', '.psm1', '.psd1')
{
# Run casing separately: the combined analyzer pass crashes in 1.25.0.
# Both passes together retain every configured rule and severity.
$findings = @(
Invoke-ScriptAnalyzer -Path $script.FullName -Settings ./PSScriptAnalyzerSettings.psd1 -IncludeRule PSUseCorrectCasing
Invoke-ScriptAnalyzer -Path $script.FullName -Settings ./PSScriptAnalyzerSettings.psd1 -ExcludeRule PSUseCorrectCasing
)
$findings = @(Invoke-ScriptAnalyzer -Path $script.FullName -Settings ./PSScriptAnalyzerSettings.psd1)
if($findings.Count -gt 0) { $findings | Format-Table; throw "Script analysis failed: $($script.Name)" }
}
- name: Verify script boundary tests
-1
View File
@@ -43,7 +43,6 @@ The command-line tool ships in each release as a self-contained single-file
startup update check on or off. Choices are saved immediately.
- **Help → Activity log:** a live view of the app's log, with an errors-only filter and a
shortcut to the log folder.
- **Help → View on GitHub / Submit an issue… / Community support…:** this app's repository, bug/feature templates and GitHub Discussions.
- **Help → Check for updates** and **Help → About**.
<!--#if (cli) -->
- **Command line:** `kof22-app greet --name Ada`, `kof22-app --version`, `kof22-app --help`.
-7
View File
@@ -7,13 +7,6 @@ repository files, and the shared CI, security, and release workflows. It impleme
This file is not copied into generated apps.
## Default Help menu
Generated apps include gclo's repository, issue-template and community-support
commands. Their URLs use the generated `AppInfo.RepositoryUrl`, so `--owner` and
`-n` point to the new project. Repository setup enables and validates GitHub
Discussions; this is required for Community support to work.
## Create an app
```powershell
-10
View File
@@ -135,16 +135,6 @@ Describe 'Generated repository administration' `
@($script:Writes | Where-Object { $_ -match 'POST|environments/release$' }).Count | Should -Be 0
}
It 'requires community Discussions and detects disabled support without writing' `
{
Invoke-GitHubRepositorySetup -Plan $script:Plan -Mode Apply -CheckSha ('a' * 40) -Confirm:$false
$script:State[$script:Root].has_discussions | Should -BeTrue
$script:State[$script:Root].has_discussions = $false
$script:Writes.Clear()
{ Invoke-GitHubRepositorySetup -Plan $script:Plan -Mode Validate } | Should -Throw '*Configuration mismatch: repos/Example/Notes*'
$script:Writes.Count | Should -Be 0
}
It 'previews all mutations without writing' `
{
Invoke-GitHubRepositorySetup -Plan $script:Plan -Mode Apply -CheckSha ('a' * 40) -WhatIf
+1 -1
View File
@@ -60,7 +60,7 @@ function Get-GitHubRepositoryPlan
@{ Method = 'PATCH'; Path = $root; Body = @{
allow_squash_merge = $true; allow_merge_commit = $false; allow_rebase_merge = $false
squash_merge_commit_title = 'PR_TITLE'; squash_merge_commit_message = 'PR_BODY'
delete_branch_on_merge = $true; has_issues = $true; has_discussions = $true; has_wiki = $false
delete_branch_on_merge = $true; has_issues = $true; has_wiki = $false
homepage = "https://github.com/$Repository#readme"
security_and_analysis = @{
secret_scanning = @{ status = 'enabled' }; secret_scanning_push_protection = @{ status = 'enabled' }
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.100",
"version": "10.0.403",
"rollForward": "latestFeature"
}
}
+3 -3
View File
@@ -10,9 +10,9 @@
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.12, )",
"resolved": "10.0.12",
"contentHash": "xi+BDjFpW+Sb+MHFHaH6Y/gV9I8BluFwRXc1QyCdoZbIK26eNiBeFuMTe/FMwc33G1wdHCyDg7CVTmb8OdQrMQ=="
"requested": "[10.0.14, )",
"resolved": "10.0.14",
"contentHash": "YlTamXppEQls6DYWt7RdUuwWDb0lyw/vXCaGeQpLCpsLmfwGuCkOz/vIGgMTV7ECsJcavkCOkxZXv9s8rdAHkQ=="
},
"kof22app.core": {
"type": "Project",
-10
View File
@@ -52,16 +52,6 @@ public static class AppIdentity
/// <summary>The app's bug/feature templates, independent of any upstream project credited in About.</summary>
public static Uri IssuesUrl { get; } = new(Info.RepositoryUrl!.AbsoluteUri.TrimEnd('/') + "/issues/new/choose");
/// <summary>Community questions and tips in the app's own repository.</summary>
public static Uri SupportUrl { get; } = new(Info.RepositoryUrl!.AbsoluteUri.TrimEnd('/') + "/discussions");
/// <summary>Loads and saves <see cref="AppSettings"/> at <see cref="AppPaths.SettingsFile"/>.</summary>
public static SettingsStore<AppSettings> SettingsStore { get; } = new(Paths.SettingsFile, AppSettingsContext.Default.AppSettings);
+3 -3
View File
@@ -22,9 +22,9 @@
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.12, )",
"resolved": "10.0.12",
"contentHash": "xi+BDjFpW+Sb+MHFHaH6Y/gV9I8BluFwRXc1QyCdoZbIK26eNiBeFuMTe/FMwc33G1wdHCyDg7CVTmb8OdQrMQ=="
"requested": "[10.0.14, )",
"resolved": "10.0.14",
"contentHash": "YlTamXppEQls6DYWt7RdUuwWDb0lyw/vXCaGeQpLCpsLmfwGuCkOz/vIGgMTV7ECsJcavkCOkxZXv9s8rdAHkQ=="
}
},
"net10.0/win-arm64": {},
-45
View File
@@ -1,45 +0,0 @@
/*
* Copyright (c) 2026 James Maes
* SPDX-License-Identifier: MIT
*/
using Microsoft.UI.Xaml;
namespace Kof22App;
/// <summary>Opens only the app's fixed public Help destinations after deliberate native menu actions.</summary>
public sealed partial class MainWindow
{
/// <summary>Shows this Windows app's repository in the user's default browser.</summary>
private async void GitHub_Click(object sender, RoutedEventArgs args) => await OpenHelpPageAsync(AppIdentity.Info.RepositoryUrl!);
/// <summary>Offers the app's issue templates without sending private document data.</summary>
private async void SubmitIssue_Click(object sender, RoutedEventArgs args) => await OpenHelpPageAsync(AppIdentity.IssuesUrl);
/// <summary>Opens community questions and tips in this app's repository.</summary>
private async void CommunitySupport_Click(object sender, RoutedEventArgs args) => await OpenHelpPageAsync(AppIdentity.SupportUrl);
/// <summary>A missing browser or OS launch failure is visible; no document path/content is included.</summary>
private async Task OpenHelpPageAsync(Uri target)
{
try
{
if(!await global::Windows.System.Launcher.LaunchUriAsync(target))
{
StatusText.Text = "The link could not be opened. Check your default browser.";
}
}
catch(Exception exception) when(exception is System.Runtime.InteropServices.COMException or InvalidOperationException or ArgumentException)
{
StatusText.Text = "The link could not be opened. Check your default browser.";
App.Log.Warning(FormattableString.Invariant($"A Help link could not be opened ({exception.GetType().Name}, 0x{exception.HResult:X8})."));
}
}
}
+1 -5
View File
@@ -26,11 +26,7 @@
<ToggleMenuFlyoutItem x:Name="SplashToggle" Text="Show splash at startup" Click="Splash_Click" />
<ToggleMenuFlyoutItem x:Name="UpdateCheckToggle" Text="Check for updates at startup" Click="UpdateCheck_Click" />
</MenuBarItem>
<MenuBarItem Title="Help" AccessKey="H">
<MenuFlyoutItem Text="View on GitHub" AccessKey="G" AutomationProperties.AutomationId="MenuGitHub" Click="GitHub_Click" />
<MenuFlyoutItem Text="Submit an issue…" AccessKey="I" AutomationProperties.AutomationId="MenuSubmitIssue" ToolTipService.ToolTip="Report a bug or request a feature on GitHub" Click="SubmitIssue_Click" />
<MenuFlyoutItem Text="Community support…" AccessKey="S" AutomationProperties.AutomationId="MenuCommunitySupport" ToolTipService.ToolTip="Ask a question or share tips in GitHub Discussions" Click="CommunitySupport_Click" />
<MenuFlyoutSeparator />
<MenuBarItem Title="Help">
<MenuFlyoutItem x:Name="CheckForUpdatesMenuItem" Text="Check for updates" Click="CheckForUpdates_Click" />
<MenuFlyoutItem x:Name="ActivityLogMenuItem" Text="Activity log" Click="ActivityLog_Click" />
<MenuFlyoutSeparator />
@@ -20,8 +20,6 @@ public class AppIdentityTests
Assert.EndsWith("_DATA_DIR", AppIdentity.Info.DataDirectoryVariable, StringComparison.Ordinal);
Assert.NotNull(AppIdentity.Info.RepositoryUrl);
Assert.NotEmpty(AppIdentity.Info.Attributions);
Assert.Equal("https://github.com/KofTwentyTwo/windows-app-template/issues/new/choose", AppIdentity.IssuesUrl.AbsoluteUri);
Assert.Equal("https://github.com/KofTwentyTwo/windows-app-template/discussions", AppIdentity.SupportUrl.AbsoluteUri);
}
+2 -7
View File
@@ -151,12 +151,7 @@ public sealed class AppSession : IDisposable
/// expand (its flyout opens before the menu is ready), so the menu is re-opened
/// until the item appears.
/// </summary>
public void InvokeMenuItem(string menuName, string itemAutomationId) => Invoke(OpenMenuItem(menuName, itemAutomationId));
/// <summary>Opens an owned native menu and returns its item without launching an external destination.</summary>
public AutomationElement OpenMenuItem(string menuName, string itemAutomationId)
public void InvokeMenuItem(string menuName, string itemAutomationId)
{
MainWindow.SetForeground();
AutomationElement menu = WaitFor(
@@ -176,7 +171,7 @@ public sealed class AppSession : IDisposable
Wait.UntilInputIsProcessed();
item = Retry.WhileNull(() => FindInApp(cf => cf.ByAutomationId(itemAutomationId)), timeout: TimeSpan.FromSeconds(3), interval: s_pollInterval, ignoreException: true).Result;
}
return item ?? throw new TimeoutException($"Menu item '{itemAutomationId}' never appeared under '{menuName}'. (app exited: {App.HasExited})");
Invoke(item ?? throw new TimeoutException($"Menu item '{itemAutomationId}' never appeared under '{menuName}'. (app exited: {App.HasExited})"));
}
-17
View File
@@ -36,23 +36,6 @@ public class AppTests
/// <summary>The real Help menu exposes the app's repository, issue reporting and community commands without launching a browser.</summary>
[Fact]
public void Help_PublicDestinations_AreAvailableInTheNativeMenu()
{
using var session = new AppSession();
_ = session.OpenMenuItem("Help", "MenuGitHub");
foreach((string identity, string label) in new[] { ("MenuGitHub", "GitHub"), ("MenuSubmitIssue", "issue"), ("MenuCommunitySupport", "support") })
{
AutomationElement item = session.WaitForElement(identity);
Assert.True(item.IsEnabled);
Assert.False(item.IsOffscreen);
Assert.Contains(label, item.Name, StringComparison.OrdinalIgnoreCase);
}
}
/// <summary>About: shows the build version.</summary>
[Fact]
public void About_ShowsTheBuildVersion()