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
19 changes: 9 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,22 @@ jobs:
.\tools\test-release-compliance-negative.ps1
-PublishDirectory artifacts/win-x64

- name: Verify final package hashes and provenance failures
shell: pwsh
run: >-
.\tools\test-final-package-gate-negative.ps1
-PublishDirectory artifacts/win-x64

- name: Create and verify Windows alpha archive
shell: pwsh
run: |
$archivePath = "artifacts/${{ steps.release.outputs.artifact_name }}.zip"
$reportPath = "artifacts/${{ steps.release.outputs.artifact_name }}-compliance.json"
Remove-Item $archivePath -Force -ErrorAction SilentlyContinue
Remove-Item $reportPath -Force -ErrorAction SilentlyContinue
Compress-Archive `
-Path "artifacts/win-x64/*" `
-DestinationPath $archivePath `
-CompressionLevel Optimal
.\tools\verify-release-compliance.ps1 `
.\tools\new-verified-release-package.ps1 `
-DirectoryPath artifacts/win-x64 `
-ArchivePath $archivePath `
-ReportPath $reportPath
.\tools\verify-dotnet-component-coverage.ps1 `
-ArchivePath $archivePath
-ReportPath $reportPath `
-DistributionChannel github-actions-artifact

- name: Upload verified Windows alpha and compliance report
uses: actions/upload-artifact@v4
Expand Down
82 changes: 32 additions & 50 deletions docs/BUILD.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build and package SightAdapt as a standalone EXE
# Build and package SightAdapt

These steps create a self-contained Windows x64 executable and verified ZIP.
These steps create the maintained self-contained Windows x64 portable ZIP and its schema-3 compliance report.

## Prerequisites

Expand All @@ -18,22 +18,26 @@ Windows Desktop Runtime 8.0.29
.\tools\verify-release-metadata.ps1
```

`Directory.Build.props` is the canonical source for product, SDK, runtime, RID, publish mode and artifact metadata. A mismatched reviewed configuration or blocked maintainer decision stops the build.
`Directory.Build.props` is the canonical source for product, SDK, runtime, RID, publish mode and artifact metadata.

## 2. Restore and test
## 2. Restore, build and test

```powershell
dotnet restore .\src\SightAdapt\SightAdapt.csproj
dotnet restore .\tests\SightAdapt.Tests\SightAdapt.Tests.csproj

dotnet build .\src\SightAdapt\SightAdapt.csproj `
--configuration Release `
--no-restore

dotnet test .\tests\SightAdapt.Tests\SightAdapt.Tests.csproj `
--configuration Release `
--no-restore
```

Both `project.assets.json` files are required later. The SBOM generator reads all package entries and dependency edges from the application and test restore graphs, not only direct `PackageReference` elements.
Both `project.assets.json` files are consumed by the complete SBOM/license inventory.

## 3. Publish and capture bundle inputs
## 3. Publish

```powershell
dotnet publish .\src\SightAdapt\SightAdapt.csproj `
Expand All @@ -42,83 +46,57 @@ dotnet publish .\src\SightAdapt\SightAdapt.csproj `
--output .\artifacts\win-x64
```

The project captures the SDK-provided `FilesToBundle` list in `artifacts\dotnet-files-to-bundle.tsv`. This temporary file identifies exact single-file inputs; absolute paths are not copied into the release ZIP.
The publish target captures SDK `FilesToBundle` inputs in `artifacts\dotnet-files-to-bundle.tsv`. The temporary file is not included in the ZIP.

## 4. Import exact official .NET notices
## 4. Generate legal and supply-chain evidence

```powershell
.\tools\generate-dotnet-notices.ps1 `
-PublishDirectory .\artifacts\win-x64
```

This verifies the runtime release train and official SDK ZIP SHA-512, then writes the exact Microsoft license, third-party notices and base metadata.

## 5. Generate component-level notice coverage

```powershell
.\tools\generate-dotnet-component-coverage.ps1 `
-PublishDirectory .\artifacts\win-x64
```

This maps every embedded package asset and every loose runtime binary to:

- exact package and version;
- package SHA-512;
- package-relative asset path;
- component SHA-256;
- reviewed notice mapping.

The step also adds exact package notice sections for components not covered by the official .NET release bundle. Unreviewed, non-shipped or unmapped package components fail the build.

## 6. Generate redistribution summary

