Introdução

Tempo estimado de leitura: 1min


Chegará o momento que como um profissional da área de tecnologia, um desesvolvedor, vai precisar utilizar todos os seus conhecimentos da área para poder planejar e executar um desenvolvimento de uma aplicação, seja ela mobile, web ou desktop, ou vai precisar trabalhar com o desenvolvimento de um produto que já existe...

A principal intenção desse trabalho é conseguir deixar claro quais passos tomar para o desenvolvimento de um trabalho de mundo real. Desde o design do produto até a sua conclusão devemos encarar alguns passos como a prototipação, levantamento de requisitos, para poder fazer uma priorização de cada funcionalidade, configuração de ambiente, documentação, integração... A lista é vasta mas a ideia é que esse "guia" mostra o caminho das pedras para poder prosseguir.

Guia

Sofisticação Técnica - Como se virar

Tempo estimado de leitura: 1min


Bem, assim como tudo na vida, a prática leva a perfeição, sabe-se que quando conhecemos ferramentas novas, alguns estilos e técnicas passamos a inserir isso no nosso cotidiano e trazemos a praticidade vindo da experiência e de alguns costumes.

Bem a ideia desse capítulo é passar um conjunto de técnicas ou formas de enfrentar um problema sozinho, antes de recorrer a forças maiores, como aquele seu amigo que entende bem mais do assunto.

Aqui iremos falar sobre:

Como Googlar

Tempo estimado de leitura: 4min

Contexto

Bem nos encontramos no mundo da informação e sabemos que basicamente qualquer resposta está ao alcance dos nossos dedos, basta apenas termos acesso a internet e fazermos uma rápida pesquisa em uma ferramenta voltada para busca em acervos web.

Nesse ponto temos uma certa variedades de ferramentas que nos auxiliam a encontrar o que precisamos, o Bing, Yahoo!, DuckDuck Go, e alguns outros que de vez em quando esbarramos em algum computador. Embora tenhamos uma variedade ampla de buscadores, não dá para não associar a supremacia da Google nesse aspecto, por dominar cerca de 94% de todo tráfego orgânico da surface (parte 'superficial' que possui indexadores para serem localixados) internet.

E você precisa encontrar a solução de um problema ou uma definição precisa a cerca de um assunto, as necessidades podem ser diversas, mas a chances de encontrar isso são altíssimas. Então por conta disso voltamos ao google.

Conseguindo Melhores Resultados na Busca

Bem infelizmente quando vamos atrás de alguma informação na internet, podemos enfrentar alguns problemas, por conta da língua, ou da grande quantidade de informações e definições similares para alguns nichos diferentes.

Um exemplo simples: você pode estar procurando por alguma coisa relacionada a Harry Potter no google e usar apenas Hp, e se depara com informações a cerca de impressoras e suporte.

Por conta desse e alguns outros pequenos problemas (como língua e ambiguidades), é necessário conhecer algumas "técnicas" para conseguir resultados eficientes que melhorem o nosso resultado de busca.

1: Palavras chave:

É possível encontrar o que procura bem rápido usando várias palavras chave na sua pesquisa, ao invés de apenas uma palavra ou termo.

Ao procurar por: Vídeos de crianças engraçadas procure por cada palavra separadamente ao invés da frase completa. Então o ideal nesse cenário seria usar apenas Crianças engraçadas e usar o filtro de busca dentro da ferramenta.

Evite algo como: quero ver vídeos de crianças engraçadas por exemplo. Isso mais atrapalha do que ajuda a busca.

2: Use aspas:

Caso se recorde de uma frase ou termo completo que tenha interesse em achar, coloque a frase exata entre aspas, dessa forma o google irá buscar a string exata que escreveu ao invés de tentar filtrar por outros aspectos para encontrar "relacionados" ou "conceitos" que são parecidos.

Eperimente por exemplo pesquisar um nome completo entre aspas, as chances de encontrar a pessoa específica é bem maior do que apenas usar o nome completo na busca. Dessa forma você evita que o google procure por pessoas com cada um dos nomes e que ele faça algumas combinações com a ordem para tentar achar o que você procura.

3: Use o menos, como operador de exclusão

Caso por exemplo queira saber algo sobre Washington entretanto não quer saber de Washington DC, coloque o - (menos) antes, da palavra DC.

Washington -DC (o menos deve estar antes, e colado ao termo que não quer na sua busca).

Uma boa é usar as aspas para excluir uma frase inteira colocando o - logo antes a frase (entre aspas).

4: Use o OU

O google permite que você faça busca de duas coisas ao mesmo tempo com esse operador. Experimente pesquisar jogos de nintendo OU jogos de playstation, ele retornará parte da busca voltada para cada um.

5: Operador .. (Ponto Ponto)

Funciona para casos em que queira colocar um filtro de intervalo numérico, caso queira buscar músicas de 1980 até 2000, pode pesquisar como:

Músicas 1980..2000

6: Operador Site:

Caso saiba que um conteúdo vai ser encontrado dentro de um site específico, pode evitar gerar mais sugestões de lugares desnecessários colocando site:Fonte.com ao final da busca.

Via Lactea site:Nasa.gov

7: Operador Defina:

Caso não saiba o que uma palavra ou termo significa, use o google como dicionário e coloque Defina: antes do termo que gostaria de pesquisar.

8: Curinga *

O asterisco ao final de uma frase ou termo pede para o google encontrar o termo que completaria aquela sentença como por exemplo:

garota de *, retornará Garota de Ipanema por exemplo. Dessa forma ajuda a gente a se lembrar de algumas coisas.

9: Operador FILETYPE:

Depois de escrever o termo que gostaria de pesquisar, use o FILETYPE: para poder logo em seguida dizer ao google quais são os tipos de arquivos que gostaria que fossem retornados, por exemplo, ao invés de achar algum texto pode optar por retornar uma versão de áudio daquela obra adicionando o FILETYPE:mp3.

10: Utiliza os filtros:

Dentro do Google possui algumas ferramentas de busca, pode filtrar para retornar vídeos, imagens, numa língua específica. Pense que dado momento pode pesquisar por Star Wars, essa busca vai retornar resultados no idioma em que pesquisou, mas se for nos filtros do google vai encontrar uma parte voltada para selecionar o idioma de preferência para retornar aquela informação.

Começando com Linux

Tempo estimado de leitura: 1min


Vamos iniciar a sua experiência com o que possivelmente vai ser o seu sistema operacional mais utilizado. Assim como os demais capítulos desse ebook, existe muito a ser abordado. Nesse capítulo, discutiremos sobre:

Resumão Linux

Tempo estimado de leitura: 2min


Nesse capítulo você terá um 'Resumão' sobre introdução e conceitos básicos e alguns comandos em um terminal Linux, além de aprender como instalar um outro sistema operacional no seu computador ou usar uma ferramenta para poder utilizar algumas outras funções Linux.

Explicação

Criado em 1991 por Linus Torvalds.

Baseado em Unix

O Linux é o Kernel ou Núcleo do Sistema Operacional (faz a comunicação entre o hardware e o sistema operacional)

É desenvolvido por diversas pessoas e empresas ao redor do mundo (vários sistemas operacionais e Kernels baseados no Linux)

Multitarefa / Multiusuário

Distribuições

O que é uma "distro"? Uma distro é uma distribuição baseada no Kernel Linux.

O Linux pode ser "distribuído" por empresas, organizações ou mesmo pessoas, quem podem colocar características próprias no sistema operacional, como configurações, aplicações, sistemas de instalação entre outras peculiaridades, assim damos o nome de distribuição, sua escolha é pessoal e depende da aplicação.

Distribuições mais conhecidas:

  • Ubuntu
  • Debian
  • SuSE
  • fedora
  • popOS!

Requisitos para Instalação

Processador dual core de 2GHz ou superior.

2 GB de memória RAM.

40 GB de espaço livre no disco rígido.

Dual Boot

Particionar o HD para receber o novo sistema operacional. Bootar um pendrive para colocar o instalador do sistema operacional dentro dele. Dentro da Bios, e com o pendrive no computador fazer ele bootar primeiro o pendrive para rodar o instalador do sistema operacional.

Terminal

O Terminal, Shell ou Konsole é uma linha de comando onde podemos executar programas específicos do Linux.

A maioria dos comandos são iguais em diversas distribuições.

Uso para automação de processos através dos comandos, facilita o trabalho no Sistema para Profissionais da Área.

O Terminal pode ser aberto de diversas formas, mas a sequência de teclas CTRL + ALT + T facilita seu acesso.

Dentro do terminal a sequência de teclas CTRL + SHIFT + T abre uma nova aba.

O comando pwd mostra o caminho do diretório.

$ pwd

O comando ls lista os diretórios e arquivos da pasta. O comando dir pode ser usado também, mas o comando anterior funciona melhor.

$ ls

O comando cd (change directory) muda o diretório atual, ou seja, muda o caminho, podemos entrar e sair de pastas.

$ cd

O comando mkdir (make directory) cria um diretório de arquivos, uma pasta.

$ mkdir teste

Para sair da pasta atual e retornar para a anterior é só digitar o comando cd ...

Linux

Tempo estimado de leitura: 1min


Da mesma forma que o Windows e o Mac OS, Linux é o sistema operacional, é o programa dentro do seu computador responsável pela comunicação dos hardwares, como mouse e teclado, e outros softwares que você instala. O Kernel do Linux, ou conjunto do Kernel, são conjuntos de instruções responsáveis por como será usado funções como memória, processador, periféricos e disco. Ele é o software que atua em todo sistema operacional (SO) e através das instruções repassadas ele cuida da forma como se comporta o computador.

Além do Kernel é necessário um conjunto de outros programas adicionais para podermos utilizar essa SO com mais efetividade, dentre esses interpretadores, editores de texto, e compiladores para que seja possível o desenvolvimento de outros programas. O desenvolvimento dos principais programas responsáveis pela interação com o Kernel são de autoria da fundação GNU. É por conta disso que muita gente se referencia ao Linux como GNU/Linux.

Por que utilizar?

Caso pare um instate para pesquisar a respeito, vai encontrar algumas páginas comparando o Linux com outros sistemas operacionais, principalmente com o windows. No geral tem várias vantagens, principalmente atreladas a desempenho, e por se tratar de um software livre, centenas de desenvolvedores já trabalharam no Linux buscando por melhorias que acreditavam poder acrescer nesse sistema operacional.

Distribuições

Por haver tanta gente trabalhando e desenvolvendo programas, acabou surgindo alguns flavors (sabores), que nada mais são que distribuições com características diferentes.

É como se te fornecessem uma variação de uma mesma coisa. Acaba que isso influencia em coisas como interface, e conjunto de programas que são inseridos junto com as instalações.

É um pouco difícil de dizer se existe uma melhor do que a outra, na verdade a melhor acaba sendo uma que você tenha mais familiaridade. Então leia um pouco sobre algumas para poder ver se encontra uma que acredite ser mais a sua cara.

WSL - Windows Subsystem for Linux

Tempo estimado de leitura: 1min


Esta é uma opção para pessoas que ao invés de migrar para outro sistema operacional, prefere desenvolver e usufruir de um terminal Linux, dentro do seu Windows.

Guia de instalação do Subsistema Windows para Linux

Basicamente é uma forma de se utilizar o terminal do Linux dentro de um subsistema windows.

Dual Boot

Tempo estimado de leitura: 4min


Esse passo tem a inteção de explicar como vai ser a instalação de outro sistema operacional dentro do seu computador, para não ter que abrir mão de uma interface que já é uma amiga de longa data, mas quer também exeperimentar a possibilidade de ter uma nova opção para desenvolver.

Começando

Warning: Antes de mais nada, faça esse passo a passo num dia calmo e que esteja tranquilo e disposto a possivelmente encontrar alguns problemas.

Esse tutorial segue um fluxo simples e contínuo para instalação do Ubuntu, caso se interesse por outra distribuição do Linux, aconselho a pesquisar um pouco, mas caso seja essa mesmo que gostaria, vamos para o tutorial! :)

  1. Faça o download do Ubuntu (a 20.04 LTS é uma boa opção).

  2. Tenha certeza de que tem ao menos 50 GB de espaço sobrando no seu HD.

  3. Uma unidade flash USB com no mínimo 8 GB.

  4. E um programa para para poder criar um USB bootável, uma sugestão seria o Rufus

1º passo: Crie um USB bootável com o Ubuntu

Antes de começar esse passo certifique-se de que não há nenhum arquivo importante dentro do seu pendrive, já que esse procedimento irá ser reescrito fazendo com que perca qualquer conteúdo que havia contido antigamente.

Com seu pendrive pronto para ser reescrito, insira-o em seu computador e abra o Rufus.

interface_Rufus

Ele irá detectar automaticamente o drive USB inserido.

Em 'Boot selection' selecione a imagem ISO do Ubuntu que havia feito o download anteriormente. Depois que selecionar irá aparecer alguns avisose uma opção de escrita da ISO. Responda 'yes', escolha a opção recomendada, e confirme que está ciente que os arquivos dentro da unidade serão destruídos.

Depois disso o botão de "Ready", vai ser habilitado. Clique e espere alguns minutos enquanto o USB vai sendo reescrito. Isso pode demorar alguns minutos dependendo da velocidade do seu USB.

Copying_ISO_Rufus

Quando terminar, remova seu USB de forma segura, agora dentro dele existe uma imagem de Ubuntu pronta para ser instalada em um computador.

Embora não seja exatamente necessário, recomenda-se desfragmentar o disco do seu computador para poder consolidar os arquivos do seu computador para o 'início' do seu disco, dessa forma liberando mais espaço antes de fazer o particionamento.

2º passo: Aloque espaço no seu disco rígido.

É necessário particionar seu disco rígido para separar um espaço de atuação para o novo sistema operacional que será instalado no seu computador.

Vá no menu iniciar do windows e procure por "Partições" ou por "Gerenciamento de disco".

Vai se deparar com uma tela similar a esta, nela vai listar as unidades de espaço contidas dentro do seu computador.

Gerenciamento_de_disco

Escolha algum dos espaços tendo em mente que você irá dividir uma dessas unidades, caso tenha mais de uma, opte pela maior provavelmente a sua unidade "(C:)".

Clique com o botão direito na que escolheu. Clique em "Diminuir Volume...".

O espaço de disco que o Ubuntu LTS costuma requisitar, gira em torno de 20 GB ou mais.

Tenha em mente que por mais que ele precise apenas de pouco espaço, vai ser um outro sistema operacional onde irá precisar instalar softwares e salvar arquivos, logo é bom se reservar uma boa quantidade de espaço para não ter problemas por falta dele no futuro

