Skip to content

Check the winget manifest before it is submitted - #81

Open
chiragjangiir wants to merge 2 commits into
JeremySNR:mainfrom
chiragjangiir:fix/winget-release-hardening
Open

chiragjangiir wants to merge 2 commits into
JeremySNR:mainfrom
chiragjangiir:fix/winget-release-hardening

Conversation

@chiragjangiir

@chiragjangiir chiragjangiir commented Oct 10, 2026 •

Copy link
Copy Markdown

What does this change?

Two things on the winget path, refs #47.

vedantmgoyal9/winget-releaser is pinned to a commit instead of the moving @v2
tag, and scripts/print-winget-manifest.mjs now checks the three YAML files it
generates against the winget 1.6.0 manifest schemas — and against
package.json's NSIS scope — before it writes anything.

Why?

#47 lists three remaining steps. Two of them live in this repo, and both were
still open.

The pin. The issue comment asks for this: the action is handed
WINGET_TOKEN, a PAT that can open PRs on a fork of winget-pkgs, and @v2 is
a mutable tag. That is not hypothetical — as of today @v2 resolves to a commit
pushed 2026-10-07, while the v2 release dates from 2025-01-27, so the
tag has already moved without a release:

$ gh api repos/vedantmgoyal9/winget-releaser/git/ref/tags/v2 -q .object.sha
f29ed1ffd34f91223a2aebaa71beaab0901ff9fd
$ gh api repos/vedantmgoyal9/winget-releaser/commits/v2 -q '.sha, .commit.committer.date'
f29ed1ffd34f91223a2aebaa71beaab0901ff9fd
2026-10-07T15:28:57Z

The pin uses the commit @v2 resolves to today, so the published behaviour is
identical; a later tag move now needs a deliberate bump, and docs/winget.md
documents the one-liner for that.

The checks. The first submission to microsoft/winget-pkgs is still
outstanding, and this generator is what feeds it. Nothing verified its output,
so a manifest winget-pkgs would reject only surfaced during review —
winget validate and wingetcreate do not run on macOS or Linux. The
generator now refuses to write a trio that fails, and prints why:

  • the fields the 1.6.0 schemas mark required in each file, plus the enum values
    for architecture, installer type, scope and upgrade behaviour;
  • the identifier and version agreeing across all three files, and the locale
    fields matching DefaultLocale;
  • the installer URL naming the versioned asset the release actually uploads,
    and the hash being 64 hex characters;
  • the locale string length limits (the minima matter as much as the maxima);
  • Scope matching package.json's NSIS config. Scope: user is only correct
    while electron-builder stays per-user, which is the other thing the Publish a winget manifest for Windows installs #47
    comment flags — the test fails if perMachine is ever turned on without
    moving the manifest with it.

Release notes

None. No file under src/, resources/ or package.json changes, so the
release-notes check passes without a CHANGELOG.md line and without the label:

$ BASE_REF=origin/main PR_LABELS='[]' node scripts/check-release-notes.mjs
No app changes; release notes not required.

How did you test it?

  • npm test
  • npm run typecheck
  • npm run lint
  • Checked the change in the running app — not applicable, nothing in src/
    changes, so preview/export parity is untouched

Beyond the suite:

  • The fixture is real, not synthetic. The generator was run against the live
    v0.13.0 release and the trio passed. The hash it wrote was cross-checked
    against GitHub's own digest for the real 208 MB installer
    (7A1AD50C…8B152B), and the URL against the asset that is actually published.

  • The emitted manifest is unchanged. Output from main and from this branch
    is byte-for-byte identical, in both --out DIR and stdout mode:

    $ diff -r /tmp/cutawan/orig /tmp/cutawan/new
    IDENTICAL: byte-for-byte the same output
  • The checks actually fire. tests/wingetManifest.test.ts goes from 2 to 15
    tests, and each bad-manifest case asserts the exact problem reported rather
    than only that something failed. Covered: a bad hash, a version that disagrees
    across the trio, a missing required locale field, a licence below the minimum,
    an unsupported architecture, a URL that does not name the versioned asset,
    duplicate tags, a non-http URL, a one-part identifier, a version with a
    forbidden character, and scope drift from package.json.

  • The failure path was exercised by hand as well as in tests: with
    perMachine: true the CLI refuses to write and explains why.

Anything to watch out for?

  • This does not close Publish a winget manifest for Windows installs #47. The remaining bullet — the actual pull request to
    microsoft/winget-pkgs — needs a fork of that repo and WINGET_TOKEN, so it
    can only be done from the maintainer side. winget install JeremySNR.Cutawan
    does not work until that lands, which is why this says "refs" rather than
    "fixes".
  • The checks cover the shape winget-pkgs validates. They cannot cover the parts
    of their pipeline that need Windows — the installer actually installing and
    uninstalling — or the manual review.
  • The pin will go stale by design; bumping it is a deliberate act now.
  • Only the third-party action is pinned here. The other uses: are actions/*,
    which is a separate call worth making on purpose.
  • No CLA exists in this repo, so nothing needs signing.

Note

Medium Risk
Changes release CI supply-chain handling for a PAT-backed action and adds gates that can block manifest generation if packaging config drifts; no app runtime impact.

Overview
Hardens the winget publish path so bad manifests fail locally and the release workflow does not run unreviewed third-party code with a PAT.

print-winget-manifest.mjs now validates the three generated YAML files against winget 1.6.0 schema rules (required fields, enums, locale length limits, SHA256/URLs, cross-file identifier/version/locale alignment) and repo-specific checks (installer URL must include the release version; Scope must match electron-builder NSIS settings in package.json). Validation runs on the emitted YAML bytes via js-yaml; if anything fails, the script errors and writes nothing.

The Release workflow pins vedantmgoyal9/winget-releaser to commit f29ed1f… instead of the moving @v2 tag, since that action uses WINGET_TOKEN. docs/winget.md documents both behaviors and how to bump the pin.

tests/wingetManifest.test.ts adds coverage for validateManifests and scope derivation (many rejection cases).

Reviewed by Cursor Bugbot for commit fe2bffa. Bugbot is set up for automated code reviews on this repo. Configure here.

chiragjangiir and others added 2 commits October 10, 2026 22:07
winget-releaser is handed WINGET_TOKEN, a PAT that can open PRs on a fork of
winget-pkgs, and @v2 is a moving tag: it currently resolves to a commit pushed
2026-10-07 while the v2 release dates from 2025-01-27. Pinning to the commit
@v2 resolves to today keeps the published behaviour identical while stopping a
later tag move from running unreviewed code with that token.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
The first submission to winget-pkgs is still outstanding (JeremySNR#47), and the three
YAML files it needs are produced by print-winget-manifest.mjs. Nothing checked
them, so a manifest winget-pkgs would reject only surfaced during review.

The generator now parses its own output and checks it against the winget 1.6.0
manifest schemas: required fields, identifier and version agreement across the
trio, the installer URL naming the versioned asset the release uploads, hash
format, the architecture and installer type enums, and the locale length
limits. It also checks the one invariant only this repo knows: Scope has to
match electron-builder's NSIS config in package.json, because that is what
decides where winget installs the app. Nothing is written when a check fails.

The emitted manifest is unchanged byte for byte for the current release, so
this only adds the checks.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Publish a winget manifest for Windows installs

1 participant