Nix学習備忘録Part 2-1-2: GitHubのzshプラグインを利用する

2022-01-31T20:37:15Z

1 はじめに

本記事ではHome Managerを用いて導入したZ ShellでGitHub上にあるプラグインを利用する方法について書きます。 Home Mangerを用いたzshの導入がお済みでない方は、前回の記事を先にご覧ください。

また、GitHub上のオープンソースを利用させて頂き、 lsコマンドの見た目も整えてみたいと思います。

2 外部パッケージの導入方法

基本的にはpkgs.fetchFromGitHub関数を用いて、 GitHubからソースを引っ張ってきます。 let ~ in ...式を用いて、パッケージをインストールします。 let式の中にはパッケージの情報を持つ定数を記述します。 そのフォーマットが以下の通りです:

variableName = {
  name = "プラグイン名";
  src = pkgs.fetchFromGitHub {
    owner = "作成者のGitHubユーザ名";
    repo = "プラグインのリポジトリ名";
    rev = "コミットID";
    sha256 = "Nixが生成するハッシュ値";
  };
};

sha256以外はすぐにイメージが付くと思います。 一番戸惑うのがsha256ですが、これはNixが自動で生成してくれるハッシュを記述します。 Nixの記事では多くの場合、"00000000000000000000000000000000000000000000"などの適当な値で束縛している例をよく見ますが、 一応lib.fakeSha256という関数が用意されており、この関数を使えば桁数を覚えておく必要も無いので、 私は最初にこのlib.fakeSha256という関数を利用しています。

zshプラグインの場合、home.nixzsh内に以下のように記述します:

      plugins =
        let
          plugin1 = {
            name = "plugin1";
            src = pkgs.fetchFromGitHub {
              owner = "someone";
              repo = "repository1";
              rev = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
              sha256 = "mrPMgwVkqOlKjvy1106MUKFjfkslajfjfkdslasjdkfj";
            };
          };
          plugin2 = {
            name = "plugin2";
            src = pkgs.fetchFromGitHub {
              owner = "somebody";
              repo = "repository2";
              rev = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
              sha256 = "KLUYpUu4DHRumQZ3w59m9aTW6TBKMCXl2UcKihfjdhsj";
            };
          };
        in [
          plugin1
          plugin2
        ];

ざっくり説明すると、plugins = [];というリスト形式で宣言するものを、 let ~ in ...式を用いて、 プラグインを定義してから、リストに入れるということをしています。

では、早速autojumpというzshのプラグインを入れてみましょう。

2.1 autojump: 曖昧な名前でディレクトリ移動を可能にする

autojumpは一度訪問したことのあるディレクトリであれば、 曖昧な名前で移動できるようになるという便利なプラグインです。 GitHubリポジトリはwting/autojumpです。

2022/01/31現在の最新バージョンを上のフォーマットを参考にして設定してみます。 記述するのはhome.nixzshの中です。

      plugins =
        let
          autojump = {
            name = "autojump";
            src = pkgs.fetchFromGitHub {
              owner = "wting";
              repo = "autojump";
              rev = "06e082c91805cb022900819b2e0881eeae780d58";
              sha256 = lib.fakeSha256;
            };
          };
        in [
          autojump
        ];

これでhome-manager switchを実行すると、sha256の値が適当であるため、エラーが出ます:

$ home-manager switch
these 3 derivations will be built:
  /nix/store/dvhngvpnk39ib19i9mlh8m89j621nykx-source.drv
  /nix/store/2a26hw00za9x5bpwfmxglq06v6dksdw4-home-manager-files.drv
  /nix/store/2l3b8vl5bfkcjspd56cimzw7wxnwyfj6-home-manager-generation.drv
building '/nix/store/dvhngvpnk39ib19i9mlh8m89j621nykx-source.drv'...

trying https://github.com/wting/autojump/archive/06e082c91805cb022900819b2e0881eeae780d58.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   156    0   156    0     0    491      0 --:--:-- --:--:-- --:--:--   490
100 55488    0 55488    0     0  79451      0 --:--:-- --:--:-- --:--:-- 79451
unpacking source archive /tmp/nix-build-source.drv-0/06e082c91805cb022900819b2e0881eeae780d58.tar.gz
error: hash mismatch in fixed-output derivation '/nix/store/dvhngvpnk39ib19i9mlh8m89j621nykx-source.drv':
         specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
            got:    sha256-mrPMgwVkqOlKjvy1106MUKF7OlEtKdt8E9mqCg7U9+U=
error: 1 dependencies of derivation '/nix/store/2a26hw00za9x5bpwfmxglq06v6dksdw4-home-manager-files.drv' failed to build
error: 1 dependencies of derivation '/nix/store/2l3b8vl5bfkcjspd56cimzw7wxnwyfj6-home-manager-generation.drv' failed to build

エラーメッセージの以下の部分を見ます:

         specified: sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
            got:    sha256-mrPMgwVkqOlKjvy1106MUKF7OlEtKdt8E9mqCg7U9+U=