Logo o espaço que irá diminuir seria de ao menos 20000 MB.

gerenciamento_de_disco_exemplo

3º passo: Instalação.

Coloque o Pendrive de com o Boot do Ubuntu em uma unidade USB do seu computador e o reinicie. Enqaunto ele estiver fazendo o processo de "Ligar" novamente, PRESSIONE VÁRIAS VEZES a tecla F11 ou F12 (na verdade pode variar até mesmo para as teclas F10, F8, F2, já que é uma coisa diferente desde a fabricação do computador), então caso não consiga chegar até a sua tela de bios, basta reiniciar novamente e escolher outra tecla.

Obtendo êxito, use as teclas do seu teclado para acessar o Boot Menu (menu de boot) e escolher o seu pendrive de boot, que nesse caso é o "3. USB HDD: SanDisk-(USB 2.0)".

Boot_Menu

Sempre selecionando as opções e clicando em Continuar

Dessa forma o processo de instalação do Ubuntu será iniciado e poderá escolher qual idioma gostaria de fazer o processo de instalação.

Install_ubuntu_1

Depois poderá escolher a opção do Layout do seu teclado;

Irá escolher entre a instalação normal e a instalação mínima, e dependendo se já efetuou a configuração da sua internet, opte por selecionar as opções que baixa as atualizações juntamente com a instalação do Ubuntu.

Install_ubuntu_2

Install_ubuntu_3

Escolha a sua localidade;

Defina as suas credenciais de nome, usuário e senha;

Espere as etapas finais da instalação e ao fim clique no botão "Reiniciar agora".

Lempre-se de desconectar o seu pen-drive antes de reiniciar

Quando terminar de reiniciar irá se deparar com uma tela indicando que conseguiu chegar ao enfim dual boot :)

Menu_de_Boot

Terminal

Tempo estimado de leitura: 11min


Por não possuir uma interface gráfica, a primeiro contato pode ser intimidador, mas é essencial saber ao menos o básico do terminal para interagir de forma mais prática com seu computador. Neste tópico vamos ensinar e explicar alguns comandos simples, que, no primeiro momento, vão ser de grande ajuda para iniciantes no assunto.

Experimente abrir o terminal e vamos fazer algumas experimentações;

No windows você tinha um gerenciador de tarefas, uma interface que te mostrava como funcionava a estrutura da organização dos seus arquivos, assim podendo entrar e sair de pastas, e acessar esses arquivos, executar, abrir, editar...

A primeira vista vai reparar no símbolo ~ no começo da primeira linha, basicamente é uma abreviação para a sua pasta home no terminal.

Diretórios

Com o terminal aberto, utilize o comando cd (change directory) para alterar o diretório atual, com o acréssimo de / você pode acessar os arquivos de sistema do seu computador, então no seu terminal, experimente:

$ cd /

Bem nesse ponto haverá um símbolo ~$ no começo do seu terminal, indicando uma nova linha de comando, a frente do sinal de $ será o local onde há de ser inserido novos comandos, ao entrar em novas pastas/diretórios / a frente do sinal de ~ indicando qual a pasta em que você se encontra no momento.

Agora escreva ls (list contents) para poder listar pastas e arquivos dentro daquele diretório. Então dentro da pasta / experimente usar ls:

$ ls

terminal_ls

Nesse ponto você pode por exemplo tentar entrar em uma pasta dentro dessa pasta utilizando cd ali acima, junto com o nome de uma pasta presente dentro daquele diretório (que você acabou de ver usando o ls).

$ cd bin

Experimente mais uma vez listar os arquivos dentro desse outro diretório.

O diretório /bin contém os comandos binários essenciais (programas) que devem estar presentes quando o sistema é montado no modo para usuário único. Os aplicativos (como o Chrome, ) são armazenados em /usr/bin, enquanto os programas importantes do sistema e utilitários, como o shell bash, ficam localizados em /bin. O diretório /urs pode ser armazenado em outra partição. Por isso, colocar esses arquivos no diretório /bin garante que o sistema terá os programas mais importantes, mesmo se não houver outros sistemas de arquivos montados. O diretório /sbin é semelhante, pois ele contém os arquivos binários para a administração do sistema.

Bem, sabendo que, independente do seu sistema operacional as pastas do seu computador se comportam como uma árvore, e possuem uma certa hierarquia quanto a sua importância. Isso acontece para que haja uma certa segurança quando formos manipular alguns arquivos. Já que os programas mais importantes para o funcionamento estão no root do computador, e uma de suas ramificações é responsável pelas aplicações instaladas pelo usuário, sendo a nossa usr/bin.

Além da pasta bin que guarda arquivos binários para execução na root, vamos citar alguns outros que provavelmente vai se deparar com eles e se perguntar, sua função ou necessidade, até porque eles também estão consumindo uma parte da "memória" do seu computador.

/sbin: Da mesma forma que a bin, a sbin são conjuntos de executáveis do sistema que cuidam de configurações como a segurança do computador por exemplo. É responsável por pedir credenciais ou permissão de um adm quando for instalar algum programa por exemplo.

/boot: Se chegou a fazer o processo de dual boot, já tem uma ideia do que é guardado aqui dentro. Bem basicamente é tudo que o seu computador precisa para poder iniciar o seu Linux corretamente.

/dev: Como uma abreviação para developers, nessa pasta estão guardadas os arquivos responsáveis pelas configurações de desenvolvedores, no geral a conversa que ocorre entre hardwares como o seu teclado, mouse, câmera, com outras partes do seu computador.

/etc: Guarda também um conjunto de instruções que podem ser configuradas por texto, são tão importantes quanto os arquivos contidos em dev, entretanto podem ser simplesmente serem editados em um editor de texto do seu computador.

/home: Por default, praticamente sempre que abre o seu terminal começa nessa pasta, então funciona quase como o desktop do seu windows.

/lib(32 e 64): É onde as aplicações guardam arquivos ao se instalarem. Serão armazedos arquivos binários como em bin e sbin.

/media e /mnt: São diretórios em que você pode montar um dispositivo físico, como CD-ROM, partição do seu HD, um pendrive etc.

Executáveis

São os arquivos binários que execumatos ao utilizar um . antes do arquivo, algo como ./nomedoarquivo.

Já que temos várias pastas para guardar arquivos, e nem sempre sabemos onde foi parar aquele executável... Bem, dá para adotar algumas estratégias de como podemos agir a partir daqui. Uma forma interessante de fazer com que o Linux mapeie arquivos executáveis a partir de uma pasta é utilizar o comando echo $PATH.

$ echo $PATH

Isso fará com que seja listado os PATH (que é aquele 'caminho' da 'URL') de onde possuem pastas com executáveis.

Caso queira saber onde contém um binário específico:

$ which curl

Onde which é a abreviação de Which binary is this, e curl o executável que estamos procurando. O retorno vai ser o PATH desse executável.

Instalando um software

Você também pode usar o terminal para instalar programas facilmente. Isso é especialmente útil se o software não está iniciando a instalação sozinho ou se você só quer agilizar o processo.

Para fazer isso, digite o comando sudo apt-get install nome e substitua o “nome” que colocamos no exemplo pelo nome do programa que você quer instalar. Em seguida, é só apertar o “Enter”.

$ sudo apt-get install nome

+ Comandos

sudo: permissões

Por razões de segurança, o Linux trabalha com permissões de usuários. Por isso, determinados comandos ou arquivos são acessíveis apenas pelo próprio dono ou pelo usuário administrador (root). Para que você não tenha que trocar de usuário a todo instante, existe o comando sudo, que garante credenciais de usuário root temporariamente, mediante a informação de uma senha (sua senha de usuário do seu Ubuntu).

Para fazer o teste, tente executar o comando:

$ ls /root

Você reberá um aviso de permissão negada. Em seguida, execute:

$ sudo ls /root

Depois de informar a senha do seu próprio usuário (no caso do Ubuntu), o comando será executado normalmente e os arquivos a pasta root serão listados no terminal.

É equivalente ao executar um programa como administrador no Windows.

man: manual dos comandos

O comando man é a abreviação de manual, basicamente, se precisar saber todas as coisas referentes aquele comando, experimente utilizar o comando man na frente dele adicionar o comando que quer saber sobre.

$ man ls

Criar uma pasta naquele diretório

$ mkdir nome_da_pasta

Executar um arquivo

$ ./nome_do_arquivo

Apagar um arquivo

$ rm nome_arquivo

caso o arquivo não esteja na pasta em que está no momento, basta colocar o caminho antes do nome.

$ rm /caminho/nome_arquivo

cada / representa um caminho antes de uma pasta

cp: copiar

Copiar um arquivo utilizando o terminal é bem simples. Use o comando cp seguindo do arquivo de origem e o destino para ele, que pode ser tanto uma nova pasta quanto também um novo arquivo, com nome diferente. Exemplo:

$ cp arquivo1.txt arquivo2.txt
$ cp arquivo1.txt pastanova/

Caso a intenção seja de copiar um diretório inteiro, basta inserir o parâmetro -r. Se quiser clonar uma pasta, use:

$ cp -r pasta1 pasta2
cal: calendário

Um simples comando que pode ser usado para abrir o calendário automaticamente é o cal.

$ cal
cmp: comparar arquivos

Se usar o comando cmp e listar dois arquivos em seguida, o terminal se encarregará de compará-los automaticamente.

$ cmp arquivo1.txt arquivo2.txt
mv: mover

Para mover arquivos existe o comando mv e ele pode ser usado tanto para remanejar arquivos como para renomeá-los. Se quiser enviar o arquivo de uma pasta para outra, basta seguir o exemplo:

$ mv pasta1/arquivo1 pasta2/

Se preferir apenas renomeá-lo:

$ mv arquivo1 arquivo2
more: ler

Caso você precise ler o conteúdo de um arquivo de texto, use o comando more seguido do caminho e nome do arquivo:

$ more /home/user/arquivo.txt

Todo conteúdo do arquivo será exibido no terminal, preenchendo a tela com texto. Para prosseguir com a leitura, pressione a barra de espaço e, caso precise voltar uma ou mais páginas, use a tecla "b". Se quiser sair antes do fim do arquivo, pressione "q". Leitura de texto no Linux com o comando more.

df: conferir o espaço em disco

Quer saber qual é o espaço total e quantos GB disponíveis existem em cada partição do sistema? Use o comando df -h .

$ df -h

A opção -h, aliás, quer dizer human-readable, ou seja, legível para humanos. Se você executar o comando sem ela, as informações serão exibidas em kilobytes e será necessário convertê-las mentalmente para outras unidades.

free: ver uso de memória

Se quiser dar uma rápida olhada no uso de memória do seu computador, basta abrir o terminal e inserir o comando free e apertar a tecla Enter. Dessa forma, poderá encontrar aplicações que estão consumindo mais memória, ideal para identificar fontes de problemas.

$ free
history: histórico de comandos

Para ter uma visualização dos comandos que você usou no terminal previamente, simplesmente insira o comando history.

$ history
locate: encontrar arquivos

Para encontrar arquivos no Linux com facilidade, basta inserir o comando locate seguido pelo nome dos arquivos que você achar.

$ locate arquivo1.txt
grep: buscas em textos

Imagine a seguinte situação: você tem um arquivo .txt com vários nomes de alunos de certa escola, mas não tem certeza se um nome em específico está listado. O grep ajuda você a procurar por esse aluno e a fazer muito mais com a ajuda de expressões regulares.

Bastaria executar o comando:

$ grep "Nome do Aluno" arquivo.txt

Fará com que o terminal busque pelo nome indicado dentro da relação.

Caso você não tenha certeza se o nome do aluno foi escrito respeitando as letras maiúsculas, adicione o parâmetro -i para que grep passe a ignorar essa distinção durante a busca.

clear: limpar buffer

Por último, um comando que ajuda a organizar um pouco a confusão de letras que ficam no terminal depois de horas de uso. Para limpar toda a ela, execute o comando clear. Depois, é só voltar a usar o terminal normalmente, como se nada tivesse acontecido.

$ clear

Ou então, pode utilizar o comando Ctrl + L para a mesma coisa.

Atalhos

Alguns atalhos que podem facilitar o manuseio dentro do terminal:

tab : Ele irá automaticamente completar o que você estava digitando ou mostrará todos os resultados possíveis.

Ctrl+L : Para limpar o terminal.

Ctrl+A : Este atalho moverá o cursor para o início da linha. Suponha que você digitou um longo comando ou caminho no terminal e deseja ir para o início dele. Se usar a tecla de seta para mover o cursor levará bastante tempo.

Ctrl+E : Esse atalho é oposto ao Ctrl + A. O Ctrl + A envia o cursor para o início da linha, enquanto Ctrl + E move o cursor para o final da linha.

Ctrl+C : Cancela o comando atual em funcionamento.

Ctrl+Z : Pausa o comando atual, em primeiro plano ou segundo plano.

Ctrl+D : Faz o logout da sessão atual (fecha o terminal).

Ctrl+W : Apaga uma palavra na linha atual.

Ctrl+U : Apaga a linha inteira.

Ctrl+K : Este é semelhante ao atalho Ctrl + U. A única diferença é que, em vez do início da linha, ela apaga tudo, da posição atual do cursor até o final da linha.

Ctrl+Y : Isto irá colar o texto apagado ao usar os atalhos Ctrl + W, Ctrl + U e Ctrl + K. Útil no caso de você ter apagado o texto errado ou se você precisar usar o texto apagado em outro lugar.

Ctrl+P : Você pode usar este atalho para visualizar o comando anterior. Você pode pressioná-lo repetidamente para continuar retornando ao histórico de comandos. Em muitos terminais, o mesmo pode ser alcançado com a tecla PgUp.

Ctrl+N : Você pode usar este atalho junto com o Ctrl + P. Ctrl + N exibe o próximo comando. Portanto, se você estiver visualizando comandos anteriores com Ctrl + P, poderá usar Ctrl + N para navegar para frente e para trás. Muitos terminais têm esse atalho mapeado para a chave PgDn.

Ctrl+R : Busca um comando recente.

!! : Repete o último comando.

exit : Faz o logout da sessão atual (fecha o terminal).

F.A.Q

Tempo estimado de leitura: 3min


As perguntas mais frequentes & votadas no Stackoverflow :^)

Linux