```powershell
.\tools\generate-dotnet-redistribution-notice.ps1 `
-PublishDirectory .\artifacts\win-x64
```

## 7. Generate SBOM and license report

```powershell
.\tools\generate-sbom.ps1 `
-PublishDirectory .\artifacts\win-x64
```

The generator writes `DEPENDENCIES.md`, schema-2 `LICENSE-REPORT.json` and SPDX 2.3 `SBOM.spdx.json` from the same inventory. For every NuGet package it retains exact package SHA-512 and `.nuspec` SHA-256 plus declared license metadata. Direct and transitive application/test packages are discovered automatically; the dependency policy supplies decisions and custom overrides rather than the inventory itself.
These steps generate exact Microsoft notices, component mappings, `DEPENDENCIES.md`, schema-2 `LICENSE-REPORT.json` and SPDX 2.3 `SBOM.spdx.json`.

## 8. Run negative checks
## 5. Run negative checks

```powershell
.\tools\test-sbom-license-negative.ps1 `
-PublishDirectory .\artifacts\win-x64

.\tools\test-release-compliance-negative.ps1 `
-PublishDirectory .\artifacts\win-x64

.\tools\test-final-package-gate-negative.ps1 `
-PublishDirectory .\artifacts\win-x64
```

The validators must reject a transitive package with no resolved license, an incomplete package, stale redistribution metadata and a package with a deliberately removed component mapping.
The tests reject unknown licenses, incomplete/stale/unmapped packages, changed archive bytes, incorrect commit provenance, inconsistent tags and inactive channels.

## 9. Create and verify the final ZIP
## 6. Create the maintained portable ZIP

```powershell
$archive = '.\artifacts\SightAdapt-0.5.0.50-alpha-win-x64.zip'
$report = '.\artifacts\SightAdapt-0.5.0.50-alpha-win-x64-compliance.json'

Compress-Archive `
-Path '.\artifacts\win-x64\*' `
-DestinationPath $archive `
-CompressionLevel Optimal

.\tools\verify-release-compliance.ps1 `
-DirectoryPath .\artifacts\win-x64 `
.\tools\new-verified-release-package.ps1 `
-DirectoryPath '.\artifacts\win-x64' `
-ArchivePath $archive `
-ReportPath $report

.\tools\verify-dotnet-component-coverage.ps1 `
-ArchivePath $archive
-ReportPath $report `
-DistributionChannel 'local-portable-zip'
```

The compliance gate checks license evidence, package counts, graph edges and scope-aware SPDX relationships in addition to the existing package invariants. Retain the verified archive and compliance report together.
Do not create an official package with a separate `Compress-Archive` command. The reusable entry point creates the archive, runs all final validators and records per-file SHA-256 equality and Git provenance.

GitHub Actions uses the same command with channel `github-actions-artifact`; commit/ref and workflow/run values are read from the GitHub environment.

## Expected package root

Expand All @@ -136,7 +114,11 @@ LICENSE-REPORT.json
PRIVACY.md
```

Additional loose native runtime DLLs are allowed only when schema-2 metadata maps each file by exact output path and SHA-256.
The compliance report is kept next to the ZIP because it contains the final archive SHA-256.

## Maintained scope

`release/distribution-channels.json` currently allows only the GitHub Actions portable ZIP and the local maintainer portable ZIP. GitHub Releases, installers, store packages and mirrors remain inactive until separately implemented with the same final gate.

## Clean rebuild

