Czas na małą opowiastkę z linii frontu. Odkryłem niedawno, że nie działa mi Bundler – program pozwalający podejrzeć treści na bloga przed ich publikacją.

Wiedząc, że chodzi rzekomo o brakujący plik, nieco pośledziłem Bundlera programem monitorującym interakcje.
Odkryłem, że źródłem błędu był brak wykonywalności pliku (silnika blogowego Jekyll); swoją rolę odgrywał również potencjalnie mylący komunikat o błędzie.

Kontekst

Mój blog opiera się na popularnym silniku Jekyll, napisanym w języku Ruby.

Przed publikacją mam zwyczaj uruchamiać tryb podglądu, żeby zobaczyć, czy wszystko wygląda i działa jak należy. Używam do tego programu-menedżera o nazwie Bundler.

Rutynowo wpisuję w konsolę:

bundle exec jekyll serve

Zwykle widzę informację zwrotną, że blog jest dostępny na lokalnym serwerze testowym (wszystko w obrębie mojego komputera, nawet bez łączności z siecią).
Klikam w konsoli link do wewnętrznego adresu 127.0.0.1:4000 – a po chwili w domyślnej przeglądarce wyświetla się Ciemna Strona.

Tym razem jednak coś poszło nie tak i zamiast Ciemnej pojawił się komunikat o błędzie:

Zrzut ekranu z konsoli, pokazujący błąd programu Bundle i komunikat o braku modułu Jekyll

Bundler: command not found: jekyll
Install missing gem executables with 'bundle install'

Pokazuje, że nie znalazło modułu jekyll. A ja przecież wzywałem Jekylla już nieraz, dokładnie w ten sposób. Wiedziałem, że go mam.

Miałem już kiedyś identyczny błąd; pamiętam, że po prostu skopiowałem wtedy pliki z jakiegoś backupu. Zaczęło działać, nie drążyłem tematu. Tym razem byłem bardziej zmotywowany.

Debugowanie programem strace

Skoro moim problemem był rzekomy brak pliku (o którym wiedziałem, że go mam), postanowiłem sięgnąć do programu strace (czyt. strejs).

Jest on swego rodzaju szpiegiem, który śledzi wszystkie interakcje wskazanego programu z jądrem systemu Linux (a takiego używam).
Każde otwarcie pliku na Linuksie zostanie odnotowane przez strace’a, bo wymaga takiej interakcji. Mógłbym zobaczyć, gdzie Bundler szukał plików.

Użyłem zatem strace’a w trybie śledzenia zarówno samego Bundlera, jak i jego podprocesów (-f) oraz zapisywania szczegółowych informacji (-v) do pliku nazwanego subiektywnie nojekyll.txt (-o nojekyll.txt):

strace -f -v -o nojekyll.txt bundle exec jekyll serve

Po wyskoczeniu błędu program skończył działanie, a strace przestał notować. Powstał pokaźny plik z zapiskami, liczący ponad 17 MB.
Przy takich ścianach tekstu trzeba zrobić przesiew informacji.

Mogłem na przykład założyć, że początek pliku to wszelkiego rodzaju ładowanie modułów, czyli rzeczy nieciekawe. Była szansa, że ciekawe rzeczy znajdę bliżej końca, tuż przed wystąpieniem błędu i zakończeniem działania. Mogłem też filtrować po nazwie, bo interesował mnie konkretny moduł – jekyll. Wyszukałem tę nazwę w zapisanym pliku:

grep 'jekyll' nojekyll.txt

…I wyskoczyły bardzo ciekawe rzeczy:

Kilka linijek z konsoli, w których wyróżniono słowo 'jekyll'. Widać, że to próby otwierania plików pod kilkoma różnymi ścieżkami.

Widać, że w pierwszej linijce z obrazka (wyróżnionej kolorem) Bundler próbuje zaglądać do tego właściwego pliku, ale zderza się z brakiem pozwolenia (Permission denied).
Potem zagląda też w inne miejsca, ale tam nic nie ma (No such file or directory).

Klucz do rozwiązania zagadki krył się w tej pierwszej, wyróżnionej linijce. W słowie X_OK. W konwencji wielu systemów litera X odpowiada słowu eXecutable. „Wykonywalny” (jako program).
Mając pewne podejrzenie, wyszukałem X_OK w internecie. Moje potwierdzenia się przypuściły, ten skrót odpowiada wykonywalności.

…A tak się składa, że w ostatnim czasie ingerowałem w tę właściwość.

Rozwiązanie zagadki

Sprawa wynikała ze zmian, jakie wprowadziłem niedawno, po napisaniu poradnika na temat wyłączania irytującego komunikatu.

