Skip to main content

Winget

Publish to the Windows Package Manager (winget)

Anodizer generates WinGet YAML manifests and submits pull requests to the winget-pkgs community repository (or your own fork) via the GitHub API. WinGet is the official Windows Package Manager, allowing users to install your tool with winget install Publisher.AppName.

Minimal config

crates:
  - name: myapp
    publish:
      winget:
        repository:
          owner: myorg
          name: winget-pkgs
        publisher: "My Organization"
        license: MIT
        package_identifier: "MyOrg.MyApp"

How it works

  1. Anodizer collects your Windows .zip archive artifacts (or portable binary artifacts).
  2. It generates three YAML manifest files following the WinGet 1.12.0 schema.
  3. The manifests are committed to a branch in your fork of the winget-pkgs repository.
  4. A pull request is submitted against microsoft/winget-pkgs (or a custom upstream).

Package identifier format

The package_identifier must follow the WinGet convention: 2 to 8 dot-separated segments, where each segment contains no whitespace or special characters (\ / : * ? " < > |).

Examples of valid identifiers:

  • MyOrg.MyApp
  • Publisher.Category.AppName

If package_identifier is not set, Anodizer auto-generates it as Publisher.Name (with spaces stripped from the publisher name).

WinGet config fields

FieldTypeDefaultDescription
namestringcrate nameOverride the package name
package_namestringsame as nameDisplay name shown in WinGet gallery
package_identifierstringPublisher.NameWinGet package identifier (e.g. Publisher.AppName)
publisherstringrepo ownerPublisher name (required)
publisher_urlstringnonePublisher homepage URL
publisher_support_urlstringnonePublisher support URL
privacy_urlstringnonePrivacy policy URL
authorstringnoneAuthor name
copyrightstringnoneCopyright notice
copyright_urlstringnoneCopyright URL
licensestringrequiredSPDX license identifier (e.g. MIT)
license_urlstringnoneLicense URL
short_descriptionstringdescription or crate nameShort description (max 256 chars)
descriptionstringnoneFull package description
homepagestringnoneProject homepage URL
url_templatestringrelease URLCustom download URL template
idslist of stringsallBuild IDs filter: only include matching artifacts
skip_uploadbool or stringfalseSkip publishing (true always skips, "auto" skips for prereleases)
commit_msg_templatestringNew version: {{ PackageIdentifier }} {{ Version }}Custom commit message template
pathstringauto-generatedCustom manifest path inside the repo
release_notesstringnoneRelease notes for this version
release_notes_urlstringnoneURL to full release notes
installation_notesstringnonePost-install notes shown to the user
tagslist of stringsnoneTags for package discovery (lowercased, spaces replaced with hyphens)
dependencieslist of objectsnonePackage dependencies (see below)
product_codestringnoneProduct code for Add/Remove Programs
usestringarchiveArtifact type: archive, msi, or nsis
amd64_variantstringv1amd64 microarchitecture variant filter (v1, v2, v3, v4)

Repository config

You can configure the target repository with either the legacy manifests_repo or the unified repository field. The repository field supports additional options like branch control, SSH access, and pull request settings.

Legacy: manifests_repo

FieldTypeDescription
manifests_repo.ownerstringGitHub owner of your winget-pkgs fork
manifests_repo.namestringRepository name of your fork

Unified: repository

FieldTypeDescription
repository.ownerstringRepository owner
repository.namestringRepository name
repository.tokenstringAuth token (falls back to env-based resolution)
repository.branchstringBranch to push to (default: auto-generated as PackageIdentifier-Version)
repository.git.urlstringGit URL for SSH-based publishing
repository.git.ssh_commandstringCustom SSH command
repository.git.private_keystringPath to SSH private key
repository.pull_request.enabledboolEnable PR creation
repository.pull_request.draftboolCreate PR as draft
repository.pull_request.bodystringBody text for the PR
repository.pull_request.base.ownerstringUpstream repo owner to PR against
repository.pull_request.base.namestringUpstream repo name to PR against
repository.pull_request.base.branchstringUpstream base branch to target

Commit author

FieldTypeDescription
commit_author.namestringGit commit author name
commit_author.emailstringGit commit author email

Dependencies

Each entry in the dependencies list has:

FieldTypeDescription
package_identifierstringWinGet package identifier of the dependency
minimum_versionstringMinimum required version (optional)

Generated manifests

Anodizer generates the WinGet 3-file manifest format:

  • PackageId.yaml -- Version manifest declaring the package identifier, version, and default locale.
  • PackageId.installer.yaml -- Installer manifest with download URLs, SHA-256 checksums, architecture mappings, and upgrade behavior. For .zip archives, nested installer entries map each binary as a portable executable.
  • PackageId.locale.en-US.yaml -- Default locale manifest with publisher info, descriptions, license, tags, release notes, and other metadata.

Each file includes a YAML language server schema reference header and a generated-by-anodizer comment.

Manifests are placed at manifests/<first-char>/<PackageId segments>/<version>/ inside the repository. For example, TJSmith.Anodizer version 1.0.0 would be written to manifests/t/TJSmith/Anodizer/1.0.0/. You can override this with the path field.

Architecture mapping

Anodizer maps Rust target triples to WinGet architecture identifiers:

Rust targetWinGet architecture
x86_64-pc-windows-*x64
i686-pc-windows-*x86
aarch64-pc-windows-*arm64

Only Windows artifacts (detected by target triple or path) are included. Non-zip archives (tar.gz, 7z) are rejected -- WinGet requires .zip format for archive installers.

Installer types

Anodizer supports two installer types:

  • zip -- Archive artifacts containing portable executables. Each binary gets a NestedInstallerFiles entry with a PortableCommandAlias. If the archive wraps contents in a top-level directory, RelativeFilePath entries are prefixed accordingly.
  • portable -- Bare binary artifacts. Each binary gets a Commands entry.

You cannot mix archive and portable binary artifacts in the same manifest. Anodizer will error if both types are found.

skip_upload

The skip_upload field controls whether publishing is skipped:

  • false (default) -- Always publish.
  • true -- Always skip.
  • "auto" -- Skip when the version is a prerelease (e.g. 1.0.0-rc1).
  • A template string -- Evaluated at runtime; if the result is "true", publishing is skipped.

Template rendering

All string fields support Tera template rendering. The commit message template receives two extra variables beyond the standard context:

  • {{ PackageIdentifier }} -- The resolved package identifier.
  • {{ Version }} -- The release version.

The default commit message is New version: {{ PackageIdentifier }} {{ Version }}.

Full example

crates:
  - name: myapp
    publish:
      winget:
        package_identifier: "MyOrg.MyApp"
        publisher: "My Organization"
        publisher_url: "https://myorg.example.com"
        license: MIT
        license_url: "https://github.com/myorg/myapp/blob/main/LICENSE"
        short_description: "A fast CLI tool for doing things"
        description: "A fast CLI tool for doing things, with detailed features and capabilities."
        homepage: "https://myorg.example.com/myapp"
        tags:
          - cli
          - devtools
        dependencies:
          - package_identifier: "Microsoft.VCRedist.2015+.x64"
            minimum_version: "14.0.0"
        release_notes_url: "https://github.com/myorg/myapp/releases/tag/{{ version }}"
        installation_notes: "Run 'myapp --help' to get started."
        commit_msg_template: "Update {{ PackageIdentifier }} to {{ Version }}"
        repository:
          owner: myorg
          name: winget-pkgs
          branch: "myapp-{{ version }}"
          pull_request:
            enabled: true
            base:
              owner: microsoft
              name: winget-pkgs
              branch: master
        commit_author:
          name: "Release Bot"
          email: "bot@myorg.example.com"