2 Commits
Author SHA1 Message Date
james.maesandCodex 758e773772 fix(template): allow pinned security scanner containers
Hosted bootstrap showed that GitHub rejects the security builder container actions under the selected-actions policy. Permit only its exact published Gitleaks and Trivy image digests; preserve selected actions and required security gates.

Validation: 55 script cases, full analysis of changed module/tests, guide lint and normal hooks.

Refs: KofTwentyTwo/AppKit#10, KofTwentyTwo/AppKit#20

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-10 19:02:05 -05:00
james.maesandCodex fac10a78dc docs(template): clarify first restore and required checks
Document local package trials and creation of NuGet locks after generation. Refer onboarding to the canonical check table and correct standalone installed-update and tooling context names.

Refs: KofTwentyTwo/AppKit#10, KofTwentyTwo/AppKit#20

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-10 18:50:54 -05:00
10 changed files with 57 additions and 84 deletions

No files matched your search

-66
View File
@@ -1,66 +0,0 @@
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
+2 -2
View File
@@ -90,8 +90,8 @@ pre-commit install --hook-type pre-commit --hook-type commit-msg
| `ci / build-test` | Locked restore, zero-warning build, unit tests, 100% coverage gate |
| `ci / format` | `dotnet format --verify-no-changes --severity warn` |
| `ci / ui-tests` | FlaUI end-to-end tests of the app |
| `ci / installed-updates` | Real stable/dev installers, upgrades, restart and published-output UI smoke |
| `scripts / powershell`, `scripts / javascript` | Tool lint, setup/coverage boundary tests, review tests and dependency signatures |
| `installed-updates` | Real stable/dev installers, upgrades, restart and published-output UI smoke |
| `powershell`, `javascript` | Tool lint, setup/coverage boundary tests, review tests and dependency signatures |
| `codeql / analyze (javascript-typescript)` | Static analysis of repository review tooling |
| `review / automated` (single maintainer) | Current-commit automated review acknowledged by the configured human maintainer |
+2
View File
@@ -61,6 +61,8 @@ Prerequisites:
- Optionally Visual Studio 2026 (Windows App SDK workload) or Rider
```powershell
# Once after generation, create and commit the NuGet locks locally:
# dotnet restore Kof22App.slnx -p:Platform=x64
dotnet restore Kof22App.slnx -p:Platform=x64 --locked-mode
dotnet build Kof22App.slnx -p:Platform=x64 -warnaserror
dotnet test Kof22App.slnx -p:Platform=x64 --no-build --filter "FullyQualifiedName!~UiTests"
+17 -8
View File
@@ -18,6 +18,15 @@ dotnet new k22-winapp -n Contoso.Notes --appId contoso-notes --displayName "Cont
--description "Quick notes that stay out of the way." -o Contoso.Notes
```
To try the installable package before its first publication, run from this clone:
```powershell
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
dotnet new k22-winapp -n Contoso.Notes --appId contoso-notes --displayName "Contoso Notes" -o artifacts/Contoso.Notes
# Add --cli false for a GUI-only app; use a fresh output directory each time.
```
| Option | Default | What it sets |
| --- | --- | --- |
| `-n`, `--name` | (required) | Solution, project, namespace, and executable names (`Contoso.Notes.slnx`, `Contoso.Notes.Core`, `Contoso.Notes.exe`), and the repository name in links |
@@ -32,8 +41,10 @@ The copyright year in license headers becomes the current year.
## After generating
The generator omits dependency lock files. Before the first commit, run the README's
restore, build, test, coverage and format commands, then commit the resulting
The generator omits NuGet dependency lock files. Before the first commit, run
`dotnet restore <name>.slnx -p:Platform=x64` in the generated directory to create
them, then run the README's locked restore, build, test, coverage and format
commands. Commit the resulting
`packages.lock.json` files. Install the included hooks with
`pre-commit install --hook-type pre-commit --hook-type commit-msg` after `git init`.
The generated PowerShell tooling gate uses PSScriptAnalyzer 1.25.0.
@@ -41,12 +52,10 @@ The generated PowerShell tooling gate uses PSScriptAnalyzer 1.25.0.
1. Follow [the preview/apply/validate setup runbook](docs/repository-setup.md).
Create the GitHub repository (public, Product tier), push `main`, and apply the
[repository standard's](https://github.com/KofTwentyTwo/standards/blob/main/standards/repository.md)
settings and rulesets, with these required checks: `pr / title`, `pr / dco`,
`pr / dependency-review`, `security / secrets`, `security / sca`,
`security / workflows`, `codeql / analyze (csharp)`, `codeql / analyze (actions)`,
`ci / build-test`, `ci / format`, `ci / ui-tests`, `scripts / powershell`,
`scripts / javascript`, `codeql / analyze (javascript-typescript)`, and
`review / automated` for the single-maintainer policy.
settings and rulesets. Use the setup tool's required contexts and
[CONTRIBUTING.md](CONTRIBUTING.md)'s check table; standalone job names have no
workflow-name prefix. The source repository additionally requires `template`;
generated application repositories do not.
2. Create the `release` environment limited to `v*` tags, and list the repository in the
[standards repository inventory](https://github.com/KofTwentyTwo/standards/blob/main/MAINTAINERS.md#repositories)
(`K22-REPO-42`; the conformance checker fails until it is listed).
+15
View File
@@ -47,6 +47,21 @@ Describe 'Required checks match standalone workflow jobs' `
Describe 'Generated repository administration' `
{
It 'allows the digest-pinned security containers without permitting arbitrary container actions' `
{
$plan = Get-GitHubRepositoryPlan -Repository 'Example/Notes'
$policy = ($plan.Requests | Where-Object Path -Like '*/actions/permissions/selected-actions').Body
$policy.patterns_allowed.Count | Should -Be 3
$policy.patterns_allowed | Should -Contain 'KofTwentyTwo/standards/*@*'
$containers = @($policy.patterns_allowed | Where-Object { $_.StartsWith('docker://', [StringComparison]::Ordinal) })
$containers.Count | Should -Be 2
foreach($container in $containers)
{
$container | Should -Match '^docker://ghcr\.io/(gitleaks/gitleaks:v8\.30\.1|aquasecurity/trivy:0\.75\.0)@sha256:[0-9a-f]{64}$'
$container | Should -Not -Match '\*'
}
}
InModuleScope GitHubRepository `
{
BeforeEach `
+8 -1
View File
@@ -74,7 +74,14 @@ function Get-GitHubRepositoryPlan
@{ Method = 'PUT'; Path = "$root/actions/permissions/workflow"; Body = @{ default_workflow_permissions = 'read'; can_approve_pull_request_reviews = $false } },
@{ Method = 'PUT'; Path = "$root/actions/permissions/fork-pr-contributor-approval"; Body = @{ approval_policy = 'all_external_contributors' } },
@{ Method = 'PUT'; Path = "$root/actions/permissions"; Body = @{ enabled = $true; allowed_actions = 'selected' } },
@{ Method = 'PUT'; Path = "$root/actions/permissions/selected-actions"; Body = @{ github_owned_allowed = $true; verified_allowed = $true; patterns_allowed = @('KofTwentyTwo/standards/*@*') } },
@{ Method = 'PUT'; Path = "$root/actions/permissions/selected-actions"; Body = @{
github_owned_allowed = $true; verified_allowed = $true
# Exact container actions used by the published v0.1.2 security builder.
patterns_allowed = @('KofTwentyTwo/standards/*@*',
'docker://ghcr.io/gitleaks/gitleaks:v8.30.1@sha256:c00b6bd0aeb3071cbcb79009cb16a60dd9e0a7c60e2be9ab65d25e6bc8abbb7f',
'docker://ghcr.io/aquasecurity/trivy:0.75.0@sha256:af6acf9a6b85dfe389a1941505c0ce9efef52a4719635e1a962f022a3d855daa')
}
},
@{ Method = 'PUT'; Path = "$root/environments/release"; Body = @{ deployment_branch_policy = @{ protected_branches = $false; custom_branch_policies = $true } } },
@{ Method = 'POST'; Path = "$root/environments/release/deployment-branch-policies"; Body = @{ name = 'v*'; type = 'tag' } },
@{ Method = 'POST'; Path = "$root/rulesets"; Body = $main },
+6
View File
@@ -66,6 +66,12 @@ and environment protections are retained. API failures stop immediately; earlier
successful requests may remain, so validate and resolve the reported failure before
rerunning. The tool never creates repositories, pushes, merges, tags or releases.
The selected Actions policy permits the shared standards workflows, GitHub-owned
and verified actions, and the exact digest-pinned Gitleaks/Trivy container actions
used by the published security builder. GitHub also checks container actions
against this policy; missing entries prevent the security workflow from starting.
Review these exact references whenever the shared builder pin changes.
## Finish and verify
Register ownership/tier in the standards `MAINTAINERS.md` inventory through a
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.403",
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
+3 -3
View File
@@ -10,9 +10,9 @@
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.14, )",
"resolved": "10.0.14",
"contentHash": "YlTamXppEQls6DYWt7RdUuwWDb0lyw/vXCaGeQpLCpsLmfwGuCkOz/vIGgMTV7ECsJcavkCOkxZXv9s8rdAHkQ=="
"requested": "[10.0.12, )",
"resolved": "10.0.12",
"contentHash": "xi+BDjFpW+Sb+MHFHaH6Y/gV9I8BluFwRXc1QyCdoZbIK26eNiBeFuMTe/FMwc33G1wdHCyDg7CVTmb8OdQrMQ=="
},
"kof22app.core": {
"type": "Project",
+3 -3
View File
@@ -22,9 +22,9 @@
},
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.14, )",
"resolved": "10.0.14",
"contentHash": "YlTamXppEQls6DYWt7RdUuwWDb0lyw/vXCaGeQpLCpsLmfwGuCkOz/vIGgMTV7ECsJcavkCOkxZXv9s8rdAHkQ=="
"requested": "[10.0.12, )",
"resolved": "10.0.12",
"contentHash": "xi+BDjFpW+Sb+MHFHaH6Y/gV9I8BluFwRXc1QyCdoZbIK26eNiBeFuMTe/FMwc33G1wdHCyDg7CVTmb8OdQrMQ=="
}
},
"net10.0/win-arm64": {},