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
17 changes: 14 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,32 @@ jobs:
.\tools\generate-sbom.ps1
-PublishDirectory artifacts/win-x64

- name: Verify incomplete packages are rejected
shell: pwsh
run: .\tools\test-release-compliance-negative.ps1

- 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\test-release-package.ps1 -ArchivePath $archivePath
.\tools\verify-release-compliance.ps1 `
-DirectoryPath artifacts/win-x64 `
-ArchivePath $archivePath `
-ReportPath $reportPath

- name: Upload Windows alpha
- name: Upload verified Windows alpha and compliance report
uses: actions/upload-artifact@v4
with:
name: ${{ steps.release.outputs.artifact_name }}-${{ github.run_id }}
path: artifacts/${{ steps.release.outputs.artifact_name }}.zip
path: |
artifacts/${{ steps.release.outputs.artifact_name }}.zip
artifacts/${{ steps.release.outputs.artifact_name }}-compliance.json
if-no-files-found: error
retention-days: 7
45 changes: 34 additions & 11 deletions docs/PACKAGING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,38 @@ artifacts/win-x64/

Additional reviewed runtime files may be present depending on the publish settings.

## Archive creation and validation
## Final package compliance gate

Create the archive from the contents of the publish directory so the required files remain at the ZIP root:
Create the archive from the contents of the publish directory so the required files remain at the ZIP root, then validate both the staged directory and final archive:

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

Remove-Item $archive -Force -ErrorAction SilentlyContinue
Remove-Item $report -Force -ErrorAction SilentlyContinue
Compress-Archive `
-Path '.\artifacts\win-x64\*' `
-DestinationPath $archive `
-CompressionLevel Optimal

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

The validation script opens the final ZIP and checks the entries listed in `release/required-files.txt`. Required documents must be readable UTF-8 text. The exact .NET metadata must match the pinned release inputs and map the required runtime packs. SBOM generation has already failed the workflow if a dependency is absent from the reviewed policy, uses a different version or has a denied/unreviewed license.
The gate consumes the canonical manifest and existing notice, license-report and SBOM outputs. It verifies the staged directory, final ZIP, build identity, archive name, exact-version metadata, license-policy result and shipped-file coverage. It writes a machine-readable report containing the final archive SHA-256 and validation result.

The negative check must also pass:

```powershell
.\tools\test-release-compliance-negative.ps1
```

This creates a deliberately incomplete temporary package and proves that the validator rejects it.

See [Release compliance gate](legal/RELEASE-COMPLIANCE-GATE.md).

## Microsoft .NET notices and redistribution position

