DEV Community

Cover image for O que é Nix e como utilizá-lo como seu gerenciador de pacotes
Douglas Massolari
Douglas Massolari

Posted on • Updated on

O que é Nix e como utilizá-lo como seu gerenciador de pacotes

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";
  };
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
            };
          })
        ];
      };
    };
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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 arquivos nix.

  2. 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 temos inputs e outputs no arquivo.

  3. 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, usar if 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:

  1. 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.
  2. 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
Enter fullscreen mode Exit fullscreen mode

Depois, vamos fazer o build do nosso flake.nix executando:

nix --extra-experimental-features 'nix-command flakes' build .#homeConfigurations."\"$USER\"".activationPackage 
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Ao terminar, você já deve ter o comando home-manager disponível.

$ home-manager --version
22.05
Enter fullscreen mode Exit fullscreen mode

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";
}
Enter fullscreen mode Exit fullscreen mode

Salve o arquivo e, no terminal, execute:

home-manager switch --flake .
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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
  ];
};
Enter fullscreen mode Exit fullscreen mode

Basta executarmos o comando:

home-manager switch --flake .
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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 .
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
evertonlopesc profile image
Everton Lopes

Ótimo artigo, parabéns!