Skip to content

Canonical-protected: C1 drift + nullable + S1 + D6 + D8 + A1 + CI3 + T1 + T3#101

Closed
Chris-Wolfgang wants to merge 69 commits into
mainfrom
canonical-protected
Closed

Canonical-protected: C1 drift + nullable + S1 + D6 + D8 + A1 + CI3 + T1 + T3#101
Chris-Wolfgang wants to merge 69 commits into
mainfrom
canonical-protected

Conversation

@Chris-Wolfgang

@Chris-Wolfgang Chris-Wolfgang commented May 22, 2026

Copy link
Copy Markdown
Owner

Template-drift resolution for ETL-Test-Kit (initiative C1) plus eight additional canonical initiatives stacked onto this same canonical-protected branch over the C1 campaign. Review carefully before admin-bypass merge — bypass waives all ruleset gates.

Stacked scope (in commit order)

  1. C1 — protected-file drift re-sync (root config / workflow files that trip the pr.yaml Detect .NET Projects guard).
  2. Nullable consolidation<Nullable>enable</Nullable> hoisted into root Directory.Build.props (conditioned to .csproj only); per-csproj <Nullable> lines removed for atomicity (this is why the PR also touches src/tests/examples csproj files — they must merge together with the Directory.Build.props change so Release builds never see a window with nullable annotations but no nullable context).
  3. S1 — CodeQL queries: security-extended added to .github/workflows/codeql.yaml.
  4. D8verify-docs-build job added to .github/workflows/release.yaml; publish-nuget now needs [pack-and-validate, verify-docs-build] so a docs failure blocks the release.
  5. D6versions.json preservation guard added to .github/workflows/docfx.yaml (aborts deploy if previously-published version entries would be lost).
  6. A1Microsoft.CodeAnalysis.PublicApiAnalyzers infrastructure added to Directory.Build.props with opt-in AdditionalFiles globbing (per-project enablement via PublicAPI.Shipped.txt/PublicAPI.Unshipped.txt).
  7. CI3 — Fleet-canonical NuGet metadata defaults (Authors/Company/Copyright/RepositoryType/SymbolPackageFormat/SourceLink/symbols) added to root Directory.Build.props.
  8. T1 — Coverage-report generation step added to docfx.yaml (publishes /coverage/ alongside /api/ on the docs site).
  9. T3 — Stryker mutation-testing workflow (.github/workflows/stryker.yaml) folded in via merge commit (was previously a separate t3-stryker-mutation-testing PR; consolidating onto this branch reduces the per-repo release flow to one admin-bypass merge instead of two).

Why these ride together

The pr.yaml "Detect .NET Projects" guard fires on protected file changes and forces admin-bypass merge. Bypass waives every ruleset rule at once, so each canonical pass costs at most one bypass per repo by design.

Per the C1 plan's standing-branch model, additional canonical changes stack onto this branch rather than fanning out as separate admin-bypass PRs.

🤖 Generated with Claude Code

Template-drift resolution for ETL-Test-Kit — the protected half. 5 files
that trip the pr.yaml guard, isolated for admin-bypass merge.

Part of #72.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 15:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR re-syncs protected repository-template files as part of initiative C1 (issue #72), aligning workflow and configuration content back to the canonical template while keeping the PR-guarded files consistent for an admin-bypass merge.

Changes:

  • Reworked workflow steps to include dotnet workload restore in CI (PR checks + CodeQL) to support workload-bearing TFMs.
  • Updated DocFX workflow logic (SemVer prerelease ordering comments/behavior, and gh-pages deploy/auth handling adjustments).
  • Normalized template/config text updates in .editorconfig and BannedSymbols.txt.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
BannedSymbols.txt Updates header text to template placeholder and removes trailing whitespace line.
.github/workflows/pr.yaml Adjusts protected-config fetching logic placement/duplication and adds workload restore before restore/build/test steps.
.github/workflows/docfx.yaml Fixes SemVer prerelease comparison commentary/behavior and refactors gh-pages deploy auth/cleanup flow.
.github/workflows/codeql.yaml Adds workload restore step prior to restore/build for CodeQL analysis runs.
.editorconfig Clarifies PowerShell encoding/LF guidance comment formatting.

Comment thread .github/workflows/docfx.yaml
Add <Nullable>enable</Nullable> to the root Directory.Build.props and
remove the now-redundant per-project <Nullable> lines from every
SDK-style csproj. Nullable reference types are configured in one place;
a newly added project inherits the setting automatically.

Both halves ride this protected branch so they merge atomically — the
Directory.Build.props addition and the csproj removals land together,
so there is never a window where nullable reference types are off.

