Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
a65b148
ci(pr): add ReSharper InspectCode job (canonical)
Chris-Wolfgang Jul 13, 2026
1b8afcb
ci(inspectcode): windows-latest + pwsh gate + simplified build (sync …
Chris-Wolfgang Jul 14, 2026
0735982
ci: add SourceLink verification workflow
Chris-Wolfgang Jul 16, 2026
7b763aa
Bump the dotnet-dependencies group with 5 updates
dependabot[bot] Jul 18, 2026
25780fd
ci: add SourceLink step-into (F11) verification (#133)
Chris-Wolfgang Jul 21, 2026
a07e45f
ci: add Native-AOT publish+run smoke test (#132)
Chris-Wolfgang Jul 21, 2026
d9ca672
ci: align checkout SHA pin to repo canonical (9c091bb v7)
Chris-Wolfgang Jul 21, 2026
63611a1
Merge pull request #210 from Chris-Wolfgang/dependabot/nuget/dotnet-d…
Chris-Wolfgang Jul 21, 2026
cd0b782
Merge pull request #208 from Chris-Wolfgang/ci/sourcelink-verify
Chris-Wolfgang Jul 21, 2026
e48f467
Merge pull request #212 from Chris-Wolfgang/ci/sourcelink-stepinto
Chris-Wolfgang Jul 21, 2026
2a645d2
ci(pr): align InspectCode SDK list + select solution before build
Chris-Wolfgang Jul 21, 2026
693b594
chore(deps): bump Wolfgang.Etl.Abstractions 0.15.0 -> 0.16.0
Chris-Wolfgang Jul 21, 2026
e48fa88
ci: SHA-pin all GitHub Actions + zizmor hash-pin policy (#143)
Chris-Wolfgang Jul 21, 2026
2af41c3
Merge branch 'vNext' into chore/add-inspectcode
Chris-Wolfgang Jul 21, 2026
470bfca
Merge pull request #211 from Chris-Wolfgang/chore/abstractions-0.16.0
Chris-Wolfgang Jul 21, 2026
9504d92
ci: SHA-pin actions to current major-tag commits in aot-smoke.yaml (#…
Chris-Wolfgang Jul 21, 2026
a821b12
Merge pull request #203 from Chris-Wolfgang/chore/add-inspectcode
Chris-Wolfgang Jul 21, 2026
c7df3d0
ci: isolate AOT-smoke fixture from repo .editorconfig (CA2007)
Chris-Wolfgang Jul 21, 2026
2d0a074
ci: add Actions security/quality audit (actionlint + zizmor) (#143)
Chris-Wolfgang Jul 21, 2026
5747da6
ci: SHA-pin actions to current major-tag commits in actions-audit.yam…
Chris-Wolfgang Jul 21, 2026
4a946f7
ci: add transitive-dependency license audit (#137)
Chris-Wolfgang Jul 21, 2026
46e2289
ci: SHA-pin actions to current major-tag commits in license-audit.yam…
Chris-Wolfgang Jul 21, 2026
e722fdf
ci: add OSSF Scorecard security-posture scan (#142)
Chris-Wolfgang Jul 21, 2026
67c1c7f
ci: SHA-pin actions to current major-tag commits in scorecard.yaml (#…
Chris-Wolfgang Jul 21, 2026
72ca58c
ci: add Semgrep SAST beyond CodeQL (#117)
Chris-Wolfgang Jul 21, 2026
707e060
ci: SHA-pin actions to current major-tag commits in semgrep.yaml (#143)
Chris-Wolfgang Jul 21, 2026
69d1764
ci: add build-reproducibility verification (#135)
Chris-Wolfgang Jul 21, 2026
0efc022
ci: SHA-pin actions to current major-tag commits in reproducible-buil…
Chris-Wolfgang Jul 21, 2026
4503408
ci: add per-PR CycloneDX SBOM generation (#127)
Chris-Wolfgang Jul 21, 2026
4af1203
Merge pull request #213 from Chris-Wolfgang/ci/aot-smoke
Chris-Wolfgang Jul 21, 2026
c7748f1
Merge pull request #214 from Chris-Wolfgang/ci/actions-audit
Chris-Wolfgang Jul 21, 2026
3910bc1
Merge pull request #215 from Chris-Wolfgang/ci/license-audit
Chris-Wolfgang Jul 21, 2026
16746c3
Merge pull request #216 from Chris-Wolfgang/ci/scorecard
Chris-Wolfgang Jul 21, 2026
b9ab6b6
Merge pull request #217 from Chris-Wolfgang/ci/semgrep
Chris-Wolfgang Jul 21, 2026
f610ff8
Merge pull request #218 from Chris-Wolfgang/ci/reproducible-build
Chris-Wolfgang Jul 21, 2026
23f3ba4
Merge pull request #219 from Chris-Wolfgang/ci/pin-refresh
Chris-Wolfgang Jul 21, 2026
b6868b4
Merge pull request #220 from Chris-Wolfgang/ci/sbom
Chris-Wolfgang Jul 21, 2026
1f40e9d
ci: add per-PR perf-regression detection (#144)
Chris-Wolfgang Jul 22, 2026
45f8adb
ci: add cross-platform/multi-arch differential (#128)
Chris-Wolfgang Jul 22, 2026
3231e22
ci: add Stryker mutation-score enforcement + trend (#124)
Chris-Wolfgang Jul 22, 2026
ac952a1
test: add XML-doc example-rot detection (#130)
Chris-Wolfgang Jul 22, 2026
0600c7f
build: enable PackageValidation ABI gate (#125)
Chris-Wolfgang Jul 22, 2026
b19b7c0
docs: add Architecture Decision Records (#139)
Chris-Wolfgang Jul 22, 2026
15be25c
test: guard the doubles' zero-alloc per-item hot path (#136)
Chris-Wolfgang Jul 22, 2026
ed5a821
test: add CsCheck property-based fuzz suite (#115)
Chris-Wolfgang Jul 22, 2026
15299d4
docs: scaffold major-version migration guide convention (#138)
Chris-Wolfgang Jul 22, 2026
20f924d
test: assert the doubles are culture-invariant (#134)
Chris-Wolfgang Jul 22, 2026
543b1a8
test: add Coyote systematic concurrency tests (#126)
Chris-Wolfgang Jul 22, 2026
62798cb
docs: add worked 'real consumer' sample (#116)
Chris-Wolfgang Jul 22, 2026
2311251
docs,ci: consumer-side reproducible-build verification (#145)
Chris-Wolfgang Jul 22, 2026
510d5ee
ci: calibrate the Actions audit so it stops blocking PRs (#143)
Chris-Wolfgang Jul 22, 2026
8d89b36
ci: correct codeql-action v4 SHA pin to match the tag (#143)
Chris-Wolfgang Jul 23, 2026
9e4dfa7
ci: pass release tag via env to avoid template-injection (#145)
Chris-Wolfgang Jul 23, 2026
7ab833b
test: suppress use-defused-xml on trusted .trx parsing (#128)
Chris-Wolfgang Jul 23, 2026
c3dbb03
Merge pull request #234 from Chris-Wolfgang/ci/audit-calibration
Chris-Wolfgang Jul 23, 2026
eb5f86b
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/pe…
Chris-Wolfgang Jul 23, 2026
a140c6d
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/cr…
Chris-Wolfgang Jul 23, 2026
4a0c5e8
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/st…
Chris-Wolfgang Jul 23, 2026
2b28aab
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/do…
Chris-Wolfgang Jul 23, 2026
8f363af
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/ap…
Chris-Wolfgang Jul 23, 2026
3eba1e5
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/adr
Chris-Wolfgang Jul 23, 2026
2e8d85d
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/al…
Chris-Wolfgang Jul 23, 2026
49de540
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/fuzz
Chris-Wolfgang Jul 23, 2026
453934c
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/mi…
Chris-Wolfgang Jul 23, 2026
2f35bc3
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/gl…
Chris-Wolfgang Jul 23, 2026
c181a6d
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/co…
Chris-Wolfgang Jul 23, 2026
5f8fbe3
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/sa…
Chris-Wolfgang Jul 23, 2026
a302289
Merge remote-tracking branch 'origin/ci/audit-calibration' into ci/re…
Chris-Wolfgang Jul 23, 2026
1e8bdce
Merge pull request #221 from Chris-Wolfgang/ci/perf-regression
Chris-Wolfgang Jul 24, 2026
e4fca49
Merge branch 'vNext' into ci/cross-platform-differential
Chris-Wolfgang Jul 24, 2026
cb8216a
test: use a bare nosemgrep for the trusted-trx XML parse (#128)
Chris-Wolfgang Jul 24, 2026
3e1a319
test: parse .trx with defusedxml instead of suppressing (#128)
Chris-Wolfgang Jul 24, 2026
fd3cdf3
docs(readme): correct Supported Frameworks TFM list
Chris-Wolfgang Jul 24, 2026
38a3337
Merge pull request #222 from Chris-Wolfgang/ci/cross-platform-differe…
Chris-Wolfgang Jul 24, 2026
817b424
Merge pull request #223 from Chris-Wolfgang/ci/stryker-gate
Chris-Wolfgang Jul 24, 2026
d6f8559
Merge pull request #224 from Chris-Wolfgang/ci/doc-example-rot
Chris-Wolfgang Jul 24, 2026
9e9fc9b
Merge pull request #225 from Chris-Wolfgang/ci/api-compat
Chris-Wolfgang Jul 24, 2026
bf8fb5e
Merge pull request #227 from Chris-Wolfgang/ci/alloc-guard
Chris-Wolfgang Jul 24, 2026
e1ed61a
Merge remote-tracking branch 'origin/vNext' into ci/fuzz
Chris-Wolfgang Jul 24, 2026
e73d548
Merge remote-tracking branch 'origin/vNext' into ci/concurrency
Chris-Wolfgang Jul 24, 2026
5d4a3b7
Merge remote-tracking branch 'origin/vNext' into ci/adr
Chris-Wolfgang Jul 24, 2026
f679195
Merge remote-tracking branch 'origin/vNext' into ci/migration-guide
Chris-Wolfgang Jul 24, 2026
df93158
Merge remote-tracking branch 'origin/vNext' into ci/globalization
Chris-Wolfgang Jul 24, 2026
1d5e58a
Merge remote-tracking branch 'origin/vNext' into ci/samples
Chris-Wolfgang Jul 24, 2026
469e10b
Merge remote-tracking branch 'origin/vNext' into ci/reproducible-verify
Chris-Wolfgang Jul 24, 2026
2e0ea89
test: make the allocation guard stable across platforms (#136)
Chris-Wolfgang Jul 24, 2026
174b3ec
Merge remote-tracking branch 'origin/fix/alloc-test-stability' into c…
Chris-Wolfgang Jul 24, 2026
990b49d
Merge pull request #228 from Chris-Wolfgang/ci/fuzz
Chris-Wolfgang Jul 24, 2026
8ad0224
Merge pull request #229 from Chris-Wolfgang/ci/migration-guide
Chris-Wolfgang Jul 24, 2026
9438113
Merge pull request #232 from Chris-Wolfgang/ci/samples
Chris-Wolfgang Jul 24, 2026
9eb22ae
Merge pull request #233 from Chris-Wolfgang/ci/reproducible-verify
Chris-Wolfgang Jul 24, 2026
17f89f1
Merge branch 'main' into docs/readme-supported-frameworks-canonical
Chris-Wolfgang Jul 24, 2026
3771d94
Merge pull request #237 from Chris-Wolfgang/docs/readme-supported-fra…
Chris-Wolfgang Jul 24, 2026
44dbce0
Merge remote-tracking branch 'origin/fix/alloc-test-stability' into c…
Chris-Wolfgang Jul 24, 2026
168571d
Merge remote-tracking branch 'origin/fix/alloc-test-stability' into c…
Chris-Wolfgang Jul 24, 2026
779ddb0
Merge pull request #230 from Chris-Wolfgang/ci/globalization
Chris-Wolfgang Jul 24, 2026
a1d2a43
Merge pull request #231 from Chris-Wolfgang/ci/concurrency
Chris-Wolfgang Jul 24, 2026
f18752a
ci(release): migrate NuGet publish to Trusted Publishing / OIDC (#207)
Chris-Wolfgang Jul 24, 2026
e39e0b8
Merge pull request #239 from Chris-Wolfgang/ci/oidc-trusted-publishing
Chris-Wolfgang Jul 24, 2026
df9794a
docs(security): add Release path & compromise scope appendix (#140)
Chris-Wolfgang Jul 24, 2026
96499d9
release: prep 0.10.1
Chris-Wolfgang Jul 25, 2026
faae478
Merge pull request #242 from Chris-Wolfgang/docs/security-release-path
Chris-Wolfgang Jul 25, 2026
a69d37d
Merge branch 'vNext' into ci/adr
Chris-Wolfgang Jul 25, 2026
48c52da
Merge pull request #226 from Chris-Wolfgang/ci/adr
Chris-Wolfgang Jul 25, 2026
5bf786e
Merge branch 'vNext' into release/prep-0.10.1
Chris-Wolfgang Jul 25, 2026
b3ec72b
fix: renormalize docfx.json to LF (.gitattributes eol=lf)
Chris-Wolfgang Jul 25, 2026
5caab8f
Merge pull request #244 from Chris-Wolfgang/release/prep-0.10.1
Chris-Wolfgang Jul 26, 2026
70e3831
deps: reconcile 0.10.1 up to Abstractions 0.17.0 (match main)
Chris-Wolfgang Jul 26, 2026
87788aa
ci(security): pass select-solution output via env to avoid template i…
Chris-Wolfgang Jul 26, 2026
6b2004f
Merge branch 'main' into vNext
Chris-Wolfgang Jul 27, 2026
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
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Security

## [0.10.1] - 2026-07-24

Maintenance release: the deferred "thorough-review" hardening tier plus the
`Wolfgang.Etl.Abstractions` 0.17.0 bump. **No public API or behaviour change** to
either shipped package — the test doubles and contract-test base classes are
unchanged.

### Changed

- Built against `Wolfgang.Etl.Abstractions` 0.17.0 (was 0.15.0).

### Security

- Release now publishes via **OIDC / NuGet Trusted Publishing** (`NuGet/login`), removing
the long-lived `NUGET_API_KEY` from the release path.
- Added supply-chain / security CI: transitive-dependency **license audit**, **CycloneDX SBOM**,
**OSSF Scorecard**, **Semgrep** SAST, GitHub **Actions audit** (actionlint + zizmor, all
actions SHA-pinned), and **build-reproducibility** verification with a per-release
reproducible-build manifest attached to each GitHub Release.
- Documented the release path and compromise scope in `SECURITY.md`, and added a
consumer-side reproducible-build verification guide (`docs/REPRODUCIBLE-BUILD.md`).

## [0.10.0] - 2026-06-29

Adds an opt-in contract-test base for the `ISupportDryRun` interface introduced in
Expand Down
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ An Extractor, Transformer and Loader designed to be used in testing libraries bu
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-Multi--Targeted-purple.svg)](https://dotnet.microsoft.com/)
[![GitHub](https://img.shields.io/badge/GitHub-Repository-181717?logo=github)](https://github.com/Chris-Wolfgang/ETL-Test-Kit)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Chris-Wolfgang/ETL-Test-Kit/badge)](https://scorecard.dev/viewer/?uri=github.com/Chris-Wolfgang/ETL-Test-Kit)

---

Expand Down Expand Up @@ -239,17 +240,15 @@ public sealed class MyLoaderContractTests

---

## 🎯 Target Frameworks
## 🎯 Supported Frameworks

Both packages multi-target the following frameworks:
This library targets:

| Framework | Versions |
|-----------|----------|
| .NET Framework | .NET 4.6.2, .NET 4.8.1 |
| .NET Standard | .NET Standard 2.0 |
| .NET | .NET 8.0, .NET 10.0 |
- **.NET Framework:** 4.6.2, 4.8.1
- **.NET Standard:** 2.0
- **.NET:** 8.0, 10.0

---
See the [NuGet package page](https://www.nuget.org/packages/Wolfgang.Etl.TestKit/) for the authoritative per-TFM compatibility matrix.

## 🔍 Code Quality & Static Analysis

Expand Down Expand Up @@ -357,6 +356,16 @@ docfx build --serve

---

## 🔐 Verify the build

Every release is built deterministically, and each GitHub Release attaches a
`reproducible-build-manifest.json` with the SHA-256 of every shipped assembly.
You can independently rebuild from the tag and confirm the hashes match — see
[docs/REPRODUCIBLE-BUILD.md](docs/REPRODUCIBLE-BUILD.md) for the step-by-step
procedure and how to publish a third-party attestation.

---

## 🤝 Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for:
Expand Down
50 changes: 50 additions & 0 deletions REPRODUCIBLE-BUILD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Reproducible builds

Both shipped assemblies — `Wolfgang.Etl.TestKit` and `Wolfgang.Etl.TestKit.Xunit`
— are built to be **byte-for-byte reproducible**: the same source commit produces
the same compiled output regardless of *where* it is built.

## What makes the build reproducible

`Directory.Build.props` sets the compiler inputs that a reproducible build
requires:

- `<Deterministic>true</Deterministic>` — the compiler emits deterministic
output (no embedded timestamps, ordered metadata).
- `<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>` (in CI) —
normalises embedded source paths to a deterministic `/_/` root via `PathMap`,
so the checkout directory does not leak into the assembly.
- SourceLink — embeds the commit SHA rather than machine-local paths.

## How it is verified

[`.github/workflows/reproducible-build.yaml`](.github/workflows/reproducible-build.yaml)
checks the same commit out to two independent directories, builds each with
`-p:ContinuousIntegrationBuild=true`, and fails if the produced `.dll`s do not
hash identically (`sha256sum`). This proves **path-independent** reproducibility
on a single runner — the property that lets a third party rebuild and match.

## How to verify it yourself

```bash
git clone https://github.com/Chris-Wolfgang/ETL-Test-Kit a
git clone https://github.com/Chris-Wolfgang/ETL-Test-Kit b
for d in a b; do
dotnet build "$d/src/Wolfgang.Etl.TestKit/Wolfgang.Etl.TestKit.csproj" \
-c Release -f net10.0 -p:ContinuousIntegrationBuild=true
done
sha256sum \
a/src/Wolfgang.Etl.TestKit/bin/Release/net10.0/Wolfgang.Etl.TestKit.dll \
b/src/Wolfgang.Etl.TestKit/bin/Release/net10.0/Wolfgang.Etl.TestKit.dll
# The two hashes must be identical.
```

## Scope / follow-up

The verification above covers path-independent reproducibility on a single OS —
the fleet-proven guarantee. **Cross-OS** byte-identity (building on Ubuntu vs
Windows and matching) is a stronger claim that is not yet asserted here: `.pdb`
and some embedded metadata can differ across SDK patch levels and operating
systems even with deterministic inputs. Extending the matrix to cross-OS
comparison (with any required `.pdb`/metadata normalisation) is tracked as a
follow-up to #135.
12 changes: 12 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,15 @@ We will acknowledge your report within 48 hours and provide an estimated timelin

Your help is greatly appreciated!
Responsible disclosure of security vulnerabilities helps protect our entire community.

## Release path & compromise scope

Facts a maintainer would need at 2am if the release identity is compromised. Generic incident-response steps (rotating credentials, revoking OAuth apps, publishing advisories, unlisting NuGet packages) are not duplicated here — GitHub's and NuGet's own docs update faster than a checked-in runbook.

- **Release path**: OIDC / NuGet Trusted Publishing via `NuGet/login@v1` in `.github/workflows/release.yaml`. The workflow mints an ephemeral push token per run via OIDC — the release path does not depend on a long-lived API key stored in GitHub secrets or on the NuGet account. During an incident, check the NuGet account for any long-lived API keys anyway (they can be created outside of CI) and delete anything you don't recognize.
- **Fallback**: none. If Trusted Publishing is compromised, the incident is at the GitHub-account level (the OIDC identity is `Chris-Wolfgang/ETL-Test-Kit`).
- **Owner**: @Chris-Wolfgang.
- **Downstream consumers**: known Wolfgang.* dependents (test projects) include ETL-Xml, ETL-FixedWidth, Etl-DbClient, ETL-Json, and ETL-Transformers; unknown external consumers may also exist on nuget.org.
- **Package coordinates for unlisting**: this repo ships two packages —
- `Wolfgang.Etl.TestKit` — https://www.nuget.org/packages/Wolfgang.Etl.TestKit/
- `Wolfgang.Etl.TestKit.Xunit` — https://www.nuget.org/packages/Wolfgang.Etl.TestKit.Xunit/
130 changes: 65 additions & 65 deletions docfx_project/docfx.json
Original file line number Diff line number Diff line change
@@ -1,65 +1,65 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json",
"metadata": [
{
"src": [
{
"files": [
"src/**/*.csproj"
],
"src": "../"
}
],
"dest": "api",
"properties": {
"TargetFramework": "net8.0"
},
"disableGitFeatures": false,
"disableDefaultFilter": false
}
],
"build": {
"content": [
{
"files": [
"**/*.{md,yml}"
],
"exclude": [
"_site/**"
]
}
],
"resource": [
{
"files": [
"logo.svg",
"apple-touch-icon.png",
"favicon.svg",
"favicon.ico",
"images/**",
"public/**",
"versions.json"
]
}
],
"output": "_site",
"template": [
"default",
"modern"
],
"globalMetadata": {
"_appName": "Wolfgang.Etl.TestKit",
"_appTitle": "Wolfgang.Etl.TestKit Documentation",
"_appLogoPath": "logo.svg",
"_appFaviconPath": "favicon.svg",
"_enableSearch": true,
"_appFooter": "Made with DocFX <script>(function(){var r='/';if(window.location.hostname.endsWith('.github.io')){var s=window.location.pathname.split('/').filter(Boolean);if(s.length)r='/'+s[0]+'/';}var t=document.createElement('script');t.src=r+'public/version-picker.js';t.async=true;document.head.appendChild(t);})();</script>",
"_disableSidebar": false,
"_disableTocFilter": false,
"_enableDarkMode": true,
"colorMode": "dark",
"_baseUrl": "https://Chris-Wolfgang.github.io/ETL-Test-Kit/",
"pdf": true
}
}
}
{
"$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json",
"metadata": [
{
"src": [
{
"files": [
"src/**/*.csproj"
],
"src": "../"
}
],
"dest": "api",
"properties": {
"TargetFramework": "net8.0"
},
"disableGitFeatures": false,
"disableDefaultFilter": false
}
],
"build": {
"content": [
{
"files": [
"**/*.{md,yml}"
],
"exclude": [
"_site/**"
]
}
],
"resource": [
{
"files": [
"logo.svg",
"apple-touch-icon.png",
"favicon.svg",
"favicon.ico",
"images/**",
"public/**",
"versions.json"
]
}
],
"output": "_site",
"template": [
"default",
"modern"
],
"globalMetadata": {
"_appName": "Wolfgang.Etl.TestKit",
"_appTitle": "Wolfgang.Etl.TestKit Documentation",
"_appLogoPath": "logo.svg",
"_appFaviconPath": "favicon.svg",
"_enableSearch": true,
"_appFooter": "Made with DocFX <script>(function(){var r='/';if(window.location.hostname.endsWith('.github.io')){var s=window.location.pathname.split('/').filter(Boolean);if(s.length)r='/'+s[0]+'/';}var t=document.createElement('script');t.src=r+'public/version-picker.js';t.async=true;document.head.appendChild(t);})();</script>",
"_disableSidebar": false,
"_disableTocFilter": false,
"_enableDarkMode": true,
"colorMode": "dark",
"_baseUrl": "https://Chris-Wolfgang.github.io/ETL-Test-Kit/",
"pdf": true
}
}
}
91 changes: 91 additions & 0 deletions docs/REPRODUCIBLE-BUILD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Verifying the build is reproducible

Every release of `Wolfgang.Etl.TestKit` and `Wolfgang.Etl.TestKit.Xunit` is built
deterministically: the same source at the same tag produces byte-identical
assemblies, independent of who builds it or where. This page lets **you** confirm
that independently, so "our builds are reproducible" is a checkable claim rather
than a promise.

CI already proves *same-environment* reproducibility on every push
([`reproducible-build.yaml`](../.github/workflows/reproducible-build.yaml), #135):
it builds the library twice and asserts the assembly hashes match. This document
is the *consumer-side* flip — how a third party reproduces and attests to it.

## What is published

Each GitHub Release attaches a **`reproducible-build-manifest.json`** listing the
SHA-256 of every shipped assembly, plus the reference environment (OS and .NET SDK
version) and the exact build command used to produce them. Example:

```json
{
"schema": "wolfgang.reproducible-build-manifest/v1",
"version": "v0.11.0",
"targetFramework": "net10.0",
"buildCommand": "dotnet build <project> -c Release -f net10.0 -p:ContinuousIntegrationBuild=true",
"referenceEnvironment": { "os": "Linux", "dotnetSdk": "10.0.110" },
"assemblies": [
{ "assembly": "Wolfgang.Etl.TestKit.dll", "sha256": "…" },
{ "assembly": "Wolfgang.Etl.TestKit.Xunit.dll", "sha256": "…" }
]
}
```

## Reproduce it yourself

1. **Match the reference environment.** Use the same OS family and .NET SDK
version named in the release's manifest (`referenceEnvironment`).
`ContinuousIntegrationBuild=true` normalises source paths, so the *checkout
location* does not matter — but the compiler version does, so match the SDK.

2. **Clone at the exact tag:**

```bash
git clone --branch <tag> --depth 1 https://github.com/Chris-Wolfgang/ETL-Test-Kit
cd ETL-Test-Kit
```

3. **Build each library with the documented command** (the manifest's
`buildCommand`):

```bash
dotnet build src/Wolfgang.Etl.TestKit/Wolfgang.Etl.TestKit.csproj \
-c Release -f net10.0 -p:ContinuousIntegrationBuild=true
dotnet build src/Wolfgang.Etl.TestKit.Xunit/Wolfgang.Etl.TestKit.Xunit.csproj \
-c Release -f net10.0 -p:ContinuousIntegrationBuild=true
```

4. **Hash your output and compare** against the manifest:

```bash
sha256sum \
src/Wolfgang.Etl.TestKit/bin/Release/net10.0/Wolfgang.Etl.TestKit.dll \
src/Wolfgang.Etl.TestKit.Xunit/bin/Release/net10.0/Wolfgang.Etl.TestKit.Xunit.dll
```

Each hash must equal the corresponding `sha256` in
`reproducible-build-manifest.json`. The repo's own generator
([`scripts/reproducible-manifest.sh`](../scripts/reproducible-manifest.sh))
runs exactly these steps, so you can also regenerate the whole manifest and
`diff` it against the published one.

## If a hash does not match

A mismatch means either the environments differ (most commonly a different SDK
patch version) or the artifact was tampered with. Please
[open an issue](https://github.com/Chris-Wolfgang/ETL-Test-Kit/issues/new) titled
"Reproducible-build mismatch for `<tag>`" including:

- the release tag,
- your OS and `dotnet --version`,
- your computed hashes vs the manifest's,
- the exact commands you ran.

## Publishing a third-party attestation

Independent verification is most useful when it is *public*. If you reproduced a
release successfully, you can publish an attestation following the
[Reproducible Builds project](https://reproducible-builds.org/) conventions (or a
service such as [vouchsafe.io](https://vouchsafe.io/)): sign a statement naming the
tag, the manifest hash, and your environment, and link it back on the mismatch/
verification issue so others can find corroborating rebuilds.
27 changes: 27 additions & 0 deletions docs/adr/0001-record-architecture-decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 1. Record architecture decisions

## Status

Accepted

## Context

Wolfgang.Etl.TestKit is a small but long-lived pair of NuGet packages (the test
doubles and the xUnit contract-test base classes) maintained across many release
cycles, often by different contributors and automated agents. Several non-obvious
design choices — the pinned `AssemblyVersion`, the split into two packages, the
injectable progress timer — are easy to accidentally undo in a later change
because the *reasoning* lives only in commit messages or a reviewer's memory.

## Decision

We will keep Architecture Decision Records in `docs/adr/`, one Markdown file per
decision, in the Nygard format (Context / Decision / Consequences). Records are
immutable once accepted; a changed decision is captured as a new, superseding ADR.

## Consequences

- The rationale behind load-bearing choices is discoverable next to the code.
- Reviewers can point at an ADR instead of re-litigating a settled decision.
- There is a small ongoing cost: a genuinely architectural change should come
with an ADR, not just code.
Loading
Loading