Dica 01 - Criando Issues com API do Github (Linux)
Versões usadas no vídeo: não usa Django; Python 3.10.6, click 8.1.3, python-decouple 3.6 e requests 2.28.1.
Github: https://github.com/rg3915/dicas-de-django (branch aula01)
Documentação: https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#create-an-issue
Este é o primeiro vídeo do Projeto Dicas de Django, uma série em que cada dica continua o projeto da anterior. Antes de começar a programar o projeto, vamos criar uma ferramenta de linha de comando, em Python, que cria issues no GitHub direto do terminal do Linux, usando a API REST do GitHub.
O script faz duas coisas:
- Cria a issue no repositório (título, descrição e labels).
- Anota a issue criada num arquivo de tarefas local (
~/tarefas.txt), já com o comando do commit que vai fechar a issue (close #número).
Estrutura de pastas
Na pasta do projeto, crie a pasta cli, o script e os arquivos de apoio:
mkdir cli
touch cli/create_issue.py
touch .env.sample
touch requirements.txt
Instalação
Crie e ative o ambiente virtual, e instale as três bibliotecas que vamos usar:
- click: para criar a interface de linha de comando (as opções
--title,--bodye--labels). - python-decouple: para ler o token e o nome do repositório do arquivo
.env. - requests: para fazer a requisição
POSTna API do GitHub.
python -m venv .venv
source .venv/bin/activate
pip install click python-decouple requests
pip freeze | grep -E 'click|python-decouple|requests' > requirements.txt
No vídeo, o pip freeze mostrou também as dependências do requests (certifi, charset-normalizer, idna, urllib3). Só as três que usamos diretamente vão para o requirements.txt:
click==8.1.3
python-decouple==3.6
requests==2.28.1
Pegando o token no GitHub
Para usar a API você precisa de um personal access token:
- No GitHub, clique na sua foto e vá em Settings.
- No fim do menu lateral, clique em Developer settings.
- Clique em Personal access tokens e depois em Tokens (classic).
- Clique em Generate new token e escolha Generate new token (classic).
- Dê um nome ao token (no vídeo,
dicas 2023) e escolha a expiração (30 dias, por exemplo, ou sem expiração). - Marque os escopos. Para criar issues, o essencial é o repo. No vídeo também foram marcados gist e user.
- Clique em Generate token e copie o token, porque ele só aparece uma vez.
Variáveis de ambiente
O arquivo .env.sample é só um modelo, e vai para o repositório sem o token:
# .env.sample
REPO_OWNER=rg3915
REPO_NAME=dicas-de-django
TOKEN=
Copie para .env e coloque o seu token nele:
cp .env.sample .env
# .env
REPO_OWNER=rg3915
REPO_NAME=dicas-de-django
TOKEN=********************
Troque REPO_OWNER e REPO_NAME pelo seu usuário e pelo seu repositório. Nunca deixe o token no .env.sample.
O script create_issue.py
Os trechos principais de create_issue.py (o arquivo completo está no link logo abaixo):
# create_issue.py
import click
import requests
from decouple import config
# ... (veja o arquivo completo no GitHub)
# O repositório para adicionar a issue
REPO_OWNER = config('REPO_OWNER')
REPO_NAME = config('REPO_NAME')
TOKEN = config('TOKEN')
# ... (write_file: veja o arquivo completo no GitHub)
@click.command()
@click.option('--title', prompt='Title', help='Digite o título.')
@click.option('--body', prompt='Description', help='Digite a descrição.')
# @click.option('--assignee', prompt='Assignee', help='Digite o nome da pessoa a ser associada.')
@click.option('--labels', prompt='Labels', help='Digite as labels.')
def make_github_issue(title, body=None, assignee=None, milestone=None, labels=None):
'''
Cria issue no github.com
'''
url = f'https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/issues'
headers = {
"Authorization": f"token {TOKEN}"
}
labels = labels.split(',')
# Cria a issue
issue = {
"title": title,
"body": body,
"labels": labels
}
if assignee:
issue['assignees'] = [assignee]
# Adiciona a issue no repositório
req = requests.post(url, headers=headers, json=issue)
if req.status_code == 201:
print(f'Successfully created Issue "{title}"')
number = req.json()['number']
description = body
filename = '/home/seu-usuario/tarefas.txt'
write_file(filename, number, title, description, labels)
else:
print(f'Could not create Issue "{title}"')
if __name__ == '__main__':
make_github_issue()
Código completo: cli/create_issue.py
Explicando por partes:
- Configuração:
config('REPO_OWNER'),config('REPO_NAME')econfig('TOKEN')leem os valores do.env. - Opções do click: cada
@click.optionvira um parâmetro de linha de comando. Opromptfaz o click perguntar o valor se você não passar a opção, e ohelpaparece no--help. A opção--assignee(a pessoa associada à issue) ficou comentada no vídeo, mas a função já está pronta para recebê-la. - URL e cabeçalho: a issue é criada com um
POSTemhttps://api.github.com/repos/{dono}/{repositório}/issues, autenticado com o cabeçalhoAuthorization: token SEU_TOKEN. - Labels:
labels.split(',')transforma'feature,bug'na lista['feature', 'bug'], que é o formato que a API espera. - Resposta: a API devolve o status
201quando a issue é criada. Do JSON da resposta pegamos onumber, o número da issue. write_file: abre o arquivo de tarefas no modo'a'(append, acrescenta no fim) e escreve a issue com uma caixinha[ ], as labels, a descrição e, por fim, o comando que será usado para fechar a issue. Troque/home/seu-usuario/tarefas.txtpelo caminho da sua pasta pessoal (no vídeo é/home/regis/tarefas.txt).
No comando escrito no arquivo, g é um alias do git (no Linux, alias g=git no seu ~/.bashrc) e g co é um alias do git para commit. O make lint será explicado num vídeo seguinte, na dica do Makefile. Ao fazer o commit com close #número na mensagem, o GitHub fecha a issue automaticamente.
Usando o script
Veja as opções com --help:
python cli/create_issue.py --help
Usage: create_issue.py [OPTIONS]
Cria issue no github.com
Options:
--title TEXT Digite o título.
--body TEXT Digite a descrição.
--labels TEXT Digite as labels.
--help Show this message and exit.
Como o script grava no arquivo de tarefas, crie-o antes, na sua pasta pessoal:
touch ~/tarefas.txt
Então digite:
python cli/create_issue.py \
--title='' \
--body='' \
--labels='feature'
No vídeo, o teste foi este:
python cli/create_issue.py --title='Teste' \
--body='Lorem ipsum' \
--labels='test'
Successfully created Issue "Teste"
E o arquivo de tarefas ficou assim:
cat ~/tarefas.txt
---
[ ] 92 - Teste
test
Lorem ipsum
make lint; g add . ; g co -m 'Teste. close #92'; g push
No GitHub, a issue #92 Teste aparece na aba Issues do repositório, com a label test e a descrição Lorem ipsum.
Se você não passar as opções, o click pergunta cada uma (Title:, Description:, Labels:).
Versionando sem o token
O .env (com o token) e a pasta .venv não podem ir para o repositório. Crie o .gitignore:
# .gitignore
.env
.venv
Confira com git status que o .env não aparece mais na lista, e faça o commit:
git status
git add .
git commit -m 'Criando Issues com API do Github. close #91'
git push --set-upstream origin aula01
No vídeo, o trabalho foi feito na branch aula01. Como ela ainda não existia no GitHub, o primeiro git push deu o erro fatal: The current branch aula01 has no upstream branch., e por isso foi preciso usar --set-upstream origin aula01. O commit com close #91 fechou a issue #91 Criando Issues com API do Github.
Conclusão
Com cerca de 70 linhas de Python você cria issues no GitHub sem sair do terminal e ainda mantém uma lista de tarefas local com o comando de commit pronto. Nas próximas dicas, cada passo do projeto vai começar com uma issue criada por este script.
Observação: hoje o GitHub recomenda os tokens fine-grained e o cabeçalho Authorization: Bearer SEU_TOKEN; o token clássico com token SEU_TOKEN, usado no vídeo, continua funcionando.