Legacy non-SDK project files do not import Directory.Build.props and
are left untouched with their explicit settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope added to this PR — nullable consolidation. <Nullable>enable</Nullable> is now set once in the root Directory.Build.props, and the redundant per-csproj <Nullable> lines have been removed from every SDK-style project. Both halves are on this protected branch so they merge atomically — there is never a window where nullable reference types are off. Legacy non-SDK example projects do not import Directory.Build.props and keep their explicit settings.

Legacy non-SDK .csproj files explicitly import Microsoft.Common.props,
so they DO inherit Directory.Build.props — the earlier unconditional
<Nullable>enable</Nullable> reached projects it should not have:

  * F# (.fsproj) / VB (.vbproj) projects — now excluded by conditioning
    the property on '$(MSBuildProjectExtension)' == '.csproj'.
  * legacy non-SDK C# example projects (C# 7.3, no nullable support) —
    given an explicit <Nullable>disable</Nullable> opt-out, restoring
    their pre-hoist state. These are the documented C5 carve-outs.

SDK-style C# projects are unaffected — they still inherit enable from
the single Directory.Build.props.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 19:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Comment thread BannedSymbols.txt Outdated
Comment thread .github/workflows/docfx.yaml Outdated
Comment thread .github/workflows/docfx.yaml Outdated
Chris-Wolfgang and others added 3 commits May 22, 2026 21:07
Adds queries: security-extended to the CodeQL init step so the broader
security query pack runs on top of the default queries. Slightly longer
scans, materially more security coverage.

Initiative S1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New .github/workflows/stryker.yaml runs Stryker.NET against the repo's
test projects on workflow_dispatch and a weekly schedule. The workflow
is a no-op until a stryker-config.json is added at repo root or under
tests/<project>/ — this commit is the canonical infrastructure;
per-repo Stryker config is the follow-up.

Initiative T3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a verify-docs-build job to release.yaml that runs DocFX without
deploying (metadata + build + output check). publish-nuget now needs
[pack-and-validate, verify-docs-build] so a broken docs build blocks
the release before the NuGet package goes live.

If a repo has no docfx_project/docfx.json, the job no-ops with a
notice; this is the canonical infrastructure, with per-repo docs
coverage tracked separately.

Initiative D8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 01:19
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope additions to this PR — S1 + D8 stacked.

The canonical-protected branch now also carries:

  • S1queries: security-extended added to .github/workflows/codeql.yaml (3-line addition under the CodeQL init step) for broader security-query coverage.
  • D8 — new verify-docs-build job in .github/workflows/release.yaml; publish-nuget now needs: [pack-and-validate, verify-docs-build] so a docs failure blocks the release before the NuGet package goes live.

All four logical changes — C1 drift re-sync, nullable consolidation, S1, D8 — ride this single protected PR per the standing-branch model and merge atomically via admin bypass.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread BannedSymbols.txt Outdated
Add a guard step to docfx.yaml that fetches the currently-deployed
versions.json from gh-pages and confirms the newly-generated one has
at least as many entries AND retains every previously-published version
label. Aborts the deploy if the version selector would shrink or lose
entries.

If no existing versions.json is found (first deploy), the step no-ops
with a notice.

Initiative D6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope addition to this PR — D6 stacked.

Adds a Verify previous versions preserved step to .github/workflows/docfx.yaml (right before Compute destination directory). The step fetches the currently-deployed versions.json from gh-pages and aborts the deploy if the newly-generated file would shrink or drop any previously-published version label — guards against accidentally wiping the docs version selector.

Add the Microsoft.CodeAnalysis.PublicApiAnalyzers package plus opt-in
AdditionalFiles globbing for PublicAPI.Shipped.txt / Unshipped.txt to
the root Directory.Build.props.

The AdditionalFiles use Exists() conditions, so the analyzer activates
per-project only when those files are present. Library projects opt in
by dropping the two text files into the src directory; test, example,
and benchmark projects stay dormant.

Per-repo enablement (populate Unshipped.txt with the current public API
surface) is tracked as a separate follow-up maintenance issue.

Initiative A1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 18:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Comment thread BannedSymbols.txt Outdated
Comment thread .github/workflows/docfx.yaml
Comment thread .github/workflows/docfx.yaml
Chris-Wolfgang and others added 2 commits May 23, 2026 15:20
Add fleet-canonical defaults to root Directory.Build.props:

- Authors / Company / Copyright (uniform across the fleet; per-csproj
  values still win where set explicitly).
- RepositoryType=git, PublishRepositoryUrl=true.
- IncludeSymbols=true + SymbolPackageFormat=snupkg so .snupkg ships
  with every .nupkg.