Top 10 questões (ingles/global)Top 10 questões (portugues)
How do I find all files containing specific text on Linux?Programar em C para ARM
How can I recursively find all files in current and subfolders based on wildcard matching?O que significa “:-!!” em linguagem C?
How to change the output color of echo in LinuxConfigurar Cronjob para executar de 5 em 5 minutos, quando estiver dentre 5 a 20 horas
How can I symlink a file in Linux? [closed]Como agendar uma tarefa recorrente no linux?
How to change permissions for a folder and its subfolders/files in one step?Significado de “__”
How can I profile C++ code running on Linux?Quais as linguagens de programação que dão suporte a Linux e Windows?
How do I grep recursively?Como responder programaticamente a um comando no terminal Linux?
How can I update NodeJS and NPM to the next versions?Como lidar com um processo cometa?
How do I delete an exported environment variable?O que acontece com um Cronjob de 2:30, se o servidor cair às 2:28 e só voltar 5 minutos depois?
What is “:-!!” in C code?Programar C em Linux - IDE vs. editor + linha de comando

Bash

Top 10 questões (ingles/global)Top 10 questões (portugues)
How can I get the source directory of a Bash script from within the script itself?Como inserir uma quebra de linha dentro do echo no shell script?
How can I check if a directory exists in a Bash shell script?O que significa '2>' e '&>' em Bash?
How do I tell if a regular file does not exist in Bash?Qual a diferença de /bin/bash e /usr/bin/env bash?
How to concatenate string variables in BashComo passar o valor de uma variável para maiúscula ou minuscula?
How to check if a string contains a substring in BashNo bash, qual é a diferença entre <, << e <<<?
Echo newline in Bash prints literal \nComo extrair o caminho para o ficheiro
In the shell, what does “ 2>&1 ” mean?Converter páginas de documento PDF para JPGs
How can I check if a program exists from a Bash script?Como posso pedir uma confirmação ao usuário num arquivo bash?
Extract filename and extension in BashCMD, PowerShell, Bash - Qual a diferença?
How do I split a string on a delimiter in Bash?Verificar se variável é um número inteiro positivo

Ubuntu

Top 10 questões (ingles/global)Top 10 questões (portugues)
How to kill a process on a port on ubuntuComo inicializar o postgresql com o ubuntu?
How to install the JDK on Ubuntu LinuxHabilitar o .htaccess no ubuntu
`require': no such file to load — mkmf (LoadError)Como manter um servidor nodejs em execução permanente?
How to install Boost on UbuntuEnviando arquivos no Nginx erro “413 Request Entity Too Large”
Lost httpd.conf file located apache [closed]Fluxo de trabalho (workflow) Git usando GitHub?
Nginx no-www to www and www to no-wwwComo instalar o driver PDO_PGSQL no ubuntu?
Postgresql: password authentication failed for user “postgres”Como compilar um arquivo .java usando o terminal do ubuntu
How to permanently add a private key with ssh-add on Ubuntu? [closed]Como mudar a versão do PHP em que o Composer é executado no Linux?
Cannot install packages using node package manager in UbuntuLista de diretórios do projeto em Laravel é exibida ao invés de executar a aplicação [duplicada]
How to install lxml on UbuntuComo ter duas versões do php no mesmo servidor Apache?

Começando com Git

Tempo estimado de leitura: 1min


Vamos começar sua jornada com Git! Há muita coisa a se aprender, mas toda caminhada tem um ponto de partido. Nesse capítulo, discutiremos sobre:

Resumão

Tempo estimado de leitura: 1min


O que você verá nesse capítulo:

Explicação

_O que é
GitFerramenta para "salvar" as varias versões dos seus arquivos/código-fonte
GithubLugar pra salvar teus arquivos/código-fonte

Flow básico de utilização

1. Após criar repositório no Github, no terminal

Clonar um repositório

$ git clone <endereço do novo repositório>

Trocar de diretório

$ cd <nome do repositorio>

Exemplo completo

# ex:
# git clone https://github.com/airbnb/javascript
# cd javascript

2. Após fazer as mudanças nos arquivos que quiser, no terminal

Listar todas as alterações feitas (o que foi adicionado/modificado/removido)

$ git status

Adicionar alterações feitas

$ git add <arquivo_1> <arquivo_2>

Commitar

$ git commit -m "mensagem explicando o proposito desse commit"

Subir as alterações pro Github

$ git push origin <branch_desejada>

Exemplo completo

# ex:
# git add RemoveUserButton.js styles.css
# git commit -m "adicionando botão para remoção de usuários"
# git push origin main

Git & Github

Tempo estimado de leitura: 1min

Git

Lembra quando salvava os arquivos como projeto-final.txt, projeto-final-2.txt, projeto-final-mesmo_agora_vai.txt (etc)?

Git soluciona isso. Serve justamente pra versionar seus arquivos (além de providenciar varias outras utilidades como consequencia disso), mas de uma forma muito mais elegante.

Considerando aquele exemplo besta acima, suponha que cada um dos arquivos possua os seguintes conteúdos:

  1. projeto-final.txt

    Amanhã será um dia bonito
    
  2. projeto-final-2.txt

    Amanhã será um belíssimo dia!
    
  3. projeto-final-mesmo_agora_vai.txt

    Os dias porvir hão de ser belos!
    

Utilizando git, considerando essa linha do tempo, você poderia ter apenas um arquivo (projeto-final.txt [com o conteúdo de projeto-final-mesmo_agora_vai.txt]), com a opção de retornar ao conteúdo das versões anteriores sempre que quisesse.

Além disso, Git possui algo muito incrível que é o sistema de Branches (traduzindo, seriam "galhos" ou "ramificações"); isso é, você pode criar branches independentes com propósitos distintos (por exemplo: uma branch com código mais estavel, outra para experimentações e ai vai da imaginação [ou não])

Tudo isso ficará mais claro conforme formos praticando :)

Github

Bom, todo esse versionamento é incrivel; mas ele funciona somente localmente. Github surge, portanto, para armazenar repositórios online; dessa forma possibilita que:

  • Você possa recuperar seu repositório/código para trabalhar em outros computadores
  • Você possa compartilhar seu código com outras pessoas de forma que ambos trabalharão na mesma base de código, cada um podendo inserir seus próprios versionamentos (ou, commits).

Instalação e Configuração

Tempo estimado de leitura: 4min


O que você verá nesse capítulo:

Instalando

Windows

Para Windows, não tem segredo, basta simplesmente baixar o GitBash :)

Linux

Via de regra, voce pode replicar todos os comandos aqui no Mac; basta substituir os "sudo apt install ..." por "brew install ..."

  1. Por ser muito comum, provavelmente o git já virá instalado no seu Linux; para averiguar, basta rodar, no seu terminal:

    $ which git
    /usr/bin/git
    
  2. Caso o output tenha sido "git not found", há de se instalar com*:

    $ sudo apt install git
    

* Dependendo do Linux utilizado, pode ser que o gerenciador de pacotes seja outro em vez do apt-get (ex: pacman)

Configurando

Primeiro, tendo em vista que o commit é um registro, é importante se identificar antes de começar a utilizar o git:

git config --global user.name "Seu Nome"
git config --global user.email seu_email@exemplo.com
# ex:
# git config --global user.name "Joao da Silva"
# git config --global user.email joao_da_silva@gmail.com

Caso queira adotar outras identidades em outras pastas de trabalho, basta replicar o comando acima sem a flag --global:

cd pasta_de_trabalho
git config user.name "Outro Nome"
git config user.email outro_email@exemplo.com

SSH

Caso esteja utilizando GitBash no Windows não precisa se preocupar com isso

Caso tenha utilizado git outras vezes, já deve ter percebido como é chato ter de se autenticar todas as vezes que vai subir um commit para o Github

Para contornar isso, vamos nos autenticar utilizando uma chave ssh

  1. Verificar se já foram registradas chaves ssh para seu sistema operacional

    $ ls -a ~/.ssh
    .  ..  id_rsa  id_rsa.pub  known_hosts
    
  2. Caso não hajam chaves neste diretorio, isto é, o output tenha sido

    $ ls -a ~/.ssh
    .  ..
    

    temos de criá-las.

    No terminal:

    • Pressione [enter] quantas vezes for necessário

      $ ssh-keygen
      

      Pequena explicação: A flag '-s' gera um "script" bash pro stdout (em texto), necessário para conectar/iniciar o agente.

    • O eval, então, "roda" esses comandos do script

      $ eval `ssh-agent -s`
      
    • adicionar a chave pro agente (caso tenha atribuido outro nome [no "$ ssh-keygen"] para a chave privada, mude o "id_rsa" para o nome correto)

      $ ssh-add ~/.ssh/id_rsa
      
  3. Então, basta adicionar a chave pública no Github. Abra este link, clique no botão "New SSH Key", e então copie a sua chave pública em ~/.ssh/id_rsa.pub:

    esses comandos são totalmente dispensáveis, já que pode abrir o arquivo e copiar manualmente

    • Para copiar pro clipboard a chave publica

      Instalar xclip:

      $ sudo apt install xclip
      

      Copiar arquivo pro clipboard (o equivalente a dar Ctrl+C no conteúdo do arquivo)

      $ xclip -sel c < ~/.ssh/id_rsa.pub
      
    • Agora basta colar essa chave no campo adequado para criar a chave dentro do Github

  4. Extra: Atualizar as urls dos repositorios clonados para utilizar o ssh em vez do https. Primeiro, checar como o repositório está rastreado.

    • listar os repositórios rastreados
    $ git remote -v
    origin	https://github.com/appointment-octopus/ebook (fetch)
    origin	https://github.com/appointment-octopus/ebook (push)
    

    Caso realmente esteja utilizando https (e deseje alterar para utilizar ssh), basta rodar, no terminal, o seguinte comando:

    $ echo "$(git ls-remote --get-url origin)" |\
     { read url;\
      eval `git remote set-url origin git@github.com:${${url#*com/}%.git}.git`; } 
    

Como curiosidade, como o comando anterior foi construído:


Tendo em vista que uma url https padrão do github é:

https://github.com/appointment-octopus/ebook

e uma url ssh padrão é:

git@github.com:appointment-octopus/ebook.git

percebemos que necessitamos extrair o nome da organização + nome do repositório; isto é: appointment-octopus/ebook. Utilizando o pattern matching para strings padrão do bash, conseguimos fazer isso. O # é utilizado para remover prefixos, então:

$ URL=https://github.com/appointment-octopus/ebook

O * vai ignorar qualquer caracter até encontrar a sequencia com/, e então retornar o resto da string

$ echo "${URL#*com/}"

Porém, algumas vezes a url https vem com um .git no final; para ignorá-lo, utilizaremos o %, cujo qual é utilizado para remover sufixos:

$ URL=appointment-octopus/ebook.git

Irá retornar a string até encontrar a sequencia ".git"

$ echo "${URL%.git}

Portanto, juntando tudo, fica:

$ URL=https://github.com/appointment-octopus/ebook.git
$ echo "${${URL#*com/}%.git}"

Entretanto, estamos inserindo a url na mão. Felizmente existe um comando que nos retorna a url que precisamos:

$ URL="$(git ls-remote --get-url origin)"

Agora que temos o que queríamos (nome do usuário/organização+nome do repositório), basta formatar o url de output.

$ URL="$(git ls-remote --get-url origin)"
$ echo "git@github.com:${${URL#*com/}%.git}.git"

Mas não basta ter a url formatada se não "setarmos" ela:

$ URL="$(git ls-remote --get-url origin)"
eval `git remote set-url origin git@github.com:${${URL#*com/}%.git}.git`

Juntando tudo em um comando só, utilizando o pipe ("|") do bash para passar a url para a variável url:

$ echo "$(git ls-remote --get-url origin)" |\
   { read url;\
   eval `git remote set-url origin git@github.com:${${url#*com/}%.git}.git`; } 

Principais comandos

Tempo estimado de leitura: 18min


Bom, enfim vamos trabalhar com um exemplo do zero.

O que você verá nesse capítulo:

Git init

Para este tutorial, crie uma pasta para que possa acompanhar esse tutorial de forma mais interativa:

$ mkdir aprendendo_git
$ cd aprendendo_git

Para versionar uma pasta/diretório, é preciso iniciar um repositório. A partir da pasta raíz do seu projeto, execute:

$ git init
Initialized empty Git repository in <caminho_até_diretório>/aprendendo_git/.git/

Após isso, liste os arquivos neste diretório:

$ ls -a
. .. .git

a flag -a significa: "do not ignore entries starting with ."

O Git criou uma pasta chamada .git. Essa pasta contém os metadados que o git necessita para operar naquele repositório.

Extra

Sinta-se, obviamente, a vontade para explorar essa pasta :)

$ sudo apt install tree
$ tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

9 directories, 16 files

Git status

Sempre que você estiver nesse repositório, terá acesso a versão mais atual do projeto. Utilizando o comando git status, pode aferir quais foram as ultimas mudanças em relação ao ultimo commit (traduzindo: a ultima versão "salva" do projeto)

Como não adicionamos nenhum arquivo, caso rode este comando terá o seguinte output:

$ git status
On branch master

No commits yet

nothing to commit (create/copy files and use "git add" to track)

hoje em dia há um movimento para que a branch padrão se chame main em vez de master, então não se surpreenda caso o seu output tenha sido diferente

Ou seja, não há nada para adicionar (e depois commitar), assim como não há uma versão anterior para se comparar com a versão atual da pasta

Git add

Crie um arquivo lorem_ipsum.txt:

$ echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris malesuada augue vel quam dignissim, nec egestas nisl pretium. Integer porttitor eleifend turpis non ullamcorper. Maecenas maximus sit amet ipsum et facilisis. Aenean tincidunt urna a ex consequat venenatis. Curabitur eget ante scelerisque tellus efficitur pulvinar ac quis lorem. Donec imperdiet ligula id tortor viverra, imperdiet consequat est suscipit. Donec congue pellentesque velit eget rhoncus. Vestibulum bibendum erat sem, sed maximus ipsum condimentum et. Vivamus lectus justo, hendrerit ac ornare eget, ultrices ut massa. Sed euismod, magna vitae ultrices aliquam, est quam volutpat mauris, id fringilla mauris sem ut diam. Mauris laoreet efficitur urna at tempor. Nulla scelerisque iaculis ligula et consequat. Nulla in viverra nunc. Nunc nibh neque, ornare ut magna sed, tempor egestas ligula. Quisque iaculis justo ut diam mattis, vitae iaculis eros luctus." > lorem_ipsum.txt

Verifique que o mesmo foi criado:

$ ls
lorem_ipsum.txt

Entretanto, o mesmo ainda não está sendo rastreado pelo Git:

$ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	lorem_ipsum.txt

nothing added to commit but untracked files present (use "git add" to track)

Perceba que o próprio git nos dá dicas. Adicione o arquivo a zona de "staging": uma pre-seleção do que deverá, de fato, ser versionado/commitado.

$ git add lorem_ipsum.txt

O arquivo não foi adicionado, ainda, ao repositório do git. A zona de staging é um intermediário entre o Local e o Repositório.

O status mudou:

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
	new file:   lorem_ipsum.txt

Git commit

Vamos, então, commitar o arquivo: ou seja, adicionar ao repositório do git para que o mesmo comece a ser versionado.

$ git commit -m "Adiçionando arquivo Lorem Ipsum de 1 paragrafo"

O status, novamente, mudou:

$ git status
On branch master
nothing to commit, working tree clean

Git log/reflog

Para verificar quais commits já fizera, basta rodar o comando log:

$ git log
commit 0e1027f301d18c72e37a755775e8c189a79823e3 (HEAD -> master)
Author: Seu Nome <seu@email.com>
Date:   Thu Mar 18 02:03:29 2021 -0300

    Adiçionando arquivo Lorem Ipsum de 1 paragrafo

Perceba que é informado qual o "id"/hash desse commit (0e1027f301d18c72e37a755775e8c189a79823e3), o autor do commit, data e a mensagem inserida -- todas as informações necessárias para rastrear esse versionamento do repositório.

Se utilizar reflog, terá um log mais enxuto (com hashs encurtecidas também):

$ git reflog
0e1027f (HEAD -> master) HEAD@{0}: commit (initial): Adicionando arquivo Lorem Ipsum de 1 paragrafo

Note que o início de ambas as hashs são iguais

O log têm inumeras utilidades que serão abordadas no próximo capítulo :)

