diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5fc1c48..312f187 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -56,7 +56,8 @@ jobs: - name: Upload Pages artifact if: github.event_name != 'pull_request' - uses: actions/upload-pages-artifact@v5 + # Pinned to the v3+v4 PAIR on purpose — see the deploy step below. + uses: actions/upload-pages-artifact@v3 with: path: site @@ -71,4 +72,14 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5 + # v4, NOT v5. The bump to upload-pages-artifact@v5 + deploy-pages@v5 in + # ff347b2 broke this deploy twice in a row: the artifact uploads and + # "Build MkDocs" passes, then v5 polls the Pages API, sees + # `updating_pages` for about a minute, reads one empty status and aborts + # with "Timeout reached". The same commit's CI and Vulnerability scan + # were green, and the identical workflow on v3+v4 succeeded on the + # commit immediately before (a217c63), so the pair is the variable. + # + # Re-open the bump when v5 has a fix; nothing else about the Actions + # sweep is reverted. + uses: actions/deploy-pages@v4 diff --git a/pkg/cli/keys_test.go b/pkg/cli/keys_test.go index 1ca50a8..3603a8e 100644 --- a/pkg/cli/keys_test.go +++ b/pkg/cli/keys_test.go @@ -167,9 +167,27 @@ func TestInputPumpEscapeAndInterrupt(t *testing.T) { func TestInputPumpHotkeyFilter(t *testing.T) { ed := NewLineEditor(nil) - p := StartInputPump(strings.NewReader("y"), ed) + + // A PIPE, not strings.NewReader: StartInputPump launches its read loop + // immediately, and SetHotkeys installs the filter afterwards. With input + // already buffered, the pump can read and classify 'y' before the filter + // lands, and the test then sees a plain rune event instead of a hotkey. + // That is a race in the TEST's setup, not in the pump — the filter itself + // is mutex-guarded — and it failed exactly this way on a loaded CI runner: + // + // --- FAIL: TestInputPumpHotkeyFilter + // keys_test.go:177: got {Kind:1 Line: Key:y} + // + // Writing only after SetHotkeys returns makes the ordering the test means + // to assert the one it actually gets. + pr, pw := io.Pipe() + p := StartInputPump(pr, ed) defer p.Stop() p.SetHotkeys(func(k Key) bool { return k.Type == KeyRune && k.Rune == 'y' }) + go func() { + _, _ = pw.Write([]byte("y")) + _ = pw.Close() + }() select { case ev := <-p.Events():