Fresh를 모든 Linux 사용자에게 쉽게 설치할 수 있게 만들려다 마주한 패키징의 복잡성과, 정적 링크 및 자체 업데이트 바이너리에 집중하려는 다음 단계에 관한 이야기입니다.
내가 원했던 건 Fresh를 모두가, 어디서나 쉽게 설치할 수 있게 만드는 것뿐이었다. 그게 얼마나 어렵겠어?!
Windows와 macOS는 비교적 균일해서 그렇게 나쁘지 않다. Windows에서는 winget을 쓴다. 보기에는 별로지만, 초기 골칫거리를 넘기고 나니 작동한다. macOS에서는 지금은 homebrew를 쓰고 있는데, 이상적이지는 않지만 설정은 어렵지 않았다. 언젠가 제대로 패키징할 시간을 내면 더 네이티브한 서명된 mac 앱으로 옮길 것이다. 두 해결책 모두 최신 Windows 및 macOS 사용자에게는 괜찮게 작동한다.
Linux는 이야기가 다르다. 처음에는 Fresh를 npm 패키지로 배포했다. 이 방식은 보기에도 별로였고 hn의 몇몇 사람들을 짜증 나게 했지만, 실제 문제도 있다.
그래서 짜증 난 사람들의 피드백을 들었고, 여러 친절한 기여자의 도움을 받아 "모든 것"을 위한 패키지를 만들었다.
-bin 패키지의 두 변형여기에 사전 빌드 바이너리를 tarball로도 배포한다. 이 모든 것이 얼마나 취약할 수 있는지 상상할 수 있을 것이다. 릴리스할 때마다 수많은 채널 중 하나에서 어떤 문제에 부딪힐지 궁금해진다.
이 해결책들은 각각 어떤 사용자에게는 작동하지만, 모든 사용자에게 작동하는 것은 없다. 그리고 모두 단점이 있다.
많은 사람이 NIX를 설치하지 않았고, 내 앱을 쓰려고 그것만 설치하고 싶어 하지 않기 때문이다. 나는 nix를 한 가지 방식으로 지원하지만, 모두에게 통하는 방식은 아니다.
Flatpak도 배포하지만, 사실 그것과 맞서 싸우고 있다. Flatpak은 독립적인 샌드박스형 데스크톱 GUI 애플리케이션을 위해 설계됐는데, 나는 컴퓨터와 네트워크를 마음껏 돌아다닐 수 있는 터미널 기반 TUI를 배포하고 있다. 작동하게 하려고 끔찍한 "나쁜 관행" 플래그를 넘기고 있다. Flatpak 샌드박싱은 잘 맞지 않는다.
AppImage도 배포하지만, 필요할 때 수행하는 squashfs FUSE 마운트 때문에 실행 속도가 극도로 느리고, 그 결과 기동 시간이 받아들일 수 없을 만큼 느리다. 나는 프로그램이 기술적으로 가능한 한 빠르게 즉시 시작되길 원한다. 기동 시간을 합리적으로 만들기 위해 설치 스크립트에는 squashfs 내용을 어딘가에 추출하고 AppImage를 버리는 끔찍한 해킹이 들어간다. 원한다면 여전히 AppImage를 그냥 실행할 수 있지만, 시작 속도가 성가실 정도로 느릴 것이다. 두 경우 모두 FUSE가 설치되어 있어야 한다. 내 앱에 왜 FUSE가 필요한가?!
또한 내 바이너리에는 최소 libc 버전 요구 사항이 있으므로 실제로 보편적으로 이식 가능하지 않다. 따라서 2-3년 된 배포판에서는 작동하지 않을 수 있다. 그렇다면 정적 링크를 쓰고 AppImage도 없애는 편이 낫다. 어째서인지 AppImage만 사용하면 이 이식성 문제가 해결된다고 생각했다.
마지막으로 여러 이유로 많은 사람이 AppImage와 Flatpak(그리고 Snap)을 나쁘게 보고, 아예 사용을 거부할 것이다. 나는 진다.
다른 hn 토론에서 설명했듯이, 내 .deb를 Debian(및 Ubuntu) 공식 패키지로 올리는 일은 아직 하지 못했다. 그러려면 모든 수많은 rust 의존성도 Debian 패키지가 되어야 하기 때문이다. 이 정책에는 어떤 패키지든 완전히 재현 가능하고 독립적인 빌드를 제공하며 공급망 지옥을 줄이는 등 많은 타당한 이유가 있지만, 해야 할 일이 많다. 그리고 어떻게 계속 따라갈 수 있겠는가? 내 직접 의존성 하나하나를 보안 문제 등이 생길 때마다 Debian에서 다시 업데이트해야 한다. 나는 그럴 시간이 없다. 모든 의존성을 소스로 내 deb 소스 패키지에 그냥 포함할 수도 없는데, 그건 정책에 어긋난다.
또 다른 재미있는 일화로, 나는 예를 들어 오래된 Ubuntu를 실행하는 오래된 컴퓨터도 지원하고 싶다. 아마 어떤 오래된 배포판도 마찬가지일 것이다. 하지만 이런 시스템에는 오래된 libc가 있으므로, 최신 ubuntu용 빌드는 바이너리 로드 시점에 링크 오류가 나서 이전 시스템에는 설치할 수 없다. 그러면 오래된 Ubuntu 컨테이너 이미지에서 빌드하고 패치되지 않은 패키지를 빌드에 물려받거나, 아니면 그 사용자들을 완전히 포기해야 한다. 어느 쪽도 성립하지 않는다.
너무 골치 아파서 내 .deb(또는 .rpm) 패키지를 공식 소스에 받아들여지게 하지 못했다. 따라서 이 패키지를 설치한 사람들은 시스템의 네이티브 패키지 업데이트 메커니즘인 apt-get upgrade나 이에 상응하는 명령을 실행해도 자동 업데이트를 받지 못한다. 단일 패키지의 새 버전을 찾을 URL만 지정하면 apt와 dnf 모두 이를 기억하고 패키지를 업데이트해 주는 빠른 서버리스 해결책이 있으면 좋았을 것이다.
여기서 "올바른" 일은 내 패키지를 공식 채널에 넣는 것이라는 점을 잘 알고 있다. Fedora에는 bugzilla 뭔가를 열었고 Ubuntu에는 누군가가 뭐 그런 걸 시작했으며 투표 등도 있지만, 이런 요청을 끝까지 밀어붙일 시간과 에너지가 없다. 나는 그저 내 소프트웨어를 무료로 주고 싶을 뿐이다. 내 요점은 이렇다. 사람들이 온갖 배포판을 쓰기 때문에, 이를 수행하는 데 드는 누적 노력은 매우 크다.
알고 보니 일부 사람들은 mise를 로컬 사용자 수준 패키지 관리자로 사용한다. 누군가 Fresh에서 작동하게 만들었고, 훌륭한 일이다! 그런데 어느 날 내 쪽 변경 없이 작동을 멈췄다. 알고 보니 GitHub가 빌드 증명 시스템의 인증서를 교체했고, mise는 현재 인증서를 가져오는 대신 오래된 신뢰 루트를 고정해 두고 있었다. 누군가 불평하기 전까지는 무엇이 망가졌는지 전혀 몰랐다. 결국 내가 직접 수정했다. 하지만 왜 이런 종류의 문제에 내 시간을 쓰고 있는가? 내가 뭘 하고 있는지 알기나 하는가? mise는 멋지고 나도 일부 프로젝트에 쓰지만, 어떤 소프트웨어 도구의 작성자 입장에서는 생각해야 할 것들이 너무 많다.
개인적으로 Arch AUR를 사용하고 Fresh도 이 채널을 통해 제공한다. 소스와 사전 빌드된 -bin 패키지 모두 제공한다. 하지만 최근에는 새 패키지 릴리스가 막혔다. 일부 보안 침해 사고 때문에 AUR가 읽기 전용 모드라서 이미 몇 차례 버전 릴리스를 놓쳤다. 지금까지 AUR가 언제 돌아올지는 전혀 모른다. 완화책으로 Fresh를 AUR 대신 Arch extra 저장소에 넣기 위해 관리자에게 연락했고, 어쩌면 그것이 해결책이 될 수 있다.
이 모든 푸념의 결론은 배포판별 채널이 좋은 해결책이 아니라는 것이다. 배포판과 변형의 수가 늘어날수록, 사소한 패키지 하나조차 릴리스하기가 거의 불가능해지고 있다.
정말 패키지 관리자가 필요한가?
이미 릴리스의 일부로 musl 바이너리를 빌드하고 있다. 다음 버전에는 사용자가 필요할 때 실행할 수 있는 새로운 내장형 자체 업데이트 메커니즘이 포함될 것이다. 이것이 Linux의 주 배포 채널이 되며, 앞으로는 내가 지원해야 할 유일한 채널이 되기를 바란다. 기존의 모든 패키지는 적어도 당분간 계속 지원될 것이다. 목표는 정적 바이너리를 _권장 기본값_으로 만드는 것이다. 이를 기본값으로 삼는 것은 위험을 감수하는 일이다. 또 어떤 놀라움을 가져올지 누가 알겠는가? 일부 Linux 이식성 문제로 누군가에게는 내 정적 링크 바이너리가 망가질 수 있다. 지금은 여전히 소스에서 빌드하거나 내가 계속 배포하는 패키지 파일 중 하나를 사용할 수 있다.
기본적으로 나만의 작은 패키지 관리자를 구현하고 있는 셈이다. 모두에게 작동할까? 그러기를 바란다! Fresh는 내려받는 데 약 12MB이고 추출하면 약 35MB이므로, 전반적으로 합리적인 경험이어야 한다. 최종 바이너리 크기를 줄이기 위해서도 계속 작업하고 있다.
내가 할 수 있는 다른 일이 있을까?