Git commit --amend

Como pôde perceber, há um erro ortográfico na mensagem do commit: escrevemos "Adiçionando" em vez de "Adicionando". Podemos alterá-la utilizando o comando --amend:

$ git commit --amend

amend

Este editor pode parecer estranho a primeira vista. Se chama nano, e vem como editor de texto padrão no terminal do Linux.

Após fazer a alteração necessária, basta apertar Ctrl+O e [Enter] para salvar a alteração no commit, e Ctrl+X para retornar ao terminal.

Vale ressaltar que esse comando vale não somente para a mensagem do commit como, também, para os arquivos. Bastaria modificar o que fosse conveniente, utilizar git add e então git commit --amend.

message

Além disso, caso queira evitar utilizar o nano, basta incluir a flag -m:

A titulo de exemplo, vamos criar o arquivo backup-lorem_ipsum.txt:

$ cp lorem_ipsum.txt backup-lorem_ipsum.txt

Status:

$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        backup-lorem_ipsum.txt

nothing added to commit but untracked files present (use "git add" to track)

Adicionando-o:

$ git add backup-lorem_ipsum.txt

Commitando:

$ git commit --amend -m "Adicionando arquivo Lorem Ipsum de 1 paragrafo e, também, arquivo de backup"
[master 6e3bfef] Adicionando arquivo Lorem Ipsum de 1 paragrafo e, também, arquivo de backup
 Date: Thu Mar 18 02:03:29 2021 -0300
 2 files changed, 2 insertions(+)
 create mode 100644 backup-lorem_ipsum.txt
 create mode 100644 lorem_ipsum.txt

Log:

$ git log
commit 6e3bfef34d498057d67454c88188885dd82bec7f (HEAD -> master)
Author: Seu Nome <seu@email.com>
Date:   Thu Mar 18 02:03:29 2021 -0300

    Adicionando arquivo Lorem Ipsum de 1 paragrafo e, também, arquivo de backup

no-edit

Acaso queira modificar só os arquivos (e, não, a mensagem do commit), basta incluir a flag --no-edit.

Boas praticas

É interessante que só se faça isso em commits locais, haja vista que o commit "amendado" é outro commit, distinto do que fora utilizado como base. Isso significa que, caso este seja um commit publico, ao "adulterá-lo", tende a gerar uma inconveniência para outros desenvolvedores que estavam codando a partir daquele commit.

Além disso, para "subir" este commit (ver git push), você terá de usar a flag -f (force), que é extremamente perigosa de se utilizar em um repositório que multiplas pessoas estão trabalhando, já que força a sobreescrever qualquer commit que esteja la; isso significa que há grandes chances de você apagar o trabalho de outros colegas, além de dificultar a recuperação dos commits perdidos.

Git reset/restore

Seguindo a partir do exemplo anterior, não iremos utilizar o arquivo backup-lorem_ipsum.txt; vamos ver como está o nosso reflog:

$ git reflog
7bc9a79 (HEAD -> master) HEAD@{0}: commit (amend): Adicionando arquivo Lorem Ipsum de 1 paragrafo e, também, arquivo de backup
23d7a38 HEAD@{1}: commit (amend): Adicionando arquivo Lorem Ipsum de 1 paragrafo
4a6b384 HEAD@{2}: commit (initial): Adiçionando arquivo Lorem Ipsum de 1 paragrafo

já o log:

commit 7bc9a79b8c88160de6fc64f4a9f465fb52f7fcee (HEAD -> master)
Author: Seu Nome <seu@email.com>
Date:   Thu Mar 25 16:56:37 2021 -0300

    Adicionando arquivo Lorem Ipsum de 1 paragrafo e, também, arquivo de backup

Para retornamos ao estado anterior, iremos utilizar o comando git reset:

  • utilizando a flag --soft:

    O que ela faz? Retorna ao commit anterior, sem tocar nos arquivos

    $ git reset --soft HEAD@{1}
    
    $ ls
    backup-lorem_ipsum.txt  lorem_ipsum.txt
    
    $ git status
    On branch master
    Changes to be committed:
    (use "git reset HEAD <file>..." to unstage)
    
          new file:   backup-lorem_ipsum.txt
    
    $ git log
    commit 23d7a38445227f7ca71667945b66cd04d9c86c23 (HEAD -> master)
    Author: Guilherme de Lyra <guilyra12@gmail.com>
    Date:   Thu Mar 25 16:56:37 2021 -0300
    
        Adicionando arquivo Lorem Ipsum de 1 paragrafo
    
  • utilizando a flag --hard:

    O que ela faz? Retorna ao estado identico do commit anterior, modificando/removendo arquivos se necessário.

    $ git reset --hard HEAD@{1}
    
    $ ls
    lorem_ipsum.txt
    
    $ git status
    On branch master
    nothing to commit, working tree clean
    
    $ git log
    commit 23d7a38445227f7ca71667945b66cd04d9c86c23 (HEAD -> master)
    Author: Guilherme de Lyra <guilyra12@gmail.com>
    Date:   Thu Mar 25 16:56:37 2021 -0300
    
        Adicionando arquivo Lorem Ipsum de 1 paragrafo
    

Git remote

Bom, o primeiro passo é criar sua conta no Github. Após isso, crie um novo repositório:

new_repo

Agora, basta adicionar esse endereço ao nosso repositório git local:

* repare que você têm de substituir seu nome na URL abaixo

$ git remote add origin https://github.com/<seu nome de usuário>/aprendendo-git

o termo "origin" é o mais adotado para nomear os endereços dos repositórios rastreados, mas fica a seu critério escolher outro nome.

A titulo de exemplo, vamos adicionar o mesmo endereço mas em formato ssh em vez de https:

$ git remote add origin-ssh git@github.com:<seu nome de usuário>/aprendendo-git.git

Feito isso, você pode listar todos os endereços mapeados utilizando a flag -v:

$ git remote -v
origin    https://github.com/<seu nome de usuário>/aprendendo-git (fetch)
origin    https://github.com/<seu nome de usuário>/aprendendo-git (push)
origin-ssh  git@github.com:<seu nome de usuário>/aprendendo-git.git (fetch)
origin-ssh  git@github.com:<seu nome de usuário>/aprendendo-git.git (push)

Git branch

Para nos atermos aos padrões atuais de nome para a branch padrão (main em vez de master), utilizaremos o comando:

$ git branch -M main

a flag -M significa --force --move: é um renomeamento forçado da branch atual. Pode-se, também, especificar qual é a branch que será renomeada: $ git branch -M master main

Git push

Agora que criamos o repositório e mapeamos-o, podemos "subir" o nosso repositório local pra "nuvem" Github:

$ git push -u origin main
Username for 'https://github.com': <seu nome de usuário>
Password for 'https://<seu nome de usuário>@github.com':
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 741 bytes | 10.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/<seu nome de usuário>/aprendendo-git.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

a flag -u serve para indicar que este remoto (origin) será o padrão a ser utilizado nessa branch local; então quando for utilizar o git pull, não precisará explicitar qual remoto & branch (ex: git pull origin main)

Agora, basta acessar a URL do seu repositório e verificar que o arquivo lorem_ipsum.txt está lá:

lorem_ipsum_repo

README.md

README.md se trata de um arquivo Markdown, uma linguagem de marcação de texto para criar textos formatados; este arquivo, neste contexto, é utilizado para descrever o propósito desse repositório. Vamos adiciona-lo pelo proprio Github. No site do seu repositório, clique no botão:

readme

Descreva como preferir e insira uma mensagem de commit: commit_readme

Resultado: result_readme

Criando mais um arquivo

Para enriquecer nosso exemplo, vamos commitar mais um arquivo. Vamos chamá-lo de lero_lero.txt:

$ echo "Podemos já vislumbrar o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas." > lero_lero.txt

Adicionando para a área de staging:

$ git add lero_lero.txt

Commitando:

$ git commit -m "adicionando arquivo lero lero para enriquecer tutorial"

Subindo novamente para o Github :D

$ git push origin main
Username for 'https://github.com': <seu nome de usuário>
Password for 'https://<seu nome de usuário>@github.com':
To https://github.com/<seu nome de usuário>/aprendendo-git.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/<seu nome de usuário>/aprendendo-git.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Opa, deu erro! O erro nos diz que há alterações que estão na branch remota que não se encontram na sua branch local. Isso significa que, antes de subir nosso trabalho, precisamos atualizar nosso repositorio para que nenhum trabalho seja perdido (nesse caso, o README.md que está no repositório remoto).

Git pull

O git pull na verdade se trata de dois comandos em um só: git fetch e git merge.

O fetch serve para detectar as alterações que se encontram no repositório remoto cujo as quais ainda não foram mapeadas no repositório local:

$ git fetch origin main
Username for 'https://github.com': <seu nome de usuário>
Password for 'https://<seu nome de usuário>@github.com':
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/<seu nome de usuário>/aprendendo-git
 * branch            main       -> FETCH_HEAD
   fc48527..fc0e748  main       -> origin/main

Vejamos o status:

$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

nothing to commit, working tree clean

Git merge

Como vimos, as branchs remota e local não estão integradas, para sincronizarmos-as, iremos utilizar o comando merge:

*: o editor de texto nano aparecerá novamente, relembre dele aqui

$ git merge origin/main
Merge made by the 'recursive' strategy.
 README.md | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 README.md

Listando o diretório:

$ ls
README.md  lero_lero.txt  lorem_ipsum.txt

Log:

$ git log
commit 8e756f1edbac45f7626749340a1f7c4650433c99 (HEAD -> main)
Merge: 17cb2da fc0e748
Author: Guilherme de Lyra <guilyra12@gmail.com>
Date:   Thu Mar 25 23:17:32 2021 -0300

    Merge remote-tracking branch 'origin/main' into main

commit 17cb2da0dda74e14859546d9706b16cdbfcdb400
Author: Guilherme de Lyra <guilyra12@gmail.com>
Date:   Thu Mar 25 23:07:10 2021 -0300

    adicionando arquivo lero lero para enriquecer tutorial

commit fc0e7489c1886c98a55b6e7a4c71cb7f70f47d9a (origin/main)
Author: Guilherme de Lyra <guilyra12@gmail.com>
Date:   Thu Mar 25 22:52:52 2021 -0300

    Adicionando arquivo README

    para melhor descrever o propósito deste repositório

commit fc4852752385dd2c0031fb493e2b9b6c851da514
Author: Guilherme de Lyra <guilyra12@gmail.com>
Date:   Thu Mar 25 17:07:30 2021 -0300

    Adicionando arquivo Lorem Ipsum de 1 paragrafo

Perceba que um commit "lixo" foi adicionado para indicar o merge entre as branchs.

Status:

$ git status
On branch main
Your branch is ahead of 'origin/main' by 2 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

Finalmente, agora, podemos então pushar/empurrar/subir o commit pro repositório remoto:

$ git push origin main
Username for 'https://github.com': <seu nome de usuário>
Password for 'https://<seu nome de usuário>@github.com':
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 778 bytes | 778.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To https://github.com/<seu nome de usuário>/aprendendo-git.git
   fc0e748..8e756f1  main -> main

Modificando arquivo remotamente e localmente, simultaneamente

Vamos abrir o arquivo lero_lero.txt e modificar seu conteúdo:

No repositorio local

*: lembrando que, se preferir, pode abrir o arquivo para alterá-lo manualmente.

$ sed -i 's/Podemos já vislumbrar/Abacate/g' lero_lero.txt

sed é um comando bem poderoso, vale a pena ~estudá-lo a parte; mas, basicamente, o comando recebe um "padrão" para encontrar (nesse caso "Podemos já vislumbrar") e qual padrão haverá de se utilizar para substituí-lo (nesse caso, "Abacate").

Conferindo que a alteração realmente foi feita:

$ cat lero_lero.txt
Abacate o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.

Adicionando etc...

$ git add lero_lero.txt
$ git commit -m "Modificando as três palavras iniciais para serem \"Abacate\""
[main f3fadb4] Modificando as três palavras iniciais para serem "Abacate"
 1 file changed, 1 insertion(+), 1 deletion(-)

para se utilizar aspas dentro da mensagem de commit há de se escapar estas com uma barra invertida (\)

No repositório remoto

selecione_arquivo

alterando_arquivo

resultado_arquivo

Resolvendo conflitos

Merge:
$ git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/guilhermedelyra/aprendendo-git
   8e756f1..9f31cb2  main       -> origin/main
Auto-merging lero_lero.txt
CONFLICT (content): Merge conflict in lero_lero.txt
Automatic merge failed; fix conflicts and then commit the result.
$ cat lero_lero.txt
<<<<<<< HEAD
Abacate o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.
=======
Feijão o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.
>>>>>>> 9f31cb2472888021c5c6e398cdd6f9ffe358e99f

vscode_conflito

$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   lero_lero.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ git checkout --ours lero_lero.txt
$ cat lero_lero.txt
Abacate o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.
$ git add lero_lero.txt
$ git status
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

