Files
SoxojandGitHub 00281d5322 Attach the Windows binary to published releases (#3048)
`pyinstaller.yml` only runs on pushes to `main` and `dev`, so `maigret_standalone.exe` exists only on the moving `nightly-*` tags. Every stable release from v0.6.1 to v0.6.5 has zero assets, and winget and Scoop cannot pin a moving tag by hash.

- add the `release: types: [published]` trigger, the same one `python-publish.yml` uses. Checkout resolves `refs/tags/<tag>`, so the exe is built from the released code.
- guard the two nightly-only steps on `github.event_name == 'push'`, otherwise `NIGHTLY_TAG` becomes `nightly-v0.6.5` and creates a junk tag beside the real one.
- add a step attaching the binary to the published release with every `omit*DuringUpdate` set, so the title, body, prerelease and draft state are left alone.
- `tests/test_workflows.py`: the `release_step` fixture asserted there was exactly one `release-action` step, which a second one breaks. It now selects a step by the event it is guarded on, and the new step gets its own guards: every release step must be event specific, the published one must set all four omits, and it must target `github.event.release.tag_name` rather than `NIGHTLY_TAG`.

Existing releases are not backfilled. A release now costs two PyInstaller runs, one on the tag and one on the push to `main` that follows.
2026-08-30 23:22:06 +02:00

140 lines
5.8 KiB
YAML

name: Package exe with PyInstaller - Windows
on:
push:
branches: [main, dev]
release:
types: [published]
# Two builds racing on the same branch could leave the nightly tag pointing at
# one commit while the .exe attached to the release was built from the other.
concurrency:
group: pyinstaller-${{ github.ref }}
cancel-in-progress: true
# Needed by "Move the nightly tag to the built commit" below, which force-updates
# a ref under refs/tags/.
permissions:
contents: write
env:
# Deliberately prefixed. A tag named after the branch it was built from shadows
# that branch, and a bare `main` then resolves to the tag in every clone:
# `git rev-parse main` warns "refname 'main' is ambiguous" and answers with the
# tag, not the branch head.
NIGHTLY_TAG: nightly-${{ github.ref_name }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# Wine Python (not Linux) runs PyInstaller; altgraph needs pkg_resources — reinstall setuptools after all deps.
- name: Prepare requirements for Wine (setuptools last)
run: |
set -euo pipefail
cp pyinstaller/requirements.txt pyinstaller/requirements-wine.txt
{
echo ""
echo "# CI: setuptools last so pkg_resources exists for PyInstaller/altgraph in Wine"
echo "setuptools==70.0.0"
} >> pyinstaller/requirements-wine.txt
- name: PyInstaller Windows Build
uses: JackMcKew/pyinstaller-action-windows@main
with:
path: pyinstaller
requirements: requirements-wine.txt
- name: Upload PyInstaller Binary to Workflow as Artifact
if: success()
uses: actions/upload-artifact@v4
with:
name: maigret_standalone_win32
path: pyinstaller/dist/windows
- name: Download PyInstaller Binary
if: success()
uses: actions/download-artifact@v4
with:
name: maigret_standalone_win32
# release-action creates a tag only when it does not exist yet, and GitHub
# ignores target_commitish once the tag is there ("Unused if the Git tag
# already exists"). So the tag has to be moved here, otherwise it stays
# pinned to the first build forever while the attached .exe keeps being
# replaced on every push.
- name: Move the nightly tag to the built commit
if: success() && github.event_name == 'push'
run: |
set -euo pipefail
git tag --force "$NIGHTLY_TAG" "$GITHUB_SHA"
# Tag pushes do not match `on.push.branches`, and pushes authenticated
# with GITHUB_TOKEN never start a workflow run, so this cannot recurse.
git push --force origin "refs/tags/$NIGHTLY_TAG"
- name: Create New Release and Upload PyInstaller Binary to Release
if: success() && github.event_name == 'push'
uses: ncipollo/release-action@v1.14.0
id: create_release
with:
allowUpdates: true
draft: false
# A development build must never outrank a stable release. `prerelease`
# keeps it out of /releases/latest, and `makeLatest: false` stops it
# from stealing the marker back on the next push to main.
prerelease: true
artifactErrorsFailBuild: true
makeLatest: false
replacesArtifacts: true
artifacts: maigret_standalone.exe
name: Development Windows Release [${{ github.ref_name }}]
tag: ${{ env.NIGHTLY_TAG }}
commit: ${{ github.sha }}
body: |
This is a development release built from the **${{ github.ref_name }}** branch, at commit ${{ github.sha }}.
The `${{ env.NIGHTLY_TAG }}` tag is moved to that commit on every build, so it always matches the binary attached below.
It is **not** a stable release — for that, see [the latest release](https://github.com/soxoj/maigret/releases/latest).
${{ github.ref_name == 'dev' && 'The `dev` branch is more experimental than `main`. Unless you need a specific unreleased fix, prefer [the `main` development build](https://github.com/soxoj/maigret/releases/tag/nightly-main).' || '' }}
## How to run
1. Download the attached `maigret_standalone.exe`.
2. Either:
- **Double-click it** — Maigret will ask you for a username, run a default search, and pause at the end so the output stays visible.
- **Run it from a terminal** for full options. Press `Win+R`, type `cmd`, hit Enter (or open PowerShell), then:
```cmd
cd %USERPROFILE%\Downloads
maigret_standalone.exe USERNAME
maigret_standalone.exe USERNAME --html :: also save an HTML report
maigret_standalone.exe --help :: list all options
```
Video guide: https://youtu.be/qIgwTZOmMmM
Full documentation: https://maigret.readthedocs.io/en/latest/
env:
GITHUB_TOKEN: ${{ github.token }}
# A published release already has its title, notes and prerelease flag set
# by whoever published it. release-action would overwrite all three with its
# own defaults during an update, so every omit*DuringUpdate is on: this step
# attaches the binary and touches nothing else.
- name: Attach the binary to the published release
if: success() && github.event_name == 'release'
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: true
omitNameDuringUpdate: true
omitBodyDuringUpdate: true
omitPrereleaseDuringUpdate: true
omitDraftDuringUpdate: true
artifactErrorsFailBuild: true
replacesArtifacts: true
artifacts: maigret_standalone.exe
tag: ${{ github.event.release.tag_name }}