Canonical-protected: C1 drift + nullable + S1 + D6 + D8 + A1 + CI3 + T1 + T3#101
Canonical-protected: C1 drift + nullable + S1 + D6 + D8 + A1 + CI3 + T1 + T3#101Chris-Wolfgang wants to merge 69 commits into
Conversation
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>
There was a problem hiding this comment.
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 restorein 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
.editorconfigandBannedSymbols.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. |
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>
|
Scope added to this PR — nullable consolidation. |
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>
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>
|
Scope additions to this PR — S1 + D8 stacked. The canonical-protected branch now also carries:
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. |
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>
|
Scope addition to this PR — D6 stacked. Adds a |
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>
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>
|
Scope additions to this PR — A1 + CI3 + T1 stacked.
Each canonical-protected PR now carries: C1 protected drift + nullable consolidation + S1 + D6 + D8 + A1 + CI3 + T1. |
- 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>
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>
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>
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>
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>
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>
….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>
….TestKit.Xunit" This reverts commit 9e42df7.
….TestKit.Xunit" This reverts commit 7d2fe3a.
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>
|
Superseded by PR #112 (vNext → main release). Content is folded into the vNext release PR alongside the rest of the canonical maintenance round. |
Template-drift resolution for
ETL-Test-Kit(initiative C1) plus eight additional canonical initiatives stacked onto this samecanonical-protectedbranch over the C1 campaign. Review carefully before admin-bypass merge — bypass waives all ruleset gates.Stacked scope (in commit order)
pr.yamlDetect .NET Projects guard).<Nullable>enable</Nullable>hoisted into rootDirectory.Build.props(conditioned to.csprojonly); per-csproj<Nullable>lines removed for atomicity (this is why the PR also touches src/tests/examples csproj files — they must merge together with theDirectory.Build.propschange so Release builds never see a window with nullable annotations but no nullable context).queries: security-extendedadded to.github/workflows/codeql.yaml.verify-docs-buildjob added to.github/workflows/release.yaml;publish-nugetnow needs[pack-and-validate, verify-docs-build]so a docs failure blocks the release.versions.jsonpreservation guard added to.github/workflows/docfx.yaml(aborts deploy if previously-published version entries would be lost).Microsoft.CodeAnalysis.PublicApiAnalyzersinfrastructure added toDirectory.Build.propswith opt-inAdditionalFilesglobbing (per-project enablement viaPublicAPI.Shipped.txt/PublicAPI.Unshipped.txt).Authors/Company/Copyright/RepositoryType/SymbolPackageFormat/SourceLink/symbols) added to rootDirectory.Build.props.docfx.yaml(publishes/coverage/alongside/api/on the docs site)..github/workflows/stryker.yaml) folded in via merge commit (was previously a separatet3-stryker-mutation-testingPR; 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