Quando eu comecei a trabalhar na Tweag, eu conheci uma ferramenta chamada Nix.
Na Tweag praticamente todo mundo ama e usa Nix e eu me senti encorajado a aprender mais sobre ele.
Então, depois de ler, estudar e de receber uma grande ajuda do pessoal do trabalho, eu comecei a usar Nix como meu gerenciador de pacotes e configurações e estou gostando bastante.
Antes de mais nada, vamos partir do princípio...
O que é Nix?
Diretamente do site oficial
Nix is a tool that takes a unique approach to package management and system configuration. Learn how to make reproducible, declarative and reliable systems.
Traduzindo de forma livre
Nix é uma ferramenta que possui uma forma única de gerenciamento de pacotes e configuração de sistema. Aprenda como criar sistemas reprodutíveis, declarativos e confiáveis.
Isso não nos diz muita coisa, que "forma única" é essa? Vamos falar sobre os 3 pontos descritos acima: configuração declarativa, reprodutiva e confiável
Configuração declarativa
Em Nix não instalamos pacotes como em gerenciadores de pacotes tradicionais, isto é, executando um comando como apt install
ou brew install
. Até podemos fazer isso, mas não é a proposta, pois isso é um comando imperativo.
A proposta do Nix é configurar de forma declarativa e nós fazemos isso através de um arquivo de configuração aonde listamos todos pacotes e configurações, ou seja, nós declaramos quais pacotes queremos instalar e como configura-los.
Por exemplo, em vez de sudo apt install git wget yarn
, eu tenho, em meu arquivo de configuração, as linhas:
home = {
packages = with pkgs; [
wget
yarn
];
};
programs = {
git = {
enable = true;
userName = "Douglas M.";
userEmail = "douglasmassolari@hotmail.com";
};
}
Repare que, além de listar os pacotes que quero instalar, posso configurar, diretamente, o git, através do Nix. Essas configurações serão, automaticamente, aplicadas por ele no arquivo de configuração do git.
Isso, obviamente, não se limita ao git, sendo possível configurar diversas aplicações como zsh, gh, kitty etc.
Outro ponto interessante é que, olhando o arquivo de configuração, fica bem claro quais os pacotes eu instalei, diferentemente de eu fazer um apt list
, onde ele lista tudo o que eu instalei E as dependências instaladas.
Configuração reprodutível
Como temos esse arquivo de configuração, no qual declaramos os pacotes e temos a configuração dos mesmos, é fácil reproduzir um ambiente.
E é ainda mais fácil quando usamos o flakes!
Flakes permite que, além dos pacotes do repositório oficial do Nix, nós possamos declarar pacotes diretamente do GitHub, além disso, ele cria um arquivo flake.lock
que contém a versão exata do pacote. Assim você consegue reproduzir de forma mais fiel uma configuração.
Configuração confiável
Umas das funcionalidades mais interessantes do Nix são as generations.
Para aplicar as suas configurações, você manda o Nix fazer o build do arquivo. Ele vai ler o seu arquivo, gerar os binários dos pacotes e o resultado disso é uma generation, cada vez que você faz um build que difere do anterior, uma nova generation é criada. Sendo que ele não apaga a anterior automaticamente, o que torna possível que você consiga aplicar uma generation criada anteriormente!
Isso é muito útil porque, caso você desconfigure alguma coisa, você pode simplesmente voltar para a última configuração funcional que você tinha.
É útil também para testar qualquer coisa sem medo de quebrar alguma coisa.
Um ponto interessante disso tudo é que ele só gera os binários e arquivos daquilo que está no seu arquivo de configuração, ou seja, nunca mais você vai ter "lixo" no seu sistema de coisas que você instalou e desinstalou.
Começando a utilizar o Nix
Depois de tanta explicação, vamos colocar a mão na massa e começar a instalar e a utilizar o Nix para gerenciar nossos pacotes.
Instalação do Nix
Primeiro, claro, precisamos instalar o Nix.
Na página oficial de instalação tem as instruções bem claras, basta segui-las e vai dar tudo certo 🤞🏻
Criação do arquivo flake.nix
Vamos começar a criar o arquivo que vai conter nossas configurações.
Esse arquivo precisa estar em um repositório, então, se você ainda não tiver um repositório de dotfiles, você pode criar com os comandos:
mkdir dotfiles
cd dotfiles
git init
Dentro dessa pasta, crie o arquivo flake.nix
com o seguinte conteúdo:
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
home-manager = {
url = "github:nix-community/home-manager/master";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, home-manager, ... }:
let
system = "x86_64-linux"; # Se usa Mac: "x86_64-darwin"
username = "USERNAME";
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
in
{
homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
modules = [
({ config, ... }: {
home = {
stateVersion = "22.11";
inherit username;
homeDirectory = "/home/${username}"; # Se usa Mac: "/Users/${username}"
file.".config/nix/nix.conf".text = ''
experimental-features = nix-command flakes
'';
# Se usa Mac, descomente o código abaixo
#
# Isso vai fazer com que os programas instalados
# apareçam no spotlight
#
# file."Applications/Home Manager Apps".source = let
# apps = pkgs.buildEnv {
# name = "home-manager-applications";
# paths = config.home.packages;
# pathsToLink = "/Applications";
# };
# in "${apps}/Applications";
packages = with pkgs; [ ];
};
programs = {
home-manager.enable = true;
};
})
];
};
};
}
Nota: Onde está USERNAME
você precisa colocar o seu nome de usuário.
Vamos falar um pouco sobre o conteúdo desse arquivo.
Você precisa saber algumas coisas sobre ele:
A linguagem na qual ele é escrita se chama Nix.
Pois é, além de um gerenciador de pacotes e um sistema operacional (NixOS), Nix também é a linguagem que usamos em arquivosnix
.Esse arquivo é um flake (você pode ter percebido pelo nome).
Mais acima, eu falei sobre flakes e deixei um link para a página oficial onde você pode saber mais sobre ele.
Resumidamente, ele facilita a maneira com que usamos o Nix para fazer nossa configuração.
Por causa dele é que temosinputs
eoutputs
no arquivo.Esse arquivo é como um arquivo JSON, é composto de chaves e valores, porém, diferentemente do JSON, ele permite que usemos alguma lógica enquanto escrevemos.
Podemos definir variáveis, usarif
e mais.
Aqui tem um pequeno tutorial sobre a linguagem Nix.
Nos inputs
nós declaramos nossas dependências que, neste caso, são duas:
- O
nixpkgs
, que é o repositório de pacotes do Nix, estamos usando a versão unstable, mas você não precisa se preocupar com isso porque os pacotes lá são bem estáveis, apesar do nome. - O home-manager, que é uma ferramenta que permite gerenciarmos os pacotes e configuração à nível de usuário, que é o nosso objetivo. Você pode ler mais sobre ele na wiki do NixOS.
Nos outputs
nós dizemos o que queremos gerar.
Vou explicar passo-a-passo o que estamos fazendo aqui, mas não vou explicar detalhes da linguagem, para isso que eu passei o link do tutorial acima.
A gente está passando uma função e, no corpo dela, começamos declarando 3 variáveis dentro do bloco let/in
e definimos um valor no campo homeConfigurations.${username}
.
O valor para esse campo é o retorno da função home-manager.lib.homeManagerConfiguration
.
Para essa função, nós passamos um set.
inherit nome
é a mesma coisa que nome = nome
.
Em modules
nós definimos os módulos que queremos configurar, que é onde fica nossa configuração de fato, com os pacotes e a configuração de cada um.
Nós definimos um módulo com duas chaves principais: home
, que são as configurações relacionadas à pasta HOME e a lista de pacotes que queremos instalar, e programs
que é onde vamos habilitar e configurar programas que são suportados pelo home-manager. Veremos isso melhor mais adiante.
Começamos habilitando o home-manager
, assim ele mesmo se gerencia. Com essa sintaxe PROGRAMA.enable
dentro de programs
podemos instalar e configurar vários pacotes.
Usamos file
dentro de home
para criar um arquivo do nix na nossa pasta HOME com uma definição para habilitar o flakes sempre, assim, não precisamos passar --extra-experimental-features
o tempo todo.
Em packages
dentro de home
é onde vamos declarar os pacotes que queremos instalar, repare que iniciamos com []
, que é uma lista vazia.
Instalando o home-manager
O primeiro passo é adicionar o arquivo flake.nix
ao staging do repositório git, basta executar:
git add flake.nix
Depois, vamos fazer o build do nosso flake.nix
executando:
nix --extra-experimental-features 'nix-command flakes' build .#homeConfigurations."\"$USER\"".activationPackage
Esse comando é grande, mas não é complexo.
Na verdade, o comando é nix build
, mas temos que passar o argumento --extra-experimental-features
para habilitar o flakes e o nix-command, que são necessários dessa vez.
O que vem depois é só a gente passando o que ele tem que fazer o build
, nesse caso, é o arquivo flake que existe na pasta atual, por isso .
e, dentro desse arquivo (#
) queremos executar a função activationPackage
que existe dentro do homeConfigurations.$USER
.
Não se preocupe porque só temos que executar esse comando uma vez.
Quando ele terminar, vai existir uma pasta result
e, dentro dela, um binário chamado activate
.
Vamos executá-lo:
./result/activate
Ao terminar, você já deve ter o comando home-manager
disponível.
$ home-manager --version
22.05
A partir daqui, já conseguimos utilizar o Nix com home-manager para instalar e configurar nossos pacotes 🎉
Instalando e configurando pacotes
Usando programs
Vamos começar com o git
, que é um programa comum que todos devemos ter.
Adicione, dentro de programs
, as linhas:
git = {
enable = true;
userName = "NOME DE USUÁRIO";
}
Salve o arquivo e, no terminal, execute:
home-manager switch --flake .
Esse é o comando que você vai usar sempre que alterar sua configuração.
Ele vai fazer o build e criar uma generation.
Ao executar which git
você deve reparar que o caminho fica na pasta .nix-profile/bin
, o que significa que o git
foi instalado pelo nix.
Ao executar git config user.name
, você deve receber como retorno o valor que você definiu em seu arquivo de configuração.
Repare que não usamos a lista de pacotes packages
em home
, isso porque o home-manager possui a opção programs.git
.
Para saber quais opções do home-manager existem, como programs.git
, você pode olhar na página de opções do home-manager
Na linguagem Nix, os dois códigos abaixo, para habilitar o git
, são equivalentes:
programs = {
git = {
enable = true;
};
};
programs.git.enable = true;
Na documentação de opções do home-manager, você vai ver as opções sendo descritas do segundo modo, mas você pode escrever do primeiro modo, ou até uma mistura do dois, como por exemplo:
programs = {
git = {
enable = true;
userName = "userName"
};
bat.enable = true;
fzf.enable = true;
}
Usando home.packages
Agora, vamos supor que você queira instalar o wget
.
O home-manager não possui essa opção, então não vamos usar o programs
, em vez disso, vamos procurar por esse pacote no nixpkgs que é o repositório de pacotes do Nix.
Para procurar por um pacote, você pode usar o comando nix search nixpkgs PACOTE
ou ir na página de pesquisa do nix e digitar o nome do pacote que deseja instalar.
Se buscarmos wget
na página de pesquisa, encontramos o que queremos como primeiro resultado, isso significa que o pacote está no nixpkgs, então basta o colocarmos na em home.packages
:
home = {
# ...
packages = with pkgs; [
wget
];
};
Basta executarmos o comando:
home-manager switch --flake .
E, depois que o Nix terminar de fazer o build, o wget
já vai estar instalado!
Generations
Listar
Cada vez que você faz o build, você vai ver o número da generation criada.
Para listar as generations você pode fazer
home-manager generations
Ele vai te mostrar as generations existentes em ordem decrescente com o caminho para cada uma.
Rollback
Para fazer um rollback, ou seja, retornar para uma generation anterior, basta copiar o caminho dela e executar o arquivo activate
que existe dentro da pasta dela.
Por exemplo, ao listar minhas generations eu vejo que tenho:
$ home-manager generations
2022-06-17 21:06 : id 56 -> /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation
2022-06-17 21:05 : id 55 -> /nix/store/hb4p96vhkdvwirdsrjh5zi7cljd5d6mk-home-manager-generation
2022-06-15 13:02 : id 54 -> /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation
2022-06-08 12:46 : id 53 -> /nix/store/hb4p96vhkdvwirdsrjh5zi7cljd5d6mk-home-manager-generation
Se eu quero voltar para a 54, eu copio o caminho dela /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation
e executo o arquivo activate
dela:
$ /nix/store/g5qzmkcwbxx786x8lb9xff776gffngyg-home-manager-generation/activate
Apagar
Como não existe almoço grátis, as generations ocupam espaço em disco, então você vai querer ir deletando as mais antigas.
Você pode usar o comando home-manager remove-generations
para remover uma generation.
Para remover uma série delas, você pode usar {N..M}
.
Por exemplo, se eu quero remover todas as minhas generations da 1 até a 54 eu posso fazer:
home-manager remove-generations {1..54}
Dicas finais
Makefile
Para não ter que escrever sempre home-manager switch --flake .
, você pode criar um arquivo Makefile
com o conteúdo:
switch:
home-manager switch --flake .
Assim você só precisa executar make
e ele vai executar o comando do home-manager
!
Meu repositório
Eu ainda não tenho muita coisa configurada, mas, se quiser dar uma olhada nas minhas configurações, é só ir ao meu repositório de dotfiles
Espero que o Nix seja útil para você! Vou ficando por aqui!
Top comments (1)
Ótimo artigo, parabéns!