A controls-free pill for push-to-talk #80
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # The gate that runs before anything is called a version. | |
| # | |
| # `release.yml` runs the suite too, but only on a `v*` tag — so between tags nothing was | |
| # checked, and the first thing that could find a broken push was a release. Release stays | |
| # tag-only and deliberate; this is the one that runs on the way in. | |
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| # A second push to the same ref makes the first run's answer worthless before it lands. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| suite: | |
| # Two platforms, and the second one is the whole point: the suite's law is that the | |
| # platform decides what imports and `lite` decides what happens, which had never been | |
| # run anywhere but Windows. macOS now says it holds — **1128 of 1168 tests pass**, and | |
| # the 40 that do not are six named Win32 mechanisms, each carrying a `skipUnless` that | |
| # says which. | |
| # | |
| # **Ubuntu was here and was dropped on evidence, 2026-08-03.** uv's managed CPython | |
| # ships tkinter on macOS and not on Linux, and the suite reaches `flow.ui` in six | |
| # modules — 139 errors that say nothing about Flow and everything about that build. | |
| # macOS gives the same "not Windows" signal *with* the UI included, so the Linux leg | |
| # was buying a second opinion at the cost of a permanently red badge, and a CI nobody | |
| # believes is worse than no CI. Worth re-adding the day it can run the UI: the leg was | |
| # `ubuntu-latest` plus `sudo apt-get install -y libportaudio2` before the sync, which | |
| # did work — sounddevice bundles PortAudio for Windows and macOS only. | |
| # | |
| # The third leg re-runs Windows on CPython 3.14, because the `.python-version` pin | |
| # cuts both ways: every dev venv is 3.12 now, so the first 3.13+ divergence | |
| # (`ntpath.isabs`, 2026-08-15, seven tests red) was found by an *unpinned* venv and | |
| # nothing else could have found it. One leg past the pin finds the next one here, | |
| # on purpose. Windows only — path semantics are where the interpreter moved. | |
| name: suite (${{ matrix.os }}, py${{ matrix.python }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| # One platform failing is a fact about that platform; the other answer is what says | |
| # whether it is specific to it. | |
| fail-fast: false | |
| matrix: | |
| os: [windows-latest, macos-latest] | |
| python: ["3.12"] | |
| include: | |
| - os: windows-latest | |
| python: "3.14" | |
| # Both actions below are pinned to a commit SHA, not a tag. A tag is a mutable | |
| # pointer the action's owner can repoint at any time, which is how the trivy-action | |
| # and kics-github-action compromises reached everyone using them at once. The | |
| # trailing comment is the human-readable half: bump the SHA and the version together. | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 | |
| # Pinned, and the pin is the point. The first run of this workflow used whatever | |
| # each runner happened to have: **CPython 3.12.3 from `/usr/bin/python3` on Ubuntu | |
| # and 3.14.6 from Homebrew on macOS** — two minor versions past what this project | |
| # targets, and three legs running three unrelated experiments rather than one. | |
| # It also cost 137 of the 153 errors: Debian splits `tkinter` into `python3-tk`, | |
| # and the suite reaches `flow.ui` in six modules. uv's own build is the same | |
| # interpreter everywhere, which is the only way the matrix answers a question. | |
| # The matrix supplies the version now — still pinned per leg, never the runner's | |
| # choice, and this input outranks `.python-version`, which is what lets the 3.14 | |
| # leg exist beside the pin at all. | |
| - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 | |
| with: | |
| enable-cache: true | |
| python-version: ${{ matrix.python }} | |
| # `--frozen` and not a plain sync: a run that silently resolves a newer dependency | |
| # is testing a tree nobody has, and reports green about it. | |
| - run: uv sync --frozen | |
| - name: unit suite | |
| run: uv run python -m unittest discover -s tests | |
| # Catches a syntax error in a module no test happens to import — the failure that | |
| # a green suite is structurally unable to see. | |
| - name: compile everything | |
| run: uv run python -m compileall -q flow scripts | |
| # And an entry point that cannot boot at all, which is the one thing every user | |
| # does first and no unit test does. | |
| - name: the entry point starts | |
| run: uv run flow --help | |
| # The one part of Flow that is not Python, on the only runner that can compile it. | |
| # | |
| # `native/flow_stt.swift` is the macOS on-device decoder — the answer for machines that | |
| # cannot reach huggingface.co, where faster-whisper's weights are the only official | |
| # copy. It is written on Windows, where there is no Swift toolchain, so without this | |
| # leg the compiler is a person on a laptop pasting errors back. The first version was | |
| # exactly that and shipped with a hard one in it: Swift allows top-level statements | |
| # only in a file called `main.swift`, and this is not one. | |
| # | |
| # Compile only, and that is the honest limit of what CI can say here. Actually | |
| # *running* it needs a granted Speech Recognition permission and an on-device model | |
| # that only arrives when a human enables Dictation — neither of which a headless runner | |
| # has, and faking them would make this leg green about something it never checked. | |
| # `--probe` would exit 2 on the runner for exactly that reason, which is the right | |
| # answer and a useless test. | |
| helper: | |
| name: swift helper compiles | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 | |
| - name: swiftc version | |
| run: xcrun swiftc --version | |
| # `-warnings-as-errors` because this file is edited by people who cannot run it. | |
| # A warning here is the only signal that reaches them before a user does. | |
| - name: compile the macOS decoder | |
| run: xcrun swiftc -O -parse-as-library -warnings-as-errors -o /tmp/flow-stt native/flow_stt.swift | |
| # It links against AVFoundation and Speech, so a binary that builds but cannot | |
| # resolve a symbol is still a broken helper. Asking it for its usage line proves | |
| # the dynamic linker is satisfied without needing a microphone or a permission. | |
| - name: it links and runs far enough to refuse | |
| run: | | |
| set +e | |
| out=$(/tmp/flow-stt --nonsense 2>&1) | |
| code=$? | |
| echo "$out" | |
| # 1 is `die("usage: ...")`. Anything else — a link failure, a crash, or a | |
| # silent success on an argument that is not valid — is a broken build. | |
| if [ "$code" -ne 1 ]; then echo "expected exit 1, got $code"; exit 1; fi | |
| case "$out" in *usage*) ;; *) echo "expected a usage line"; exit 1 ;; esac |