mrPMgwVkqOlKjvy1106MUKF7OlEtKdt8E9mqCg7U9+U=というsha256の値を期待したのに、 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=という値を受け取ったというエラーです。 つまり、期待されているハッシュ値をgotのところで教えてくれています。 これを先ほどのlib.fakeSha256のところに設定し直します:

      plugins =
        let
          autojump = {
            name = "autojump";
            src = pkgs.fetchFromGitHub {
              owner = "wting";
              repo = "autojump";
              rev = "06e082c91805cb022900819b2e0881eeae780d58";
              sha256 = "mrPMgwVkqOlKjvy1106MUKF7OlEtKdt8E9mqCg7U9+U="; # <-ここを変更した
            };
          };
        in [
          autojump
        ];

これでhome-manager switchを実行すると、Home Managerを切り替えることに成功します。

実際、zshのプラグインは~/.config/zsh/pluginsに置かれ、 autojumpがちゃんと存在しています:

$ ls ~/.config/zsh/plugins
autojump

しかし、この段階ではautojumpは使えません。 autojumpjというコマンドで実行されるのですが、見つかりません:

$ which j
j not found

これは、プラグインをGitHubから持ってきただけで、 プラグインとして使う宣言をしていないためです。

そこで、zsh内のoh-my-zshpluginsの中にautojumpを使用することを宣言します:

      oh-my-zsh = {
        enable = true; 
        plugins = [
          "autojump" # <- この行を追加した
          "extract"
          "fzf"
          "git"
          "web-search"
        ];
      };

また、autojumpをインストールする必要があります。 autojumpのGitHubリポジトリで、 導入方法を確認してみると、 「autojumpリポジトリ内のinstall.pyを実行してインストールする」と書かれています。 つまり、autojumpを導入するには、Pythonが必要になります。

ここで、蛇足ではあるものの、ある程度重要な話をします。 インストール時のみにPythonが必要である場合、 nix-shell -p pythonを実行すれば一時的にPythonがある環境に入れるので、 グローバル環境にPythonを入れることなく、インストールを済ませることができます。 筆者はこの方法でインストールし、使用することを試みましたが、 autojumpについては、動作するのにもPythonが必要であるため、 グローバルにPythonを入れる必要があるみたいです。 今回は結局グローバル環境に必要になってしまいましたが、一時的に使う方法も覚えておくと便利です。

ということで、グローバルにPythonがある環境が必要となりました。 これは簡単です。homepackages内にpythonを追加するだけです。

  home = {
    homeDirectory = builtins.getEnv "HOME";
    language.base = "en_US.UTF-8";
    packages = with pkgs; [
      cachix
      niv
      python # <- この行を追加
      yarn
    ];
    sessionPath = [
      "$HOME/.local/bin"
    ];
    sessionVariables = {
      EDITOR = "nvim";
    };
    stateVersion = "21.11";
    username = builtins.getEnv "USER";
  };

これでhome-manager switchを実行して、autojump、 およびpythonを追加した新しい環境に切り替えます。

説明通り、Pythonを使ってautojumpをインストールします。

$ ~/.config/zsh/plugins/autojump/install.py

これでautojumpが使えるようになりました。

どのように使うのか、デモを載せておきます:

autojumpのデモ
$ cd ~
$ mkdir tmp             # デモ用の一時的なディレクトリ
$ mkdir tmp/hoge        # デモ用の一時的なディレクトリ
$ mkdir tmp/hoge/fuga   # デモ用の一時的なディレクトリ
$ cd hoge               # hogeに訪問して足跡をつけておく
$ cd fuga               # fugaに訪問して足跡をつけておく
$ cd ~                  # 一度ホームディレクトリに戻る
$ j h                   # hogeって名前を忘れてしまったけど、hを含むことは覚えている
/home/username/tmp/hoge # hogeに移動している
$ pwd                   # 本当に移動できているか確認
/home/username/tmp/hoge # 移動している
$ j f                   # fugaって名前を忘れてしまったけど、fを含むことは覚えている
/home/username/tmp/hoge/fuga # fugaに移動した
$ pwd                        # 本当に移動できているか確認
/home/username/tmp/hoge/fuga # 移動している
$ cd ~                  # お掃除するためにホームディレクトリへ
$ rm -r tmp             # お掃除

3 lsコマンドをかっこよくする

本節では、lsコマンドで表示されるディレクトリやファイルに、 色を割り当てる方法を記述します。

とはいっても、仕組みが若干複雑なので、動けばヨシ!ということで、 詳しい話は省略します。 GitHub上のtrapd/LS_COLORS を使用させていただきます。

何をしているのか詳しく知りたい方は、以下の2本の動画をご視聴ください:

home.nixhome.packagesの箇所を次のように書き換えます:

    packages = with pkgs;
      let
        LS_COLORS = pkgs.fetchFromGitHub {
        owner = "trapd00r";
        repo = "LS_COLORS";
        rev = "2402dfac278ec88909e8a4883b680fb1591fcd3f";
        sha256 = lib.fakeSha256;
      };
      ls-colors = pkgs.runCommand "ls-colors" { } ''
        mkdir -p $out/bin $out/share
        ln -s ${pkgs.coreutils}/bin/ls $out/bin/ls
        ln -s ${pkgs.coreutils}/bin/dircolors $out/bin/dircolors
        cp ${LS_COLORS}/LS_COLORS $out/share/LS_COLORS
      '';    
      in [
        cachix
        ls-colors # <- ここも追加しています
        niv
        python
        yarn
      ];

また、zshenvExtra内に

        eval $(dircolors ~/.nix-profile/share/LS_COLORS)

を追記し、shellAliasesls = "ls --color=auto -F"というエイリアスを追加すれば、完了です。

home-manager switchを実行すると、またsha256のハッシュ値でエラーが出るので、 上の説明と同様、gotに書かれたsha256を設定してください。

lsコマンドで表示される内容がカラフルになりました!

4 まとめ

外部パッケージの導入方法を紹介し、それを利用して autojumpプラグインを有効にし、 lsコマンドで表示される色をカラフルにしました。

最後に、最終的なhome.nixを載せて終わります:

ここまでのhome.nix

sha256のハッシュ値は異なるかもしれません

{ config, lib, pkgs, ... }:

{
  home = {
    homeDirectory = builtins.getEnv "HOME";
    language.base = "en_US.UTF-8";
    packages = with pkgs;
      let
        LS_COLORS = pkgs.fetchFromGitHub {
        owner = "trapd00r";
        repo = "LS_COLORS";
        rev = "2402dfac278ec88909e8a4883b680fb1591fcd3f";
        sha256 = "Ny5jeh2lZMSADfG8KuoiD3X2P8Uwk/XjsCtXoOXuHWU=";
      };
      ls-colors = pkgs.runCommand "ls-colors" { } ''
        mkdir -p $out/bin $out/share
        ln -s ${pkgs.coreutils}/bin/ls $out/bin/ls
        ln -s ${pkgs.coreutils}/bin/dircolors $out/bin/dircolors
        cp ${LS_COLORS}/LS_COLORS $out/share/LS_COLORS
      '';    
      in [
        cachix
        ls-colors
        niv
        python
        yarn
      ];
    sessionPath = [
      "$HOME/.local/bin"
    ];
    sessionVariables = {
      EDITOR = "nvim";
    };
    stateVersion = "21.11";
    username = builtins.getEnv "USER";
  };
  
  programs = {

    direnv = {
      enable = true;
      nix-direnv.enable = true;
    };

    gh = {
      enable = true;
    };

    git = {
      enable = true;
      userEmail = "potassium.iodide28@gmail.com";
      userName = "PotassiumIodide";
    };

    home-manager = {
      enable = true;
    };
    
    zsh = {
      autocd = true;
      defaultKeymap = "emacs";
      dotDir = ".config/zsh";
      enable = true;
      enableAutosuggestions = true;
      enableCompletion = true;
      enableSyntaxHighlighting = true;
      envExtra = ''
        if [ -e ~/.nix-profile/etc/profile.d/nix.sh ]; then
          . ~/.nix-profile/etc/profile.d/nix.sh
        fi
        eval $(dircolors ~/.nix-profile/share/LS_COLORS)
      '';
      initExtra =''
        setopt no_beep
        setopt auto_pushd
        setopt pushd_ignore_dups
        setopt inc_append_history
      '';
      history = {
        extended = true;
        ignoreDups = true;
        save = 1000000;
        share = true;
        size = 1000000;
      };
      oh-my-zsh = {
        enable = true; 
        plugins = [
          "autojump"
          "extract"
          "fzf"
          "git"
          "web-search"
        ];
      };
      plugins =
        let
          autojump = {
            name = "autojump";
            src = pkgs.fetchFromGitHub {
              owner = "wting";
              repo = "autojump";
              rev = "06e082c91805cb022900819b2e0881eeae780d58";
              sha256 = "mrPMgwVkqOlKjvy1106MUKF7OlEtKdt8E9mqCg7U9+U=";
            };
          };
          zsh-autosuggestions = {
            name = "zsh-autosuggestions";
            src = pkgs.fetchFromGitHub {
              owner = "zsh-users";
              repo = "zsh-autosuggestions";
              rev = "a411ef3e0992d4839f0732ebeb9823024afaaaa8";
              sha256 = "KLUYpUu4DHRumQZ3w59m9aTW6TBKMCXl2UcKi4uMd7w=";
            };
          };
        in [
          autojump
          # zsh-autosuggestions
        ];
      shellAliases =
        {
          h = "history";
          hs = "history | grep";
          hsi = "history | grep -i";
          l  = "ls -CF";
          la = "ls -A";
          ll = "ls -alF";
          ls = "ls --color=auto -F";
          vi = "nvim";
          view = "nvim -R";
          vim = "nvim";
        };
      zplug = {
        enable = true;
        plugins = [
          { name = "romkatv/powerlevel10k"; tags = [ as:theme depth:1 ]; }
        ];
      };
    };
  };
}