Import from a URL in Nix

Michael Lynch

Nix에서 URL로 import하기

저는 아직 Nix 초보자이며, 최근까지도 풀지 못했던 문제 중 하나는 configuration.nix 파일의 일부를 어떻게 소스 컨트롤 하에 둘 수 있는가였습니다.

목표

Nix 설정 파일들을 모듈화하고 재사용 가능하게 만들고 싶습니다. 그래야 시스템이나 flake에 따라 필요한 설정 파일만 가져올 수 있습니다. 모든 Nix 설정 파일을 소스 컨트롤 하에 두어 서로 다른 시스템이 각 파일의 서로 다른 버전에 의존할 수 있도록 하고 싶습니다. 그래야 모든 시스템을 동시에 각 설정 파일의 최신 버전으로 업그레이드할 필요가 없기 때문입니다.

로컬 파일로 재사용 가능한 bash 별칭 만들기

제가 만들고 싶은 재사용 가능한 Nix 설정 중 하나는 bash 별칭입니다. 기존 시스템에서는 .bashrc에 다음과 같은 내용이 있습니다.

alias gc='git commit --message'
alias gs='git status'
alias td='pushd $(mktemp -d)'

bash에서와 동일한 효과를 내려면 NixOS 시스템에 다음 내용을 담은 파일을 만들면 됩니다. 저는 shell.nix라고 이름 붙였지만 이름은 무엇이든 상관없습니다.

{
  programs.bash.shellAliases = {
    gc = "git commit --message";
    gs = "git status";
    td = "pushd $(mktemp -d)";
  };
}

이 파일을 /tmp/shell.nix 경로에 저장했다면, imports에 경로를 추가해 /etc/nixos/configuration.nix에서 import할 수 있습니다.

{
  imports = [
    "/tmp/shell.nix"
  ];
}

sudo nixos-rebuild switch를 실행한 뒤 셸을 재시작하면 bash 별칭이 활성화된 것을 확인할 수 있습니다.

$ td
/tmp/tmp.awb7PW7aus ~

[/tmp/tmp.awb7PW7aus]$ pwd
/tmp/tmp.awb7PW7aus

Nix 파일을 URL로 옮기기

위 방법은 동작하지만, 각 Nix 시스템마다 같은 파일을 복사해야 합니다.

대신 파일을 공개적으로 접근 가능한 URL에 호스팅한 뒤, 그 URL을 참조하는 표준 configuration.nix 파일을 두고 싶습니다.

원격 URL에서 shell.nix 파일을 가져오도록 configuration.nix를 수정하는 방법은 다음과 같습니다.

let
  shellAliases = builtins.fetchurl {
    url = "https://mtlynch.io/notes/nix-import-from-url/shell.nix";
  };
in {
  imports = [
    shellAliases
  ];
}

다시 한번, 이 변경 사항을 configuration.nix에 저장하고 sudo nixos-rebuild switch를 실행한 뒤 셸을 재시작하면 Nix가 URL에서 제 bash 별칭을 가져옵니다.

Using fetchGit (optional)

원격 Nix 파일을 가져오는 또 다른 방법은 공개 Git 저장소에 파일을 저장한 뒤 fetchGit으로 파일을 가져오는 것입니다.

공개 GitHub 저장소에서 제 shell.nix를 가져오는 configuration.nix 파일 예시는 다음과 같습니다.

let
  repo = builtins.fetchGit {
    url = "https://github.com/mtlynch/nix-files";
    rev = "f98500a995cb5838e40be139a8327867faaff2d5";
  };
in {
  imports = [
    "${repo}/shell.nix"
  ];
}

여기서 url은 제 GitHub 저장소의 공개 URL이고, rev는 가져오고 싶은 파일 버전의 Git 커밋 해시입니다.

configuration.nix에 이런 변경을 한 뒤 sudo nixos-rebuild switch를 다시 실행하면 Nix가 제 GitHub 저장소에서 shell.nix 파일을 가져옵니다.

더 고급 셸 설정

제 bash 별칭 대부분은 간단한 한 줄짜리지만, 몇 개는 좀 더 복잡합니다. 그런 경우에는 bash 함수를 정의한 뒤 그 함수에 대한 짧은 bash 별칭을 만듭니다.

한 예가 제 gcbm bash 별칭입니다. gcbm some-branch처럼 사용하며, 다음과 같은 동작을 합니다.

  1. main 브랜치를 체크아웃합니다.
  2. 원격 저장소에서 최신 변경 사항을 가져옵니다.
  3. some-branch라는 새 브랜치를 체크아웃합니다.

별칭의 핵심 로직은 git_sync_and_branch라는 bash 함수로 구현했습니다. 이를 shell.nix에서 구현한 방법은 다음과 같습니다.

{
  programs.bash = {
    shellInit = ''
      function git_sync_and_branch {
        local readonly TARGET_BRANCH="''${1}"
        local readonly MAIN_BRANCH='master'

        git checkout "''${MAIN_BRANCH}" && \
          git pull origin "''${MAIN_BRANCH}" && \
          if [[ -n "''${TARGET_BRANCH}" ]]; then
            git checkout -b "''${TARGET_BRANCH}"
          fi
      }
      '';
    shellAliases = {
      gc = "git commit --message";
      gcbm = "git_sync_and_branch";
      gs = "git status";
      td = "pushd $(mktemp -d)";
    };
  };
}

함정: 달러 기호 이스케이프하기

bash 함수를 Nix로 옮기려다 제가 겪은 함정 중 하나는 ${ 시퀀스를 이스케이프해야 한다는 점이었습니다. 그렇지 않으면 Nix가 이를 로컬 변수로 보간하려고 시도하는데, 이는 Nix 변수가 아니라 bash 변수이기 때문입니다.

다음은 제가 처음에 git_sync_and_branch bash 함수의 한 줄을 작성하려던 방식입니다.

git checkout "${MAIN_BRANCH}"

Nix에서 다음과 같은 오류가 발생했습니다.

error: undefined variable 'MAIN_BRANCH'

${를 이스케이프하려면 다음과 같이 앞에 작은따옴표 두 개('')를 붙여야 합니다.

git checkout "''${MAIN_BRANCH}"

왜 Home Manager를 쓰지 않나요?

Nix 설정을 모듈화하는 더 대중적인 방법은 Home Manager를 사용하는 것이라고 생각합니다.

하지만 솔직히 말하면, 아직도 Home Manager의 목적을 잘 모르겠습니다. 프로젝트 README에 따르면 다음과 같습니다.

이 프로젝트는 Nixpkgs에 포함된 Nix 라이브러리와 함께 Nix 패키지 매니저를 사용해 사용자 환경을 관리하기 위한 기본 시스템을 제공합니다. 사용자별(전역이 아닌) 패키지와 dotfile을 선언적으로 설정할 수 있게 해줍니다.

이 설명은 저를 혼란스럽게 합니다. Nix가 이미 하는 일처럼 들리기 때문입니다.

Home Manager를 몇 번 살펴본 적이 있지만, 그때마다 얻는 이점이 명확하지 않은데 이 새로운 도구를 배우는 데 많은 투자를 해야 할 것 같은 느낌이 들었습니다.

언젠가는 Home Manager로 Nix 설정을 관리하는 것의 이점을 깨닫게 될지도 모르지만, 지금으로서는 표준 Nix로 원격 Nix 파일을 관리하는 꽤 간단한 방법을 찾았습니다.

원문은 Michael Lynch님이 에 게재했습니다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.