-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
631784a
commit b76b216
Showing
3 changed files
with
167 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#!/usr/bin/env python3 | ||
|
||
"""Launcher for AlgoBOWL CLI. | ||
This is a small little launcher script for the AlgoBOWL CLI. It launches the | ||
CLI creating a Python virtual environment and installing it from GitHub. The | ||
launcher will update the CLI on startup every 24 hours. By design, this has no | ||
dependencies other than Python 3.8+ and is a single file. You should be able to | ||
"chmod +x" this script and put it in your PATH, or put it in your team's Git | ||
repo for everyone on your team to use. | ||
This launcher can be downloaded from: | ||
https://raw.githubusercontent.com/jackrosenthal/algobowl/main/cli_launcher.py | ||
All command line arguments are passed as-is to the real AlgoBOWL CLI. It can | ||
minimally be configured via environment variables: | ||
- ALGOBOWL_VENV: Path to the virtual environment to use (by default, create one | ||
in the XDG cache directory). | ||
- ALGOBOWL_FORCE_UPDATE: Set to 1 to force the launcher to re-build the virtual | ||
environment. | ||
- ALGOBOWL_NO_UPDATE: Set to 1 to force the launcher to not update the virtual | ||
environment. | ||
""" | ||
|
||
import datetime | ||
import os | ||
import subprocess | ||
import sys | ||
import venv | ||
from pathlib import Path | ||
|
||
assert sys.version_info >= (3, 8), "AlgoBOWL CLI requires Python 3.8+" | ||
|
||
|
||
def quiet_run(argv) -> None: | ||
"""Run a command, staying quiet unless there's an error.""" | ||
try: | ||
subprocess.run( | ||
argv, check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT | ||
) | ||
except subprocess.CalledProcessError as e: | ||
print(f"Command failed ({argv})!", file=sys.stderr) | ||
sys.stderr.write(subprocess.stdout) | ||
raise | ||
|
||
|
||
def get_cache_dir() -> Path: | ||
"""Get the XDG-specified cache directory.""" | ||
xdg_cache_home = os.environ.get("XDG_CACHE_HOME") | ||
if xdg_cache_home: | ||
return Path(xdg_cache_home) | ||
return Path.home() / ".cache" | ||
|
||
|
||
def get_venv_dir() -> Path: | ||
"""Get the path to the virtual environment to use.""" | ||
venv_dir = os.environ.get("ALGOBOWL_VENV") | ||
if venv_dir: | ||
return Path(venv_dir) | ||
|
||
return ( | ||
get_cache_dir() | ||
/ "algobowl" | ||
/ f"venv-{sys.version_info.major}.{sys.version_info.minor}" | ||
) | ||
|
||
|
||
def venv_cmd(executable: str) -> Path: | ||
"""Get the path to a command in the virtual environment.""" | ||
return get_venv_dir() / "bin" / executable | ||
|
||
|
||
def build_venv() -> None: | ||
"""Build the virtual environment.""" | ||
venv.EnvBuilder( | ||
system_site_packages=False, | ||
clear=bool(os.environ.get("ALGOBOWL_FORCE_UPDATE")), | ||
symlinks=True, | ||
with_pip=True, | ||
).create(get_venv_dir()) | ||
quiet_run([venv_cmd("pip"), "install", "--upgrade", "pip"]) | ||
url = os.environ.get( | ||
"ALGOBOWL_URL", | ||
"https://github.com/jackrosenthal/algobowl/archive/refs/heads/main.zip", | ||
) | ||
quiet_run([venv_cmd("pip"), "install", "--upgrade", url]) | ||
|
||
|
||
def update_venv() -> None: | ||
"""Build the virtual environment if necessary.""" | ||
if os.environ.get("ALGOBOWL_NO_UPDATE"): | ||
return | ||
update_file = get_venv_dir() / "UPDATE" | ||
force_update = os.environ.get("ALGOBOWL_FORCE_UPDATE") | ||
now = datetime.datetime.now() | ||
if not force_update and update_file.exists(): | ||
last_update = datetime.datetime.fromisoformat( | ||
update_file.read_text(encoding="ascii") | ||
) | ||
required_update = last_update + datetime.timedelta(hours=24) | ||
if required_update > now: | ||
return | ||
build_venv() | ||
update_file.write_text(now.isoformat(), encoding="ascii") | ||
|
||
|
||
def main(): | ||
"""The main function.""" | ||
update_venv() | ||
algobowl = venv_cmd("algobowl") | ||
os.execv(venv_cmd("algobowl"), sys.argv) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |