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
13 changes: 13 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"docfx": {
"version": "2.81.0",
"commands": [
"docfx"
],
"rollForward": false
}
}
}
25 changes: 17 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:
shell: pwsh
run: |
$env:PATH = "$env:USERPROFILE\.dotnet\tools;$env:PATH"
jb inspectcode CSharp_CHDSharp.sln --severity=WARNING --format=Sarif --output=inspectcode.sarif --exclude="Vendored*/**/*.cs" --no-updates
jb inspectcode CSharp_CHDSharp.sln --severity=WARNING --format=Sarif --output=inspectcode.sarif --exclude="Vendored*/**" --no-updates

- name: Fail on inspection findings
shell: pwsh
Expand Down Expand Up @@ -204,16 +204,23 @@ jobs:
retention-days: 14

docs_build:
name: docs build
name: docs build (Jekyll + DocFX)
runs-on: ubuntu-24.04
timeout-minutes: 20
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup .NET SDK 10
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: "10.0.x"
cache: true
cache-dependency-path: "**/*.csproj"

- name: Setup Ruby
uses: ruby/setup-ruby@de492cf36cbab201753d720ce4f4e336cd688c94 # v1.336.0
with:
Expand All @@ -225,6 +232,10 @@ jobs:
gem install jekyll -v 4.3.4 --no-document
jekyll build --source docs --destination _site --strict_front_matter --trace

- name: Build DocFX API reference
shell: pwsh
run: ./scripts/build-api-docs.ps1

battle:
name: chdman parity (smoke)
runs-on: windows-latest
Expand Down Expand Up @@ -286,7 +297,7 @@ jobs:
done

wiki:
name: Wiki & Pages
name: Wiki sync
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
needs: ci
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -324,7 +335,5 @@ jobs:
git push
fi

- name: Request Pages rebuild
env:
GH_TOKEN: ${{ secrets.WIKI_TOKEN }}
run: gh api -X POST repos/purelogiccode/CHDSharp/pages/builds --jq '.status'
# GitHub Pages is deployed by .github/workflows/pages.yml (build_type: workflow),
# which rebuilds the Jekyll guides and the DocFX API reference on every push.
21 changes: 21 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Blocks pull requests that introduce dependencies with known vulnerabilities
# or incompatible licenses. Requires the repository dependency graph.
#
# GitHub does not expose an API to enable the dependency graph for user-owned
# repositories; when it is off the review action can only fail with
# "Dependency review is not supported on this repository". Detect that case,
# emit a warning annotation and skip the review so the check is honest instead of
# permanently red. Enable it in Settings > Code security and analysis to turn the
# enforcement back on - no workflow change is needed.
name: Dependency Review

on:
Expand All @@ -21,7 +28,21 @@ jobs:
with:
persist-credentials: false

- name: Check dependency graph availability
id: graph
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if gh api "repos/${{ github.repository }}/dependency-graph/sbom" >/dev/null 2>&1; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
echo "::warning title=Dependency graph disabled::The repository dependency graph is not enabled, so dependency review cannot run. Enable it in Settings > Code security and analysis > Dependency graph."
fi

- name: Review dependency changes
if: steps.graph.outputs.available == 'true'
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
with:
fail-on-severity: moderate
Expand Down
88 changes: 88 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Builds the complete documentation site and deploys it to GitHub Pages:
# - the Jekyll guides from docs/
# - the DocFX API reference generated from CHDSharpLib's XML documentation (->
# /api/)
#
# GitHub Pages is configured with `build_type: workflow`, so this workflow owns the
# deployment. It runs on every push to main that can affect the site, plus manual dispatch.
name: Pages

on:
push:
branches: [main]
paths:
- "docs/**"
- "CHDSharpLib/**"
- "docfx/**"
- "docfx.json"
- ".config/dotnet-tools.json"
- "scripts/build-api-docs.ps1"
- ".github/workflows/pages.yml"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: pages
cancel-in-progress: false

