From 51c01ea436109121a3d9a21e6fa4d69d92e88f95 Mon Sep 17 00:00:00 2001 From: DavidsonGM Date: Mon, 31 Oct 2022 13:45:38 -0300 Subject: [PATCH 01/30] =?UTF-8?q?:recycle:=20refatorando=20sec=C3=A7=C3=A3?= =?UTF-8?q?o=20da=20ger=C3=AAncia?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SUMMARY.md | 6 +- execucao/projetos/gerencia.md | 347 ------------------ execucao/projetos/gerencia/README.md | 19 + .../projetos/gerencia/inicio-de-projeto.md | 100 +++++ execucao/projetos/gerencia/repositorio.md | 21 ++ execucao/projetos/gerencia/sprints.md | 57 +++ execucao/ruby-on-rails/gems.md | 153 ++++++++ 7 files changed, 355 insertions(+), 348 deletions(-) delete mode 100644 execucao/projetos/gerencia.md create mode 100644 execucao/projetos/gerencia/README.md create mode 100644 execucao/projetos/gerencia/inicio-de-projeto.md create mode 100644 execucao/projetos/gerencia/repositorio.md create mode 100644 execucao/projetos/gerencia/sprints.md create mode 100644 execucao/ruby-on-rails/gems.md diff --git a/SUMMARY.md b/SUMMARY.md index 0c2e321..50a6f03 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -31,6 +31,7 @@ * [Soluções para problemas comuns](execucao/git/problemas-comuns.md) * [Ruby on Rails](execucao/ruby-on-rails/README.md) * [Instalação](execucao/ruby-on-rails/instalacao.md) + * [Gems](execucao/ruby-on-rails/gems.md) * [Obtenção de licença Jetbrains](execucao/ruby-on-rails/licenca-rubymine.md) * [Bash de Ubuntu no Windows](execucao/ruby-on-rails/bash-de-ubuntu-no-windows.md) * [Geradores Rails](execucao/ruby-on-rails/generators.md) @@ -56,7 +57,10 @@ * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) * [Cloudinary](execucao/projetos/cloudinary.md) - * [Gerência de projetos](execucao/projetos/gerencia.md) + * [Gerência de projetos](execucao/projetos/gerencia/README.md) + * [Início de projeto](execucao/projetos/gerencia/inicio-de-projeto.md) + * [Repositório](execucao/projetos/gerencia/repositorio.md) + * [Sprints](execucao/projetos/gerencia/sprints.md) * [Finalização de Projetos](execucao/projetos/finalizacao.md) * [Gitlab](execucao/projetos/gitlab/README.md) * [CI-CD](execucao/projetos/gitlab/ci-cd.md) diff --git a/execucao/projetos/gerencia.md b/execucao/projetos/gerencia.md deleted file mode 100644 index 12a66ee..0000000 --- a/execucao/projetos/gerencia.md +++ /dev/null @@ -1,347 +0,0 @@ -# Gerência de projetos - -O gerente de um projeto possui funções essenciais à qualidade de vida de um projeto, como a criação do projeto em si, a gerência das *sprints* do projeto e a comunicação dos detalhes do projeto para os outros membros de equipe nas reuniões gerais. Em suma, o gerente do projeto conduz a execução do projeto, tomando as medidas necessárias para que seu desenvolvimento seja bem sucedido. - -Está pagina contém as informações necessárias para se gerenciar um projeto de maneira adequada. - -## Início do projeto - -O primeiro passo em um projeto de software consiste na criação e configuração do repositório em que o projeto será desenvolvido. Isso inclui criar o repositório do projeto, as estruturas de pastas para abrigar o código fonte e outros recursos e configurar adequadamente os arquivos de controle de versão, os arquivos de documentação do projeto, a utilização de bibliotecas externas \(gems\), e o banco de dados do projeto. - -Embora isso pareça ser muita coisa, iremos detalhar melhor cada passo nas seções abaixo. - -### Projeto Rails - -O primeiro passo na inicialização do projeto é a criação do projeto na framework [*Ruby on Rails*](../ruby-on-rails/README.md). Para isso, vamos escolher, utilizando o RVM ou Rbenv, uma versão de Ruby esteja um *minor release* atrás da última versão \(ou seja, se a última versão de Ruby é a 2.x, utilize a versão 2.x-1 para o projeto\). Na dúvida, utilize a versão de Ruby utilizada no último projeto. Isso evita que tenhamos que lidar com os bugs inerentes à versão mais nova do Ruby e das gems que o acompanham. Caso algum dos membros do projeto não tenha um gerenciador de versão de Ruby instalado \(como RVM e Rbenv\), ajude ele com a instalação \(esse tipo de software é praticamente essecial para se trabalhar em vários projetos, cada um com uma versão diferente de Ruby\). - -Após escolher a versão de Ruby desejada, será necessário realizar a instalação de algumas gems \(que são as bibliotecas do Ruby\). Para instalar uma gem, basta rodar o comando `gem install nome_da_gem`. Com isso em mente, instale as gems *rails* \(framework de desenvolvimento de websites e serviços web\), *mysql2* e *pg* \(gems para o MySQL e PostgreSQL respectivamente, os sistemas gerenciadores de bancos de dados, ou SGBDs, mais utilizados\). Além dessas gems, será necessário instalar os SGBDs MySQL e PostgreSQL em seu sistema operacional \(assunto que **não** será coberto nessa página\). - -Após instalar as gems e os SGBDs em seu computador, caberá a você, como gerente de projetos, decidir qual SGBD utilizar no projeto. O MySQL é mais amigável para iniciantes e possui uma melhor forma de visualizar e manipular diretamente o banco de dados, mas requer mais tempo para ser configurado e a utilização de uma gem \([*figaro*](######figaro)\) para gerenciar os credenciais de login. Já o PostgreSQL pode ser utilizado com quase nenhuma configuração adicional, mas possui uma instalação mais complicada e não permite que o usuário manipule o banco de dados com facilidade. Caso seja sua primeira vez gerenciando um projeto, fale com o diretor de projetos ou com um membro mais experiente para pedir uma recomendação \(eu, pessoalmente, recomendaria o MySQL\). - -Após escolher o SGBD a ser utilizado, chegou a hora de criar o projeto. Para isso, rode o comando `rails new nome_do_projeto_em_snake_case -d {mysql | postgresql}`, escolhendo o último argumento para coincidir com o SGBD desejado. O framework cuidará da criação de pastas e arquivos para o desenvolvimento do projeto. - -Caso você tenha escolhido o MySQL, configure a gem [*figaro*](######figaro) para gerenciar credenciais e, caso você **não** queira utilizar o usuário *root* no projeto, crie as tabelas de desenvolvimento e testes e um usuário com permissão de acesso a elas. Caso você tenha escolhido o PostgreSQL, preencha o arquivo `config/database.yml` com os credenciais do SGBD \(em sistemas operacionais com o *kernel* Linux, você pode preencher apenas o seu nome de usuário e apagar o campo de senha caso você tenha optado, na instalação do PostgreSQL, por utilizar sua conta de usuário do sistema operacional como um usuário do SGBD\). - -Por fim, tente executar o projeto utilizando o comando `rails s`. Caso a execução do projeto não gere alguma mensagem de erro, adicione todos os arquivos gerados ao controle de versão sob o nome 'Commit inicial'. Caso contrário, corrija os erros do projeto **antes** de realizar o commit inicial. - -### Repositório - -A criação do repositório para o projeto deve ser feita no grupo da Struct do GitLab. O nome do repositório geralmente segue o padrão `projeto_nome` ou `projeto_codinome` \(como gerente de projeto, sinta-se livre para criar um codinome épico para o projeto :grinning:\). Configure o repositório como privado e **não** inicialize ele com um README, pois o Rails já gera esse arquivo para você. Após criar o repositório, faça o upload dos arquivos gerados pelo projeto de Rails para o repositório. - -### Segundo commit - -Após a criação do repositório, você deverá fazer um segundo commit na branch main que faça todas as configurações necessárias no projeto. Esse segundo commit deve colocar no README informações básicas do projeto, colocar no projeto arquivos padrão que devem estar presentes em qualquer projeto da Struct e configurar as gems que o projeto irá utilizar. Mais detalhes são fornecidos nas subseções abaixo. Após executar **todos** os passos de **cada** subseção, realize um commit com o nome "Estrutura inicial" adicionando as mudanças que você fez. - -#### README - -O README deve conter as dependências do projeto \(versão do Ruby, banco de dados, etc...\) e os dados do projeto em si, os quais incluem a especificação do projeto \(bem resumida, em uma única frase\), o codinome do projeto \(se existir\), a data de assinatura do contrato, o prazo de desenvolvimento do projeto e a data prevista de entrega do projeto. - -Não utilize o README como uma lista de tarefas, pois isso pode ser feito por meio de *issues*. - -#### Arquivos padrão - -No momento, existem 2 arquivos padrão que devem estar presentes no projeto: o `.gitignore` e o script `start.sh`. - -##### Arquivo .gitignore - -Como sabemos, o `.gitignore` é utilizado para impedir que arquivos locais sejam acidentalmente adicionados ao repositório. Arquivos que geralmente devem ser ignorados incluem arquivos de configurações pessoais \(`.bundle`, `.vscode`, `.idea`, `.DS_store`\), pastas de caches \(`tmp` e `storage`\), logs \(arquivos `*.log` e pasta `log`\) e módulos do *node js* \(a pasta `node_modules`\). - -**Nunca** coloque arquivos que afetam o desenvolvimento do projeto \(como `Gemfile`\) no `.gitignore`. - -É **essencial** que a configuração do `.gitignore` seja feita no **início** do projeto, pois problemas relacionados a esse arquivo são um pesadelo para serem resolvidos. - -Segue, abaixo, um template de arquivo `.gitignore`: - -``` -# Environment normalization: -*.DS_Store -.idea -.rvmrc -.vscode -/.bundle -/vendor/bundle - -# Ignore all logfiles and tempfiles: -/log/* -/tmp/* -!/log/.keep -!/tmp/.keep - -# Ignore uploaded files in development: -/storage/* -!/storage/.keep - -# Ignore node_modules: -node_modules/ - -# Ignore yarn files: -/yarn-error.log -yarn-debug.log* -.yarn-integrity - -# Ignore precompiled javascript packs: -/public/packs -/public/packs-test -/public/assets - -# Ignore Byebug command history file: -.byebug_history - -# Ignore project secrets: -/config/initializers/secret_token.rb -/config/master.key -``` - -##### Arquivo start.sh - -O arquivo `start.sh` é um script de execução utilizado para o *Docker*, mas que também pode ser utilizado para que novos integrantes do projeto inicializem o projeto sem maiores dificuldades. - -Segue, abaixo, um template de arquivo `start.sh`: - -``` -#!/bin/sh -rails db:migrate -bundle exec puma -C config/puma.rb -``` - -#### Configuração de gems - -As *gems* do *Ruby on Rails* são bibliotecas externas que fornecem funcionalidades ao seu projeto. Dependendo do projeto em questão, os desenvolvedores precisarão utilizar gems que não vem configuradas por padrão em um projeto de *Ruby on Rails*, de forma que cabe ao gerente de projetos o trabalho de analisar quais gems serão necessárias no decorrer do projeto, colocar essas gems no `Gemfile` e realizar qualquer outra configuração para que essas gems funcionem adequadamente \(a maioria das gems requer alguma configuração adicional para funcionar\). - -Além de configurar as novas gems, o gerente de projeto também deve verificar qualquer aviso de *deprecated gem* gerado pelo Rails após a execução dos comandos `bundle install` e `rails s` e corrigir esses avisos para evitar problemas com o projeto. - -Idealmente, esse processo de configuração deve ser feito no *começo* do projeto para que todos os desenvolvedores tenham acesso às funcionalidades adequadas para o desenvolvimento do projeto \(e para que eles não tenham que ficar rodando `bundle install` toda hora\). Lembre-se de, após configurar ou substituir uma gem, sempre tentar executar o projeto com o comando `rails s` para garantir que o funcionamento do projeto não foi afetado por alguma gem mal configurada. - -Para facilitar o uso dessa seção pelo gerente de projeto \(ou qualquer outro membro da Struct\), vamos dividir as gems que devem ser configuradas manualmente em duas seções: *gems essenciais* e *gems úteis*. Também vamos listar alguns casos comuns de *deprecated gems* que devem ser substituídas. - -##### Gems essenciais - -As *gems essenciais* são gems que devem ser configuradas em **todos** os projetos da Struct de *Ruby on Rails*, por proporcionarem funcionalidades que são necessárias ou para o desenvolvimento do projeto ou para a garantia básica da qualidade do projeto. Como gerente de projeto, é sua responsabilidade incluir e configurar **todas** as gems dessa seção no projeto. - - -###### RSpec - -O RSpec é uma framework de testes utilizado para redigir testes de unidade e testes de módulo. - -Para instalar e configurar o RSpec e o factory bot, siga os passos descritos nas páginas de [instalação do Rspec](../rspec-e-factory-bot/instalacao-rspec.md) e [de instalação do factory bot](../rspec-e-factory-bot/instalacao-factory-bot.md) - -###### Devise and Simple Token Authentication - -Duas gems que trabalham juntas para a criação de um usuário com login funcional (com proteção de sua senha em um token). - -Com a gem Devise é possível criar uma model especial (device) com todas as características para permitir a criação de um usuário, como e-mail e senha. - -Já a gem Simple Token Authentication garante que o acesso a rota da controller seja permitido apenas por quem esteja com o token certo, ou seja, logado corretamente. - -1. Adicione o trecho de código abaixo ao arquivo `Gemfile`: - -``` -gem 'devise' -gem 'simple_token_authentication' -``` - -2. Execute o comando `bundle install` para instalar as gem adicionadas ao `Gemfile`. - -3. Execute o comando `rails g devise:install` para instalar todos os arquivos do devise. - - -###### Rubocop - -Rubocop é um analisador e formatador de código que serve para padronizar e -melhorar seu código como um todo. - -Essa gem é bem flexível (podendo ser configurada e estilizada) e garante um código -mais limpo e organizado, já que os projetos são feitos em grupos e, assim, cada um -tem sua forma de organizar distinta. - -1. Adicione o trecho de código abaixo ao arquivo `Gemfile`: - -``` -# Rubocop -gem 'rubocop', '~> 1.24.1', require: false -gem 'rubocop-rails', require: false -gem 'rubocop-rspec', require: false -``` - -2. Execute o comando `bundle install` para instalar as gem adicionadas ao `Gemfile`. - -3. Rode o comando `robocop -A` no seu terminal do projeto para -executar a verificação de seu código. - - Note que, provavelmente, serão identificadas ofensas que podem não ser corrigidas automaticamente, sendo necessário concertá-las manualmente antes de ser feito o - commit. - -###### Regras do rubocop - -Ao utilizar o rubocop, muito provavelmente você não vai querer utilizar todas as suas regras como padrão, já que em alguns casos vezes elas não fazem muito sentido ou são bem chatinhas e acabam atrapalhando mais do que ajudando. Nesses casos, você pode configurar desabilitar algumas regras por padrão e, para isso, basta criar na raíz do projeto o arquivo *.rubocop.yml*. Neste arquivo, primeiro adicione as seguintes linhas - -``` -require: - - rubocop-rails - - rubocop-rspec - - -AllCops: - TargetRubyVersion: 2.7.2 # Mude para a sua versão do ruby - NewCops: enable - Exclude: - - db/schema.rb - - bin/bundle - -Style/Documentation: - Enabled: false - -Style/FrozenStringLiteralComment: - Enabled: false -``` - -Nesse trecho de código, estão sendo adicionadas as extensões *rubocop-rails* e *rubocop-rspec* (especificadas na gemfile), está sendo especificada a versão do ruby e habilitando os novos cops, o que será necessário para adicionar o rubocop ao CI no github, além disso, estão sendo excluídos alguns arquivos padrões do rails que podem dar problemas. caso queira ignorar um diretório inteiro, basta adicionar nessa lista ` - dir/**`. - -As últimas duas configurações estão desabilitando o cop de documentação com comentário e desabilitando o cop de Strings literais imutáveis, que introduzem uma feature que não é utuilizada com frequência em Rails e que possui pouca aplicação no momento. - -Além disso, algumas configurações do rubocop são em relação ao tamanho das coisas (métodos, classes, testes), nesse caso, é possível apenas alterar o limite máximo ao invés de desabilitar o cop, já que esses muita vezes são importantes. Por exemplo, para alterar o tamanho máximo dos métodos para 16 linhas, basta colocar -``` -Metrics/MethodLength: - Max: 16 -``` - -##### Gems úteis - -As *gems úteis* são gems que proporcionam funcionalidades desejáveis ou essenciais apenas em alguns casos, mas que não são necessárias em todos os projetos da Struct de *Ruby on Rails*. Como gerente de projeto, é sua responsabilidade ler a descrição das gems nessa seção e incluir e configurar apenas as gems que se apliquem ao projeto em questão. - -###### Figaro - -O Figaro é uma gem utilizada para gerenciar credenciais de bancos de dados em um arquivo de configuração local. Recomenda-se sua utilização em projetos que possuam *um banco de dados com o SGBD \(Sistema gerenciador de banco de dados\) MySQL* ou qualquer outro SGBD que não possua um bom sistema de armazenamento de credenciais de login \(o PostgreSQL é um caso raro de SGBD que **não** precisa da gem Figaro\). - -Para configurar o Figaro, siga os passos abaixo: - -1. Adicione o trecho de código abaixo ao arquivo `Gemfile`: - - ``` - # Database credentials: - gem 'figaro' - ``` - -2. Execute o comando `bundle install` para instalar as gem adicionadas ao `Gemfile`. - -3. Execute o comando `bundle exec figaro install` para configurar a gem. - -4. Modifique o arquivo `config/database.yml` nas linhas demonstradas abaixo: - - ``` - default: &default - # Modifique apenas as linhas abaixo e não apague ou modifique as outras linhas! - username: <%= ENV['db_user'] %> - password: <%= ENV['db_password'] %> - ``` - -5. Adicione o trecho de código abaixo ao arquivo `config/application.yml`: - - ``` - # Database credentials: - db_user: "seu_usuario_do_DB_entre_parentesis" - db_password: "sua_senha_do_DB_entre_parentesis" - ``` - -Após configurar a gem Figaro em seu ambiente, **avise os outros membros do projeto** que eles deveram executar os passos 2, 3 e 5 para configurar a gem em seus ambientes! - - -##### *Deprecated* gems - -As *deprecated gems* são gems que não recebem mais manutenção e suporte por parte de um time de desenvolvedores, devendo ser retiradas dos projetos da Struct de *Ruby on Rails*. Como gerente de projeto, é sua responsabilidade substituir todas as gems dessa seção que estão presentes no seu projeto. - -###### Chrome driver helper - -A gem `chromedriver-helper` parou de receber suporte em Março de 2019. Substitua ela no Gemfile pela gem `webdrivers`. - -###### SASS Rails - -A gem `sass-rails` parou de receber suporte em Março de 2019. Substitua ela no Gemfile pela gem `sassc-rails`. - -### Criação de branches padrão - -Um projeto bem estruturado contém um conjunto de branches padrões e um fluxo bem definido que novas features ou mudanças em features existentes devem percorrer para serem aprovadas definitivamente e chegarem aos clientes da aplicação. O gerente de projetos é o responsável por criar e configurar as branches padrões no repositório do projeto no GitLab. Também cabe ao gerente de projetos garantir que o desenvolvimento siga o fluxo definido pelas branches padrões, evitando a utilização de atalhos e os problemas gerados pela quebra do fluxo de desenvolvimento. - -Esta seção detalha as branches padrão que todo projeto da Struct deve ter, com uma breve descrição do seu propósito geral e fluxo de desenvolvimento. - -#### Develop - -A **develop** é a branch de desenvolvimento padrão. Ela deve ser marcada como *default* e *protected* nas configurações de branch do projeto. Todas as features desenvolvidas devem ser adicionadas primeiramente à *develop* por meio de um *Merge request*. - -#### Main - -A **main** é a branch da versão oficial do software para os desenvolvedores. Se possível, ela deve ser marcada como *protected* nas configurações de branch do projeto. De tempos em tempos, as features desenvolvidas na branch *develop* são levadas a branch *main* por meio de um *Merge request*, permitindo ao time de desenvolvedores revisar com mais afinco e atenção as features desenvolvidas \(tipicamente, no final da *sprint*\). Além disso, a *main* pode ser utilizada para receber diretamente correções de *bugs* ou problemas críticos, sem a necessidade dessas correções urgentes se misturarem com as features novas da *Develop*, encurtando o caminho de desenvolvimento até o cliente. - -#### Production - -A **production** é a branch da versão oficial do software para os clientes. Se possível, também deve ser marcada como *protected* nas configurações de branch do projeto. De tempos em tempos, as features testadas e revisadas presentes na branch *main* são levadas à branch *production* por meio de um *Merge request* para que essas mudanças cheguem efetivamente aos clientes do projeto e possam ser utilizadas ou visualizadas pelos usuários do produto. Tenha muito cuidado ao colocar mudanças nessa branch, pois qualquer falha ou erro terá **consequências reais** para o cliente e para os usuários da aplicação. - -## Sprints - -Uma *sprint* consiste em um conjunto de features do projeto que deve ser desenvolvido em um conjunto específico de tempo. Cada feature possui atrelada a ela uma pontuação e um membro responsável pelo seu desenvolvimento. A utilização de *sprints* é uma característica do desenvolvimento ágil e possui o intuito de validar constantemente o projeto junto ao cliente por meio de entregas constantes de features \(as *sprints* também são úteis para impedir que os desenvolvedores percam os prazos do projeto\). - -Como gerente de projetos, é seu trabalho definir todas as características de uma *sprint*, incluindo a sua duração, as features que serão desenvolvidas, a pontuação de cada feature e quem será responsável por cada feature. Também é seu trabalho gerenciar o andamento da *sprint*, garantindo que os desenvolvedores estejam desenvolvendo as features de forma adequada \(com a utilização correta de branches e com a implementação de testes e utilização de comentários\), esclarecendo quaisquer dúvidas relacionadas às regras de negócio do projeto, documentando o desenrolar da *sprint* quando ela acabar e redistribuindo features para outras *sprints* caso isso seja necessário. - -No início de uma *sprint*, é altamente recomendado que, independente das circunstâncias, seja realizada **uma reunião com a equipe de desenvolvimento.** Isso permite ao gerente de projetos tratar todas as pendências necessárias relativas à *sprint* passada e ao começo de uma nova *sprint*. - -Você também deverá criar um cartão no *Trello* no início da *sprint* para abrigar os detalhes da *sprint*. Esse cartão deverá ser atualizado no final da *sprint* ou caso alguma mudança ocorra nos detalhes da *sprint*. - -Esta seção aborda como features devem ser elaboradas e integradas ao projeto, como o desenvolvimento de uma feature deve ser feito por um membro da equipe e quais as práticas que devem ser adotadas ao final de uma *sprint*. - -### Features - -Como dissemos, uma feature é uma parte do projeto que fornece alguma funcionalidade, e o desenvolvimento do projeto será divido no desenvolvimento de várias features. As features a serem desenvolvidas devem ser derivadas dos requisitos do projeto, colhidos em reuniões com o cliente. Por fim, as features devem ser divididas de forma que possam ser desenvolvidas por um único membro do projeto \(ou seja, sem dependências *excessivas* com o código de outras features\). - -Com o intuito de deixar o desenvolvimento do projeto organizado, cada feature deve ser registrada no repositório do projeto por meio de uma *issue*, onde todos os detalhes necessários para o desenvolvimento da feature devem estar presentes. Dessa forma, qualquer desenvolvedor que **não** conheça os requisitos do projeto consegue desenvolver a feature apenas lendo a descrição da *issue*. Aliás, qualquer membro do projeto deve abrir uma issue quando perceber que uma nova feature precisará ser desenvolvida ou quando notar algum bug no projeto que não possa ser resolvido trivialmente. - -No início de uma *sprint*, o gerente de projeto escolhe quais features serão desenvolvidas nessa *sprint* a partir das *issues* contidas no repositório, priorizando as correções de bugs e as features demandadas pelo cliente do projeto. Caso não haja *issues* no repositório, o gerente deve se reunir com a equipe de desenvolvimento para registrar as features pendentes como *issues* ou notificar o diretor de projetos que é necessário colher mais requisitos com o cliente. - -Após as features serem escolhidas, elas devem ser pontuadas pela equipe de desenvolvimento. A forma de pontuar uma feature é bem simples: o gerente se reúne com a equipe de desenvolvimento, descreve o trabalho envolvido nessa feature e pede que a equipe de desenvolvimento indique um número entre os 6 primeiros números da sequência de Fibonacci \(1, 2, 3, 5, 8, 13\) que descreva quão trabalhosa a feature seria para ser desenvolvida \(incluindo testes e comentários\). Caso os desenvolvedores concordem \(em linhas gerais\) na pontuação, ela é atribuída à feature; do contrário, os desenvolvedores conversam entre si para se chegar a um consenso. Caso a feature tenha uma pontuação maior ou igual a 5, ela é denominada um *épico*, e deve ser dividida em sub-features menores. É importante frisar que **correções de bugs não valem pontos**. - -Com as features pontuadas, elas devem ser distribuídas entre os membros da equipe de desenvolvimento. É importante garantir que a distribuição seja bem feita e leve em consideração a grade horária semanal dos desenvolvedores para evitar membros ociosos ou membros atarefados excessivamente. Também é ideal que desenvolvedores novatos sejam auxiliados no desenvolvimento de features mais complexas, por meio de recursos como *pair programming* ou maior atenção do gerente de projetos. Por fim, lembre-se de atribuir as *issues* no repositório aos seus respectivos responsáveis para que qualquer um que visite o repositório do projeto possa saber quem está trabalhando no que. - -Assim que as features forem escolhidas, pontuadas e distribuídas, é hora de começar o desenvolvimento. - -### Desenvolvimento - -O desenvolvimento de uma feature em uma *sprint* deve seguir uma lógica bem definida para garantir que as diversas features desenvolvidas venham a ser adequadamente revisadas e integradas no projeto. Como gerente de projetos, você deverá entender plenamente essa lógica e explicá-la aos demais desenvolvedores. - -Como dito anteriormente, seu projeto deve ter três branches principais: *develop*, *main* e *production*. O primeiro passo a ser feito no começo de cada *sprint*, é atualizar a branch *develop* com as mudanças contidas na branch *main*. - -Depois disso, para **cada feature** a ser desenvolvida, os seguintes passos devem ser executados: - - 1. O desenvolvedor deve abrir uma nova branch **a partir da develop**, com o padrão de nomenclatura `numero_da_issue_nome_da_feature` \(Exemplo: 001_tela_de_login\). - - 2. O desenvolvedor deve, nessa nova branch, desenvolver sua feature, **com testes e comentários.** Após o desenvolvimento acabar, o desenvolvedor deve abrir uma *merge request* da sua branch para a branch *develop*, **resolvendo qualquer conflito** com a branch *develop* após abir a *merge request*. - - 3. Um outro desenvolvedor \(que não seja o que desenvolveu a feature\) deve **revisar** essa *merge request*, rodando e inspecionando os testes e verificando quais foram as mudanças realizadas, se há bugs no código, se a feature está de acordo com as regras de negócio do projeto e se mais testes precisam ser redigidos. Esse passo é **extremamente importante** para garantir que o código do projeto possua alta qualidade e seja livre de bugs. - - 4. Caso a revisão seja bem sucedida, o revisor resolve qualquer conflito que tenha surgido entre a submissão da *merge request* e a sua revisão, e aceita a *merge request*. Do contrário, o revisor notifica nos comentários da *merge request* as mudanças que o desenvolvedor deve adotar para que a *merge request* seja aceita e voltamos ao passo 2. - -Esses passos devem ser executados até que a *sprint* chegue ao fim, o que ocorre quando todas as features da *sprint* foram desenvolvidas **ou** quando a data de finalização da *sprint* é atingida. - -### Finalização - -Quando uma *sprint* chega ao fim, os seguintes passos devem ser executados para preparar o projeto para a próxima *sprint*: - - 1. Todas as *merge requests* que estejam abertas devem ser resolvidas. Isso pode ser feito com a *merge request* sendo aceita ou com a *merge request* sendo recusada e a feature relativa a ela sendo postergada para a próxima *sprint*. - - 2. Uma *merge request* deve ser aberta da branch *develop* para a branch *main*. Essa *merge request* deve ser revisada por **todos** os membros da equipe de desenvolvimento. Essa revisão é mais sucinta que as outras revisões e tem o intuito de verificar se as features desenvolvidas foram integradas de maneira correta, se os testes estão sendo bem sucedidos ao serem executados e se a aplicação roda adequadamente \(isso deve ser testado por um breve uso da aplicação em si\). - - 3. Caso todos os membros aprovem a *merge request* da *sprint*, as features desenvolvidas na *sprint* são colocadas na branch *main*. Do contrário, as mudanças necessárias ou são implementadas rapidamente ou, caso se tratem de mudanças mais sérias, são colocadas em novas *issues* que serão resolvidas na próxima *sprint*. Nesse caso, a *merge request* poderá ser aceita para a *main* caso os bugs e problemas sejam poucos \(mesmo com alguns problemas, é bom que o código seja colocado na main para evitar que features prontas se acumulem na develop\). As correções de bugs e reparo de features incompletas **devem ter prioridade** na próxima *sprint*. - - 4. Caso a *merge request* tenha sido aprovada sem bugs ou ressalvas, as mudanças colocadas na *main* devem ser colocadas também na branch *production*, para que o cliente tenha acesso as mudanças. Caso a branch *production* esteja muito atrasada em relação a branch *main*, considere realizar uma *sprint* apenas com o intuito de corrigir bugs. - -Como gerente de projetos, você também deverá fazer,ao final da seamana, um breve resumo da *sprint*, relatando o resultado final da *sprint*, as features desenvolvidas por cada membro da equipe e as dificuldades encontradas, além de falar sobre a situação geral do projeto. Esse resumo deverá ser mandado no canal de **gerência de projetos no *slack*** direcionado ao diretor de projetos, para que ele possa estar inteirado da situação do projeto. - -## Reuniões gerais - -O gerente de um projeto também tem o trabalho de participar das reuniões gerais \(RGs\) da Struct para informar aos outros membros da empresa júnior \(em especial, o presidente e o diretor de projetos\) o estado de desenvolvimento do projeto. Nessa hora, o gerente deve destacar de forma bem resumida se o desenvolvimento do projeto está progredindo de maneira adequada, se há necessidade de mais pessoas serem alocadas no projeto e se algo de anormal está acontecendo com o projeto. - -Caso o gerente de um projeto não possa comparecer à reunião geral, ele deve instruir algum outro membro do projeto que estará na RG à realizar essa função. Caso isso não seja possível, o gerente deve enviar uma mensagem breve no *Slack* que detalhe esses fatores. - -## Ambiente de produção - -O ambiente de produção é o espaço onde colocaremos o código da branch *production* para que ele possa ser acessado diretamente pelo cliente ou pelos usuários. Como gerente de projeto, é seu trabalho colocar o código da branch *production* no ambiente de produção ao final do projeto \(ou antes da data de entrega do projeto\). Para esse fim utilizaremos o [Docker](./docker/README.md), uma ferramenta que fornece um ambiente de virtualização para colocarmos o projeto. - -Devido à complexidade do processo de colocar o código em produção, achamos melhor detalhar isso em uma [página específica](./docker/colocando-um-projeto-em-producao.md) do Gitbook. Lembre-se, também, que qualquer mudança requisitada pelo cliente só será perceptível **se o código estiver em produção**, de forma que **você deverá realizar esse processo regularmente** \(se esquecer de colocar novas features ou correções de features existentes em produção é uma excelente forma de **irritar** o cliente\). diff --git a/execucao/projetos/gerencia/README.md b/execucao/projetos/gerencia/README.md new file mode 100644 index 0000000..438024c --- /dev/null +++ b/execucao/projetos/gerencia/README.md @@ -0,0 +1,19 @@ +# Gerência de projetos + +O gerente de um projeto possui funções essenciais à qualidade de vida de um projeto, como a criação do projeto em si, a gerência das *sprints* do projeto e a comunicação dos detalhes do projeto para os outros membros de equipe nas reuniões gerais. Em suma, o gerente do projeto conduz a execução do projeto, tomando as medidas necessárias para que seu desenvolvimento seja bem sucedido. + +Está pagina contém as informações necessárias para se gerenciar um projeto de maneira adequada. + +## Reuniões gerais + +O gerente de um projeto também tem o trabalho de participar das reuniões gerais \(RGs\) da Struct para informar aos outros membros da empresa júnior \(em especial, o presidente e o diretor de projetos\) o estado de desenvolvimento do projeto. Nessa hora, o gerente deve destacar de forma bem resumida se o desenvolvimento do projeto está progredindo de maneira adequada, se há necessidade de mais pessoas serem alocadas no projeto e se algo de anormal está acontecendo com o projeto. + +Caso o gerente de um projeto não possa comparecer à reunião geral, ele deve instruir algum outro membro do projeto que estará na RG à realizar essa função. Caso isso não seja possível, o gerente deve enviar uma mensagem breve no *Slack* que detalhe esses fatores. + +## Ambiente de produção e staging + +O ambiente de produção é o espaço onde colocaremos o código da branch *production* para que ele possa ser acessado diretamente pelo cliente ou pelos usuários. Como gerente de projeto, é seu trabalho colocar o código da branch *production* no ambiente de produção ao final do projeto \(ou antes da data de entrega do projeto\). Para esse fim utilizaremos o [Docker](./docker/README.md), uma ferramenta que fornece um ambiente de virtualização para colocarmos o projeto. + +Devido à complexidade do processo de colocar o código em produção, achamos melhor detalhar isso em uma [página específica](./docker/colocando-um-projeto-em-producao.md) do Gitbook. Lembre-se, também, que qualquer mudança requisitada pelo cliente só será perceptível **se o código estiver em produção**, de forma que **você deverá realizar esse processo regularmente** \(se esquecer de colocar novas features ou correções de features existentes em produção é uma excelente forma de **irritar** o cliente\). + +O ambiente de staging é similar ao ambiente de produção, no entanto ele é considerado um *deploy temporário*, uma vez que não é uma versão apropriada para o uso dos usuários, é utilizado mais para testes e para mostrar as mudanças feitas para o cliente. Por ser uma versão mais para testes e não precisar de tantos recursos, geralmente não fazemos o deploy em staging em nosso servidor, mas sim utilizamos alguma ferramenta externa para tal finalidade, como o [heroku](../heroku.md) ou o [railway](https://railway.app/) para fazer o deploy da api do rails e o [netlify](https://www.netlify.com/) para fazer o deploy do front em react. diff --git a/execucao/projetos/gerencia/inicio-de-projeto.md b/execucao/projetos/gerencia/inicio-de-projeto.md new file mode 100644 index 0000000..86d5012 --- /dev/null +++ b/execucao/projetos/gerencia/inicio-de-projeto.md @@ -0,0 +1,100 @@ +# Início do projeto + +O primeiro passo em um projeto de software consiste na criação e configuração do repositório em que o projeto será desenvolvido. Isso inclui criar o repositório do projeto, as estruturas de pastas para abrigar o código fonte e outros recursos e configurar adequadamente os arquivos de controle de versão, os arquivos de documentação do projeto, a utilização de bibliotecas externas \(gems\), e o banco de dados do projeto. + +Embora isso pareça ser muita coisa, iremos detalhar melhor cada passo nas seções abaixo. + +## Projeto Rails + +O primeiro passo na inicialização do projeto é a criação do projeto na framework [*Ruby on Rails*](../ruby-on-rails/README.md). Para isso, vamos escolher, utilizando o RVM ou Rbenv, uma versão de Ruby esteja um *minor release* atrás da última versão \(ou seja, se a última versão de Ruby é a 2.x, utilize a versão 2.x-1 para o projeto\). Na dúvida, utilize a versão de Ruby utilizada no último projeto. Isso evita que tenhamos que lidar com os bugs inerentes à versão mais nova do Ruby e das gems que o acompanham. Caso algum dos membros do projeto não tenha um gerenciador de versão de Ruby instalado \(como RVM e Rbenv\), ajude ele com a instalação \(esse tipo de software é praticamente essecial para se trabalhar em vários projetos, cada um com uma versão diferente de Ruby\). + +Após escolher a versão de Ruby desejada, será necessário realizar a instalação de algumas gems \(que são as bibliotecas do Ruby\). Para instalar uma gem, basta adicionar a gem ao arquivo Gemfile e rodar o comando `bundle install`. Com isso em mente, instale o *rails* \(framework de desenvolvimento de websites e serviços web\) e para o banco de dados, escolha entre o *mysql2* e o *pg*, gems para o MySQL e PostgreSQL respectivamente, os sistemas gerenciadores de bancos de dados, ou SGBDs, mais utilizados\). Além dessas gems, será necessário instalar os SGBDs MySQL e PostgreSQL em seu sistema operacional \(assunto que **não** será coberto nessa página\). + +Após instalar as gems e os SGBDs em seu computador, caberá a você, como gerente de projetos, decidir qual SGBD utilizar no projeto. O MySQL é mais amigável para iniciantes e possui uma melhor forma de visualizar e manipular diretamente o banco de dados, mas requer mais tempo para ser configurado e a utilização de uma gem \([*figaro*](../../ruby-on-rails/gems.md#figaro)\) para gerenciar os credenciais de login. Já o PostgreSQL pode ser utilizado com quase nenhuma configuração adicional, mas possui uma instalação mais complicada e não permite que o usuário manipule o banco de dados com facilidade. Caso seja sua primeira vez gerenciando um projeto, fale com o diretor de projetos ou com um membro mais experiente para pedir uma recomendação \(eu, pessoalmente, recomendaria o PostgreSQL\). + +{% hint style="info"%} +Para aprender um pouco mais sobre instalação do ruby e do rails e como configurar o banco de dados, confira os nossos [guias](../../../gestao/processo-seletivo/guias-utilizados.md). +{% endhint %} + +Após escolher o SGBD a ser utilizado, chegou a hora de criar o projeto. Para isso, rode o comando `rails new nome_do_projeto_em_snake_case -d {mysql | postgresql} --api`, escolhendo o penúltimo argumento para coincidir com o SGBD desejado. O framework cuidará da criação de pastas e arquivos para o desenvolvimento do projeto. + +Caso você tenha escolhido o MySQL, configure a gem [*figaro*](../../ruby-on-rails/gems.md#figaro) para gerenciar credenciais e, caso você **não** queira utilizar o usuário *root* no projeto, crie as tabelas de desenvolvimento e testes e um usuário com permissão de acesso a elas. Caso você tenha escolhido o PostgreSQL, preencha o arquivo `config/database.yml` com os credenciais do SGBD \(em sistemas operacionais com o *kernel* Linux, você pode preencher apenas o seu nome de usuário e apagar o campo de senha caso você tenha optado, na instalação do PostgreSQL, por utilizar sua conta de usuário do sistema operacional como um usuário do SGBD\). + +Por fim, tente executar o projeto utilizando o comando `rails s`. Caso a execução do projeto não gere alguma mensagem de erro, adicione todos os arquivos gerados ao controle de versão sob o nome 'Commit inicial'. Caso contrário, corrija os erros do projeto **antes** de realizar o commit inicial. + + +## Segundo commit + +Após a criação do repositório, você deverá fazer um segundo commit na branch main que faça todas as configurações necessárias no projeto. Esse segundo commit deve colocar no README informações básicas do projeto, colocar no projeto arquivos padrão que devem estar presentes em qualquer projeto da Struct e configurar as gems que o projeto irá utilizar (confira as [gem essenciais](../../ruby-on-rails/gems.md#gems-essenciais) para um projeto). Mais detalhes são fornecidos nas subseções abaixo. Após executar **todos** esses passos, realize um commit com o nome "Estrutura inicial" adicionando as mudanças que você fez. + +### README + +O README deve conter as dependências do projeto \(versão do Ruby, banco de dados, etc...\) e os dados do projeto em si, os quais incluem a especificação do projeto \(bem resumida, em uma única frase\), o codinome do projeto \(se existir\), a data de assinatura do contrato, o prazo de desenvolvimento do projeto e a data prevista de entrega do projeto. + +Não utilize o README como uma lista de tarefas, pois isso pode ser feito por meio de *issues*. + +### Arquivos padrão + +No momento, existem 2 arquivos padrão que devem estar presentes no projeto: o `.gitignore` e o script `start.sh`. + +#### Arquivo .gitignore + +Como sabemos, o `.gitignore` é utilizado para impedir que arquivos locais sejam acidentalmente adicionados ao repositório. Arquivos que geralmente devem ser ignorados incluem arquivos de configurações pessoais \(`.bundle`, `.vscode`, `.idea`, `.DS_store`\), pastas de caches \(`tmp` e `storage`\), logs \(arquivos `*.log` e pasta `log`\) e módulos do *node js* \(a pasta `node_modules`\). + +**Nunca** coloque arquivos que afetam o desenvolvimento do projeto \(como `Gemfile`\) no `.gitignore`. + +É **essencial** que a configuração do `.gitignore` seja feita no **início** do projeto, pois problemas relacionados a esse arquivo são um pesadelo para serem resolvidos. + +Segue, abaixo, um template de arquivo `.gitignore`: + +``` +# Environment normalization: +*.DS_Store +.idea +.rvmrc +.vscode +/.bundle +/vendor/bundle + +# Ignore all logfiles and tempfiles: +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore uploaded files in development: +/storage/* +!/storage/.keep + +# Ignore node_modules: +node_modules/ + +# Ignore yarn files: +/yarn-error.log +yarn-debug.log* +.yarn-integrity + +# Ignore precompiled javascript packs: +/public/packs +/public/packs-test +/public/assets + +# Ignore Byebug command history file: +.byebug_history + +# Ignore project secrets: +/config/initializers/secret_token.rb +/config/master.key +``` + +#### Arquivo start.sh + +O arquivo `start.sh` é um script de execução utilizado para o *Docker*, servindo principalmente para que possamos fazer o deploy de nossa aplicação, mas que também pode ser utilizado para que novos integrantes do projeto inicializem o projeto sem maiores dificuldades. + +Segue, abaixo, um template de arquivo `start.sh`: + +``` +#!/bin/sh +rails db:migrate +bundle exec puma -C config/puma.rb +``` \ No newline at end of file diff --git a/execucao/projetos/gerencia/repositorio.md b/execucao/projetos/gerencia/repositorio.md new file mode 100644 index 0000000..7c8c255 --- /dev/null +++ b/execucao/projetos/gerencia/repositorio.md @@ -0,0 +1,21 @@ +# Repositório + +A criação do repositório para o projeto deve ser feita no grupo da Struct do GitLab. O nome do repositório geralmente segue o padrão `projeto_nome` ou `projeto_codinome` \(como gerente de projeto, sinta-se livre para criar um codinome épico para o projeto :grinning:\). Configure o repositório como privado e **não** inicialize ele com um README, pois o Rails já gera esse arquivo para você. Após criar o repositório, faça o upload dos arquivos gerados pelo projeto de Rails para o repositório. + +## Criação de branches padrão + +Um projeto bem estruturado contém um conjunto de branches padrões e um fluxo bem definido que novas features ou mudanças em features existentes devem percorrer para serem aprovadas definitivamente e chegarem aos clientes da aplicação. O gerente de projetos é o responsável por criar e configurar as branches padrões no repositório do projeto no GitHub. Também cabe ao gerente de projetos garantir que o desenvolvimento siga o fluxo definido pelas branches padrões, evitando a utilização de atalhos e os problemas gerados pela quebra do fluxo de desenvolvimento. + +Esta seção detalha as branches padrão que todo projeto da Struct deve ter, com uma breve descrição do seu propósito geral e fluxo de desenvolvimento. + +#### Develop + +A **develop** é a branch de desenvolvimento padrão. Ela deve ser marcada como *default* e *protected* nas configurações de branch do projeto. Todas as features desenvolvidas devem ser adicionadas primeiramente à *develop* por meio de um *Pull request*. + +#### Main + +A **main** é a branch da versão oficial do software para os desenvolvedores. Se possível, ela deve ser marcada como *protected* nas configurações de branch do projeto. De tempos em tempos, as features desenvolvidas na branch *develop* são levadas a branch *main* por meio de um *Pull request*, permitindo ao time de desenvolvedores revisar com mais afinco e atenção as features desenvolvidas \(tipicamente, no final da *sprint*\). Além disso, a *main* pode ser utilizada para receber diretamente correções de *bugs* ou problemas críticos, sem a necessidade dessas correções urgentes se misturarem com as features novas da *Develop*, encurtando o caminho de desenvolvimento até o cliente. + +#### Production + +A **production** é a branch da versão oficial do software para os clientes. Se possível, também deve ser marcada como *protected* nas configurações de branch do projeto. De tempos em tempos, as features testadas e revisadas presentes na branch *main* são levadas à branch *production* por meio de um *Pull request* para que essas mudanças cheguem efetivamente aos clientes do projeto e possam ser utilizadas ou visualizadas pelos usuários do produto. Tenha muito cuidado ao colocar mudanças nessa branch, pois qualquer falha ou erro terá **consequências reais** para o cliente e para os usuários da aplicação. diff --git a/execucao/projetos/gerencia/sprints.md b/execucao/projetos/gerencia/sprints.md new file mode 100644 index 0000000..78b4451 --- /dev/null +++ b/execucao/projetos/gerencia/sprints.md @@ -0,0 +1,57 @@ +# Sprints + +Uma *sprint* consiste em um conjunto de features do projeto que deve ser desenvolvido em um conjunto específico de tempo. Cada feature possui atrelada a ela uma pontuação e um membro responsável pelo seu desenvolvimento. A utilização de *sprints* é uma característica do desenvolvimento ágil e possui o intuito de validar constantemente o projeto junto ao cliente por meio de entregas constantes de features \(as *sprints* também são úteis para impedir que os desenvolvedores percam os prazos do projeto\). + +Como gerente de projetos, é seu trabalho definir todas as características de uma *sprint*, incluindo a sua duração, as features que serão desenvolvidas, a pontuação de cada feature e quem será responsável por cada feature. Também é seu trabalho gerenciar o andamento da *sprint*, garantindo que os desenvolvedores estejam desenvolvendo as features de forma adequada \(com a utilização correta de branches e com a implementação de testes e utilização de comentários\), esclarecendo quaisquer dúvidas relacionadas às regras de negócio do projeto, documentando o desenrolar da *sprint* quando ela acabar e redistribuindo features para outras *sprints* caso isso seja necessário. + +No início de uma *sprint*, é altamente recomendado que, independente das circunstâncias, seja realizada **uma reunião com a equipe de desenvolvimento.** Isso permite ao gerente de projetos tratar todas as pendências necessárias relativas à *sprint* passada e ao começo de uma nova *sprint*. + +Você também deverá criar um cartão no *Trello* (ou no kanbam no próprio github) no início da *sprint* para abrigar os detalhes da *sprint*. Esse cartão deverá ser atualizado no final da *sprint* ou caso alguma mudança ocorra nos detalhes da *sprint*. + +Esta seção aborda como features devem ser elaboradas e integradas ao projeto, como o desenvolvimento de uma feature deve ser feito por um membro da equipe e quais as práticas que devem ser adotadas ao final de uma *sprint*. + +## Features + +Como dissemos, uma feature é uma parte do projeto que fornece alguma funcionalidade, e o desenvolvimento do projeto será divido no desenvolvimento de várias features. As features a serem desenvolvidas devem ser derivadas dos requisitos do projeto, colhidos em reuniões com o cliente. Por fim, as features devem ser divididas de forma que possam ser desenvolvidas por um único membro do projeto \(ou seja, sem dependências *excessivas* com o código de outras features\). + +Com o intuito de deixar o desenvolvimento do projeto organizado, cada feature deve ser registrada no repositório do projeto por meio de uma *issue*, onde todos os detalhes necessários para o desenvolvimento da feature devem estar presentes. Dessa forma, qualquer desenvolvedor que **não** conheça os requisitos do projeto consegue desenvolver a feature apenas lendo a descrição da *issue*. Aliás, qualquer membro do projeto deve abrir uma issue quando perceber que uma nova feature precisará ser desenvolvida ou quando notar algum bug no projeto que não possa ser resolvido trivialmente. + +No início de uma *sprint*, o gerente de projeto escolhe quais features serão desenvolvidas nessa *sprint* a partir das *issues* contidas no repositório, priorizando as correções de bugs e as features demandadas pelo cliente do projeto. Caso não haja *issues* no repositório, o gerente deve se reunir com a equipe de desenvolvimento para registrar as features pendentes como *issues* ou notificar o diretor de projetos que é necessário colher mais requisitos com o cliente. + + + +Após as features serem colhidas, elas devem ser discutidas com os membros da equipe de desenvolvimento e, em seguida, distribuídas entre eles. É importante garantir que a distribuição seja bem feita e leve em consideração a grade horária semanal dos desenvolvedores para evitar membros ociosos ou membros atarefados excessivamente. Também é ideal que desenvolvedores novatos sejam auxiliados no desenvolvimento de features mais complexas, por meio de recursos como *pair programming* ou maior atenção do gerente de projetos. Por fim, lembre-se de atribuir as *issues* no repositório aos seus respectivos responsáveis para que qualquer um que visite o repositório do projeto possa saber quem está trabalhando no que. + +Assim que as features forem escolhidas, discutidas e distribuídas, é hora de começar o desenvolvimento. + +## Desenvolvimento + +O desenvolvimento de uma feature em uma *sprint* deve seguir uma lógica bem definida para garantir que as diversas features desenvolvidas venham a ser adequadamente revisadas e integradas no projeto. Como gerente de projetos, você deverá entender plenamente essa lógica e explicá-la aos demais desenvolvedores. + +Como dito anteriormente, seu projeto deve ter três branches principais: *develop*, *main* e *production*. O primeiro passo a ser feito no começo de cada *sprint*, é atualizar a branch *develop* com as mudanças contidas na branch *main*. + +Depois disso, para **cada feature** a ser desenvolvida, os seguintes passos devem ser executados: + + 1. O desenvolvedor deve abrir uma nova branch **a partir da develop**, com o padrão de nomenclatura `numero_da_issue_nome_da_feature` \(Exemplo: 001_tela_de_login\). + + 2. O desenvolvedor deve, nessa nova branch, desenvolver sua feature, **com testes e comentários.** Após o desenvolvimento acabar, o desenvolvedor deve abrir uma *pull request* da sua branch para a branch *develop*, **resolvendo qualquer conflito** com a branch *develop* após abir a *pull request*. + + 3. Um outro desenvolvedor \(que não seja o que desenvolveu a feature\) deve **revisar** essa *pull request*, rodando e inspecionando os testes e verificando quais foram as mudanças realizadas, se há bugs no código, se a feature está de acordo com as regras de negócio do projeto e se mais testes precisam ser redigidos. Esse passo é **extremamente importante** para garantir que o código do projeto possua alta qualidade e seja livre de bugs. + + 4. Caso a revisão seja bem sucedida, o revisor resolve qualquer conflito que tenha surgido entre a submissão da *pull request* e a sua revisão, e aceita a *pull request*. Do contrário, o revisor notifica nos comentários da *pull request* as mudanças que o desenvolvedor deve adotar para que a *pull request* seja aceita e voltamos ao passo 2. + +Esses passos devem ser executados até que a *sprint* chegue ao fim, o que ocorre quando todas as features da *sprint* foram desenvolvidas **ou** quando a data de finalização da *sprint* é atingida. + +## Finalização + +Quando uma *sprint* chega ao fim, os seguintes passos devem ser executados para preparar o projeto para a próxima *sprint*: + + 1. Todas as *pull requests* que estejam abertas devem ser resolvidas. Isso pode ser feito com a *pull request* sendo aceita ou com a *pull request* sendo recusada e a feature relativa a ela sendo postergada para a próxima *sprint*. + + 2. Uma *pull request* deve ser aberta da branch *develop* para a branch *main*. Essa *merge request* deve ser revisada por **todos** os membros da equipe de desenvolvimento. Essa revisão é mais sucinta que as outras revisões e tem o intuito de verificar se as features desenvolvidas foram integradas de maneira correta, se os testes estão sendo bem sucedidos ao serem executados e se a aplicação roda adequadamente \(isso deve ser testado por um breve uso da aplicação em si\). + + 3. Caso todos os membros aprovem a *pull request* da *sprint*, as features desenvolvidas na *sprint* são colocadas na branch *main*. Do contrário, as mudanças necessárias ou são implementadas rapidamente ou, caso se tratem de mudanças mais sérias, são colocadas em novas *issues* que serão resolvidas na próxima *sprint*. Nesse caso, a *pull request* poderá ser aceita para a *main* caso os bugs e problemas sejam poucos \(mesmo com alguns problemas, é bom que o código seja colocado na main para evitar que features prontas se acumulem na develop\). As correções de bugs e reparo de features incompletas **devem ter prioridade** na próxima *sprint*. + + 4. Caso a *pull request* tenha sido aprovada sem bugs ou ressalvas, as mudanças colocadas na *main* devem ser colocadas também na branch *production*, para que o cliente tenha acesso as mudanças. Caso a branch *production* esteja muito atrasada em relação a branch *main*, considere realizar uma *sprint* apenas com o intuito de corrigir bugs. + +Como gerente de projetos, você também deverá fazer,ao final da seamana, um breve resumo da *sprint*, relatando o resultado final da *sprint*, as features desenvolvidas por cada membro da equipe e as dificuldades encontradas, além de falar sobre a situação geral do projeto. Esse resumo deverá ser mandado no canal de **gerência de projetos no *slack*** direcionado ao diretor de projetos, para que ele possa estar inteirado da situação do projeto. diff --git a/execucao/ruby-on-rails/gems.md b/execucao/ruby-on-rails/gems.md new file mode 100644 index 0000000..68fdd8f --- /dev/null +++ b/execucao/ruby-on-rails/gems.md @@ -0,0 +1,153 @@ +# Gems + +As *gems* do *Ruby on Rails* são bibliotecas externas que fornecem funcionalidades ao seu projeto. Dependendo do projeto em questão, os desenvolvedores precisarão utilizar gems que não vem configuradas por padrão em um projeto de *Ruby on Rails*, de forma que cabe ao gerente de projetos o trabalho de analisar quais gems serão necessárias no decorrer do projeto, colocar essas gems no `Gemfile` e realizar qualquer outra configuração para que essas gems funcionem adequadamente \(a maioria das gems requer alguma configuração adicional para funcionar\). + +Além de configurar as novas gems, o gerente de projeto também deve verificar qualquer aviso de *deprecated gem* gerado pelo Rails após a execução dos comandos `bundle install` e `rails s` e corrigir esses avisos para evitar problemas com o projeto. + +Idealmente, esse processo de configuração deve ser feito no *começo* do projeto para que todos os desenvolvedores tenham acesso às funcionalidades adequadas para o desenvolvimento do projeto \(e para que eles não tenham que ficar rodando `bundle install` toda hora\). Lembre-se de, após configurar ou substituir uma gem, sempre tentar executar o projeto com o comando `rails s` para garantir que o funcionamento do projeto não foi afetado por alguma gem mal configurada. + +Para facilitar o uso dessa seção pelo gerente de projeto \(ou qualquer outro membro da Struct\), vamos dividir as gems que devem ser configuradas manualmente em duas seções: *gems essenciais* e *gems úteis*. Também vamos listar alguns casos comuns de *deprecated gems* que devem ser substituídas. + +## Gems essenciais + +As *gems essenciais* são gems que devem ser configuradas em **todos** os projetos da Struct de *Ruby on Rails*, por proporcionarem funcionalidades que são necessárias ou para o desenvolvimento do projeto ou para a garantia básica da qualidade do projeto. Como gerente de projeto, é sua responsabilidade incluir e configurar **todas** as gems dessa seção no projeto. + + +### RSpec + +O RSpec é uma framework de testes utilizado para redigir testes de unidade e testes de módulo. + +Para instalar e configurar o RSpec e o factory bot, siga os passos descritos nas páginas de [instalação do Rspec](../rspec-e-factory-bot/instalacao-rspec.md) e [de instalação do factory bot](../rspec-e-factory-bot/instalacao-factory-bot.md) + +### Devise and Simple Token Authentication + +Duas gems que trabalham juntas para a criação de um usuário com login funcional (com proteção de sua senha em um token). + +Com a gem Devise é possível criar uma model especial (device) com todas as características para permitir a criação de um usuário, como e-mail e senha. + +Já a gem Simple Token Authentication garante que o acesso a rota da controller seja permitido apenas por quem esteja com o token certo, ou seja, logado corretamente. + +1. Adicione o trecho de código abaixo ao arquivo `Gemfile`: + +``` +gem 'devise' +gem 'simple_token_authentication' +``` + +2. Execute o comando `bundle install` para instalar as gem adicionadas ao `Gemfile`. + +3. Execute o comando `rails g devise:install` para instalar todos os arquivos do devise. + + +### Rubocop + +Rubocop é um analisador e formatador de código que serve para padronizar e +melhorar seu código como um todo. + +Essa gem é bem flexível (podendo ser configurada e estilizada) e garante um código +mais limpo e organizado, já que os projetos são feitos em grupos e, assim, cada um +tem sua forma de organizar distinta. + +1. Adicione o trecho de código abaixo ao arquivo `Gemfile`: + +``` +# Rubocop +gem 'rubocop', '~> 1.24.1', require: false +gem 'rubocop-rails', require: false +gem 'rubocop-rspec', require: false +``` + +2. Execute o comando `bundle install` para instalar as gem adicionadas ao `Gemfile`. + +3. Rode o comando `robocop -A` no seu terminal do projeto para +executar a verificação de seu código. + + Note que, provavelmente, serão identificadas ofensas que podem não ser corrigidas automaticamente, sendo necessário concertá-las manualmente antes de ser feito o + commit. + +#### Regras do rubocop + +Ao utilizar o rubocop, muito provavelmente você não vai querer utilizar todas as suas regras como padrão, já que em alguns casos vezes elas não fazem muito sentido ou são bem chatinhas e acabam atrapalhando mais do que ajudando. Nesses casos, você pode configurar desabilitar algumas regras por padrão e, para isso, basta criar na raíz do projeto o arquivo *.rubocop.yml*. Neste arquivo, primeiro adicione as seguintes linhas + +``` +require: + - rubocop-rails + - rubocop-rspec + + +AllCops: + TargetRubyVersion: 2.7.2 # Mude para a sua versão do ruby + NewCops: enable + Exclude: + - db/schema.rb + - bin/bundle + +Style/Documentation: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false +``` + +Nesse trecho de código, estão sendo adicionadas as extensões *rubocop-rails* e *rubocop-rspec* (especificadas na gemfile), está sendo especificada a versão do ruby e habilitando os novos cops, o que será necessário para adicionar o rubocop ao CI no github, além disso, estão sendo excluídos alguns arquivos padrões do rails que podem dar problemas. caso queira ignorar um diretório inteiro, basta adicionar nessa lista ` - dir/**`. + +As últimas duas configurações estão desabilitando o cop de documentação com comentário e desabilitando o cop de Strings literais imutáveis, que introduzem uma feature que não é utuilizada com frequência em Rails e que possui pouca aplicação no momento. + +Além disso, algumas configurações do rubocop são em relação ao tamanho das coisas (métodos, classes, testes), nesse caso, é possível apenas alterar o limite máximo ao invés de desabilitar o cop, já que esses muita vezes são importantes. Por exemplo, para alterar o tamanho máximo dos métodos para 16 linhas, basta colocar +``` +Metrics/MethodLength: + Max: 16 +``` + +## Gems úteis + +As *gems úteis* são gems que proporcionam funcionalidades desejáveis ou essenciais apenas em alguns casos, mas que não são necessárias em todos os projetos da Struct de *Ruby on Rails*. Como gerente de projeto, é sua responsabilidade ler a descrição das gems nessa seção e incluir e configurar apenas as gems que se apliquem ao projeto em questão. + +### Figaro + +O Figaro é uma gem utilizada para gerenciar credenciais de bancos de dados em um arquivo de configuração local. Recomenda-se sua utilização em projetos que possuam *um banco de dados com o SGBD \(Sistema gerenciador de banco de dados\) MySQL* ou qualquer outro SGBD que não possua um bom sistema de armazenamento de credenciais de login \(o PostgreSQL é um caso raro de SGBD que **não** precisa da gem Figaro\). + +Para configurar o Figaro, siga os passos abaixo: + +1. Adicione o trecho de código abaixo ao arquivo `Gemfile`: + + ``` + # Database credentials: + gem 'figaro' + ``` + +2. Execute o comando `bundle install` para instalar as gem adicionadas ao `Gemfile`. + +3. Execute o comando `bundle exec figaro install` para configurar a gem. + +4. Modifique o arquivo `config/database.yml` nas linhas demonstradas abaixo: + + ``` + default: &default + # Modifique apenas as linhas abaixo e não apague ou modifique as outras linhas! + username: <%= ENV['db_user'] %> + password: <%= ENV['db_password'] %> + ``` + +5. Adicione o trecho de código abaixo ao arquivo `config/application.yml`: + + ``` + # Database credentials: + db_user: "seu_usuario_do_DB_entre_parentesis" + db_password: "sua_senha_do_DB_entre_parentesis" + ``` + +Após configurar a gem Figaro em seu ambiente, **avise os outros membros do projeto** que eles deveram executar os passos 2, 3 e 5 para configurar a gem em seus ambientes! + + +## *Deprecated* gems + +As *deprecated gems* são gems que não recebem mais manutenção e suporte por parte de um time de desenvolvedores, devendo ser retiradas dos projetos da Struct de *Ruby on Rails*. Como gerente de projeto, é sua responsabilidade substituir todas as gems dessa seção que estão presentes no seu projeto. + +### Chrome driver helper + +A gem `chromedriver-helper` parou de receber suporte em Março de 2019. Substitua ela no Gemfile pela gem `webdrivers`. + +### SASS Rails + +A gem `sass-rails` parou de receber suporte em Março de 2019. Substitua ela no Gemfile pela gem `sassc-rails`. \ No newline at end of file From 8483d8b14e61280fb3f5b6a4c4399f98090d169a Mon Sep 17 00:00:00 2001 From: artistrea Date: Sat, 5 Nov 2022 17:50:43 -0300 Subject: [PATCH 02/30] started docs --- .../next-js/conceitos-relevantes/README.md | 44 ++++ execucao/next-js/v<=12/api.md | 0 execucao/next-js/v<=12/inicio-rapido.md | 29 +++ execucao/next-js/v<=12/paginas-e-padroes.md | 216 ++++++++++++++++++ execucao/next-js/v<=12/sobre.md | 19 ++ execucao/next-js/v>=13/README.md | 1 + 6 files changed, 309 insertions(+) create mode 100644 execucao/next-js/conceitos-relevantes/README.md create mode 100644 execucao/next-js/v<=12/api.md create mode 100644 execucao/next-js/v<=12/inicio-rapido.md create mode 100644 execucao/next-js/v<=12/paginas-e-padroes.md create mode 100644 execucao/next-js/v<=12/sobre.md create mode 100644 execucao/next-js/v>=13/README.md diff --git a/execucao/next-js/conceitos-relevantes/README.md b/execucao/next-js/conceitos-relevantes/README.md new file mode 100644 index 0000000..87d1a20 --- /dev/null +++ b/execucao/next-js/conceitos-relevantes/README.md @@ -0,0 +1,44 @@ +## Separação cliente/servidor + +Quando um usuário acessa uma _url_, uma request é feita para essa _url_, e então uma resposta é retornada. + +Quando a _request_ chega no servidor, ela possui informações como headers, método http, rota, etc. O servidor pode então processar essa request. A gente escreve código para isso, e esse código será acessado somente no servidor. Isso significa que, nesse código, não existe a variável `window`, o console.log não vai aparecer no console do navegador, e sim no terminal que está rodando o servidor, etc. + +Após receber a request, o servidor envia uma resposta, que pode incluir HTML, CSS, JS, imagens, pdfs, json, etc. +O JS enviado muitas vezes também é código escrito por nós, e nesse caso esse código será rodado no cliente apenas. + +Isso significa que vão existir outras limitações, como impossibilidade de se acessar variáveis de ambiente, algumas api's do node não são padrões dos navegadores, etc. + +## _assets_ estáticos + +Um _asset_ estático nada mais é do que um objeto que é enviado para o cliente sem necessidade de alterar o objeto. + +Ou seja, o objeto já existe no servidor, e então é apenas enviado como resposta. Pode ser, por exemplo: imagem, index.html, robots.txt, pdf, etc; desde que não seja alterado pelo servidor. + +## Renderização + +Começando com um exemplo: + +Usando react, escrevemos uma função que retorna JSX. Ao rodar essa função, HTML é `renderizado`. + +Em suma, renderização é gerar algo que pode ser mostrado na tela do navegador, como um HTML. Isso exige um processamento, ou seja, um custo de tempo, espaço e energia. + +## SSR + +É quando o servidor recebe uma request, renderiza um conteúdo, e envia para o cliente algo já renderizado. + +## SG + +É quando o servidor, no processo de build/compilação, renderiza um conteúdo e gera um _asset_ estático. + +Agora, o servidor recebe uma request, e envia para o cliente algo já renderizado. + +### ISR + +É um híbrido entre o SSR e o SG, com alterações para encaixar os dois + +Na primeira request que o servidor recebe, ele renderiza um conteúdo, e envia para o cliente algo já renderizado. Então, guarda o conteúdo renderizado. + +Nas próximas requests, o servidor logo envia para o cliente algo já renderizado. + +Geralmente existe um mecanismo para revalidar o conteúdo e rerenderizá-lo. diff --git a/execucao/next-js/v<=12/api.md b/execucao/next-js/v<=12/api.md new file mode 100644 index 0000000..e69de29 diff --git a/execucao/next-js/v<=12/inicio-rapido.md b/execucao/next-js/v<=12/inicio-rapido.md new file mode 100644 index 0000000..f3bd491 --- /dev/null +++ b/execucao/next-js/v<=12/inicio-rapido.md @@ -0,0 +1,29 @@ +## Setup automático: + +```bash +npx create-next-app@latest +# ou +yarn create next-app +# ou +pnpm create next-app + +``` + +Então responder as perguntas do prompt, e pronto, seu projeto em next está criado. + +## Iniciando o projeto: + +Recomendado criar uma pasta 'src/' colocar a pasta 'pages/' e todas as outras que forem possuir js ou jsx dentro dela + +Para rodar o servidor, rode + +```bash +yarn dev +``` + +Altere os arquivos em 'pages/' para ver as mudanças na tela. + +## Recomendações: + +Adicionar às dependências e configurar ESLint e prettier. +No vscode, adicionar às recomendações do _workspace_ as extensões do ESLint e do Prettier. diff --git a/execucao/next-js/v<=12/paginas-e-padroes.md b/execucao/next-js/v<=12/paginas-e-padroes.md new file mode 100644 index 0000000..879751f --- /dev/null +++ b/execucao/next-js/v<=12/paginas-e-padroes.md @@ -0,0 +1,216 @@ +# SSR, ISR e SG, mas como? + +O next permite fazer páginas com SSR, ISR e SG, mas como que usamos isso? + +## ATENÇÃO: + +No ambiente de `desenvolvimento` local, `NÃO SERÃO PERCEBIDAS DIFERENÇAS` direito entre esses padrões!! Por usar React Fast Refresh, toda hora que uma alteração ocorrer nos arquivos de desenvolvimento, acontecerá o equivalente a tirar o servidor do ar e levantá-lo novamente. Isso dificulta observar o comportamento que ocorrerá na aplicação com o servidor no ar. + +Além disso, esses `padrões` de renderização são `somente` para `páginas`, em 'pages/'. Os `componentes` devem ser desenvolvidos como em React normal, em `outro diretório`. Isso também implica que, para se aproveitar dos padrões do Next, é necessário saber e `requisitar` os `dados` que os `componentes` de uma página necessitam a `nível` de `página`, e não a nível de `componente`. + +## SG + +Por padrão, uma página no Next será compilada para um _asset_ estático no momento de _build_. Caso queira fazer uma chamada à api no momento de build, pode ser utilizada a função `getStaticProps`. + +### Exemplo: + +```jsx +// src/pages/index.js + +import { useState } from "react"; + +// Note que é necessário fazer um export default da página +export default function Home() { + const [todos, setTodos] = useState(null); + + async function getData() { + const response = await fetch( + "https://jsonplaceholder.typicode.com/todos/1" + ); + const todos = await response.json(); + setTodos(todos); + } + + return ( +
+

