Skip to content

Commit 0b1f418

Browse files
committed
Centralised FileManager core functionalities
1 parent c33939b commit 0b1f418

File tree

7 files changed

+125
-127
lines changed

7 files changed

+125
-127
lines changed

src/cli/builder.py

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,13 @@
22

33
import inspect
44
from pathlib import Path
5-
from typing import Any, cast
5+
from typing import Any
66

77
from InquirerPy import inquirer # type: ignore
88
from InquirerPy.validator import EmptyInputValidator # type: ignore
99
from click import Command, Option
10-
from importlib_resources import as_file, files # type: ignore
11-
from yaspin import yaspin # type: ignore
1210

13-
from ..core.copy_files import copy_files # type: ignore
14-
from ..core.docker import ComposeManager
15-
from ..core.manage_json import read_json, write_json
16-
from ..core.manage_templates import template_to_file
11+
from ..core.files import FileManager
1712
from ..utils.cli import clear, confirm
1813
from .custom_group import CustomGroup
1914
from .menu import Menus
@@ -25,6 +20,7 @@ class Builder(CustomGroup):
2520

2621
def __init__(self) -> None:
2722
super().__init__()
23+
self.file_manager = FileManager()
2824

2925
def create(self) -> Command:
3026
help = (
@@ -68,7 +64,7 @@ def callback(network: bool = False) -> None:
6864
break
6965

7066
clear(0)
71-
self.__save_files(
67+
self.file_manager.save_files(
7268
data={
7369
"compose": {
7470
"services": services,
@@ -107,7 +103,7 @@ def callback(
107103
"ERROR: Missing JSON file for services. Use 'create' first."
108104
)
109105

110-
data: dicts = read_json(path) or {}
106+
data: dicts = self.file_manager.read_json(path) or {}
111107
compose: dicts = data.get("compose", {}) or {}
112108

113109
services: list[dicts] = compose.get("services", []) or []
@@ -147,7 +143,7 @@ def find_index_by_name(name: str) -> int | None:
147143
compose["networks"] = networks
148144
data["compose"] = compose
149145
data["envs"] = envs
150-
self.__save_files(data)
146+
self.file_manager.save_files(data)
151147
print(f"Service '{target}' removed and files updated.")
152148

153149
elif add:
@@ -182,7 +178,7 @@ def find_index_by_name(name: str) -> int | None:
182178
compose["networks"] = networks
183179
data["compose"] = compose
184180
data["envs"] = envs
185-
self.__save_files(data)
181+
self.file_manager.save_files(data)
186182
print(f"Service '{name}' removed and files updated.")
187183

188184
else:
@@ -212,13 +208,13 @@ def callback() -> None:
212208
"ERROR: Missing JSON file for services. Use 'create' first."
213209
)
214210

215-
data: dicts = read_json(path) or {}
211+
data: dicts = self.file_manager.read_json(path) or {}
216212

217213
if not data:
218214
exit("ERROR: JSON file is empty. Use 'create' first.")
219215

220216
clear(0)
221-
self.__save_files(data, build=True)
217+
self.file_manager.save_files(data, build=True)
222218
clear(0)
223219
print("Files saved!")
224220

@@ -253,32 +249,3 @@ def __get_name(self, message: str) -> str:
253249
break
254250

255251
return name
256-
257-
@yaspin(text="Saving files...", color="cyan")
258-
def __save_files(self, data: dicts, build: bool = False) -> None:
259-
tmps_path = files("minecraft-docker-cli.assets.templates")
260-
composer_template = tmps_path.joinpath("docker-compose.yml.j2")
261-
env_template = tmps_path.joinpath(".env.j2")
262-
263-
if not build:
264-
write_json(self.cwd.joinpath("data.json"), data)
265-
266-
composer: dicts = data.get("composer") or {}
267-
with as_file(composer_template) as composer_path: # type: ignore
268-
composer_path = cast(Path, composer_path)
269-
template_to_file(
270-
composer_path, composer, self.cwd.joinpath("docker-compose.yml")
271-
)
272-
273-
services: list[dicts] = composer.get("services", []) or []
274-
names: list[str] = [service.get("name") for service in services] # type: ignore
275-
copy_files(self.cwd, names)
276-
277-
envs: list[dicts] = data.get("envs") or []
278-
for env in envs:
279-
relative_path = f"servers/{env.get("CONTAINER_NAME")}/.env" # type: ignore
280-
with as_file(env_template) as env_path: # type: ignore
281-
env_path = cast(Path, env_path)
282-
template_to_file(
283-
env_path, env, self.cwd.joinpath(relative_path)
284-
)

src/cli/manager.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from click import Command, Option
88

99
from ..core.docker import ComposeManager
10-
from ..utils.cli import clear, confirm # type: ignore
1110
from .custom_group import CustomGroup
1211

1312

src/core/copy_files.py

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/core/docker.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from yaspin import yaspin
99

10-
from .manage_json import read_json
10+
from .files import FileManager
1111

1212

1313
class ComposeManager:
@@ -18,6 +18,7 @@ def __init__(self) -> None:
1818
exit(
1919
"ERROR: docker-compose.yml was not located in current directory."
2020
)
21+
self.file_manager = FileManager()
2122

2223
def __run(
2324
self, *args: str, capture_output: bool = False
@@ -30,7 +31,7 @@ def __run(
3031
print("Command run:\n", result.stdout)
3132
return result
3233

33-
@yaspin("Building Container...")
34+
@yaspin("Building Container...", color="cyan")
3435
def build(
3536
self, no_cache: bool = False, pull: bool = False
3637
) -> CompletedProcess[str]:
@@ -41,35 +42,35 @@ def build(
4142
args.append("--pull")
4243
return self.__run(*args)
4344

44-
@yaspin("Stopping Services...")
45+
@yaspin("Stopping Services...", color="cyan")
4546
def stop(self) -> CompletedProcess[str]:
4647
return self.__run("stop")
4748

48-
@yaspin("Starting Services...")
49+
@yaspin("Starting Services...", color="cyan")
4950
def start(self) -> CompletedProcess[str]:
5051
return self.__run("start")
5152

52-
@yaspin("Removing Container...")
53+
@yaspin("Removing Container...", color="cyan")
5354
def down(self, remove_volumes: bool = False) -> CompletedProcess[str]:
5455
args = ["down"]
5556
if remove_volumes:
5657
args.append("-v")
5758
return self.__run(*args)
5859

59-
@yaspin("Putting Up Container...")
60+
@yaspin("Putting Up Container...", color="cyan")
6061
def up(self, detached: bool = True) -> CompletedProcess[str]:
6162
args = ["up"]
6263
if detached:
6364
args.append("-d")
6465
return self.__run(*args)
6566

66-
@yaspin("Backing Up Container...")
67+
@yaspin("Backing Up Container...", color="cyan")
6768
def back_up(self, cwd: Path = Path.cwd()) -> None:
6869
backup_path = cwd.joinpath(".backup")
6970
compose_json = cwd.joinpath("data.json")
7071

7172
backup_path.mkdir(exist_ok=True)
72-
data: dict[str, Any] = read_json(compose_json)
73+
data: dict[str, Any] = self.file_manager.read_json(compose_json)
7374
services = data.get("composer", {}).get("services", []) or []
7475
names: list[str] = [svc.get("name") for svc in services if svc.get("name") is not None] # type: ignore
7576
for svc_name in names:

src/core/files.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from pathlib import Path
5+
from typing import Any, cast
6+
7+
from importlib_resources import as_file, files # type: ignore
8+
import jinja2
9+
from yaspin import yaspin # type: ignore
10+
11+
dicts = dict[str, Any]
12+
13+
14+
class FileManager:
15+
16+
cwd = Path.cwd()
17+
18+
def save_files(self, data: dicts, build: bool = False) -> None:
19+
tmps_path = files("minecraft-docker-cli.assets.templates")
20+
composer_template = tmps_path.joinpath("docker-compose.yml.j2")
21+
env_template = tmps_path.joinpath(".env.j2")
22+
23+
if not build:
24+
self.write_json(self.cwd.joinpath("data.json"), data)
25+
26+
composer: dicts = data.get("composer") or {}
27+
with as_file(composer_template) as composer_path: # type: ignore
28+
composer_path = cast(Path, composer_path)
29+
self.template_to_file(
30+
composer_path, composer, self.cwd.joinpath("docker-compose.yml")
31+
)
32+
33+
services: list[dicts] = composer.get("services", []) or []
34+
names: list[str] = [service.get("name") for service in services] # type: ignore
35+
self.copy_files(self.cwd, names)
36+
37+
envs: list[dicts] = data.get("envs") or []
38+
for env in envs:
39+
relative_path = f"servers/{env.get("CONTAINER_NAME")}/.env" # type: ignore
40+
with as_file(env_template) as env_path: # type: ignore
41+
env_path = cast(Path, env_path)
42+
self.template_to_file(
43+
env_path, env, self.cwd.joinpath(relative_path)
44+
)
45+
46+
@yaspin("Reading JSON...", color="cyan")
47+
def read_json(self, file: Path) -> dict[Any, Any]:
48+
with open(file, "r+") as f:
49+
data = dict(json.load(f))
50+
return data
51+
52+
@yaspin("Writting JSON...", color="cyan")
53+
def write_json(self, file: Path, data: dict[Any, Any]) -> None:
54+
data_str = json.dumps(data, indent=2)
55+
with open(file, "w+") as f:
56+
f.write(data_str)
57+
return None
58+
59+
@yaspin("Copying files...", color="cyan")
60+
def copy_files(self, path: Path, services: list[str]) -> None:
61+
docker_pkg = files("minecraft-docker-cli.assets.docker")
62+
dockerfile_res = docker_pkg.joinpath("Dockerfile")
63+
dockerignore_res = docker_pkg.joinpath(".dockerignore")
64+
runsh_res = files("minecraft-docker-cli.assets.scripts").joinpath(
65+
"run.sh"
66+
)
67+
readme_res = files("minecraft-docker-cli.assets").joinpath("README.md")
68+
69+
# Ensure base path exists
70+
if not path.exists():
71+
raise ValueError("Path doesnt exist")
72+
73+
# Read bytes from resources once
74+
dockerfile_bytes = dockerfile_res.read_bytes()
75+
dockerignore_bytes = dockerignore_res.read_bytes()
76+
runsh_bytes = runsh_res.read_bytes()
77+
readme_bytes = readme_res.read_bytes()
78+
79+
# Write files for each service
80+
for service in services:
81+
dest_dir = path.joinpath("servers", service)
82+
dest_dir.mkdir(parents=True, exist_ok=True)
83+
84+
(dest_dir / "Dockerfile").write_bytes(dockerfile_bytes)
85+
(dest_dir / ".dockerignore").write_bytes(dockerignore_bytes)
86+
(dest_dir / "run.sh").write_bytes(runsh_bytes)
87+
88+
# Write top-level README into the given path
89+
(path / "README.md").write_bytes(readme_bytes)
90+
91+
@yaspin("Rendering template...", color="cyan")
92+
def template_to_file(
93+
self, template_path: Path, context: dict[Any, Any], dest_path: Path
94+
) -> Path:
95+
rendered = self.__render_template(template_path, context)
96+
dest_path.parent.mkdir(parents=True, exist_ok=True)
97+
dest_path.write_text(rendered, encoding="utf-8")
98+
return dest_path
99+
100+
def __render_template(
101+
self, template_path: Path, context: dict[Any, Any]
102+
) -> str:
103+
env = jinja2.Environment(
104+
loader=jinja2.FileSystemLoader(str(template_path.parent))
105+
)
106+
template_obj = env.get_template(template_path.name)
107+
return template_obj.render(**context)

src/core/manage_json.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/core/manage_templates.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)