- EmbedUntrackedSources=true to capture generated sources in PDBs.
- ContinuousIntegrationBuild=true under $(CI)=true (deterministic
  build flag, set by GitHub Actions).
- Microsoft.SourceLink.GitHub package so debuggers can step from
  NuGet-installed code straight to GitHub source.

Repo-specific NuGet fields (Description, PackageTags, PackageProjectUrl,
RepositoryUrl, PackageLicenseExpression, PackageReadmeFile) stay in
per-src csproj where they belong and are tracked as per-repo follow-up
maintenance issues.

Initiative CI3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a docfx.yaml step that runs the test suite with Cobertura coverage
collection and generates a ReportGenerator HTML report into
docfx_project/_site/coverage/ before the deploy step. The published
docs site gains a /coverage/ subpath alongside the existing /api/.

continue-on-error keeps a coverage failure from blocking the docs
deploy; if no test projects are present the step no-ops.

Initiative T1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 19:23
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope additions to this PR — A1 + CI3 + T1 stacked.

  • A1Microsoft.CodeAnalysis.PublicApiAnalyzers added to root Directory.Build.props with opt-in AdditionalFiles globbing (Exists() conditions on PublicAPI.Shipped.txt / PublicAPI.Unshipped.txt). Infrastructure only — analyzer stays dormant per-project until the txt files are dropped into a src project dir. Per-repo enablement is tracked separately.
  • CI3 — Fleet-canonical NuGet metadata defaults (Authors / Company / Copyright / RepositoryType / SymbolPackageFormat / SourceLink + symbols). Repo-specific NuGet fields (Description, Tags, ProjectUrl, RepositoryUrl, LicenseExpression, PackageReadmeFile) remain in each src csproj and are tracked as per-repo follow-up.
  • T1.github/workflows/docfx.yaml gains a "Generate code-coverage report" step that runs the test suite with Cobertura coverage collection and publishes a ReportGenerator HTML report under /coverage/ on the docs site. continue-on-error: true keeps a coverage failure from blocking the docs deploy.

Each canonical-protected PR now carries: C1 protected drift + nullable consolidation + S1 + D6 + D8 + A1 + CI3 + T1.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread BannedSymbols.txt Outdated
- BannedSymbols.txt: replace {{PROJECT_NAME}} placeholder with the repo's
  package name (skipped on repo-template where the placeholder is the
  intended template artifact).
- docfx.yaml: 'exit 1' inside the deploy try-block changed to 'throw' so
  the outer finally always unsets the global http.extraheader token; added
  $LASTEXITCODE checks after git fetch / git worktree add / git init /
  git remote add so a setup failure surfaces a clear error.