Expand All @@ -96,20 +111,28 @@ The reviewed redistribution position is documented in [Microsoft .NET redistribu

The generated SPDX document contains component relationships and SHA-256 checksums for every separately shipped file. `SightAdapt.exe` is the documented single-file container for embedded managed and native runtime components. See [SBOM and dependency-license review](legal/SBOM-AND-LICENSE-REVIEW.md).

## Installers, stores and mirrors

Every maintained installer or store workflow must run the same logical gate after the final installed/unpacked file set is staged. The installed application directory must satisfy `release/required-files.txt`, and the outer installer/store container checksum must be retained in its compliance report.

Portable packages use the ZIP gate directly. Release mirrors publish the same verified bytes and compliance report; they must not rebuild, strip or rename required legal/compliance files.

## Release checklist

Before publishing or mirroring a binary package:

1. verify the pinned release metadata;
2. verify the Microsoft .NET redistribution analysis and package notice;
3. restore, build and test the application;
3. restore, build and run the maintained project checks;
4. publish into a clean staging directory;
5. generate exact-version .NET notices from the hash-verified official SDK package and actual restore graph;
6. generate the SPDX SBOM, license report and human-readable dependency inventory;
7. resolve every component or license-policy failure;
8. create the final archive or platform package;
9. validate the final archive with `tools/test-release-package.ps1`;
10. inspect the package manually to confirm that every legal document opens without running SightAdapt;
11. publish the same verified bytes to every official mirror.

Do not publish an official binary release when the legal bundle, redistribution notice, exact-version generation, SBOM, license report, package checksum, runtime mapping or final-archive validation is incomplete. Production or paid distribution additionally remains blocked until the qualified legal review required by Issue #93 is recorded.
8. confirm the deliberately incomplete package is rejected;
9. create the final archive or platform package;
10. run `verify-release-compliance.ps1` against the staged directory and final package;
11. retain and publish the compliance report with the package;
12. inspect the package manually to confirm that every legal document opens without running SightAdapt;
13. publish the same verified bytes to every official mirror.

Do not publish an official binary release when the legal bundle, redistribution notice, exact-version generation, SBOM, license report, package checksum, runtime mapping or final compliance gate is incomplete. Production or paid distribution additionally remains blocked until the qualified legal review required by Issue #93 is recorded.
3 changes: 2 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ The documentation describes the current SightAdapt implementation and the canoni
- [Intended purpose and medical-device claims policy](legal/INTENDED-PURPOSE-AND-MDR.md) — approved general-purpose positioning, prohibited medical claims, terminology, reviewed surfaces and renewed-assessment triggers.
- [Patent FTO commercialization gate](legal/PATENT-FTO-GATE.md) — technical feature map, current non-commercial decision, professional-review requirements and re-opening triggers.
- [Release naming and attribution](RELEASING.md) — public release-title, tag, artifact, website, publisher, store-claim and mark conventions.
- [Binary packaging standard](PACKAGING.md) — required legal files, redistribution notice, SBOM, exact-version notices, archive validation and distribution-format rules.
- [Binary packaging standard](PACKAGING.md) — required legal files, redistribution notice, SBOM, exact-version notices, final compliance gate and distribution-format rules.
- [Release compliance gate](legal/RELEASE-COMPLIANCE-GATE.md) — staged-directory/final-package verification, negative test and retained compliance report.
- [Microsoft .NET redistribution analysis](legal/DOTNET-REDISTRIBUTION.md) — Microsoft-origin component inventory, applicable terms, package implementation, review triggers and professional-review gate.
- [Exact-version .NET notice generation](legal/DOTNET-NOTICE-GENERATION.md) — pinned SDK/runtime inputs, official package verification, notice import, checksums and update triggers.
- [SBOM and dependency-license review](legal/SBOM-AND-LICENSE-REVIEW.md) — SPDX inventory, license policy, generated reports and release failure rules.
Expand Down
9 changes: 7 additions & 2 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,17 @@ Do not use `®`.

Every binary distribution must comply with [the binary packaging standard](PACKAGING.md). The final archive or platform package must include every file listed in `release/required-files.txt`, including the license, exact-version notices, Microsoft redistribution notice, privacy notice, dependency inventory, SBOM and license report.

The legal documents must be readable without starting the application. The final ZIP must pass:
Before publication, validate the final staged directory and package and retain the resulting report:

```powershell
.\tools\test-release-package.ps1 -ArchivePath <archive-path>
.\tools\verify-release-compliance.ps1 `
-DirectoryPath <staged-directory> `
-ArchivePath <archive-path> `
-ReportPath <compliance-report-path>
```

The package and compliance report must be published together. GitHub Releases, installers, store packages, portable packages and mirrors must use the same canonical manifest and equivalent final-package gate. Do not upload or publish when the report result is not `pass`.

Do not publish an official binary release until the exact .NET SDK and runtime-pack versions have been recorded and the corresponding exact-version notice material has been generated and reviewed.

## Minimal release header
Expand Down
70 changes: 70 additions & 0 deletions docs/legal/RELEASE-COMPLIANCE-GATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Release compliance gate

## Purpose

The release compliance gate treats the legal, privacy, dependency, notice and SBOM bundle as a release invariant. It runs after the final publication directory has been produced and before any artifact is uploaded.

This is a packaging control. It does not test or change SightAdapt application behavior.

## Authoritative inputs

The gate consumes existing authoritative outputs rather than creating independent legal data:

- `release/required-files.txt` — canonical package manifest;
- `Directory.Build.props` — product, artifact, SDK, runtime and RID metadata;
- `DOTNET-NOTICE-METADATA.json` — exact-version Microsoft source/checksum/runtime evidence;
- `LICENSE-REPORT.json` — dependency-license review result;
- `SBOM.spdx.json` — SPDX component and shipped-file inventory;
- the final staged publication directory;
- the final ZIP archive.

## Checks

`tools/verify-release-compliance.ps1` verifies:

- every manifest file exists in the staged publication directory;
- required files are non-empty and readable;
- required text does not contain common unresolved template markers;
- the final archive name matches the canonical artifact metadata;
- the existing archive validator accepts the final ZIP;
- every staged file is in the final ZIP and no unexpected file was added;
- exact-version notice metadata matches the product, SDK, runtime and RID;
- the license report has a `pass` result and matches the same build metadata;
- the SBOM is SPDX 2.3, describes the correct SightAdapt release and contains every separately shipped file except its own final bytes;
- the SBOM identifies SightAdapt as the single-file packaging container for embedded runtime components.

The gate writes a machine-readable compliance report containing the result, build identity, canonical artifact name, archive SHA-256, manifest, staged/archive file lists and failures.

## Deliberately incomplete package

`tools/test-release-compliance-negative.ps1` creates a temporary ZIP containing only a fake `SightAdapt.exe` and confirms that the package validator rejects it. If the incomplete ZIP is accepted, the workflow fails.

## Workflow order

The maintained release sequence is:

1. restore, build and run the existing application test suite;
2. publish the final self-contained directory;
3. generate exact-version .NET notices;
4. generate the SBOM, dependency summary and license report;
5. confirm an incomplete package is rejected;
6. create the final ZIP;
7. run the release compliance gate against the staged directory and ZIP;
8. upload the verified ZIP and compliance report together.

No upload step runs after a failed compliance gate.

## Other distribution workflows

GitHub Releases, installer builds, store packages, portable packages and release mirrors must consume the same staged directory and canonical manifest. Each maintained packaging workflow must:

- run the equivalent directory/package verification after its final files are staged;
- preserve all required documents in the installed or extractable application directory;
- retain a compliance report tied to the exact package bytes;
- stop before publication when verification fails.

A platform-specific installer or store container may use a different outer format, but it must verify the unpacked/installed contents against `release/required-files.txt` and record the final container checksum. A mirror must publish the same verified bytes and report rather than rebuilding or removing files.

## Maintenance

Update the canonical manifest first when a required compliance artifact changes. Update the SBOM generator, packaging documentation and all distribution workflows in the same pull request. Do not add a separate hard-coded list to another workflow.
43 changes: 43 additions & 0 deletions tools/test-release-compliance-negative.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[CmdletBinding()]
param()

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

if ($null -eq ('System.IO.Compression.ZipFile' -as [type])) {
Add-Type -AssemblyName System.IO.Compression.FileSystem
}

$tempRoot = Join-Path ([System.IO.Path]::GetTempPath()) (
'sightadapt-incomplete-package-' + [Guid]::NewGuid().ToString('N'))
$staging = Join-Path $tempRoot 'staging'
$archive = Join-Path $tempRoot 'incomplete.zip'

try {
[System.IO.Directory]::CreateDirectory($staging) | Out-Null
[System.IO.File]::WriteAllText(
(Join-Path $staging 'SightAdapt.exe'),
'deliberately incomplete package',
[System.Text.UTF8Encoding]::new($false))

[System.IO.Compression.ZipFile]::CreateFromDirectory($staging, $archive)

$failedAsExpected = $false
try {
& (Join-Path $PSScriptRoot 'test-release-package.ps1') `
-ArchivePath $archive
}
catch {
$failedAsExpected = $true
Write-Host "Incomplete package was rejected as expected: $($_.Exception.Message)"
}

if (-not $failedAsExpected) {
throw 'The deliberately incomplete package unexpectedly passed validation.'
}
}
finally {
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
}

Write-Host 'Negative release-package validation passed.'
Loading
Loading