Lubię błędy, które po znalezieniu wydają się banalne. Człowiek patrzy na poprawkę składającą się z kilku linii i zastanawia się, jak coś tak małego mogło zatrzymać cały proces aktualizacji. Tym razem wystarczyła jedna spacja.
Aktualizator SSH Tunnel Managera na macOS korzysta z obrazu DMG. System montuje taki obraz jako wolumin, a aplikacja musi później znaleźć jego ścieżkę. W tym przypadku wyglądała ona tak:
/Volumes/SSH Tunnel Manager
Przynajmniej powinna tak wyglądać z perspektywy aktualizatora. Parser zwracał bowiem tylko:
/Volumes/SSH
DMG był poprawnie zamontowany, aplikacja znajdowała się na swoim miejscu, ale aktualizator szukał jej w katalogu, który nie istniał. Skąd zatem wzięło się ucięcie ścieżki?
Spacja okazała się separatorem
Polecenie hdiutil zwraca informacje o zamontowanych obrazach w kilku kolumnach. Jedną z nich jest punkt montowania, czyli ścieżka rozpoczynająca się od /Volumes/.
Poprzedni parser dzielił każdy wiersz za pomocą strings.Fields. Działało to dobrze, dopóki nazwa woluminu nie zawierała spacji. Dla SSH Tunnel Manager funkcja tworzyła trzy osobne fragmenty:
/Volumes/SSH
Tunnel
Manager
Następnie parser wybierał pierwszy fragment zaczynający się od /Volumes/. Warunek został spełniony, więc funkcja grzecznie zwracała /Volumes/SSH i kończyła pracę. Technicznie zrobiła dokładnie to, o co ją poproszono. Problem polegał na tym, że poproszono ją o niewłaściwą rzecz.
W kodzie istniała jeszcze druga próba odczytania punktu montowania — na podstawie tekstu znajdującego się po ostatnim tabulatorze. Miała obsługiwać właśnie ścieżki zawierające spacje. Nigdy jednak nie dostawała szansy zadziałać, ponieważ wcześniejsza pętla zdążyła już zwrócić niepełny wynik.
Zabezpieczenie było zatem obecne. Tylko co z tego, skoro kolejność instrukcji skutecznie je omijała?
Ścieżka to nie lista słów
Poprawka nie wymagała rozbudowywania parsera. Wręcz przeciwnie — wystarczyło przestać dzielić interesującą mnie część wiersza na osobne pola.
Parser wyszukuje teraz początek /Volumes/, a następnie zachowuje wszystko od tego miejsca do końca wiersza:
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 poprawnie obsługiwane są zarówno zwykłe ścieżki:
/Volumes/SSH Tunnel Manager
jak i wariant tworzony przez macOS, gdy wolumin o tej samej nazwie jest już zamontowany:
/Volumes/SSH Tunnel Manager 2
Do poprawki dołączyłem testy dla trzech przypadków: woluminu HFS z nazwą zawierającą spacje, woluminu APFS z dodatkowym numerem oraz wyniku, w którym nie ma żadnego zamontowanego woluminu.
To ostatnie jest równie ważne. Parser powinien nie tylko zwracać pełną ścieżkę, gdy ją znajdzie, ale także poprawnie zachować się wtedy, gdy nie ma czego zwrócić.
Czytelna nazwa aplikacji ma swoją cenę
Spacja nie pojawiła się tam przypadkiem. W tym samym wydaniu zmieniłem nazwę pakietu aplikacji na macOS. Zamiast technicznego:
ssh-tunnel-manager.app
użytkownik otrzymuje teraz:
SSH Tunnel Manager.app
Konfiguracja pakietu dostała również czytelną nazwę wyświetlaną przez system.
Brzmi jak kosmetyka? Trochę tak, ale zmiana nazwy aplikacji wpływa na kilka elementów jednocześnie: budowanie, podpisywanie, tworzenie obrazu DMG, instalowanie aktualizacji oraz migrację istniejącej aplikacji.
Aktualizator musi rozpoznać starą nazwę, przenieść instalację pod nową i dalej poprawnie uruchomić program. Jeżeli użytkownik miał włączony start po zalogowaniu, aplikacja powinna również odświeżyć zapisaną ścieżkę autostartu.
Właśnie dlatego obie zmiany trafiły do jednego wydania. Chciałem, aby aplikacja na macOS nazywała się tak, jak powinna. Okazało się jednak, że każdy element procesu aktualizacji musi traktować tę nazwę jako jedną wartość, a nie trzy oddzielne słowa.
Poprawka jest już dostępna
Zmiany trafiły do wydania SSH Tunnel Manager v1.0.32-beta.1, oznaczonego na GitHubie jako prerelease.
Wydanie obejmuje naprawę odczytywania ścieżki zamontowanego obrazu DMG, czytelną nazwę pakietu oraz odświeżanie ścieżki autostartu po migracji aplikacji.
Najważniejszy wniosek? Ścieżka systemowa nie jest listą słów. Jeżeli zewnętrzne narzędzie pozwala na spacje w ostatniej kolumnie, parser powinien zachować tę kolumnę w całości.
A test najlepiej napisać z nazwą wystarczająco niewygodną, aby znalazł takie założenie wcześniej niż użytkownik.
