[Tutorial] Faça seu próprio script de pós instalação

Introdução

O @Dio lançou um vídeo sobre como ele fez o dele mas como ele não é programador o script ficou bem complicado de manter além de ter alguns problemas mais “técnicos” que eu prometo que não vou encher falando sobre, o script vai ser feito linha a linha então mesmo que você nunca tenha feito um shell script você vai conseguir compreender, então como disse o filósofo Mario Mario: let’s go!

Ah, esse artigo vai ser praticamente um mini e-book, sugiro favoritar ele e é claro que o script completo vai estar no final do artigo

Criando o Script

O script terá como base o script do dio não terá todos os pacotes mas o objetivo é só pra “pegar o felling” e você fazer o seu, tendo isso em mente vamos pro primeiro passo (e mais importante)

1. Shebang

Ao contrário do que muita gente pensa, “shell script” não é uma linguagem mas sim um conjunto de linguagens de programação interpretadas, por tanto você precisa dizer pro sistema quem você quer que interprete o seu script (sim, é como uma peça de teatro mesmo), e isso é feito através de uma linha começando com #! seguido da localização do interpretador, esse #! chama “shebang”, muita gente (inclusive o Dio), usa a linha assim:

#!/bin/bash

Mas isso está errado, ao contrário da crença popular, o bash não precisa estar em /bin, ele pode estar em qualquer lugar que o sistema consiga carregar os executáveis, mas como evitar problemas quanto a isso? É bem simples, vamos perguntar ao sistema onde o bash está, o executável que faz isso é o env e o legal dele é que ele sempre fica em /usr/bin, então nossa primeira linha fica:

#!/usr/bin/env bash

Dica para Python: ao procurar o python pelo env você garante que o seu sistema consiga encontrar a versão correta exata do python

2. Saia ao primeiro erro

Uma coisa importante nesse tipo de script é que se um comando der erro, provavelmente seu setup vai ficar incompleto e você pode literalmente quebrar o sistema, felizmente o bash traz um meio bem simples de corrigir isso, simplesmente adicione a próxima linha:

set -e

Essa foi a nossa segunda linha

3. O que o script vai instalar?

Aqui nós vamos para a segunda parte mais importante, é a parte que não é relacionada com shell script em si (totalmente) mas vai definir todo o resto daqui pra frente, nós já definimos o objetivo que é instalar pacotes e repositórios agora precisamos definir quais deles vão ser instalados, para isso nós vamos usar um tipo especial de variável chamada de “array” ou arranjo, ela permite que você adicione mais de um valor ao mesmo tempo, pense nelas como uma gaveta onde você pode colocar várias coisas e pegar depois, um array funciona assim no seu uso mais básico (e o que interessa pra gente):

nome_do_array=( valor1 valor2 valor2 ultimo_valor )

Note que os valores são separados por espaço, a lista está entre parenteses, o sinal de igual está logo depois do nome do array e o “abre parenteses” está logo depois do sinal de igual, isso é muito importante, agora vamos a um exemplo real, mas antes um aviso amigável:

Nota: O objetivo é que você aprenda a fazer o seu script então, vou colocar apenas alguns exemplo, troque pelos seus pacotes

Vamos começar com os pacotes “normais” do APT, precisamos de um nome que seja fácil de identificar, “pacotes_apt” parece bom não acha? E também precisamos da lista de pacotes, para exemplificar vou usar esses: winff (Um conversor de vídeos), guvcview (um app pra webcam), virtualbox (um virtualizador de sistemas operacionais) e o piper (um app para configurar mouses da Logitech), então nossa linha fica assim:

pacotes_apt=(winff guvcview virtualbox piper)

Simples assim, mas e se o meio de instalação for diferente? Por exemplo você quer que os pacotes tenham os pacotes recomendados? A logica é a mesma, você só precisa de um array diferente:

pacotes_apt_recomendados=(winehq-stable wine-stable wine-stable-i386 wine-stable-amd64)

Pegou a ideia? Isso vale também para PPAs, Snaps e qualquer outra coisa, exemplo de PPAs:

ppas=(graphics-drivers/ppa libratbag-piper/piper-libratbag-git)

Você deve ter sentido falta do “ppa:”, calma, ele não é necessário na lista, ele vai ser adicionado mais pra frente. Nesse momento você deve estar perguntando, mas e se eu quise adicionar aldo co espaços? Simples, coloque o valor entre aspas duplas com aspas simples assim:

repos=("deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main")

Se você quiser legibilidade e itens longos você pode colocar cada um em uma linha, por exemlo, vamos colocar links baixar alguns arquivos:

downloads=(
"https://github.com/Automattic/simplenote-electron/releases/download/v1.8.0/Simplenote-linux-1.8.0-i386.AppImage"
"https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
"https://dl.winehq.org/wine-builds/winehq.key "
)

Nota: por motivos de boas praticas (como evitar o bash interpretar o caractere & em algumas URLs) sempre coloque links entre aspas, mesmo se não tiver espaços

Como você percebeu o separador de itens pode ser tanto espaço como quebra de linha (Enter). Agora que deu pra pegar a lógica vamos as linhas que vamos adicionar:


repositorios=("deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main")
ppas=(graphics-drivers/ppa libratbag-piper/piper-libratbag-git)

downloads=(
"https://github.com/sudo-give-me-coffee/PhotoGIMP/releases/download/continuous/PhotoGIMP-x86_64.AppImage"
"https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
"https://dl.winehq.org/wine-builds/winehq.key"
)

pacotes_apt=(winff guvcview virtualbox piper)
pacotes_apt_recomendados=(winehq-stable wine-stable wine-stable-i386 wine-stable-amd64)

libs_32bits=(gnutls30 ldap-2.4-2 gpg-error0 xml2 asound2-plugins sdl2-2.0-0 freetype6 dbus-1-3 sqlite3-0)

snaps=(spotify nextcloud)
snaps_classic=(slack skype)

chaves=(winehq.key)

Pra exemplo tá de bom tamanho, se você reparar bem eu tirei “lib” e “:i386” dos itens dos arrays, como eles estão na mesma linha (separados por espaço) e todos os itens tem em comum, não é necessário adicionar no array, faremos isso via programação! Vamos ao próximo passo.

3. Crie seu diretório de trabalho

Sempre que você vai trabalhar com pós instalação, é comum você baixar arquivos, você não está afim de entupir a sua home de arquivos né? Pensando nesse tipo de situação, foi criada a ferramenta “mktemp” ela cria diretórios e arquivos temporários únicos para o seu script e imprime o local onde foram criados, como a gente vai baixar arquivos nós vamos pedir um diretório e já entrar nele, á próxima linha que vamos adicionar ao script é:

cd $(mktemp -d)

O que significa isso? bem o “cd” vem de “change directory” ou seja, ele muda o diretório de trabalho, o “mktemp” é a ferramenta que eu falei, e o $( e ), está dizendo pro bash:
pegue a saída da linha de comando que está entre ( e ). Ou seja essa linha significa:

Pegue a saída do comando “mktemp -d” e passe para o “cd”.

Tudo certo até aqui? Bora pro próximo…

4. Pegando todos os valores de um array e usando pipelines

Essa seção é meramente explicativa, não adiciona nenhuma linha ao script

O Bash permite que todos os itens de um array sejam transformados em uma linha de itens separados por espaços a sintaxe pra isso é essa:

${nome_do_array[@]}

Nós vamos usar isso aliado ao pipelines quando precisarmos formatar a saída, pipelines nada mais são que passar a saída de um comando para outro, nesse processo se usa um caractere chamado pipe (|) e os comandos podem ser escritos numa linha só dai o nome pipeline, por exemplo, se você quer que a saída do “comando 1” seja redirecionado para o “comando 2” você pode fazer assim:

comando 1 | comando 2

Você pode seguenciar quantos comandos quiser assim, agora vamos voltar para a prática

5. Baixando arquivos necessários (pacotes, AppImages, chaves…)

A primeira coisa que vamos fazer é baixar os arquivos isso é feito através da ferramenta “wget” que vem de “web get” ou “pegar da web”, nós vamos usar duas “flags” pra melhorar a visualização do script, são elas -nv que remove saídas de texto desnecessárias e -c que permite continuar de onde paramos, os links que vamos usar estão no array “downloads” nossa próxima linha vai ser essa:

wget -nv -c ${downloads[@]}

Simples assim, agora todos os arquivos serão baixados em sequência

6. Adicione as chaves dos repositórios extras

Na maioria das vezes os repositórios pedem uma chave para serem utilizados, nós baixamos no passo anterior mas elas não estão no sistema, nós podemos adicionar elas com o “apt-key” assim:

apt-key add [lista de arquivos]

A lógica que vamos usar é a mesma do passo anterior, então a próxima linha adicionada será:

apt-key add ${chaves[@]}

7. Adicionando suporte a 32 bits

Se você deseja usar aplicativos 32 bits você precisa ativar o suporte a essa arquitetura as próximas linhas fazem isso, então adicionamos elas ao script:

dpkg --add-architecture i386 
apt update

Note que tem um “apt update” no final, ele é extremamente importante, sem ele pacotes 32 bits de outros repositórios podem não funcionar