SG

+ + {todos &&
{JSON.stringify(todos, null, 2)}
} +
+ ); +} +``` + +Essa página seria acessada pela url `http://localhost:3000/`. + +### Exemplo com getStaticProps: + +```jsx +// src/pages/wGetStaticProps.js + +import { useState } from "react"; + +// Note que é necessário fazer um export default da página +export default function wGetStaticProps({ initialTodos }) { + const [todos, setTodos] = useState(initialTodos); + + async function getData() { + const response = await fetch( + "https://jsonplaceholder.typicode.com/todos/1" + ); + const todos = await response.json(); + setTodos(todos); + } + + return ( +
+

SG

+ + {todos &&
{JSON.stringify(todos, null, 2)}
} +
+ ); +} + +// getStaticProps é uma função que é executada no momento de build +// e que retorna um objeto com as props que serão passadas para a página +export async function getStaticProps() { + const response = await fetch("https://jsonplaceholder.typicode.com/todos/1"); + const todos = await response.json(); + + return { + props: { + initialTodos: todos, + }, + }; +} +``` + +Essa página seria acessada pela url `http://localhost:3000/wGetStaticProps`. A diferença entre essa página e a primeira é que essa faz uma chamada à api no momento de build, preenchendo as props da página com a resposta. + +Essas páginas são renderizadas na build do app, e _assets_ estáticos são gerados, incluindo seu HTML e JS. Quando o usuário acessar a página, o HTML e JS serão enviados como estão, e o usuário não precisará esperar o servidor renderizar a página ou fazer qualquer processamento. + +## ISR + +Para transformar uma página de SG para ISR, existem algumas maneiras. Uma delas é usar a função `getStaticProps` e passar um parâmetro `revalidate` para ela. Esse parâmetro é um número que representa o tempo em segundos que o Next deve esperar para revalidar a página. Por exemplo, se o parâmetro for 10, a página será revalidada a cada 10 segundos. + +### Exemplo com revalidação a cada 10 segundos: + +```jsx +// src/pages/wISR/index.js.js + +// export default Pagina () {...} + +export async function getStaticProps() { + const response = await fetch("https://jsonplaceholder.typicode.com/todos/1"); + const todos = await response.json(); + + return { + props: { + initialTodos: todos, + }, + revalidate: 10, // Revalida a página a cada 10 segundos + }; +} +``` + +Página acima acessada pela url `http://localhost:3000/wISR`. + +Outra maneira de transformar uma página de SG para ISR é usando a função `getStaticPaths`. Essa função é executada no momento de build e deve retornar um objeto com um parâmetro `paths`, um array de objetos com `params`. Cada objeto dentro de `paths` representa uma página/url que será gerada no momento de build. + +### Exemplo de getStaticPaths: + +```jsx +// src/pages/wISR/[id].js + +// export default Pagina () {...} + +export async function getStaticProps(context) { + // context é um objeto que sempre é passado para essas funções, + // contendo informações sobre a requisição + + // pegando id da rota do next + const { id } = context.params; + + const response = await fetch( + `https://jsonplaceholder.typicode.com/todos/${id}` + ); + + const todos = await response.json(); + + return { + props: { + initialTodos: todos, + }, + }; +} + +export async function getStaticPaths() { + return { + paths: [ + { params: { id: "1" } }, + { params: { id: "2" } }, + { params: { id: "3" } }, + ], + fallback: "blocking", + }; +} +``` + +O parâmetro `params` é um objeto com os parâmetros que serão passados para a página. De acordo com o exemplo exemplo, se a página for acessada pela url `http://localhost:3000/wISR/1`, o objeto `params` deve ser `{ id: 1 }`. O Next irá gerar uma página para cada objeto dentro de `paths`. + +Além disso, o fallback determina o comportamento que o Next deve ter quando uma página não estiver gerada ainda. + +- Se o fallback for `false`, o Next irá retornar um erro 404. +- Se o fallback for `true`, não lembro o que acontece. Pesquisa aí e adiciona nessa documentação, caro leitor. +- Se o fallback for `blocking`, o Next irá gerar a página no momento da requisição, e irá bloquear a requisição até que a página seja gerada (que é o mesmo comportamento que no SSR). + +## SSR + +Para fazer SSR, é necessário que a página tenha a função `getServerSideProps` exportada. Essa função é executada no servidor no momento da request, e o retorno dela é passado para a página como `props`. + +Usamos SSR quando precisamos de dados que não podem ser gerados no momento de build, ou quando queremos sempre o conteúdo mais atualizado possível. + +```jsx +// src/pages/wSSR/index.js +import { api } from "../../services/api"; + +export default function Page({ products }) { + return ( +
+

