diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cbaaff0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,165 @@ +build +updater.spec + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..27ff556 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# GitHub Release Updater + +Make an `update.exe` that checks github for new releases and updates the current directory if behind. + +## Setup +1. Have python3 +2. [Download](https://github.com/xjiro/githubreleaseupdater/archive/refs/heads/master.zip) and extract this repo +3. Run `pip install -Ur requirements.txt` + +## Configuration +- Set `repo_url` in `update.py` +- Run `.\build.cmd` +- Copy `update.exe` to your project you want to auto update + +## Usage Details +- Will only grab the first release artifact +- Expects that artifact to be a zip +- Creates and maintains an `update.txt` with the latest releases' publish timestamp + +### Downsides +I might fix these issues later if necessary +- Simply overwrites files (watch out for user configurations, default saves) +- Will redudantly download and overwrite unchanged files (no file hashing or manifest usage) +- Doesn't support architecture selection +- No configuration for installation paths +- Windows only + +## License +This project is licensed under the MIT License. diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..a10d63f --- /dev/null +++ b/build.cmd @@ -0,0 +1 @@ +pyinstaller --onefile update.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..231fa13 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyinstaller +requests diff --git a/update.py b/update.py new file mode 100644 index 0000000..61f8c61 --- /dev/null +++ b/update.py @@ -0,0 +1,71 @@ +import sys +import os +import requests +import datetime +from urllib.parse import urlparse + +repo_url = "https://github.com/xjiro/releasetest" + +parsed_url = urlparse(repo_url) +path_parts = parsed_url.path.strip('/').split('/') +username, repo = path_parts[-2:] + + +# get the last update we performed +latest = 0 +try: + with open('lastupdate.txt', 'r') as f: + latest = int(f.read()) +except: + with open('lastupdate.txt', 'w+') as f: + f.write(str(latest)) + + +# get the most recent release from github repo +def get_release(): + api = f"https://api.github.com/repos/{username}/{repo}/releases/latest" + response = requests.get(api) + if response.status_code == 200: + return response.json() + return None + + +release = None +try: + release = get_release() +except Exception as e: + pass + +if not release: + print("Failed to get release info") + sys.exit(0) + +# check if the latest release is newer than the last checked +published = datetime.datetime.strptime(release['published_at'], '%Y-%m-%dT%H:%M:%SZ') +published_unix = int(published.timestamp()) + +if published_unix <= latest: + print("We are up to date") + sys.exit(0) + +print(f"New release from {published}, updating...") + +with requests.get(release['assets'][0]['browser_download_url'], stream=True) as r: + r.raise_for_status() + with open('release.zip', 'wb') as f: + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) + +# unzip release.zip +import zipfile +with zipfile.ZipFile('release.zip', 'r') as zip_ref: + zip_ref.extractall('.') + +print(f"Update complete !") + +# clean up +os.remove('release.zip') + +# update the release info +with open('lastupdate.txt', 'w') as f: + f.write(str(published_unix))