8. Laço for

Existem casos onde o comando só aceita um item por vez, nesse caso passar ${nome_do_array[@]} vai dar problema, mas como corrigir? Não pode ser complicado, nós vamos executar o comando para cada item no array, parece difícil, mas é simples:

for item in ${array[@]}; do
  comando "$item"
done

Um exemplo é o “add-repository” você só pode carregar um por vez as próximas linhas fazem isso:

for repositorio in ${repositorios[@]};; do
  apt-add-repository "$repositorio" -y
done

Simples assim, as 3 linhas acima vão pro nosso script

9. PPAs

Se você reparoubem lá no começo nós retiramos o “ppa:” do inicio dos nomes do PPAs, agora a gente vai adicionar elas, vale lembrar que ppas são repositórios então nós precisamos do laço for, por hora vamos apenas estruturar:

for ppa in ${ppas[@]}; do

done

Para adicionar o ppa: na frente basta dar um echo “ppa:”$ppa, o nosso parâmetro fica assim:

for ppa in ${ppas[@]}; do
  apt-add-repository "ppa:"$ppa  -y
done

10. Atualizando os repositórios

Após adicionar os repositórios nós precisamos dizer pro APT que nós fizemos isso, para isso a próxima linha do nosso script faz isso:

apt update

Ok, com o sistema configurado vamos finalmente começar a fazer algo visível

11. Instalando os programas do repositório

Até aqui seu sistema vai ser configurado para receber programas mas não recebeu nenhum (tirando eventuais AppImages baixados) aqui começa o fim do script porque nós finalmente vamos instalar os programas, primeiro os que podem ser instalados sem precisar formatar o nome, as próximas linhas vão ser adicionadas:


apt install "snapd" ${pacotes_apt[@]} -y
apt install --install-recommends  ${pacotes_apt_recomendados[@]} -y

snap install ${snaps[@]}
snap install --classic ${snaps_classic[@]}

Muito fácil né? Agora complica um pouquinho, porque os próximos pacotes a serem instalados precisam ser formatados, ou seja adicionado texto antes e depois, apesar de ser simples, pode parecer um tanto complexo a primeira vista, pode ser feito assim:

echo "${libs_32bits[@]}" | tr ' ' '\n' | awk '{print "lib"$1":i386"}' | tr '\n' ' '

Mas calma que eu vou explicar por partes:

echo "${libs_32bits[@]}" 

Serve pra imprimir todos os itens do array separados por espaços

tr ' ' '\n' 

Traduz espaços ' ' para quebras de linha '\n'

awk '{print "lib"$1":i386"}'

Eé um script awk que imprime (print) o primeiro item de cada linha ($1) adicionando um texto antes ("lib") e depois (":i386")

tr '\n' ' ' 

Traduz quebras de linha '\n' para espaços ' '

Sabendo disso podemos adicionar a próxima linha do script:

sudo apt install $(echo "${libs_32bits[@]}" | tr ' ' '\n' | awk '{print "lib"$1":i386"}' | tr '\n' ' ')

Agora falta apenas os programas de fora do repositório

12. Programas de fora do repositório (.deb)

Você pode instalar os programas de fora do repositórios de forma automatizada também, no caso dos . debs e considerando que você baixou pelo script você pode colocar essa linha:

apt install ./*.deb

13. Programas de fora do repositório (.AppImage)

Os AppImages podem ser “instalados” no sistema também, primeiro você precisa criar uma pasta na sua home para eles, pra isso a próxima linha resolve:

mkdir $HOME/Applications

Dica: O bash substitui $HOME com o caminho para a sua home automaticamente

Agora movemos os AppImages pra lá:

mv *.AppImages $HOME/Applications

Por fim damos permissão para o seu usuário para a sua pasta e os AppImages:

chmod 777 -r $HOME/Applications/

Agora instalamos o AppImageD ele vai adicionar os AppImages no menu de apps automaticamente:

wget "https://github.com/AppImage/appimaged/releases/download/continuous/appimaged-x86_64.AppImage"
chmod +x appimaged-x86_64.AppImage
./appimaged-x86_64.AppImage --install

14. O script completo:

#!/usr/bin/env bash
set -e

# Aqui começam as configurações do script

repositorios=("deb https://dl.winehq.org/wine-builds/ubuntu/ bionic main")
ppas=(graphics-drivers/ppa libratbag-piper/piper-libratbag-git)

downloads=(
"https://github.com/sudo-give-me-coffee/PhotoGIMP/releases/download/continuous/PhotoGIMP-x86_64.AppImage"
"https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
"https://dl.winehq.org/wine-builds/winehq.key"
)

pacotes_apt=(winff guvcview virtualbox piper)
pacotes_apt_recomendados=(winehq-stable wine-stable wine-stable-i386 wine-stable-amd64)
libs_32bits=(gnutls30 ldap-2.4-2 gpg-error0 xml2 asound2-plugins sdl2-2.0-0 freetype6 dbus-1-3 sqlite3-0)

snaps=(spotify nextcloud)
snaps_classic=(slack skype)

chaves=(winehq.key)

# A partir daqui o script irá trabalhar com as configurações

cd $(mktemp -d)
wget -nv -c ${downloads[@]}
apt-key add ${chaves[@]}
dpkg --add-architecture i386 
apt update

for repositorio in ${repositorios[@]};; do
  apt-add-repository "$repositorio" -y
done

for ppa in ${ppas[@]}; do
  apt-add-repository "ppa:"$ppa  -y
done

apt update

apt install "snapd" ${pacotes_apt[@]} -y
apt install --install-recommends  ${pacotes_apt_recomendados[@]} -y
sudo apt install $(echo "${libs_32bits[@]}" | tr ' ' '\n' | awk '{print "lib"$1":i386"}' | tr '\n' ' ')

snap install ${snaps[@]}
snap install --classic ${snaps_classic[@]}

apt install ./*.deb
mkdir $HOME/Applications
chmod 777 -r $HOME/Applications/

wget "https://github.com/AppImage/appimaged/releases/download/continuous/appimaged-x86_64.AppImage"
chmod +x appimaged-x86_64.AppImage
./appimaged-x86_64.AppImage --install

apt dist-upgrade -y
apt autoclean 

echo "Chegamos ao final"

Conclusão

Por mais simples e otimizado que esse script esteja ele não esá completo, agora é a sua vez de fazer, algumas sugestões:

  • Pacotes que NÃO instalam dependências recomendadas (só retirar o --install-recommend não resolve
  • Suporte a Flatpaks
  • Suporte a função remover pacotes

16 Curtidas

Já favoritei para ler mais tarde. Isso me interessa bastante.

3 Curtidas

Opa!
Quais as mudanças que eu devo fazer para criar um desses para o Fedora?

Muito bacana o tutorial, mas tem um ponto super importante aí que eu acho que você não considerou, que é a ordem de instalação/configuração de determinados pacotes, no meu pós instalação eu setei os comandos numa determinada disposição, justamente pra evitar que um comando sobreescreva alguma configuração feita anteriormente…

Claro, no meu script, além de instalar pacotes, eu também seto configurações do ambiente gráfico e esse é um ponto que você não citou, vale a inclusão, eu acho.

2 Curtidas

Não são muitas mas requerem atenção:

O Fedora usa o DNF e não o APT.

Substitua

apt install

por

dnf install

O Fedora não suporta PPAs

Alternativamente é possível usar o RPM Fusion, mais detalhes aqui:
https://rpmfusion.org/Configuration/

O mecanismo de adição de repositórios precisa ser ativada

Pra isso você instala esse pacote antes:

dnf -y install dnf-plugins-core

Depois substitua

apt-add-repository "$repositorio" -y

Por

dnf config-manager --add-repo "$repositorio" -y

O DNF se auto sincroniza

Isso significa que não existe um dnf update, simplesmente remova o

apt update

O mesmo vale pra “apt-key” e “dpkg --add-architecture” e a flag “–install-recommends”. Não as utilize

O Fedora não possui o “dist-upgrade”

Seu equivalente mais próximo é:

dnf upgrade --refresh

O autoclean é problematico

Ele removeu alguns pacotes que eu instalei além dos orfãos sugiro retirar


Que eu me lembro são essas mano se faltar algo me fale

1 Curtida

Verdade, fica pra uma parte 2 porque eu não posso editar o post mais

Na verdade eu considerei sim é mais uma questão de visão, na minha opinião (claro que isso vai de cada um mas…) isso deve ser feito após a instalação dos programas, desse jeito fica mais bem mais rápido o script

Eu uso a instalação e a configuração de determinados apps juntos, por uma questão de organização na execução dos comandos, mas também, o meu script não é um shell pra ser rodado de uma vez só no terminal, eu prefiro rodar um comando por vez, manualmente.

Ah sim, entendo

Já eu prefiro rodar tudo de uma vez, até porque eu sempre uso as mesmas coisas do mesmo jeito

Eu também uso sempre as mesmas coisa, mas eventualmente algum download pode mudar o link, numa atualização de versão ou alguem PPA pode sair do ar e coisas do tipo, por isso prefiro conferir manualmente 1 comando por vez.

Show mano! Vou testar e te passar o feedback depois então. Vlw!