env:
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
DOTNET_NOLOGO: "true"

jobs:
build:
name: Build docs site
runs-on: ubuntu-24.04
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup .NET SDK 10
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: "10.0.x"
cache: true
cache-dependency-path: "**/*.csproj"

- name: Setup Ruby
uses: ruby/setup-ruby@de492cf36cbab201753d720ce4f4e336cd688c94 # v1.336.0
with:
ruby-version: "3.3"
bundler-cache: false

- name: Build Jekyll guides
run: |
gem install jekyll -v 4.3.4 --no-document
jekyll build --source docs --destination _site --strict_front_matter --trace

- name: Build DocFX API reference
shell: pwsh
run: ./scripts/build-api-docs.ps1

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: _site

deploy:
name: Deploy to Pages
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
7 changes: 1 addition & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# - six platform bundles (framework-dependent single-file CLI) attached to the GitHub release
# - build provenance attestations for the bundles and the NuGet package
# - the CHDSharp NuGet package
# - the docs/ folder synced to the GitHub wiki (and a Pages rebuild request)
# - the docs/ folder synced to the GitHub wiki (Pages is deployed by pages.yml)
#
# Assets are named CHDSharp_<os>-<arch>_v<version>.zip - the exact names the CLI's
# built-in update check (VersionCheckService) looks up via the GitHub releases API.
Expand Down Expand Up @@ -162,8 +162,3 @@ jobs:
git commit -m "wiki: sync from docs/ (${GITHUB_REF_NAME})"
git push
fi

- name: Request Pages rebuild
env:
GH_TOKEN: ${{ secrets.WIKI_TOKEN }}
run: gh api -X POST repos/purelogiccode/CHDSharp/pages/builds --jq '.status'
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
################################################################################
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################

Expand All @@ -17,3 +17,8 @@ TestResults/

# Release packaging / wiki staging
artifacts/

# DocFX generated API metadata and rendered site
docfx/*.yml
docfx/.manifest
_site/
52 changes: 33 additions & 19 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ the CHD (Compressed Hunks of Data) disk-image format used by MAME.
| `CHDSharpBenchmark/` | BenchmarkDotNet suite |
| `CHDSharpTestDataGeneration/` | Deterministic corpus generator |
| `docs/` | Jekyll documentation site (deployed to GitHub Pages) |
| `docfx/`, `docfx.json` | DocFX API reference source + config (generated `/api/` site, pinned tool in `.config/dotnet-tools.json`) |
| `References/` | Local chdman reference material; excluded from builds |

## Build / test / run
Expand All @@ -46,11 +47,13 @@ jb inspectcode CSharp_CHDSharp.sln --severity=WARNING # ReSharper inspections
- Keep builds warning-free: Meziantou.Analyzer and Roslynator.Analyzers run in every project.
- `CHDSharpLib` treats missing XML docs (CS1591) as errors — document every public API addition.
- Keep ReSharper inspections clean too: `jb inspectcode CSharp_CHDSharp.sln --severity=WARNING`
must report zero findings. The policy lives in the root `.editorconfig`: the vendored codec
folders are exempt from style inspections and must stay verbatim for `chdman` parity — never
"fix" a warning there, suppress the inspection instead. InspectCode 2026.2 logs a few internal
`AssertionException`s while analyzing `VendoredZSTD` (an analyzer bug, not a finding); the
report is still empty, and `--exclude="Vendored*/**/*.cs"` silences the console.
must report zero findings. The four vendored codec projects - `VendoredFlac`, `VendoredLZMA`,
`VendoredZLib` and `VendoredZSTD` - are excluded from InspectCode entirely
(`--exclude="Vendored*/**"`, projects and sources). They must stay verbatim for byte-for-byte
`chdman` parity: never edit them to satisfy an inspection, and never remove the exclusion. The
same rule lives in the root `.editorconfig` so IDE inspections stay quiet inside those folders.
InspectCode 2026.2 also logs a few internal `AssertionException`s while analyzing
`VendoredZSTD` (an analyzer bug, not a finding); the report is still empty.
- Always run `dotnet test` before committing. Run the battle harness for changes touching
encoding, decoding, CLI argument parsing, or anything chdman-visible.