SSR

+
    + {products.map((product) => ( +
  • +

    {product.name}

    +

    {product.description}

    +
  • + ))} +
+
+ ); +} + +export async function getServerSideProps(ctx) { + // get request info from context + const { req } = ctx; + + // get req headers + const { headers } = req; + + // get auth header + const authToken = headers["x-auth-token"]; + + // get products + api.get("/products", { + headers: { + "x-auth-token": authToken, + }, + }); + + return { + props: { + products: products, + }, + }; +} +``` diff --git a/execucao/next-js/v<=12/sobre.md b/execucao/next-js/v<=12/sobre.md new file mode 100644 index 0000000..e2fa604 --- /dev/null +++ b/execucao/next-js/v<=12/sobre.md @@ -0,0 +1,19 @@ +# O que é? + +Next js é um _framework full stack_ construído em cima do React. +Da mesma maneira que React é construído em cima do JS, Next foi feito em cima do React, e por isso é muito simples nos adaptarmos e até usarmos a documentação do react normal para boa parte dos problemas. + +## Por que se usaria? + +SG (_Static Generation_) e SSR (_Server Side Rendering_) ajudam muito na performance e no SEO (_Search Engine Optimization_) do web app. +Além disso, o diferencial do Next é o ISR (_Incremental Static Regeneration_), ou seja, o Next pode gerar páginas estáticas novas caso necessário. Ou seja, a página é renderizada no servidor uma vez quando surgir a primeira request para ela, e então é guardada em cachê como um _asset_ estático. Essa rota pode ser revalidada para gerar um novo _asset_ estático a ser guardado, caso necessário. + +## Diferença principal do nosso React "padrão" + +A diferença principal entre o Next e o Create React App (que normalmente usamos em projetos React): + +SG (_Static Generation_) e SSR (_Server Side Rendering_) do diretório '/pages' + +Geração de rotas a partir da organização de arquivos do diretório '/pages' + +Capacidade de dar uma resposta de api (ou seja, com um json), desde que seja feito dentro do diretório 'pages/api/' diff --git a/execucao/next-js/v>=13/README.md b/execucao/next-js/v>=13/README.md new file mode 100644 index 0000000..8e0b7ff --- /dev/null +++ b/execucao/next-js/v>=13/README.md @@ -0,0 +1 @@ +Nessas versões, o Next funciona totalmente diferente das versões anteriores. É melhor que seja usado quando passar da fase experimental, mas documentação de versões anteriores estão totalmente obsoletas para o Next v13 adiante. From c149b67db453ab62662716dc86865fc34de9ae74 Mon Sep 17 00:00:00 2001 From: artistrea Date: Sun, 6 Nov 2022 10:15:28 -0300 Subject: [PATCH 03/30] more about next routing and api --- execucao/next-js/v<=12/api.md | 102 ++++++++++++++++++++++++++++++++ execucao/next-js/v<=12/rotas.md | 100 +++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 execucao/next-js/v<=12/rotas.md diff --git a/execucao/next-js/v<=12/api.md b/execucao/next-js/v<=12/api.md index e69de29..8bb48ae 100644 --- a/execucao/next-js/v<=12/api.md +++ b/execucao/next-js/v<=12/api.md @@ -0,0 +1,102 @@ +# Next API + +O Next JS usa api padrão do Node.js, então você pode usar qualquer biblioteca que você quiser. O Next JS também tem uma API própria que pode ser usada para algumas coisas, como rotas, renderização do lado do servidor e renderização estática. + +```jsx +// src/pages/api/users/[id].js + +export default function handler(req, res) { + const { id } = req.query; + prisma.users.findUnique({ where: { id } }).then((user) => { + res.status(200).json(user); + }); +} +``` + +Exemplo usando o prisma para acessar o banco de dados. + +```jsx +// src/pages/api/users.js + +export default function handler(req, res) { + prisma.users.findMany().then((users) => { + res.status(200).json(users); + }); +} +``` + +Exemplo usando múltiplos parâmetros + +```jsx +// src/pages/api/users/[id]/posts/[postId].js + +export default function handler(req, res) { + const { id, postId } = req.query; + prisma.posts + .findUnique({ + where: { id: postId }, // encontrando o post pelo seu id + include: { user: true }, // incluindo o usuário que criou o post + }) + .then((post) => { + res.status(200).json(post); + }); +} +``` + +Exemplo usando o método POST na mesma rota, autenticando com headers antes + +```jsx +// src/pages/api/users.js + +export default function handler(req, res) { + if (req.method === "POST") { + handlePost(req, res); + } else { + // handle outros métodos + prisma.users.findMany().then((users) => { + res.status(200).json(users); + }); + } +} + +function handlePost(req, res) { + // get auth header + const authToken = req.headers["X-Auth-Token"]; + const authEmail = req.headers["X-Auth-Email"]; + + // check if auth header is valid + const userRequesting = await prisma.users.findUnique({ + where: { email: authEmail }, + }); + + // guard clause para caso o usuário não tenha permissão + if (!userRequesting || !userRequesting.authentication_token === authToken) { + res.status(403).json({ error: "Forbidden" }); + // caso seja inválido, não proceder com a função, retornar + return; + } + + const { name, email, password } = req.body; + + // validar corpo da request para criar o usuário + const {isInvalid, errors} = validateUser({ name, email, password }); + + if (isInvalid) { + res.status(422).json({ errors }); + // caso seja inválido, não proceder com a função, retornar + return; + } + + // create user + prisma.users + .create({ + data: {name, email, password} + }) + .then((user) => { + res.status(200).json(user); + }) + .catch((err) => { + res.status(422).json(err); + }); +} +``` diff --git a/execucao/next-js/v<=12/rotas.md b/execucao/next-js/v<=12/rotas.md new file mode 100644 index 0000000..074a585 --- /dev/null +++ b/execucao/next-js/v<=12/rotas.md @@ -0,0 +1,100 @@ +# Rotas no Next JS + +Rotas no Next.js são de fato endpoints criados para serem acessados via HTTP. Para criar uma rota, basta criar um arquivo com o nome da rota dentro da pasta `pages`. Next cuida do resto. + +Para que a mágica do Next funcione, é necessário exportar como default a função que será executada quando a rota for acessada (ou seja,a página ou a api req handler). + +## Páginas + +Para criar uma rota chamada `about`, basta criar um arquivo chamado `about.js`, ou `about/index.js` dentro da pasta `pages`. Esse arquivo será acessado via `http://localhost:3000/about`. + +Para criar uma rota com parâmetros, basta colocar usar colchetes ao redor desse parâmetro. Por exemplo, para criar uma rota chamada `/user/:id`, com parâmetro `id`, basta criar uma pasta e arquivo formando a rota `pages/user/[id].js`. Esse arquivo será acessado via `http://localhost:3000/user/1`. + +Para criar uma rota com múltiplos parâmetros, podemos criar pasta e arquivo `users/[id]-[name].js`, ou então `users/[id]/[name].js`, como sempre dentro da pasta `pages`. Esse arquivo será acessado via `http://localhost:3000/user/1-joao` no primeiro caso, e via `http://localhost:300/user/1/joao`. + +Lembre que essas rotas são apenas endpoints, e não páginas. Para criar páginas, é necessário criar um componente React na rota e exportá-lo como default. + +### Exemplos + +Podemos usar o hook useRouter dentro da página caso queremos acessar os parâmetros da rota pela página. + +```jsx +// src/pages/users/[id].js + +import { useRouter } from "next/router"; + +export default function User() { + const router = useRouter(); + const { id } = router.query; + + return

User {id}

; +} +``` + +```jsx +// src/pages/users/[id]/posts/[postId].js + +import { useRouter } from "next/router"; + +export default function Post() { + const router = useRouter(); + const { id, postId } = router.query; + + return ( + <> +

