* Generate a CycloneDX SBOM per published NuGet package
GitHub's dependency-graph SBOM export reports unresolved version ranges
for most packages and can't see which source projects get merged into
which final package by Numerge. Add a CreateSbom Nuke target that runs
CycloneDX against each project's already-restored assets, reassembles
the Numerge merge groups from numerge.json, and publishes one accurate
SBOM per shipped package as a pipeline artifact.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix SBOM dependency graph merge and cover bundled npm packages
The per-package SBOM merge only kept the dependency-graph (dependsOn)
edges from the first-scanned constituent project, so the flagship
Avalonia package listed 38 components but wired only 3 into its graph.
Union dependsOn edges by ref (each constituent's -sn/-sv override makes
its root collapse onto the shared final-package ref) so all edges are
retained - the Avalonia root now reports 17 direct dependencies.
cyclonedx-dotnet only walks the NuGet graph, so JS that ships inside a
package via a Bun/npm-built webapp was invisible: Avalonia.Browser's
staticwebassets and Avalonia.DesignerSupport's embedded previewer (which
merges into Avalonia). Scan each constituent's webapp/package.json
production dependencies, resolving versions from the installed
node_modules (git-pinned deps become pkg:github purls), so those shipped
components appear in the SBOM.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Enrich npm SBOM components to match NuGet component fidelity
Previously the bundled-webapp npm packages were added as bare inventory
entries: no bom-ref, no license, no hashes, direct-only, and orphaned
from the dependency graph - second-class next to the fully-formed NuGet
components cyclonedx-dotnet emits.
Walk the installed node_modules transitively so npm depth matches the
NuGet side (e.g. react-dom now pulls in scheduler, loose-envify,
object-assign; loose-envify pulls in js-tokens), assign each component a
bom-ref, resolve its license from the installed package.json (SPDX id or
expression, with legacy license/licenses object fallback), and wire the
whole tree into the CycloneDX dependency graph - the shipped package now
declares its direct npm deps and every transitive edge below them.
Hashes are intentionally omitted for npm: the verifiable integrity
hashes live in bun's binary lockfile, not the installed tree, and a hash
of the unpacked directory couldn't be checked against a registry.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Enrich SBOM root component and verify package contents
Two gaps remained after the npm work. First, cyclonedx-dotnet emits only
type/name/version for the root metadata.component - no manufacturer,
license, or provenance, which a CRA-facing SBOM is expected to carry.
Read the shipped .nuspec and fill in purl, publisher/supplier (also set
as the document-level manufacturer), SPDX license, description,
copyright, and website/vcs external references.
Second, nothing checked what the package actually ships against the
components derived from the dependency graph. Scan the final .nupkg's
binaries: Avalonia's own Numerge-merged modules (e.g. Avalonia.Base,
Avalonia.Controls folded into Avalonia; Avalonia.Win32.Automation into
Avalonia.Win32) are recorded as manufacturer-supplied components with a
SHA-512 of the shipped bytes, and any third-party binary that no
restored dependency accounts for is added and flagged - a regression
guard so bundled binaries can't silently escape the SBOM. Reference
assemblies under ref/ and the package's own primary assembly are skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Exclude type-only @types/* packages from the SBOM
The DesignerSupport previewer's package.json lists @types/react and
@types/react-dom under "dependencies" rather than "devDependencies", so
they were being scanned in. TypeScript declaration packages are stripped
by esbuild and never appear in the shipped bytes, so they fall outside
the SBOM's scope of delivery. Skip @types/* at both the direct and
transitive level (which also drops csstype, only reachable via
@types/react), leaving the SBOM listing exactly the npm packages that
are actually bundled and delivered.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add SBOM coverage rules to Copilot review instructions
Instruct code review to flag two cases that would otherwise let a
package ship without accurate SBOM coverage (EU CRA): a newly published
NuGet package not covered by CreateSbom, and a change to what
dependencies are delivered with a component - notably adding npm/JS to a
previously .NET-only component, bundling third-party binaries directly,
or new Numerge merge groups - without a corresponding update to
nukebuild/SbomGenerator.cs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Link package-content binaries into the dependency graph
The binary:* components from the .nupkg content scan were added to
components but had no dependency node or root edge, leaving them
unreachable for SBOM consumers that traverse the graph from the root -
so the shipped merged modules (and any flagged third-party binary)
didn't appear in the delivered dependency tree. Give each its own leaf
node and a dependsOn edge from the root component.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Prune dangling dependency edges from generated SBOMs
cyclonedx-dotnet leaves dependsOn edges pointing at packages it excluded
as dev dependencies (e.g. analyzers stripped by -ed), so after exclusion
the graph references components that aren't in the SBOM. Drop any
dependsOn target that doesn't resolve to a present component, keeping the
dependency graph internally consistent - consistent with treating those
build-only dev dependencies as out of scope of delivery.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Reference upstream issue for dangling-edge prune
Cite CycloneDX/cyclonedx-dotnet#761 in the PruneDanglingDependencyEdges
comment so a future reader knows the dangling dependsOn edges are an
upstream bug (still reproducing in 6.2.0), not something introduced here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Resolve CycloneDX via NuGetPackage instead of a global tool install
`dotnet tool update --global CycloneDX` mutated the developer's (and CI
agent's) global tool set as a side effect of running the build. Follow the
same pattern as the other build tools (ApiCompat, ApiDiff, ilrepack): pin
the package via PackageDownload in _build.csproj and resolve it through a
[NuGetPackage] Tool, passed into SbomGenerator.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Reuse existing nuspec/numerge parsing helpers in the SBOM generator
Drop the hand-rolled Numerge* POCOs in favour of Numerge.MergeConfiguration.
LoadFile (already used by CreateNugetPackages), and have BuildToNuGetCache
call SbomGenerator.ReadPackageId instead of duplicating the nuspec-id
extraction inline, so the two can't drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Resolve each package's nupkg from a prebuilt id->path map
GenerateForPackage's fallback re-globbed and re-parsed every nupkg in the
output directory once per final package whenever the versioned-filename glob
missed - O(n^2) archive reads. Build the id->path map once in Generate
(reading each nupkg's id a single time) and look each package up in it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Degrade unrecognised git/URL npm specifiers instead of throwing
ResolveNpmComponent routed any git/URL dependency range to a GitHub-only
parser that threw NotSupportedException on anything else (GitLab/Bitbucket,
a raw tarball URL, ...). Because CreateSbom is a hard release dependency,
one such spec would fail the whole build. Fall back to a generic component
(preferring the installed on-disk version) with a warning instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Guard against an npm package.json with no version field
A package.json without a "version" (valid for private packages) made the
null-forgiving ["version"]! throw an NRE, aborting the build. Fall back to
the declared range with a warning instead.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Don't emit a file-license path as an SPDX id
A nuspec <license type="file"> (from PackageLicenseFile) stores a file path,
not an SPDX id. It was being fed to SpdxToLicenses and emitted as
{license:{id:"licenses/LICENSE"}}, an invalid SPDX id that can fail license
validation. Map type="file" to a license name instead. (Avalonia's own
packages all use PackageLicenseExpression=MIT, so this is a latent-correctness
fix rather than a live bug.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Generate and publish SBOMs from the macOS CI job too
CreateSbom was only wired into CiAzureWindows, so the macOS job's NuGetOSX
artifact - which carries the real macOS-native binaries the Windows build
can't produce - shipped without matching SBOMs. Wire CreateSbom into
CiAzureOSX and publish its output as a SBOMOSX artifact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Embed each package's CycloneDX SBOM inside its .nupkg
The SBOM previously existed only as a detached build artifact. Also embed
it at _manifest/cyclonedx/bom.cdx.json inside the shipped .nupkg so it
travels with the package, mirroring the _manifest/ layout Microsoft.Sbom.
Targets uses for SPDX. A .nupkg is an OPC package, so [Content_Types].xml
gains a json default or strict OPC readers (including NuGet signature
verification) would reject the modified package. Skips already-signed
packages to avoid invalidating their signature.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Address Copilot review: SBOM zip ordering, nuspec read, component key
- Order ZipFiles after CreateSbom so zipped NuGet artifacts include the
embedded SBOM.
- Read the .nuspec entry with an ordinal EndsWith and dispose its stream.
- Key components by purl/bom-ref, falling back to name+version so distinct
versions sharing a name aren't de-duplicated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Model bundled assemblies as a composition, not dependencies
The numerge-folded first-party assemblies (Avalonia.Base, Avalonia.Controls,
etc.) are constituents of the package, not dependencies of it. Represent them
as a CycloneDX "assembly" via a top-level compositions entry with
aggregate=complete instead of drawing dependsOn edges from the root - contains
and depends-on are distinct relationships. They stay flat top-level components
so their per-assembly hashes remain visible to scanners that ignore nested
components, and the dependency graph is left holding only genuine dependencies.
Also carry the package's licence onto these first-party components, which
previously came out blank (cyclonedx-dotnet only sets it on the root).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* [Text] Move Unicode trie generation to a dedicated Nuke-driven tool
The trie and enum generators used to live as [Fact(Skip = ...)] tests in
Avalonia.Base.UnitTests, writing to a local Generated/ folder that had to
be hand-copied into src/Avalonia.Base/Media/TextFormatting/Unicode/. Move
them into a standalone tool project invoked by a new GenerateUnicodeData
Nuke target that writes the canonical files in place.
- New src/tools/Avalonia.UnicodeTrieGenerator console app (mirrors the
GenerateCppHeaders pattern), signed via SharedVersion.props and added
to Avalonia.Base's InternalsVisibleTo for UnicodeTrieBuilder access.
- UCD downloads are cached under artifacts/ucd-cache/<version>/ so
reruns work offline; the target accepts --output and --cache args.
- LineBreakClass and WordBreakClass orderings now come from
PropertyValueAliases (with hot-path tags seeded at the front for
cache density), removing the BreakPairTable.txt resource and the
dead BreakPairTable.cs writer that nothing in src/ referenced.
- ParseDataEntries dedupes by Name OR Tag so seeds can use friendlier
names (Carriage_Return) than UCD's WB primaries (CR).
- Deprecated WB classes (E_Base, E_Modifier, Glue_After_Zwj,
E_Base_GAZ) are filtered out — they have no codepoint mappings.
- Conformance tests stay in the test project, repointed at a small
UnicodeTestData.Ucd constant.
Regenerating into the canonical Unicode directory produces a zero diff
against the previously committed enums and tries, confirming the port
preserves output byte-for-byte. Invoke with `./build.cmd GenerateUnicodeData`.
* Align generated file names
Validate all tries
Unify UCD path
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Make the trie generation standalone
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Update to xunit.v3
* Little more progress
* More fixes
* Keep VSTest supported
* Adjust Nuke
* Few fixes
* Fix for xunit 2
* Fix GetData override
* Adjust
* Use MTP for xunit 2
* Fix test
* Better fix
* --no-progress
* Few more fixes
* no progress
* Fix test
* Better fix
* TRX
* Move to Directory.Build.props
* Unify on MTP v2
* Update
* Update to stable
* 1.0.1
* 1.0.2
* Fix some warnings
* Fix more warnings
* Fix more warnings
* Try to fix dnx random failure
* Use full paths for WebAppDir everywhere
* Call dnx explicitly on CI
---------
Co-authored-by: Max Katz <maxkatz6@outlook.com>
* Replace node.js npm with bun
* Run bun test target, instead of direct npm, remove `SkipPreviewer` prop
* Add "--yes" argument to dnx command
* Remove unused scripts
* Added BuildTests projects
* Added VerifyXamlCompilation build target
* Use TargetsTriggerByCompilation for XAML compilation
* Add *.binlog to gitignore
* VerifyXamlCompilation target: set NuGetPackageRoot
* Ensure WpfHybrid build test uses two markup compilation passes
* Fail build tests restore immediately if AvaloniaVersion isn't set
* Fix "could not extract MVID" for up-to-date builds
* Run VerifyXamlCompilation on CI
* Add FSharp build test
* Add .NET 8 target to packable projects
* Update test projects
* Update sample projects to target .NET 8
* Update main Avalonia package to target .NET 8 as well
* Remove MSBuildEnableWorkloadResolver hack and some minor change
* Use net8.0 in nuke build as well
* Pin 8.0.0 SDK
* Adjust API validation
* Pin a valid version
* Remove net461 target from Avalonia package
* Remove unused net6.0 target from Avalonia.Designer.HostApp
* Adjust API diff
* Update ControlCatalog.Android.csproj
* Remove MSBuildEnableWorkloadResolver
* Fix Browser issues on .NET 8
* Fix .NET 8 error
* Fix merge conflicts
* Replace explicit TFMs in .csproj files with a centralized registry
* Fix merge conflict
* Fix AvsLegacyWindowsTargetFrameworks and add hacks for the tests
* Update XCode project CommandLineArgument
---------
Co-authored-by: Nikita Tsukanov <keks9n@gmail.com>
* BuildToNuGetCache target
* Skip more stuff that's not required for quick local testing of apps
---------
Co-authored-by: Max Katz <maxkatz6@outlook.com>