$ git commit --no-edit
[main dc59eb9] Merge branch 'main' of https://github.com/guilhermedelyra/aprendendo-git into main
$ git log --online
dc59eb9 (HEAD -> main) Merge branch 'main' of https://github.com/guilhermedelyra/aprendendo-git into main
9f31cb2 (origin/main) Modificando as três palavras iniciais
f3fadb4 Modificando as três palavras iniciais para serem "Abacate"
8e756f1 Merge remote-tracking branch 'origin/main' into main
17cb2da adicionando arquivo lero lero para enriquecer tutorial
fc0e748 Adicionando arquivo README
fc48527 Adicionando arquivo Lorem Ipsum de 1 paragrafo
Rebase:
$ git pull --rebase
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/guilhermedelyra/aprendendo-git
   8e756f1..9f31cb2  main       -> origin/main
First, rewinding head to replay your work on top of it...
Applying: Modificando as três palavras iniciais para serem "Abacate"
Using index info to reconstruct a base tree...
M       lero_lero.txt
Falling back to patching base and 3-way merge...
Auto-merging lero_lero.txt
CONFLICT (content): Merge conflict in lero_lero.txt
error: Failed to merge in the changes.
Patch failed at 0001 Modificando as três palavras iniciais para serem "Abacate"
Use 'git am --show-current-patch' to see the failed patch

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
$ cat lero_lero.txt
<<<<<<< HEAD
Feijão o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.
=======
Abacate o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.
>>>>>>> Modificando as três palavras iniciais para serem "Abacate"

vscode_rebase_conflito

$ git status
rebase in progress; onto 9f31cb2
You are currently rebasing branch 'main' on '9f31cb2'.
  (fix conflicts and then run "git rebase --continue")
  (use "git rebase --skip" to skip this patch)
  (use "git rebase --abort" to check out the original branch)

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

        both modified:   lero_lero.txt

no changes added to commit (use "git add" and/or "git commit -a")
$ git checkout --ours lero_lero.txt
$ cat lero_lero.txt
Feijão o modo pelo qual a adoção de políticas descentralizadoras talvez venha a ressaltar a relatividade de alternativas às soluções ortodoxas.
$ git add lero_lero.txt
$ git status
rebase in progress; onto 9f31cb2
You are currently rebasing branch 'main' on '9f31cb2'.
  (all conflicts fixed: run "git rebase --continue")

nothing to commit, working tree clean
$ git rebase --continue
Applying: Modificando as três palavras iniciais para serem "Abacate"
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
$ git rebase --skip
$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
$ git log --online
9f31cb2 (HEAD -> main, origin/main) Modificando as três palavras iniciais
8e756f1 Merge remote-tracking branch 'origin/main' into main
17cb2da adicionando arquivo lero lero para enriquecer tutorial
fc0e748 Adicionando arquivo README
fc48527 Adicionando arquivo Lorem Ipsum de 1 paragrafo

F.A.Q

Tempo estimado de leitura: 1min


As perguntas mais frequentes & votadas no Stackoverflow :^)

Top 10 questões (ingles/global)Top 10 questões (portugues)
How do I undo the most recent local commits in Git?Quais as diferenças entre Git, SVN e CVS?
How do I delete a Git branch locally and remotely?Como versionar banco de dados MySQL?
What is the difference between 'git pull' and 'git fetch'?Qual a diferença entre os comandos 'git pull' e 'git fetch'?
How do I undo 'git add' before commit?Qual é a diferença entre um “branch” e uma “tag”?
How do I rename a local Git branch?Como voltar o projeto a um commit específico?
How do I force “git pull” to overwrite local files?Por que usar Git para desenvolvimento individual?
How to modify existing, unpushed commit messages?Como funciona o comando git rebase?
How do I revert a Git repository to a previous commit?Aviso Git: LF will be replaced by CRLF
How do I check out a remote Git branch?Como excluir um ramo(branch) do Git local e remoto?
How to remove local (untracked) files from the current Git working treeComo desfaço o último commit no Git?

Ambientação

Tempo estimado de leitura: 1min


Nesse capítulo você aprenderá:

  • Como funciona uma arquitetura cliente-servidor
  • Projeto completo em Javascript
  • O que é e como utilizar Docker
  • Contribuindo com um repositório Open Source

Arquitetura Cliente-Servidor

Tempo estimado de leitura: 1min


De forma geral, principalmente se tratando de aplicações Web:

  • Cliente = Front-end
    • executado no computador do usuário
    • responsável pelo "visual"
  • Servidor = Back-end
    • processos e serviços que são executados em um servidor dedicado (ou processos executados em background na máquina do usuário)
    • responsável pela comunicação com banco de dados

Ok, mas como isso funciona? Quando você acessa um site, está acessando a aplicação "cliente" - que é responsável por estruturar os dados vindos do Servidor em um template visual.

Exemplo 1

(não real, apenas para ilustração)

Blogimg

Fonte: Dribbble - My Personal Website - Blog Launched 🚀

  1. o Usuário acessa a aplicação-cliente no endereço https://www.blog.com
  2. a aplicação-cliente pede à aplicação-servidor os últimos 3 posts em https://www.api_do_blog.com/posts
    1. aplicação-servidor acessa o banco de dados, que retorna todos os N posts já feitos, estruturados da forma:

      {
          nome: "Nome do post",
          conteúdo: "Lorem ipsum dolor sit amet",
          data_de_publicação: "DD-MM-YYYY"
      }
      
    2. aplicação-servidor ordena esses dados pela data

    3. aplicação-servidor retorna os 3 posts mais recentes para a aplicação-cliente

  3. aplicação-cliente estrutura os dados recebidos de uma forma visual:
    <body>
        { for post in posts }
        <div>
            <p>{ post.nome }</p>
            <p>{ post.conteúdo }</p>
            <p>{ post.data_de_publicação }</p>            
        </div>
        { end for}
    </body>
    
  4. aplicação-cliente retorna html final do endereço https://www.blog.com ao Usuário, que então é renderizado pelo navegador
    <body>
        <div>
            <p>Post A</p>
            <p>Conteudo do Post A</p>
            <p>25-03-2021</p>
        </div>
        <div>
            <p>Post B</p>
            <p>Conteudo do Post B</p>
            <p>24-03-2021</p>
        </div>
        <div>
            <p>Post C</p>
            <p>Conteudo do Post C</p>
            <p>21-03-2021</p>
        </div>
    </body>
    

Exemplo 2

(não real, apenas para ilustração)

  1. Usuário abre jogo Pokemon pelo aplicativo no celular
  2. Usuário toca no ícone de lista de pokemons
  3. aplicativo faz requisição para servidor em pokeapi.co/api/v2/pokemon
  4. servidor acessa banco de dados, retorna todos os pokemons, com nome + url...
  5. aplicativo mostra lista de pokemons

Projeto completo em Javascript

Tempo estimado de leitura: 5min


Este tutorial têm como objetivo:

  • Elucidar melhor como funciona a arquitetura Cliente-Servidor
  • Demonstrar como preparar o ambiente da sua máquina
  • Demonstrar como funciona a estrutura de um projeto Javascript
  • Conseguir fazer uma interação completa entre o front, back e banco

Se trata de uma aplicação de mundo real, baseada no Medium: https://demo.realworld.io/#/ Pode encontrar mais informações sobre esse projeto aqui.

ProjetoStackUrl do projeto
BackendNode.js, Express, MongoDBhttps://github.com/gothinkster/node-express-realworld-example-app
FrontendReact, Reduxhttps://github.com/gothinkster/react-redux-realworld-example-app

Instalação

Clonar repositórios

$ mkdir conduit
$ cd conduit
$ git clone https://github.com/gothinkster/node-express-realworld-example-app backend
$ git clone https://github.com/gothinkster/react-redux-realworld-example-app frontend

Instalar MongoDB

MongoDB é um SGBD (Sistema Gerenciador de Banco de Dados) orientado a Documentos, sendo categorizado como NoSql.

$ wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
$ echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
$ sudo apt update
$ sudo apt-get install -y mongodb-org
$ sudo systemctl start mongod
$ sudo systemctl status mongod
$ sudo mkdir -p /data/db
$ sudo chown -R `id -u` /data/db
$ mongod

Istalar NPM e Yarn

São gerenciadores de pacotes Javascript, portanto, utilizados para instalar bibliotecas externas.

  • npm = gerenciador padrão
  • yarn = surgiu com algumas otimizações em relação ao npm, sendo normalmente mais rápido

hoje em dia há quem argumente que ambos estão em pé de igualdade

$ sudo apt install nodejs npm
$ npm install --global yarn

Entendendo estrutura dos projetos

Felizmente, é bem simples de se abstrair o funcionamento de qualquer projeto JS: caso o README.md não providencie todas as informações necessárias, basta começar a ler pelo package.json :D

A partir da pasta conduit:

$ tree -L 2
.
├── backend
│   ├── app.js
│   ├── config
│   ├── models
│   ├── package.json
│   ├── project-logo.png
│   ├── public
│   ├── README.md
│   ├── routes
│   ├── tests
│   └── yarn.lock
└── frontend
    ├── package.json
    ├── project-logo.png
    ├── public
    ├── README.md
    ├── src
    └── yarn.lock

9 directories, 9 files

Package.json

Antes de começarmos a ler de fato ambos os package.json, vamos ver a similaridade destes:

backend/package.json:

{
  "name": "conduit-node",
  "version": "1.0.0",
  "description": "conduit on node",
  "main": "app.js",
  "scripts": {
    "mongo:start": "docker run --name realworld-mongo -p 27017:27017 mongo & sleep 5",
    "start": "node ./app.js",
    "dev": "nodemon ./app.js",
    "test": "newman run ./tests/api-tests.postman.json -e ./tests/env-api-tests.postman.json",
    "stop": "lsof -ti :3000 | xargs kill",
    "mongo:stop": "docker stop realworld-mongo && docker rm realworld-mongo"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/gothinkster/productionready-node-api.git"
  },
  "license": "ISC",
  "dependencies": {
    "body-parser": "1.15.0",
    "cors": "2.7.1",
    "ejs": "2.4.1",
    "errorhandler": "1.4.3",
    "express": "4.13.4",
    "express-jwt": "3.3.0",
    "express-session": "1.13.0",
    "jsonwebtoken": "7.1.9",
    "method-override": "2.3.5",
    "methods": "1.1.2",
    "mongoose": "4.4.10",
    "mongoose-unique-validator": "1.0.2",
    "morgan": "1.7.0",
    "passport": "0.3.2",
    "passport-local": "1.0.0",
    "request": "2.69.0",
    "slug": "0.9.1",
    "underscore": "1.8.3"
  },
  "devDependencies": {
    "newman": "^3.8.2",
    "nodemon": "^1.11.0"
  }
}

frontend/package.json:

{
  "name": "react-redux-realworld-example-app",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "cross-env": "^5.1.4",
    "react-scripts": "1.1.1"
  },
  "dependencies": {
    "history": "^4.6.3",
    "marked": "^0.3.6",
    "prop-types": "^15.5.10",
    "react": "^16.3.0",
    "react-dom": "^16.3.0",
    "react-redux": "^5.0.7",
    "react-router": "^4.1.2",
    "react-router-dom": "^4.1.2",
    "react-router-redux": "^5.0.0-alpha.6",
    "redux": "^3.6.0",
    "redux-devtools-extension": "^2.13.2",
    "redux-logger": "^3.0.1",
    "superagent": "^3.8.2",
    "superagent-promise": "^1.1.0"
  },
  "scripts": {
    "start": "cross-env PORT=4100 react-scripts start",
    "build": "react-scripts build",
    "test": "cross-env PORT=4100 react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

Bom, fora o trivial (como name, version, description etc), o que salta aos olhos são: dependencies e scripts.

  • Scripts: são definidos pelo próprio desenvolvedor, sendo que podem ser até mesmo comandos que não utilizam alguma biblioteca JS. Por exemplo, poderiamos adicionar a chave "list": "ls" dentro de "scripts", e rodar o comando com npm run list ou yarn run list, que teria como output a listagem do diretório do repositório atual.

Obs: na maioria das vezes não é necessário incluir run no uso do yarn (ex: yarn build em vez de yarn run build); neste caso há de se incluir o run pois list é um comando padrão do yarn utilizado para listar todos os pacotes instalados neste projeto (incluindo dependencias das dependencias etc)

  • Dependencies: como o próprio nome da a entender, são as dependencias utilizadas naquele projeto. É sempre interessante/importante que você tenha ao menos uma noção do que as libs incluídas fazem... Basta acessar https://www.npmjs.com/package/ e incluir no final da url o pacote que está "inspecionando"; por exemplo: https://www.npmjs.com/package/marked

Você pode encontrar mais informações sobre o package.json aqui.

Instalando dependencias dos projetos

Antes de executarmos cada um dos projetos, precisamos instalar as dependencias previamente citadas: Abra dois terminais distintos (ou dê "split" caso seu terminal tenha essa opção), que serão referenciados aqui no tutorial como T1 e T2.

Segue, então, o passo-a-passo a ser realizado em cada um dos terminais:

Ambos a partir da pasta conduit

Comandos no T1

Comandos no T2

$ cd frontend
$ cd backend
$ yarn
$ yarn
$ yarn start
$ yarn dev

Url: localhost:3000

Url: localhost:4100

yarn é o mesmo npm install yarn start é o mesmo que npm run start yarn dev é o mesmo que npm run dev

Caso apareça um erro de conexão ao subir o serviço do backend como o mostrado abaixo, provavelmente acontecera por conta do serviço mongod não ter sido inicializado; para tanto, basta executar:

$ sudo systemctl start mongod

* obs: pode executar o mesmo comando acima substituindo start por enable para inicializar o serviço junto com o sistema operacional.

Mongod error

mongod_error


Agora, com ambos os serviços funcionando, você pode interagir com seu site localmente :D

Por ora é isso :) Veja os próximos capítulos para compreender melhor o funcionamento do Backend & Frontend.

Backend

Tempo estimado de leitura: 1min


Para interagir com o backend, em geral, é mais indicado utilizar Postman ou Insomnia

Ambas as ferramentas são utilizadas para fazer requisições http; dessa forma, para testar alguma rota especifica do backend, não será necessário, por exemplo, navegar nas telas do frontend até chegar no botão X que faz uma requisição especifica em alguma rota do backend. Não é necessário, também, ir ao mais "baixo nível" que seria utilizando o comando curl e afins.

Exemplificando:

  1. Crie um novo request no Postman postman_new_request

  2. Preencha as informações:

url = http://localhost:3000/api/users/
tipo = POST
body = raw, json
{
    "user": {
        "email": "john@jacob.com",
        "password": "johnnyjacob",
        "username": "johnjacob"
    }
}

postman_post_users

Para entender melhor esse request, vamos verificar como está especificada a criação de usuários no backend.

Abra backend/routes/api/users.js(L63-73): backend_users

