Lubię błędy, które po znalezieniu wyglądają banalnie. Nie dlatego, że są łatwe do naprawienia, ale dlatego, że dobrze przypominają, jak niewielkie założenie potrafi zatrzymać cały proces. W aktualizatorze SSH Tunnel Managera takim założeniem było potraktowanie spacji jak bezpiecznego separatora.
Na macOS aktualizacja aplikacji korzysta z obrazu DMG. System montuje go jako wolumin, a aktualizator musi odczytać jego ścieżkę z wyniku polecenia hdiutil. Dla SSH Tunnel Managera poprawna ścieżka mogła wyglądać tak:
/Volumes/SSH Tunnel Manager
Problem polegał na tym, że parser widział z niej tylko:
/Volumes/SSH
DMG był zamontowany. Nazwa aplikacji była poprawna. Aktualizator szukał jednak plików w miejscu, które nie istniało.
Skąd wzięło się ucięcie ścieżki
Wynik hdiutil jest podzielony na kolumny. Jedną z nich jest punkt montowania, czyli właśnie ścieżka zaczynająca się od /Volumes/. Poprzedni parser rozbijał każdy wiersz funkcją strings.Fields, a następnie zwracał pierwszy fragment zaczynający się od /Volumes/.
To działało dla nazw bez spacji. Dla SSH Tunnel Manager powstawały jednak osobne fragmenty: /Volumes/SSH, Tunnel i Manager. Pierwszy z nich spełniał warunek, więc funkcja kończyła pracę za wcześnie.
W kodzie istniała jeszcze próba obsługi spacji przez odczyt tekstu po ostatnim tabulatorze. Nie mogła pomóc w tym przypadku, ponieważ wcześniejsza pętla zdążyła już zwrócić skróconą wartość. To jeden z tych błędów, w których zabezpieczenie jest obecne, ale kolejność instrukcji sprawia, że nigdy nie dostaje szansy zadziałać.
Cała końcówka zamiast jednego pola
Poprawka jest mała: parser nie dzieli już punktu montowania na słowa. W każdym wierszu znajduje początek /Volumes/ i zachowuje wszystko od tego miejsca do końca, usuwając jedynie białe znaki z brzegów.
W uproszczeniu zmiana sprowadza się do takiej reguły:
if idx := strings.Index(line, "/Volumes/"); idx >= 0 {
return strings.TrimSpace(line[idx:])
}
Dzięki temu pełną wartością pozostaje zarówno /Volumes/SSH Tunnel Manager, jak i wariant /Volumes/SSH Tunnel Manager 2, który macOS może utworzyć przy kolejnym montowaniu woluminu o tej samej nazwie.
Do poprawki dołączyły testy trzech sytuacji: woluminu HFS z nazwą zawierającą spacje, woluminu APFS z dodatkowym numerem oraz wyniku bez zamontowanego woluminu. To ważne, bo test nie sprawdza abstrakcyjnego ciągu znaków. Utrwala dokładnie ten kształt danych, który wcześniej zepsuł aktualizację.
Czytelna nazwa aplikacji też jest częścią techniki
W tym samym wydaniu uporządkowałem nazwę pakietu na macOS. Zamiast technicznego ssh-tunnel-manager.app użytkownik otrzymuje SSH Tunnel Manager.app. Konfiguracja pakietu dostała również czytelną nazwę wyświetlaną przez macOS.
Nie jest to wyłącznie kosmetyka. Zmiana nazwy pakietu wpływa na ścieżki używane podczas budowania, podpisywania, tworzenia DMG i instalowania aktualizacji. Aktualizator musi też przenieść istniejącą instalację ze starej nazwy na nową. Jeżeli aplikacja miała włączony start po zalogowaniu, uruchomienie nowej wersji odświeża zapisaną ścieżkę autostartu.
Właśnie dlatego te dwie poprawki dobrze pasują do jednego wydania. Spacja pojawiła się w nazwie po to, aby aplikacja wyglądała na macOS tak, jak powinna. Każdy element procesu aktualizacji musiał jednak traktować tę nazwę jako jedną wartość, a nie trzy słowa.
Dostępność poprawki
Obie zmiany są dostępne w wydaniu SSH Tunnel Manager v1.0.32-beta.1, oznaczonym na GitHubie jako prerelease. Notatki do wydania wymieniają naprawę aktualizacji z DMG, czytelną nazwę pakietu oraz odświeżenie ścieżki autostartu po migracji.
Najważniejsza lekcja jest prosta: ścieżka systemowa nie jest listą słów. Jeżeli format zewnętrznego narzędzia pozwala na spacje w ostatniej kolumnie, parser powinien zachować tę kolumnę w całości. A test powinien używać nazwy wystarczająco niewygodnej, by sprawdzić to założenie przed użytkownikiem.
