Michał Góral Blog about software https://blog.mgoral.org/index.xml Hugo -- gohugo.io pl-PL Tue, 04 Dec 2018 10:00:00 +0000 Tue, 04 Dec 2018 10:00:00 +0000 End of Dotfiles series <p>This series will be discontinued. I stopped writing at all because I couldn&rsquo;t motivate myself to write a next part of <em>Dotfiles</em> series and I felt that it should be finished before anything else. A year passed and exactly nothing happened with <em>Dotfiles</em>. I hope that existing 3 parts of will be useful for someone, but I won&rsquo;t go into this topic any further. Not that there is a lot more to say.</p> <p>Hopefully this way I&rsquo;ll be able to force myself to write more.</p> Tue, 04 Dec 2018 10:00:00 +0000 https://blog.mgoral.org/post/dotfiles-end/ https://blog.mgoral.org/post/dotfiles-end/ PGP key refreshed <p>I&rsquo;m in a process of moving this site and my mail addresses to the new domain: goral.net.pl. Due to this I added my new e-mails to PGP key. Old domain (mgoral.org) will continue to work work at least until mid-May 2019. After that I&rsquo;ll decide whether I need to continue renewing that domain or will it be abandoned.</p> <p>The key is still located at: <a href="https://mgoral.org/dld/michal.goral.pgp">https://mgoral.org/dld/michal.goral.pgp</a>. Fingerprint: <strong>0423 DE59 98D1 2C33 E599 CDCF E3DD DA4D C45F 58CB</strong> (not changed)</p> Mon, 03 Dec 2018 17:40:00 +0000 https://blog.mgoral.org/post/pgp-key-refreshed-2018/ https://blog.mgoral.org/post/pgp-key-refreshed-2018/ New Year's resolution <p>It&rsquo;s a little late to make a New Year&rsquo;s resolution, but I&rsquo;ll make one nevertheless. From now I&rsquo;m writing only in English - at least on this blog.</p> <p>This move is not about the audience, thought I admit - it&rsquo;d be nice to show some of my previous posts to non-Polish speakers (and as I&rsquo;m getting more and more confident about my work, I&rsquo;ll probably have something more to share in the future). It&rsquo;s a known fact that programming world is dominated by English so I often had to translate some phrases to Polish, at least if I didn&rsquo;t want my articles to turn into a weird mix of Polish and English words randomly thrown here and there (and I didn&rsquo;t, but often it was <a href="https://blog.mgoral.org/post/problem-dependant-names">unavoidable</a>). It&rsquo;s a burden because usually Polish lacks good equivalents for some particular words so I either had to skip these or come up with rough and unnatural neologisms. So I figured that using English all over the place will better fit the purpose of these posts.</p> <p>The only problem is my recent &ldquo;Dotfiles&rdquo; series. I don&rsquo;t want to leave it partially Polish and partially English and because I&rsquo;m not particularly interested in running multilingual blog, I&rsquo;ll probably start translating first three parts to English English in the first place.</p> <p>Stay tuned!</p> Mon, 05 Feb 2018 10:58:32 +0100 https://blog.mgoral.org/post/new-year-resolution/ https://blog.mgoral.org/post/new-year-resolution/ Dotfiles, część 3: GNU Stow <p>Od publikacji <a href="https://blog.mgoral.org/post/dotfiles-1">ostatniej części</a> serii <em>Dotfiles</em> minęło już trochę czasu i biję się sam ze sobą czy publikować kolejną. Powód jest prozaiczny - dwa poprzednie artykuły niemal wyczerpały temat mojego sposobu na zarządzanie plikami konfiguracyjnymi. Obiecywałem jednak, że opiszę pokrótce program GNU Stow oraz skrypty, które pomagają mi w sytuacjach kryzysowych, więc obietnicy dotrzymam. W tym odcinku przyjrzymy się bliżej wyśmienitemu programowi <a href="https://www.gnu.org/software/stow/">GNU Stow</a>, który odwala za mnie lwią część roboty.</p> <p></p> <h2 id="jak-działa-gnu-stow">Jak działa GNU Stow</h2> <p>GNU Stow jest managerem instalacji wielu programów (zwanych w nomenklaturze twórców programu &ldquo;paczkami&rdquo;) we wspólnych katalogach docelowych. Osiąga to poprzez tworzenie dowiązań symbolicznych do plików i katalogów położonych w innych lokacjach niż te, do których są instalowane.</p> <p>Czemu miałoby to być przydatniejsze niż zwyczajowe <code>make &amp;&amp; make install</code>? Żeby to lepiej zobrazować, posłużę się przykładem z manuala Stowa: gdybyśmy chcieli zainstalować Perla i Emacsa w katalogu <em>/usr/local</em>, musielibyśmy, między innymi, skopiować manuale obu tych programów do katalogu <em>/usr/local/man/man1</em>. Są to następujące pliki: a2p.1, ctags.1, emacs.1, etags.1, h2ph.1, perl.1 i s2p.1. Robi się tam śmietnik i w przypadku chęci odinstalowania lub aktualizacji np. Perla, administrator systemu musi ręcznie sprawdzić, który plik należy do Perla, a który do Emacsa.</p> <p>Stow ułatwia administrację instalowanymi programami dzięki instalacji każdego z nich w jego własnym poddrzewie katalogów, które jednak powinno odzwierciedlać strukturę wspólnego katalogu docelowego (<em>/usr/local</em>). Na przykład wspomniany Perl i Emacs mogłyby zostać zainstalowane do katalogów <em>/usr/local/stow/{perl,emacs}</em>, wewnątrz których znajdowałyby się takie katalogi jak <em>bin</em>, <em>man</em>, <em>share</em> itd. Następnie pliki wewnątrz tych katalogów zostałyby zsymlinkowane do <em>/usr/local/{bin,man,share}</em>.</p> <p>Wiele build systemów domyślnie tworzy poprawne poddrzewa katalogów, możliwe do użycia bez żadnych dodatkowych modyfikacji. Na przykład <em>autotools</em> obsługuje przełącznik <code>--prefix</code> oraz zmienną środowiskową <code>$DESTDIR</code>, które umożliwiają zainstalowanie poszczególnych fragmentów programu w katalogach <code>$DESTDIR$PREFIX/{bin,share,share/man}</code> (zwracam uwagę na brak separatora między zmiennymi <code>$DESTDIR</code> i <code>$PREFIX</code> związany z tym, że prefix jest ścieżką absolutną, domyślnie ustawioną na <em>/usr</em>).</p> <p>Dokumentacja Stowa sugeruje utworzenie jednego katalogu-repozytorium. Jest to o tyle wygodne, że wystarczy wejść do danego katalogu i wywołać komendę <code>stow &lt;package&gt;</code>, żeby zainstalować daną paczkę w katalogu o poziom wyżej (czyli wewnątrz rodzica bieżącego katalogu). Ta akcja, oprócz tego, że stworzy symlinki dla żądanej paczki, dodatkowo sprawdzi czy inne pliki w modyfikowanych katalogach są zarządzane przez GNU Stow i odpowiednio je zmodyfikuje, jeśli będzie taka potrzeba (GNU Stow stara się tworzyć jak najmniejszą liczbę dowiązań symbolicznych).</p> <p>Wszystkie akcje Stowa są przy tym domyślnie bezpieczne: program nie dotknie plików, które nie są przez niego zarządzane (czyli zwykłych plików oraz linków symbolicznych wskazujących gdzieś poza repozytorium Stowa).</p> <p>Aby usunąć daną paczkę (czyli usunąć jej dowiązania symboliczne), należy użyć opcji <code>-D</code>: <code>stow -D &lt;package&gt;</code>. Przeinstalowanie paczki wiąże się natomiast z użyciem opcji <code>-R</code>: <code>stow -R &lt;package&gt;</code>. Proste jak drut.</p> <h2 id="wykorzystanie-stowa">Wykorzystanie Stowa</h2> <p>Moje repozytorium dotfile&rsquo;ów jest w dużej mierze repozytorium paczek Stowa, które symlinkuję do katalogu domowego <code>$HOME</code>.</p> <p>Mój plik config.ini (dla programu config-manage opisanego w poprzednich częściach) posiada następującą sekcję domyślną:</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="k">[DEFAULT]</span> <span class="na">stow_install</span> <span class="o">=</span> <span class="s">stow -R ${targetname} -t ${var:home}</span> <span class="na">stow_uninstall</span> <span class="o">=</span> <span class="s">stow -D ${targetname} -t ${var:home}</span> <span class="na">install_cmd</span> <span class="o">=</span> <span class="s">${stow_install}</span> <span class="na">uninstall_cmd</span> <span class="o">=</span> <span class="s">${stow_uninstall}</span> </code></pre></div> <p>Dzięki temu nie muszę w ogóle tworzyć sekcji w pliku konfiguracyjnym dla prostych modułów, które muszą zaledwie zsymlinkować kilka plików - dla wszystkich innych modułów mogę natomiast użyć komend zapisanych pod zmiennymi <code>${stow_install}</code> i <code>${stow_uninstall}</code>, które automatycznie będą znały nazwę stow-owanych paczek.</p> <h2 id="w-następnej-części">W następnej części</h2> <p>W następnej części (lub częściach) opiszę skrypty pomocnicze dostarczane razem z frameworkiem konfiguracyjnym. Opiszę również sposób, w jaki konfiguracje są dzielone między różnymi komputerami i jak to wszystko jest razem połączone.</p> <p>Ciąg dalszy nastąpi&hellip;</p> Mon, 18 Dec 2017 22:23:00 +0200 https://blog.mgoral.org/post/dotfiles-3/ https://blog.mgoral.org/post/dotfiles-3/ PGP key refreshed <p>Due to the extension of expiration date of my signing key, I&rsquo;ve refreshed my public PGP key, both on the website and in key directories.</p> <p>The key is located at: <a href="https://mgoral.org/dld/michal.goral.pgp">https://mgoral.org/dld/michal.goral.pgp</a>. Fingerprint: <strong>0423 DE59 98D1 2C33 E599 CDCF E3DD DA4D C45F 58CB</strong></p> Thu, 16 Nov 2017 21:06:00 +0000 https://blog.mgoral.org/post/pgp-key-refreshed/ https://blog.mgoral.org/post/pgp-key-refreshed/ Dotfiles, część 2: argumenty <p>W <a href="https://blog.mgoral.org/post/dotfiles-1">poprzedniej części</a> przedstawiłem zarys sposobu działania <a href="https://git.mgoral.org/mgoral/config-framework/">frameworku</a> obsługującego instalację i zarządzanie zbiorami konfiguracji, którego jestem autorem, i którego używam na co dzień. W tej części opowiem bardziej szczegółowo o tym jak wywołuje się polecenia instalacji.</p> <p></p> <h2 id="potrzeba-argumentów-pozycyjnych">Potrzeba argumentów pozycyjnych</h2> <p>O tym, że instalacja modułów wywoływana jest przez polecenie <code>config-manage install</code>, a także o tym co wtedy dzieje się pod maską, nie będę przypominał, gdyż ten temat załatwiliśmy poprzednim razem. Czasem jednak chcielibyśmy dodatkowo sparametryzować wywołania instalatora. Na przykład, możemy zechcieć stworzyć<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup> moduł <em>pkg</em>, który będzie skarbnicą zasad kompilacji i instalacji paczek. Nie ma sensu przekompilowywać wszystkich paczek za każdym razem, gdyż zajęłoby to zbyt wiele czasu - kompilacja jest niezbędna tylko w przypadku braku danej paczki lub potrzeby jej aktualizacji.</p> <p><em>Summa summarum</em> config-manage to program do wywoływania szeregu komend. Dlatego niezbędna będzie nam komenda, która na podstawie przekazanych parametrów wywoła odpowiednie zadania kompilacji/instalacji dla konkretnej paczki. Będzie to parametryzowany skrypt, który umieścimy wewnątrz katalogu modułu. Drzewo plików może wówczas wyglądać następująco:</p> <pre><code>$HOME/config/ pkg/ firefox/ Makefile neovim/ Makefile other-program/ Makefile install config.ini </code></pre> <p>Sam skrypt <code>install</code> może zaś wyglądać na przykład tak:</p> <div class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span><span class="ch">#!/bin/bash</span> <span class="nv">DIR</span><span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span> <span class="nb">cd</span> <span class="s2">&quot;</span><span class="k">$(</span> dirname <span class="s2">&quot;</span><span class="si">${</span><span class="nv">BASH_SOURCE</span><span class="p">[0]</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">)</span><span class="s2">&quot;</span> <span class="o">&amp;&amp;</span> <span class="nb">pwd</span> <span class="k">)</span><span class="s2">&quot;</span> <span class="k">if</span> <span class="o">[[</span> <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> !<span class="o">=</span> <span class="s2">&quot;&quot;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span> <span class="k">for</span> name in <span class="nv">$@</span><span class="p">;</span> <span class="k">do</span> <span class="o">(</span><span class="nb">cd</span> <span class="s2">&quot;</span><span class="nv">$DIR</span><span class="s2">/</span><span class="nv">$name</span><span class="s2">&quot;</span> <span class="o">&amp;&amp;</span> make <span class="o">&amp;&amp;</span> make install<span class="o">)</span> <span class="k">done</span> <span class="nb">exit</span> <span class="k">fi</span> <span class="k">for</span> pkg in <span class="k">$(</span>find <span class="s2">&quot;</span><span class="si">${</span><span class="nv">DIR</span><span class="si">}</span><span class="s2">&quot;</span> -mindepth <span class="m">1</span> -maxdepth <span class="m">1</span> -type d<span class="k">)</span><span class="p">;</span> <span class="k">do</span> <span class="o">(</span><span class="nb">cd</span> <span class="s2">&quot;</span><span class="nv">$DIR</span><span class="s2">/</span><span class="nv">$pkg</span><span class="s2">&quot;</span> <span class="o">&amp;&amp;</span> make <span class="o">&amp;&amp;</span> make install<span class="o">)</span> <span class="k">done</span> </code></pre></div> <p>Jego jedynym zadaniem jest przeanalizowanie przekazanych mu nazw paczek i wywołanie dla nich poleceń <code>make</code> i <code>make install</code>, które zawierają ostateczny opis procedury kompilacji i instalacji. W przypadku gdy skrypt nie otrzyma żadnego parametru, rozpocznie kompilację wszystkich paczek znalezionych w tym samym katalogu, w którym się znajduje.</p> <p>Oczywiście skrypt taki może wywoływać dowolne inne polecenia i być w dowolnie skomplikowany - podałem jedynie łatwy do przetrawienia przykład.</p> <h2 id="wywołanie-skryptu-install">Wywołanie skryptu <code>install</code></h2> <p>Tak uzbrojeni, musimy teraz jedynie przekazać odpowiednie argumenty do skryptu <code>install</code>. Z poziomu konsoli wywołalibyśmy go np. poleceniem <code>pkg/install neovim</code> albo <code>pkg/install firefox</code>. W przypadku programu config-manage sprawa jest nieco bardziej skomplikowana, ale nie znów aż tak bardzo: dla każdego argumentu polecenia <code>install</code> lub <code>uninstall</code> można podać po dwukropku dodatkowe argumenty, które zostaną przypisane do specjalnej zmiennej <code>${posargs}</code> dostępnej wewnątrz pliku konfiguracyjnego. Sekcja pliku config.ini dla modułu pkg wyglądać będzie w naszym przypadku następująco:</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="k">[pkg]</span> <span class="na">install_cmd</span> <span class="o">=</span> <span class="s">${targetname}/install ${posargs}</span> </code></pre></div> <p>Jeśli <code>install_cmd</code> zawierałoby więcej komend, to każda z nich mogłaby przyjmować zmienną <code>${posargs}</code> (co jednak wielce prawdopodobnie nie ma większego sensu).</p> <p>Samo wywołanie config-manage wyglądać będzie następująco (zwracam uwagę na drugie wywołanie: jeśli potrzebujemy rozdzielić spacjami część argumentów/opcji, musimy całość ująć w cudzysłów tak, aby słowo <em>firefox</em> zostało potraktowane jako część wywołania modułu pkg, a nie oddzielnego modułu o nazwie firefox):</p> <div class="highlight"><pre><code class="language-shell" data-lang="shell"><span></span>$ config-manage install pkg:neovim pkg:firefox $ config-manage install <span class="s2">&quot;pkg:neovim firefox&quot;</span> </code></pre></div> <p>Możemy pójść o krok dalej i połączyć zasady automatycznej kompilacji paczek i instalacji ich plików konfiguracyjnych. Jak? W bardzo prosty sposób: poprzez rekursywne wywołanie config-manage z wnętrza konfiguracji modułu. Poniżej przedstawiam przykład instalacji programu neovim:</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="k">[neovim]</span> <span class="na">install_cmd</span> <span class="o">=</span> <span class="s">which nvim &gt; /dev/null || config-manage install pkg:neovim</span> <span class="s"> ${stow_install}</span> <span class="s"> nvim -c &quot;:PlugClean!&quot; -c &quot;:PlugUpdate&quot; -c &quot;:PlugInstall&quot; -c &quot;:qa!&quot;</span> </code></pre></div> <p>Powyższa zasada wykonuje następujące kroki:</p> <ol> <li>sprawdza, czy program nvim jest dostępny w <code>$PATH</code> użytkownika, a jeśli nie jest, to go próbuje zainstalować przy pomocy modułu pkg;</li> <li>tworzy odpowiednie dowiązania symboliczne przy pomocy GNU Stow;</li> <li>instaluje pluginy vima.</li> </ol> <p>Pragnę przy okazji zaznaczyć, że wywołanie instalacji paczki w pierwszej komendzie jako rekursywnego config-manage jest preferowane w stosunku do zwykłego wywołania skryptu <code>pkg/install</code> z tego powodu, że zasada modułu pkg może wykonywać jeszcze jakieś dodatkowe czynności (np. sprawdzanie wersji, instalacja zależności), których nie ma sensu dublować w opisach innych modułów.</p> <h2 id="w-następnej-części">W następnej części</h2> <p>W gruncie rzeczy przekazywanie argumentów do modułów jest proste i intuicyjne, żeby nie powiedzieć &ldquo;banalne&rdquo;, jednak wymagało omówienia, głównie z tego powodu, że zarządzanie instalacją oprogramowania, które nie znajduje się w repozytoriach systemowych to rzecz spora, a config-manage nieco ją uprzyjemnia.</p> <p>W następnych częściach przyjrzymy się bliżej programowi GNU Stow oraz skryptom pomocniczym, które są dostarczane razem z frameworkiem. Opiszę także w jaki sposób utrzymuję spójne repozytorium z plikami konfiguracyjnym: jak są one dzielone między różnymi komputerami i jak owo repozytorium jest połączone z frameworkiem.</p> <p>Ciąg dalszy nastąpi&hellip;</p> <div class="footnotes"> <hr /> <ol> <li id="fn:1">czyt.: używam takiego modułu na co dzień. <a class="footnote-return" href="#fnref:1">↩</a></li> </ol> </div> Tue, 08 Aug 2017 22:24:00 +0200 https://blog.mgoral.org/post/dotfiles-2/ https://blog.mgoral.org/post/dotfiles-2/ Dotfiles, część 1: config-manage <p>Zarządzanie konfiguracją komputerów jest skomplikowane, a sprawa dodatkowo nabiera kolorytu wraz ze wzrostem liczby obsługiwanych komputerów osobistych, włączając w to telefony komórkowe. Każdy z nich trochę się różni: a to wersją systemu operacyjnego, a to ilością podpiętych monitorów, a to bebechami. System operacyjny wiele rzeczy przykrywa pod warstewką spójnego interfejsu, jednak część różnic konfiguracja użytkownika musi uwzględniać.</p> <p></p> <p>Prostota przystosowania konfiguracji do wielu urządzeń to nie jedyna z pożądanych cech systemu służącego do zarządzani zbiorem konfiguracji. Innymi cechami są łatwość instalacji/usunięcia pewnych części konfiguracji (np. usunięcia plików konfiguracyjnych dla programu, którego nie będziemy więcej używać), a także prostota definiowania zasad opisujących sam proces instalacji &ndash; skrypty sh tudzież basha, poza tymi najbardziej trywialnymi, mają to do siebie, że szybko stają się nieutrzymywalnymi koszmarkami: wodospadami wywołań komend przekazywanych do grepa, seda i awka wywoływanych z niezrozumiałymi wyrażeniami regularnymi, wyrytych na stałe w spaghetti if-elsów i swich-case&rsquo;ów.</p> <h2 id="nieco-historii">Nieco historii</h2> <p>Przez ostatnie 4 lata tworzyłem (nadal tworzę) system umożliwiający mi zarządzanie konfiguracjami w prosty i czytelny sposób. Celowo używam słowa &ldquo;konfiguracjami&rdquo;, a nie &ldquo;plikami konfiguracjami&rdquo;, gdyż konfiguracja jest pojęciem szerszym. Owszem, są to również pliki tekstowe zawierające opis najczęściej używanych opcji różnych programów, lecz oprócz tego są to na przykład reguły instalacji oprogramowania, komendy vima (<code>vim -c</code>), czy też komendy kompilujące i patchujące kod źródłowy. &ldquo;Konfiguracja&rdquo; to w tym kontekście ujęcie całościowe sposobu działania komputera dla danego użytkownika.</p> <p>&ldquo;Framework&rdquo;, którego dziś używam, przez ostatnie lata ewoluował i w niczym nie przypomina kilku swoich poprzednich wersji. Część zmian zresztą nie ostała się nawet w historii gita; mam za sobą dość długi epizod tworzenia brancha dla każdej maszyny z osobna i cherry-pickowania między nimi części wspólnych (koszmar), który zakończył się usunięciem wzmiankowanych branchy.</p> <p>W pierwszej wersji ów system był właśnie wspomnianym zestawem skryptów shellowych &ndash; każdy we własnym katalogu &ndash; zarządzanych przez skrypt centralny oraz &ldquo;bibliotekę&rdquo; wspólnych funkcji (np. do patchowania kodu, wyświetlania diffów, czy interaktywnego menu wyświetlającego się od wielkiego dzwonu). Każdy skrypt instalacyjny posiadał zatem obowiązkowe dwie pierwsze linijki: określenie swojej ścieżki bezwzględnej i source&rsquo;owanie owej nieszczęsnej &ldquo;biblioteki&rdquo;:</p> <div class="highlight"><pre><code class="language-bash" data-lang="bash"><span></span><span class="nv">DIR</span><span class="o">=</span><span class="s2">&quot;</span><span class="k">$(</span> <span class="nb">cd</span> <span class="s2">&quot;</span><span class="k">$(</span> dirname <span class="s2">&quot;</span><span class="si">${</span><span class="nv">BASH_SOURCE</span><span class="p">[0]</span><span class="si">}</span><span class="s2">&quot;</span> <span class="k">)</span><span class="s2">&quot;</span> <span class="o">&amp;&amp;</span> <span class="nb">pwd</span> <span class="k">)</span><span class="s2">&quot;</span> . <span class="s2">&quot;</span><span class="nv">$DIR</span><span class="s2">/../libconfig&quot;</span> </code></pre></div> <p>Następnie następowała seria wywołań magicznych funkcji, które miały za zadanie &ldquo;bezpiecznie&rdquo; kopiować i usuwać pliki, zmieniać ich nazwy, tworzyć linki symboliczne itp. Ciekawe było, że część funkcji tworzyła linki symboliczne, podczas gdy inna część tworzyła zwykłe kopie plików, przez co zmiany nie były natychmiastowo odzwierciedlane w repozytorium.</p> <p>System ten istniał sobie dość długo, bo blisko 3 lata. Przez ten czas ewoluowała głównie biblioteka. Funkcja usuwająca pliki stała się bezpieczniejsza po tym jak przez przypadek usunęła ich zbyt wiele. Funkcja tworząca linki symboliczne nie zawsze chciała je tworzyć. Dopisane zostało wykrywanie już zainstalowanych plików konfiguracyjnych i interaktywne potwierdzanie ich nadpisywania. Takie buty.</p> <p>Potem zaś nadeszła rewolucja &ndash; spowodowana głównie niemożliwością utrzymania różnych konfiguracji dla różnych komputerów.</p> <h2 id="operacja-na-otwartym-sercu">Operacja na otwartym sercu</h2> <p>Wszystkie skrypty, łącznie z łamagowatą biblioteką, zostały wysadzone w puch, a ich miejsce zajął tandem <a href="https://git.mgoral.org/mgoral/config-framework/src/c0dff363316f02e13ccfddd163d424bd904346db/config-manage">config-manage</a><sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup> i <a href="https://www.gnu.org/software/stow/">GNU Stow</a><sup class="footnote-ref" id="fnref:2"><a rel="footnote" href="#fn:2">2</a></sup>. Skrypt config-manage ma za zadanie wykonywać polecenia zapisane w pliku konfiguracyjnym za pomocą prostego do zrozumienia i edycji formatu ini. GNU Stow jest natomiast managerem dowiązań symbolicznych - to on wykonuje lwią część pracy polegającej na odpowiednim ulokowaniu plików konfiguracyjnych.</p> <p>Repozytorium nadal jest podzielone na katalogi odpowiadające instalowalnym modułom. Na przykład może ono wyglądać następująco:</p> <pre><code>$HOME/config/ home/ .local/bin/ script1 script2 .aliases .xsessionrc self/ -&gt; .config-framework/self zsh/ .zprofile .zshenv .zshrc .config-framework self/ .local/bin/ cmpver config-manage -&gt; ../../../config-manage .zsh/completion/ _config-manage config-manage config.ini </code></pre> <p>GNU Stow odpowiada za odwzorowanie wewnątrz katalogu domowego struktury plików i katalogów z każdego modułu. Zatem po zainstalowaniu modułów <code>home</code>, <code>self</code> i <code>zsh</code>, katalog domowy będzie miał następującą zawartość:</p> <pre><code>$HOME/ .local/bin cmpver -&gt; ../../config/self/.local/bin/cmpver config-manage -&gt; ../../config/self/.local/bin/config-manage script1 -&gt; ../../config/home/.local/bin/script1 script2 -&gt; ../../config/home/.local/bin/script2 .zsh/completion/ -&gt; ../../config/self/.zsh/completion .aliases -&gt; ../../config/home/.aliases .xsessionrc -&gt; ../../config/home/.xsessionrc .zprofile -&gt; ../../config/zsh/.zprofile .zshenv -&gt; ../../config/zsh/.zshenv .zshrc -&gt; ../../config/zsh/.zshrc </code></pre> <p>Szczególnie warto zwrócić uwagę na <code>.zsh/completion/</code> &ndash; w tej chwili jest to link symboliczny do katalogu, jednak gdyby więcej modułów zdefiniowało skrypty autouzupełniania dla zsh, wówczas GNU Stow utworzyłby w jego miejscu katalog, a w nim odpowiednie dowiązania.</p> <h2 id="config-manage">config-manage</h2> <p>Skryptem, który zajmuje się wprawianiem maszynerii w ruch jest config-manage. Jego zadaniem jest zmiana katalogu roboczego, ustawienie kilku zmiennych dostępnych w pliku konfiguracyjnym i koniec końców odczyt i wykonanie zadań zapisanych w pliku config.ini - pierwszym znalezionym w bieżącym katalogu lub dowolnym katalogu nadrzędnym. Nie będę przytaczał tutaj kodu źródłowego samego skryptu, gdyż jest on dostępny w <a href="https://git.mgoral.org/mgoral/config-framework">repozytorium</a>, które specjalnie na tę okazję stworzyłem.</p> <p>Repozytorium to jest stworzone z myślą o uwzględnieniu go w repozytorium konfiguracji jako submoduł gita. Domyślnie posiada moduł o nazwie self, który zainstaluje skrypt config-manage oraz dodatkowe skrypty pomocnicze w katalogu <code>.local/bin</code></p> <p>config-manage posiada dwie podkomendy służace do instalacji i deinstalacji modułów: <code>install</code> i <code>uninstall</code>. Obie po prostu przyjmują listę nazw modułów<sup class="footnote-ref" id="fnref:3"><a rel="footnote" href="#fn:3">3</a></sup>. Domyślne zasady (komendy) dlań wywoływane znajdują się w sekcji <code>[DEFAULT]</code> pliku config.ini. Sekcja <code>[DEFAULT]</code> zawiera zresztą domyślne pola dla każdej sekcji pliku konfiguracyjnego, a nawet jeśli dany moduł nie posiada odpowiadającej mu sekcji, to zostanie w zamian użyta sekcja domyślna. Oznacza to, że jeśli zadowolimy się ustawieniami domyślnimi (czyli jedynie instalacją plików konfiguracyjnych tu i ówdzie), to w ogóle nie trzeba się przejmować istnieniem pliku config.ini. Dodatkowo dla wygody w sekcji domyślnej utworzone są dwie dodatkowe zmienne, które mogą zostać użyte gdziekolwiek: <code>${stow_install}</code> i <code>${stow_uninstall}</code>. Ułatwiają one pisanie zasad instalacji modułów, w których wywołanie programu Stow jest zledwie jednym z kilku kroków.</p> <p>Spójrzmy jak może wyglądać przykładowy plik config.ini:</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="k">[DEFAULT]</span> <span class="na">stow_install</span> <span class="o">=</span> <span class="s">stow -R ${targetname}</span> <span class="na">stow_uninstall</span> <span class="o">=</span> <span class="s">stow -D ${targetname}</span> <span class="na">install_cmd</span> <span class="o">=</span> <span class="s">${stow_install}</span> <span class="na">uninstall_cmd</span> <span class="o">=</span> <span class="s">${stow_uninstall}</span> <span class="k">[self]</span> <span class="c1"># disable uninstall</span> <span class="na">uninstall_cmd</span> <span class="o">=</span> <span class="k">[home]</span> <span class="na">install_cmd</span> <span class="o">=</span> <span class="s">${stow_install}</span> <span class="s"> script1 ${env:PWD}</span> <span class="s"> ls ${var:home}/.local/bin</span> </code></pre></div> <p>Jego treść mówi raczej sama za siebie, jednak dla jasności opiszę co się dzieje w jego poszczególnych fragmentach.</p> <p>Najpierw zostały zdefiniowane domyśle zasady instalowania i odinstalowywania modułów. Następnie, w sekcji <code>[self]</code>, określamy, że chcemy moduł o takiej nazwie jedynie instalować, uniemożliwiając jego usunięcie.</p> <p>W sekcji <code>[home]</code> pozostawiamy domyślne zasady deinstalacji programów, jednak przy instalacji chcemy, oprócz utworzenia odpowiednich linków symbolicznych, wykonać uruchomienie testowe jednego z instalowanych skryptów, a następnie wylistowanie katalogu, w którym został zainstalowany. Wobec tego przekazujemy konkretne polecenia do pola <code>install_cmd</code>, każde w nowej linii. Teraz config-manage będzie wiedział, że przy instalacji musi wykonać więcej niż jedną komendę, jednak niepowodzenie dowolnej z nich (sygnalizowane przez zwrócenie statusu innego niż 0) zakończy wykonywanie całego &ldquo;skryptu&rdquo;.</p> <p>Warto jeszcze zatrzymać się nad zmiennymi dostępnymi w pliku konfiguracyjnym, jest ich bowiem kilka:</p> <ul> <li><code>${targetname}</code> - specjalna zmienna dodana przez skrypt config-manage, dostępna wewnątrz danej sekcji i zawierająca jej nazwę.</li> <li><code>${var:...}</code> - kilka zmiennych pomocniczych dodanych przez config-manage. Należą do nich: <code>${var:home}</code> (ścieżka bezwzględna do katalogu domowego) oraz <code>${var:workdir}</code> (ścieżka bezwzględna do katalogu, w którym rezyduje skrypt config-manage). Dostępne są w każdej sekcji.</li> <li><code>${env:...}</code> - wszystkie zmienne środowiskowe ustawione w momencie wywoływania config-manage. Dostępne w każdej sekcji.</li> </ul> <h2 id="w-następnej-części">W następnej części</h2> <p>To właściwie podsumowuje podstawowe założenia filozofii, którą się kierowałem przy tworzeniu mojego autorskiego frameworku do zarządzania konfiguracjami. Ponieważ artykuł mocno już napęczniał, to w tym miejscu się zatrzymamy &ndash; informacji do przetrawienia jest aż nadto. W następnej części opiszę sposób przekazywania argumentów do zasad instalacyjnych oraz podam przykłady modułów, w których może się to przydać.</p> <p>Ciąg dalszy nastąpi&hellip;</p> <div class="footnotes"> <hr /> <ol> <li id="fn:1"><p>W tej części opiszę jedynie najprostszy przypadek - zarządzanie plikami konfiguracyjnymi. Dodatkowe informacje na temat frameworku znajdują się w <a href="https://blog.mgoral.org/post/dotfiles-2">części 2</a></p> <a class="footnote-return" href="#fnref:1">↩</a></li> <li id="fn:2">Opis programu GNU Stow znajduje się w <a href="https://blog.mgoral.org/post/dotfiles-3">części 3</a> <a class="footnote-return" href="#fnref:2">↩</a></li> <li id="fn:3">To nie do końca prawda, ale opowiem o tym następnym razem. <a class="footnote-return" href="#fnref:3">↩</a></li> </ol> </div> Sat, 29 Jul 2017 22:24:00 +0200 https://blog.mgoral.org/post/dotfiles-1/ https://blog.mgoral.org/post/dotfiles-1/ About me <figure class="wp-caption alignleft"> <img class="wp-image" src="https://blog.mgoral.org/img/miniportret.png"> </figure> <p><strong>Michał Góral</strong>. I&rsquo;m a programmer, hobby guitarist, hard sci-fi reader, <a href="https://en.wikipedia.org/wiki/The_Free_Software_Definition">Free Software</a> advocate, as well as a faithful fan and user of Debian. Even though I don&rsquo;t have beard, most of the time I&rsquo;m found in terminal instead of a nice graphical environment<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup>.</p> <p>I use Free Software a lot. I try to give back to the community as much as I can. Among many bigger and smaller projects which I created or co-created, there are few which I don&rsquo;t have to be ashamed of (hopefully):</p> <ul> <li><a href="https://github.com/mgoral/subconvert">Subconvert</a> - movie subtitles converter and editor;</li> <li><a href="https://gitlab.com/mgoral/feed-commas">Feed Commas</a> - CommaFeed&rsquo;s (RSS aggregator and reader) console client;</li> <li><a href="https://gitlab.com/mgoral/quicksend">Quicksend</a> - console program for quick sending messages via different protocols;</li> <li><a href="https://gitlab.com/mgoral/commenter">Commenter</a> - filter used for commenting-out and uncommenting blocks of text;</li> <li><a href="https://gitlab.com/mgoral/awh">AnyWebHook</a> - generic web hook, or rather a framework for writing your own web hooks.</li> </ul> <p>You can mail me: <strong>dev</strong> at the same domain as this blog. Mysterious strangers can encrypt their e-mails. My <a href="https://mgoral.org/dld/michal.goral.pgp">PGP key</a> is available to download from this site or from the most of popular key servers.</p> <div class="footnotes"> <hr /> <ol> <li id="fn:1"><p>Not entirely true. X server runs all the time, but its main client is usually terminal emulator.</p> <a class="footnote-return" href="#fnref:1">↩</a></li> </ol> </div> Fri, 21 Jul 2017 22:10:00 +0000 https://blog.mgoral.org/mg/ https://blog.mgoral.org/mg/ Git Credential Helper <p>Git ciągle mnie zaskakuje modularnością i rozszerzalnością swojej architektury. W pracy mam standardowe zablokowane porty SSH, które są używane przez większość popularnych serwisów hostujących serwery gita (Giltab, Github); również mój prywatny serwer stoi na niestandardowym porcie, który padł ofiarą działu IT. W związku z tym, nie mogę korzystać z logowania przy pomocy kluczy SSH i od kilku lat, kiedy tylko chcę uzyskać dostęp do prywatnego repozytorium lub spushować jakąś zmianę, muszę korzystać z komunikacji przez HTTPS i wpisywać swoją nazwę użytkownika i hasło.</p> <p></p> <h2 id="słabe-rozwiązanie-cache">Słabe rozwiązanie: cache</h2> <p>Jakiś czas temu nieco ułatwiłem sobie to żmudne zadanie ustawiając cache&rsquo;owanie haseł:</p> <div class="highlight"><pre><code class="language-sh" data-lang="sh"><span></span>$ git config --global credential.helper <span class="s1">&#39;cache --timeout=3600&#39;</span> </code></pre></div> <p>Okazuje się jednak, że nie do końca pojąłem pełną złożoność tego ustawienia. <a href="https://git-scm.com/docs/git-credential-cache"><code>cache</code></a> jest bowiem jedynie programem rozprowadzanym wraz z gitem (<em>git-credential-cache</em>), który jest wywoływany z tego powodu, że został ustawiony jako wartość pola <em>helper</em> sekcji <em>[credential]</em> w pliku konfiguracyjnym gita. Może to być jednak dowolne inne polecenie, które wypisze na standardowe wyjście odpowiednio sformatowane dane logowania.</p> <h2 id="dobre-rozwiązanie-własny-pomocnik">Dobre rozwiązanie: własny pomocnik</h2> <p>Na co dzień do zarządzania hasłami korzystam z programu <a href="pass">pass</a>, który szyfrowanie haseł deleguje do gpg, a sam jedynie zarządza zaszyfrowanymi plikami z hasłami. Jest to niezwykle wygodne narzędzie z tego względu, że w naturalny sposób integruje się z systemem operacyjnym &ndash; dla każdego programu, który jest w stanie wywołać dowolne polecenie shella możemy w prosty sposób napisać manager haseł. Dzięki temu, że pass korzysta bezpośrednio z gpg, możemy użyć gpg-agenta, który, w zależności od konfiguracji, będzie w odpowiedni sposób pytał i przechowywał hasła kluczy PGP.</p> <p>Wobec powyższego, naturalnym dla mnie przykładem będzie napisanie prostego skryptu-pomocnika konwertującego wyjście programu pass na format akceptowany przez gita.</p> <p>W skrócie: git akceptuje listę <code>klucz=wartość</code>, gdzie każda para zakończona jest znakiem nowej linii, zaś pole <em>klucz</em> przyjmuje jedną z następujących wartości: <code>protocol</code>, <code>host</code>, <code>path</code>, <code>username</code>, <code>password</code>. Lista par nie musi zawierać wszystkich wartości, szczególnie że nie zawsze mają one sens. W naszym przypadku zwrócenie pól <code>username</code> i <code>password</code> w zupełności wystarcza.</p> <p>Przykład owej listy, którą powinien zwracać helper:</p> <pre><code>username=johnny password=haxorpass </code></pre> <p>Format passa zaś jest wymuszony architekturą samego programu; pierwsza linijka to hasło, pozostałe dane to metadane, które generalnie są ignorowane przez passa jako takie, jednak można je wypisać na ekran przy pomocy komendy <code>pass show &lt;nazwa&gt;</code>:</p> <pre><code>haxorpass username: johnny </code></pre> <p>Wobec bijącego po oczach podobieństwa, zamiana jednego formatu na drugi to prosta transformacja, którą można wykonać przy pomocy jednego obrotu seda. Zanim jednak podam kod programu, musimy się zaznajomić ze sposobem, w jaki git będzie go uruchamiał.</p> <h2 id="credential-helper">credential.helper</h2> <p>Jak już na początku wspomniałem, git wykona komendę zapisaną w polu <em>helper</em> w sekcji <em>[credential]</em> konfiguracji<sup class="footnote-ref" id="fnref:1"><a rel="footnote" href="#fn:1">1</a></sup>. Nie jest to jednak pełna informacja, gdyż przed wykonaniem git wykonuje pewną transformację wpisu:</p> <ol> <li><p>jeśli wpis rozpoczyna się wykrzyknikiem, to wszystko po nim jest traktowane jako kod shella i zostaje użyte tak jak zostało wpisane;</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="na">helper</span> <span class="o">=</span> <span class="s">!f() { echo &quot;password=abc&quot; }; f</span> </code></pre></div></li> <li><p>jeśli wpis jest ścieżką bezwzględną, zostanie on użyty jako wywoływane polecenie tak, jak został wpisany;</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="na">helper</span> <span class="o">=</span> <span class="s">/usr/local/bin/foobar</span> </code></pre></div></li> <li><p>w przeciwnym wypadku (gdy wpis jest ścieżką względną), zostanie on wywołany jako <code>git credential-$NAME</code> (czyli w $PATH musi być dostępny program o nazwie &ldquo;git-credential-$NAME&rdquo;).</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="na">helper</span> <span class="o">=</span> <span class="s">foobar</span> </code></pre></div></li> </ol> <p>Do wynikowego polecenia git następnie dopisuje, jako ostatni argument, typ operacji:</p> <ul> <li><em>get</em> &ndash; prośba o zwrócenie danych logowania</li> <li><em>store</em> &ndash; prośba o zapisanie danych logowania wewnątrz programu</li> <li><em>erase</em> &ndash; prośba o usunięcie zapisanych danych logowania</li> </ul> <p>Na przykład, dla <code>helper = foo</code> git wywoła polecenie <code>git credential-foo get</code>, ale dla <code>helper = !foo</code> wywoła <code>foo get</code>.</p> <h2 id="skrypt">Skrypt</h2> <p>Uzbrojeni w tę wiedzę możemy stworzyć skrypt i odpowiednio skonfigurować gita. Zacznijmy od konfiguracji gita. Jeśli skrypt nazwiemy <code>git-credential-pass</code> i umieścimy go w $PATH, to dla interesujących nas serwisów możemy dodać następujące pola konfiuracyjne:</p> <div class="highlight"><pre><code class="language-ini" data-lang="ini"><span></span><span class="k">[credential &quot;https://gitlab.com&quot;]</span> <span class="na">helper</span> <span class="o">=</span> <span class="s">pass my-credentials/gitlab.com</span> <span class="k">[credential &quot;https://github.com&quot;]</span> <span class="na">helper</span> <span class="o">=</span> <span class="s">pass my-credentials/github.com</span> </code></pre></div> <p>Sam skrypt zaś to proste filtrowanie wyjścia passa, o którym mówiłem już wcześniej. Należy umieścić go gdzieś w $PATH lub $GIT_EXEC_PATH:</p> <div class="highlight"><pre><code class="language-sh" data-lang="sh"><span></span><span class="ch">#!/bin/sh</span> <span class="nb">test</span> <span class="s2">&quot;</span><span class="nv">$2</span><span class="s2">&quot;</span> <span class="o">=</span> <span class="s2">&quot;get&quot;</span> <span class="o">||</span> <span class="nb">exit</span> <span class="m">0</span> pass show <span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span> <span class="p">|</span> sed -e <span class="s1">&#39;1 s/^\(.*\)$/password=\1/&#39;</span> <span class="se">\</span> -e <span class="s1">&#39;2,$ s/username: \?/username=/&#39;</span> </code></pre></div> <p>Więcej szczegółów, jak zwykle, w <a href="https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html#_credential_helpers">dokumentacji</a><sup class="footnote-ref" id="fnref:2"><a rel="footnote" href="#fn:2">2</a></sup>.</p> <!-- vim: set tw=80 colorcolumn=81: --> <div class="footnotes"> <hr /> <ol> <li id="fn:1"><p>Git sprawdzi najpierw lokalną konfigurację beżącego repozytorium zapisaną w pliku <code>.git/config</code>, a następnie konfigurację globalną w <code>~/.gitconfig</code>.</p> <a class="footnote-return" href="#fnref:1">↩</a></li> <li id="fn:2"><p>Dokumentację znalazłem jedynie na serwerze Linuksa &ndash; ta z git-scm.com jest niedostępna.</p> <a class="footnote-return" href="#fnref:2">↩</a></li> </ol> </div> Mon, 10 Apr 2017 14:36:37 +0200 https://blog.mgoral.org/post/git-credential-helper/ https://blog.mgoral.org/post/git-credential-helper/ I love Free Software <p><a href="http://fsfe.org/campaigns/ilovefs/index.en.html"> <figure class="wp-caption aligncenter"> <img class="wp-image" src="https://blog.mgoral.org/img/posts/ilovefs.png"> </figure> </a></p> <p>Z okazji walentynek chciałbym złożyć najserdeczniejsze życzenia wszystkim twórcom Wolnego Oprogramowania. Nie ma lepszego dnia, żeby okazać swoją miłość. Dzięki!</p> <p><em>Baner na górze ninejszej notki pochodzi ze strony kampanii &ldquo;<a href="http://fsfe.org/campaigns/ilovefs/index.en.html">I love Free Software</a>&rdquo; zorganizowanej przez Free Software Foundation Europe. Właścicielem praw autorskich do niego jest FSFE. Kopiowanie dozwolone: Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.</em></p> Tue, 14 Feb 2017 20:40:16 +0100 https://blog.mgoral.org/post/ilovefs/ https://blog.mgoral.org/post/ilovefs/