Conversation
`pylon update` existed and nothing in the GUI reached it, so installing a release meant knowing there was a terminal command for it. A new Hakkında tab offers it in two steps — check, then install — because replacing Pylon on disk is not something to do on one click without first saying what is about to land. The bigger half is that the update never updated the GUI. The archive ships both binaries; selfupdate extracted only `pylon`. Every update left the interface behind at the old version, and nothing on screen said so — the GUI did not even know its own version, because the Makefile passed -X main.version= to a variable pylon-ui never declared and the linker dropped it in silence. The GUI still cannot replace itself: on Windows a process cannot overwrite the binary it is running from, and on neither platform can it restart into a new one without losing its state. The daemon has neither problem with it, so the daemon does both — which is also why the button goes through IPC rather than doing the work in the window. macOS is left out, where the GUI is a .app bundle and swapping one file inside it leaves a bundle disagreeing with itself about its version. The daemon is replaced first, on purpose. Two binaries cannot be swapped as one operation, so an interrupted update leaves a mismatched pair either way, and of the two an old GUI against a new daemon is the harmless one: the wire protocol only grows, so old commands still resolve. The reverse produces "unknown command" against a daemon that is merely older. Hakkında shows both versions so that window is visible rather than mysterious. Also fixed: GitHub's "latest" endpoint answers 404 when nothing is published, and skips prereleases — so this project's own state reaches it. That surfaced as "check for updates: github returned 404 Not Found", which reads as a fault on the user's machine instead of the project not having released yet. Apply is now testable: os.Executable() is the one input a test cannot supply, so it is lifted into Apply and everything downstream takes the path as an argument. Four tests drive a real signed release over httptest — both binaries replaced, a CLI-only install, an older archive with no GUI in it, and a tampered download that must leave both files untouched. The GUI swap was disabled to watch the first one fail before it was kept. Verified live: `version` answers, `update check` returns "false\t\tHenüz güncellenecek yayınlanmış bir sürüm yok." instead of the 404.
Both were the tests' fault, not the code's, and both are the same class of
mistake this project has hit before: a test written on Linux that assumes
Linux.
- Windows: t.TempDir() hands back an 8.3 short path (RUNNER~1), and
guiBeside canonicalises it — the same EvalSymlinks call that makes an
install through a symlinked ~/.local/bin land on the real file. The test
compared against the path it had built rather than the resolved one.
- macOS: guiName() is empty there, and buildArchive was handed it
unconditionally, writing a tar header ending in a slash. The test failed
on the archive it had built itself.
filepath.Join(dir, "") is dir, and guiName() is empty on macOS — so "assert no GUI appeared" was asserting that the temp directory did not exist. Guarded rather than skipped: the daemon half of that test is worth running everywhere. Verified by making guiName() return "" locally, which is exactly the macOS condition, and running the package: it passes both ways now.
Owner
Author
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pylon updateexisted and nothing in the GUI reached it: installing a release meant knowing there was a terminal command for it. A new Hakkında tab offers it in two steps — check, then install — because replacing Pylon on disk is not a one-click action to take without first saying what is about to land.The bigger half: the update never updated the GUI
The archive ships both binaries.
selfupdateextracted onlypylon. Every update left the interface behind at the old version, and nothing on screen said so — the GUI did not even know its own version, because the Makefile and the release workflow both passed-X main.version=to a variablepylon-uinever declared, and the linker dropped it in silence.The GUI still cannot replace itself: on Windows a process cannot overwrite the binary it is running from, and on neither platform can it restart into a new one without losing its state. The daemon has neither problem with it — which is also why the button goes through IPC instead of doing the work in the window. macOS is excluded, where the GUI is a
.appbundle and swapping one file inside it leaves a bundle disagreeing with itself.The daemon is replaced first, on purpose. Two binaries cannot be swapped as one operation, so an interrupted update leaves a mismatched pair either way — and of the two, an old GUI against a new daemon is the harmless one. The wire protocol only grows, so an old GUI sends commands the new daemon still understands; the reverse produces "unknown command" against a daemon that is merely older. Hakkında shows both versions so that window is visible rather than mysterious.
Also fixed
check for updates: github returned 404 Not Found, which reads as a fault on the user's machine.Verification
Applywas untestable:os.Executable()is the one input a test cannot supply. It is lifted intoApply, and everything downstream takes the path as an argument. Four tests drive a real signed release overhttptest— a tar.gz nested under a versioned directory, SHA256SUMS signed with a throwaway ed25519 key:TestApplyReplacesBothBinariesTestApplyWithoutAGUIInstalledTestApplyWhenTheArchiveHasNoGUITestApplyWritesNothingWhenTheChecksumIsWrongThe GUI swap was disabled to watch the first one fail before it was kept:
Live, against the running daemon:
go test ./...,pylon-ui's tests,npm run check:i18n(7 languages, 174 keys) andmake guiall pass.🤖 Generated with Claude Code