Perceba que user é uma instância da model User: Em backend/routes/api/users.js(L4) backend_user_model_users_route

Para entendermos o que é essa model,

Abra backend/models/Users.js:

Não precisamos nos aprofundar muito, mas, de acordo com o código visto acima, há 3 trechos relevantes:

Schema: user_schema

Set Password: user_schema_set_password

é recomendada a pesquisa sobre pbkdf2, bcrypt, sha512, salt, pepper no contexto de criptografia :) mas, basicamente trata-se de uma segurança a mais para armazenar as senhas dos usuários dificultando que hackers consigam "adivinhar" a senha dos usuários (seja através de bruteforce ou de "ataques rainbow table")

To Auth Json: user_schema_to_auth_json

Com isso tudo, podemos inferir bastante sobre o funcionamento do request que fizemos: A api recebe nosso request (incluindo o corpo de texto [em formato json] com informações de cadastro do usuário), valida se o username, email e password atendem as especificações do Schema (por ex: não podemos cadastrar "abc" como email), a senha é criptografada e, então, o usuário é salvo no banco de dados Mongo; Caso tudo tenha ocorrido corretamente, o servidor responde o request do usuário mostrando informações básicas de como o usuário foi cadastrado no banco de dados.

Frontend

Tempo estimado de leitura: 1min


Primeiramente, vamos modificar uma linha de código:

Vá até conduit/frontend/src/agent.js(L6) e substitua a url:

De:

const API_ROOT = 'https://conduit.productionready.io/api';

Para:

const API_ROOT = 'http://localhost:3000/api';

Dessa forma, garantimos que a aplicação esteja comunicando com nossa api (e banco) locais.

Verificando Login

Caso você tenha realizado os passos do capítulo anterior, o usuário ja foi criado; portanto, basta acessar a página de login:

login

E inserir as credenciais

email: john@jacob.com
password: johnnyjacob

Você deve ser redirecionado para pagina inicial:

post_login

Resumão Docker

Tempo estimado de leitura: 4min


Disclaimer: esse capítulo (de introdução) é 100% baseado nesse vídeo ->

Docker in 100 seconds

Problema

Suponha que voce está desenvolvendo um app em Cobol, que roda numa plataforma Linux aleatória (ex: archlinux), e você deseja compartilhar esse app com seu amigo; entretanto, ele utiliza um sistema totalmente diferente (ex: debian, windows 7, etc)...

Então, surge a questão:

Como replicar esse ambiente que meu app precisa em qualquer computador?

Máquinas Virtuais

Uma das soluções possíveis é utilizar uma Virtual Machine (e existem varias opções para isso, como: VirtualBox, Vmware, GnomeBoxes, Qemu, dentre outros)

O que são máquinas virtuais? São computadores completos: possuem CPU, memória RAM, disco rigido, sistema operacional, arquivos, aplicativos... tudo. Mas, tudo isso é virtual/simulado, utilizando recursos do computador físico/servidor "real" -- também conhecido como Host.

Então, a idéia seria: um simulador de computador que será configurado de forma igual (idealmente) pelos devs do projeto, utilizando um mesmo sistema operacional e instalando as dependencias necessárias pro projeto.

exemplo: todos instalam uma VM usando Manjaro, instalam os pacotes g++, libncurses etc (qualquer app/dependencia necessária pra rodar aquele projeto)

Mas... isso não é muito escalável quando se considera uma organização com múltiplos apps sendo desenvolvidos simultaneamente, por exemplo. Como as VMs tendem a ser pesadas (tanto em consumo de recursos computacionais do computador [real], como na performance individual), essa opção se torna meio inviável

Docker ao resgate

obs: existem outras opções surgindo atualmente, como o Podman (da Redhat); mas, no geral, container = Docker

Um Container de Docker é conceitualmente muito similar a uma VM, com uma distinção chave: em vez de virtualizar o Hardware (um computador inteiro), os Containers virtualizam apenas o S.O.; ou seja, todos os apps (ou, Containers) são executados por um único Kernel

É isso que torna a utilização de Docker tão mais rápida, flúida e eficiente.

Estrutura básica do Docker

Existem 3 pilares que constituem o universo de Docker:

3 pilares

Dockerfile

Este arquivo é como o DNA: um código que especifica ao Docker como ele deve buildar uma imagem.

Imagem

A imagem é um snapshot da sua aplicação: contém desde as dependencias do seu projeto (ex: pacotes npm etc) até o "sistema operacional". Vale ressaltar que essa imagem é imutavel, e pode ser utilizada para construir múltiplos containers.

Container

É a instancia em execução, criada a partir da imagem.

fazendo um paralelo com OO: img = classe; container = objeto [instancia da classe]

Exemplo Mínimo

Não é necessário reproduzir esses comandos, já que praticaremos isso junto no segundo sub-capítulo.

A partir desse Dockerfile de exemplo:

Dockerfile

FROM ubuntu:20.04

Como um "import"; é uma imagem pré-definida que será utilizada como base nesse arquivo. Essa imagem é "puxada" do docker hub; essa imagem em específico se refere a este Dockerfile.

Existem N imagens pré-definidas, que podem ser utilizadas de acordo com a necessidade do seu projeto. Por exemplo: Python, Ruby, Node, dentre outras infinitas possibilidades.

Normalmente, quando há de se partir de uma imagem de um S.O. mais básico (como esse caso do exemplo), há uma certa preferência para a distribuição Alpine em vez de Ubuntu, por exemplo, por se tratar de uma distribuição mais minimalista.

RUN apt-get update -y && apt-get install -y tree

Com o comando RUN você pode utilizar basicamente qualquer comando bash; nesse caso, estamos instalando o pacote tree (basicamente um ls que verifica as subpastas).

RUN mkdir -p test/nested && \
    cd test && \
    touch nested/a.txt b.txt

Para verificar o comando tree em ação, dentro dessa imagem, a partir do endereço base /, criamos uma pasta aleatoria e outra subpasta; entramos* na pasta criada, e criamos dois arquivos.

RUN echo "current path: `pwd`"

RUN ls --format=across

RUN tree test/

Mesmo utilizando o comando cd na linha passada, ao utilizarmos os comandos pwd e ls verificamos que retornamos ao endereço base; isso porque cada comando RUN é executado a partir do WORKDIR atual.

CMD ["echo", "Hello World!"]

O comando CMD distingue-se do RUN por se tratar de um comando que sera rodado ao executarmos um container; ou seja, no processo de build da imagem, este comando não é executado.

Então, ao executarmos o comando de build:

$ docker build -t exemplo_minimo .

A flag -t serve pra especificar o nome da imagem que será construída

Output 1/2

Output 2/2

Após isso, podemos finalmente executar essa imagem (iniciando um container):

Container


Observação:

A linha de criação de pastas etc poderia ser reescrita da seguinte forma:

WORKDIR test
RUN mkdir nested
RUN touch nested/a.txt b.txt

O comando WORKDIR funciona como um mkdir (se necessário) + cd (modificando o contexto ao longo de toda a imagem)
Entao, o comando tree poderia ser utilizado no contexto atual ., já que o WORKDIR dessa imagem não é mais o caminho base /, e, sim, o caminho /test/

Construindo um container mínimo

Tempo estimado de leitura: 11min


Para a construção desse container, precisaremos, basicamente, de 4 coisas:

  • chroot: significa change root. traz isolamento pro nosso container; ao utilizar isso, cria-se um novo processo que têm o root aparente modificado. Ou seja, dentro desse novo root, voce é incapaz de modificar os arquivos do root original.

  • namespace: feature do kernel Linux que traz isolamento; basicamente, um container não consegue visualizar/manipular recursos de outros containers (ou do próprio host) [o host também não consegue visualizar os recursos/processos dentro dos containers]

    • controla o que voce pode ver/fazer
  • cgroup: significa control group. traz o controle de recursos; basicamente, voce pode especificar quanto de RAM, CPU etc aquele container poderá utilizar (de outra forma, ele tem acesso ilimitado aos recursos do host*)

    • controla o que voce pode usar
  • overlay: um dos sistemas de arquivos utilizados no kernel*. traz a integridade da imagem. ou seja, permite criar uma imagem limpa que será utilizada como base pra construção de containers. dessa forma, vc pode manipular os conteudos dos containers (adicionar libs arquivos, etc), mas isso não modificará a imagem original

    *existem inumeros tipos de filesystems (sistemas de arquivos); para listar os disponiveis no seu computador, rode o comando cat /proc/filesystems

    exemplo de alguns:

    rootfs: o core de um sistema linux. contém todas as aplicações, configurações, dispositivos etc necessários para rodar seu sistema Linux

    procfs: um pseudo-filesystem que provê uma interface de comunicação entre o kernel e o usuario (é utilizado por exemplo pelo "gerenciador de tarefas" [system monitor] para visualizar os recursos atuais do pc etc)

    ext2-4: o sistema de arquivos utilizado por sistemas Unix; lida com conceitos de blocos, inodes, diretorios etc

Resumo dos comandos

Essa seção serve como um "cheatsheet" para recapitular quais comandos executar etc; caso seja a primeira vez lendo esse capitulo, pode passar para a proxima parte =)

Criando imagem do container (dentro da VM já):

mkdir container_minimo && cd "$_"

mkdir -p {,usr/}{{,s}bin,lib{,64}}

wget https://www.busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-i686 -O bin/busybox

chmod +x bin/busybox

chroot . /bin/busybox --install -s

cp /bin/bash bin/

cp /lib/x86_64-linux-gnu/libtinfo.so.6 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 lib/

cp /lib64/ld-linux-x86-64.so.2 lib64/

cd

Criando o script make_container.sh:

echo $'#!/bin/bash

IMAGE_PATH=$1

for ARGUMENT in "$@"
    do
    KEY=$(echo $ARGUMENT | cut -f1 -d=)
    VALUE=$(echo $ARGUMENT | cut -f2 -d=)

    case "$KEY" in
        CPU)   CPU=${VALUE} ;;
        RAM)   RAM=${VALUE} ;;     
        *)   
    esac    
done


function createRandomContainerName()
{
    local prefix=$(</dev/urandom tr -dc a-z | head -c 1)
    local randomName=$(</dev/urandom tr -dc a-z0-9_ | head -c 12)
    echo "$prefix$randomName"
}
containerName=$(createRandomContainerName)
export containerName


function prepareFoldersForOverlayFS() {
    mkdir -p /tmp/$containerName/{upper,workdir,overlay}
}


function createOverlayFS()
{
    mount -t \
        overlay -o lowerdir=$IMAGE_PATH,upperdir=/tmp/$containerName/upper,workdir=/tmp/$containerName/workdir \
        none \
        /tmp/$containerName/overlay
}


function installBusybox() {  
    chroot /tmp/$containerName/overlay/ /bin/busybox --install -s
}

function createCGroup() {
    sleep 1

    PID=$(ps aux | grep unshare | tail -2 | head -1 | awk \'{print $2}\') 
    
    cgcreate -a $containerName -g cpu,memory:$containerName

    set -x
    echo 5MB > /sys/fs/cgroup/memory/$containerName/memory.limit_in_bytes
    echo 100 > /sys/fs/cgroup/cpu/$containerName/cpu.shares

    # cgexec -g cpu,memory:$containerName $PID
    cgclassify -g cpu,memory:$containerName $PID
    set +x

    # Limit usage at 5% for a multi core system
    # cgset -r cpu.cfs_period_us=100 -r cpu.cfs_quota_us=$[ 5000 * $(getconf _NPROCESSORS_ONLN) ] $containerName

    # Set a limit of 80M
    # cgset -r memory.limit_in_bytes=80M $containerName
}

function setUpContainer() {
    export PS1="$containerName-# ";
    mkdir proc;
    mount -t proc none proc;
    bash
}
export -f setUpContainer

function launchContainer() {
    unshare --mount --uts --ipc --net --pid --fork --user --map-root-user \
    chroot /tmp/$containerName/overlay \
    bash -c "setUpContainer"
}
export -f launchContainer

function makeContainer() {
    set -x

    sudo -u $containerName bash -c "launchContainer"

    set +x
}

prepareFoldersForOverlayFS
createOverlayFS
installBusybox
adduser --disabled-password --gecos "" $containerName
usermod -aG sudo $containerName
printf "\n$containerName ALL=(ALL) NOPASSWD: /usr/sbin/chroot, /usr/bin/unshare\n" >> /etc/sudoers

createCGroup &

makeContainer
' > make_container.sh

Instalando uma máquina virtual para rodar esse tutorial

Para evitar poluir seu sistema operacional com esse tutorial, fazer testes livremente, vamos instalar uma VM (extremamente leve) para servir de sandbox:

#PassoComando
1Instalar VirtualBoxAbra o link e selecione o metodo de sua preferencia: https://www.virtualbox.org/wiki/Linux_Downloads
2Instalar bakerX (front-end for creating and managing (micro) virtual environments)Opção 1: npm install ottomatica/bakerx -g
Opção 2: acesse o link
3Baixar iso do ubuntubakerx pull focal cloud-images.ubuntu.com
4Criar a VMbakerx run construindo_container_minimo focal
5Entrar na VMbakerx ssh construindo_container_minimo
6Garantir que sempre entraremos nessa VM como rootprintf "sudo -i\n" >> ~/.bashrc && exec $SHELL
6Dar update p instalar alguns utilitariosapt update
7Instalar:
tree (listar diretorios e subdiretorios)
apt install tree

Atenção

Caso o terminal esteja tendo comportamento inesperado (ex: ao apertar Backspace, surge um espaço), talvez tenha que modificar o $TERM

# no seu pc mesmo, no host
~# echo $TERM
xterm-kitty
# na VM {que acessa com o bakerx}
~# echo "export TERM=xterm" >> ~/.bashrc
~# exec $SHELL

Preparando um diretorio para ser nosso container

Ainda dentro de nossa VM (passo 5):

Vamos criar algumas pastas replicando o filesystem do Linux:

Obs: omitirei o nome root@ubuntu-focal:~# aqui para poluir menos

Ou seja, no seu terminal você verá root@ubuntu-focal:~# , aqui no tutorial apenas ~#

~# mkdir container_minimo && cd "$_"

~/container_minimo# mkdir -p {,usr/}{{,s}bin,lib{,64}}

A estrutura atual da pasta deve estar assim:

~/container_minimo# tree

.
├── bin
├── lib
├── lib64
├── sbin
└── usr
    ├── bin
    ├── lib
    ├── lib64
    └── sbin

7 directories, 0 files

Vamos adicionar um pacote para utilizarmos dentro desse container que estamos criando?

Copie o pacote ls para a pasta atual:

~/container_minimo# cp /bin/ls bin/ls

Vamos rodar o comando ls dentro do nosso novo container (utilizando o comando chroot):

~/container_minimo# chroot . ls

chroot: can't execute 'ls': No such file or directory

Hmm... O problema é que esse comando (ls) necessita de algumas bibliotecas para funcionar. Felizmente, o comando ldd nos lista todas as dependencias que esse pacote possa ter:

~/container_minimo# ldd /bin/ls

	/lib/ld-musl-x86_64.so.1 (0x7f951dcf1000)
	libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f951dcf1000)