Expand All @@ -59,8 +62,13 @@ jb inspectcode CSharp_CHDSharp.sln --severity=WARNING # ReSharper inspections
- The CLI and Tester configure Serilog at startup (`MinimumLevel.Debug`) and bridge the library
with `Chd.LoggerFactory = new SerilogLoggerFactory(...)`; the logger is flushed on exit.
- CLI sinks: console + rolling-day file (`%LocalAppData%\CHDSharp\logs\chdsharp-<date>.log`) +
`BugReportSink`. The sink POSTs **every Warning+ event** (except user cancellation) to
`https://www.purelogiccode.com/bugreport/api/send-bug-report`.
`BugReportSink`. The sink POSTs **every Warning+ event** (except user cancellation and expected
user/usage errors) to `https://www.purelogiccode.com/bugreport/api/send-bug-report`.
- Expected user/usage errors (bad flags, missing paths, sector-misaligned input, `--force` needed,
format overrides, ...) are diagnostics about the user's input, not defects. Log them with
`logger.UserError(...)` (`CHDSharpCli/UserErrorLogging.cs`), which keeps them at warning level in
the console/file logs but tags them so `BugReportSink` never reports them. Real runtime failures
(`Open failed`, `* failed: {Message}`, verify failures, ...) stay on `logger.Warning(...)`.
- Every report must embed: `=== Environment Details ===` (Date, Application Name, Application
Version, OS Version, Architecture, Bitness, Windows, Linux or MacOsX Version, Processor Count,
Base Directory, Temp Path), `=== Error Details ===` (message), and `=== Exception Details ===`
Expand Down Expand Up @@ -116,8 +124,8 @@ matches `Directory.Build.props`.
```

4. **Commit and push `main`**, then wait for the `CI gate` (three-OS build/test, InspectCode,
package, coverage, docs build, and the chdman parity smoke), after which the `Wiki & Pages` job
syncs the wiki and requests a Pages rebuild.
package, coverage, Jekyll + DocFX docs build, and the chdman parity smoke), after which the
`Wiki sync` job mirrors `docs/` to the wiki and the `Pages` workflow deploys the site.
5. **Build the bundles locally** (optional before tagging, required when the user asks for them):

```bash
Expand All @@ -140,8 +148,8 @@ matches `Directory.Build.props`.

`release.yml` verifies the tag against `Directory.Build.props`, rebuilds the six bundles,
creates the GitHub release with `WhatsNew.md` as the body, uploads the assets, publishes the
NuGet package (skipped when the version already exists), syncs the wiki and requests a Pages
rebuild.
NuGet package (skipped when the version already exists), syncs the wiki and
deploys the documentation site (`pages.yml`).

7. **Verify the release**:

Expand Down Expand Up @@ -170,27 +178,33 @@ Pages rebuild request; needs `repo` scope).
`jb inspectcode --severity=WARNING` finding, uploading the SARIF to code scanning.
- **`package`** - packs `CHDSharpLib`, checks the `.nupkg` contents, and uploads it as an artifact.
- **`coverage`** - collects merged Cobertura coverage for both suites on `net10.0`.
- **`docs build`** - builds the Jekyll site with `--strict_front_matter`.
- **`docs build (Jekyll + DocFX)`** - builds the Jekyll site with `--strict_front_matter`, then
generates the DocFX API reference (`scripts/build-api-docs.ps1`).
- **`chdman parity (smoke)`** - Windows; runs the quick battle harness against
`CHDSharpTester/chdman.exe`.
- **`CI gate`** - aggregates all of the above; this is the status check to require.
- **`Wiki & Pages`** - on `main` only, after the gate, regenerates the GitHub wiki from `docs/`
and requests a Pages rebuild.
- **`Wiki sync`** - on `main` only, after the gate, regenerates the GitHub wiki from `docs/`.

