A Forgejo action that submits a new version of a program to the JCOllie winget source as a pull request.
  • Python 91.6%
  • Nix 8.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-14 13:22:40 -05:00
.forgejo/workflows add the submit action 2026-10-14 01:26:16 -05:00
docs/agents Add agent skill configuration 2026-10-14 00:17:37 -05:00
LICENSES start the winget-submit repository 2026-10-14 00:11:35 -05:00
tests pass product codes through to new-version 2026-10-14 03:16:46 -05:00
action.yml describe product-codes as an override 2026-10-14 12:03:38 -05:00
AGENTS.md Add agent skill configuration 2026-10-14 00:17:37 -05:00
CONTRIBUTING.md add the submit action 2026-10-14 01:26:16 -05:00
flake.lock start the winget-submit repository 2026-10-14 00:11:35 -05:00
flake.nix add the submit action 2026-10-14 01:26:16 -05:00
README.md describe product-codes as an override 2026-10-14 12:03:38 -05:00
REUSE.toml add the submit action 2026-10-14 01:26:16 -05:00
winget_submit.py describe product-codes as an override 2026-10-14 12:03:38 -05:00

winget-submit

A Forgejo Actions step that submits a new release of a program to the JCOllie winget source, as a pull request against its manifest repository, jeff/winget-pkgs.

git clone https://git.jcollie.dev/jeff/winget-submit.git

It is mirrored at https://tangled.org/jcollie.dev/winget-submit.

What it does

Given a package identifier, a version and the URL of each installer, it:

  1. clones jeff/winget-pkgs at main;
  2. runs that repository's wingetsource new-version, which copies the package's newest version and replaces what changes with the release — the version, the release date, each installer's URL and SHA-256, and the release notes link;
  3. commits the new version and runs wingetsource check on it, which downloads each installer and confirms its hash;
  4. force-pushes the branch submit/<identifier>/<version>, and opens a pull request titled <identifier> <version> unless one is already open for that branch.

The pull request still needs jeff/winget-pkgs' own test check to pass and a merge by hand; branch protection there keeps the submitter off main.

The manifest is never written here. Writing it is wingetsource's job, and this action runs whatever wingetsource is on main, so a change to how manifests are written does not move the commit every program pins. A package's first version is still written by hand; this only submits a package that already has one, for the same set of architectures.

Using it

Pin it by commit, in a job after the one that uploads the release's assets. It submits whatever version it is given, so the workflow is what skips a prerelease — here, any tag with a - in it:

  winget:
    needs: release
    if: ${{ !contains(forge.ref_name, '-') }}
    runs-on: nixos-do-s
    steps:
      # The tag is v0.3.0; the version is 0.3.0.
      - name: Version
        id: version
        run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

      - name: Submit to the JCOllie winget source
        uses: https://git.jcollie.dev/jeff/winget-submit@<commit>
        with:
          identifier: JCOllie.Clipwatch
          version: ${{ steps.version.outputs.version }}
          installers: |
            x64 https://git.jcollie.dev/jeff/clipwatch/releases/download/${{ forge.ref_name }}/clipwatch-x86_64-windows.exe
          release-notes-url: https://git.jcollie.dev/jeff/clipwatch/releases/tag/${{ forge.ref_name }}
          token: ${{ secrets.WINGET_BOT_TOKEN }}
Input Required Meaning
identifier yes The package identifier, e.g. JCOllie.Clipwatch.
version yes The version to submit.
installers yes One <architecture> <url> per line, for exactly the architectures of the package's newest version.
product-codes no One <architecture> <code> per line: a ProductCode to write instead of the one read from the new MSI.
release-notes-url no The release's notes. Without it the new version has none.
release-date no YYYY-MM-DD; today in UTC by default.
token yes, except for a dry run The submitter's token: secrets.WINGET_BOT_TOKEN.
dry-run no true writes and checks the new version, prints it, and pushes nothing.

An MSI built with Product Id="*" has a new ProductCode every release. wingetsource new-version never copies the old one: it reads the new one from the MSI it downloads, and refuses an MSI whose UpgradeCode is not the package's, so an MSI needs nothing passed for it. product-codes is only for writing a different code than the MSI carries.

The job needs nothing but Nix: the action's script comes from this repository's flake, and wingetsource runs in jeff/winget-pkgs' own development shell.

The submitter's token

WINGET_BOT_TOKEN is a token for winget-bot, a restricted Forgejo user with write access to jeff/winget-pkgs and to nothing else, which can push branches and open pull requests there but not merge them. Each program has a token of its own, limited to jeff/winget-pkgs and to the write:repository and write:issue scopes. It is a secret on the program's repository, because a workflow's own forge.token reaches only the repository it runs in. The commit is authored as winget-bot <[email protected]>, built into the action: a token limited to one repository cannot read its own user.

tools/setup-submitter in jeff/winget-pkgs sets all of this up for a program, and is run again for each new one; its CONTRIBUTING.md says how.

git is handed the token in an HTTP header through the environment, so it appears neither in the process list nor in the clone.

Reporting issues

Report issues and send pull requests at https://tangled.org/jcollie.dev/winget-submit. See CONTRIBUTING.md for how to work on it.

License

MIT. The repository follows the REUSE specification, and reuse lint passes.

References cited