Agora, basta copiar essa biblioteca para nosso container:

~/container_minimo# cp /lib/ld-musl-x86_64.so.1 lib/.

Agora, vamos tentar novamente rodar ls:

~/container_minimo# chroot . ls /
bin   lib   sbin  usr

Funcionou :)

Vamos adicionar o bash também:

~/container_minimo# cp /bin/bash bin/

~/container_minimo# cp /lib/x86_64-linux-gnu/libtinfo.so.6 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 lib/

~/container_minimo# cp /lib64/ld-linux-x86-64.so.2 lib64/

Entretanto, seria muito tedioso e exaustivo copiar cada comando que julgassemos necessarios (como, por exemplo, o shell bash, o comando cp, mv e assim por diante)

Portanto, vamos utilizar o Busybox (= varios comandos unix uteis, como cd, alias, mv etc)

~/container_minimo# wget https://www.busybox.net/downloads/binaries/1.31.0-defconfig-multiarch-musl/busybox-i686 -O bin/busybox

Torne esse arquivo em executavel

~/container_minimo# chmod +x bin/busybox

Instale os symlinks dentro do container:

~/container_minimo# chroot . /bin/busybox --install -s

Verifique que agora existem inumeros pacotes dentro da pasta de binarios:

~/container_minimo# chroot . ls /bin/

Interagindo com nosso container

Para entrarmos nesse nosso novo container (e utilizar o shell dentro do mesmo), basta rodar o comando bash:

~/container_minimo# PS1="C-$ " chroot . bash

o PS1 modifica o prompt; é so para distinguirmos o nome do shell dentro do container em relação ao mundo externo :)

Vamos criar um arquivo aleatorio dentro desse container:

C-$ touch teste.txt

C-$ ls

bin        lib        linuxrc    sbin       teste.txt  usr

C-$ exit

Agora que saímos do container (com o exit), podemos ver um problema. A modificação que fizemos dentro do container veio para o "mundo real"...

~/container_minimo# ls

bin        lib        linuxrc    sbin       teste.txt  usr

Aprimorando nosso container

Overlay

Para mantermos a integridade, vamos utilizar o overlayFS. Esse sistema consiste de 3 camadas:

  • Lower: read-only, a imagem (não modificavel dentro do container) utilizada como base pra criação do container
  • Upper: read-write, onde será armazenado as modificações feitas
  • Overlay: o container de fato. a composição das duas camadas

existe ainda o workdir. mas ele não têm tanto valor semantico, é so um diretorio aleatorio utilizado pelo kernel enquanto ele monta o Overlay

overlay

Como bônus, vamos montar o filesystem proc, também, para monitorarmos os recursos dentro desse container.

Crie um novo arquivo dentro da VM (não container) chamado make_container.sh (e vamos sair dessa pasta do container, também):

~/container_minimo# cd
~# vim make_container.sh

Aperte i para entrar no modo inserção dentro do editor de texto vim, copie o codigo abaixo e cole (ctrl + shift + v)

#!/bin/bash

CONTAINER_PATH=$1


function createRandomContainerName()
{
    local randomName=$(</dev/urandom tr -dc A-Za-z0-9-_ | head -c 10)
    echo "$randomName"
}


containerName=$(createRandomContainerName)


function prepareFoldersForOverlayFS()
{
    mkdir -p /tmp/$containerName/upper \
             /tmp/$containerName/workdir \
             /tmp/$containerName/overlay
}


function createOverlayFS()
{
    mount -t \
        overlay -o lowerdir=$CONTAINER_PATH,upperdir=/tmp/$containerName/upper,workdir=/tmp/$containerName/workdir \
        none \
        /tmp/$containerName/overlay
}


function installBusybox() {  
    chroot /tmp/$containerName/overlay/ /bin/busybox --install -s
}


function launchContainer() { 
    PS1="$containerName-# " \
    chroot /tmp/$containerName/overlay \
    bash -c "mkdir /proc;
    mount -t proc none /proc;
    bash"
}


prepareFoldersForOverlayFS
createOverlayFS
installBusyLogarbox
launchContainer

Salve o arquivo (aperte ESC, digite :wq e aperte ENTER)

Vulnerabilidade: visualizando recursos que não deveria

Para visualizarmos melhor o motivo de precisarmos de namespaces, façamos o seguinte:

Inicie um container no background (adicionando & ao final do comando):

~# bash make_container.sh container_minimo/ &

Vamos verificar qual o PID (process id) do mesmo:

~# ps aux | grep make_container.sh
1327 root      0:00 bash make_container.sh container_minimo/
1445 root      0:00 grep make_container.sh

Mas, quando se inicia um processo no background (utilizando o operador &), podemos verificar o PID deste processo de forma mais simples:

~# echo $!
1327

Ok, sabemos agora que o PID desse container rodando no background é 1327. Logar Vamos verificar quais processos são possiveis de ser visualizados dentro desse container:

qqyioEo0gM-# ps

...
 1097 0         0:00 sshd: root@pts/0
 1099 0         0:00 -ash
 1326 0         0:00 [kworker/u2:1-ev]
 1327 0         0:00 bash make_container.sh container_minimo/
 1337 0         0:00 bash
 1412 0         0:00 bash make_container.sh container_minimo/
 1422 0         0:00 bash
 1427 0         0:00 ps

Opa! Eu consigo visualizar o PID do outro container, o que é uma vulnerabilidade bem grande. Com isso, eu poderia encerrar o outro container facilmente:

qqyioEo0gM-# kill -9 1327

Saindo do container atual, vamos verificar se o container que estava em background ainda está em execução:

qqyioEo0gM-# exit
~# ps aux | grep make_container.sh
1447 root      0:00 grep make_container.sh

Logaro Namespace. De acordo com a Wikipedia:

Namespaces are a feature of the Linux kernel that partitions kernel resources such that one set of processes sees one set of resources while another set of processes sees a different set of resources. The feature works by having the same namespace for a set of resources and processes, but those namespaces refer to distinct resources. Resources may exist in multiple spaces. Examples of such resources are process IDs, hostnames, user IDs, file names, and some names associated with network access, and interprocess communication.

. . .

Three syscalls can directly manipulate namespaces:

clone,   flags to specify which new namespace the new process should be migrated to.

unshare,     allows a process (or thread) to disassociate parts of its execution context that are currently being shared with other processes (or threads)

setns,  enters the namespace specified by a file descriptor.

Para o nosso caso, utilizaremos o comando unshare.

Modifique a função launchContainer() dentro do arquivo make_container.sh da seguinte forma:

function launchContainer() { 
    PS1="$containerName-# " \
    unshare --mount --uts --ipc --net --pid --fork --user --map-root-user \
    chroot /tmp/$containerName/overlay \
    bash -c "mkdir /proc;
    mount -t proc none /proc;
    bash"
}

existe uma flag do unshare que já monta o procfs para nós (--mount-proc), o que significa que bastaria instanciar o shell (sem necessidade de rodar a flag -c e os respectivos comandos criando & montando o procfs)

Vamos instanciar um processo aleatorio em background novamente:

~# sleep 6000 &

Verificando qual o PID do mesmo:

~# echo $!
1107

Agora, vamos entrar num container e tentar encerrar esse processo:

uaLxGqwWPW-# kill -9 1107
bash: can't kill pid 1107: No such process

Epa! Vamos verificar quais processos podemos enxergar:

uaLxGqwWPW-# ps
PID   USER     TIME  COMMAND
    1 0         0:00 bash
    4 0         0:00 ps

Pronto! Criamos um novo namespace isolado do host :D

CGroups

Bem, a ultima questão é o fato de que nosso container tem acesso irrestrito ao uso de recursos computacionais do host.

Significa que pode usar quanta cpu, ram etc precisar

Isso é um problema pois, imagine o seguite cenario:

voce tem um servidor com 500tb de armazenamento, 512gb de ram, cpu brabo etc
e ai decide alugar esse servidor da seguinte forma: 
qualquer pessoa manda o container da aplicação que tem, e esse servidor vai rodar elas

imagine que Joao mandou o container dele contendo o e-commerce dele, e a Ana mandou o container dela contendo um site de noticias

durante a black friday, houve um pico de acessos no e-commerce do joao, que sugou todos os recursos computacionais do servidor (host)

o servidor travou e, consequentemente, parou de rodar o container da Ana tambem

obs: esse ~storytelling foi roubado daqui

Vamo bota a mao na massa entao.

Primeiramente, vamos instalar o cgroup-tools:

~# apt install cgroup-tools -y

Logar Fontes:

Diferenças entre VMs e Containers

Tempo estimado de leitura: 3min

Recomendação de materiais

Relembrando FSO

Conceito"Definição"
Sistema OperacionalKernel + Apps de Sistema
Kernelcoração do S.O., primeiro programa executado quando se inicia um SO;
responsavel por gerenciar recursos do computador [uso da cpu, memoria ram, etc] de acordo com as demandas de Apps de Sistema e Apps comuns
A interface entre Software e Hardware
Apps de Sistemasistema de arquivos, programas de network, drivers etc;
(nao sao os programas "comuns", como navegador, vscode, discord etc)

Aprofundando um pouco em Máquinas Virtuais

Existem dois tipos de Hypervisors (softwares que lidam com VMs):

DistinçõesTipo 2Tipo 1
Conhecido como:Virtual Machine Monitor (VMM)Nativo/Bare Metal
Softwares:Qemu, VMware Workstation, Oracle Virtual BoxXen HVM, KVM*, VMware vSphere
Onde roda:Em cima do sistema operacional que voce estiver utilizandoNo Hardware do seu computador (usa a bios e td mais)
Na prática:O mais comum/conhecido: você instala um aplicativo no seu PC, e nele tem uma tela pra você adicionar os SO's que quiser. Então, voce pode bootar em qualquer um desses SO's que estarão disponiveis em janelas de aplicativo diferentes.No geral, em vez de instalar um sistema operacional, se instala o Hypervisor, e neste você será capaz de instalar varios S.Os (chamados de guests) distintos; A diferença disso p/ um dual-boot, por exemplo, é que multiplos SOs podem ser executados simultaneamente

Ou seja, no tipo 1, o Hypervisor pode ser considerado um Kernel; no tipo 2, o Hypervisor se aproveita do Kernel utilizado para gerenciar os recursos de suas VMs

Lembrando que cada máquina virtual, naturalmente, terá seu próprio Sistema Operacional e, consequentemente, seu próprio Kernel, também!


Fontes:

Aprofundando um pouco em Containers

Containers consistem de virtualizações do Sistema Operacional (em vez do Hardware). Têm como caracteristicas o isolamento e gerenciamento de recursos. Isso é possível pela existencia das seguintes funcionalidades do Kernel Linux:

  • Namespaces: responsavel pelo isolamento [processos distintos enxergam recursos (hostnames, ids de usuarios, ids de processos, etc) distintos]

  • Cgroups: responsavel pelo gerenciamento de recursos [limita, prioriza e controla grupos de processos quanto ao uso de CPU, memória, I/O, network, etc]

Ou seja, com apenas 1 Kernel (o do computador que está utilizando agora) consegue-se instanciar inumeros containers (todos se utilizando do seu Kernel), sendo que cada um destes é apenas um processo a mais no seu computador.

Justamente por isso, você não conseguiria rodar um container com imagem Windows num pc Linux: via de regra, o kernel especificado no seu Dockerfile tem de ser compativel com o sistema operacional do seu computador.

*mas é possivel utilizar imagens linux dentro do windows, caso utilize o WSL (Windows Subsystem for Linux)


Fontes:

Resumo

VirtualizaçãoComportamento
Máquinas Virtuais
Container DockerContainer

Ou seja, num cenário onde você gostaria de rodar seu app multiplas vezes simultaneamente (por questões de escalabilidade, por exemplo):

VMsContainers
Necessitaria de 1 máquina virtual para cada instancia;
cada máquina virtual necessita, normalmente, de dezenas de GB para armazenar no Host
geralmente precisa-se de alguns minutos para bootar
necessita-se de apenas 1 imagem (que pesa lá seus 200mb~1gb) e pode-se instanciar infinitas vezes, de forma quase instantanea

Conceitos de Docker

Tempo estimado de leitura: 6min

Dockerfile

O Dockerfile provê instruções de como se construir a imagem de um container (usando o comando docker build -t <nome_dessa_img> <caminho_do_dockerfile>). Começa a partir de alguma imagem Base (usando o comando FROM), seguido de quaisquer outras instruçoes necessárias

Então, a gente "compila" esse código, tendo como resultado uma imagem que poderemos interagir com.

principais comandos

Comando Descrição Uso
ARG Define uma variavel que o usuário pode passar ao buildar a imagem (usando a flag --build-arg

essa variável só fica disponivel nas etapas de build da imagem (ou seja, só no dockerfile, mas nao no container)

nao use isso pra passar 'segredos' já que os valores podem ser acessados usando docker history

ˢᶦⁿᵗᵃˣᵉ ↴

ARG <name>[=<default value>]

ᵉˣᵉᵐᵖˡᵒ ↴

ARG version
ARG user_name=admin

como se usaria:

$ docker build --build-arg version=1.0 -t <nome_img> .

$ docker build --build-arg version=1.1 \
               --build-arg user_name=joao \
               -t <nome_img> .
ENV Define uma variavel de ambiente que será utilizada tanto em build-time como em run-time (ou seja, os containers em execução também possuem essa ENV definida). O usuário pode passar o valor utilizando a flag --env ou -e

nao use ENV caso so precise do valor em build-time

ˢᶦⁿᵗᵃˣᵉ ↴

ENV <key>=<value> ...
ENV <key> <value> # com essa sintaxe, apenas um por linha

ᵉˣᵉᵐᵖˡᵒ ↴

ENV NODE_ENV="development"

ENV POSTGRES_USER="username" \
    POSTGRES_PASSWORD="password" \

ARG build_value
ENV run_value=$build_value

como se usaria:

# cenario 1
$ docker run --env NODE_ENV=production <nome_img>


# cenario 2
$ docker build --build-arg build_value=5 -t <nome_img> .

$ docker run -e run_value=8 <nome_img>
FROM especifica a imagem base que será utilizada

primeiro busca-se essa imagem localmente, caso nao encontre, busca-se no dockerhub o repositorio mais adequado

ˢᶦⁿᵗᵃˣᵉ ↴

FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]