Tests that need `chdman` no-op when it is absent, so the unit suites only run chdman validation
on Windows; the quick battle job always passes the checked-in `CHDSharpTester/chdman.exe`
explicitly. CI sets `CHDSHARP_NO_BUG_REPORTS=1`/`CHDSHARP_NO_TELEMETRY=1` so test-spawned CLI
runs never reach the production bug-report API or usage stats. The same wiki/Pages steps run in
`release.yml` for tags, so code, wiki and the documentation site stay in sync. GitHub Pages is
still served by the repository-managed `pages-build-deployment` workflow from `main`/`docs`
(build type `legacy`).
runs never reach the production bug-report API or usage stats. The same wiki step runs in
`release.yml` for tags, so code and wiki stay in sync.

`.github/workflows/pages.yml` owns the documentation site: it builds the Jekyll guides plus the
DocFX API reference (`/api/`, from `CHDSharpLib` XML docs, DocFX pinned in
`.config/dotnet-tools.json`, driven by `scripts/build-api-docs.ps1`) and deploys the combined
artifact with `actions/deploy-pages`. GitHub Pages is configured with `build_type: workflow`;
there is no legacy `pages-build-deployment` build any more.

## Security

- CodeQL (`.github/workflows/codeql.yml`), Dependency Review
(`.github/workflows/dependency-review.yml`), and OpenSSF Scorecard
(`.github/workflows/scorecard.yml`) run in CI; Dependabot is configured in
`.github/dependabot.yml`.
`.github/dependabot.yml`. Dependency Review self-skips with a warning when the repository
dependency graph is disabled (GitHub exposes no API to enable it for user-owned repos - it is a
Settings > Code security and analysis toggle); the enforcement returns as soon as it is on.
- Repository settings: Dependabot alerts/security updates, secret scanning with push protection,
private vulnerability reporting, read-only default workflow permissions, and mandatory
full-SHA action pinning with GitHub-owned/verified actions only.
Expand Down
2 changes: 1 addition & 1 deletion CHDSharpBattleTest/CHDSharpBattleTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="3.0.317">
<PackageReference Include="Meziantou.Analyzer" Version="3.0.320">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
2 changes: 1 addition & 1 deletion CHDSharpBenchmark/CHDSharpBenchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<PackageReference Include="Meziantou.Analyzer" Version="3.0.317">
<PackageReference Include="Meziantou.Analyzer" Version="3.0.320">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
13 changes: 13 additions & 0 deletions CHDSharpCli/BugReportSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ internal sealed class BugReportSink : ILogEventSink, IDisposable
private static readonly HttpClient Client = new();
private static readonly string ApiKey = DecodeApiKey();

/// <summary>
/// Property attached to log events that describe an expected user or usage error (for example a bad
/// command-line flag, an input file whose size is not sector-aligned, or a missing path). Those events
/// are diagnostics about the user's input, not defects in this application, so they are never forwarded
/// to the Bug Report API. Attach it with <c>logger.UserError(...)</c>.
/// </summary>
public const string UserErrorProperty = "UserError";

private readonly EnvironmentSnapshot _env;
private readonly string _environmentLabel;

Expand Down Expand Up @@ -54,6 +62,11 @@ public void Emit(LogEvent logEvent)
{
if (logEvent.Level < LogEventLevel.Warning)
return;
if (
logEvent.Properties.TryGetValue(UserErrorProperty, out var userError)
&& userError is ScalarValue { Value: true }
)
return;
if (DisabledByEnvironment)
return;
if (logEvent.Exception is OperationCanceledException or TaskCanceledException)
Expand Down
2 changes: 1 addition & 1 deletion CHDSharpCli/CHDSharpCli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ProjectReference Include="..\CHDSharpLib\CHDSharpLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Meziantou.Analyzer" Version="3.0.317">
<PackageReference Include="Meziantou.Analyzer" Version="3.0.320">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
Loading
Loading