Skip to content

Commit 4ff28e1

Browse files
committed
Setup download for hands-on
1 parent 51cd464 commit 4ff28e1

File tree

2 files changed

+120
-15
lines changed

2 files changed

+120
-15
lines changed

app/commands/download.py

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
exercise_exists,
2626
get_gitmastery_file_path,
2727
get_variable_from_url,
28+
hands_on_exists,
29+
load_file_namespace,
2830
read_gitmastery_exercise_config,
2931
require_gitmastery_root,
3032
)
@@ -104,21 +106,13 @@ def setup_exercise_folder(
104106
info("Start working on it:")
105107

106108

107-
# TODO: Think about streamlining the config location
108-
# TODO: Maybe store the random "keys" in config
109-
@click.command()
110-
@click.argument("exercise")
111-
@click.pass_context
112-
def download(ctx: click.Context, exercise: str) -> None:
113-
"""Download an exercise"""
114-
download_time = datetime.now(tz=pytz.UTC)
115-
116-
verbose = ctx.obj["VERBOSE"]
117-
118-
formatted_exercise = exercise.replace("-", "_")
119-
120-
require_gitmastery_root(requires_root=True)
121-
109+
def download_exercise(
110+
ctx: click.Context,
111+
exercise: str,
112+
formatted_exercise: str,
113+
download_time: datetime,
114+
verbose: bool,
115+
) -> None:
122116
info(f"Checking if {exercise} is available")
123117
if not exercise_exists(exercise):
124118
error(f"Missing exercise {exercise}. Make sure you typed the name correctly.")
@@ -199,3 +193,90 @@ def download(ctx: click.Context, exercise: str) -> None:
199193
info(click.style(f"cd {exercise}", bold=True, italic=True))
200194
with open(".gitmastery-exercise.json", "w") as gitmastery_exercise_file:
201195
gitmastery_exercise_file.write(config.to_json())
196+
197+
198+
def download_hands_on(
199+
ctx: click.Context,
200+
hands_on: str,
201+
formatted_hands_on: str,
202+
verbose: bool,
203+
) -> None:
204+
info(f"Checking if {hands_on} is available")
205+
206+
hands_on_without_prefix = (
207+
formatted_hands_on[3:]
208+
if formatted_hands_on.startswith("hp_")
209+
else formatted_hands_on
210+
)
211+
212+
if not hands_on_exists(hands_on):
213+
error(f"Missing hands-on {hands_on}. Make sure you typed the name correctly.")
214+
215+
info(
216+
f"Downloading {hands_on} to {click.style(hands_on + '/', bold=True, italic=True)}"
217+
)
218+
219+
if os.path.isdir(hands_on):
220+
warn(f"You already have {hands_on}, removing it to download again")
221+
rmtree(hands_on)
222+
223+
os.makedirs(hands_on)
224+
os.chdir(hands_on)
225+
226+
hands_on_namespace = load_file_namespace(f"hands_on/{hands_on_without_prefix}.py")
227+
requires_git = hands_on_namespace.get("__requires_git__", False)
228+
requires_github = hands_on_namespace.get("__requires_github__", False)
229+
230+
if requires_git:
231+
try:
232+
info("Hands-on requires Git, checking if you have it setup")
233+
ctx.invoke(git)
234+
except SystemExit as e:
235+
if e.code == 1:
236+
# Exited because of missing Github configuration
237+
# Rollback the download and remove the folder
238+
warn("Git is not setup. Rolling back the download")
239+
os.chdir("..")
240+
rmtree(formatted_hands_on)
241+
warn("Setup Git before downloading this hands-on")
242+
exit(1)
243+
244+
if requires_github:
245+
try:
246+
info("Hands-on requires Github, checking if you have it setup")
247+
ctx.invoke(github)
248+
except SystemExit as e:
249+
if e.code == 1:
250+
# Exited because of missing Github configuration
251+
# Rollback the download and remove the folder
252+
warn("Github is not setup. Rolling back the download")
253+
os.chdir("..")
254+
rmtree(formatted_hands_on)
255+
warn("Setup Github and Github CLI before downloading this hands-on")
256+
exit(1)
257+
258+
execute_py_file_function_from_url(
259+
"hands_on", f"{hands_on_without_prefix}.py", "download", {"verbose": verbose}
260+
)
261+
success(f"Completed setting up {click.style(hands_on, bold=True, italic=True)}")
262+
263+
264+
# TODO: Think about streamlining the config location
265+
# TODO: Maybe store the random "keys" in config
266+
@click.command()
267+
@click.argument("exercise")
268+
@click.pass_context
269+
def download(ctx: click.Context, exercise: str) -> None:
270+
"""Download an exercise"""
271+
download_time = datetime.now(tz=pytz.UTC)
272+
273+
verbose = ctx.obj["VERBOSE"]
274+
275+
formatted_exercise = exercise.replace("-", "_")
276+
is_hands_on = exercise.startswith("hp-")
277+
278+
require_gitmastery_root(requires_root=True)
279+
if is_hands_on:
280+
download_hands_on(ctx, exercise, formatted_exercise, verbose)
281+
else:
282+
download_exercise(ctx, exercise, formatted_exercise, download_time, verbose)

app/utils/gitmastery.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ def download_file(url: str, path: str, is_binary: bool) -> None:
161161
T = TypeVar("T")
162162

163163

164+
def load_file_namespace(file_path: str) -> dict[str, Any]:
165+
sys.dont_write_bytecode = True
166+
py_file = fetch_file_contents(get_gitmastery_file_path(file_path), False)
167+
namespace: Dict[str, Any] = {}
168+
exec(py_file, namespace)
169+
sys.dont_write_bytecode = False
170+
return namespace
171+
172+
164173
def get_variable_from_url(
165174
exercise: str,
166175
file_path: str,
@@ -192,6 +201,21 @@ def exercise_exists(exercise: str, timeout: int = 5) -> bool:
192201
return False
193202

194203

204+
def hands_on_exists(hands_on: str, timeout: int = 5) -> bool:
205+
if hands_on.startswith("hp-"):
206+
hands_on = hands_on[3:]
207+
208+
try:
209+
response = requests.head(
210+
get_gitmastery_file_path(f"hands_on/{hands_on.replace('-', '_')}.py"),
211+
allow_redirects=True,
212+
timeout=timeout,
213+
)
214+
return response.status_code < 400
215+
except requests.RequestException:
216+
return False
217+
218+
195219
def execute_py_file_function_from_url(
196220
exercise: str, file_path: str, function_name: str, params: Dict[str, Any]
197221
) -> Optional[Any]:

0 commit comments

Comments
 (0)