ᵉˣᵉᵐᵖˡᵒˢ ↴

padrão
FROM node:14-alpine

usando imagem de outro registry (ECR da AWS)
FROM public.ecr.aws/micahhausler/alpine:3.14.0

recebendo plataformas da cli como ARG
ARG architecture
FROM --platform=linux/${architecture} openjdk
LABEL adiciona metadados a imagem.
é feito através de pares de key-value (criados por você mesmo)

isso pode ser, então, verificado com o comando docker inspect

ˢᶦⁿᵗᵃˣᵉ ↴

LABEL <key>=<value> <key>=<value> <key>=<value> ...

ᵉˣᵉᵐᵖˡᵒ ↴

LABEL aleatorio.nome="valor1"
LABEL aleatorio.data="valor2"
LABEL chave1="c1" chave2="c2" \
      chave3="c3"
WORKDIR define qual será o diretorio de trabalho em build-time (o caminho relativo de qualquer comando RUN, CMD, ENTRYPOINT, COPY e ADD será esse diretório);

caso o diretório não exista, este será criado

caso passe um caminho relativo (não-absoluto), este será usado em relação ao ultimo WORKDIR definido

ˢᶦⁿᵗᵃˣᵉ ↴

WORKDIR /path/to/workdir

ᵉˣᵉᵐᵖˡᵒ ↴

WORKDIR /pasta1
WORKDIR /pasta2
WORKDIR pasta3

o resultado seria:

.
├── pasta1
└── pasta2
         └── pasta3 <diretorio atual>

USER configura qual será o usuário (opcionalmente o grupo, também [caso não especifique, será root]) que será utilizado dali em diante (tanto em build-time como run-time)

o usuário tem que ser criado por você previamente no dockerfile

ˢᶦⁿᵗᵃˣᵉ ↴

USER <user>[:<group>]

ᵉˣᵉᵐᵖˡᵒ ↴

RUN groupadd -r grupo \
 && useradd usuario --groups grupo

USER usuario
USER usuario:grupo
COPY

copia arquivos/diretorios do Host (em relação ao contexto da build) para o Container (em relação ao WORKDIR)


pode-se utilizar wildcards nos caminhos advindos do host:

  • * da match em qualquer sequencia de chars que não sejam separador (/, ou \ no windows)
  • ? o mesmo, mas pra 1 char só

--chown não funciona no windows
não é mt recomendado o uso disso, já que o usuario só precisa de permissão pra executar, e não o ~domínio/ownership

para efeito de comparação/entendimento:


ˢᶦⁿᵗᵃˣᵉ ↴

COPY [--chown=<user>:<group>] <caminho_no_seu_pc>... <destino_no_container>
COPY [--chown=<user>:<group>] ["<caminho_no_seu_pc>",... "<destino_no_container>"]

A versão com [] é utilizada qnd se tem whitespace no path (ex: '/home/joao/minha pasta/')

ᵉˣᵉᵐᵖˡᵒ ↴

COPY minha_pasta/ /tmp/my_folder
COPY ["package*.json", "yarn.lock", "/meu_app/"]
COPY . .
ADD

O mesmo que o COPY, mas:

  1. a fonte pode ser uma URL também (não recomendado, melhor usar RUN curl ou RUN wget etc)
  2. auto-extrai arquivos .tar

Mais sobre as diferenças aqui.

ˢᶦⁿᵗᵃˣᵉ ↴

ADD [--chown=<user>:<group>] <src>... <dest>
ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]

ᵉˣᵉᵐᵖˡᵒ ↴

ADD hom* /mydir/
ADD rootfs.tar.xz /
ADD https://example.com/music.mp3 /usr/src/things/
CMD

Provê algum padrão pro container em execução

só pode um CMD por dockerfile (caso tenham multiplos apenas o ultimo é considerado)

o formato exec não invoca um shell (a nao ser que especifique [ex: CMD ["sh", "-c", "echo $HOME"] ])

justamente por isso, CMD [ "echo", "$HOME" ] não teria o resultado esperado já que não há um shell pra processar/substituir a variavel

ˢᶦⁿᵗᵃˣᵉ ↴

# exec
CMD ["executable","param1","param2"]

# parametros pro ENTRYPOINT
CMD ["param1","param2"]

# shell
CMD command param1 param2

ᵉˣᵉᵐᵖˡᵒ ↴

CMD echo "This is a test."
CMD ["npm", "start"]
ENTRYPOINT

permite configurar um container pra ser rodado como um executavel

ˢᶦⁿᵗᵃˣᵉ ↴

# exec
ENTRYPOINT ["executable", "param1", "param2"]

# shell
ENTRYPOINT command param1 param2

ᵉˣᵉᵐᵖˡᵒ ↴

# exec
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

# shell
ENTRYPOINT exec top -b

BuildKit - Segredos

No Windows/Mac, BuildKit já é utilizado por padrão; no Linux, não. Para saber como ativar, leia aqui

Caso queira, o comando abaixo verifica se o arquivo de configurações personalizadas já existe e, caso nao exista, cria o mesmo já especificando o uso do Buildkit:

$ test -f /etc/docker/daemon.json || (echo '{ "features": { "buildkit": true } }' |  sudo tee /etc/docker/daemon.json)

~~ dar exemplo com segredo do Github, JWT ou bCrypt etc

otimizando build

docker layered cache

torre de hanoi

Only the instructions RUN, COPY, ADD create layers. Other instructions create temporary intermediate images, and do not increase the size of the build.

Docker cacheia as instruções no Dockerfile por camadas. Fazendo um paralelo com a Torre de Hanoi: pense que o Dockerfile é a torre (de cabeça pra baixo); entao, sempre que ouver alteração em alguma linha/camada, todas as peças que estão em cima são descartadas.

Por isso é tão frequente ver a seguinte estrutura em um projeto JS:

Correto Incorreto
COPY package*.json .

RUN npm install

COPY . .
COPY . .

RUN npm install

Já que o comando COPY . . copia todos os arquivos, e o código-fonte está em constante alteração, isso significa que toda vez que fosse buildar essa imagem, haver-se-ia de descartar o comando seguinte (npm install), resultando na re-execução desse comando.

comando onbuild

multistage build

dockerignore

volumes

docker pull

registry x repositories

https://stackoverflow.com/a/34004418/11947314

running a container

interactively

background

log files

remove containers

remove images

docker ps

docker swarm

Instalação do Docker

Tempo estimado de leitura: 1min

Docker

O método mais recomendado é indo diretamente pela fonte: Install Docker

Mas, por questões de simplicidade:

$ curl -sSL https://get.docker.com/ | sh
$ exec $SHELL
$ sudo usermod -aG docker $(whoami)

Verifique se consegue rodar normalmente:

$ docker run hello-world

Docker-compose

Novamente, o mais recomendado é ir direto pela fonte: Install Docker Compose

Mas, por simplicidade -> para instalar a versão mais atual do docker-compose, execute o seguinte script:

url="https://github.com/docker/compose/releases/latest"

latest_version=$(curl $url -s -L -I -o /dev/null -w '%{url_effective}' | { read -r redirected_url; echo "${redirected_url##*/}"; })

curl -L https://github.com/docker/compose/releases/download/"$latest_version"/docker-compose-"$(uname -s)"-"$(uname -m)" > /usr/local/bin/docker-compose

chmod +x /usr/local/bin/docker-compose

Verifique que instalou corretamente:

$ docker-compose -v

docker-compose version 1.29.2, build 5becea4c

Appointment-Octopus

Tempo estimado de leitura: 1min


Esse aplicativo consiste de uma ideia muito simples: agendamento de consultas. Em tese, seria utilizado por um profissional da saúde/personal trainer (por exemplo), onde os pacientes podem visualizar os horarios disponíveis e agendar uma consulta (com o pagamento antecipado).

Resumão Documentação

Tempo estimado de leitura: 3min


Nesse capítulo você terá um resumo sobre a importância de um repositório para guardar informações, digamos, mais 'técnica' sobre o desenvolvimento de um projeto.

Explicação

Independente do quão grande um projeto Open Source pode ser, é sempre importante manter todos os passos documentados, até porque, outras pessoas precisam saber o porquê das coisas.

O porquê escolheu essa arquitetura, como fazer para compilar seu código e como utilizar o trabalho que foi feito até aquele momento. Existem vários modelos de documentos que regem esses 'Artefatos', mas o importante mesmo é saber explicar para os outros como utilizar seu produto, e o README é um bom lugar para colocar algumas dessas informações como uma breve descrição do nome e o que seu produto faz.

Docs Appointment

Acesse aqui o Repositório.

Embora não tenhamos criado todo o aparato de documentação que geralmente aparece, como documento de visões e de arquitetura (dentre outros,como políticas de branch e de commits), listamos aqui alguns artefatos produzidos que nos ajudaram a visualizar melhor o que gostaríamos de produzir.

Para conseguirmos ter aquela ideia visual melhor sobre a nossa aplicação, fizemos alguns casos de uso e um protótipo.

Licença

Como a maioria dos Commits iniciais de um repositório, adicionamos a licença, que é uma espécie de documento contratual que te permite comercializar, editar, distribuir seu software. Embora não vamos entrar em tantos detalhes, é bom saber que existem vários tipos de licença, e com restrições de uso, tanto para utilização como para desenvolvimento, e divergem, então é sempre bom parar um pouco para ler mais sobre algumas, principalmente para saber qual é aquela que melhor se encaixa no contexto do seu produto e/ou organização.

No nosso caso, a escolhida foi MIT License que em resumo é: Uma licença que "permite usar, copiar, modificar, mesclar, publicar, distribuir, sublicenciar e / ou vender", sem qualquer restrição. Então disponibilizamos do jeito que está, qualquer um pode usar, e não tem nenhuma garantia nossa em cima do que foi produzido.

Use Cases

Pode visualizar os casos de uso aqui.

Tanto os casos de uso como o protótipo caminharam juntos na sua estruturação, já que os casos de uso eram para saber tudo o que um usuário poderia fazer e como ele o faria dentro da plataforma, desde coisas simples, mas que foram descritas, como:

- *logar* na aplicação
- escolher uma consulta;
- adicionar um cartão;
- adicionar um método de pagamento;

Enquanto obtínhamos essas descrições, íamos colocando esses elementos em um protótipo - efetuar a compra;

Protótipo

Pode acessar o protótipo aqui.

Paleta de Cores + Tipografia

A parte principal.

Paleta de cores

Com isso construímos todas as telas de forma consistente.

Via de regra, as telas são compostas de: 60% cor básica + 30% cor primária + 10% cor de acentuação

As cores primárias são a maior identidade visual da sua aplicação, as cores básicas são utilizadas para background e/ou texto (escala de cinzas) e, por fim, a cor de acentuação é utilizada para destacar algo relevante (normalmente alguma ação)

Por fim, as cores de controle são padrões universais (ex: verde = sucesso, vermelho = falha/perigo, etc)

Telas do usuário

fluxo 1 fluxo 2 fluxo 3

DB - Data Base

Pode visualizar as especificações do banco de dados aqui.

Nosso Banco de dados, uma das nossas primeiras tomadas de decisão foi tentar entender como seria o comportamento entre os nossos serviços, sendo assim, quais seriam os dados que de cada um iria guardar e quais dados seriam manipuladaos por cada um.

Depois de esquematizarmos quiais seriam todas as "Entidades", que nada mais são do que estruturas com mais de uma informação importante para se guardar, nosso MER (Modelo Entidade Relacionamento) esquematizamos um modelo lógico e conceitual de como seria o nosso banco.

Caso queiram saber, utilizamos o BrModelo para auxiliar na criação desses modelos, o MER e o diagrama Lógico de bancos.

Esses passos nada mais são que etapas para execução de uma modelagem de um tradicional banco de dados. Logo após essa modelagem levantamos scripts em SQL para criação e população de dados nesse banco, basicamente para saber se a modelagem foi realizada com êxito.

Resumão Backend

Tempo estimado de leitura: 1min


Nesse capítulo você terá um resumo sobre a ideia do backend, estruturado como microsserviços, e vamos explicar também cada microsserviço contido dentro do projeto.

Microsserviços

Diferentemente de uma arquitetura moldada em um software monolítico (uma aplicação onde todos os processos estão acoplados em um único lugar), optamos por uma organização de desenvolvimento estruturada em microsserviços, onde cada pequeno serviço tem sua responsabilidade independente e se comunicam utilizando de APIs que são bem definidas. Dessa forma facilita designar a responsabilidade de cada serviço a pequenas equipes que conseguem se autogerir.

"As arquiteturas de microsserviços facilitam a escalabilidade e agilizam o desenvolvimento de aplicativos, habilitando a inovação e acelerando o tempo de introdução de novos recursos no mercado."
fonte: https://aws.amazon.com/pt/microservices/

Entretanto, não seguimos 100% a risca: utilizamos um unico banco de dados para todos os serviços.

Appointments

https://github.com/appointment-octopus/appointments

É o nosso microsserviço responsável por todo o CRUD (Create, Read, Update e Delete ) das consultas. É possível ver as consultas de um usuário (tanto as que passaram, como as que ainda acontecerão) e, também, listar horários disponíveis para agendar.

Stack: Javascript + Express.js + Sequelize + Jest

Auth

Microsserviço responsável pela autenticação do usuário. Cria usuário, faz login (e gera um token jwt), logout etc.

Stack: Go + mux + Gorm + redigo (cache do usuário)

Resumão Frontend

Tempo estimado de leitura: 1min


Nesse capítulo você terá um resumo sobre a ideia do Frontend, que se encontra como um microsserviço no nosso projeto.

Front

Nosso Frontend no início tinha o objetivo de ser composto por um aplicativo de usuário, e por um de adminitrador.

O de usuário (UserApp), consistia em:

  • login;
  • navegação;
  • escolha do tipo de consulta;
  • adicionar métodos de pagamento;
  • agendamento da consulta propriamente dito.

Quanto ao de administrador, iria "liberar" dias possíveis juntamente com seus horários para as consultas poderem ser realizadas.

Devido ao escopo que propomos e até onde conseguimos produzir, o único que foi 'planejado e construído', ao menos de forma visual foi o app de Usuário.

UserApp

Microsserviço responsável pela parte visual, logo o nosso Frontend. Como nos propomos a desenvolver uma aplicação mobile utilizamos React Native, como framework, e com o auxílio do StoryBook que é uma ferramenta open source para construção de componentes de interface, e o desenvolvimento de páginas.