Skip to content

C1 drift: re-sync unprotected template-tracked files#183

Merged
Chris-Wolfgang merged 23 commits into
vNextfrom
canonical-unprotected
Jun 1, 2026
Merged

C1 drift: re-sync unprotected template-tracked files#183
Chris-Wolfgang merged 23 commits into
vNextfrom
canonical-unprotected

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

Template-drift resolution for ETL-Abstractions (initiative C1) — the unprotected half. Re-syncs 15 template-tracked infrastructure files (scripts, docs process files, .github templates, .gitattributes, etc.) to the canonical repo-template.

Excludes docfx_project/* (repo-specific docs — docs initiatives), protected config/workflow files (separate PR), and .gitkeep / expected-noise files.

Part of #155.

🤖 Generated with Claude Code

Template-drift resolution for ETL-Abstractions — the non-protected half. Re-syncs
15 template-tracked infrastructure files to the canonical
repo-template. Excludes docfx_project/* (repo-specific docs), protected
config/workflow files (separate PR), and .gitkeep / expected-noise files.

Part of #155.

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:29

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

Template-drift resolution (C1) that re-syncs several unprotected, template-tracked repository infrastructure assets (scripts, docs, issue/PR templates, and git attributes) to match the canonical repo-template.

Changes:

  • Adds maintenance-framework setup assets (labels script updates, parent-issue template + creation script, and an issue form for maintenance sub-issues).
  • Adds branch ruleset provisioning/repair scripts and enhances the main setup script’s “next steps” output.
  • Updates docs/process files and enforces/clarifies repo-wide LF line endings via .gitattributes.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
scripts/Validate-DocsDeploy.sh New post-DocFX deployment validation for gh-pages.
scripts/templates/maintenance-parent-body.md Canonical body template for the per-repo Maintenance parent issue.
scripts/setup.ps1 Updates setup “next steps” to include labels + Maintenance issue provisioning.
scripts/Setup-Maintenance.ps1 Creates the parent Maintenance issue (idempotent) using gh.
scripts/Setup-Labels.ps1 Updates label provisioning to include Maintenance framework labels.
scripts/Setup-BranchRuleset.ps1 Creates a main-branch ruleset (PR reviews, required checks, scanning rules).
scripts/format.ps1 Updates guidance text around SDK requirements and usage examples.
scripts/Fix-BranchRuleset.ps1 Disables/renames existing rulesets to allow recreating a clean one.
docs/WORKFLOW_SECURITY.md Expands protected-file patterns to include workflow YAMLs.
docs/RELEASE-WORKFLOW-SETUP.md Adjusts troubleshooting guidance wording for Actions logs.
docs/README-FORMATTING.md Updates formatting prerequisites guidance and LF line-ending narrative.
CONTRIBUTING.md Re-syncs template content and updates formatting/link references.
.github/pull_request_template.md Adds Maintenance-framework linking guidance for PRs.
.github/ISSUE_TEMPLATE/maintenance-task.yaml New issue form for Maintenance framework sub-issues.
.gitattributes Clarifies/enforces LF normalization, including for PowerShell shebang compatibility.

Comment thread CONTRIBUTING.md Outdated
Comment on lines +1 to +3
# Contributing to {{PROJECT_NAME}}

Thank you for your interest in contributing to **Wolfgang.Etl.Abstractions**! We welcome contributions to help improve this project.
Thank you for your interest in contributing to **{{PROJECT_NAME}}**! We welcome contributions to help improve this project.
Comment thread CONTRIBUTING.md Outdated
Comment thread scripts/format.ps1 Outdated
Comment thread scripts/Setup-Labels.ps1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
-H "X-GitHub-Api-Version: 2022-11-28" `
"/repos/$Repository/rulesets" `
--paginate `
--jq '.[] | select(.name == "Protect main branch")' 2>&1
Comment thread scripts/Setup-BranchRuleset.ps1 Outdated
@{ context = "Stage 2: Windows Tests (.NET 5.0-10.0, Framework 4.6.2-4.8.1)" },
@{ context = "Stage 3: macOS Tests (.NET 6.0-10.0)" },
@{ context = "Security Scan (DevSkim)" },
@{ context = "Security Scan (CodeQL) (csharp)" },
Chris-Wolfgang and others added 5 commits May 22, 2026 12:27
…stractions)

The C1 drift re-sync brought the canonical CONTRIBUTING.md but left the
template's {{PROJECT_NAME}} placeholder literal. Replace it with this repo's
project name so the synced CONTRIBUTING.md is correct, not a raw template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fold-down of the repo-template fix (PR #389): Replace-Placeholders now
writes with -Encoding utf8NoBOM -NoNewline. Rolled to every repo so the
campaign stays consistent with the updated canonical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Propagates the three files folded into repo-template during drift Phase 0:
tests/Directory.Build.props + benchmarks/Directory.Build.props
(TreatWarningsAsErrors=false) and tests/.editorconfig (test-project analyzer
relaxations). Excluded from the initial C1 sync; rolled out now so every repo
matches the canonical template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the TreatWarningsAsErrors=false override on test projects. The
genuinely test-inappropriate analyzer rules are already silenced per-rule in
tests/.editorconfig; test code now inherits the root Directory.Build.props
(TreatWarningsAsErrors in Release, like src/examples). benchmarks remain
exempt for now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Append a github-actions package-ecosystem to .github/dependabot.yml so
Dependabot opens weekly PRs to bump pinned action versions in the repo's
GitHub workflows. Grouped under a single PR per week. Existing nuget
ecosystems are left unchanged.

Initiative CI2.

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 — CI2 stacked.

This canonical-unprotected branch now also adds a github-actions package-ecosystem to .github/dependabot.yml so Dependabot opens weekly grouped PRs to bump pinned action versions in .github/workflows/*. Existing nuget ecosystems are unchanged.

Initiative CI2 rides this existing branch per your guidance ("add to existing branch").

Keep-a-Changelog skeleton with an [Unreleased] section so release
notes can accumulate here between releases instead of being lost.
Follows the canonical format used across the fleet.

Initiative D3.

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 — D3 stacked.

Adds CHANGELOG.md (Keep-a-Changelog skeleton with an [Unreleased] section) so release notes can accumulate here between releases instead of being lost in commit history. Canonical content — identical across the fleet.

- scripts/Setup-BranchRuleset.ps1: jq filter wrapped in '[ .[] | select() ]'
  so the output is always valid JSON even with multiple matches; gh stderr
  redirected to a temp file so it can't poison stdout.
- scripts/Fix-BranchRuleset.ps1: same stderr-isolation fix on the rulesets
  fetch (no more '2>&1' merge).
- scripts/Validate-DocsDeploy.sh: distinguish 'versions.json never created'
  from 'versions.json failed validation' in step 4 so the skip message
  reflects reality.
- .github/dependabot.yml: drop the stale 'dotnet' label (no longer in the
  Setup-Labels.ps1 taxonomy).
- REPO-INSTRUCTIONS.md: replace the stale label list (dependabot-*, dotnet)
  with the current Maintenance-framework labels Setup-Labels.ps1 actually
  creates.

IAsyncEnumerable-Extensions also gets README.md / CONTRIBUTING.md updated
to point at docs/README-FORMATTING.md since the root copy was removed by
the earlier D7 cleanup.

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang Chris-Wolfgang changed the base branch from main to vNext May 23, 2026 23:59
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Retargeted: base changed from `main` to `vNext`.

Per the per-repo release plan, this PR now merges into the repo's `vNext` integration branch instead of directly into `main`. vNext currently equals `canonical-protected`; merging this PR brings the unprotected canonical work onto vNext, where the Release build + automated tests run as the release gate.

After both halves are on vNext and the gate is green, the per-repo release flow is:

  1. Merge `canonical-protected` → `main` (admin bypass — protected files).
  2. Merge `vNext` → `main` (its diff vs. main is then purely unprotected → normal review).
  3. Tag a release; `release.yaml` publishes the new version.

Chris-Wolfgang and others added 15 commits May 23, 2026 21:07
These scripts were run once when this repo was bootstrapped from
repo-template and are not needed afterward:

- scripts/setup.ps1                              (orchestrator for the others)
- scripts/Setup-Maintenance.ps1                  (created the Maintenance issues)
- scripts/Setup-BranchRuleset.ps1                (created the main branch ruleset)
- scripts/Setup-GitHubPages.ps1                  (bootstrapped gh-pages branch)
- scripts/templates/maintenance-parent-body.md   (template used by Setup-Maintenance)

repo-template keeps these files — they remain available there so any new
repo created from the template can still run them at bootstrap.

Recurring utilities are intentionally kept: Setup-Labels.ps1 (idempotent),
Fix-BranchRuleset.ps1, format.ps1, Validate-DocsDeploy.sh, build-pr.ps1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The formatting section referenced `pwsh ./format.ps1` and linked to
`README-FORMATTING.md` at the repo root, but the actual paths are
`scripts/format.ps1` and `docs/README-FORMATTING.md`. Both are
broken as written — contributors who follow the instructions get a
"file not found" on the script and a 404 on the link.

Correcting both references to match the canonical repo layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related problems:
  1. EXAMPLE comments showed `.\format.ps1` (current directory), but
     the script lives at `scripts/format.ps1`. Contributors who copy-
     pasted the examples got "file not found".
  2. The script used `Get-ChildItem -Path .` to find the solution,
     which only worked when invoked from the repo root. Invoking from
     anywhere else (including the scripts/ folder where the script
     lives) failed with "No solution file found".

Now the script pins cwd to the repo root via Resolve-Path
$PSScriptRoot/..  inside a Push-Location / try / finally Pop-Location
block, so it works from any working directory inside the repo. The
EXAMPLE comments and the Check-mode error message both reference
the correct `.\scripts\format.ps1` path.

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

The AsyncFixer bullet listed "Ensures proper ConfigureAwait() usage" as
one of its capabilities. That's inaccurate — ConfigureAwait enforcement
in this fleet is handled by Meziantou's MA0004 (plus SonarAnalyzer's
S3216 and Microsoft's CA2007), not by AsyncFixer. AsyncFixer covers
other async anti-patterns: AsyncFixer01–05, cancellation-token
propagation, fire-and-forget detection.

Updated the bullet to describe what AsyncFixer actually does and added a
note pointing readers at the right analyzer for ConfigureAwait
enforcement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When setup.ps1 reads the repo from an SSH-style git remote
(`git@github.com:owner/repo.git`), the placeholder replacement can
leave a leading "@" and/or trailing ".git" in the Repository parameter
of this script. Both break `gh api /repos/<repo>/rulesets` calls — the
"@" turns into a 404 and the ".git" suffix is rejected as an unknown
repo.

Added a normalization step right after the parameter declaration that
strips both. Auto-detection via `gh repo view` already produces a
clean nameWithOwner value, so this only affects the user-supplied or
placeholder-substituted path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three related fixes so the local script behaves like the per-PR
workflow instead of silently passing where CI would fail:

  1. Linux/macOS gitleaks install used `curl ... | tar -C /usr/local/bin`,
     which requires sudo for normal users and fails out of the box on
     most local dev shells. Now installs to $HOME/.local/bin and adds
     that to PATH (only if not already there). Matches the user-writable
     pattern the CI workflows use.

  2. When CoverageReport/Summary.txt was missing after a successful test
     run, the script printed "skipping threshold check" and reported
     success. pr.yaml fails the job in that situation. Align: emit a
     hard failure with a clear message about ReportGenerator and mark
     Coverage as failed.

  3. "No test projects found in ./tests — skipping" let the script
     finish "All checks passed" even when the repo had src/ projects.
     Same rule as the pr.yaml tests-gate strict mode now: if ./src has
     projects, refuse the silent skip and fail. If neither has projects
     (template-pack / in-dev repos), graceful skip is preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The prerequisites listed ".NET 8.0 SDK or later", but the repo targets
net10.0 (and multi-targets older TFMs). SDK 8.0 cannot load a net10.0
csproj — contributors who follow the instructions would hit NETSDK1045
"does not recognize TargetFramework net10.0".

Bumped to .NET 10.0 SDK with a short note about why older SDKs
won't work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`git worktree add` requires the target path NOT to exist, but the
script created the path first via `mktemp -d` and then immediately
called `git worktree add "$WORK_DIR" origin/gh-pages` — failing with
"fatal: '<path>' already exists" on every invocation.

Fix: `rmdir` the directory right after mktemp reserves the unique
name. mktemp still guarantees uniqueness, the trap still cleans up
afterward, and worktree-add now succeeds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The [ValidateScript] guard for -Repository was dropped in an earlier
refactor, letting callers pass a full URL
(https://github.com/owner/repo), a value with a leading "@" from an
SSH remote, or other malformed inputs. gh api would later fail with a
confusing 404 and the user wouldn't know the parameter format was the
problem.

Restored an early format check via [ValidatePattern]:
  - empty       (auto-detect via `gh repo view` kicks in downstream)
  - the template {{GITHUB_USERNAME}}/{{REPO_NAME}} placeholder
    (also replaced downstream by setup.ps1)
  - strict `owner/repo` (no /, no @, no whitespace)

Anything else fails at parameter binding with a clear error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Windows gitleaks install path uses Invoke-WebRequest with
-UseBasicParsing, which is a Windows-PowerShell-5.1-only flag. The
script's shebang is pwsh, so PowerShell 7+ errors on the unsupported
flag and gitleaks never installs locally.

Same fix we applied to docfx.yaml's Invoke-WebRequest earlier — drop
the flag (it's unnecessary in pwsh).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Policy: TreatWarningsAsErrors=true applies to ALL projects in Release
(src + tests + examples + benchmarks). The subtree Directory.Build.props
files in examples/, benchmarks/ were overriding the root
property back to false for their respective subtrees, contradicting the
stated convention.

Per-csproj <NoWarn> is the right tool for stylistic / test-pattern /
benchmark-specific suppressions — broad TWEA flips at the subtree level
are too coarse and let real regressions slip through CI. (The
.editorconfig [tests/**/*.cs] relaxations don't help either: the Windows
runner doesn't honor them, so test-project warnings need per-csproj
<NoWarn> regardless.)

