gitpr-cli 0.0.12__tar.gz → 0.0.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 0.0.12
3
+ Version: 0.0.13
4
4
  Summary: Automação de PRs, Commits e Code Review com IA (Gemini e DeepSeek)
5
5
  Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
6
6
  Requires-Python: >=3.10
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitpr-cli
3
- Version: 0.0.12
3
+ Version: 0.0.13
4
4
  Summary: Automação de PRs, Commits e Code Review com IA (Gemini e DeepSeek)
5
5
  Author-email: Natan Fiuza <contato@natanfiuza.dev.br>
6
6
  Requires-Python: >=3.10
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gitpr-cli"
7
- version = "0.0.12"
7
+ version = "0.0.13"
8
8
  description = "Automação de PRs, Commits e Code Review com IA (Gemini e DeepSeek)"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -5,7 +5,7 @@ import sys
5
5
 
6
6
  # Importações dos nossos módulos internos atualizadas
7
7
  from src.config import setup_environment, check_internet_connection, get_ai_provider
8
- from src.updater import check_and_update, __version__
8
+ from src.updater import check_and_update, __version__, print_update_notice
9
9
  from src.core import (
10
10
  get_git_diff,
11
11
  get_git_full_diff,
@@ -109,17 +109,10 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, q
109
109
  check_internet_connection()
110
110
 
111
111
  # Módulo de Atualização (Pip-Aware)
112
- if update:
113
- if is_compiled:
114
- click.secho("🔍 Verificando atualizações no GitHub...", fg="cyan")
115
- check_and_update()
116
- else:
117
- click.secho("💡 Como você instalou via PIP, atualize rodando: pip install --upgrade gitpr-cli", fg="cyan", bold=True)
112
+ if update:
113
+ click.secho("🔍 Verificando atualizações...", fg="cyan")
114
+ check_and_update()
118
115
  return
119
- else:
120
- # Verificação automática em segundo plano a cada uso (Apenas binário)
121
- if is_compiled:
122
- check_and_update()
123
116
 
124
117
  # Opção --skill: Gera o template e encerra
125
118
  if skill:
@@ -294,6 +287,9 @@ def cli(commit, review, fullreview, linter, skill, update, installhooks, hook, q
294
287
  click.secho(f"\n✅ Sucesso! O arquivo '{output_filename}' foi gerado na pasta atual.", fg="green", bold=True)
295
288
  except Exception as e:
296
289
  click.secho(f"\n❌ Erro ao guardar o arquivo: {e}", fg="red")
290
+
291
+ if not quiet:
292
+ print_update_notice()
297
293
 
298
294
  if __name__ == "__main__":
299
295
  cli()
@@ -0,0 +1,167 @@
1
+ import urllib.request
2
+ import json
3
+ import os
4
+ import sys
5
+ import shutil
6
+ import click
7
+ from datetime import datetime
8
+
9
+ # Versão atual do seu executável local (Atualize isso a cada novo build!)
10
+ __version__ = "0.0.13"
11
+ GITHUB_API_URL = "https://api.github.com/repos/natanfiuza/gitpr/releases/latest"
12
+ PYPI_API_URL = "https://pypi.org/pypi/gitpr-cli/json"
13
+
14
+ def get_gitpr_dir():
15
+ """Retorna o diretório ~/.gitpr/"""
16
+ return os.path.join(os.path.expanduser("~"), ".gitpr")
17
+
18
+ def get_update_cache_file():
19
+ """Retorna o caminho do arquivo de cache de atualizações."""
20
+ return os.path.join(get_gitpr_dir(), "update_cache.json")
21
+
22
+ def parse_version(version_str):
23
+ """Converte 'v0.1.0' ou '0.1.0' em uma tupla (0, 1, 0) para matemática de versões."""
24
+ clean_version = version_str.lower().replace("v", "")
25
+ try:
26
+ return tuple(map(int, clean_version.split(".")))
27
+ except ValueError:
28
+ return (0, 0, 0)
29
+
30
+ def get_latest_remote_version(is_compiled):
31
+ """Busca a última versão na API correta (PyPI ou GitHub) com cache diário."""
32
+ cache_file = get_update_cache_file()
33
+ today = datetime.now().strftime("%Y-%m-%d")
34
+
35
+ # 1. Tenta ler do cache para não atrasar o terminal do usuário
36
+ if os.path.exists(cache_file):
37
+ try:
38
+ with open(cache_file, "r") as f:
39
+ cache_data = json.load(f)
40
+ if cache_data.get("date") == today:
41
+ return cache_data.get("version"), cache_data.get("download_url")
42
+ except Exception:
43
+ pass
44
+
45
+ # 2. Busca na Web se o cache expirou
46
+ latest_version = ""
47
+ download_url = ""
48
+
49
+ try:
50
+ if is_compiled:
51
+ # Busca no GitHub (Para executável)
52
+ req = urllib.request.Request(GITHUB_API_URL, headers={'User-Agent': 'GitPR-Updater'})
53
+ with urllib.request.urlopen(req, timeout=3) as response:
54
+ data = json.loads(response.read().decode())
55
+ latest_version = data.get("tag_name", "").replace("v", "")
56
+
57
+ assets = data.get("assets", [])
58
+ exe_asset = next((a for a in assets if a.get("name") == "gitpr.exe"), None)
59
+ if exe_asset:
60
+ download_url = exe_asset.get("browser_download_url")
61
+ else:
62
+ # Busca no PyPI (Para instalação via PIP)
63
+ req = urllib.request.Request(PYPI_API_URL, headers={'User-Agent': 'GitPR-Updater'})
64
+ with urllib.request.urlopen(req, timeout=3) as response:
65
+ data = json.loads(response.read().decode())
66
+ latest_version = data.get("info", {}).get("version", "")
67
+
68
+ # 3. Salva no cache
69
+ if latest_version:
70
+ os.makedirs(get_gitpr_dir(), exist_ok=True)
71
+ with open(cache_file, "w") as f:
72
+ json.dump({"date": today, "version": latest_version, "download_url": download_url}, f)
73
+
74
+ except Exception:
75
+ pass # Falha silenciosa em caso de falta de internet
76
+
77
+ return latest_version, download_url
78
+
79
+ def print_update_notice():
80
+ """Imprime o bloco de aviso estilo PIP no fim da execução."""
81
+ is_compiled = getattr(sys, 'frozen', False)
82
+ latest_version, _ = get_latest_remote_version(is_compiled)
83
+
84
+ if not latest_version:
85
+ return
86
+
87
+ current_v = parse_version(__version__)
88
+ latest_v = parse_version(latest_version)
89
+
90
+ if latest_v > current_v:
91
+ click.echo("")
92
+ click.secho(f"[notice] A new release of gitpr is available: {__version__} -> {latest_version}", fg="yellow", dim=True)
93
+ if is_compiled:
94
+ click.secho(f"[notice] To update, run: gitpr --update", fg="yellow", dim=True)
95
+ else:
96
+ click.secho(f"[notice] To update, run: pip install --upgrade gitpr-cli", fg="yellow", dim=True)
97
+ click.echo("")
98
+
99
+ def check_and_update():
100
+ """Função disparada apenas quando o usuário força a flag --update."""
101
+ is_compiled = getattr(sys, 'frozen', False)
102
+
103
+ if not is_compiled:
104
+ click.secho("💡 Como você instalou via PIP, atualize rodando: pip install --upgrade gitpr-cli", fg="cyan", bold=True)
105
+ return
106
+
107
+ latest_version, download_url = get_latest_remote_version(is_compiled=True)
108
+
109
+ if not latest_version or not download_url:
110
+ click.secho("❌ Não foi possível verificar atualizações no momento.", fg="red")
111
+ return
112
+
113
+ current_v = parse_version(__version__)
114
+ latest_v = parse_version(latest_version)
115
+
116
+ if latest_v > current_v:
117
+ click.secho(f"\n🚀 Nova versão do GitPR encontrada (v{latest_version})!", fg="green", bold=True)
118
+ click.secho("Baixando atualização em segundo plano...", fg="cyan")
119
+ _perform_hot_swap(download_url)
120
+ else:
121
+ click.secho("✅ Você já está usando a versão mais recente do GitPR.", fg="green")
122
+
123
+ def _perform_hot_swap(download_url):
124
+ """Faz o download e substitui o executável atual."""
125
+ current_exe = sys.executable
126
+ old_exe = current_exe + ".old"
127
+
128
+ try:
129
+ if os.path.exists(old_exe):
130
+ os.remove(old_exe)
131
+ os.rename(current_exe, old_exe)
132
+ urllib.request.urlretrieve(download_url, current_exe)
133
+ click.secho(f"✅ Atualização concluída com sucesso! Na próxima execução você já usará a nova versão.\n", fg="green", bold=True)
134
+ except Exception as e:
135
+ click.secho(f"❌ Falha ao aplicar atualização: {e}", fg="red")
136
+ if os.path.exists(old_exe) and not os.path.exists(current_exe):
137
+ os.rename(old_exe, current_exe)
138
+ """Faz o download e substitui o executável atual (Hot-Swap)."""
139
+ current_exe = sys.executable
140
+
141
+ # Se não estiver rodando como executável compilado (PyInstaller), aborta o update
142
+ if not getattr(sys, 'frozen', False):
143
+ click.secho("⚠️ Aviso: Rodando via script Python. O Auto-Update funciona apenas no executável compilado.", fg="yellow")
144
+ return
145
+
146
+ old_exe = current_exe + ".old"
147
+
148
+ try:
149
+ # 1. Renomeia o executável atual que está em uso
150
+ if os.path.exists(old_exe):
151
+ os.remove(old_exe) # Remove restos antigos se existirem
152
+ os.rename(current_exe, old_exe)
153
+
154
+ # 2. Faz o download direto para o caminho original
155
+ urllib.request.urlretrieve(download_url, current_exe)
156
+
157
+ # 3. Salva o novo hash
158
+ with open(sha_file, "w") as f:
159
+ f.write(new_digest)
160
+
161
+ click.secho(f"✅ Atualização concluída com sucesso! Na próxima execução você já usará a nova versão.\n", fg="green", bold=True)
162
+
163
+ except Exception as e:
164
+ # Se algo falhar na renomeação/download, tenta desfazer a bagunça
165
+ click.secho(f"❌ Falha ao aplicar atualização: {e}", fg="red")
166
+ if os.path.exists(old_exe) and not os.path.exists(current_exe):
167
+ os.rename(old_exe, current_exe)
@@ -1,90 +0,0 @@
1
- import urllib.request
2
- import json
3
- import os
4
- import sys
5
- import shutil
6
- import click
7
-
8
- # Versão atual do seu executável local (Atualize isso a cada novo build!)
9
- __version__ = "0.0.12"
10
- GITHUB_API_URL = "https://api.github.com/repos/natanfiuza/gitpr/releases/latest"
11
-
12
- def get_gitpr_dir():
13
- """Retorna o diretório ~/.gitpr/"""
14
- return os.path.join(os.path.expanduser("~"), ".gitpr")
15
-
16
- def check_and_update():
17
- """Verifica na API do GitHub se há um novo release com um hash diferente."""
18
- try:
19
- # Timeout curto para não travar a CLI se a API do GitHub estiver lenta
20
- req = urllib.request.Request(GITHUB_API_URL, headers={'User-Agent': 'GitPR-Updater'})
21
- with urllib.request.urlopen(req, timeout=3) as response:
22
- data = json.loads(response.read().decode())
23
-
24
- # Pega a versão remota (apenas para log/exibição)
25
- remote_version = data.get("tag_name", "").replace("v", "")
26
-
27
- # Procura o executável nos assets
28
- assets = data.get("assets", [])
29
- exe_asset = next((a for a in assets if a.get("name") == "gitpr.exe"), None)
30
-
31
- if not exe_asset:
32
- return # Nenhum executável encontrado no release mais recente
33
-
34
- remote_digest = exe_asset.get("digest", "").replace("sha256:", "")
35
- download_url = exe_asset.get("browser_download_url")
36
-
37
- if not remote_digest or not download_url:
38
- return
39
-
40
- # Compara com o hash local
41
- gitpr_dir = get_gitpr_dir()
42
- sha_file = os.path.join(gitpr_dir, ".sha256")
43
- local_digest = ""
44
-
45
- if os.path.exists(sha_file):
46
- with open(sha_file, "r") as f:
47
- local_digest = f.read().strip()
48
-
49
- # Se os hashes forem diferentes, dispara o update!
50
- if remote_digest != local_digest:
51
- click.secho(f"\n🚀 Nova versão do GitPR encontrada (v{remote_version})!", fg="green", bold=True)
52
- click.secho("Baixando atualização em segundo plano...", fg="cyan")
53
- _perform_hot_swap(download_url, remote_digest, sha_file)
54
-
55
- except Exception as e:
56
- # Silencia erros de timeout ou rede para não atrapalhar o fluxo do usuário
57
- click.secho(f"Debug Updater: {e}", fg="red") # Descomente para debugar
58
- pass
59
-
60
- def _perform_hot_swap(download_url, new_digest, sha_file):
61
- """Faz o download e substitui o executável atual (Hot-Swap)."""
62
- current_exe = sys.executable
63
-
64
- # Se não estiver rodando como executável compilado (PyInstaller), aborta o update
65
- if not getattr(sys, 'frozen', False):
66
- click.secho("⚠️ Aviso: Rodando via script Python. O Auto-Update funciona apenas no executável compilado.", fg="yellow")
67
- return
68
-
69
- old_exe = current_exe + ".old"
70
-
71
- try:
72
- # 1. Renomeia o executável atual que está em uso
73
- if os.path.exists(old_exe):
74
- os.remove(old_exe) # Remove restos antigos se existirem
75
- os.rename(current_exe, old_exe)
76
-
77
- # 2. Faz o download direto para o caminho original
78
- urllib.request.urlretrieve(download_url, current_exe)
79
-
80
- # 3. Salva o novo hash
81
- with open(sha_file, "w") as f:
82
- f.write(new_digest)
83
-
84
- click.secho(f"✅ Atualização concluída com sucesso! Na próxima execução você já usará a nova versão.\n", fg="green", bold=True)
85
-
86
- except Exception as e:
87
- # Se algo falhar na renomeação/download, tenta desfazer a bagunça
88
- click.secho(f"❌ Falha ao aplicar atualização: {e}", fg="red")
89
- if os.path.exists(old_exe) and not os.path.exists(current_exe):
90
- os.rename(old_exe, current_exe)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes