Skip to content

Enterprise quality bar: build hardening, CI/CD gates, security tooling, integration tests#4

Merged
jkeeley2073 merged 5 commits into
mainfrom
Dev-EnterpriseQuality
May 2, 2026
Merged

Enterprise quality bar: build hardening, CI/CD gates, security tooling, integration tests#4
jkeeley2073 merged 5 commits into
mainfrom
Dev-EnterpriseQuality

Conversation

@jkeeley2073
Copy link
Copy Markdown
Contributor

Summary

Five focused commits that raise PinballWizard to a portfolio-grade quality bar across build, test, security, and documentation. Each commit builds and tests independently green (bisect-safe).

Commit Type What
ab7c5aa chore(build) Build hardening + CI gates + sanitization guardrail
05295f3 chore(security) Dependabot + CodeQL workflows
186b363 test(integration) DI graph + resilience pipeline + catalog reconciliation tests
9b6d886 docs Engineering standards reference
65cff07 fix(catalog) Resolve nullable Source dereference + drop transitional suppression

Guardrails this PR delivers

Guardrail Lives in
TreatWarningsAsErrors=true + latest-recommended analyzers Directory.Build.props
Deterministic builds, ContinuousIntegrationBuild on CI Directory.Build.props
Locked-mode NuGet restore Directory.Build.props + packages.lock.json files
CI on every PR (build + test + coverage) .github/workflows/ci.yml
Sanitization scan (forbidden tokens fail the build) .github/workflows/sanitization.yml
Automated dependency updates .github/dependabot.yml
SAST on push, PR, and weekly .github/workflows/codeql.yml
DI graph + resilience pipeline regression tests tests/.../IntegrationTests.cs
Durable PR checklist docs/quality-bar.md
Deep engineering standards reference docs/ENGINEERING_STANDARDS.md
Contributor onboarding CONTRIBUTING.md

Notable findings during this work

  • Logging discipline already clean. A structured-logging audit of all 27 _logger.Log* call sites found zero offending interpolations — the team already uses message templates with PascalCase placeholders. No source changes needed.
  • Sanitization workflow earned its keep before its first run. Two JungleTech references slipped into docs/ENGINEERING_STANDARDS.md and were scrubbed before commit; the workflow would have caught them on first CI run.
  • CS8602 was a band-aid replaced by a real fix. Initially suppressed at project level to keep the build green; final commit fixes the one site (CatalogBuilder.MergeGameRecord accessing optional Source) and removes the suppression. The fix correctly merges fresh-scraped Source with existing.

NoWarn list (documented in Directory.Build.props)

Rule Disposition Why
CA1848 / CA1873 DELIBERATE LoggerMessage perf delegates are overkill at our log volume (one scraper run/day). Re-evaluate at higher scale.
CA1310 / CA1822 Transitional Pending focused follow-up audits
CA1707 DELIBERATE xUnit Method_Scenario_ExpectedResult naming convention requires underscores

Down from 6 suppressions to 5 — CS8602 was retired by this PR.

Test plan

  • dotnet build PinballWizard.slnx /warnaserror clean (0 warnings, 0 errors)
  • dotnet test --no-build → 74/74 pass (was 68; +6 integration tests)
  • dotnet restore --locked-mode succeeds against committed lockfiles
  • No client refs anywhere in committed files (manual sanitization scan)
  • Each of the 5 commits builds and tests green independently
  • CI workflows can't be tested before this PR merges — they'll run on first push to main after merge. README badges will go from "no status" to live status at that point.

Out of scope (deferred)

  • LICENSE file — explicit user decision pending. README still says "reach out before reusing."
  • Branch protection on main — needs to be enabled via repo settings AFTER this PR merges (the new CI check needs to run on main once before it can be set as a required check). Will surface as a separate ask.
  • LoggerMessage.Define migration — deliberate non-goal; suppressed via <NoWarn> with documented rationale.
  • Code coverage threshold gate — coverage is collected and uploaded as artifact; threshold gating can be added once we have a baseline.

…rail

Lift project-wide MSBuild settings into Directory.Build.props so the
quality bar is enforced uniformly: TreatWarningsAsErrors, EnableNETAnalyzers
with latest-recommended, Deterministic=true, ContinuousIntegrationBuild
on CI, RestorePackagesWithLockFile, source link via Microsoft.SourceLink
.GitHub. Add packages.lock.json files for both projects so package versions
can no longer drift silently across restores.

Pin SDK via global.json (10.0.100, rollForward latestFeature) so local
and CI use the same toolchain. Add .editorconfig for consistent formatting
and .gitattributes for line-ending normalization.

CI workflow runs build (with /warnaserror) and tests with coverage on
every PR to main. Sanitization workflow greps every committed file for
forbidden tokens (prior client refs, personal email patterns, common
credential signatures) and fails the workflow on any hit — last-line
defense if a future doc edit slips a leak past human review.

NoWarn list is documented inline with WHY for each suppression. CA1848/
CA1873 are deliberate (LoggerMessage perf delegates would be overkill at
our log volume); CA1310/CA1822 are transitional pending follow-up audits;
CS8602 is transitional pending a one-site source fix on this branch;
CA1707 is permanent (xUnit naming convention).

quality-bar.md is the durable PR checklist authors should read before
opening a PR. Includes the Git Bash /warnaserror MSYS gotcha.
Two complementary defenses for a public repo:

Dependabot (v2 syntax) covers nuget at /, github-actions at /, runs
weekly Monday 06:00 UTC, opens at most 5 PRs per ecosystem, groups
security updates separately. Commit-message prefix follows the team's
conventional format as closely as Dependabot allows.

CodeQL (security-and-quality query set) on push to main, every PR to
main, and weekly. Restores in --locked-mode against the lock files
added in the build-hardening commit, then builds Release. Permissions
scoped to the minimum (security-events:write, actions:read, contents:read).

Note: a structured-logging audit of the codebase was performed as part
of this work and found no offending sites — all 27 _logger.Log* calls
already use proper message templates with PascalCase placeholders. No
source changes required.
…tion

Add IntegrationTests.cs that exercises the full host-builder graph the
way Program.cs.CreateHost does. Six tests:

  Host_BuildsWithoutErrors
  Host_AllSourceScrapersAreRegistered
  Host_CatalogBuilderAndDependenciesResolve
  Host_HttpClientForFileDownloader_HasResiliencePipeline
  Host_HttpClientForManualsScraper_HasResiliencePipeline
  Host_BuildCatalogAsync_ProducesValidCatalogJson

The resilience-pipeline tests use the behaviour pattern (fake primary
handler returns 503 then 200, assert retry occurred) rather than walking
the message-handler chain, since the resilience handler's concrete type
is internal to the package and matching by type-name string would be
brittle. Behaviour tests fail naturally if retry stops working for any
reason without coupling to package internals.

The BuildCatalogAsync test is a regression guard for the
"preserve LastDownloadedAt on missing-file" fix: it seeds a catalog with
a missing-on-disk file, runs reconciliation, then asserts the JSON is
valid, File is cleared, AND LastDownloadedAt is preserved (so a missing
file is distinguishable from a never-downloaded one).

No new NuGet packages were needed; all required types are transitively
available via the existing project reference.

CONTRIBUTING.md captures local setup, branch naming, commit format,
quality bar pointer, and PR conventions. README.md gains three badges
(CI, CodeQL, .NET version) and a CONTRIBUTING pointer; the badges will
go green once this PR merges and the workflows run on main.

Test count: 68 -> 74. Build clean, tests pass.
Companion to docs/quality-bar.md: the lighter quality-bar is a 30-second
PR checklist; ENGINEERING_STANDARDS.md is the deep reference covering
guiding principles, C#/.NET conventions, project structure, error
handling, logging, configuration, testing, security, observability,
build/CI, dependencies, documentation, performance, and explicit
non-goals.

Sanitized to remove client-name references (replaced with generic
"any other project" / "standalone Azure infrastructure" framing) — the
sanitization guardrail added in the build-hardening commit would have
caught these on first CI run.
GameRecord.Source is GameSourceInfo? (optional), but MergeGameRecord was
unconditionally writing existing.Source.ScrapedAt — caught by CS8602 once
the build moved to latest-recommended analyzers, and previously
suppressed at project level pending a real fix.

Prefer the freshly-scraped Source when the incoming GameRecord has one
(it already carries the current ScrapedAt baked in by the scraper);
otherwise stamp the existing Source if non-null; otherwise leave null
rather than synthesize a value with no truthful ScrapedFrom URL.

Removes the CS8602 entry from project NoWarn — a band-aid replaced by a
real fix at the site.
@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread tests/PinballWizard.Scraper.Tests/IntegrationTests.cs Dismissed
Comment on lines +30 to +32
_tempDir = Path.Combine(
Path.GetTempPath(),
"pinballwizard-integration-" + Guid.NewGuid().ToString("N"));
Comment on lines +43 to +46
catch
{
// best-effort cleanup
}
@jkeeley2073 jkeeley2073 merged commit 639dc2d into main May 2, 2026
4 checks passed
jkeeley2073 added a commit that referenced this pull request May 8, 2026
Replaces the README placeholder ("No license file yet; reach out before
reusing.") with an actual MIT license file. MIT is the most permissive
common option and matches typical portfolio-project convention; explicit
licensing also unblocks any future code-reuse questions.

Copyright holder is the GitHub org owner: Early Bird Solutions LLC.
README's License section now points to the LICENSE file.

A license-status badge can be added alongside the CI/CodeQL/.NET badges
in a small followup once PR #4 merges (PR #4 currently owns the badge
area; deferred to avoid a merge-conflict-prone change here).
jkeeley2073 added a commit that referenced this pull request May 8, 2026
Replaces the README placeholder ("No license file yet; reach out before
reusing.") with an actual MIT license file. MIT is the most permissive
common option and matches typical portfolio-project convention; explicit
licensing also unblocks any future code-reuse questions.

Copyright holder is the GitHub org owner: Early Bird Solutions LLC.
README's License section now points to the LICENSE file.

A license-status badge can be added alongside the CI/CodeQL/.NET badges
in a small followup once PR #4 merges (PR #4 currently owns the badge
area; deferred to avoid a merge-conflict-prone change here).
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