User {id}

+

Post {postId}

+ + ); +} +``` + +Podemos acessar os parâmetros da rota pelo contexto, caso queremos acessar os parâmetros da rota fora da página. + +```jsx +// src/pages/users/[id].js + +export default function User({ id }) { + return

User {id}

; +} + +export async function getServerSideProps(context) { + const { id } = context.params; + // podemos fazer uma chamada para a api agora, com o id da rota + + return { + props: { + id, + }, + }; +} +``` + +## API + +Para criar uma rota de api, basta criar um arquivo dentro da pasta `pages/api`. Esse arquivo será acessado via `http://localhost:3000/api/`. + +De resto, o roteamento de api é igual ao de páginas, com a diferença que a função exportada default recebe como parâmetro o objeto `req` e `res` do node, e deve responder à request a partir do `res`. Caso não seja feito isso, a request ficará pendente. Além disso, pode-se terminar a request com `res.end()`. Caso isso não seja feito, a request responderá com o definido até a função terminar sua execução pelo objeto `res`. + +### Exemplos + +Exemplo padrão de rota de api. + +```jsx +// src/pages/api/hello.js + +export default function handler(req, res) { + res.status(200).json({ name: "John Doe" }); +} +``` + +Exemplo usando parâmetros + +```jsx +// src/pages/api/users/[id].js + +export default function handler(req, res) { + const { id } = req.query; + res.status(200).json({ id, name: "John Doe" }); +} +``` From 5493cd2967ce8773fb2a00200860fb60fac0f171 Mon Sep 17 00:00:00 2001 From: artistrea Date: Wed, 9 Nov 2022 21:12:11 -0300 Subject: [PATCH 04/30] more docs --- execucao/next-js/v<=12/rotas.md | 2 +- execucao/next-js/v<=12/sobre.md | 6 ++-- execucao/next-js/v<=12/styled-components.md | 39 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 execucao/next-js/v<=12/styled-components.md diff --git a/execucao/next-js/v<=12/rotas.md b/execucao/next-js/v<=12/rotas.md index 074a585..0513afc 100644 --- a/execucao/next-js/v<=12/rotas.md +++ b/execucao/next-js/v<=12/rotas.md @@ -72,7 +72,7 @@ export async function getServerSideProps(context) { ## API -Para criar uma rota de api, basta criar um arquivo dentro da pasta `pages/api`. Esse arquivo será acessado via `http://localhost:3000/api/`. +Para criar uma rota de api, basta criar um arquivo dentro da pasta `pages/api`. Esse arquivo será acessado via `http://localhost:3000/api/`. O arquivo deve exportar default uma função que será executada quando a rota for acessada. De resto, o roteamento de api é igual ao de páginas, com a diferença que a função exportada default recebe como parâmetro o objeto `req` e `res` do node, e deve responder à request a partir do `res`. Caso não seja feito isso, a request ficará pendente. Além disso, pode-se terminar a request com `res.end()`. Caso isso não seja feito, a request responderá com o definido até a função terminar sua execução pelo objeto `res`. diff --git a/execucao/next-js/v<=12/sobre.md b/execucao/next-js/v<=12/sobre.md index e2fa604..3199ed0 100644 --- a/execucao/next-js/v<=12/sobre.md +++ b/execucao/next-js/v<=12/sobre.md @@ -1,11 +1,11 @@ # O que é? -Next js é um _framework full stack_ construído em cima do React. -Da mesma maneira que React é construído em cima do JS, Next foi feito em cima do React, e por isso é muito simples nos adaptarmos e até usarmos a documentação do react normal para boa parte dos problemas. +Next js é um _framework full stack_ construído em cima do React, da mesma maneira que React é construído em cima do JS. Por isso é muito simples nos adaptarmos e até usarmos a documentação do react normal para boa parte dos problemas enfrentados. -## Por que se usaria? +## Por que usar? SG (_Static Generation_) e SSR (_Server Side Rendering_) ajudam muito na performance e no SEO (_Search Engine Optimization_) do web app. + Além disso, o diferencial do Next é o ISR (_Incremental Static Regeneration_), ou seja, o Next pode gerar páginas estáticas novas caso necessário. Ou seja, a página é renderizada no servidor uma vez quando surgir a primeira request para ela, e então é guardada em cachê como um _asset_ estático. Essa rota pode ser revalidada para gerar um novo _asset_ estático a ser guardado, caso necessário. ## Diferença principal do nosso React "padrão" diff --git a/execucao/next-js/v<=12/styled-components.md b/execucao/next-js/v<=12/styled-components.md new file mode 100644 index 0000000..e577297 --- /dev/null +++ b/execucao/next-js/v<=12/styled-components.md @@ -0,0 +1,39 @@ +# Usando styled-components com Next.js + +## Possíveis problemas + +Para usarmos o styled-components no Next.js, precisamos mudar configurações. Caso contrário, poderão ocorrer _hydration errors_, e comportamento estranho dos estilos. + +## Configuração + +Para isso, vamos substituir o compilador padrão do next por um do babel, e adicionar o styled-components como plugin. + +Primeiro, vamos instalar as dependências necessárias: + +```bash +yarn add -D babel-plugin-styled-components +yarn add styled-components +``` + +Agora, vamos criar um arquivo de configuração do babel, chamado `.babelrc`, na raiz do projeto, e adicionar o seguinte conteúdo: + +```js +// .babelrc +{ + "presets": ["next/babel"], + "plugins": [ + [ + "styled-components", + { + "ssr": true, + "displayName": true, + "preprocess": false + } + ] + ] +} + +``` + +Pronto! Agora, podemos usar o styled-components no Next.js. +{% hint style="info" %} OBS: Seria melhor nem usar styled-components... {% endhint %} From 55cc82d8dd5428f61da0c510cc9f899910670960 Mon Sep 17 00:00:00 2001 From: artistrea Date: Wed, 9 Nov 2022 21:38:54 -0300 Subject: [PATCH 05/30] updated docs --- SUMMARY.md | 11 +++++++ execucao/next-js/v<=12/api.md | 2 ++ execucao/next-js/v<=12/styled-components.md | 36 ++++++++------------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index ec66ed1..2214570 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -53,6 +53,17 @@ * [Estilizando](execucao/front-end/react-js/estilizando.md) * [Hooks](execucao/front-end/react-js/hooks.md) * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) +* [Next.js](execucao/next-js/) + * [Conceitos relevantes](execucao/next-js/conceitos-relevantes/README.md) + * [V<=12](execucao/next-js/v<=12) + * [Sobre](execucao/next-js/v<=12/sobre.md) + * [Inicio rapido](execucao/next-js/v<=12/inicio-rapido.md) + * [Rotas](execucao/next-js/v<=12/rotas.md) + * [Paginas e padroes](execucao/next-js/v<=12/paginas-e-padroes.md) + * [Api](execucao/next-js/v<=12/api.md) + * [Styled Components](execucao/next-js/v<=12/styled-components.md) + * [Prisma](execucao/next-js/v<=12/prisma.md) + * [V>=13](execucao/next-js/v>=13/README.md) * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) * [Cloudinary](execucao/projetos/cloudinary.md) diff --git a/execucao/next-js/v<=12/api.md b/execucao/next-js/v<=12/api.md index 8bb48ae..60d0e4d 100644 --- a/execucao/next-js/v<=12/api.md +++ b/execucao/next-js/v<=12/api.md @@ -2,6 +2,8 @@ O Next JS usa api padrão do Node.js, então você pode usar qualquer biblioteca que você quiser. O Next JS também tem uma API própria que pode ser usada para algumas coisas, como rotas, renderização do lado do servidor e renderização estática. +No caso, os exemplos estão sendo dados com [Prisma](https://www.prisma.io/) (docs a fazer), uma ORM que facilita muito queries no banco de dados. Interprete que, quando `prisma` é chamada, uma busca no banco de dados está sendo feita. + ```jsx // src/pages/api/users/[id].js diff --git a/execucao/next-js/v<=12/styled-components.md b/execucao/next-js/v<=12/styled-components.md index e577297..b9440dd 100644 --- a/execucao/next-js/v<=12/styled-components.md +++ b/execucao/next-js/v<=12/styled-components.md @@ -6,32 +6,22 @@ Para usarmos o styled-components no Next.js, precisamos mudar configurações. C ## Configuração -Para isso, vamos substituir o compilador padrão do next por um do babel, e adicionar o styled-components como plugin. +Basta alterar o arquivo `next.config.js`, alterando `nextConfig.compiler.styledComponents = true`: -Primeiro, vamos instalar as dependências necessárias: - -```bash -yarn add -D babel-plugin-styled-components -yarn add styled-components -``` - -Agora, vamos criar um arquivo de configuração do babel, chamado `.babelrc`, na raiz do projeto, e adicionar o seguinte conteúdo: ```js -// .babelrc -{ - "presets": ["next/babel"], - "plugins": [ - [ - "styled-components", - { - "ssr": true, - "displayName": true, - "preprocess": false - } - ] - ] -} +// next.config.js +/** @type {import('next').NextConfig} */ +const nextConfig = { + // outras opções + compiler: { + // possivelmente outras opções + // adicionar: + styledComponents: true, + }, +}; + +module.exports = nextConfig; ``` From d01bcf2afdcc158068f6fb414c0559d0ef66d086 Mon Sep 17 00:00:00 2001 From: artistrea Date: Thu, 10 Nov 2022 08:57:54 -0300 Subject: [PATCH 06/30] added next api docs --- execucao/next-js/v<=12/paginas-e-padroes.md | 21 ++++++- execucao/next-js/v<=12/rotas.md | 1 + execucao/next-js/v<=12/sobre.md | 70 +++++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/execucao/next-js/v<=12/paginas-e-padroes.md b/execucao/next-js/v<=12/paginas-e-padroes.md index 879751f..4371f87 100644 --- a/execucao/next-js/v<=12/paginas-e-padroes.md +++ b/execucao/next-js/v<=12/paginas-e-padroes.md @@ -33,6 +33,10 @@ export default function Home() { return (
+ + Home + +

SG

{todos &&
{JSON.stringify(todos, null, 2)}
} @@ -64,6 +68,10 @@ export default function wGetStaticProps({ initialTodos }) { return (
+ + Better Home + +

SG

{todos &&
{JSON.stringify(todos, null, 2)}
} @@ -171,18 +179,26 @@ Para fazer SSR, é necessário que a página tenha a função `getServerSideProp Usamos SSR quando precisamos de dados que não podem ser gerados no momento de build, ou quando queremos sempre o conteúdo mais atualizado possível. ```jsx -// src/pages/wSSR/index.js +// src/pages/products/index.js import { api } from "../../services/api"; -export default function Page({ products }) { +export default function ProductsIndex({ products }) { return (
+ + My Page + +

SSR

    {products.map((product) => (
  • {product.name}

    {product.description}

    + + + Saiba mais +
  • ))}
@@ -192,6 +208,7 @@ export default function Page({ products }) { export async function getServerSideProps(ctx) { // get request info from context + // Mais sobre ctx: https://nextjs.org/docs/api-reference/data-fetching/get-server-side-props#context-parameter const { req } = ctx; // get req headers diff --git a/execucao/next-js/v<=12/rotas.md b/execucao/next-js/v<=12/rotas.md index 0513afc..9be9219 100644 --- a/execucao/next-js/v<=12/rotas.md +++ b/execucao/next-js/v<=12/rotas.md @@ -61,6 +61,7 @@ export default function User({ id }) { export async function getServerSideProps(context) { const { id } = context.params; // podemos fazer uma chamada para a api agora, com o id da rota + // const data = await api.get(`/users/${id}`) return { props: { diff --git a/execucao/next-js/v<=12/sobre.md b/execucao/next-js/v<=12/sobre.md index 3199ed0..5ae3477 100644 --- a/execucao/next-js/v<=12/sobre.md +++ b/execucao/next-js/v<=12/sobre.md @@ -17,3 +17,73 @@ SG (_Static Generation_) e SSR (_Server Side Rendering_) do diretório '/pages' Geração de rotas a partir da organização de arquivos do diretório '/pages' Capacidade de dar uma resposta de api (ou seja, com um json), desde que seja feito dentro do diretório 'pages/api/' + +## Usando api específica do Next + +Next disponibiliza api's construídas em cima do React e que devem ser usadas, como: +- [Link](https://nextjs.org/docs/api-reference/next/link); +- [Router](https://nextjs.org/docs/api-reference/next/router); +- [Head](https://nextjs.org/docs/api-reference/next/head); +- [Image](https://nextjs.org/docs/api-reference/next/image); +- etc. + +Exemplo de uso: + + +```jsx +// src/pages/index.js + +import Link from 'next/link' +import Router from 'next/router' +import Head from 'next/head' +import Image from 'next/image' + +export default function Home() { + return ( +
+ {/** + Head é usado para adicionar tags html no head do documento. + É possível adicionar tags como title, meta, link, etc. + É essa tag que torna o Next ótimo para SEO. + **/} + + Home + + + {/**] + Quaisquer outras tags podem ser adicionadas aqui. + Elas podem depender de uma resposta de api também, por exemplo. + **/} + +

Home

+ + {/** + Link melhora a performance do app, por usar prefetch em certos casos + **/} + + Acessar página Sobre + +
+ + {/** + Router, em geral, é melhor usado em lógicas mais complexas, como + redirecionamento condicional, + acessar proriedades da rota atual, etc. + **/} + +
+ + {/** + Image é usado para carregar imagens de forma otimizada. + Como src, deve receber uma imagem importada. + Também pode receber uma url externa ou um caminho relativo, desde que configurado. + Caso seja passado um asset não estático para a tag Image, precisa possuir tamanho definido, ou então preencher o pai. + https://nextjs.org/docs/api-reference/next/image#src + **/} + +
+ ) +} + + +``` \ No newline at end of file From 5e68032853bc42df4980a25033755fc3e4bed984 Mon Sep 17 00:00:00 2001 From: artistrea Date: Thu, 10 Nov 2022 20:05:09 -0300 Subject: [PATCH 07/30] =?UTF-8?q?feita=20documenta=C3=A7=C3=A3o=20prisma?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SUMMARY.md | 5 ++ execucao/back-end/prisma-js/ATENCAO.md | 3 + execucao/back-end/prisma-js/README.md | 4 + .../back-end/prisma-js/configuracao/local.md | 78 ++++++++++++++++++ .../prisma-js/configuracao/projeto.md | 11 +++ execucao/back-end/prisma-js/exemplos.md | 82 +++++++++++++++++++ 6 files changed, 183 insertions(+) create mode 100644 execucao/back-end/prisma-js/ATENCAO.md create mode 100644 execucao/back-end/prisma-js/README.md create mode 100644 execucao/back-end/prisma-js/configuracao/local.md create mode 100644 execucao/back-end/prisma-js/configuracao/projeto.md create mode 100644 execucao/back-end/prisma-js/exemplos.md diff --git a/SUMMARY.md b/SUMMARY.md index 5bc8738..95c6042 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -54,6 +54,11 @@ * [Estilizando](execucao/front-end/react-js/estilizando.md) * [Hooks](execucao/front-end/react-js/hooks.md) * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) +* [Prisma JS](execucao/back-end/prisma-js/README.md) + * [ATENCAO](execucao/back-end/prisma-js/ATENCAO.md) + * [Exemplos](execucao/back-end/prisma-js/exemplos.md) + * [Configurando projeto](execucao/back-end/prisma-js/configuracao/projeto.md) + * [Configurando local](execucao/back-end/prisma-js/configuracao/local.md) * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) * [Cloudinary](execucao/projetos/cloudinary.md) diff --git a/execucao/back-end/prisma-js/ATENCAO.md b/execucao/back-end/prisma-js/ATENCAO.md new file mode 100644 index 0000000..2c57cac --- /dev/null +++ b/execucao/back-end/prisma-js/ATENCAO.md @@ -0,0 +1,3 @@ +# O que pode dar muito errado: + +Acessar o banco de dados em dois apps diferentes. Por exemplo, se colocarmos validações somente no rails, e não no prisma, podemos ter problemas de segurança, pois o prisma não vai validar nada. Se fizermos nos dois, podemos ter problemas de duplicação de código, dificuldade de manutenção e mais bugs. Sendo assim, o ideal é deixar a responsabilidade de escrever no banco de dados para um único app. diff --git a/execucao/back-end/prisma-js/README.md b/execucao/back-end/prisma-js/README.md new file mode 100644 index 0000000..b79e69d --- /dev/null +++ b/execucao/back-end/prisma-js/README.md @@ -0,0 +1,4 @@ +# Prisma + +Prisma é uma biblioteca javascript/typescript, escrita em Rust, feita para funcionar no Node. Sendo assim, funciona com o código de servidor do Next JS, e essa será sua principal utilidade para nós. + diff --git a/execucao/back-end/prisma-js/configuracao/local.md b/execucao/back-end/prisma-js/configuracao/local.md new file mode 100644 index 0000000..3cbbb11 --- /dev/null +++ b/execucao/back-end/prisma-js/configuracao/local.md @@ -0,0 +1,78 @@ +# Configuração no projeto (uma única vez): + +{% hint style="info" %} +Trecho voltado à gerência +{% endhint %} + +Adicionar o prisma CLI (Command Line Interface) ao projeto como dependência de desenvolvimento: + +```bash +yarn add -D prisma +``` + +Então inicializar o prisma: + +```bash +yarn prisma init --datasource-provider postgresql +``` + +Adicione `.env` ao `.gitignore` + +#### Integrando com banco de dados externo (e.g. do rails): + +{% hint style="info" %} +O prisma pode cuidar da criação do banco de dados, mas por enquanto vamos usar o banco de dados do rails. Para isso, precisamos configurar o prisma para usar um banco de dados externo. +{% endhint %} + +Adicionar ao arquivo `.env.example` (criá-lo caso não exista) o seguinte conteúdo: + +```text +# Variáveis de ambiente para o prisma: + +DATABASE_URL="postgresql://user:password@localhost:5432/" +# DATABASE_URL="postgresql://:@localhost:5432/?schema=public" +``` + +Substitua acima o pelo nome do banco de dados. O rails por padrão pega o nome do projeto e coloca adiciona um pósfixo `_development`. Então, se o nome do projeto for `projeto-api`, o banco de dados de desenvolvimento será `Projeto_api_development`, e o arquivo ficaria assim: + +```text +DATABASE_URL="postgresql://:@localhost:5432/Projeto_api_development" +``` + + +{% hint style="info" %} + +A url de conexão com o banco de dados tem o seguinte padrão, caso seja necessário mudar alguma parte: + +```text +postgresql://:@:/?schema= +``` + +#### Criando o client do prisma: + +Adicionar isso, adicionar aos script em `package.json`: + +```json +"scripts": { + // ...scripts anteriores + "prisma:update": "yarn prisma db pull && yarn prisma generate", +}, +``` + +Então, na pasta `prisma`, criar um arquivo chamado client.js, com o seguinte conteúdo: + +```js +import { PrismaClient } from "@prisma/client"; + +// Permite que BigInt seja serializável para JSON, e funcione corretamente +// caso existam tais campos no banco de dados +// O rails por padrão usa BigInt para os ids +BigInt.prototype.toJSON = function () { + return this.toString(); +}; + +export const prisma = new PrismaClient({ + log: ['query', 'info', 'warn', 'error'], +}); +``` + diff --git a/execucao/back-end/prisma-js/configuracao/projeto.md b/execucao/back-end/prisma-js/configuracao/projeto.md new file mode 100644 index 0000000..2c3382e --- /dev/null +++ b/execucao/back-end/prisma-js/configuracao/projeto.md @@ -0,0 +1,11 @@ +# Configuração do ambiente (para cada dev): + +Criar um arquivo `.env` na raiz do projeto, seguindo o exemplo do `.env.example` + +Substituindo os valores de acordo com o banco de dados local, basta rodar o comando: + +```bash +yarn prisma:update +``` + +E agora o prisma já pode ser usado localmente. diff --git a/execucao/back-end/prisma-js/exemplos.md b/execucao/back-end/prisma-js/exemplos.md new file mode 100644 index 0000000..7287feb --- /dev/null +++ b/execucao/back-end/prisma-js/exemplos.md @@ -0,0 +1,82 @@ +# Exemplos de uso + + +## Next JS + +```js +// src/pages/api/users/index.js + +// Tenha certeza de que o caminho está correto +import { prisma } from "../../../prisma/client"; + +export default async function handler(req, res) => { + // Encontra todos os usuários no banco de dados + const users = await prisma.users.findMany(); + res.status(200).json({ users }); +}; +``` + +```js +// src/pages/api/users/[id].js +import { prisma } from "../../../prisma/client"; + +export default async function handler(req, res) => { + // Pegando id da request: + const { id } = req.query; + + // Encontra por propriedade que é única + const user = await prisma.users.findUnique({ + where: { + id: req.query.id, + }, + }); + + res.status(200).json({ user }); +}; +``` + +Caso o usuário tenha uma relação com outra tabela que desejamos mostrar, podemos usar o `include`. Exemplo sendo que usuário tem uma relação com `posts`: + +```js +// src/pages/api/users/[id].js +import { prisma } from "../../../prisma/client"; + +export default async (req, res) => { + // Pegando id da request: + const { id } = req.query; + + // Encontra por propriedade que é única + const user = await prisma.users.findUnique({ + where: { + id: id, + }, + include: { + posts: true, + }, + }); + + res.status(200).json({ user }); +}; +``` + + +```js +// src/pages/api/users/create.js +import { prisma } from "../../../prisma/client"; + +export default async function handler(req, res) => { + // Pegando dados da request: + const { name, email } = req.body; + + const user = await prisma.users.create({ + data: { + name, + email, // equivalente a 'email: email' + }, + }); + + res.status(200).json({ user }); +}; +``` + + From db7455c13fa71fe8a1f2f7364fe18d3e905148f3 Mon Sep 17 00:00:00 2001 From: artistrea Date: Wed, 7 Dec 2022 20:15:10 -0300 Subject: [PATCH 08/30] ignoring .vscode --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..600d2d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode \ No newline at end of file From 2c862e13beed5c0f25ccc245424434e26c027c31 Mon Sep 17 00:00:00 2001 From: artistrea Date: Wed, 7 Dec 2022 22:08:12 -0300 Subject: [PATCH 09/30] docs netlify done --- SUMMARY.md | 2 + execucao/projetos/deploy/README.md | 22 ++++++++++ .../projetos/deploy/terceirizado/netlify.md | 38 ++++++++++++++++++ imagens/Netlify_deploy.png | Bin 0 -> 105842 bytes 4 files changed, 62 insertions(+) create mode 100644 execucao/projetos/deploy/README.md create mode 100644 execucao/projetos/deploy/terceirizado/netlify.md create mode 100644 imagens/Netlify_deploy.png diff --git a/SUMMARY.md b/SUMMARY.md index 5bc8738..5388c51 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -56,6 +56,8 @@ * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) + * [Deploy](execucao/projetos/deploy/README.md) + * [Netlify](execucao/projetos/deploy/terceirizado/netlify.md) * [Cloudinary](execucao/projetos/cloudinary.md) * [Gerência de projetos](execucao/projetos/gerencia.md) * [Finalização de Projetos](execucao/projetos/finalizacao.md) diff --git a/execucao/projetos/deploy/README.md b/execucao/projetos/deploy/README.md new file mode 100644 index 0000000..385f8b0 --- /dev/null +++ b/execucao/projetos/deploy/README.md @@ -0,0 +1,22 @@ +# Deploy + +Deploy nada mais é do que colocar o seu projeto no ar, para que ele possa ser acessado por qualquer pessoa. Para tal, é utilizada um domínio que aponta para o endereço ip de um servidor, que é onde o projeto está hospedado. O servidor pode ser um computador pessoal, um servidor de hospedagem, um servidor de cloud, etc. + +Em processo de [staging](https://pt.wikipedia.org/wiki/Ambiente_de_implanta%C3%A7%C3%A3o), é comum terceirizar e utilizar domínios temporários, usando como Heroku, Netlify, Railway, etc. + +Para entender deploy é necessário entender o fluxo desencadeado ao entrar num site. + +## Entrando num site + +Quando entramos num site, o que acontece é o seguinte: + +1. O navegador faz uma requisição (get '/', ou outra rota) para o domínio. +2. O domínio é encontrado (ou não) nas listas de autoridades de domínios. Ele aponta para o endereço ip do servidor, para o qual a requisição é direcionada. +3. O servidor responde com algo (html, css e js; um json; etc). + +Então, precisamos definir o que é esse algo. Para isso, em serviços terceirizados geralmente são automatizados alguns processos (mas nem sempre), como: + +- Instalação de dependências +- Compilação de arquivos +- Criação de assets estáticos (js bundles, css bundles, etc.) +- Criação de banco de dados diff --git a/execucao/projetos/deploy/terceirizado/netlify.md b/execucao/projetos/deploy/terceirizado/netlify.md new file mode 100644 index 0000000..a77907d --- /dev/null +++ b/execucao/projetos/deploy/terceirizado/netlify.md @@ -0,0 +1,38 @@ +# Deploy no Netlify + +## O que é o [Netlify](https://www.netlify.com/) + +O Netlify é um serviço de hospedagem de sites estáticos, que também oferece serviços de _staging_ e _deploy_ automatizados. Isso significa que não é possível hospedar backend no Netlify, apenas frontend (com possibilidade de _AWS Lambda_). + +Para fazer _deploy_ no Netlify, a possiblidade atualmente gratuita é fazer _deploy_ manual, que consiste em gerar e colocar manualmente os arquivos que serão servidos no site. Sendo assim, o processo de _bundling_ deve ser feito na mão. + +## Como fazer _deploy_ manual + +### Preparando o projeto + +Na própria main, mude o `/public/favicon.ico` para o real ícone do projeto. Mude o `index.html`, trocando os conteúdos das tags ``, `` e a `lang` da tag `<html>`. Pense na possibilidade de adicionar um `/public/robots.txt`. + + +Para seguir os próximos passos, primeiro crie uma branch chamada `netlify` no seu projeto, caso ainda não exista. + +Mude todas as referências a localhost por suas respectivas urls de produção. Por exemplo, se você tem uma instância axios com a url `http://localhost:3333/api/v1`, mude para `https://seu-projeto.railway.app/api/v1` ou equivalente (a url de _staging_). Se a url das imagens é `http://localhost:3333/`, mude para `https://amazonaws.com/seu-projeto/` ou equivalente. + + +#### Para roteamento client side + +Geralmente fazemos _deploy_ de React como [_single page app_](https://en.wikipedia.org/wiki/Single-page_application), com um roteador _client side_, como react-router-dom. Sendo assim, rotas não são [_endpoints_](https://www.cloudflare.com/pt-br/learning/security/api/what-is-api-endpoint/), e sempre devem ser retornados os mesmos arquivos pra requisições, independente da rota. + +Para tal, adicione o arquivo `_redirects` no do projeto com o seguinte conteúdo: +``` +/* /index.html 200 + +``` + +### Fazendo _deploy_ manual + +1. Vá para a branch `netlify` +2. Rode o comando `yarn build` para gerar uma pasta `build` com os arquivos estáticos. +3. Faça login na conta de projetos da struct +4. Selecione a opção de _deploy_ manual (__deploy_ manually_) +![Netlify_deploy](../../../../imagens/Netlify_deploy.png) +5. Arraste e solte os arquivos da pasta `build` na área de upload \ No newline at end of file diff --git a/imagens/Netlify_deploy.png b/imagens/Netlify_deploy.png new file mode 100644 index 0000000000000000000000000000000000000000..9f45406a626c1a32acd9a9eaeb008530b86fcf44 GIT binary patch literal 105842 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 KuLap&HOLD`U22p(*Dm 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 literal 0 HcmV?d00001 From f96ff6f062e8039e5579713300ba4eeac01b2ba2 Mon Sep 17 00:00:00 2001 From: "Artur P. P" <89547366+artistrea@users.noreply.github.com> Date: Fri, 9 Dec 2022 11:04:05 -0300 Subject: [PATCH 10/30] Update SUMMARY.md --- SUMMARY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index 5388c51..3723059 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -57,7 +57,8 @@ * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) * [Deploy](execucao/projetos/deploy/README.md) - * [Netlify](execucao/projetos/deploy/terceirizado/netlify.md) + * [Terceirizando]() + * [Netlify](execucao/projetos/deploy/terceirizado/netlify.md) * [Cloudinary](execucao/projetos/cloudinary.md) * [Gerência de projetos](execucao/projetos/gerencia.md) * [Finalização de Projetos](execucao/projetos/finalizacao.md) From 8f58ca9bbec8baf7d25358d1296a78628dc32a98 Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Fri, 9 Dec 2022 11:24:51 -0300 Subject: [PATCH 11/30] doc heroku criada --- SUMMARY.md | 3 ++ .../projetos/deploy/terceirizando/heroku.md | 28 +++++++++++++++++++ execucao/projetos/heroku.md | 12 -------- 3 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 execucao/projetos/deploy/terceirizando/heroku.md delete mode 100644 execucao/projetos/heroku.md diff --git a/SUMMARY.md b/SUMMARY.md index 5bc8738..02c8c60 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -56,6 +56,9 @@ * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) + * [Deploy](execucao/projetos/deploy/README.md) + * [Terceirizando]() + * [Heroku](execucao/projetos/deploy/terceirizado/heroku.md) * [Cloudinary](execucao/projetos/cloudinary.md) * [Gerência de projetos](execucao/projetos/gerencia.md) * [Finalização de Projetos](execucao/projetos/finalizacao.md) diff --git a/execucao/projetos/deploy/terceirizando/heroku.md b/execucao/projetos/deploy/terceirizando/heroku.md new file mode 100644 index 0000000..397b697 --- /dev/null +++ b/execucao/projetos/deploy/terceirizando/heroku.md @@ -0,0 +1,28 @@ +# Heroku + + +No momento da atualização dessa documentação, não existe mais plano grátis no Heroku. Como a gente é muito mão de vaca, não usamos mais. Mas caso necessário, basta usar a conta da struct e seguir os passos abaixo. + +## O que é? +Heroku é uma plataforma de deployment de website, desenvolvidos em quase todos frameworks mais famosos utimamente, ela automaticamente analisa o repositório e verifica a linguagem que você está utilizando, e já faz o setup completo, facilitando uma das principais e mais demoradas atividades de um projeto de website. + +O heroku roda um container docker com seus scripts de inicialização. Isso significa que é necessário que o seu projeto seja um servidor, ou seja, que ele tenha uma porta para ser acessado, e que ele seja capaz de responder a requisições. Sendo assim, é ideal para api's, mas não para servir _assets estáticos_ (como os gerados numa aplicação React comum). + + +## Como subir o site no heroku? + + + +- Se for sua primeira fez, siga o tutorial para instalar o cli do heroku https://devcenter.heroku.com/articles/heroku-cli#download-and-install +- Depois basta logar no heroku e criar um novo app. +- Na pasta do repositório do projeto, digite o comando ```heroku git:remote -a nome_do_app``` +- Configure o banco de dados para postgresql +- E por fim rode o comando ```git push heroku master``` + +### Vídeo tutorial +- https://youtu.be/V8wGbZOAe38 + + +## Observação: + +Boa parte dessa documentação foi produzida por Pedro Augusto Ramalho Duarte, um ex-membro da Struct. Atualmente não estamos mais usando o Heroku, e por isso o tutorial pode estar desatualizado. diff --git a/execucao/projetos/heroku.md b/execucao/projetos/heroku.md deleted file mode 100644 index 4bd0244..0000000 --- a/execucao/projetos/heroku.md +++ /dev/null @@ -1,12 +0,0 @@ -# Heroku -Heroku é uma plataforma de deployment de website, desenvolvidos em quase todos frameworks mais famosos utimamente, ela automaticamente analisa o repositório e verifica a linguagem que você está utilizando, e já faz o setup completo, facilitando uma das principais e mais demoradas atividades de um projeto de website. - -# Como subir o site no heroku? -- Se for sua primeira fez, siga o tutorial para instalar o cli do heroku https://devcenter.heroku.com/articles/heroku-cli#download-and-install -- Depois basta logar no heroku e criar um novo app. -- Na pasta do repositório do projeto, digite o comando ```heroku git:remote -a nome_do_app``` -- Configure o banco de dados para postgresql -- E por fim rode o comando ```git push heroku master``` - -# Vídeo tutorial -- https://youtu.be/V8wGbZOAe38 From 4f68853d79918ee513e6f6bdd2b29b1b644a6017 Mon Sep 17 00:00:00 2001 From: "Artur P. P" <89547366+artistrea@users.noreply.github.com> Date: Mon, 19 Dec 2022 11:24:23 -0300 Subject: [PATCH 12/30] Update execucao/projetos/deploy/terceirizado/netlify.md Co-authored-by: DavidsonGM <65040370+DavidsonGM@users.noreply.github.com> --- execucao/projetos/deploy/terceirizado/netlify.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/execucao/projetos/deploy/terceirizado/netlify.md b/execucao/projetos/deploy/terceirizado/netlify.md index a77907d..97e6682 100644 --- a/execucao/projetos/deploy/terceirizado/netlify.md +++ b/execucao/projetos/deploy/terceirizado/netlify.md @@ -15,7 +15,7 @@ Na própria main, mude o `/public/favicon.ico` para o real ícone do projeto. Mu Para seguir os próximos passos, primeiro crie uma branch chamada `netlify` no seu projeto, caso ainda não exista. -Mude todas as referências a localhost por suas respectivas urls de produção. Por exemplo, se você tem uma instância axios com a url `http://localhost:3333/api/v1`, mude para `https://seu-projeto.railway.app/api/v1` ou equivalente (a url de _staging_). Se a url das imagens é `http://localhost:3333/`, mude para `https://amazonaws.com/seu-projeto/` ou equivalente. +Mude todas as referências a localhost por suas respectivas urls de produção. Por exemplo, se você tem uma instância axios com a url `http://localhost:3333/api/v1`, mude para `https://seu-projeto.railway.app/api/v1` ou equivalente (a url de _staging_). Se a url das imagens é `http://localhost:3333/`, mude para `https://seu-projeto.railway.app` ou equivalente. #### Para roteamento client side From 8c3d8af9f9092fb9ccab84cf46166befb53979cf Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Wed, 25 Jan 2023 19:44:25 -0300 Subject: [PATCH 13/30] changes to heroku docs --- .../projetos/deploy/terceirizando/heroku.md | 100 +++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/execucao/projetos/deploy/terceirizando/heroku.md b/execucao/projetos/deploy/terceirizando/heroku.md index 397b697..7b8b80d 100644 --- a/execucao/projetos/deploy/terceirizando/heroku.md +++ b/execucao/projetos/deploy/terceirizando/heroku.md @@ -2,6 +2,7 @@ No momento da atualização dessa documentação, não existe mais plano grátis no Heroku. Como a gente é muito mão de vaca, não usamos mais. Mas caso necessário, basta usar a conta da struct e seguir os passos abaixo. +(Documentação atual feita pensada em Rails) ## O que é? Heroku é uma plataforma de deployment de website, desenvolvidos em quase todos frameworks mais famosos utimamente, ela automaticamente analisa o repositório e verifica a linguagem que você está utilizando, e já faz o setup completo, facilitando uma das principais e mais demoradas atividades de um projeto de website. @@ -12,17 +13,110 @@ O heroku roda um container docker com seus scripts de inicialização. Isso sign ## Como subir o site no heroku? +### Preparando o projeto + +Crie uma branch chamada heroku, caso ainda não tenha, e faça as seguintes mudanças: + +1. Crie um arquivo chamado `start.sh` na raíz do projeto com o seguinte conteúdo: + +```sh +#!/bin/sh +rails db:migrate +bundle exec puma -C config/puma.rb +``` + +`#!/bin/sh`: essa linha tem, por principal objetivo, informar ao shell qual intérprete deverá ser usado para a execução do script. No nosso caso, o intérprete usado será o sh, ou seja o Bourne shell. + +`rails db:migrate`: a linha de código padrão para executar as migrations no rails e garantir que todos os bancos de dados estejam atualizados e com suas informações certas + + +`bundle exec puma -C config/puma.rb`: esse código funciona como um rails s, ou seja, serve para executar o puma (servidor padrão do rails) com alguns recursos a mais que o rails s por si só, não oferece. + + +2. Caso seu projeto utilize Active Storage, ou guarda alguma imagem enviada por upload, é necessário mudar onde os arquivos ficam armazenados, escolhendo entre [cloudinary](https://cloudinary.com/documentation/ruby_rails_quickstart) e [amazon s3](https://devcenter.heroku.com/articles/active-storage-on-heroku). Em resumo, deve ser configurado os arquivos: + +ATENÇÃO: Não colocar as chaves de acesso no repositório, apenas no heroku. Na branch de produção, requerer as variáveis de ambiente no heroku, e colocar no código com `ENV['NOME_DA_VARIAVEL']`. Por exemplo +```ruby +# NÃO FAZER ISSO NUNCA!!!!!!!! +Cloudinary.config_from_url("cloudinary://API_KEY:API_SECRET@CLOUD_NAME") + +# No lugar disso, fazer: +Cloudinary.config_from_url("cloudinary://#{ENV['CLOUDINARY_API_KEY']}:#{ENV['CLOUDINARY_API_SECRET']}@#{ENV['CLOUDINARY_CLOUD_NAME']}") +# E colocar as variáveis de ambiente no heroku +``` + +- `Gemfile` (instalar a SDK do serviço escolhido, depois rodar bundle); +- `config/environments/production.rb`; +- `config/storage.yml`; +- `config/initializers/active_storage.rb`. +Além de precisar colocar as chaves de acesso do serviço escolhido, lá no heroku. + + +### Subindo o projeto + +#### Por CLI (Command Line Interface) +(Essa parte da documentação foi feita há bastante tempo por Pedro Augusto Ramalho Duarte) - Se for sua primeira fez, siga o tutorial para instalar o cli do heroku https://devcenter.heroku.com/articles/heroku-cli#download-and-install +- Vídeo tutorial: https://youtu.be/V8wGbZOAe38 - Depois basta logar no heroku e criar um novo app. - Na pasta do repositório do projeto, digite o comando ```heroku git:remote -a nome_do_app``` - Configure o banco de dados para postgresql - E por fim rode o comando ```git push heroku master``` -### Vídeo tutorial -- https://youtu.be/V8wGbZOAe38 +#### Por Dashboard +Acesse a dashboard da conta de projetos da Struct e crie um novo app. Em seguida, vá até a aba Deploy e selecione a opção GitHub. Selecione o repositório do projeto, escolha a branch `heroku` e clique em Deploy Branch. O deploy será feito automaticamente. ## Observação: -Boa parte dessa documentação foi produzida por Pedro Augusto Ramalho Duarte, um ex-membro da Struct. Atualmente não estamos mais usando o Heroku, e por isso o tutorial pode estar desatualizado. +É possível usar do Heroku para fazer deploy de front-end. Basta criar um único endpoint para enviar os assets estáticos do front. Exemplo: + + +```js +// /package.json + +{ + "scripts": { + "build": "um comando de build qualquer", // comando para gerar os assets estáticos + "start": "node server.js" // comando para rodar o servidor que servirá os assets + } +} + + +``` + +```js +// /server.js + +const express = require("express"); +//chamamos o express para o arquivo + +const { resolve } = require('path') +//para garantir que pegaremos o path exato + +const app = express(); +//criamos uma aplicação com o express + +app.use("/", + express.static( + resolve( + __dirname, + './build' // Onde os assets estáticos do front-end estão depois de rodar yarn build + ) + ) +); +//serve para pegarmos os itens de forma estática e servirmos o build + +app.listen(process.env.PORT || 3000, (err) => { + //usaremos a porta que o heroku definir ou a 300 + if (err) { + return console.log(err); + } + //caso tenha um erro vai retornar o erro na callback + console.log("Deu bom!"); + //no mais, esse console.log aparecerá na tela +}); +``` + +Agora temos uma api, e então o deploy pode ser feito no Heroku. From 49b01d3342f16a929c98056f5c05ed9a4a0b4b56 Mon Sep 17 00:00:00 2001 From: DavidsonGM <davidgmendes@hotmail.com> Date: Wed, 1 Feb 2023 01:02:29 -0300 Subject: [PATCH 14/30] Add missing gems for projects --- execucao/ruby-on-rails/gems.md | 71 ++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/execucao/ruby-on-rails/gems.md b/execucao/ruby-on-rails/gems.md index 68fdd8f..a42a845 100644 --- a/execucao/ruby-on-rails/gems.md +++ b/execucao/ruby-on-rails/gems.md @@ -12,6 +12,34 @@ Para facilitar o uso dessa seção pelo gerente de projeto \(ou qualquer outro m As *gems essenciais* são gems que devem ser configuradas em **todos** os projetos da Struct de *Ruby on Rails*, por proporcionarem funcionalidades que são necessárias ou para o desenvolvimento do projeto ou para a garantia básica da qualidade do projeto. Como gerente de projeto, é sua responsabilidade incluir e configurar **todas** as gems dessa seção no projeto. +### Rack Cors + +Para um projeto utilizando rails como uma REST API, provavelmente será necessário configurar o CORS (Cross-Origin Resourse Sharing) de sua aplicação para realizar a integração com o front-end, que é basicamente a configuração de quais domínios terão acesso a sua API. + +#### Configuração + +Para configurar os CORS no rails: + +1. Adicione a linha `gem 'rack-cors'` à sua Gemfile (normalmente essa linha já vem por padrão ao gerar um projeto rails, então basta descomentá-la) + +2. Navegue até o arquivo *config/initializers/cors.rb* e descomente o bloco de código: + ``` + Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins 'example.com' + + resource '*', + headers: :any, + methods: [:get, :post, :put, :patch, :delete, :options, :head] + end + end + ``` + +3. Altere a string em origins para o domínio no qual o seu frontend está rodando (Ex.: `origins 'http://localhost:3001'`) + +{% hint style="info" %} + Caso você queira disponibilizar a sua API para qualquer domínio, você pode colocar um `*` em origins. Mas tenha em mente que isso **não é nem um pouco recomendado**, uma vez que qualquer um terá acesso a sua API. Se por algum motivo você alguém precisou colocar essa configuração, não se esqueça de corrigi-la na hora de realizar o deploy. +{% endhint %} ### RSpec @@ -38,6 +66,34 @@ gem 'simple_token_authentication' 3. Execute o comando `rails g devise:install` para instalar todos os arquivos do devise. +### Active Model Serializers + +Os serializers do rails são módulos que definem o formato padrão de resposta para os objetos de nossa API. O uso de serializers não é exatamente necessário ao construir uma API em rails, mas é extremamente útil pois facilita bastante a vida do programador, removendo a necessidade de alterar manualmente em cada requisição quais os dados que precisam e quais não precisam ser enviados nas respostas. + +Na {Struct}, usamos a gem *active_model_serializers* para fazer essa configuração, e seu uso é bem simples: + +1. Adicione `gem 'active_model_serializers'` à Gemfile e execute o bundle; + +2. Gere um serializer para a sua model com `rails g serializer modelName`; + +3. Configure a sua serializer da forma que precisar, segue um exemplo: + ``` + class MemberSerializer < ActiveModel::Serializer + attributes :id, :name, :photo_url # Campos da model ou personalizados que aparecerão nas requests para esse recurso + + has_one :role # Associação com outra serializer existente + + # Método Customizado para pegar a url da imagem atrelada a esse recurso + def photo_url + Rails.application.routes.url_helpers.rails_blob_path(object.photo, only_path: true) if object.photo.attached? + end + end + ``` + +{% hint style="info" %} + Apesar de já termos padronizado o uso da gem Active Model serializer, fique à vontade para explorar outras gems, até porque, apesar de bem fácil de usar, a gem AMS está [longe de ser a mais performática](https://gist.github.com/tjwallace/f2fdadd656c74cdbfcc3218e6188d466#file-results.md), perdendo em performance para outras gems bastante utilizadas, tais como [panko](https://rubygems.org/gems/panko_serializer), [blueprinter](https://rubygems.org/gems/blueprinter), [fast json api](https://rubygems.org/gems/fast_jsonapi), entre outras. +{% endhint %} + ### Rubocop @@ -139,6 +195,21 @@ Para configurar o Figaro, siga os passos abaixo: Após configurar a gem Figaro em seu ambiente, **avise os outros membros do projeto** que eles deveram executar os passos 2, 3 e 5 para configurar a gem em seus ambientes! +### Active Storage + +Sempre que precisar trabalhar em alguma api que envolva o armazenamento de arquivos (images, documentos, audios, etc.), essa gem será sua maior aliada. + +#### Configuração + +A configuração inicial para essa gem é bem simples: + +1. Adicione-a à Gemfile: `gem "activestorage"` e dê o bundle; + +2. Execute o comando `rails active_storage:install` delegar para a gem praticamente toda a configuração inicial; + +3. Execute o comando `rails db:migrate` para rodar as migrations geradas pelo comando anterior; + +Para mais informações sobre como utilizar a gem, dê uma olhada no [drive da empresa](https://docs.google.com/document/d/1LfNYolHHFG0Q8C5wNrfXgJSWm1MpGV5U_hBIcOtqpB4/edit?usp=sharing). ## *Deprecated* gems From d7a0dd36294db37d45b724da45a17ea8c17c1875 Mon Sep 17 00:00:00 2001 From: DavidsonGM <davidgmendes@hotmail.com> Date: Wed, 1 Feb 2023 03:42:11 -0300 Subject: [PATCH 15/30] Add soft skills sction --- execucao/projetos/gerencia/README.md | 66 ++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/execucao/projetos/gerencia/README.md b/execucao/projetos/gerencia/README.md index 438024c..44d2ed7 100644 --- a/execucao/projetos/gerencia/README.md +++ b/execucao/projetos/gerencia/README.md @@ -10,6 +10,72 @@ O gerente de um projeto também tem o trabalho de participar das reuniões gerai Caso o gerente de um projeto não possa comparecer à reunião geral, ele deve instruir algum outro membro do projeto que estará na RG à realizar essa função. Caso isso não seja possível, o gerente deve enviar uma mensagem breve no *Slack* que detalhe esses fatores. + +## Soft Skills + +Ser gerente não é só sobre ter capacitade técnica para pode revisar o que está sendo entregue e passar os devidos feedbacks ou sobre saber mostrar aos membros como desenvolver determinada funcionalidade, aliás, essas coisas não são tão relevantes para definir se um gerente está apto a assumir o cargo. As principais competências que um gerente precisa dominar são mais relacionadas a soft skills. Sendo assim, nessa seção, serão apresentadas alguns pontos para os futuros gerentes levarem em consideração longo do desenvolvimento do projeto. + +### Sempre considere que irão ocorrer atrasos + +Muitas vezes o projeto já chega em você com um prazo bem definido e você se vê tendo que criar um cronograma para tentar entregar todas as funcionalidades dentro do prazo estipulado. Mas lembre-se: **os pilotos não são máquinas**. Sempre ao longo do desenvolvimento de um projeto, todos os membros irão atrasar issues em algum momento, principalmente dada a natureza de uma EJ, em que os pilotos também estão ocupados com tarefas da universidade. + +Sendo assim, o seu objetivo é tentar minimizar ao máximo esses atrasos e o impacto que eles trazem para o desenvolvimento do projeto como um todo. Para isso, tente sempre montar o cronograma o mais realista possível (ou até bem pessimista), considerando que praticamente toda semana haverão issues em atraso. Além disso, para casos de atrasos recorrentes, é muito importante deixar sempre claro para os desenvolvedores o impacto desses atrasos para o progresso do projeto e para a relação com o cliente. + +{% hint style="info" %} + Procure sempre saber o motivo dos atrasos e evite passar a sensação para o membro de que "Atrasou, mas não tem problema", pois isso pode gerar uma sensação para a equipe como um todo de que não tem problema atrasar as atividades. Tente ajudar o membro que atrasou a correr atrás do que falta antes da próxima reunião. +{% endhint %} + +### Organização + +- Mantenha o controle de tudo que falta e do que já está feito; +- Se uma issue for mergeada sem estar 100%, anote o qe ficou faltando ou crie uma nova issue; + +### Relação com o cliente + +#### Não deixe o cliente no escuro + +Esse tópico entra um pouco no tópico anterior. Em um cenário nada ideal, existe a possibilidade de você como gerente se deparar com uma situação bem chata: não será possível entregar o projeto dentro do prazo. + +Mas o que devemos fazer agora? Tentamos correr atrás e fazer um milhão de coding days seguidos para tentar colocar o projeto de volta nos trilhos? Adicionamos mais membros ao projeto? Paramos a empresa inteira para fazer um mutirão para entregarmos o projeto? Bem, já adianto que, na maioria dos casos, essas possibilidades não irão funcionar, inclusive podem ter o efeito reverso e acabar atrasando o projeto ainda mais, visto que vão acabar desgastando os desenvolvedores. + +Sendo assim, antes de sair procurando soluções para tentar cumprir com um prazo irreal, procure sempre entrar em contato com o cliente, explicando desde cedo os problemas que vocês estão enfrentando e já prepará-lo para um possível atraso, podendo até chegar a um acordo para realizar uma entrega inicial parcial, mesmo com algumas funcionalidades faltando. + +{% hint style="warning" %} + Ao falar sobre atrasos com o cliente, tente focar mais nas questões técnicas que causaram o atraso e evite dizer coisas como "está atrasando pois estamos em final de semestre na universidade", dando a impressão que houve uma falta de planejamento. +{% endhint %} + +#### Reuniões + +Procure sempre levar outro membro do projeto para as reuniões com o cliente, pois é sempre bom ter um ouvinte na reunião para garantir que nada se perca. Também é bom manter o controler de tudo que foi discutido nas reuniões para que o cliente não possa cobrar coisas que não pediu. + +Para mudanças solicitadas pelo cliente (principalmente novas funcionalidades), evite de dar uma resposta na mesma hora ou ao final da reunião. Avisa=a que irá pensar à respeito mesmo que tenha praticamente certeza de que a mudança será implementada. + +### Revisão de PRs + +Essa é uma questão delicada, muitas vezes você vai precisar usar um pouco do bom senso para balancear o aprendizado do membro com a entrega das issues no prazo. + +Ao revisar um PR e perceber que algumas mudanças devem ser feitas antes do merge, na maioria das vezes é recomendado que você solicite ao próprio membro que realize as mudanças, procurando sempre deixar bem explicado para o membro o porquê daquela mudança ser necessária (Não sendo algo óbvio, como um bug) e, caso você tenha conhecimento, dar um caminho para o membro de como realizar essa mudança. +Dessa forma, quando o membro passar por uma situação semelhante no futuro, ele mesmo irá resolver sem nem precisar de sua review, o que não aconteceria caso você tivesse simplesmente realizado a mudança e mergeado o PR. + +{% hint style="warning" %} + Apesar de pedir para o próprio membro realizar as mudanças ser o ideal, é bom sempre ter em mente que isso pode atrasar um pouco o desenvolvimento do projeto. +{% endhint %} + +{% hint style="warning" %} + Também não esqueça de levar em consideração a sua própria disponibilidade, evitando tentar resolver tudo sozinho e acabar se sobrecarregando. +{% endhint %} + +#### Final de projeto + +Mais para o final do projeto, as coisas tendem a ficar meio caóticas com a correria para entregar o projeto dentro do prazo. Se for o caso, apesar de não ser muito recomendado, não tenha medo de colocar a mão na massa você mesmo. Para pequenas alterações (como feedbacks do cliente, ou detalhes que faltaram em PRs), muitas vezes vale mais você fazer direto do que passar para algum membro fazer, poupando bastante tempo no processo. + +### Conheça seus membros + +Conhecer os membros com os quais você está trabalhando no dia a dia é imprenscindível para uma boa gerência de projetos. Procure sempre ter saber como está a semana de cada membro, quando eles pretendem fazer suas issues, quais são suas dificuldades, facilidades e preferências. Com isso, você terá uma boa noção de quando oferecer ajuda e quais atividades passar para cada um. +Não ache que você está sendo chato ao mandar uma mensagem para o membro, no dia seguinte ao que ele falou que estaria trabalhando, perguntando como está o andamento. Muitas vezes ele pode estar com algum problema que só seria descoberto no dia da reunião. + +Crie um ambiente agradável para o time, incentivando o pessoal a mandar dúvidas no canal e tirar dúvidas dos demais. + ## Ambiente de produção e staging O ambiente de produção é o espaço onde colocaremos o código da branch *production* para que ele possa ser acessado diretamente pelo cliente ou pelos usuários. Como gerente de projeto, é seu trabalho colocar o código da branch *production* no ambiente de produção ao final do projeto \(ou antes da data de entrega do projeto\). Para esse fim utilizaremos o [Docker](./docker/README.md), uma ferramenta que fornece um ambiente de virtualização para colocarmos o projeto. From 098b703fbbb2ce150633c87877da29e1dacd5f4a Mon Sep 17 00:00:00 2001 From: DavidsonGM <davidgmendes@hotmail.com> Date: Wed, 1 Feb 2023 17:04:41 -0300 Subject: [PATCH 16/30] Add small paragraph about PO --- execucao/projetos/gerencia/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/execucao/projetos/gerencia/README.md b/execucao/projetos/gerencia/README.md index 44d2ed7..0a91b42 100644 --- a/execucao/projetos/gerencia/README.md +++ b/execucao/projetos/gerencia/README.md @@ -76,6 +76,10 @@ Não ache que você está sendo chato ao mandar uma mensagem para o membro, no d Crie um ambiente agradável para o time, incentivando o pessoal a mandar dúvidas no canal e tirar dúvidas dos demais. +## Product Owner + +O Product Owner é um papel do SCRUM fundamental para garantir uma boa comunicação com o cliente. Diferentemente do gerente do projeto, ele não terá responsabilidades técnicas, como revisão de código ou colocar a mão na massa durante o projeto. Sua principal função é ser a ponte entre os membros do projeto e o cliente, buscando sempre deixar claro para os desenvolvedores o quê o cliente quer e/ou precisa e também comunicar ao cliente as dificuldades que estão sendo enfrentadas e as sugestões trazidas pela equipe, buscando sempre ser o mais transparente possível com o cliente. + ## Ambiente de produção e staging O ambiente de produção é o espaço onde colocaremos o código da branch *production* para que ele possa ser acessado diretamente pelo cliente ou pelos usuários. Como gerente de projeto, é seu trabalho colocar o código da branch *production* no ambiente de produção ao final do projeto \(ou antes da data de entrega do projeto\). Para esse fim utilizaremos o [Docker](./docker/README.md), uma ferramenta que fornece um ambiente de virtualização para colocarmos o projeto. From 2be5aec3f701fbe603d0225165e7cf4b374062d7 Mon Sep 17 00:00:00 2001 From: DavidsonGM <davidgmendes@hotmail.com> Date: Fri, 10 Feb 2023 00:20:46 -0300 Subject: [PATCH 17/30] :construction: inicio da pagina do guia de gerencia --- execucao/projetos/gerencia/guia.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 execucao/projetos/gerencia/guia.md diff --git a/execucao/projetos/gerencia/guia.md b/execucao/projetos/gerencia/guia.md new file mode 100644 index 0000000..9b91a9d --- /dev/null +++ b/execucao/projetos/gerencia/guia.md @@ -0,0 +1,12 @@ +# Guia da gerência + +Para auxiliar a vida do gerente, essa página irá listar uma série de protocolos e processos a serem seguidos visando um bom andamento dos projetos. + + +## Montagem de cronograma + +- Procure montar um cronograma realista, já considerando os possíveis atrasos; +- Não se esqueça de considerar o calendário acadêmico, para poder antever as semanas que normalmente tem baixa produtividade (como semanas finais de semestre ou feriados prolongados); +- Caso esteja na dúvida sobre o período necessário para implementar alguma funcionalidade, consulte algum membro mais experiente ou até mesmo pilotos aposentados; +- Se possível, tente criar um relatório bem detalhado sobre o tempo que durou cada tarefa para futuras referências. Essas informações podem ser muito úteis no futuro para melhorar a precificação e os prazos dos projetos. Leve em conta tanto o tempo estimado quanto o real para cada issue e se possível especifique o máximo possível (Ex.: a issue demorou x horas, ao invés de a issue demorou y sprints). + From 3184f193cc80aad76af1e4bec55881ea7d92ef64 Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Sat, 18 Mar 2023 16:33:09 -0300 Subject: [PATCH 18/30] fixed typos --- execucao/projetos/gerencia/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/execucao/projetos/gerencia/README.md b/execucao/projetos/gerencia/README.md index 0a91b42..d824395 100644 --- a/execucao/projetos/gerencia/README.md +++ b/execucao/projetos/gerencia/README.md @@ -28,7 +28,7 @@ Sendo assim, o seu objetivo é tentar minimizar ao máximo esses atrasos e o imp ### Organização - Mantenha o controle de tudo que falta e do que já está feito; -- Se uma issue for mergeada sem estar 100%, anote o qe ficou faltando ou crie uma nova issue; +- Se uma issue for mergeada sem estar 100%, anote o que ficou faltando ou crie uma nova issue; ### Relação com o cliente @@ -48,7 +48,7 @@ Sendo assim, antes de sair procurando soluções para tentar cumprir com um praz Procure sempre levar outro membro do projeto para as reuniões com o cliente, pois é sempre bom ter um ouvinte na reunião para garantir que nada se perca. Também é bom manter o controler de tudo que foi discutido nas reuniões para que o cliente não possa cobrar coisas que não pediu. -Para mudanças solicitadas pelo cliente (principalmente novas funcionalidades), evite de dar uma resposta na mesma hora ou ao final da reunião. Avisa=a que irá pensar à respeito mesmo que tenha praticamente certeza de que a mudança será implementada. +Para mudanças solicitadas pelo cliente (principalmente novas funcionalidades), evite de dar uma resposta na mesma hora ou ao final da reunião. Avise que irá pensar à respeito mesmo que tenha praticamente certeza de que a mudança será implementada. ### Revisão de PRs @@ -78,12 +78,12 @@ Crie um ambiente agradável para o time, incentivando o pessoal a mandar dúvida ## Product Owner -O Product Owner é um papel do SCRUM fundamental para garantir uma boa comunicação com o cliente. Diferentemente do gerente do projeto, ele não terá responsabilidades técnicas, como revisão de código ou colocar a mão na massa durante o projeto. Sua principal função é ser a ponte entre os membros do projeto e o cliente, buscando sempre deixar claro para os desenvolvedores o quê o cliente quer e/ou precisa e também comunicar ao cliente as dificuldades que estão sendo enfrentadas e as sugestões trazidas pela equipe, buscando sempre ser o mais transparente possível com o cliente. +O Product Owner é um papel do SCRUM fundamental para garantir uma boa comunicação com o cliente. Diferentemente do gerente do projeto, ele não terá responsabilidades técnicas, como revisão de código ou colocar a mão na massa durante o projeto. Sua principal função é ser a ponte entre os membros do projeto e o cliente, buscando sempre deixar claro para os desenvolvedores o quê o cliente quer, também deve comunicar ao cliente as dificuldades que estão sendo enfrentadas e as sugestões trazidas pela equipe, buscando sempre ser o mais transparente possível com o cliente. ## Ambiente de produção e staging -O ambiente de produção é o espaço onde colocaremos o código da branch *production* para que ele possa ser acessado diretamente pelo cliente ou pelos usuários. Como gerente de projeto, é seu trabalho colocar o código da branch *production* no ambiente de produção ao final do projeto \(ou antes da data de entrega do projeto\). Para esse fim utilizaremos o [Docker](./docker/README.md), uma ferramenta que fornece um ambiente de virtualização para colocarmos o projeto. +O ambiente de produção é o espaço onde colocaremos o código da branch *production* para que ele possa ser acessado diretamente pelo cliente ou pelos usuários. Como gerente de projeto, é seu trabalho colocar o código da branch *production* no ambiente de produção ao final do projeto \(ou antes da data de entrega do projeto\). Para esse fim utilizaremos o [Docker](../docker/README.md), uma ferramenta que fornece um ambiente de virtualização para colocarmos o projeto. -Devido à complexidade do processo de colocar o código em produção, achamos melhor detalhar isso em uma [página específica](./docker/colocando-um-projeto-em-producao.md) do Gitbook. Lembre-se, também, que qualquer mudança requisitada pelo cliente só será perceptível **se o código estiver em produção**, de forma que **você deverá realizar esse processo regularmente** \(se esquecer de colocar novas features ou correções de features existentes em produção é uma excelente forma de **irritar** o cliente\). +Devido à complexidade do processo de colocar o código em produção, achamos melhor detalhar isso em uma [página específica](../docker/colocando-um-projeto-em-producao.md) do Gitbook. Lembre-se, também, que qualquer mudança requisitada pelo cliente só será perceptível **se o código estiver em produção**, de forma que **você deverá realizar esse processo regularmente** \(se esquecer de colocar novas features ou correções de features existentes em produção é uma excelente forma de **irritar** o cliente\). O ambiente de staging é similar ao ambiente de produção, no entanto ele é considerado um *deploy temporário*, uma vez que não é uma versão apropriada para o uso dos usuários, é utilizado mais para testes e para mostrar as mudanças feitas para o cliente. Por ser uma versão mais para testes e não precisar de tantos recursos, geralmente não fazemos o deploy em staging em nosso servidor, mas sim utilizamos alguma ferramenta externa para tal finalidade, como o [heroku](../heroku.md) ou o [railway](https://railway.app/) para fazer o deploy da api do rails e o [netlify](https://www.netlify.com/) para fazer o deploy do front em react. From 8cbaac940f553228e0c12391b079f57dbb8fe6f2 Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Sat, 18 Mar 2023 17:23:52 -0300 Subject: [PATCH 19/30] typo --- execucao/projetos/gerencia/sprints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/execucao/projetos/gerencia/sprints.md b/execucao/projetos/gerencia/sprints.md index 78b4451..10262d1 100644 --- a/execucao/projetos/gerencia/sprints.md +++ b/execucao/projetos/gerencia/sprints.md @@ -20,7 +20,7 @@ No início de uma *sprint*, o gerente de projeto escolhe quais features serão d <!-- Após as features serem escolhidas, elas devem ser pontuadas pela equipe de desenvolvimento. A forma de pontuar uma feature é bem simples: o gerente se reúne com a equipe de desenvolvimento, descreve o trabalho envolvido nessa feature e pede que a equipe de desenvolvimento indique um número entre os 6 primeiros números da sequência de Fibonacci \(1, 2, 3, 5, 8, 13\) que descreva quão trabalhosa a feature seria para ser desenvolvida \(incluindo testes e comentários\). Caso os desenvolvedores concordem \(em linhas gerais\) na pontuação, ela é atribuída à feature; do contrário, os desenvolvedores conversam entre si para se chegar a um consenso. Caso a feature tenha uma pontuação maior ou igual a 5, ela é denominada um *épico*, e deve ser dividida em sub-features menores. É importante frisar que **correções de bugs não valem pontos**. --> -Após as features serem colhidas, elas devem ser discutidas com os membros da equipe de desenvolvimento e, em seguida, distribuídas entre eles. É importante garantir que a distribuição seja bem feita e leve em consideração a grade horária semanal dos desenvolvedores para evitar membros ociosos ou membros atarefados excessivamente. Também é ideal que desenvolvedores novatos sejam auxiliados no desenvolvimento de features mais complexas, por meio de recursos como *pair programming* ou maior atenção do gerente de projetos. Por fim, lembre-se de atribuir as *issues* no repositório aos seus respectivos responsáveis para que qualquer um que visite o repositório do projeto possa saber quem está trabalhando no que. +Após as features serem escolhidas, elas devem ser discutidas com os membros da equipe de desenvolvimento e, em seguida, distribuídas entre eles. É importante garantir que a distribuição seja bem feita e leve em consideração a grade horária semanal dos desenvolvedores para evitar membros ociosos ou membros atarefados excessivamente. Também é ideal que desenvolvedores novatos sejam auxiliados no desenvolvimento de features mais complexas, por meio de recursos como *pair programming* ou maior atenção do gerente de projetos. Por fim, lembre-se de atribuir as *issues* no repositório aos seus respectivos responsáveis para que qualquer um que visite o repositório do projeto possa saber quem está trabalhando no que. Assim que as features forem escolhidas, discutidas e distribuídas, é hora de começar o desenvolvimento. From a040567bd4d0ca88905a19233f4cbefa31c9d5ee Mon Sep 17 00:00:00 2001 From: DavidsonGM <davidgmendes@hotmail.com> Date: Mon, 20 Mar 2023 18:17:01 -0300 Subject: [PATCH 20/30] requested changes --- SUMMARY.md | 1 + execucao/projetos/gerencia/README.md | 51 --------- execucao/projetos/gerencia/boas-praticas.md | 65 +++++++++++ execucao/projetos/gerencia/guia.md | 9 -- .../projetos/gerencia/inicio-de-projeto.md | 102 +++-------------- execucao/projetos/gerencia/repositorio.md | 103 +++++++++++++++++- execucao/ruby-on-rails/gems.md | 18 ++- 7 files changed, 188 insertions(+), 161 deletions(-) create mode 100644 execucao/projetos/gerencia/boas-praticas.md diff --git a/SUMMARY.md b/SUMMARY.md index 50a6f03..1ec73a8 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -61,6 +61,7 @@ * [Início de projeto](execucao/projetos/gerencia/inicio-de-projeto.md) * [Repositório](execucao/projetos/gerencia/repositorio.md) * [Sprints](execucao/projetos/gerencia/sprints.md) + * [Boas práticas](execucao/projetos/gerencia/boas-praticas.md) * [Finalização de Projetos](execucao/projetos/finalizacao.md) * [Gitlab](execucao/projetos/gitlab/README.md) * [CI-CD](execucao/projetos/gitlab/ci-cd.md) diff --git a/execucao/projetos/gerencia/README.md b/execucao/projetos/gerencia/README.md index 0a91b42..9a91017 100644 --- a/execucao/projetos/gerencia/README.md +++ b/execucao/projetos/gerencia/README.md @@ -25,57 +25,6 @@ Sendo assim, o seu objetivo é tentar minimizar ao máximo esses atrasos e o imp Procure sempre saber o motivo dos atrasos e evite passar a sensação para o membro de que "Atrasou, mas não tem problema", pois isso pode gerar uma sensação para a equipe como um todo de que não tem problema atrasar as atividades. Tente ajudar o membro que atrasou a correr atrás do que falta antes da próxima reunião. {% endhint %} -### Organização - -- Mantenha o controle de tudo que falta e do que já está feito; -- Se uma issue for mergeada sem estar 100%, anote o qe ficou faltando ou crie uma nova issue; - -### Relação com o cliente - -#### Não deixe o cliente no escuro - -Esse tópico entra um pouco no tópico anterior. Em um cenário nada ideal, existe a possibilidade de você como gerente se deparar com uma situação bem chata: não será possível entregar o projeto dentro do prazo. - -Mas o que devemos fazer agora? Tentamos correr atrás e fazer um milhão de coding days seguidos para tentar colocar o projeto de volta nos trilhos? Adicionamos mais membros ao projeto? Paramos a empresa inteira para fazer um mutirão para entregarmos o projeto? Bem, já adianto que, na maioria dos casos, essas possibilidades não irão funcionar, inclusive podem ter o efeito reverso e acabar atrasando o projeto ainda mais, visto que vão acabar desgastando os desenvolvedores. - -Sendo assim, antes de sair procurando soluções para tentar cumprir com um prazo irreal, procure sempre entrar em contato com o cliente, explicando desde cedo os problemas que vocês estão enfrentando e já prepará-lo para um possível atraso, podendo até chegar a um acordo para realizar uma entrega inicial parcial, mesmo com algumas funcionalidades faltando. - -{% hint style="warning" %} - Ao falar sobre atrasos com o cliente, tente focar mais nas questões técnicas que causaram o atraso e evite dizer coisas como "está atrasando pois estamos em final de semestre na universidade", dando a impressão que houve uma falta de planejamento. -{% endhint %} - -#### Reuniões - -Procure sempre levar outro membro do projeto para as reuniões com o cliente, pois é sempre bom ter um ouvinte na reunião para garantir que nada se perca. Também é bom manter o controler de tudo que foi discutido nas reuniões para que o cliente não possa cobrar coisas que não pediu. - -Para mudanças solicitadas pelo cliente (principalmente novas funcionalidades), evite de dar uma resposta na mesma hora ou ao final da reunião. Avisa=a que irá pensar à respeito mesmo que tenha praticamente certeza de que a mudança será implementada. - -### Revisão de PRs - -Essa é uma questão delicada, muitas vezes você vai precisar usar um pouco do bom senso para balancear o aprendizado do membro com a entrega das issues no prazo. - -Ao revisar um PR e perceber que algumas mudanças devem ser feitas antes do merge, na maioria das vezes é recomendado que você solicite ao próprio membro que realize as mudanças, procurando sempre deixar bem explicado para o membro o porquê daquela mudança ser necessária (Não sendo algo óbvio, como um bug) e, caso você tenha conhecimento, dar um caminho para o membro de como realizar essa mudança. -Dessa forma, quando o membro passar por uma situação semelhante no futuro, ele mesmo irá resolver sem nem precisar de sua review, o que não aconteceria caso você tivesse simplesmente realizado a mudança e mergeado o PR. - -{% hint style="warning" %} - Apesar de pedir para o próprio membro realizar as mudanças ser o ideal, é bom sempre ter em mente que isso pode atrasar um pouco o desenvolvimento do projeto. -{% endhint %} - -{% hint style="warning" %} - Também não esqueça de levar em consideração a sua própria disponibilidade, evitando tentar resolver tudo sozinho e acabar se sobrecarregando. -{% endhint %} - -#### Final de projeto - -Mais para o final do projeto, as coisas tendem a ficar meio caóticas com a correria para entregar o projeto dentro do prazo. Se for o caso, apesar de não ser muito recomendado, não tenha medo de colocar a mão na massa você mesmo. Para pequenas alterações (como feedbacks do cliente, ou detalhes que faltaram em PRs), muitas vezes vale mais você fazer direto do que passar para algum membro fazer, poupando bastante tempo no processo. - -### Conheça seus membros - -Conhecer os membros com os quais você está trabalhando no dia a dia é imprenscindível para uma boa gerência de projetos. Procure sempre ter saber como está a semana de cada membro, quando eles pretendem fazer suas issues, quais são suas dificuldades, facilidades e preferências. Com isso, você terá uma boa noção de quando oferecer ajuda e quais atividades passar para cada um. -Não ache que você está sendo chato ao mandar uma mensagem para o membro, no dia seguinte ao que ele falou que estaria trabalhando, perguntando como está o andamento. Muitas vezes ele pode estar com algum problema que só seria descoberto no dia da reunião. - -Crie um ambiente agradável para o time, incentivando o pessoal a mandar dúvidas no canal e tirar dúvidas dos demais. - ## Product Owner O Product Owner é um papel do SCRUM fundamental para garantir uma boa comunicação com o cliente. Diferentemente do gerente do projeto, ele não terá responsabilidades técnicas, como revisão de código ou colocar a mão na massa durante o projeto. Sua principal função é ser a ponte entre os membros do projeto e o cliente, buscando sempre deixar claro para os desenvolvedores o quê o cliente quer e/ou precisa e também comunicar ao cliente as dificuldades que estão sendo enfrentadas e as sugestões trazidas pela equipe, buscando sempre ser o mais transparente possível com o cliente. diff --git a/execucao/projetos/gerencia/boas-praticas.md b/execucao/projetos/gerencia/boas-praticas.md new file mode 100644 index 0000000..7a5dae2 --- /dev/null +++ b/execucao/projetos/gerencia/boas-praticas.md @@ -0,0 +1,65 @@ +# Boas práticas durante o projeto + +## Organização + +- Mantenha o controle de tudo que falta e do que já está feito; +- Se uma issue for mergeada sem estar 100%, anote o qe ficou faltando ou crie uma nova issue; + +## Relação com + +### Cliente + +#### Não deixe o cliente no escuro + +Esse tópico entra um pouco no tópico anterior. Em um cenário nada ideal, existe a possibilidade de você como gerente se deparar com uma situação bem chata: não será possível entregar o projeto dentro do prazo. + +Mas o que devemos fazer agora? Tentamos correr atrás e fazer um milhão de coding days seguidos para tentar colocar o projeto de volta nos trilhos? Adicionamos mais membros ao projeto? Paramos a empresa inteira para fazer um mutirão para entregarmos o projeto? Bem, já adianto que, na maioria dos casos, essas possibilidades não irão funcionar, inclusive podem ter o efeito reverso e acabar atrasando o projeto ainda mais, visto que vão acabar desgastando os desenvolvedores. + +Sendo assim, antes de sair procurando soluções para tentar cumprir com um prazo irreal, procure sempre entrar em contato com o cliente, explicando desde cedo os problemas que vocês estão enfrentando e já prepará-lo para um possível atraso, podendo até chegar a um acordo para realizar uma entrega inicial parcial, mesmo com algumas funcionalidades faltando. + +{% hint style="info" %} + Uma possível alternativa para acelerar o desenvolvimento do projeto é introduzir os coding moments na rotina da equipe, que são basicamente momentos em que os próprios membros do projeto se reúnem para codar durante a semana. Isto pode contribuir bastante para a integração dos membros e para tornar o time mais produtivo no geral +{% endhint %} + +{% hint style="warning" %} + Ao falar sobre atrasos com o cliente, tente focar mais nas questões técnicas que causaram o atraso e evite dizer coisas como "está atrasando pois estamos em final de semestre na universidade", dando a impressão que houve uma falta de planejamento. +{% endhint %} + + +#### Reuniões + +Procure sempre levar outro membro do projeto para as reuniões com o cliente, pois é sempre bom ter um ouvinte na reunião para garantir que nada se perca. Também é bom manter o controler de tudo que foi discutido nas reuniões para que o cliente não possa cobrar coisas que não pediu. + +Para mudanças solicitadas pelo cliente (principalmente novas funcionalidades), evite de dar uma resposta na mesma hora ou ao final da reunião. Avisa=a que irá pensar à respeito mesmo que tenha praticamente certeza de que a mudança será implementada. + +### Membros + +Conhecer os membros com os quais você está trabalhando no dia a dia é imprenscindível para uma boa gerência de projetos. Procure sempre ter saber como está a semana de cada membro, quando eles pretendem fazer suas issues, quais são suas dificuldades, facilidades e preferências. Com isso, você terá uma boa noção de quando oferecer ajuda e quais atividades passar para cada um. +Não ache que você está sendo chato ao mandar uma mensagem para o membro, no dia seguinte ao que ele falou que estaria trabalhando, perguntando como está o andamento. Muitas vezes ele pode estar com algum problema que só seria descoberto no dia da reunião. + +É importante também que os desenvolvedores estejam em comunicação constante entre si, e é isso que a *daily* garante. Eles devem entender o projeto e se sentirem incluídos na tomada de decisão, nem que seja somente porque foi explicada a motivação por trás das decisões. + +Crie um ambiente agradável para o time, incentivando o pessoal a mandar dúvidas no canal e tirar dúvidas dos demais. + +### Diretoria de projetos + +Comunique sempre o andamento do projeto à diretoria de projetos. Caso precise de ajuda com alguma coisa, seja numa decisão ou numa ação, também não hesite em perguntar. + +## Revisão de PRs + +Essa é uma questão delicada, muitas vezes você vai precisar usar um pouco do bom senso para balancear o aprendizado do membro com a entrega das issues no prazo. + +Ao revisar um PR e perceber que algumas mudanças devem ser feitas antes do merge, na maioria das vezes é recomendado que você solicite ao próprio membro que realize as mudanças, procurando sempre deixar bem explicado para o membro o porquê daquela mudança ser necessária (Não sendo algo óbvio, como um bug) e, caso você tenha conhecimento, dar um caminho para o membro de como realizar essa mudança. +Dessa forma, quando o membro passar por uma situação semelhante no futuro, ele mesmo irá resolver sem nem precisar de sua review, o que não aconteceria caso você tivesse simplesmente realizado a mudança e mergeado o PR. + +{% hint style="warning" %} + Apesar de pedir para o próprio membro realizar as mudanças ser o ideal, é bom sempre ter em mente que isso pode atrasar um pouco o desenvolvimento do projeto. +{% endhint %} + +{% hint style="warning" %} + Também não esqueça de levar em consideração a sua própria disponibilidade, evitando tentar resolver tudo sozinho e acabar se sobrecarregando. +{% endhint %} + +### Final de projeto + +Mais para o final do projeto, as coisas tendem a ficar meio caóticas com a correria para entregar o projeto dentro do prazo. Se for o caso, apesar de não ser muito recomendado, não tenha medo de colocar a mão na massa você mesmo. Para pequenas alterações (como feedbacks do cliente, ou detalhes que faltaram em PRs), muitas vezes vale mais você fazer direto do que passar para algum membro fazer, poupando bastante tempo no processo. diff --git a/execucao/projetos/gerencia/guia.md b/execucao/projetos/gerencia/guia.md index 9b91a9d..1d4886a 100644 --- a/execucao/projetos/gerencia/guia.md +++ b/execucao/projetos/gerencia/guia.md @@ -1,12 +1,3 @@ # Guia da gerência Para auxiliar a vida do gerente, essa página irá listar uma série de protocolos e processos a serem seguidos visando um bom andamento dos projetos. - - -## Montagem de cronograma - -- Procure montar um cronograma realista, já considerando os possíveis atrasos; -- Não se esqueça de considerar o calendário acadêmico, para poder antever as semanas que normalmente tem baixa produtividade (como semanas finais de semestre ou feriados prolongados); -- Caso esteja na dúvida sobre o período necessário para implementar alguma funcionalidade, consulte algum membro mais experiente ou até mesmo pilotos aposentados; -- Se possível, tente criar um relatório bem detalhado sobre o tempo que durou cada tarefa para futuras referências. Essas informações podem ser muito úteis no futuro para melhorar a precificação e os prazos dos projetos. Leve em conta tanto o tempo estimado quanto o real para cada issue e se possível especifique o máximo possível (Ex.: a issue demorou x horas, ao invés de a issue demorou y sprints). - diff --git a/execucao/projetos/gerencia/inicio-de-projeto.md b/execucao/projetos/gerencia/inicio-de-projeto.md index 86d5012..09fa233 100644 --- a/execucao/projetos/gerencia/inicio-de-projeto.md +++ b/execucao/projetos/gerencia/inicio-de-projeto.md @@ -1,100 +1,24 @@ # Início do projeto -O primeiro passo em um projeto de software consiste na criação e configuração do repositório em que o projeto será desenvolvido. Isso inclui criar o repositório do projeto, as estruturas de pastas para abrigar o código fonte e outros recursos e configurar adequadamente os arquivos de controle de versão, os arquivos de documentação do projeto, a utilização de bibliotecas externas \(gems\), e o banco de dados do projeto. +Os primeiros passos em um projeto de software consistem na decisão da equipe de desenvolvimento, escolha das tecnologias a serem utilizadas, montagem do cronograma e [configuração dos repositórios](./repositorio.md#configuracao) nos quais o projeto será desenvolvido. -Embora isso pareça ser muita coisa, iremos detalhar melhor cada passo nas seções abaixo. +## Decisão da equipe de desenvolvimento -## Projeto Rails +Normalmente essa etapa é feita pela diretoria de projetos, em que deve ser verificado quais membros estão disponíveis e que fazem mais sentido de incluir no projeto. Procure sempre ter um membro mais experiente na equipe de desenvolvimento, uma vez que isso contribui demais tanto para o progresso do projeto, quanto para o aprendizado dos demais. -O primeiro passo na inicialização do projeto é a criação do projeto na framework [*Ruby on Rails*](../ruby-on-rails/README.md). Para isso, vamos escolher, utilizando o RVM ou Rbenv, uma versão de Ruby esteja um *minor release* atrás da última versão \(ou seja, se a última versão de Ruby é a 2.x, utilize a versão 2.x-1 para o projeto\). Na dúvida, utilize a versão de Ruby utilizada no último projeto. Isso evita que tenhamos que lidar com os bugs inerentes à versão mais nova do Ruby e das gems que o acompanham. Caso algum dos membros do projeto não tenha um gerenciador de versão de Ruby instalado \(como RVM e Rbenv\), ajude ele com a instalação \(esse tipo de software é praticamente essecial para se trabalhar em vários projetos, cada um com uma versão diferente de Ruby\). +## Escolha das tecnologias -Após escolher a versão de Ruby desejada, será necessário realizar a instalação de algumas gems \(que são as bibliotecas do Ruby\). Para instalar uma gem, basta adicionar a gem ao arquivo Gemfile e rodar o comando `bundle install`. Com isso em mente, instale o *rails* \(framework de desenvolvimento de websites e serviços web\) e para o banco de dados, escolha entre o *mysql2* e o *pg*, gems para o MySQL e PostgreSQL respectivamente, os sistemas gerenciadores de bancos de dados, ou SGBDs, mais utilizados\). Além dessas gems, será necessário instalar os SGBDs MySQL e PostgreSQL em seu sistema operacional \(assunto que **não** será coberto nessa página\). +Na grande maioria das vezes, iremos fazer os projetos em nossa stack padrão (rails API + React JS), no entanto, é sempre bom discutir um pouco com o pessoal da diretoria de projetos para verificar se para o projeto em questão faz realmente sentido fazermos em nossa stack padrão ou se deveríamos utilizar alguma outra. -Após instalar as gems e os SGBDs em seu computador, caberá a você, como gerente de projetos, decidir qual SGBD utilizar no projeto. O MySQL é mais amigável para iniciantes e possui uma melhor forma de visualizar e manipular diretamente o banco de dados, mas requer mais tempo para ser configurado e a utilização de uma gem \([*figaro*](../../ruby-on-rails/gems.md#figaro)\) para gerenciar os credenciais de login. Já o PostgreSQL pode ser utilizado com quase nenhuma configuração adicional, mas possui uma instalação mais complicada e não permite que o usuário manipule o banco de dados com facilidade. Caso seja sua primeira vez gerenciando um projeto, fale com o diretor de projetos ou com um membro mais experiente para pedir uma recomendação \(eu, pessoalmente, recomendaria o PostgreSQL\). +Além disso, dentro da stack escolhida, também existem diversas tecnologias diferentes para escolher que realizam uma mesma funcionalidade. Por exemplo, para implementarmos uma funcionalidade de paginação na api do rails, podemos tanto usar uma gem externa (pagy, will_paginate), quanto fazermos um método customizado na mão, ficando a cargo da equipe de desenvolvimento decidir qual o melhor caminho a se seguir. -{% hint style="info"%} -Para aprender um pouco mais sobre instalação do ruby e do rails e como configurar o banco de dados, confira os nossos [guias](../../../gestao/processo-seletivo/guias-utilizados.md). +{% hint style="info" %} + Dependendo da complexidade da funcionalidade, é interessante tomar essa decisão em conjunto com a diretoria de projetos, principalmente se os membros do projeto não tiverem muito confiantes de implementar essa funcionalidade. {% endhint %} -Após escolher o SGBD a ser utilizado, chegou a hora de criar o projeto. Para isso, rode o comando `rails new nome_do_projeto_em_snake_case -d {mysql | postgresql} --api`, escolhendo o penúltimo argumento para coincidir com o SGBD desejado. O framework cuidará da criação de pastas e arquivos para o desenvolvimento do projeto. +## Montagem de cronograma -Caso você tenha escolhido o MySQL, configure a gem [*figaro*](../../ruby-on-rails/gems.md#figaro) para gerenciar credenciais e, caso você **não** queira utilizar o usuário *root* no projeto, crie as tabelas de desenvolvimento e testes e um usuário com permissão de acesso a elas. Caso você tenha escolhido o PostgreSQL, preencha o arquivo `config/database.yml` com os credenciais do SGBD \(em sistemas operacionais com o *kernel* Linux, você pode preencher apenas o seu nome de usuário e apagar o campo de senha caso você tenha optado, na instalação do PostgreSQL, por utilizar sua conta de usuário do sistema operacional como um usuário do SGBD\). - -Por fim, tente executar o projeto utilizando o comando `rails s`. Caso a execução do projeto não gere alguma mensagem de erro, adicione todos os arquivos gerados ao controle de versão sob o nome 'Commit inicial'. Caso contrário, corrija os erros do projeto **antes** de realizar o commit inicial. - - -## Segundo commit - -Após a criação do repositório, você deverá fazer um segundo commit na branch main que faça todas as configurações necessárias no projeto. Esse segundo commit deve colocar no README informações básicas do projeto, colocar no projeto arquivos padrão que devem estar presentes em qualquer projeto da Struct e configurar as gems que o projeto irá utilizar (confira as [gem essenciais](../../ruby-on-rails/gems.md#gems-essenciais) para um projeto). Mais detalhes são fornecidos nas subseções abaixo. Após executar **todos** esses passos, realize um commit com o nome "Estrutura inicial" adicionando as mudanças que você fez. - -### README - -O README deve conter as dependências do projeto \(versão do Ruby, banco de dados, etc...\) e os dados do projeto em si, os quais incluem a especificação do projeto \(bem resumida, em uma única frase\), o codinome do projeto \(se existir\), a data de assinatura do contrato, o prazo de desenvolvimento do projeto e a data prevista de entrega do projeto. - -Não utilize o README como uma lista de tarefas, pois isso pode ser feito por meio de *issues*. - -### Arquivos padrão - -No momento, existem 2 arquivos padrão que devem estar presentes no projeto: o `.gitignore` e o script `start.sh`. - -#### Arquivo .gitignore - -Como sabemos, o `.gitignore` é utilizado para impedir que arquivos locais sejam acidentalmente adicionados ao repositório. Arquivos que geralmente devem ser ignorados incluem arquivos de configurações pessoais \(`.bundle`, `.vscode`, `.idea`, `.DS_store`\), pastas de caches \(`tmp` e `storage`\), logs \(arquivos `*.log` e pasta `log`\) e módulos do *node js* \(a pasta `node_modules`\). - -**Nunca** coloque arquivos que afetam o desenvolvimento do projeto \(como `Gemfile`\) no `.gitignore`. - -É **essencial** que a configuração do `.gitignore` seja feita no **início** do projeto, pois problemas relacionados a esse arquivo são um pesadelo para serem resolvidos. - -Segue, abaixo, um template de arquivo `.gitignore`: - -``` -# Environment normalization: -*.DS_Store -.idea -.rvmrc -.vscode -/.bundle -/vendor/bundle - -# Ignore all logfiles and tempfiles: -/log/* -/tmp/* -!/log/.keep -!/tmp/.keep - -# Ignore uploaded files in development: -/storage/* -!/storage/.keep - -# Ignore node_modules: -node_modules/ - -# Ignore yarn files: -/yarn-error.log -yarn-debug.log* -.yarn-integrity - -# Ignore precompiled javascript packs: -/public/packs -/public/packs-test -/public/assets - -# Ignore Byebug command history file: -.byebug_history - -# Ignore project secrets: -/config/initializers/secret_token.rb -/config/master.key -``` - -#### Arquivo start.sh - -O arquivo `start.sh` é um script de execução utilizado para o *Docker*, servindo principalmente para que possamos fazer o deploy de nossa aplicação, mas que também pode ser utilizado para que novos integrantes do projeto inicializem o projeto sem maiores dificuldades. - -Segue, abaixo, um template de arquivo `start.sh`: - -``` -#!/bin/sh -rails db:migrate -bundle exec puma -C config/puma.rb -``` \ No newline at end of file +- Procure montar um cronograma realista, já considerando os possíveis atrasos; +- Não se esqueça de considerar o calendário acadêmico, para poder antever as semanas que normalmente tem baixa produtividade (como semanas finais de semestre ou feriados prolongados); +- Caso esteja na dúvida sobre o período necessário para implementar alguma funcionalidade, consulte algum membro mais experiente ou até mesmo pilotos aposentados; +- Se possível, tente criar um relatório bem detalhado sobre o tempo que durou cada tarefa para futuras referências. Essas informações podem ser muito úteis no futuro para melhorar a precificação e os prazos dos projetos. Leve em conta tanto o tempo estimado quanto o real para cada issue e se possível especifique o máximo possível (Ex.: a issue demorou x horas, ao invés de a issue demorou y sprints). diff --git a/execucao/projetos/gerencia/repositorio.md b/execucao/projetos/gerencia/repositorio.md index 7c8c255..b534482 100644 --- a/execucao/projetos/gerencia/repositorio.md +++ b/execucao/projetos/gerencia/repositorio.md @@ -1,6 +1,107 @@ # Repositório -A criação do repositório para o projeto deve ser feita no grupo da Struct do GitLab. O nome do repositório geralmente segue o padrão `projeto_nome` ou `projeto_codinome` \(como gerente de projeto, sinta-se livre para criar um codinome épico para o projeto :grinning:\). Configure o repositório como privado e **não** inicialize ele com um README, pois o Rails já gera esse arquivo para você. Após criar o repositório, faça o upload dos arquivos gerados pelo projeto de Rails para o repositório. +A criação do repositório para o projeto deve ser feita no grupo da Struct do GitHub. O nome do repositório geralmente segue o padrão `projeto_nome` ou `projeto_codinome` \(como gerente de projeto, sinta-se livre para criar um codinome épico para o projeto :grinning:\). Configure o repositório como privado e **não** inicialize ele com um README, pois o Rails já gera esse arquivo para você. Após criar o repositório, faça o upload dos arquivos gerados pelo projeto de Rails para o repositório. + +## Configuração + +Antes de dar início a um projeto, primeiro a necessário configurar os repositórios nos quais o projeto será desenvolvido. Isso inclui criar o repositório do projeto, as estruturas de pastas para abrigar o código fonte e outros recursos e configurar adequadamente os arquivos de controle de versão, os arquivos de documentação do projeto, a utilização de bibliotecas externas \(gems/packages\), e o banco de dados do projeto. + +Embora isso pareça ser muita coisa, iremos detalhar melhor cada passo nas seções abaixo. + +### Projeto Rails + +O primeiro passo na inicialização do projeto é a criação do projeto na framework [*Ruby on Rails*](../ruby-on-rails/README.md). Para isso, vamos escolher, utilizando o RVM ou Rbenv, uma versão de Ruby esteja um *minor release* atrás da última versão \(ou seja, se a última versão de Ruby é a 2.x, utilize a versão 2.x-1 para o projeto\). Na dúvida, utilize a versão de Ruby utilizada no último projeto. Isso evita que tenhamos que lidar com os bugs inerentes à versão mais nova do Ruby e das gems que o acompanham. Caso algum dos membros do projeto não tenha um gerenciador de versão de Ruby instalado \(como RVM e Rbenv\), ajude ele com a instalação \(esse tipo de software é praticamente essecial para se trabalhar em vários projetos, cada um com uma versão diferente de Ruby\). + +Após escolher a versão de Ruby desejada, será necessário realizar a instalação de algumas gems \(que são as bibliotecas do Ruby\). Para instalar uma gem, basta adicionar a gem ao arquivo Gemfile e rodar o comando `bundle install`. Com isso em mente, instale o *rails* \(framework de desenvolvimento de websites e serviços web\) e para o banco de dados, escolha entre o *mysql2* e o *pg*, gems para o MySQL e PostgreSQL respectivamente, os sistemas gerenciadores de bancos de dados, ou SGBDs, mais utilizados\). Além dessas gems, será necessário instalar os SGBDs MySQL e PostgreSQL em seu sistema operacional \(assunto que **não** será coberto nessa página\). + +Após instalar as gems e os SGBDs em seu computador, caberá a você, como gerente de projetos, decidir qual SGBD utilizar no projeto. O MySQL é mais amigável para iniciantes e possui uma melhor forma de visualizar e manipular diretamente o banco de dados, mas requer mais tempo para ser configurado e a utilização de uma gem \([*figaro*](../../ruby-on-rails/gems.md#figaro)\) para gerenciar os credenciais de login. Já o PostgreSQL pode ser utilizado com quase nenhuma configuração adicional, mas possui uma instalação mais complicada e não permite que o usuário manipule o banco de dados com facilidade. Caso seja sua primeira vez gerenciando um projeto, fale com o diretor de projetos ou com um membro mais experiente para pedir uma recomendação \(eu, pessoalmente, recomendaria o PostgreSQL\). + +{% hint style="info"%} +Para aprender um pouco mais sobre instalação do ruby e do rails e como configurar o banco de dados, confira os nossos [guias](../../../gestao/processo-seletivo/guias-utilizados.md). +{% endhint %} + +Após escolher o SGBD a ser utilizado, chegou a hora de criar o projeto. Para isso, rode o comando `rails new nome_do_projeto_em_snake_case -d {mysql | postgresql} --api`, escolhendo o penúltimo argumento para coincidir com o SGBD desejado. O framework cuidará da criação de pastas e arquivos para o desenvolvimento do projeto. + +Caso você tenha escolhido o MySQL, configure a gem [*figaro*](../../ruby-on-rails/gems.md#figaro) para gerenciar credenciais e, caso você **não** queira utilizar o usuário *root* no projeto, crie as tabelas de desenvolvimento e testes e um usuário com permissão de acesso a elas. Caso você tenha escolhido o PostgreSQL, preencha o arquivo `config/database.yml` com os credenciais do SGBD \(em sistemas operacionais com o *kernel* Linux, você pode preencher apenas o seu nome de usuário e apagar o campo de senha caso você tenha optado, na instalação do PostgreSQL, por utilizar sua conta de usuário do sistema operacional como um usuário do SGBD\). + +Por fim, tente executar o projeto utilizando o comando `rails s`. Caso a execução do projeto não gere alguma mensagem de erro, adicione todos os arquivos gerados ao controle de versão sob o nome 'Commit inicial'. Caso contrário, corrija os erros do projeto **antes** de realizar o commit inicial. + + +### Segundo commit + +Após a criação do repositório, você deverá fazer um segundo commit na branch main que faça todas as configurações necessárias no projeto. Esse segundo commit deve colocar no README informações básicas do projeto, colocar no projeto arquivos padrão que devem estar presentes em qualquer projeto da Struct e configurar as gems que o projeto irá utilizar (confira as [gem essenciais](../../ruby-on-rails/gems.md#gems-essenciais) para um projeto). Mais detalhes são fornecidos nas subseções abaixo. Após executar **todos** esses passos, realize um commit com o nome "Estrutura inicial" adicionando as mudanças que você fez. + +#### README + +O README deve conter as dependências do projeto \(versão do Ruby, banco de dados, etc...\) e os dados do projeto em si, os quais incluem a especificação do projeto \(bem resumida, em uma única frase\), o codinome do projeto \(se existir\), a data de assinatura do contrato, o prazo de desenvolvimento do projeto e a data prevista de entrega do projeto. + +Não utilize o README como uma lista de tarefas, pois isso pode ser feito por meio de *issues*. + +#### Arquivos padrão + +No momento, existem 2 arquivos padrão que devem estar presentes no projeto: o `.gitignore` e o script `start.sh`. + +##### Arquivo .gitignore + +Como sabemos, o `.gitignore` é utilizado para impedir que arquivos locais sejam acidentalmente adicionados ao repositório. Arquivos que geralmente devem ser ignorados incluem arquivos de configurações pessoais \(`.bundle`, `.vscode`, `.idea`, `.DS_store`\), pastas de caches \(`tmp` e `storage`\), logs \(arquivos `*.log` e pasta `log`\) e módulos do *node js* \(a pasta `node_modules`\). + +**Nunca** coloque arquivos que afetam o desenvolvimento do projeto \(como `Gemfile`\) no `.gitignore`. + +É **essencial** que a configuração do `.gitignore` seja feita no **início** do projeto, pois problemas relacionados a esse arquivo são um pesadelo para serem resolvidos. + +Segue, abaixo, um template de arquivo `.gitignore`: + +``` +# Environment normalization: +*.DS_Store +.idea +.rvmrc +.vscode +/.bundle +/vendor/bundle + +# Ignore all logfiles and tempfiles: +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore uploaded files in development: +/storage/* +!/storage/.keep + +# Ignore node_modules: +node_modules/ + +# Ignore yarn files: +/yarn-error.log +yarn-debug.log* +.yarn-integrity + +# Ignore precompiled javascript packs: +/public/packs +/public/packs-test +/public/assets + +# Ignore Byebug command history file: +.byebug_history + +# Ignore project secrets: +/config/initializers/secret_token.rb +/config/master.key +``` + +##### Arquivo start.sh + +O arquivo `start.sh` é um script de execução utilizado para o *Docker*, servindo principalmente para que possamos fazer o deploy de nossa aplicação, mas que também pode ser utilizado para que novos integrantes do projeto inicializem o projeto sem maiores dificuldades. + +Segue, abaixo, um template de arquivo `start.sh`: + +``` +#!/bin/sh +rails db:migrate +bundle exec puma -C config/puma.rb +``` ## Criação de branches padrão diff --git a/execucao/ruby-on-rails/gems.md b/execucao/ruby-on-rails/gems.md index a42a845..707959e 100644 --- a/execucao/ruby-on-rails/gems.md +++ b/execucao/ruby-on-rails/gems.md @@ -185,7 +185,7 @@ Para configurar o Figaro, siga os passos abaixo: password: <%= ENV['db_password'] %> ``` -5. Adicione o trecho de código abaixo ao arquivo `config/application.yml`: +5. Adicione o trecho de código abaixo ao arquivo `config/application.yml.example`: ``` # Database credentials: @@ -193,12 +193,16 @@ Para configurar o Figaro, siga os passos abaixo: db_password: "sua_senha_do_DB_entre_parentesis" ``` -Após configurar a gem Figaro em seu ambiente, **avise os outros membros do projeto** que eles deveram executar os passos 2, 3 e 5 para configurar a gem em seus ambientes! +Após configurar a gem Figaro em seu ambiente, **avise os outros membros do projeto** que eles devem criar um `config/application.yml` baseado no `.example`, colocando as próprias informações ou chaves usadas no projeto. ### Active Storage Sempre que precisar trabalhar em alguma api que envolva o armazenamento de arquivos (images, documentos, audios, etc.), essa gem será sua maior aliada. +### Mailjet + +Para envios de email, pode ser utilizada a SDK (kit de desenvolvimento de software) que a Mailjet criou para o ruby (Verificar a secção sobre [mailer](../mailer.md)). + #### Configuração A configuração inicial para essa gem é bem simples: @@ -213,12 +217,4 @@ Para mais informações sobre como utilizar a gem, dê uma olhada no [drive da e ## *Deprecated* gems -As *deprecated gems* são gems que não recebem mais manutenção e suporte por parte de um time de desenvolvedores, devendo ser retiradas dos projetos da Struct de *Ruby on Rails*. Como gerente de projeto, é sua responsabilidade substituir todas as gems dessa seção que estão presentes no seu projeto. - -### Chrome driver helper - -A gem `chromedriver-helper` parou de receber suporte em Março de 2019. Substitua ela no Gemfile pela gem `webdrivers`. - -### SASS Rails - -A gem `sass-rails` parou de receber suporte em Março de 2019. Substitua ela no Gemfile pela gem `sassc-rails`. \ No newline at end of file +As *deprecated gems* são gems que não recebem mais manutenção e suporte por parte de um time de desenvolvedores, devendo ser retiradas dos projetos da Struct de *Ruby on Rails*. Como gerente de projeto, é sua responsabilidade verificar se existe alguma dessas gems no seu projeto e substituí-las por uma equivalente. From b38b0bcc6ffc6cdd2a6266a9517003d6a4d73ce4 Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Tue, 21 Mar 2023 00:41:57 -0300 Subject: [PATCH 21/30] =?UTF-8?q?=E2=9C=A8=20feat/self-host=20deploy=20doc?= =?UTF-8?q?s=20for=20react=20app?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- execucao/projetos/deploy/README.md | 9 ++- .../projetos/deploy/self-hosting/README.md | 75 +++++++++++++++++++ .../projetos/deploy/self-hosting/react-js.md | 71 ++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 execucao/projetos/deploy/self-hosting/README.md create mode 100644 execucao/projetos/deploy/self-hosting/react-js.md diff --git a/execucao/projetos/deploy/README.md b/execucao/projetos/deploy/README.md index 385f8b0..64445f5 100644 --- a/execucao/projetos/deploy/README.md +++ b/execucao/projetos/deploy/README.md @@ -4,10 +4,15 @@ Deploy nada mais é do que colocar o seu projeto no ar, para que ele possa ser a Em processo de [staging](https://pt.wikipedia.org/wiki/Ambiente_de_implanta%C3%A7%C3%A3o), é comum terceirizar e utilizar domínios temporários, usando como Heroku, Netlify, Railway, etc. -Para entender deploy é necessário entender o fluxo desencadeado ao entrar num site. +## Configuração do repositório + +Cada maneira de se fazer deploy tem sua própria maneira de configurar o repositório. Mas essa configuração sempre deve ser feita, e muitas vezes deve ser feita tanto no github (numa branch separada) quanto no ambiente de deploy. + ## Entrando num site +Para entender deploy é necessário entender o fluxo desencadeado ao entrar num site. + Quando entramos num site, o que acontece é o seguinte: 1. O navegador faz uma requisição (get '/', ou outra rota) para o domínio. @@ -20,3 +25,5 @@ Então, precisamos definir o que é esse algo. Para isso, em serviços terceiriz - Compilação de arquivos - Criação de assets estáticos (js bundles, css bundles, etc.) - Criação de banco de dados + + diff --git a/execucao/projetos/deploy/self-hosting/README.md b/execucao/projetos/deploy/self-hosting/README.md new file mode 100644 index 0000000..d497f1d --- /dev/null +++ b/execucao/projetos/deploy/self-hosting/README.md @@ -0,0 +1,75 @@ +{% hint style="warning" %} +Leia antes para entender o que é [imagem e container docker](/execucao/projetos/docker/README.md). +{% endhint %} + +# Nosso servidor + +No momento, pagamos por um servidor no DigitalOcean, que é um serviço de cloud computing, e usamos ssh para acessá-lo. + + +## Nossa infraestrutura + +Usamos Docker e Docker Compose para poder isolar e rodar vários servidores na mesma máquina. Para levar o tráfego que chega em nossa máquina para o container correto, usamos o Traefik v2 como proxy reverso. + +É mantido um repositório no gitbucket com os Dockerfiles utilizados para construir as imagens do nosso servidor. + +É mantido um repositório que está presente no servidor, com os arquivos do Docker Compose. + +## Fazendo deploy + +### Configure o projeto + +Crie uma branch `production`, caso não exista, no projeto desenvolvido, e faça a configuração específica para esse deploy. + +### Crie a imagem docker + +Pegue o template de `Dockerfile` (no momento não existe template, então busque do último projeto produzido) e modifique conforme necessário para gerar uma imagem docker. + +Para gerar a imagem, use o comando `docker build -t structej/projetos:nome-projeto-1.0 .` na raíz do projeto. + +Várias das nossas imagens são um pull do projeto, e para isso é necessário um token de acesso no github. Para gerar esse token, pode ser seguida a [documentação do github](https://docs.github.com/pt/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic). + + +### Faça o push da imagem + +Faça login no Docker Hub, usando o comando `docker login`. Talvez seja necessário inserir as nossas credenciais. + +Faça o push da imagem para o Docker Hub, usando o comando `docker push structej/projetos:nome-projeto-1.0`. + +### Faça o docker-compose no servidor + +{% hint style="info" %} +Estamos guardando templates de docker-compose.yml na pasta `templates` do repositório dos docker-compose. Veja atualizações por lá. +{% endhint %} + +Faça o pull/clone do repositório do Docker Compose, usando o comando `git pull/clone`. Crie um docker-compose.yml com o serviço do projeto, usando o template de `docker-compose.yml` (no momento o template só inclui configuração do traefik, então busque configurações adicionais do último projeto produzido). + +## Atualizando o deploy + +### Atualize a branch `production` + +Peça para que um membro da equipe faça um PR para a branch `production` do projeto, com as adições que devem ser feitas no deploy. + +Procure por coisas que podem quebrar ou requerem passos adicionais no deploy, como mudanças de banco de dados, ou mudanças de configuração de serviços. + +### Atualize a imagem docker + +Faça o build da imagem docker, usando o comando `docker build -t structej/projetos:nome-projeto-x.x .` na raíz do projeto. + +Agora a versão da imagem docker é `x.x`. Sempre incrementamos a versão da imagem docker, para que possamos fazer o rollback caso dê merda. + +Faça o push da imagem para o Docker Hub, usando o comando `docker push structej/projetos:nome-projeto-x.x`. + +### Atualize o docker-compose no servidor + +Mude a propriedade `image` do serviço do projeto no docker-compose.yml para a versão atual da imagem docker. Faça o commit e o push do docker-compose.yml. + +```docker-compose +version: '3.7' + +services: + service-we-want-to-update: + image: structej/projetos:nome-projeto-x.x + ... +``` + diff --git a/execucao/projetos/deploy/self-hosting/react-js.md b/execucao/projetos/deploy/self-hosting/react-js.md new file mode 100644 index 0000000..226bc04 --- /dev/null +++ b/execucao/projetos/deploy/self-hosting/react-js.md @@ -0,0 +1,71 @@ +# Fazendo o deploy de um react-app padrão + +Considerando que nenhum framework, e nem SSR, está sendo usado, o app react não inclui servidor. No ambiente de desenvolvimento são usadas ferramentas para servir o app na porta determinada (na Struct determinamos como 3000), mas no deploy que ferramenta é usada? O NGinx. + +## Configurando o repositório + +Considere a [branch production do projeto front-end-template](https://github.com/StructCE/react-template/tree/production) e [suas alterações](https://github.com/StructCE/react-template/compare/main...production). + +### Configurando o NGinx + +O NGinx é um servidor web que pode ser usado para servir arquivos estáticos, como imagens, css, js, etc. Sendo assim, depois que nosso app react é buildado, ele é servido pelo NGinx. + +Para isso, é necessário criar um arquivo de configuração para o NGinx na raíz do projeto, chamado `nginx.conf`. Esse arquivo deve conter: + +```nginx +server { + listen 80 default_server; + listen [::]:80 default_server; + root /usr/share/nginx/html; + server_name dominio.exemplo.ex www.dominio.exemplo2; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} +``` + +Trocando os valores de `dominio.exemplo.ex` e `www.dominio.exemplo2` pelos domínios que o app será servido. Por exemplo, se o app for servido em `www.struct.com.br`, o arquivo deve conter: + +```nginx +server { + listen 80 default_server; + listen [::]:80 default_server; + root /usr/share/nginx/html; + server_name www.struct.com.br; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} +``` + +### Mudando as urls de localhost + +O app react usa urls locais para acessar a API, por exemplo, `http://localhost:3333/api/v1`. Essas urls devem ser alteradas para as urls de produção, por exemplo, `https://api.struct.com.br/api/v1`. + +É possível fazer isso usando variáveis de ambiente, mas no momento deve ser trocado manualmente, como no nosso repositório de exemplo (talvez esse gitbook esteja desatualizado em relação ao repositório, verifique). + + +### Mudando o index.html + +O arquivo `index.html` deve ser alterado para conter informações corretas sobre o app, e metadados. Talvez também seja útil criar um `robots.txt`, que serve para ajudar mecanismos de busca a indexar o site da maneira desejada, veja de acordo com seu projeto. + +As mudanças gerais são colocar o título correto, colocar descrição, mudar o favicon e a linguagem para pt-BR. + + +## Configurando o servidor + +### Criando a docker image + +Vá para a branch production localmente. Crie um arquivo chamado `Dockerfile` na raíz do projeto. Use como base os templates de projetos recentes anteriores, e atualize o url do git presente no arquivo, mudando o nome do projeto e o token de oauth do github ([veja aqui](./README.md#crie-a-imagem-docker)). + +### Criando o container + +Dê um git pull no repositório de docker_compose da Struct, e crie uma pasta com o nome do projeto. Modifique o template de docker-compose do Traefik com os nomes que podem ser usados para identificar o projeto nos logs, caso ocorra algum erro. + +Além da configuração do traefik, é importante definir a imagem que será usada com o valor de `image`, alterar os valores de `environment`, `restart`, `volumes`, e `networks`. Use um projeto anterior como base. + +Crie o container usando o comando `docker-compose up -d`. A imagem será baixada do repositório da Struct, e o container será criado e rodado em plano de fundo. From 560bfdb77586ed16fe73cde1e8db2f80e1dac23c Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Sat, 29 Apr 2023 23:32:47 -0300 Subject: [PATCH 22/30] 3rd party deploy working --- SUMMARY.md | 4 ++-- .../deploy/{terceirizado => terceirizando}/netlify.md | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename execucao/projetos/deploy/{terceirizado => terceirizando}/netlify.md (100%) diff --git a/SUMMARY.md b/SUMMARY.md index 054dbff..0d7713a 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -65,8 +65,8 @@ * [Projetos](execucao/projetos/README.md) * [Deploy](execucao/projetos/deploy/README.md) * [Terceirizando]() - * [Heroku](execucao/projetos/deploy/terceirizado/heroku.md) - * [Netlify](execucao/projetos/deploy/terceirizado/netlify.md) + * [Heroku](execucao/projetos/deploy/terceirizando/heroku.md) + * [Netlify](execucao/projetos/deploy/terceirizando/netlify.md) * [Cloudinary](execucao/projetos/cloudinary.md) * [Gerência de projetos](execucao/projetos/gerencia/README.md) * [Início de projeto](execucao/projetos/gerencia/inicio-de-projeto.md) diff --git a/execucao/projetos/deploy/terceirizado/netlify.md b/execucao/projetos/deploy/terceirizando/netlify.md similarity index 100% rename from execucao/projetos/deploy/terceirizado/netlify.md rename to execucao/projetos/deploy/terceirizando/netlify.md From 918b3b01218ba95bce830b75d280d64ecc3d0f6d Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Tue, 2 May 2023 10:58:44 -0300 Subject: [PATCH 23/30] mudado coisa pequena --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index 0d7713a..c70535c 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -64,7 +64,7 @@ * [Flutter](execucao/flutter.md) * [Projetos](execucao/projetos/README.md) * [Deploy](execucao/projetos/deploy/README.md) - * [Terceirizando]() + * [Terceirizando](execucao/projetos/deploy/terceirizando) * [Heroku](execucao/projetos/deploy/terceirizando/heroku.md) * [Netlify](execucao/projetos/deploy/terceirizando/netlify.md) * [Cloudinary](execucao/projetos/cloudinary.md) From d4db0d3e0a353c0f1d7d9cbc69536219243497b4 Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Tue, 2 May 2023 11:30:40 -0300 Subject: [PATCH 24/30] mudadas coisas pedidas --- .../projetos/deploy/self-hosting/README.md | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/execucao/projetos/deploy/self-hosting/README.md b/execucao/projetos/deploy/self-hosting/README.md index d497f1d..bd3835a 100644 --- a/execucao/projetos/deploy/self-hosting/README.md +++ b/execucao/projetos/deploy/self-hosting/README.md @@ -4,16 +4,16 @@ Leia antes para entender o que é [imagem e container docker](/execucao/projetos # Nosso servidor -No momento, pagamos por um servidor no DigitalOcean, que é um serviço de cloud computing, e usamos ssh para acessá-lo. +No momento, pagamos por um servidor no DigitalOcean, que é um serviço de cloud computing, ou seja, uma plataforma que usa a conectividade da internet para hospedar e prover recursos, programas e informações em nuvem, e usamos ssh para acessá-lo e fazê-lo rodar exatamente o que queremos. ## Nossa infraestrutura -Usamos Docker e Docker Compose para poder isolar e rodar vários servidores na mesma máquina. Para levar o tráfego que chega em nossa máquina para o container correto, usamos o Traefik v2 como proxy reverso. +Usamos Docker e Docker Compose para poder isolar e rodar vários servidores na mesma máquina. Para levar o tráfego que chega em nossa máquina para o container correto, usamos o [Traefik v2](https://doc.traefik.io/traefik/) como [proxy reverso](https://pt.wikipedia.org/wiki/Proxy_reverso). É mantido um repositório no gitbucket com os Dockerfiles utilizados para construir as imagens do nosso servidor. -É mantido um repositório que está presente no servidor, com os arquivos do Docker Compose. +É mantido um repositório que está presente no servidor, com os arquivos do Docker Compose utilizados para rodar as imagens construídas. ## Fazendo deploy @@ -21,11 +21,22 @@ Usamos Docker e Docker Compose para poder isolar e rodar vários servidores na m Crie uma branch `production`, caso não exista, no projeto desenvolvido, e faça a configuração específica para esse deploy. +{% hint style="warning" %} +Também adicione `Dockerfile` ao `.gitignore` do projeto. +{% endhint %} + ### Crie a imagem docker +{% hint style="danger" %} +Não coloque Dockerfiles no repositório do projeto, utilize o repositório do gitbucket para isso. + +Colocar esse arquivo no projeto pode causar problemas de segurança, e não é uma boa prática. Muitos dos nossos Dockerfiles usam credenciais que não devem ser expostas. +{% endhint %} + Pegue o template de `Dockerfile` (no momento não existe template, então busque do último projeto produzido) e modifique conforme necessário para gerar uma imagem docker. -Para gerar a imagem, use o comando `docker build -t structej/projetos:nome-projeto-1.0 .` na raíz do projeto. +Para gerar a imagem, use o comando `docker build -t structej/projetos:nome-projeto-1.0 .` na raíz do projeto. Isso constrói a imagem e coloca a tag `nome-projeto-1.0` nela. + Várias das nossas imagens são um pull do projeto, e para isso é necessário um token de acesso no github. Para gerar esse token, pode ser seguida a [documentação do github](https://docs.github.com/pt/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token#creating-a-personal-access-token-classic). @@ -34,16 +45,26 @@ Várias das nossas imagens são um pull do projeto, e para isso é necessário u Faça login no Docker Hub, usando o comando `docker login`. Talvez seja necessário inserir as nossas credenciais. -Faça o push da imagem para o Docker Hub, usando o comando `docker push structej/projetos:nome-projeto-1.0`. +Faça o push da imagem para o Docker Hub, usando o comando `docker push structej/projetos:nome-projeto-1.0`. Isso envia a imagem para o Docker Hub, onde ela pode ser acessada pelo docker-compose. + +{% hint style="info" %} +O `structej/projetos` é o `usuário/projeto` que enviamos a imagem de tag `nome-projeto-1.0`. É importante fazer assim, pois por padrão projetos no Docker Hub são públicas, então enviamos ela para um repositório que sabemos ser privado. +{% endhint %} ### Faça o docker-compose no servidor {% hint style="info" %} -Estamos guardando templates de docker-compose.yml na pasta `templates` do repositório dos docker-compose. Veja atualizações por lá. +Estamos guardando templates de docker-compose.yml na pasta `templates` do repositório presente no servidor. Veja atualizações por lá. +{% endhint %} + +{% hint style="info" %} +É recomendado fazer as atualizações localmente, dar um push para o repositório, e depois dar um pull no servidor. {% endhint %} Faça o pull/clone do repositório do Docker Compose, usando o comando `git pull/clone`. Crie um docker-compose.yml com o serviço do projeto, usando o template de `docker-compose.yml` (no momento o template só inclui configuração do traefik, então busque configurações adicionais do último projeto produzido). +O template de traefik possui palavras chaves que devem ser substituídas. Utilize múltiplos cursores para garantir que não esqueceu de alterar uma das palavras chaves em algum lugar. + ## Atualizando o deploy ### Atualize a branch `production` @@ -56,14 +77,23 @@ Procure por coisas que podem quebrar ou requerem passos adicionais no deploy, co Faça o build da imagem docker, usando o comando `docker build -t structej/projetos:nome-projeto-x.x .` na raíz do projeto. -Agora a versão da imagem docker é `x.x`. Sempre incrementamos a versão da imagem docker, para que possamos fazer o rollback caso dê merda. +Agora a versão da imagem docker é `x.x`. Sempre incrementamos a versão da imagem docker, para que possamos fazer o rollback caso algo aconteça de errado. Faça o push da imagem para o Docker Hub, usando o comando `docker push structej/projetos:nome-projeto-x.x`. +{% hint style="danger" %} +O que pode acontecer de errado? +- A branch possuía coisas que quebram o deploy, e o container fica em estado de erro. Nesse caso, o traefik não consegue acessar o container, e o site fica fora do ar; +- A atualização à branch introduziu um bug grave, mas o container não fica em estado de erro. Nesse caso, o traefik consegue acessar o container, mas o site pode não funcionar corretamente; +- O Dockerfile foi alterado e possui alguma coisa errada (por exemplo em versionamento), mas o build ainda funciona. Nesse caso, novamente, o traefik consegue acessar o container, mas o site pode não funcionar corretamente; +- etc. +{% endhint %} + ### Atualize o docker-compose no servidor Mude a propriedade `image` do serviço do projeto no docker-compose.yml para a versão atual da imagem docker. Faça o commit e o push do docker-compose.yml. +Exemplo: ```docker-compose version: '3.7' From 195706eaa4b1441b72bfe79d5915be96c8b7c82c Mon Sep 17 00:00:00 2001 From: artistrea <apiravesi@gmail.com> Date: Mon, 15 May 2023 14:18:18 -0300 Subject: [PATCH 25/30] changed terceirizando deploy readme --- execucao/projetos/deploy/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/execucao/projetos/deploy/README.md b/execucao/projetos/deploy/README.md index 385f8b0..17d74a6 100644 --- a/execucao/projetos/deploy/README.md +++ b/execucao/projetos/deploy/README.md @@ -20,3 +20,5 @@ Então, precisamos definir o que é esse algo. Para isso, em serviços terceiriz - Compilação de arquivos - Criação de assets estáticos (js bundles, css bundles, etc.) - Criação de banco de dados + +Permitindo que nossa aplicação rode e responda a requisições da mesma maneira que os faz em nossa máquina local (`localhost`). From 007be3f5334dbd178245d282948f064764c628ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Corr=C3=AAa?= <89353709+LaKHamote@users.noreply.github.com> Date: Fri, 19 May 2023 11:28:54 -0300 Subject: [PATCH 26/30] Update SUMMARY.md --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index 9ebc9ce..bf3d97a 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -56,7 +56,7 @@ * [Estilizando](execucao/front-end/react-js/estilizando.md) * [Hooks](execucao/front-end/react-js/hooks.md) * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) -* [Next.js](execucao/next-js/) +* [Next JS](execucao/next-js/) * [Conceitos relevantes](execucao/next-js/conceitos-relevantes/README.md) * [V<=12](execucao/next-js/v<=12) * [Sobre](execucao/next-js/v<=12/sobre.md) From e2d2d40729ac4fe513eed21844f9f47fef93769f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Corr=C3=AAa?= <89353709+LaKHamote@users.noreply.github.com> Date: Fri, 19 May 2023 11:31:31 -0300 Subject: [PATCH 27/30] Update SUMMARY.md --- SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index bf3d97a..f824908 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -56,7 +56,7 @@ * [Estilizando](execucao/front-end/react-js/estilizando.md) * [Hooks](execucao/front-end/react-js/hooks.md) * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) -* [Next JS](execucao/next-js/) +* [Next JS](execucao/next-js/conceitos-relevantes/README.md) * [Conceitos relevantes](execucao/next-js/conceitos-relevantes/README.md) * [V<=12](execucao/next-js/v<=12) * [Sobre](execucao/next-js/v<=12/sobre.md) From 8726a25a424cbbc262ac7d89b4dc1948bfa0bdde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Corr=C3=AAa?= <89353709+LaKHamote@users.noreply.github.com> Date: Fri, 19 May 2023 11:36:48 -0300 Subject: [PATCH 28/30] Update SUMMARY.md Next JS nao tinha arquivo md, entao a subpasta conceitos relevantes foi deletada e seu md redirecionado para Next JS --- SUMMARY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index f824908..b94b92f 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -57,7 +57,6 @@ * [Hooks](execucao/front-end/react-js/hooks.md) * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) * [Next JS](execucao/next-js/conceitos-relevantes/README.md) - * [Conceitos relevantes](execucao/next-js/conceitos-relevantes/README.md) * [V<=12](execucao/next-js/v<=12) * [Sobre](execucao/next-js/v<=12/sobre.md) * [Inicio rapido](execucao/next-js/v<=12/inicio-rapido.md) From 718aa0a4e10d2d646b865d0d8ba1c30a304db66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Corr=C3=AAa?= <89353709+LaKHamote@users.noreply.github.com> Date: Fri, 19 May 2023 11:40:00 -0300 Subject: [PATCH 29/30] Update SUMMARY.md Subpastas de versoes de next estavam sem .md --- SUMMARY.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SUMMARY.md b/SUMMARY.md index b94b92f..ad264d5 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -57,8 +57,7 @@ * [Hooks](execucao/front-end/react-js/hooks.md) * [Mais sobre](execucao/front-end/react-js/mais-sobre.md) * [Next JS](execucao/next-js/conceitos-relevantes/README.md) - * [V<=12](execucao/next-js/v<=12) - * [Sobre](execucao/next-js/v<=12/sobre.md) + * [V<=12](execucao/next-js/v<=12/sobre.md) * [Inicio rapido](execucao/next-js/v<=12/inicio-rapido.md) * [Rotas](execucao/next-js/v<=12/rotas.md) * [Paginas e padroes](execucao/next-js/v<=12/paginas-e-padroes.md) From 6e4230e583d9415289a6e838dfde5f9f53821a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Corr=C3=AAa?= <89353709+LaKHamote@users.noreply.github.com> Date: Fri, 19 May 2023 11:44:01 -0300 Subject: [PATCH 30/30] Update SUMMARY.md codigo morto --- SUMMARY.md | 1 - 1 file changed, 1 deletion(-) diff --git a/SUMMARY.md b/SUMMARY.md index ad264d5..8d03d20 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -63,7 +63,6 @@ * [Paginas e padroes](execucao/next-js/v<=12/paginas-e-padroes.md) * [Api](execucao/next-js/v<=12/api.md) * [Styled Components](execucao/next-js/v<=12/styled-components.md) - * [Prisma](execucao/next-js/v<=12/prisma.md) * [V>=13](execucao/next-js/v>=13/README.md) * [Prisma JS](execucao/back-end/prisma-js/README.md) * [ATENCAO](execucao/back-end/prisma-js/ATENCAO.md)