Documentação das Tarefas
Tarefa 1: Variável de Ambiente
Modificar a aplicação para consumir uma variável de ambiente chamada NAME
e, no lugar de "World", exibir o valor dessa variável.
Para a aplicação consumir a variável de ambiente NAME
, basta usar o módulo os
nativo do Python. O método os.environ.get()
recebe duas strings de argumento. A primeira refere-se ao nome da variável de ambiente. Se ela existir, então a função retorna seu valor, senão, retorna o segundo argumento passado.
@app.route("/")
def hello_world():
name = os.environ.get("NAME", "World")
return f"Hello, {name}!"
Tarefa 2: .gitignore
Modificar o arquivo .gitignore
para que seja adequado a uma aplicação Python.
No arquivo .gitignore
, faz-se referência a arquivos/diretórios com as seguintes características:
- Arquivos otimizados / binários
- Ambientes virtuais Python (
venv
) - Arquivos específicos do IDE
- Arquivos de Log
entre outros.
Tarefa 3: Fluxo de Desenvolvimento
Elaborar, implementar e reforçar o fluxo de desenvolvimento do repositório.
Sugestão de fluxo de desenvolvimento com o seguinte padrão de criação/merge de branches:
-
Branches Principais:
master
: Representa a versão estável e pronta para produção da aplicação.develop
: Serve como branch de integração para o desenvolvimento contínuo.
-
Branches de Funcionalidades:
- Para cada nova funcionalidade ou tarefa, crie uma nova branch a partir da
develop
. - Dê um nome descritivo à branch de funcionalidade, como
feature/user-authentication
.
- Para cada nova funcionalidade ou tarefa, crie uma nova branch a partir da
-
Hotfix:
- Se houver um bug crítico na versão de produção, crie uma branch de Hotfix a partir da
master
. - Dê um nome descritivo à branch de Hotfix, como
hotfix/fix-login-issue
.
- Se houver um bug crítico na versão de produção, crie uma branch de Hotfix a partir da
-
Branches de Lançamento (Release):
- Quando a branch
develop
atingir um ponto estável para um lançamento, crie uma branch de release a partir dela. - Dê um nome da branch com base no número da versão, como
release/1.0
.
- Quando a branch
-
Merging:
- Faça merge nas branches de funcionalidades de volta à
develop
assim que as funcionalidades estiverem concluídas e testadas. - Faça merge nas branches de release tanto na
develop
quanto namaster
após testar e garantir a estabilidade. - Faça merge nas branches de Hotfix tanto no
develop
quanto nomaster
para aplicar correções críticas à versão de produção. - Sempre usar Pull Requests para as branches principais (branches protegidas).
- Faça merge nas branches de funcionalidades de volta à
-
Tagging:
- Sempre que dar merge numa branch de release na
master
, crie uma tag para a versão específica. Isso facilita o rastreamento dos lançamentos.
- Sempre que dar merge numa branch de release na
Mensagens de commits devem deve ser estruturada seguindo o padrão Conventional Commits v1.0.0
:
A especificação Conventional Commits fornece um conjunto simples de regras para criar um histórico de commits explícito, o que facilita a escrita de ferramentas automatizadas por cima dele. Essa convenção se alinha com o SemVer, descrevendo as funcionalidades, correções e mudanças que quebram compatibilidade presentes nas mensagens de commit.
<tipo>[escopo opcional]: <descrição>
[corpo opcional]
[rodapé(s) opcional(is)]
Tarefa 4: Boas Práticas no Repositório
Consolidar boas práticas no repositório através de ferramentas de análise estática, hooks de pré-commit, etc. a seu critério.
Foram usadas as seguintes ferramentas com o intuito de consolidar boas práticas no repositório:
- Poetry: ferramenta de gerenciamento de dependências e empacotamento de projetos Python. Com
poetry.lock
garante um snapshot das dependencias podendo ser facilmente replicado em outras instâncias. - Semgrep: ferramenta de análise estática de código que permite encontrar e corrigir problemas de segurança, bugs e más práticas em seus projetos de código-fonte. Ele usa regras de análise baseadas em padrões e expressões regulares para identificar possíveis vulnerabilidades e problemas no código.
- Black: garante que o código siga um estilo de formatação consistente e padronizado, tornando-o mais legível e mantendo uma aparência uniforme em projetos compartilhados por várias pessoas.
- Isort: ferramenta de ordenação de importações em código fonte Python. Ele reorganiza as declarações de importação em ordem alfabética e agrupa-as de forma coerente, tornando o código mais organizado.
- Flake8: combina várias ferramentas populares, como o PyFlakes (verificador de erros estáticos) e o pycodestyle (verificador de conformidade de estilo PEP 8), além de verificar a complexidade do código com o mccabe. O Flake8 ajuda a identificar problemas potenciais no código, como erros de sintaxe, problemas de estilo e complexidade excessiva.
- MKDocs: ferramenta de criação de documentação estática em Markdown. Ele possui várias facilidades como o comando
mkdocs serve
que inicia um servidor local em que você pode visualizar a documentação no navegador em tempo real enquanto faz alterações no conteúdo ou no estilo. Além do comandomkdocs gh-deploy
, que compila a documentação e a implanta na branch "gh-pages" do repositório do GitHub, tornando-a publicamente acessível em um URL específico do GitHub Pages. - PyTest: oferece recursos poderosos, como detecção automática de testes e testes parametrizados. Com PyTest-cov foi possível intregá-lo com a ferramente descrita a seguir.
- Coverage: com essa ferramenta é possível medir a cobertura de código do projeto, ou seja, quantas linhas de código foram executadas durante a execução dos testes. Com o comando
coverage html
ele gera uma aplicação web local (hospedada por padrão na porta8000
) contendo relatórios detalhados que mostram quais partes do código não foram cobertas pelos testes. -
TaskiPy: diminui o quanto digitamos para usar as ferramentas que foram descritas até então. A ideia é definir as tarefas numa sessão do
pyproject.toml
, e estas são executadas com um comando simples (task <nome do comando>
).[tool.taskipy.tasks] ... lint = "semgrep scan --verbose --config auto && black . && isort . && flake8 ." docs = "mkdocs serve" docs_deploy = "mkdocs gh-deploy" test = "pytest -s -x --cov=src" cov = "coverage html"
-
pre-commit: permite configurar pre-commit hooks que são executados antes de um commit ser realizado. Neste caso, foi incluido a
task test
definido com o Taskipy além da checagem estática com o semgrep, black, isort e flake8, além de verificar marcador de ordem de byte UTF-8, corrigir pragma de codificação do Python, remover espaços em branco no final de cada linha, não permitir fazer commit em branches protegidas e verificar se arquivos grandes foram adicionados. -
Code Owners: recurso que permite atribuir revisores específicos para determinados arquivos ou pastas em um repositório do GitHub. Ao definir um arquivo CODEOWNERS na pasta .github, é especificado os proprietários responsáveis por revisar e aprovar as alterações em determinados caminhos do código-fonte. Neste projeto exemplo, apenas meu usuário foi definido como revisor Default de todas as mudanças.
Tarefa 5: Ambiente de Produção
Preparar a aplicação para que seja production-ready.
Embora o Flask seja um excelente framework para criar aplicativos web em Python de forma rápida e simples, ele não é considerado "production ready" por si só. O servidor embutido no Flask é adequado apenas para fins de desenvolvimento e testes. Ele não é projetado para lidar com cargas de tráfego significativas ou oferecer recursos de alta disponibilidade.
Com isso, foi escolhido o Guonicorn (Green Unicorn) como ferramenta para tal: um servidor HTTP WSGI (Web Server Gateway Interface) que permite que a aplicação atenda a mais solicitações ao mesmo tempo.
Tarefa 6: Containerização
Criar arquivos e scripts para que a aplicação possa ser executada em um container.
Foi criado um Dockerfile que copia somente os arquivos necessários para que o container instale as dependências do projeto (poetry.lock
e pyproject.toml
), e rode a aplicação (conteúdo do diretório src/
). Isso é feito com o auxílio de um arquivo .dockerignore
.
Como ENTRYPOINT
, o container executa o script scope.sh
que roda a aplicação com o servidor do próprio flask caso exista uma variável de ambiente LOCAL=True
. Caso contrário, o Guonicorn é usado.
Tarefa 7: Desenvolvimento Local
Elaborar um modelo de desenvolvimento que permita a execução da aplicação em um ambiente de desenvolvimento local.
O modo Debug do Flask, além de fornecer informações detalhadas sobre erros e exceções que ocorrem durante a execução, também ativará o recurso de "auto-reload", que fará com que a aplicação seja recarregada automaticamente sempre que ocorrer uma alteração no código.
Com um arquivo docker-compose.yml
, definimos a criação de um container na nossa máquina local que terá a variável de ambiente LOCAL=True
, que ativa o modo Debug do Flask e roda a aplicação sem o Guonicorn.
Além disso, é usado um Volume mapeando o repositório local ao diretório da aplicação no container. Com isso, toda modificação do código fonte também é alterado dentro do container, e com a função DEBUG, podemos ver as modificações sem precisar rebuildar a imagem.
Para subir o container localmente, basta usar a task definida no Taskipy task up
(subir o container) task down
(parar)
[tool.taskipy.tasks]
up = "docker compose up -d --build --force-recreate"
down = "docker compose down --rmi local"
Tarefa 8: CI/CD
Construir pipelines de CI/CD para a aplicação utilizando GitHub Actions. Esse item possui forte relação com o fluxo de desenvolvimento, pois deve compreender o deployment em dois ambientes diferentes: homologação e produção. O deployment da aplicação deve ser realizado em um serviço serverless da Google Cloud Platform.
GitHub Actions é um serviço de automação oferecido pelo GitHub para facilitar a criação de fluxos de trabalho automatizados. Por meio de arquivos yaml na pasta .github/workflow
do repositório, pode-se definir e configurar tarefas personalizadas que são acionadas por eventos, como push de código, abertura de pull requests ou outros eventos do repositório.
-
docs.yml: com todo "push" na
main
(apenas por meio de um Pull Request aceito) o site de documentação gerado pelo MKDocs é implantada na plataforma GitHub Actions. -
lint.yml: com todo Pull Request, a task de "lint code" com as ferramentas Semgrep para seguir boas práticas de segurança e Black, Isort e Flak8 para garantir a boa formatação do código Python. Além da ferramente hadolint, por meio de uma action para a devida formatação do Dockerfile seguindo especificações formais.
-
cd.yml: com todo "push" na
main
(apenas por meio de um Pull Request aceito) a imagem Docker da aplicação é buildada para o Container Registry da Google, e depois é implantada no Google Run. A autorização é feita passando a Json Service Account Access Key por meio de um secret do repositório. Além disso, há um arquivocd-dev.yml
seguindo o mesmo fluxo, mas para a implantação de homologação.Obs: A recomendação da Cloud Run é que os sistemas de CI/CD não definam nem alterem configurações para permitir "invocações" não autenticadas. Novas implantações são automaticamente configuradas como serviços privados, enquanto a implantação de uma revisão de um serviço público (não autenticado) preservará a configuração de IAM como pública (não autenticada). Sendo assim, depois da primeira implantação, deve-se realizar o seguinte comando num ambiente com autenticação para deixar a aplicação de produção pública.
gcloud run services add-iam-policy-binding [SERVICE_NAME] \ --member="allUsers" \ --role="roles/run.invoker"