11import subprocess
22from pathlib import Path
3- from typing import List
3+ from typing import List , Tuple , cast , TextIO
44import yaml
5+ import i18n
6+ import os
57
68from . import os_utils , print_utils , env_vars
79
1416)
1517
1618
17- def assert_installed (install_path : Path ):
19+ def assert_installed (install_path : Path ) -> None :
1820 compose_path = install_path / "docker-compose.yml"
1921
2022 if not compose_path .is_file ():
@@ -24,7 +26,9 @@ def assert_installed(install_path: Path):
2426 )
2527
2628
27- def run (install_path : Path , commands : List [str ]):
29+ def run (install_path : Path , commands : List [str ]) -> None :
30+ _assert_has_docker_permissions ()
31+
2832 compose_path = install_path / "docker-compose.yml"
2933
3034 full_command = ["docker-compose" , "--file" , str (compose_path )]
@@ -42,7 +46,9 @@ def run(install_path: Path, commands: List[str]):
4246 os_utils .run (full_command + commands )
4347
4448
45- def download (target : Path , version = "latest" ):
49+ def download (target : Path , version : str = "latest" ) -> None :
50+ _assert_has_write_permissions (target .parent )
51+
4652 subdomain , auth_flags , version = check_download_version (version = version )
4753
4854 url = BRAINFRAME_DOCKER_COMPOSE_URL .format (
@@ -58,7 +64,9 @@ def download(target: Path, version="latest"):
5864 os_utils .give_brainframe_group_rw_access ([target ])
5965
6066
61- def check_download_version (version = "latest" ):
67+ def check_download_version (
68+ version : str = "latest" ,
69+ ) -> Tuple [str , List [str ], str ]:
6270 subdomain = ""
6371 auth_flags = []
6472
@@ -87,14 +95,57 @@ def check_download_version(version="latest"):
8795 stdout = subprocess .PIPE ,
8896 encoding = "utf-8" ,
8997 )
90- version = result .stdout .readline ().strip ()
98+ # stdout is a file-like object opened in text mode when the encoding
99+ # argument is "utf-8"
100+ stdout = cast (TextIO , result .stdout )
101+ version = stdout .readline ().strip ()
91102
92103 return subdomain , auth_flags , version
93104
94105
95- def check_existing_version (install_path : Path ):
106+ def check_existing_version (install_path : Path ) -> str :
96107 compose_path = install_path / "docker-compose.yml"
97108 compose = yaml .load (compose_path .read_text (), Loader = yaml .SafeLoader )
98109 version = compose ["services" ]["core" ]["image" ].split (":" )[- 1 ]
99110 version = "v" + version
100111 return version
112+
113+
114+ def _assert_has_docker_permissions () -> None :
115+ """Fails if the user does not have permissions to interact with Docker"""
116+ if not (os_utils .is_root () or os_utils .currently_in_group ("docker" )):
117+ error_message = (
118+ i18n .t ("general.docker-bad-permissions" )
119+ + "\n "
120+ + _group_recommendation_message ("docker" )
121+ )
122+
123+ print_utils .fail (error_message )
124+
125+
126+ def _assert_has_write_permissions (path : Path ) -> None :
127+ """Fails if the user does not have write access to the given path."""
128+ if os .access (path , os .W_OK ):
129+ return
130+
131+ error_message = i18n .t ("general.file-bad-write-permissions" , path = path )
132+ error_message += "\n "
133+
134+ if path .stat ().st_gid == os_utils .BRAINFRAME_GROUP_ID :
135+ error_message += " " + _group_recommendation_message ("brainframe" )
136+ else :
137+ error_message += " " + i18n .t (
138+ "general.unexpected-group-for-file" , path = path , group = "brainframe"
139+ )
140+
141+ print_utils .fail (error_message )
142+
143+
144+ def _group_recommendation_message (group : str ) -> str :
145+ if os_utils .added_to_group ("brainframe" ):
146+ # The user is in the group, they just need to restart
147+ return i18n .t ("general.restart-for-group-access" , group = group )
148+ else :
149+ # The user is not in the group, so they need to either add
150+ # themselves or use sudo
151+ return i18n .t ("general.retry-as-root-or-group" , group = group )
0 commit comments