Skip to content

Latest commit

 

History

History
131 lines (94 loc) · 9.29 KB

File metadata and controls

131 lines (94 loc) · 9.29 KB

Release process

This is the manual runbook for shipping a WinClean release. It is deliberately manual: an automated release.yml workflow is a future improvement and is intentionally deferred for now, so every release goes through the checklist below and the machine gate Invoke-ReleaseCheck.ps1.

Why all channels must agree

WinClean is distributed through several channels, and they must all show the same version at the same time:

  • main holds the source of truth for the code.
  • README badges advertise the current version. The version badge is dynamic (it reads the latest GitHub Release via shields.io), so it follows the release automatically; the version that still needs a manual bump is the flow-diagram heading in each README.
  • GitHub Release is what the one-line bootstrap scripts consume: both get.ps1 and install.ps1 download WinClean.ps1 from the latest GitHub Release and verify it against the published WinClean.ps1.sha256 asset. They are fail-closed: a missing asset or a hash mismatch aborts the run, and there is no fallback to a mutable branch.
  • PSGallery is what the script's built-in update check uses: Test-ScriptUpdate compares the running version against the PowerShell Gallery. An installed copy only learns about a new version if it was published there.
  • CHANGELOG.md is the human record of what changed.

If these drift, users get confused or, worse, run an old version while the README promises a new one. A git push alone does not publish a release: the bootstrap scripts keep serving the previous GitHub Release until you cut a new one.

Where the version lives

The version appears in several places inside WinClean.ps1 (the $script:Version variable, the PSScriptInfo .VERSION, .RELEASENOTES, the .SYNOPSIS, and the NOTES block) and in the docs (the flow diagram heading in both READMEs, and the CHANGELOG entry). The README version badge is dynamic and needs no bump; the test-count lines in CONTRIBUTING and CLAUDE are a separate counter the gate also checks. Line numbers move between releases, so find each spot by content (grep), not by a fixed line number.

You do not verify these by hand. The machine gate does it for you.

The machine gate

pwsh tools/Invoke-ReleaseCheck.ps1

This is fail-closed and checks, at minimum:

  • the version is consistent across every place it appears;
  • there are no em-dashes or en-dashes anywhere in the tracked text (only hyphen-minus is allowed);
  • the documented test counters match the real Pester count;
  • PowerShell syntax parses;
  • PSScriptAnalyzer is clean over exactly the files and severities CI enforces (both call the same tools/Invoke-Lint.ps1, so the gate cannot be greener than CI);
  • Pester runs with no skipped tests (a skipped integration suite is a silent gap);
  • the smoke test passes (a real -ReportOnly run, console box geometry, result JSON);
  • the working tree is clean and pushed.

Optional flags:

  • -IncludeStand also runs one real cleanup on the stand (Invoke-StandTest.ps1 -Mode Full -Source local against the default config). The broader RU + EN and ReportNoCleanup matrix below is run manually.
  • -VerifyPublished is run after the GitHub Release exists: it confirms both assets are present and that the published WinClean.ps1.sha256 matches the released WinClean.ps1.

Cutting the GitHub Release

Compute the hash, write the sidecar file, and create the release with both assets:

$hash = (Get-FileHash .\WinClean.ps1 -Algorithm SHA256).Hash
"$hash  WinClean.ps1" | Out-File "$env:TEMP\WinClean.ps1.sha256" -Encoding ascii -NoNewline

gh release create vX.Y `
  ".\WinClean.ps1#WinClean.ps1" `
  "$env:TEMP\WinClean.ps1.sha256#WinClean.ps1.sha256" `
  --title "WinClean vX.Y" `
  --notes "..."

Both assets are mandatory. A release that publishes WinClean.ps1 without WinClean.ps1.sha256 is refused by get.ps1 (it will not run unverified code elevated). If you have to correct the script inside an already-published release, re-upload with --clobber and recompute the hash so the two stay in sync:

$hash = (Get-FileHash .\WinClean.ps1 -Algorithm SHA256).Hash
"$hash  WinClean.ps1" | Out-File "$env:TEMP\WinClean.ps1.sha256" -Encoding ascii -NoNewline
gh release upload vX.Y ".\WinClean.ps1#WinClean.ps1" "$env:TEMP\WinClean.ps1.sha256#WinClean.ps1.sha256" --clobber

Release notes template

For a tool that runs elevated, predictability is worth more than a long feature list, so every release uses the same headings. Keep them even when a section is short - a reader looking for "did this change what gets deleted?" should find the answer in the same place every time.

## What changed
Grouped as Fixed / Changed / Added, one line each, in user terms rather than internal
task numbers. Say what was wrong, not only what was done.

## Safety notes
Anything that alters what is deleted, what is reported, or how the script decides it is
finished. Say "nothing in this release changes what gets deleted" when that is the case -
its absence is what people check for.

## Verification
Release gate, Pester count, PSScriptAnalyzer, smoke, and the RU/EN stand runs, plus the
end-to-end run through the published one-liner. State what was actually run for THIS
release, not what the process says in general.

## Assets
`WinClean.ps1` and `WinClean.ps1.sha256`. Both are required: the bootstrap scripts verify
the hash fail-closed and refuse to run without it.

## Upgrade
Which of the four installation methods needs an action, and which update themselves.

## Known limitations
Anything deliberately not fixed in this release, with the reason. This section is the one
most worth writing honestly - it is where trust is earned or lost.

Publishing to PSGallery

Publish-PSResource -Path .\WinClean.ps1 -Repository PSGallery -ApiKey $env:PSGALLERY_API_KEY

The API key lives in the PSGALLERY_API_KEY environment variable. Publishing here is what lets already-installed copies discover the update through Test-ScriptUpdate; skipping it leaves PSGallery users behind even though the GitHub one-liners are current.

Publish to PSGallery last. It is the only irreversible step in this runbook: a published version cannot be deleted or replaced, only unlisted, and the same version number can never be re-used. Everything else can be corrected (assets re-uploaded with --clobber, a tag deleted, a commit reverted). So run the post-release verification first and publish to PSGallery only once the GitHub Release is confirmed good.

Stand verification

Every release is validated on real Windows 11 VMs before and after publishing, run manually with tools/proxmox/Invoke-StandTest.ps1:

  • RU (VM 190) and EN (VM 191) (via -ConfigPath), so locale-dependent parsing is exercised in both cultures.
  • Full runs (real cleanup) plus -Mode ReportNoCleanup (a preview with -SkipCleanup, which verifies end-to-end that the whole cleanup group is suppressed and reported as skipped).
  • The nightly matrix additionally runs a quick pass with -Source release, which downloads WinClean.ps1 from the latest release tag (raw.githubusercontent.com/.../<tag>/WinClean.ps1). This catches a broken release with a healthy main; note it exercises the tag's script content, not the release asset download or the get.ps1 one-liner (those are covered by -VerifyPublished and a manual one-liner run). The nightly also has a dead-man heartbeat check: an independent, later cron reports if the matrix never ran, so a silent night is not mistaken for a healthy one.

Checklist

  1. Land all code and doc changes on the release branch; update CHANGELOG.md with the new version section.
  2. Bump the version in every place inside WinClean.ps1 and the docs (verify by grep, not line number).
  3. Run pwsh tools/Invoke-ReleaseCheck.ps1 until green.
  4. Run pwsh tools/Invoke-ReleaseCheck.ps1 -IncludeStand (one Full/local pass), and manually run the RU + EN stand (Invoke-StandTest.ps1 with each -ConfigPath) in Full and -Mode ReportNoCleanup.
  5. Merge to main and push.
  6. Compute the SHA256, write WinClean.ps1.sha256, and gh release create vX.Y with both assets.
  7. Run pwsh tools/Invoke-ReleaseCheck.ps1 -VerifyPublished (confirms both assets exist and the SHA256 matches), then a manual one-liner run (irm https://raw.githubusercontent.com/bivlked/WinClean/main/get.ps1 | iex) to confirm the release asset and the get.ps1 path serve the new version. Run that one-liner on a stand VM, never on your own workstation: it is a real elevated cleanup run, not a preview. (Invoke-StandTest.ps1 -Source release on the stand is a separate check - it runs the tag's raw WinClean.ps1, not the release asset or the one-liner.)
  8. Publish-PSResource to PSGallery - last, because it is the one step that cannot be undone.
  9. Confirm README badges, the GitHub Release, PSGallery and the CHANGELOG all show the same version.
  10. Clean up. Delete the release branch once it is merged (locally and on the remote) and resolve any open dependency PRs, so that main is the only branch left. The repository has "automatically delete head branches" enabled, so a branch merged through a PR removes itself; a branch merged any other way does not. The tag and the release keep the history, so a merged branch carries no information that is not already in main.