Each removed file contained only the MSBuild Import + the TWEA override,
nothing else worth preserving. After this commit, examples/, benchmarks/ subtree(s)
inherit root TWEA=true in Release.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`REPO_NAME=${REPO_URL##*/}` worked for HTTPS remotes
(`https://github.com/owner/repo[.git]` → "repo") but is brittle for
SSH-style remotes (`git@github.com:owner/repo.git`). For example
if someone has an SSH remote without the `.git` suffix, the parsing
falls back to using parts of the host instead of the repo name.

Changing the parameter expansion to strip after either `/` or `:`
(`${REPO_URL##*[/:]}`) so both URL forms produce just "repo".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After installing dotnet-reportgenerator-globaltool, the script invokes
`reportgenerator` immediately. On a shell where the .NET installer
hasn't yet appended $HOME/.dotnet/tools (Linux/macOS) or
$env:USERPROFILE\.dotnet\tools (Windows) to PATH — common for
fresh terminals or pwsh sessions launched from a script — the call
fails with "reportgenerator: command not found".

Switched to update-or-install (idempotent for already-installed
tools) and added an explicit PATH-prepend after the install so the
subsequent reportgenerator call always finds the binary.

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

Fixes two bugs in the canonical-unprotected scripts that were fanned out
from repo-template:

- Setup-Labels.ps1: the ValidatePattern attribute string was left
  unterminated and the [string]$Repository declaration + closing paren
  were dropped, leaving a duplicated script body that ran with
  $Repository unbound. Param block restored to a single well-formed
  declaration; duplicated body removed.

- Fix-BranchRuleset.ps1: gh api --paginate concatenates multiple JSON
  array payloads when results span pages, which breaks ConvertFrom-Json.
  Switched to ?per_page=100 in a single call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every shippable src csproj across the fleet had `<AssemblyVersion>1.0.0</AssemblyVersion>`
hardcoded — last touched at v1.0.0 and never bumped. Released NuGet
packages from v1.1.0 onward have shipped with AssemblyVersion=1.0.0
even though the package <Version> bumped normally.

Drop the explicit AssemblyVersion line. .NET SDK derives AssemblyVersion,
FileVersion, and InformationalVersion from <Version> when they are not
specified, so all four track every <Version> bump automatically going
forward. They are not byte-identical — AssemblyVersion is normalized to
a 4-part System.Version, InformationalVersion can carry a +<git-sha>
under SourceLink — but they all reflect the current Version, which is
the point.

Mirrors DateTime-Extensions PR #184 (already merged to main).

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