A Forgejo action that submits a new version of a program to the JCOllie winget source as a pull request.

Files
README.md

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 <winget-bot@jcollie.dev>, 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 #