Expand All @@ -147,4 +129,4 @@ Remove-Item .\artifacts\SightAdapt-*-win-x64.zip -Force -ErrorAction SilentlyCon
Remove-Item .\artifacts\SightAdapt-*-win-x64-compliance.json -Force -ErrorAction SilentlyContinue
```

Do not publish when metadata review, notice import, component coverage, complete dependency/license review, negative checks or final-package validation fails.
Do not publish when any generator, negative test or final-package validation fails.
151 changes: 58 additions & 93 deletions docs/PACKAGING.md
Original file line number Diff line number Diff line change
@@ -1,126 +1,91 @@
# SightAdapt binary packaging standard

This document defines the minimum contents and verification rules for every binary distribution of SightAdapt.
## Maintained format

SightAdapt currently maintains one binary distribution format: a self-contained Windows x64 portable ZIP.

The allowed producer contexts are registered in `release/distribution-channels.json`. Both the GitHub Actions artifact and a maintainer-created local ZIP must use `tools/new-verified-release-package.ps1`.

Installers, store packages, GitHub Releases and mirrors are not maintained channels yet. Their future implementation must activate a registry entry and use the reusable final-package gate.

## Canonical required files

The machine-readable source of truth is `release/required-files.txt`.
The machine-readable package manifest is `release/required-files.txt`.

Every portable ZIP must contain at its root:

| File | Purpose |
|---|---|
| `SightAdapt.exe` | Application executable and single-file container |
| `LICENSE.txt` | MIT License for SightAdapt project code |
| `THIRD-PARTY-NOTICES.txt` | Exact .NET notices and package-specific notice sections |
| `DOTNET-LICENSE-NOTICE.txt` | Exact Microsoft .NET license text and source metadata |
| `DOTNET-NOTICE-METADATA.json` | Exact package, component, notice and checksum evidence |
| `MICROSOFT-DOTNET-REDISTRIBUTION.txt` | Generated maintainer-reviewed redistribution notice |
| `THIRD-PARTY-NAMES-AND-DRM-NOTICE.txt` | Mark ownership, no-endorsement and DRM/access-control boundaries |
| `DEPENDENCIES.md` | Human-readable dependency inventory generated from complete restore graphs |
| `SBOM.spdx.json` | SPDX 2.3 package graph and shipped-file inventory |
| `LICENSE-REPORT.json` | Schema-2 dependency-license evidence and policy result |
| `PRIVACY.md` | Application privacy and local-data notice |

A package is incomplete if a required file is missing, unreadable, stale, inconsistent with pinned inputs, contains a binary without exact component notice evidence or has an unresolved dependency/license result.
| `THIRD-PARTY-NOTICES.txt` | Exact .NET and package-specific notices |
| `DOTNET-LICENSE-NOTICE.txt` | Exact Microsoft .NET license text and source evidence |
| `DOTNET-NOTICE-METADATA.json` | Exact package/component/notice evidence |
| `MICROSOFT-DOTNET-REDISTRIBUTION.txt` | Maintainer-reviewed redistribution summary |
| `THIRD-PARTY-NAMES-AND-DRM-NOTICE.txt` | Affiliation, trademark and DRM boundaries |
| `DEPENDENCIES.md` | Human-readable dependency inventory |
| `SBOM.spdx.json` | SPDX 2.3 package and file inventory |
| `LICENSE-REPORT.json` | Complete license-evidence and policy result |
| `PRIVACY.md` | Local data and privacy notice |

The files must be readable without running SightAdapt.

## Publish sequence

1. `SightAdapt.csproj` copies the static legal baseline and captures SDK `FilesToBundle` inputs.
2. `generate-dotnet-notices.ps1` imports exact official .NET legal text.
3. `generate-dotnet-component-coverage.ps1` maps all embedded and loose package components and adds package-specific notices.
4. `generate-dotnet-redistribution-notice.ps1` renders the reviewed redistribution summary.
5. `generate-sbom.ps1` traverses complete application/test restore graphs, collects package license evidence and generates `DEPENDENCIES.md`, `LICENSE-REPORT.json` and `SBOM.spdx.json`.
6. Negative tests prove rejection of incomplete, stale, unmapped and unknown-license inputs.

## Exact component coverage

Schema-2 `DOTNET-NOTICE-METADATA.json` records:

- SHA-256 of the temporary `FilesToBundle` manifest;
- exact package identities and NuGet SHA-512 values;
- repository and license metadata;
- notice mappings for exact .NET release text and package-specific terms;
- one sanitized record per embedded or loose component;
- package-relative asset path and component SHA-256;
- zero unmapped external components.

The reviewed alpha contains 452 mapped package components across:

- `Microsoft.NETCore.App.Runtime.win-x64/8.0.29`;
- `Microsoft.WindowsDesktop.App.Runtime.win-x64/8.0.29`;
- `Microsoft.Windows.SDK.NET.Ref/10.0.19041.56`.

`Microsoft.Windows.SDK.NET.Ref` is classified as `shipped-embedded`, because `Microsoft.Windows.SDK.NET.dll` and `WinRT.Runtime.dll` are present in `FilesToBundle`.

## Complete dependency and license inventory
1. restore, build and test;
2. publish into a clean staging directory;
3. import exact Microsoft notices;
4. generate component-level notice coverage;
5. generate the redistribution summary;
6. generate `DEPENDENCIES.md`, `LICENSE-REPORT.json` and `SBOM.spdx.json`;
7. run negative compliance tests;
8. call the reusable package entry point;
9. retain the ZIP and schema-3 compliance report together.

The SBOM generator uses both application and test `project.assets.json` files. Every NuGet package in `libraries`, every package dependency edge in `targets`, every framework download dependency, each package contributing published components, the pinned SDK and each maintained GitHub Action is inventoried.

For NuGet packages the generated evidence retains exact package SHA-512, `.nuspec` SHA-256, declared license metadata, packaged-license SHA-256 where applicable, repository information and direct/transitive scope. `release/dependency-policy.json` supplies allow/deny/review decisions and selected custom-license overrides; it does not define which packages exist.

SPDX root relationships distinguish scopes:

- shipped packages: `SightAdapt DEPENDS_ON package`;
- test-only packages: `package TEST_DEPENDENCY_OF SightAdapt`;
- SDK, Actions, restore-only and application-build packages: `package BUILD_DEPENDENCY_OF SightAdapt`.

Package-to-package restore edges remain `DEPENDS_ON` and identify the originating graph.

## Final package validation
## Reusable package command

```powershell
$archive = '.\artifacts\SightAdapt-0.5.0.50-alpha-win-x64.zip'
$report = '.\artifacts\SightAdapt-0.5.0.50-alpha-win-x64-compliance.json'

