diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0a1accc..306115b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,6 +4,16 @@ on:
branches: [master]
pull_request:
branches: [master]
+ # Manual: rebuild and re-push the Chocolatey package for an EXISTING release, e.g.
+ # after a moderator asks for a nuspec change. Uses the published release's own
+ # hash, so the EXE is untouched and no new version (or antivirus resubmission)
+ # is needed. Chocolatey accepts a re-push of a version that is still in moderation.
+ workflow_dispatch:
+ inputs:
+ chocolatey_version:
+ description: 'Existing release version to republish to Chocolatey (e.g. 1.123.1)'
+ required: true
+ type: string
# Top-level: read-only. Write privileges granted only on the test job that needs them.
# Scorecard's Token-Permissions check awards full credit for this least-privilege pattern.
@@ -11,6 +21,7 @@ permissions: read-all
jobs:
test:
+ if: github.event_name != 'workflow_dispatch'
permissions:
contents: write # needed for the auto-release step (gh release create)
id-token: write # needed for actions/attest-* (SLSA L3 provenance + SBOM)
@@ -549,7 +560,11 @@ jobs:
$hash = (Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLower()
Push-Location dist/chocolatey
# Stamp the templated nuspec + install script with this version + hash.
- (Get-Content 'rackstack.nuspec' -Raw) -replace '0\.0\.0', "$ver" |
+ # The icon URL is pinned to this release's tag (Chocolatey moderation does not
+ # accept raw.githubusercontent.com icons; a tag-pinned CDN URL is immutable).
+ (Get-Content 'rackstack.nuspec' -Raw) `
+ -replace '0\.0\.0', "$ver" `
+ -replace '@v0\.0\.0/', "@v$ver/" |
Set-Content 'rackstack.nuspec' -Encoding utf8
(Get-Content 'tools/chocolateyinstall.ps1' -Raw) `
-replace '__VERSION__', $ver `
@@ -631,3 +646,63 @@ jobs:
)
$lines -join "`n" | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8
Write-Host "Submission reminder written to the run summary (SHA-256 $sha)."
+
+ # ───────────────────────────────────────────────────────────────────────────
+ # Manual republish of the Chocolatey package for an existing release
+ # (workflow_dispatch only). Rebuilds the .nupkg from the CURRENT dist/chocolatey
+ # templates and the published release's own EXE hash, then pushes it. The EXE
+ # is never rebuilt, so the release hash, cosign signature, and any antivirus
+ # clearance for that hash all stay valid. Chocolatey accepts a re-push of a
+ # version that is still in moderation, which is how a moderator's nuspec
+ # request gets answered without cutting a new version.
+ # ───────────────────────────────────────────────────────────────────────────
+ republish-chocolatey:
+ if: github.event_name == 'workflow_dispatch'
+ permissions:
+ contents: read
+ runs-on: windows-2025
+ timeout-minutes: 10
+ defaults:
+ run:
+ shell: pwsh
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+
+ - name: Republish Chocolatey package
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ CHOCO_API_KEY: ${{ secrets.CHOCO_API_KEY }}
+ run: |
+ $ver = '${{ inputs.chocolatey_version }}'
+ if ($ver -notmatch '^\d+\.\d+\.\d+$') { throw "chocolatey_version '$ver' is not x.y.z" }
+ if ([string]::IsNullOrWhiteSpace($env:CHOCO_API_KEY)) { throw 'CHOCO_API_KEY secret is not set' }
+
+ # The hash comes from the published release, never from a local build.
+ $work = Join-Path $env:RUNNER_TEMP 'release'
+ New-Item -ItemType Directory -Force $work | Out-Null
+ gh release download "v$ver" --pattern 'release-hashes.txt' --dir $work --clobber
+ if ($LASTEXITCODE -ne 0) { throw "release v$ver or its release-hashes.txt not found" }
+ $line = Get-Content (Join-Path $work 'release-hashes.txt') | Where-Object { $_ -match '^\s*([a-f0-9]{64})\s+RackStack\.exe\s*$' } | Select-Object -First 1
+ if (-not $line) { throw 'RackStack.exe hash not present in release-hashes.txt' }
+ $hash = ([regex]::Match($line, '^\s*([a-f0-9]{64})')).Groups[1].Value
+ Write-Host "Release v$ver RackStack.exe SHA-256: $hash"
+
+ Push-Location dist/chocolatey
+ (Get-Content 'rackstack.nuspec' -Raw) `
+ -replace '0\.0\.0', "$ver" `
+ -replace '@v0\.0\.0/', "@v$ver/" |
+ Set-Content 'rackstack.nuspec' -Encoding utf8
+ (Get-Content 'tools/chocolateyinstall.ps1' -Raw) `
+ -replace '__VERSION__', $ver `
+ -replace '__CHECKSUM_SHA256__', $hash |
+ Set-Content 'tools/chocolateyinstall.ps1' -Encoding utf8
+ Get-Content 'rackstack.nuspec' | Select-String 'version>|iconUrl|projectUrl|projectSourceUrl|docsUrl' | ForEach-Object { Write-Host " $($_.Line.Trim())" }
+ choco pack rackstack.nuspec
+ if ($LASTEXITCODE -ne 0) { throw "choco pack exited $LASTEXITCODE" }
+ $nupkg = Get-ChildItem -Filter "rackstack.$ver.nupkg" | Select-Object -First 1
+ if (-not $nupkg) { throw 'choco pack produced no .nupkg for this version' }
+ choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
+ choco push $nupkg.FullName --source https://push.chocolatey.org/
+ if ($LASTEXITCODE -ne 0) { throw "choco push exited $LASTEXITCODE" }
+ Pop-Location
+ Write-Host "Republished rackstack $ver to Chocolatey."
diff --git a/README.md b/README.md
index 269812c..345f7d9 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@
-
+
diff --git a/RackStack.psd1 b/RackStack.psd1
index 8ef7643..88d627c 100644
--- a/RackStack.psd1
+++ b/RackStack.psd1
@@ -29,7 +29,7 @@
Tags = @('RackStack', 'Windows', 'WindowsServer', 'HyperV', 'Sysadmin', 'CLI', 'Automation', 'ServerConfig')
LicenseUri = 'https://github.com/TheAbider/RackStack/blob/master/LICENSE'
ProjectUri = 'https://github.com/TheAbider/RackStack'
- IconUri = 'https://raw.githubusercontent.com/TheAbider/RackStack/master/.github/assets/icon.png'
+ IconUri = 'https://cdn.jsdelivr.net/gh/TheAbider/RackStack@master/.github/assets/icon.png'
ReleaseNotes = 'See https://github.com/TheAbider/RackStack/releases for per-version changelog.'
}
}
diff --git a/dist/chocolatey/rackstack.nuspec b/dist/chocolatey/rackstack.nuspec
index 3550c02..4c8e149 100644
--- a/dist/chocolatey/rackstack.nuspec
+++ b/dist/chocolatey/rackstack.nuspec
@@ -8,12 +8,12 @@
RackStack — Windows Server configuration toolkit
TheAbider
https://github.com/TheAbider/RackStack
- https://raw.githubusercontent.com/TheAbider/RackStack/master/RackStack.ico
+ https://cdn.jsdelivr.net/gh/TheAbider/RackStack@v0.0.0/.github/assets/icon.png
Copyright (c) 2026 TheAbider
https://github.com/TheAbider/RackStack/blob/master/LICENSE
false
- https://github.com/TheAbider/RackStack
- https://github.com/TheAbider/RackStack#readme
+ https://github.com/TheAbider/RackStack/tree/master
+ https://github.com/TheAbider/RackStack/wiki
https://github.com/TheAbider/RackStack/issues
windows-server hyper-v iscsi clustering powershell sysadmin automation msp admin-tools
PowerShell automation toolkit for configuring Windows Server hosts