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
6 changes: 4 additions & 2 deletions .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ jobs:
ref_name: ${{ github.ref_name }}

# The same gate the PR runs, on the branch tip, running only when a publish will happen. Still this repo's
# own validate-task.yml rather than the hub's: the hub's unit-test step is the VSTest invocation that
# Microsoft.Testing.Platform rejects on the .NET 10 SDK (ptr727/ProjectTemplate#1088).
# own validate-task.yml rather than the hub's. The unit-test step matches the hub's now that
# ptr727/ProjectTemplate#1107 migrated it, but the hub's task declares no ref input and checks out the
# caller's default ref, where ref: github.sha below is what makes the publish gate validate the exact commit
# being published, per D4.6. Adopting it as-is would validate a different tree.
validate:
name: Validate job
needs: [plan]
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ jobs:

# `!github.event.deleted` skips a branch-deletion push (github.sha is all-zeros, so checkout/build fail).
# The same unit-test + lint gate the publisher runs, so the PR gate and the publish gate are identical.
# Still this repo's own validate-task.yml rather than the hub's: the hub's unit-test step is the VSTest
# invocation that Microsoft.Testing.Platform rejects on the .NET 10 SDK (ptr727/ProjectTemplate#1088).
# Still this repo's own validate-task.yml rather than the hub's. The unit-test step matches the hub's now,
# but the hub's task declares no ref input, which publish-release.yml's own validate job needs (see there).
validate:
name: Validate job
if: ${{ !github.event.deleted }}
Expand Down
16 changes: 13 additions & 3 deletions .github/workflows/validate-task.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,20 @@ jobs:

# Builds with TreatWarningsAsErrors, so analyzer and code-style warnings fail here.
# global.json opts dotnet test into Microsoft.Testing.Platform (MTP), the .NET 10 SDK's replacement for
# the VSTest runner; --coverlet drives coverlet.MTP (coverlet.collector's VSTest data collector is silently
# ignored under MTP) to emit Cobertura XML into ./coverage/.
# the VSTest runner, and Microsoft.Testing.Extensions.CodeCoverage emits the Cobertura XML.
# --coverage-output stays unset, since pinning one filename gives every test project in the solution the
# same path and the last to finish overwrites the rest. The default <guid>.cobertura.xml it writes instead
# is a name codecov-cli's own file finder does not match, its patterns being *coverage*.* and an exact
# cobertura.xml, so each report is prefixed rather than renamed, keeping the guid that makes it unique.
# This step is byte-identical to the hub validator's own, so the two gates cannot drift on coverage.
- name: Run unit tests step
run: dotnet test --coverlet --coverlet-output-format cobertura --results-directory ./coverage
run: |
set -Eeuo pipefail
dotnet test --coverage --coverage-output-format cobertura --results-directory ./coverage
for report in ./coverage/*.cobertura.xml; do
[ -e "$report" ] || continue
mv "$report" "./coverage/coverage-$(basename "$report")"
done

# Report-only: fail_ci_if_error is false so a Codecov hiccup or an absent token never fails the gate.
- name: Upload coverage to Codecov step
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
.artifacts
.codex
coverage/
# MTP writes <guid>.cobertura.xml, into ./TestResults when a run names no --results-directory.
*.cobertura.xml
TestResults/
.DS_Store

*.log
Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ Two entry-point workflows, whose build and publish jobs are thin callers of the

- **test-pull-request.yml**: CI on every push. `validate` (this repo's own `validate-task.yml`, unit tests plus the full lint set) and `smoke-build` (the hub's `build-release-task.yml` with `smoke: true`, which builds both targets and hard-disables every publish path) feed the `Check pull request workflow status` aggregator, whose name is the ruleset-bound required check.
- **publish-release.yml**: the sole publisher (weekly `schedule` plus `workflow_dispatch`). `plan` (the hub's `publish-plan-task.yml`) makes the release-gate decision once, `validate` re-runs the gate on the branch tip, `publish` (the hub's `build-release-task.yml`) versions once and builds the executable 7z and the multi-arch Docker image before cutting the GitHub release, and `publish-docker-readme` (the hub's `publish-docker-readme-task.yml`) pushes the Docker Hub overview on `main`.
- **validate-task.yml** is the one task still carried here rather than reached at the hub, because the hub's own unit-test step is the VSTest invocation that Microsoft.Testing.Platform rejects on the .NET 10 SDK (ptr727/ProjectTemplate#1088). It converges once that lands.
- **validate-task.yml** is the one task still carried here rather than reached at the hub. Its unit-test step now matches the hub's, which ptr727/ProjectTemplate#1107 migrated to Microsoft.Testing.Platform, so that is no longer the blocker. The hub's task declares no `ref` input and checks out the caller's default ref, while the `validate` job in `publish-release.yml` passes `github.sha` so the publish gate validates the exact commit being published (D4.6), so adopting it as-is would validate a different tree.
- The executable and Docker builds run the hub's default hooks rather than repo-specific ones: `dotnet_publish_project` names `./PlexCleaner/PlexCleaner.csproj`, which the default publishes across the 7-runtime matrix and archives as `PlexCleaner.7z`, and `docker_image` names `ptr727/plexcleaner`, which the default builds from `./Docker/Dockerfile` for `linux/amd64,linux/arm64` on a `main` publish.
- Version info: `version.json` with Nerdbank.GitVersioning format. The hub's `get-version-task.yml`, reached from inside `build-release-task.yml`, surfaces `SemVer2`, the assembly versions, and `GitCommitId` (used to pin the release `target_commitish`).
- Branches: `main` (stable releases, `latest`), `develop` (pre-releases, `develop`).
Expand Down
17 changes: 14 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@
<ItemGroup>
<PackageVersion Include="AwesomeAssertions" Version="9.6.0" />
<PackageVersion Include="CliWrap" Version="3.10.5" />
<!-- coverlet.MTP replaces coverlet.collector: the VSTest data collector is silently ignored
under Microsoft.Testing.Platform (MTP0001), so coverage needs the MTP-native extension. -->
<PackageVersion Include="coverlet.MTP" Version="10.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.400" />
<!-- The MTP-native coverage extension WORKFLOW.md D1.2 requires of an MTP-based test project.
It replaces coverlet.MTP here, the interim choice made before ptr727/ProjectTemplate#1088
resolved the fleet on this package. The baseline it displaces further back is the VSTest
coverlet.collector, whose data collector MTP ignores without failing, which is the wording
D1.2 itself uses.
The 18.9.0 floor is load-bearing rather than cautionary, and it takes two reasons rather
than one. Below 18.1.0 the extension is built against Microsoft.Testing.Platform 1.x, so an
18.0.x resolution throws a TypeLoadException against the 2.x platform xunit.v3 4.0.0
carries, runs zero tests, and still writes a well-formed Cobertura file reporting full
coverage, leaving only the non-zero exit to say the run reported nothing. 18.9.0 is then
the first release on Microsoft.Testing.Platform 2.3.x, where every test project writes into
the one shared results directory the invocation names rather than resolving that relative
path per project. Do not lower the floor to 18.1.0 on the first reason alone. -->
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.10.0" />
<PackageVersion Include="NEbml" Version="1.1.0.5" />
<PackageVersion Include="ptr727.LanguageTags" Version="1.5.90" />
<PackageVersion Include="ptr727.Utilities" Version="4.0.38" />
Expand Down
18 changes: 10 additions & 8 deletions PlexCleanerTests/PlexCleanerTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!-- coverlet.MTP is a native Microsoft.Testing.Platform extension, not a build-time-only tool
like the VSTest coverlet.collector was: its self-registration codegen needs the package on
this project's own compile-time closure, so IncludeAssets must not exclude "compile" (the
collector's restricted list omitted it, which is fine for a VSTest data collector but
breaks a compiled-against library). PrivateAssets is unrelated to that and stays unset,
since it only controls exposure to downstream consumers, which this test project has none
of. xunit.runner.visualstudio is gone with the VSTest runner it adapted. -->
<PackageReference Include="coverlet.MTP" />
<!-- Microsoft.Testing.Extensions.CodeCoverage replaces coverlet.MTP, per WORKFLOW.md D1.2.
Like it, and unlike the VSTest coverlet.collector both displace, it is a native
Microsoft.Testing.Platform extension rather than a build-time-only tool: its
self-registration codegen needs the package on this project's own compile-time closure, so
IncludeAssets must not exclude "compile" (the collector's restricted list omitted it, which
is fine for a VSTest data collector but breaks a compiled-against library). A build failure
is what enforces that, since the generated SelfRegisteredExtensions.cs calls into the
package directly. PrivateAssets is unrelated and stays unset, since it only controls
exposure to downstream consumers, which this test project has none of. -->
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PlexCleaner\PlexCleaner.csproj" />
Expand Down
32 changes: 25 additions & 7 deletions WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ Dependabot pull requests merge themselves once their checks pass.
The **build, publish, plan, merge-bot and Docker-Hub-overview jobs are thin callers of hub-hosted reusable
tasks** in `ptr727/ProjectTemplate`, reached by a commit-SHA `uses:` pin that Dependabot bumps, per that
repo's `docs/reusable-workflows.md`. This repo carries no copy of them. The one task still carried here is
`validate-task.yml`, because the hub's own unit-test step is the VSTest invocation that
Microsoft.Testing.Platform rejects on the .NET 10 SDK (ptr727/ProjectTemplate#1088); it converges once that
lands.
`validate-task.yml`. Its unit-test step is now byte-identical to the hub's, which
ptr727/ProjectTemplate#1107 migrated to Microsoft.Testing.Platform, so that is no longer what blocks
adoption. What blocks it is that the hub's task declares no `ref` input and checks out the caller's default
ref, where the `validate` job in `publish-release.yml` passes `github.sha` so the publish gate validates the
exact commit being published, which is D4.6. Adopting the hub task without a `ref` input there would
silently validate a different tree.

### Glossary

Expand Down Expand Up @@ -333,8 +336,20 @@ Each is a **MUST**, stated as input -> output plus the failure it prevents.
- **D1.2 Unit tests always run.** Output: `validate-task`'s `unit-test` job runs `dotnet test` (build with
`TreatWarningsAsErrors`, so analyzer/style warnings fail here). [`global.json`](./global.json) opts the run
into Microsoft.Testing.Platform, which the .NET 10 SDK requires of a test project carrying
`Microsoft.Testing.Platform.MSBuild`, and `--coverlet --coverlet-output-format cobertura` drives
`coverlet.MTP` to emit the Cobertura XML the Codecov upload reads.
`Microsoft.Testing.Platform.MSBuild`, and `--coverage --coverage-output-format cobertura` drives
`Microsoft.Testing.Extensions.CodeCoverage` to emit the Cobertura XML the Codecov upload reads. Two details
of that invocation are load-bearing and neither fails the job on its own, so both are asserted in 5A.
`--coverage-output` stays unset, because pinning one filename gives every test project in the solution the
same path and the last to finish overwrites the rest. The `<guid>.cobertura.xml` default it writes instead is
a name `codecov-cli`'s own file finder does not match (its patterns are `*coverage*.*` and an exact
`cobertura.xml`), so the step prefixes each report to `coverage-<guid>.cobertura.xml`, keeping the guid that
makes it unique. The extension is pinned at or above **18.9.0**, for two reasons rather than one. Below
18.1.0 it is built against Microsoft.Testing.Platform 1.x, so an 18.0.x resolution throws a
`TypeLoadException` against the 2.x platform xunit.v3 4.0.0 carries, runs zero tests, and still writes a
well-formed Cobertura file reporting full coverage, leaving only the non-zero exit to say the run reported
nothing. 18.9.0 is then the first release on Microsoft.Testing.Platform 2.3.x, where every test project
writes into the one shared `--results-directory` the invocation names rather than resolving that relative
path per project, which is what the rename loop's glob depends on.
- **D1.3 Lint enforces the editor checks in CI.** Output: `validate-task`'s `lint` job runs CSharpier check,
`dotnet format style --verify-no-changes`, `markdownlint-cli2`, `cspell` on the user-facing docs (README,
HISTORY), `ruff` and `mypy` over the `RegressionTests` Python tooling, `actionlint` (which shellchecks every
Expand Down Expand Up @@ -507,8 +522,11 @@ Read the workflow files plus `version.json` and assert the fact behind each appl
exists under `.github/workflows/`.
- **D1:** CI runs on `push` with no paths filter; `validate` + `smoke-build` (both targets, `smoke: true`)
run; every build `upload-artifact` is gated `!smoke`; `global.json` declares
`test.runner = Microsoft.Testing.Platform` and the unit-test step passes `--coverlet
--coverlet-output-format cobertura`; `lint` runs CSharpier, `dotnet format style`, markdownlint, cspell on
`test.runner = Microsoft.Testing.Platform`, the unit-test step passes `--coverage
--coverage-output-format cobertura` with no `--coverage-output`, and prefixes each report to
`coverage-<guid>.cobertura.xml` before the upload reads the directory;
`Directory.Packages.props` pins `Microsoft.Testing.Extensions.CodeCoverage` at 18.9.0 or above;
`lint` runs CSharpier, `dotnet format style`, markdownlint, cspell on
README/HISTORY, ruff, mypy, actionlint, editorconfig-checker; the aggregator `needs:` both and blocks on
non-success.
- **D2:** the hub task's `validate-release` job runs before the build jobs and they `needs:` it; it checks
Expand Down