Compress-Archive `
-Path '.\artifacts\win-x64\*' `
-DestinationPath $archive `
-CompressionLevel Optimal

.\tools\verify-release-compliance.ps1 `
.\tools\new-verified-release-package.ps1 `
-DirectoryPath '.\artifacts\win-x64' `
-ArchivePath $archive `
-ReportPath $report

.\tools\verify-dotnet-component-coverage.ps1 `
-ArchivePath $archive
-ReportPath $report `
-DistributionChannel 'local-portable-zip'
```

The general compliance gate validates package, metadata, license-report and SBOM invariants. The focused component validator independently verifies package evidence, notice mappings, exact loose-binary hashes and absence of unmapped binaries.
In GitHub Actions the channel is `github-actions-artifact`; GitHub environment variables supply commit/ref and workflow/run provenance.

## Negative checks
## Final-package invariants

```powershell
.\tools\test-sbom-license-negative.ps1 `
-PublishDirectory '.\artifacts\win-x64'
The reusable gate verifies:

.\tools\test-release-compliance-negative.ps1 `
-PublishDirectory '.\artifacts\win-x64'
```
- the channel is registered as maintained;
- the final package contains the canonical legal/compliance bundle;
- exact .NET notices, component coverage, SBOM and license report match the build;
- every staged path exists in the ZIP and no unexpected path was added;
- every staged file SHA-256 equals the corresponding ZIP entry SHA-256;
- source commit SHA matches repository HEAD;
- tag and source ref are consistent;
- the archive name, size and SHA-256 are recorded;
- the final report is schema 3 with `result: pass`.

The workflow proves rejection of:
The report is distributed alongside the ZIP, not inside it, because it records the final ZIP checksum.

- a transitive package with valid package metadata but no declared/resolved license;
- an incomplete package;
- stale redistribution metadata;
- a package containing a real runtime DLL after its component mapping is deliberately removed.
## Negative checks

## Distribution formats
The workflow proves rejection of incomplete, stale, unmapped, unknown-license, byte-modified and provenance-inconsistent packages. It also proves that a planned channel cannot publish before activation.

The same legal/compliance bundle is required for Actions artifacts, manual ZIPs, installers, store packages, portable builds and mirrors. Each maintained format must validate its final installed or unpacked file set. Mirrors publish the same verified bytes and report.
## Adding a new format or channel

## Release checklist
A new GitHub Release, installer, store package or mirror must be implemented in its own tracked work. The implementation must:

1. verify canonical release metadata and maintainer decision;
2. restore application and test projects;
3. build, test, publish and capture `FilesToBundle`;
4. import exact official .NET notices;
5. generate exact package/component coverage;
6. generate the redistribution summary;
7. generate complete SBOM, license report and dependency inventory;
8. resolve every unknown, denied, review-required or evidence failure;
9. prove unknown-transitive, incomplete, stale and unmapped inputs are rejected;
10. create and run both validators against the final package;
11. retain the package and compliance report;
12. publish identical verified bytes to official mirrors.
- define its final installed/unpacked staging tree;
- preserve the canonical legal bundle;
- activate its registry entry;
- call the reusable final-package gate;
- retain the verified container and report;
- add container-specific tests.

Do not publish when any notice, package hash, component mapping, restore graph, SBOM, license, metadata or maintainer-decision check fails.
Do not describe a planned channel as maintained before those controls exist.
Loading
Loading