Fan-out of the round-2 Copilot fixes verified against DateTime-Extensions
(#178 / #179 pilot).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 25, 2026 19:26
Stage 2 (Windows) coverage parser was hardened earlier — greedy regex
+ matched-count guard that fails when no modules parse. Stage 1
(Linux bash) still ran the original loose awk parser without the
guard, so if Summary.txt format changes (or the report is malformed
in a way that bypasses the line regex), Stage 1 would silently pass
with an empty failed_projects list — exact opposite of Stage 2's
new behavior.

Adding the same matched_count guard so Stage 1 matches Stage 2
semantically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Comment thread .github/workflows/docfx.yaml Outdated
Comment thread .github/workflows/docfx.yaml
Chris-Wolfgang and others added 2 commits May 25, 2026 15:36
1. (Stage 1/3 bash, exact-file branch) Added an explicit exit-code
   check around `git show > "$config_file"`. The globbed branch was
   hardened earlier; the exact-file branch had been left silently
   falling back to the PR version on any git show failure.

2. (Stage 1/3 bash, globbed branch) The process substitution
   `done < <(... | grep -E ...)` runs under `set -eo pipefail`, and
   grep exits 1 when no entries match. For repos that don't ship a
   `*.ruleset` (or any other optional pattern), the previously-fine
   "no matches → skip" behavior was actually failing the step.
   Wrapped the grep in `{ ... || true; }` so an empty match is
   treated as zero iterations, not a failure.

3. (Stage 2 pwsh) `Out-File ... -NoNewline` strips trailing newlines
   from the copied workflow/globalconfig/ruleset/editorconfig files,
   producing malformed copies. Removed -NoNewline so the file's
   trailing newline is preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy-Item -Force overwrites files that exist in both source and
destination, but doesn't remove destination-only files. Over multiple
releases — if a docs page or asset is dropped from docfx output —
the stale file lingers indefinitely in gh-pages, served alongside
the current docs.

Adding a clear-before-copy step for both versions/<VERSION_DIR> and
versions/latest. Preserves the directory entry itself so per-release
git diffs stay clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 25, 2026 19:39
The earlier "Restore dependencies" step already does the restore, so
the implicit restore inside dotnet test is wasted I/O. Adding
--no-restore alongside the existing --no-build to skip both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Chris-Wolfgang and others added 2 commits May 25, 2026 15:43
The D6 "Verify previous versions preserved in versions.json" guard
ran whenever deploy_to_pages != false. But the deploy step only
touches the root versions.json when deploy_as_latest is ALSO true:

  - deploy_to_pages=false → dry-run, nothing deploys
  - deploy_to_pages=true + deploy_as_latest=false → rebuild a single
    older version; deploy writes only versions/<dir>/, doesn't touch
    the root
  - deploy_to_pages=true + deploy_as_latest=true → full deploy that
    overwrites the root versions.json

The guard exists to protect that root file. In the rebuild-an-older-
version case there's nothing for the guard to protect, but it still
fetched the live Pages versions.json and compared — meaning a
transient Pages fetch/parse error would block a legitimate rebuild.

Tightening the if condition so the guard only runs in the third case
(both inputs true).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Write-Error prefixes its output with PowerShell error formatting
(file/line/category metadata), so a literal ::error::... payload
no longer starts the line — and Actions' workflow-command parser
looks for ::error:: at the BEGINNING of a stream line. The result
was that the missing-versions.json failure produced a red error in
the log but no annotation marker on the run summary.

Switching to Write-Host (literal output, no PowerShell prefixing)
plus the existing exit 1 keeps the annotation visible and still
fails the step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 25, 2026 19:56
My earlier Stage 1 hardening kept the integer-only regex [0-9]+% and
the bash integer comparator -lt. ReportGenerator typically emits
decimals like "92.3%" — the regex missed those, and the new
matched_count=0 guard would then fail jobs whose Summary.txt had only
decimal rows. Even after broadening the regex to accept decimals, bash
[ errors with "integer expression expected" on a non-integer value.

Two fixes:

  1. Regex now matches [0-9]+(\.[0-9]+)?% so decimal percents
     count toward matched_count.

  2. Percent is floored to an integer via awk '{print int($1)}'
     before the -lt comparison. Matches Stage 2 pwsh's
     [int][math]::Floor([double]$Matches[2]) semantically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

Chris-Wolfgang and others added 2 commits May 25, 2026 16:59
….Xunit

Activates the PublicApiAnalyzer that has been scaffolded but silent
since A1 landed. Initial Shipped.txt captures the current public surface,
frozen as the v0.x baseline. Unshipped.txt is empty header-only.

Posture: v0.x baseline. Future PRs that change public API will show
diffs in Unshipped.txt (additions) or move Shipped entries to Unshipped
with *REMOVED* markers (deletions). Pre-1.0 the diff is informational —
surface intentional vs accidental change — and becomes a stability
contract post-1.0.

Build verified clean across all TFMs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
….Xunit

Activates the PublicApiAnalyzer that has been scaffolded but silent
since A1 landed. Initial Shipped.txt captures the current public surface,
frozen as the v0.x baseline. Unshipped.txt is empty header-only.

Posture: v0.x baseline. Future PRs that change public API will show
diffs in Unshipped.txt (additions) or move Shipped entries to Unshipped
with *REMOVED* markers (deletions). Pre-1.0 the diff is informational —
surface intentional vs accidental change — and becomes a stability
contract post-1.0.

Build verified clean across all TFMs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 25, 2026 21:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

Chris-Wolfgang and others added 3 commits May 25, 2026 18:40
The Stage 2 (Windows) coverage gate regex

    ^\s*(\S+)\s+.*(\d+(?:\.\d+)?)%\s*$

had a greedy `.*` between the module name and the trailing `\d+%`,
which ate all but the last digit of the percent. On lines like

    Wolfgang.Extensions.<X>                         100%

the regex captured percent=0 (the last "0" of "100"), reported the
module as failing the 90% threshold, and tanked the entire gate even
on 100%-covered code. First surfaced on DateTime-Extensions vNext —
PR #189 has the original fix.

Two changes to align with Stage 1 (Linux), whose awk-based parser is
correct:

- Anchor on `^(\S+)` to skip indented sub-class rows (Stage 1's
  `^[^ ]` does the same — assembly rows carry the aggregate
  percent, so nothing is lost).

- Drop the `.*`; let `\s+` separate the module from the final
  `\d+%` directly, so there is no greedy region to swallow digits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 26, 2026 16:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.

@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Superseded by PR #112 (vNext → main release). Content is folded into the vNext release PR alongside the rest of the canonical maintenance round.

@Chris-Wolfgang Chris-Wolfgang deleted the canonical-protected branch June 22, 2026 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants