This project uses an automated release pipeline via GitHub Actions. Creating a GitHub Release triggers a workflow that builds the extension, runs tests, creates MSIX packages, submits to the Microsoft Store, and publishes to WinGet.
- Go to Azure Portal → App registrations
- Click New registration
- Name it (e.g., "WeatherExtension CI")
- Note the Application (client) ID and Directory (tenant) ID
- Go to Certificates & secrets → New client secret → note the Value
- In Partner Center → Account Settings → User Management
- Add the application with Manager role
- Seller ID: Partner Center → Account Settings → Organization profile → Identifiers
- Product ID: Partner Center → Apps and Games → select "Weather for Command Palette" → note the App ID from the URL
The MSIX packages are signed using Azure Trusted Signing after building. This requires an Azure service principal with access to the signing account.
- Ensure the Azure Trusted Signing account
bbb-signingand certificate profileBaldBeardedBuilderare configured (shared with WalkThisWay) - Create an Azure service principal with access to the signing account
- Store the service principal credentials as a GitHub secret named
AZURE_CREDSin JSON format:{ "clientId": "<Application (client) ID>", "clientSecret": "<Client secret value>", "subscriptionId": "<Subscription ID>", "tenantId": "<Directory (tenant) ID>" }
- Go to GitHub → Settings → Developer Settings → Personal access tokens → Tokens (classic)
- Click Generate new token (classic)
- Give it a descriptive name (e.g., "WinGet Releaser")
- Select the
public_reposcope - Generate and note the token value
Note: The WinGet package identifier is
BaldBeardedBuilder.WeatherForCmdPal. Before the first automated release, you must manually submit the initial package manifest to microsoft/winget-pkgs usingwingetcreate new. Subsequent releases will be handled automatically.
The Microsoft Store requires a specific publisher identity in the MSIX manifest that differs from the Trusted Signing certificate subject. The release workflow builds two sets of packages:
- Sideload/WinGet — Uses the Trusted Signing publisher, signed with Azure Trusted Signing
- Microsoft Store — Uses the Store publisher identity (
STORE_PUBLISHER_NAMEsecret), unsigned (Microsoft re-signs during Store publication)
To find your Store publisher identity:
- Go to Partner Center → Apps and Games → select your app
- Product management → Product Identity
- Copy the Package/Identity/Publisher value (e.g.,
CN=A8D6094E-...)
Go to the repo Settings → Secrets and variables → Actions → New repository secret. Add:
| Secret Name | Source |
|---|---|
PARTNER_CENTER_TENANT_ID |
Entra ID → Directory (tenant) ID |
PARTNER_CENTER_CLIENT_ID |
Entra ID → Application (client) ID |
PARTNER_CENTER_CLIENT_SECRET |
Entra ID → Client secret value |
PARTNER_CENTER_SELLER_ID |
Partner Center → Seller ID |
STORE_PRODUCT_ID |
Partner Center → Product/App ID |
AZURE_CREDS |
Azure service principal JSON for Trusted Signing |
STORE_PUBLISHER_NAME |
Partner Center → App Identity → Package/Identity/Publisher (e.g., CN=...) |
WINGET_TOKEN |
GitHub PAT with public_repo scope |
- Ensure all changes are merged to
main - Go to GitHub → Releases → Create a new release
- Create a new tag using semantic versioning:
v1.0.1,v1.2.0, etc. - Write release notes (or use auto-generated notes)
- Click Publish release
The workflow automatically:
- Builds x64 and ARM64 architectures
- Runs all tests
- Creates MSIX packages
- Uploads MSIX packages to the release as downloadable artifacts
- Submits to the Microsoft Store
- Submits to WinGet (via PR to microsoft/winget-pkgs)
- Format:
vMAJOR.MINOR.PATCH(e.g.,v1.0.1) - The
vprefix is stripped and.0is appended to create the 4-part version for the MSIX manifest (e.g.,1.0.1.0)
- The Microsoft Store reviews the submission (may take 1–3 business days)
- If approved, the update is available to users via the Store
- The MSIX packages attached to the GitHub Release can be used for sideloading
| Issue | Solution |
|---|---|
| Workflow fails at "Configure Store credentials" | Verify all 5 Partner Center GitHub secrets are set correctly |
| Store submission rejected | Check Partner Center dashboard for validation errors |
| WinGet submission fails | Verify WINGET_TOKEN secret is set and not expired. For the first release, manually submit using wingetcreate new |
| Build fails | The release workflow uses the same build process as CI — check for build errors in the Actions log |
The extension now supports graceful shutdown during uninstall without requiring Command Palette to close. This is implemented via:
- ShutdownCoordinator: Listens on a named pipe (
\\.\pipe\WeatherExtension-Shutdown) for external shutdown signals, with a fallback named event (WeatherExtension-Shutdown) - Program.cs: Uses
WaitHandle.WaitAny()to coordinate between normal Command Palette shutdown and external uninstall signals - Watchdog Timer: A 5-second timeout ensures the process exits cleanly even if disposal hangs
- Windows initiates uninstall
- Uninstall script or tool sends shutdown signal via the named pipe
- Extension process receives signal and gracefully disposes all resources
- Process exits cleanly, allowing Windows to remove the package
- No manual restart of Command Palette required
# Signal graceful shutdown via named pipe
$pipeName = "WeatherExtension-Shutdown"
$pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream(".", $pipeName, "Out")
try {
$pipeClient.Connect(1000)
$sw = New-Object System.IO.StreamWriter($pipeClient)
$sw.WriteLine("SHUTDOWN")
$sw.Flush()
} catch {
Write-Host "Could not signal shutdown: $_"
} finally {
$pipeClient.Close()
}Alternatively, use the named event for simpler but less robust signaling:
# Signal shutdown via named event
[System.Threading.EventWaitHandle]::new($false, "Global", "WeatherExtension-Shutdown").Set()The extension ensures all resources are properly disposed during shutdown:
- Timers are stopped
- HTTP client connections are closed
- Cancellation tokens are signalled
- Event handler subscriptions are unsubscribed
- No dangling handles or resources remain
See ShutdownCoordinator.cs and disposal methods for implementation details.