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.awb7PW7ausNix 파일을 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처럼 사용하며, 다음과 같은 동작을 합니다.
- main 브랜치를 체크아웃합니다.
- 원격 저장소에서 최신 변경 사항을 가져옵니다.
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 파일을 관리하는 꽤 간단한 방법을 찾았습니다.
글을 무작위로 읽기