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
29 changes: 0 additions & 29 deletions .github/workflows/ci-linux.ym_

This file was deleted.

36 changes: 0 additions & 36 deletions .github/workflows/ci-windows.ym_

This file was deleted.

358 changes: 327 additions & 31 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Mammoth.LiteMapper Implementation Plan

## Issue #17: release quality and test discovery

- Scope: harden the canonical release workflow and helper for bare stable/prerelease SemVer tags, exact six-artifact handoff, checksums/provenance, package/API/consumer/AOT validation, explicit test discovery, analyzer-only generator packaging, and synchronized release documentation.
- Workflow handoff: `.github/workflows/ci.yml` performs one pack/validate/upload phase, a checksum/provenance verification phase, and an approval-gated publish phase. Dry runs execute the validation and artifact handoff but never invoke NuGet push. Artifact transfer uses the Node 24-compatible `actions/upload-artifact@v6` and `actions/download-artifact@v7`. `publish-nuget.ps1` enforces the same exact six-artifact and no-duplicate-suppression contract for local releases.
- Validation target: run `dotnet test tests\Mammoth.LiteMapper.Packaging.Tests\Mammoth.LiteMapper.Packaging.Tests.csproj --no-restore --filter ReleaseQualityContractTests`, the Milestone 14 packaging filter, and the complete solution test command.
- Current evidence (2026-09-17): ReleaseQualityContractTests 17/17; Milestone 14 24 passed with one expected Windows Native AOT prerequisite skip; the previous full solution run passed 649 with the same one expected skip; all six local packages packed successfully and passed package-content checks; Basic, Collections, and ASP.NET Core smoke samples passed sequentially. The Windows package consumer restore and no-restore trimmed/AOT publishes use an explicit `win-x64` runtime, and the Native AOT executable is discovered recursively instead of assuming a fixed output path. Artifact transfer now uses Node 24-compatible action releases. `dotnet tool restore`, package-level `apicompat`, and the BenchmarkDotNet generated-project smoke were blocked by local NuGet SSL access (zero benchmark cases executed); the first remote consumer validation exposed NETSDK1047, and the follow-up exposed a hard-coded AOT executable path; both are fixed in the pending PR rerun.

Current execution state (2026-09-16): W01 through W10 and the approved Milestone 13 flat-mapping optimization remain complete. GitHub issues #1 through #7 are implemented, documented, merged, and closed. Issue #7 was merged into `develop` via PR [#10](https://github.com/PrimordialCode/Mammoth.LiteMapper/pull/10) at `a8ac8bd`; AgentStack is `done` and its claim is released. Existing source-policy behavior already satisfies the requested mapper, method, assembly, severity, and editorconfig boundaries; this scope added adoption evidence and synchronized guidance without a public API or semantic change. Remote CI remains unexecuted.

PENDING-0006 and PENDING-0007 Option A are approved, incorporated, implemented, and regression-tested. All milestone and dated checkpoint sections below are historical planning and revision-specific evidence; their pending-work statements are superseded by the current execution state above.
Expand Down
7 changes: 7 additions & 0 deletions STATUS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Mammoth.LiteMapper Status

## GitHub issue #17 release quality checkpoint (2026-09-17)

- Scope: canonical CI/release hardening for bare stable/prerelease SemVer tags, exact six-artifact handoff, package/API/consumer/AOT validation, explicit MSTest discovery, analyzer-only generator packaging, and release documentation.
- Workflow seam: `.github/workflows/ci.yml` validates the exact tag/GitVersion version before one pack, verifies checksums/provenance and package contents, uploads the validated packages with `actions/upload-artifact@v6`, and publishes only from a protected tag or approved production dispatch after `actions/download-artifact@v7`. Dry-run follows the same validation and handoff path and never calls `dotnet nuget push`.
- Local helper seam: `publish-nuget.ps1` accepts the same stable/prerelease versioned artifacts, requires exactly three .nupkg plus three .snupkg, and does not suppress duplicate push failures.
- Current evidence (2026-09-17): ReleaseQualityContractTests 17/17; Milestone 14 24 passed with one expected Windows Native AOT prerequisite skip; the previous full solution run passed 649 with the same one expected skip; six local packages packed and content-checked; Basic, Collections, and ASP.NET Core smoke samples passed sequentially. The Windows package consumer restore and no-restore trimmed/AOT publishes use an explicit `win-x64` runtime after the first remote run exposed NETSDK1047; Native AOT validation now discovers exactly one executable recursively after the follow-up exposed a hard-coded output path. Artifact transfer now uses Node 24-compatible action releases. `dotnet tool restore`, package-level `apicompat`, and the BenchmarkDotNet generated-project smoke were blocked by local NuGet SSL access (zero benchmark cases executed); the corrected remote rerun and NuGet publication are not yet claimed.

Latest completed work: GitHub issue #7 strict source-member completeness was merged into `develop` via PR [#10](https://github.com/PrimordialCode/Mammoth.LiteMapper/pull/10) at `a8ac8bd`, and closed. AgentStack issue #7 is `done` and its claim is released. Issue #6 existing-target update atomicity was previously merged into `develop` via PR [#9](https://github.com/PrimordialCode/Mammoth.LiteMapper/pull/9) and closed. Issue #5 declaration, registration, constructor, and accessibility coverage was also previously merged into `develop` and closed.

- Current scope: issue #7 strict source-member completeness adoption evidence and synchronized documentation is complete; remote CI remains unexecuted.
Expand Down
26 changes: 26 additions & 0 deletions docs/RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Release checklist

The canonical release workflow is .github/workflows/ci.yml. Releases use bare SemVer tags, for example 2.0.0, 2.0.0-alpha.1, or 2.0.1-beta.1.

## Before release

- Confirm the tag exactly matches the GitVersion SemVer value.
- Confirm the tag is protected and the production environment requires the configured human approval.
- Run the workflow manually with dry_run=true to rehearse the complete validation path.

## Validation and artifacts

- Build and test the solution on Linux and Windows.
- Run the pinned Roslyn host matrix.
- Pack the three shipping projects once at the exact tag version.
- Verify exactly one .nupkg and one .snupkg for each package.
- Verify package API compatibility, package contents, clean-consumer mappings, trimming, Native AOT, checksums, and provenance.
- Ensure the Windows consumer restore includes the `win-x64` runtime before no-restore trimmed/AOT publishes.
- Use Node 24-compatible artifact actions: `actions/upload-artifact@v6` and `actions/download-artifact@v7`.
- Retain the uploaded package, checksum, and provenance artifacts.

## Publish

- Publish only from a valid SemVer tag or an explicitly approved production workflow dispatch on main, release/**, or hotfix/**.
- The publish job downloads and verifies the uploaded artifacts, then pushes those exact six files without duplicate suppression.
- A dry run must report the artifacts that would be published and must never call dotnet nuget push or mutate NuGet.
46 changes: 29 additions & 17 deletions publish-nuget.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,32 @@ foreach ($project in $packProjects) {
Assert-NativeSuccess "dotnet pack $project -c Release --no-restore -o $resolvedOutput with GitVersion properties"
}

$packages = Get-ChildItem -Path $resolvedOutput -Filter "Mammoth.LiteMapper*.$version.nupkg" |
Where-Object { $_.Name -notlike '*.symbols.nupkg' } |
Sort-Object Name

if ($packages.Count -ne 3) {
throw "Expected 3 packages for version $version, found $($packages.Count)."
}

foreach ($package in $packages) {
if ($DryRun) {
Write-Host "Dry run: would publish $($package.FullName) to $Source"
continue
}

& dotnet nuget push $package.FullName --source $Source --api-key $ApiKey --skip-duplicate
Assert-NativeSuccess "dotnet nuget push $($package.FullName) --source $Source --api-key *** --skip-duplicate"
}
$packageIds = @(
'Mammoth.LiteMapper.Abstractions',
'Mammoth.LiteMapper.Generator',
'Mammoth.LiteMapper'
)
$expectedArtifacts = foreach ($packageId in $packageIds) {
foreach ($extension in @('nupkg', 'snupkg')) {
"$packageId.$version.$extension"
}
}
$artifacts = @(Get-ChildItem -Path $resolvedOutput -File |
Where-Object { $_.Extension -in '.nupkg', '.snupkg' } |
Sort-Object Name)
$unexpected = @($artifacts | Where-Object { $_.Name -notin $expectedArtifacts })
$missing = @($expectedArtifacts | Where-Object { -not (Test-Path -LiteralPath (Join-Path $resolvedOutput $_)) })
if ($unexpected.Count -ne 0 -or $missing.Count -ne 0 -or $artifacts.Count -ne $expectedArtifacts.Count) {
throw "Expected exactly $($expectedArtifacts.Count) versioned package artifacts for $version; missing: $($missing -join ', '); unexpected: $($unexpected.Name -join ', ')."
}

foreach ($artifactName in $expectedArtifacts) {
$artifactPath = Join-Path $resolvedOutput $artifactName
if ($DryRun) {
Write-Host "Dry run: would publish $artifactPath to $Source"
continue
}

& dotnet nuget push $artifactPath --source $Source --api-key $ApiKey
Assert-NativeSuccess "dotnet nuget push $artifactPath --source $Source --api-key ***"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<Description>Source generator package for Mammoth.LiteMapper.</Description>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<NoWarn>$(NoWarn);RS2008</NoWarn>
Expand All @@ -14,4 +15,10 @@
PackagePath="analyzers/dotnet/cs"
Visible="false" />
</ItemGroup>

<Target Name="ExcludeGeneratorRuntimeAssemblyFromPackage" AfterTargets="BuiltProjectOutputGroup">
<ItemGroup>
<BuiltProjectOutputGroupOutput Remove="@(BuiltProjectOutputGroupOutput)" />
</ItemGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<RoslynTestVersion Condition="'$(RoslynTestVersion)' == ''">4.8.0</RoslynTestVersion>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public void GeneratorPackageContainsAnalyzerAsset()
var entries = archive.Entries.Select(static e => e.FullName).ToArray();

CollectionAssert.Contains(entries, "analyzers/dotnet/cs/Mammoth.LiteMapper.Generator.dll");
Assert.IsFalse(entries.Any(static e => e.StartsWith("lib/", StringComparison.Ordinal) || e.StartsWith("ref/", StringComparison.Ordinal)),
string.Join(Environment.NewLine, entries));
Assert.IsFalse(entries.Any(static e => e.Contains("Microsoft.CodeAnalysis", StringComparison.OrdinalIgnoreCase)),
string.Join(Environment.NewLine, entries));
}

[TestMethod]
Expand Down
Loading