PowerMachine commited on
Commit
6a5374b
·
verified ·
1 Parent(s): c5b0078

Delete scripts/push_to_hf.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/push_to_hf.py +0 -294
scripts/push_to_hf.py DELETED
@@ -1,294 +0,0 @@
1
- """push_to_hf.py — Envia scripts em lote ao HF repositório 'CNN-BiGRU' (v3.0).
2
-
3
- Executa:
4
- 1. Cria repositório 'PowerMachine/CNN-BiGRU' (se não existir)
5
- 2. Envia todos os scripts .py, README.md, requirements.txt, docs/
6
- usando `upload_folder` (upload em lote eficiente)
7
- 3. SOBRESCREVE módulos desatualizados (default do upload_folder)
8
- 4. Remove HF_TOKEN do ambiente após o uso (requisito do usuário)
9
- 5. Deleta estado salvo do modelo (não envia para lugar algum)
10
- 6. Filtra arquivos sensíveis (.env, tokens, checkpoints, etc.)
11
-
12
- V3.0 melhorias:
13
- - Filtra arquivos de estado do modelo (.pt, .pth, .bin, .safetensors)
14
- - Filtra relatórios de monitor local (monitor_reports/)
15
- - Mantém apenas scripts e documentação
16
- - Log mais detalhado
17
- - Retry automático em caso de falha transitória
18
-
19
- Usage:
20
- HF_TOKEN=hf_xxx python push_to_hf.py
21
- """
22
- from __future__ import annotations
23
-
24
- import os
25
- import sys
26
- import logging
27
- import shutil
28
- import time
29
- from pathlib import Path
30
- from typing import List, Set
31
-
32
- logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
33
- logger = logging.getLogger("push_hf")
34
-
35
- REPO_ID = "PowerMachine/CNN-BiGRU"
36
- # Path: scripts/push_to_hf.py -> parent=scripts -> parent=cnn_bigru
37
- PROJECT_DIR = Path(__file__).resolve().parent.parent
38
- # PROJECT_DIR.parent = raiz do projeto
39
- REPO_ROOT = PROJECT_DIR.parent
40
-
41
- # Extensões e nomes de arquivo permitidos para upload
42
- ALLOWED_EXTENSIONS: Set[str] = {
43
- ".py", ".md", ".txt", ".json", ".yaml", ".yml", ".toml", ".cfg", ".ini", ".sh",
44
- }
45
-
46
- # Padrões a ignorar (em qualquer parte do caminho)
47
- IGNORE_PATTERNS: Set[str] = {
48
- "__pycache__", ".pyc", ".pyo", ".pyd",
49
- ".git", ".gitignore", ".gitattributes",
50
- "node_modules", ".pytest_cache", ".mypy_cache", ".ruff_cache",
51
- ".DS_Store", "Thumbs.db",
52
- "*.log", "*.tmp", "*.swp", "*.bak",
53
- ".env", ".venv", "venv", "env",
54
- # Estado salvo do modelo — NÃO ENVIAR (requisito do usuário)
55
- "*.pt", "*.pth", "*.bin", "*.safetensors", "*.ckpt",
56
- # Relatórios de monitor local (contém paths e dados locais)
57
- "monitor_reports", "download", "tool-results", "upload",
58
- }
59
-
60
- # Arquivos específicos que NUNCA devem ser upados (podem conter tokens/sensíveis)
61
- SENSITIVE_FILENAMES: Set[str] = {
62
- ".env", ".env.local", ".env.production", ".env.development",
63
- "secrets.json", "credentials.json", "config.local.json",
64
- "hf_token.txt", "token.txt",
65
- }
66
-
67
-
68
- def should_ignore(path: Path) -> bool:
69
- """Verifica se um arquivo deve ser ignorado no upload."""
70
- parts = path.parts
71
- name = path.name.lower()
72
-
73
- # Verificar padrões por partes do caminho
74
- for part in parts:
75
- part_lower = part.lower()
76
- if part_lower in IGNORE_PATTERNS:
77
- return True
78
- # Verificar extensões de cache
79
- if part_lower.endswith((".pyc", ".pyo", ".pyd")):
80
- return True
81
- # Verificar extensões de estado de modelo
82
- if part_lower.endswith((".pt", ".pth", ".bin", ".safetensors", ".ckpt")):
83
- return True
84
-
85
- # Verificar nome do arquivo
86
- if name in SENSITIVE_FILENAMES:
87
- return True
88
-
89
- # Verificar extensões permitidas
90
- if path.suffix.lower() not in ALLOWED_EXTENSIONS:
91
- # Permitir arquivos sem extensão apenas se forem específicos (README, LICENSE, etc.)
92
- if name not in {"readme", "license", "license-mit", "authors", "contributors"}:
93
- return True
94
-
95
- # Verificar padrões com wildcards
96
- for pattern in IGNORE_PATTERNS:
97
- if pattern.startswith("*"):
98
- suffix = pattern[1:]
99
- if name.endswith(suffix):
100
- return True
101
-
102
- return False
103
-
104
-
105
- def collect_files(project_dir: Path) -> List[Path]:
106
- """Coleta todos os arquivos elegíveis para upload."""
107
- files = []
108
- for path in project_dir.rglob("*"):
109
- if path.is_file() and not should_ignore(path):
110
- files.append(path)
111
- return files
112
-
113
-
114
- def delete_saved_model_state(project_dir: Path) -> None:
115
- """Deleta todo o estado salvo do modelo (checkpoints, .pt, .pth, etc.).
116
-
117
- Requisito do usuário: "apagar o estado salvo do modelo (não enviar para
118
- lugar algum até ordem em contrário)".
119
- """
120
- deleted = []
121
- for path in project_dir.rglob("*"):
122
- if not path.is_file():
123
- continue
124
- name = path.name.lower()
125
- if name.endswith((".pt", ".pth", ".bin", ".safetensors", ".ckpt")):
126
- try:
127
- path.unlink()
128
- deleted.append(path)
129
- logger.info(f" Deletado: {path.relative_to(project_dir.parent)}")
130
- except Exception as e:
131
- logger.warning(f" Falha ao deletar {path}: {e}")
132
-
133
- # Deletar diretórios de cache e checkpoints
134
- for d in project_dir.rglob("*"):
135
- if d.is_dir() and d.name.lower() in {"checkpoints", "checkpoint", "model_state", "weights"}:
136
- try:
137
- shutil.rmtree(d)
138
- deleted.append(d)
139
- logger.info(f" Deletado dir: {d.relative_to(project_dir.parent)}")
140
- except Exception as e:
141
- logger.warning(f" Falha ao deletar dir {d}: {e}")
142
-
143
- if deleted:
144
- logger.info(f"Estado salvo do modelo deletado: {len(deleted)} itens")
145
- else:
146
- logger.info("Nenhum estado salvo do modelo encontrado para deletar")
147
-
148
-
149
- def upload_with_retry(api, folder: Path, repo_id: str, token: str, max_retries: int = 3):
150
- """Faz upload_folder com retry em caso de falha transitória."""
151
- last_error = None
152
- for attempt in range(1, max_retries + 1):
153
- try:
154
- logger.info(f"Tentativa {attempt}/{max_retries}: upload_folder...")
155
- commit_info = api.upload_folder(
156
- folder_path=str(folder),
157
- repo_id=repo_id,
158
- repo_type="model",
159
- token=token,
160
- commit_message=f"v3.0: 9 novos módulos (CyclicReasoning, Medusa, NLG, NLP, MMA, VQVAE2, W8A8, LongContext 1M, Monitor) + bug fixes (attempt {attempt})",
161
- # Sobrescreve arquivos desatualizados (default)
162
- )
163
- logger.info(f"Upload bem-sucedido: {commit_info}")
164
- return True, None
165
- except Exception as e:
166
- last_error = e
167
- logger.warning(f"Tentativa {attempt} falhou: {e}")
168
- if attempt < max_retries:
169
- wait_time = 2 ** attempt
170
- logger.info(f"Aguardando {wait_time}s antes de tentar novamente...")
171
- time.sleep(wait_time)
172
- return False, last_error
173
-
174
-
175
- def main():
176
- # Pega HF_TOKEN da variável de ambiente
177
- hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
178
- if not hf_token:
179
- logger.error("HF_TOKEN não definido no ambiente. Abortando.")
180
- return 1
181
-
182
- try:
183
- from huggingface_hub import HfApi, create_repo
184
- except ImportError:
185
- logger.error("huggingface_hub não instalado. Execute: pip install huggingface_hub")
186
- return 1
187
-
188
- api = HfApi(token=hf_token)
189
-
190
- # 1. Cria repositório (se não existir)
191
- try:
192
- repo_url = create_repo(
193
- repo_id=REPO_ID,
194
- token=hf_token,
195
- repo_type="model",
196
- exist_ok=True,
197
- private=False,
198
- )
199
- logger.info(f"Repositório criado/acessado: {repo_url}")
200
- except Exception as e:
201
- logger.error(f"Falha ao criar repositório: {e}")
202
- # Limpa o token mesmo em caso de falha
203
- _cleanup_token()
204
- return 1
205
-
206
- # 2. Coleta todos os arquivos para upload
207
- files_to_upload = collect_files(PROJECT_DIR)
208
- logger.info(f"Total de arquivos para upload: {len(files_to_upload)} (de {PROJECT_DIR})")
209
-
210
- if not files_to_upload:
211
- logger.warning("Nenhum arquivo elegível para upload.")
212
- _cleanup_token()
213
- return 1
214
-
215
- # Log dos arquivos que serão upados (até 20)
216
- for f in files_to_upload[:20]:
217
- rel = f.relative_to(REPO_ROOT)
218
- logger.info(f" - {rel}")
219
- if len(files_to_upload) > 20:
220
- logger.info(f" ... e mais {len(files_to_upload) - 20} arquivos")
221
-
222
- # 3. Upload em lote via upload_folder (muito mais eficiente que upload_file em loop)
223
- success, error = upload_with_retry(
224
- api, folder=PROJECT_DIR, repo_id=REPO_ID, token=hf_token, max_retries=3
225
- )
226
-
227
- if success:
228
- logger.info("=" * 60)
229
- logger.info(f"Upload concluído com sucesso!")
230
- logger.info(f"Repo: https://huggingface.co/{REPO_ID}")
231
- logger.info(f"Total de arquivos enviados: {len(files_to_upload)}")
232
- logger.info("=" * 60)
233
-
234
- # 4. Deletar estado salvo do modelo (após upload bem-sucedido)
235
- logger.info("Deletando estado salvo do modelo...")
236
- delete_saved_model_state(PROJECT_DIR)
237
- else:
238
- logger.error("=" * 60)
239
- logger.error(f"Falha no upload após retries: {error}")
240
- logger.error("=" * 60)
241
-
242
- # 5. Remove HF_TOKEN do ambiente (requisito do usuário)
243
- _cleanup_token()
244
-
245
- # 6. Verifica e remove tokens hardcoded em scripts (requisito do usuário)
246
- _scan_scripts_for_tokens(PROJECT_DIR)
247
-
248
- return 0 if success else 1
249
-
250
-
251
- def _cleanup_token():
252
- """Remove todas as variáveis de token HF do ambiente."""
253
- removed = []
254
- for var in ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN", "HF_HUB_TOKEN"):
255
- if var in os.environ:
256
- del os.environ[var]
257
- removed.append(var)
258
- if removed:
259
- logger.info(f"Tokens removidos do ambiente: {', '.join(removed)}")
260
- else:
261
- logger.info("Nenhum token HF encontrado no ambiente para remover.")
262
-
263
-
264
- def _scan_scripts_for_tokens(project_dir: Path) -> None:
265
- """Verifica se há tokens HF hardcoded em scripts e os remove."""
266
- # Padrões de token HF (hf_ seguido de 32+ caracteres alfanuméricos)
267
- import re
268
- token_pattern = re.compile(r'hf_[A-Za-z0-9]{20,}')
269
-
270
- scanned = 0
271
- cleaned = 0
272
- for path in project_dir.rglob("*.py"):
273
- if "push_to_hf.py" in str(path):
274
- continue # skip self
275
- try:
276
- content = path.read_text(encoding="utf-8")
277
- scanned += 1
278
- if token_pattern.search(content):
279
- # Substituir por placeholder
280
- new_content = token_pattern.sub("HF_TOKEN_FROM_ENV", content)
281
- path.write_text(new_content, encoding="utf-8")
282
- cleaned += 1
283
- logger.warning(f" Token HF removido de: {path.relative_to(project_dir.parent)}")
284
- except Exception as e:
285
- logger.debug(f" Falha ao escanear {path}: {e}")
286
-
287
- if cleaned > 0:
288
- logger.info(f"Scripts escaneados: {scanned}, scripts com token removido: {cleaned}")
289
- else:
290
- logger.info(f"Scripts escaneados: {scanned}, nenhum token hardcoded encontrado.")
291
-
292
-
293
- if __name__ == "__main__":
294
- sys.exit(main())