WSL UI was already on the Microsoft Store. Job done, right? Not quite. I noticed the app wasn't showing up on any of the winget package search websites. Turns out, the Microsoft Store and the winget community repository are completely separate distribution channels.
Two Sources, One Package Manager
winget pulls packages from two sources:
msstore— The Microsoft Store. Apps submitted there appear when you search with--source msstore.winget— The community repository at microsoft/winget-pkgs. This is the default source and what most winget websites and tools index.
Publishing to the Store doesn't automatically list your app in the community repo. If someone runs winget search "WSL UI" without specifying a source, they won't find it unless you've submitted a manifest to winget-pkgs.
The Manifest Format
A winget package manifest consists of three YAML files, stored in a specific directory structure:
manifests/
o/
OctasoftLtd/
WSLUI/
0.16.2/
OctasoftLtd.WSLUI.yaml
OctasoftLtd.WSLUI.installer.yaml
OctasoftLtd.WSLUI.locale.en-US.yamlThe directory path follows the pattern: first letter of publisher / publisher / app name / version.
Version Manifest
The simplest file. It just declares what exists:
# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json
PackageIdentifier: OctasoftLtd.WSLUI
PackageVersion: 0.16.2
DefaultLocale: en-US
ManifestType: version
ManifestVersion: 1.9.0Installer Manifest
This tells winget where to download your installers and how to verify them:
# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json
PackageIdentifier: OctasoftLtd.WSLUI
PackageVersion: 0.16.2
InstallerType: nullsoft
UpgradeBehavior: uninstallPrevious
ReleaseDate: 2026-02-09
Installers:
- Architecture: x64
InstallerUrl: https://github.com/octasoft-ltd/wsl-ui/releases/download/v0.16.2/WSL.UI_0.16.2_x64-setup.exe
InstallerSha256: 2B435C6A9657F13336ED57BE5BB1D9ECE56A0F565469E0F1D20018173971E984
- Architecture: arm64
InstallerUrl: https://github.com/octasoft-ltd/wsl-ui/releases/download/v0.16.2/WSL.UI_0.16.2_arm64-setup.exe
InstallerSha256: 13D09805DEBE88B51170C70C1281A2B5B4EF7F726866D16AF930635138C0C9AC
ManifestType: installer
ManifestVersion: 1.9.0Key details for Tauri apps:
- InstallerType: nullsoft — Tauri produces NSIS installers, which winget calls
nullsoft - SHA256 hashes — Must be uppercase hex. Compute with
sha256sumorGet-FileHash - Multiple architectures — List each as a separate entry under
Installers
I found the right format by looking at Clash Verge Rev, another Tauri app already in winget-pkgs.
Default Locale Manifest
Package metadata for the default language:
# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json
PackageIdentifier: OctasoftLtd.WSLUI
PackageVersion: 0.16.2
PackageLocale: en-US
Publisher: Octasoft Ltd
PublisherUrl: https://github.com/octasoft-ltd
PackageName: WSL UI
PackageUrl: https://github.com/octasoft-ltd/wsl-ui
License: BUSL-1.1
LicenseUrl: https://github.com/octasoft-ltd/wsl-ui/blob/main/LICENSE
ShortDescription: A lightweight desktop application to manage Windows Subsystem for Linux (WSL) distributions.
Tags:
- developer-tools
- linux
- virtualization
- windows-subsystem-for-linux
- wsl
- wsl2
ReleaseNotesUrl: https://github.com/octasoft-ltd/wsl-ui/releases/tag/v0.16.2
ManifestType: defaultLocale
ManifestVersion: 1.9.0Submitting the PR
The process is straightforward: fork microsoft/winget-pkgs, add your manifest files, and open a PR.
Fork and Create the Files
You can do this manually through the GitHub UI, or use the gh CLI. I used the API approach to avoid cloning the massive repo (it has hundreds of thousands of files):
# Fork the repo
gh repo fork microsoft/winget-pkgs --clone=false
# Create a branch via the API
gh api repos/your-account/winget-pkgs/git/refs \
-X POST \
-f ref="refs/heads/OctasoftLtd.WSLUI-0.16.2" \
-f sha="$(gh api repos/your-account/winget-pkgs/git/ref/heads/master -q '.object.sha')"Then push the manifest files and open a PR. The conventional PR title format is:
New package: OctasoftLtd.WSLUI version 0.16.2
For subsequent versions, use:
Update: OctasoftLtd.WSLUI version 0.17.0
The CLA Requirement
First-time contributors to winget-pkgs must sign Microsoft's Contributor License Agreement. A bot will comment on your PR asking you to agree. Reply with:
@microsoft-github-policy-service agree company="Your Company Name"
Or without the company parameter if you're contributing as an individual:
@microsoft-github-policy-service agree
This is a one-time requirement. Future PRs won't need it.
Validation Pipeline
After the CLA is signed, Microsoft's automated validation pipeline runs. It checks:
- Schema compliance (correct YAML structure, required fields)
- Installer URLs are accessible
- SHA256 hashes match the actual downloads
- No duplicate package identifiers
- Manifest version consistency
If validation passes, a moderator reviews and merges. First submissions get extra scrutiny. Our PR was #340204.
Automating Future Releases
Submitting a manual PR for every release would get old fast. Microsoft provides wingetcreate — a CLI tool that generates and submits manifest updates automatically.
The CI/CD Job
I added a publish-winget job to the existing release-please pipeline. It runs after both architecture builds (x64 and arm64) complete:
publish-winget:
needs: [release-please, build-release]
if: ${{ needs.release-please.outputs.release_created }}
runs-on: windows-latest
steps:
- name: Publish to winget
shell: pwsh
run: |
# Install wingetcreate
Invoke-WebRequest -Uri https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
# Extract version from tag
$tag = "${{ needs.release-please.outputs.tag_name }}"
$version = $tag -replace '^v', ''
# Update manifest and submit PR to microsoft/winget-pkgs
.\wingetcreate.exe update OctasoftLtd.WSLUI `
--version $version `
--urls `
"https://github.com/octasoft-ltd/wsl-ui/releases/download/${tag}/WSL.UI_${version}_x64-setup.exe" `
"https://github.com/octasoft-ltd/wsl-ui/releases/download/${tag}/WSL.UI_${version}_arm64-setup.exe" `
--submit `
--token "${{ secrets.WINGET_CREATE_PAT }}"How It Works
The wingetcreate update command:
- Downloads the existing manifest from winget-pkgs for
OctasoftLtd.WSLUI - Downloads both installer URLs you provide
- Computes SHA256 hashes automatically
- Updates the version, URLs, and hashes in the manifest YAML
- With
--submit, forks winget-pkgs (if needed), pushes the updated manifest, and opens a PR
The --token flag provides the GitHub PAT that authorises the fork and PR creation.
Creating the PAT
The default GITHUB_TOKEN in GitHub Actions only has access to your own repository. Since wingetcreate needs to create PRs against microsoft/winget-pkgs, it needs a Personal Access Token.
- Go to GitHub Settings > Developer settings > Personal access tokens
- Create a classic token with the
public_reposcope (this is the minimum required — it allows creating forks and PRs on public repos) - Add it as a repository secret in your repo: Settings > Secrets and variables > Actions > New repository secret
- Name it
WINGET_CREATE_PAT
Fine-grained tokens can also work, but classic tokens with public_repo are the simplest option and what Microsoft's documentation recommends.
The Full Release Flow
With this in place, here's what happens when a new version of WSL UI is released:
- Conventional commit lands on
main - Release-please creates a version bump PR
- PR merges, release-please creates a GitHub Release with the new tag
- build-release runs for both x64 and arm64 — builds Tauri, creates NSIS/MSI/MSIX/portable installers, uploads to the release
- publish-winget waits for both builds, then runs
wingetcreate updatewhich submits a manifest PR to winget-pkgs - Microsoft's validation bot checks the manifest and a moderator merges it
- The app becomes installable via
winget install OctasoftLtd.WSLUI
The whole pipeline is hands-off after the initial commit.
Installer URL Pattern
One thing to get right: the installer URLs must be predictable from the version number. For WSL UI, the Tauri build produces NSIS installers with this naming convention:
WSL.UI_{version}_{arch}-setup.exe
So the URL template is:
https://github.com/octasoft-ltd/wsl-ui/releases/download/v{version}/WSL.UI_{version}_{arch}-setup.exe
This is determined by the productName in tauri.conf.json and the Tauri build configuration. If your naming is different, adjust the URL pattern in the CI job accordingly.
Tips for Other Tauri Developers
- Use
nullsoftas InstallerType — That's what winget calls NSIS installers - Support multiple architectures — Tauri can build for both x64 and arm64 on Windows. List both in the manifest
- Reference existing Tauri apps — Search winget-pkgs for apps using
InstallerType: nullsoftto find format examples - Don't clone winget-pkgs — The repo is enormous. Use the GitHub API or
wingetcreateinstead - Schema version 1.9.0 — As of early 2026, this is the latest manifest schema version
- Hashes must be uppercase — winget expects uppercase hex in
InstallerSha256
Try It Yourself
Once the winget-pkgs PR is merged, WSL UI will be available through three channels:
- winget:
winget install OctasoftLtd.WSLUI - Microsoft Store: Search for "WSL UI" or visit the store listing
- Direct Download: Official Website
This is Part 7 of a series on building WSL UI, a desktop app for managing WSL distributions on Windows.