Import from a URL in Nix

Michael Lynch

Nix에서 URL로 import하기

원문은 Michael Lynch님이 에 게재했습니다. 이 블로그 구독하기

저는 아직 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로 가져올 수 있습니다:

{
  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 별칭을 가져옵니다.

fetchGit 사용하기 (선택 사항)

원격 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 파일을 관리하는 꽤 직관적인 방법을 찾았습니다.

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

댓글