Skip to content

Commit

Permalink
Auto-Updater refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
GuiBrandt committed Feb 19, 2019
1 parent 7bfab32 commit 9b2be55
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 113 deletions.
4 changes: 2 additions & 2 deletions __main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import god.log as log
import god.quotes as quotes
import god.config as config
import god.updater as updater
import god.version as version


def load_settings():
Expand Down Expand Up @@ -51,7 +51,7 @@ def main():
cli.clear()
cli.header()

updater.check_updates()
version.check_updates()
load_settings()
load_phrases()

Expand Down
17 changes: 15 additions & 2 deletions apply_update.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
"""Aplica a última atualização baixada
Esse arquivo é basicamente um `.cmd` glorificado
"""

import os
import os.path

if os.path.isdir(".update"):
os.system("move .update\\*.* .\\ >nul 2>nul")
# Atualiza tudo da pasta raíz
os.system("move /Y .update\\*.* .\\ >nul 2>nul")

# Cria e atualiza cada uma das subpastas necessárias
for subdir in os.walk(".update"):
dirname = subdir[0].replace(".update\\", '')

os.system(
f"if not exist .\\{dirname}" +
f" mkdir .\\{dirname} >nul 2>nul")

os.system(
f"move .update\\{dirname}\\*.* .\\{dirname}\\ " +
f"move /Y .update\\{dirname}\\*.* .\\{dirname}\\ " +
">nul 2>nul")

# Apaga a .update (agora vazia)
os.system("rmdir .update /s /q >nul 2>nul")

# Roda o god
os.system("start python .")
13 changes: 12 additions & 1 deletion god/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os

import god
import god.version as version
import god.config as config
import god.quotes as quotes

Expand All @@ -27,7 +28,7 @@ def header(color=Fore.BLUE):
newline()
print(color + f.renderText('G o d'))
newline()
print(color + "v2.0.0".center(width()))
print(color + version.current().center(width()))
print(flush=True)


Expand Down Expand Up @@ -95,6 +96,16 @@ def read_command():
return input().strip()


def yesno(prompt="Confirmar?"):
while True:
answer = input(f"\t{prompt} [S/n] ").lower().strip()
if answer in ['s', '', 'n']:
break

newline()
return answer.lower().strip() in ['s', '']


def interactive_header():
header(Fore.RED if god.state == 'alert' else Fore.BLUE)
print_random_phrase()
Expand Down
5 changes: 3 additions & 2 deletions god/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import sys

import god
import god.version as version
import god.cli as cli
import god.log as log
import god.config as config
Expand Down Expand Up @@ -45,8 +46,8 @@ def cmd_quit():

@no_arg
def cmd_help():
print(Fore.YELLOW + """
GOD v2.0.0, by PD16
print(Fore.YELLOW + f"""
GOD {version.current()}, by PD16
sm|threshold X : Define o limite de memória para X Kb
sp|process X : Define o processo monitorado para X
Expand Down
106 changes: 0 additions & 106 deletions god/updater.py

This file was deleted.

178 changes: 178 additions & 0 deletions god/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""Gerenciador de versão e atualização
Esse módulo gerencia a versão do God e faz o pareamento com a última versão
disponível no GitHub, e faz a atualização de forma automática.
"""

import requests
import zipfile
import tempfile
import io
import os
import sys
import os.path
import glob

from shutil import copyfile

import god.cli as cli
import god.log as log

# Informações do GitHub
_GITHUB_API_URL = "https://api.github.com"
_REPO = "GuiBrandt/god"

# Informação da versão atual
if os.path.isfile(".version"):
with open(".version", "r") as version_file:
_VERSION = version_file.readline().strip()
else:
_VERSION = None


def current():
"""Obtém a versão atual do God"""

return _VERSION


def check_updates():
"""Executa o procedimento de busca e instalação de atualizações"""

cli.i_am("Procurando atualizações...")

try:
latest_release = _fetch_latest_release()
except Exception as e:
log.error("fetch-releases", e)
cli.error("Falha. Verifique sua conexão com a internet.")
return

cli.info("Encontrado: " + latest_release['tag_name'])

if latest_release['tag_name'] == current():
cli.success("O god está atualizado.")
return

cli.error("O god está desatualizado!")

if cli.yesno("Atualizar?"):
update_dir = _download_update(latest_release)
_install_update(update_dir)

_VERSION = latest_release['tag_name']

with open(".version", "w") as version_file:
version_file.write(_VERSION)

# os.system("start python apply_update.py")
sys.exit(0)


def _fetch_latest_release():
"""Obtém informações da última release no GitHub em JSON"""

r = requests.get(f"{_GITHUB_API_URL}/repos/{_REPO}/releases/latest")
return r.json()


def _download_update(release):
"""Faz download e extrai o zip de uma release
Parâmetros
----------
release : json
JSON da release, retornado pelo `_fetch_latest_release`
Retorno
-------
O caminho da pasta com os arquivos do `.zip` extraídos
"""

cli.i_am("Obtendo arquivo zip...")
zip_url = release['zipball_url']
r = requests.get(zip_url, stream=True)
release_zip_file = zipfile.ZipFile(io.BytesIO(r.content))
cli.success("OK")

cli.i_am("Extraindo...")
tmp_dir = tempfile.mkdtemp()
root_dir = release_zip_file.namelist()[0]
release_zip_file.extractall(path=tmp_dir)
cli.success("OK")

tmp_dir = f"{tmp_dir}/{root_dir}".replace("\\", "/")

return tmp_dir


def _install_update(update_dir):
"""Faz a instalação de uma atualização
Isso envolve resolver dependências com pip e mover os arquivos
baixados para suas respectivas pastas.
TODO: Implementar remoção de arquivos não utilizados
"""

cli.i_am("Instalando dependências...")
requirements_file = f"{update_dir}/requirements.txt"
result = os.system(
f"pip install --upgrade -r \"{requirements_file}\" >nul 2>nul")

if result == 0:
cli.success("OK")
else:
cli.error("Falha na instalação. Abortando atualização...")
log.error("update", "Falha na instação das dependências")
return

cli.i_am("Atualizando código fonte...")
sources = _map_update_sources(update_dir)
_touch_directories(sources)

for fname, dirname in sources:
path = fname if dirname == '.' else f"{dirname}/{fname}"
copyfile(f"{update_dir}/{path}", f".update/{path}")

# O arquivo apply_update.py é especial: ele não pode atualizar ele
# mesmo
os.system("move /Y .update\\apply_update.py .")

cli.success("OK")


def _map_update_sources(update_dir):
"""Mapeia o índice de uma atualização em um formato mais usável
Parâmetros
----------
update_dir : str
Caminho da pasta onde os arquivos da atualização estão
Retorno
-------
Lista de tuplas com o nome dos arquivos (sem pasta) e o nome da
pasta onde devem ser colocados (relativo à pasta raíz do god)
"""

files = glob.glob(f"{update_dir}/**/*.py", recursive=True)
directories = map(os.path.dirname, files)

def strip_update_dir(dirname):
if dirname in update_dir: # Na prática, se é o diretório raíz
return '.'
return dirname.replace("\\", "/").replace(update_dir, '')

directories = map(strip_update_dir, directories)

return list(zip(map(os.path.basename, files), directories))


def _touch_directories(sources):
"""Cria os diretórios para salvar os arquivos da atualização"""

for _, dirname in sources:
path = f".update/{dirname}"
if not os.path.isdir(path):
os.makedirs(path)

0 comments on commit 9b2be55

Please sign in to comment.