Streszczając: każdy plik na Linuksie jest opisywany przez właściwość zwaną wykonywalnością (ang. executability). Mówi ona z grubsza, czy dany plik można traktować jak program.

Nie jest to w żadnym razie rzecz uniwersalna. Wiele systemów plików (np. ten na moim pendrivie) nie wspiera tej właściwości. Żeby jakoś pogodzić dwa światy, Linux podczas zgrywania plików z takiego pendrive’a automatycznie włącza ich wykonywalność.
A to prowadzi do nieco irytującego komunikatu, gdy próbuję otworzyć pliki tekstowe w zwykłej przeglądarce plików (a robię to często, nie jestem typem konsolowca).

Inspirując się własnym, podlinkowanym wyżej poradnikiem, wyłączyłem wykonywalność wielu plików naraz (chmod -R -x+X FOLDER). Zebrałem je do jednego archiwum ZIP, a to archiwum zgrałem na pendrive’a. Potem je rozpakowałem, przenosząc narzędzia do pracy nad blogiem na inny komputer.

Dzięki temu nie miałem problemu z niechcianym komunikatem. Myślałem, że z działaniem plików też nie (mogę np. używać skryptów Pythona poleceniem python3 skrypt.py, nawet gdy nie są wykonywalne).

Jak się okazuje, Bundler (a może cały język Ruby?) idzie jednak z wymaganiami o krok dalej. Wykonywalny musi być nie tylko uruchamiacz, ale również uruchamiany programik, Jekyll. I na tym się naciąłem.

A komunikat o braku pliku? Nie do końca trafny. Po prostu po napotkaniu pierwszego błędu (braku pozwolenia) Bundler szukał dalej, nie znajdując niczego. I wspomniał wyłącznie o nieznalezieniu, pomijając pierwszy błąd.

Naprawa błędu

Skoro przyczyną był brak wykonywalności Jekylla, to rozwiązanie wydawało się proste – włączyć mu wykonywalność.

Można konsolowo:

chmod +x ŚCIEŻKA_DO_PLIKU

Czyli w moim przypadku:

chmod +x /home/mint/.gems/ruby/3.2.0/bin/jekyll

Potem ponownie używam bundle exec jekyll serve… I śmiga!

Zrzut ekranu z konsoli, pokazujący informację o załadowaniu strony na wewnętrzny serwer.

Mogłem sobie wyświetlić wierny podgląd bloga. A potem dokończyć w nim edycję tego wpisu i się nim podzielić :smile:

Sposób graficzny (na Mincie)

Na systemie Linux Mint, z którego korzystam, istnieje również prosty sposób na zmianę wykonywalności poprzez zwykłe klikanie. Osoby mniej konsolowe mogłyby to docenić.

W tym celu:

  • kopiuję pełną ścieżkę do pliku, ale bez /jekyll na końcu,
  • uruchamiam przeglądarkę plików i włączam tryb tekstowy górnego paska (przyciskiem z ikoną notesu z ołówkiem),
  • wklejam ścieżkę do paska i w ten sposób przechodzę do folderu z plikiem jekyll,
  • klikam go prawym przyciskiem myszy,
  • wybieram opcję Właściwości z menu,
  • w kolejnym oknie przechodzę do zakładki Uprawnienia i tam włączam opcję traktowania pliku jak programu (u dołu).

W tym przypadku konsola byłaby dużo szybsza niż klikanie. Ale wiem, że niektóre osoby reagują na nią alergicznie. Niech każdy ma co woli :wink:

Podsumowanie

Wykonywalność plików może prowadzić do denerwujących komunikatów, zwłaszcza jeśli jest przymusowo narzucana podczas kopiowania z pendrive’a o innym systemie plików.
Ale, jak widać w tym przypadku – czasem jej wyłączenie również może prowadzić do nieoczekiwanych błędów. W tym wypadku potrzebował jej plik z Jekyllem.

Na szczęście linuksowy program strace pozwolił w tym przypadku szybko i skutecznie dojść do źródła błędu. Od teraz mam złoty środek – wykonywalność włączoną tam, gdzie to konieczne, zaś przy pozostałych plikach wyłączoną.

Mam nadzieję, że ta opowieść z frontu pozwoli komuś uniknąć irytującego, enigmatycznego błędu. Albo przynajmniej da trochę rozrywki.

Swoją drogą widzę tu możliwość poprawienia czytelności błędów wewnątrz Bundlera – mógłby wspominać o braku potrzebnych pozwoleń, a nie o (drugorzędnym) braku plików w alternatywnych miejscach.
Może w wolnym czasie to zgłoszę jako drobną propozycję poprawki. W końcu świat open source to nieustanne szlifowanie i doskonalenie.

Dzięki za przeczytanie i do zobaczenia w kolejnych wpisach!