Skip to content

Commit 3bbea66

Browse files
committed
Remote module support #214
1 parent 2359f2e commit 3bbea66

8 files changed

+196
-15
lines changed

.github/workflows/docker-build.yml

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
- "master"
77
- "devel"
88
- "docker-github-actions"
9+
- "feature/remote-module-support"
10+
- "feature/meta-modules"
11+
- "beta"
912

1013
jobs:
1114
docker:

README.rst

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Requirements
6363
#. sudo (the script itself calls it, running as root without sudo won't work)
6464
#. p7zip-full
6565
#. Python 3.2+
66+
#. GitPython
6667

6768
Known to work building configurations
6869
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

src/Dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1313
wget \
1414
p7zip-full \
1515
python3 \
16+
python3-distutils \
17+
python3-dev \
18+
python3-git \
1619
binfmt-support \
1720
qemu-system \
1821
qemu-user \

src/execution_order.py

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#a='base(octopi,a(b,c(a2)),mm)'
22
import argparse
33
import os
4+
from get_remote_modules import get_remote_module
45

56
def handle(module, state, out):
67
out.write("# " + state + "_" + module + "\n")
@@ -9,14 +10,28 @@ def handle(module, state, out):
910
os.path.join(os.environ['CUSTOM_PI_OS_PATH'], "modules", module)
1011
]
1112

13+
found_local = False
14+
found_remote = False
1215
for module_folder in module_folders:
1316
if os.path.isdir(module_folder):
14-
script = os.path.join(module_folder, state + "_chroot_script")
15-
if os.path.isfile(script):
16-
out.write("execute_chroot_script '" + module_folder + "' '" + script + "'\n")
17-
else:
18-
print("WARNING: No file at - " + script)
17+
found_local = True
1918
break
19+
20+
if not found_local:
21+
# TODO: Handle update
22+
found_remote, module_folder = get_remote_module(module)
23+
24+
25+
if not found_local and not found_remote:
26+
print(f"Error: Module {module} does not exist and is not in remote modules list")
27+
exit(1)
28+
29+
script = os.path.join(module_folder, state + "_chroot_script")
30+
if os.path.isfile(script):
31+
out.write("execute_chroot_script '" + module_folder + "' '" + script + "'\n")
32+
else:
33+
print("WARNING: No file at - " + script)
34+
2035
return
2136

2237
def parse(a, callback):

src/get_remote_modules.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import os
2+
import yaml
3+
from pathlib import Path
4+
from typing import Tuple, Optional
5+
import git
6+
from git import RemoteProgress
7+
8+
# TODO add env var to set this
9+
REMOTES_DIR = os.path.join(os.path.dirname(__file__), "remotes")
10+
REMOTE_CONFIG = os.path.join(os.path.dirname(__file__), "modules_remote.yml")
11+
12+
13+
class CloneProgress(RemoteProgress):
14+
def update(self, op_code, cur_count, max_count=None, message=''):
15+
if message:
16+
print(message)
17+
18+
19+
def ensure_dir(d, chmod=0o777):
20+
"""
21+
Ensures a folder exists.
22+
Returns True if the folder already exists
23+
"""
24+
if not os.path.exists(d):
25+
os.makedirs(d, chmod)
26+
os.chmod(d, chmod)
27+
return False
28+
return True
29+
30+
31+
def read_remotes():
32+
if not os.path.isfile(REMOTE_CONFIG):
33+
raise Exception(f"Error: Remotes config file not found: {REMOTE_CONFIG}")
34+
with open(REMOTE_CONFIG,'r') as f:
35+
output = yaml.safe_load(f)
36+
return output
37+
38+
def get_remote_module(module: str) -> Tuple[bool, Optional[str]]:
39+
""" Gets the remote module and saves it to cache. Returns True if found, else false"""
40+
print(f"INFO: Module {module}, looking for remote module and downloading")
41+
modules_remotes = read_remotes()
42+
print(modules_remotes.keys())
43+
44+
if "modules" not in modules_remotes.keys() and module not in modules_remotes["modules"].keys():
45+
return False, None
46+
47+
ensure_dir(REMOTES_DIR)
48+
49+
if "remotes" not in modules_remotes.keys() and module not in modules_remotes["modules"].keys():
50+
return False, None
51+
52+
module_config = modules_remotes["modules"][module]
53+
54+
remote_for_module = module_config["remote"]
55+
remote_config = modules_remotes["remotes"][remote_for_module]
56+
57+
if remote_config.get("type", "git") == "git":
58+
if "repo" not in remote_config.keys():
59+
print(f"Error: repo field not set for remote: {remote_for_module} used by remote module {module}")
60+
return False, None
61+
62+
if "tag" not in remote_config.keys():
63+
print(f"Error: repo tag field not set for remote: {remote_for_module} used by remote module {module}")
64+
return False, None
65+
66+
repo_url = remote_config["repo"]
67+
branch = remote_config["tag"]
68+
69+
# credentials = base64.b64encode(f"{GHE_TOKEN}:".encode("latin-1")).decode("latin-1")
70+
# TODO: Handle update of remote
71+
remote_to_path = os.path.join(REMOTES_DIR, remote_for_module)
72+
if not os.path.exists(remote_to_path):
73+
git.Repo.clone_from(
74+
url=repo_url,
75+
single_branch=True,
76+
depth=1,
77+
to_path=f"{remote_to_path}",
78+
branch=branch,
79+
)
80+
81+
if "path" not in module_config.keys():
82+
print(f"Error: repo tag field not set for remote: {remote_for_module} used by remote module {module}")
83+
return False, None
84+
module_path = os.path.join(remote_to_path, module_config["path"])
85+
return True, module_path
86+
87+
else:
88+
print(f"Error: unsupported type {modules_remotes[module]['type']} for module {module}")
89+
return False, None
90+
return False, None

src/make_rpi-imager-snipplet.py

+12-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import argparse
77
from datetime import date
88
import glob
9+
from typing import Optional, Dict, Union
910

1011

1112

@@ -44,12 +45,13 @@ def handle_arg(key, optional=False):
4445

4546
output_path = os.path.join(workspace_path, "rpi-imager-snipplet.json")
4647

47-
json_out = {"name": name,
48-
"description": description,
49-
"url": url,
50-
"icon": icon,
51-
"release_date": release_date,
52-
}
48+
json_out: Dict[str, Optional[Union[str, int]]] = {
49+
"name": name,
50+
"description": description,
51+
"url": url,
52+
"icon": icon,
53+
"release_date": release_date,
54+
}
5355

5456
if website is not None:
5557
json_out["website"] = website
@@ -60,14 +62,14 @@ def handle_arg(key, optional=False):
6062
json_out["extract_sha256"] = f.read().split()[0]
6163

6264
json_out["extract_size"] = None
63-
with zipfile.ZipFile(zip_local) as zipfile:
64-
json_out["extract_size"] = zipfile.filelist[0].file_size
65+
with zipfile.ZipFile(zip_local) as zip_file:
66+
json_out["extract_size"] = zip_file.filelist[0].file_size
6567

6668
json_out["image_download_size"] = os.stat(zip_local).st_size
6769

6870
json_out["image_download_sha256"] = None
69-
with open(zip_local,"rb") as f:
70-
json_out["image_download_sha256"] = hashlib.sha256(f.read()).hexdigest()
71+
with open(zip_local,"rb") as fh:
72+
json_out["image_download_sha256"] = hashlib.sha256(fh.read()).hexdigest()
7173

7274
with open(output_path, "w") as w:
7375
json.dump(json_out, w, indent=2)

src/modules_remote.yml

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
modules:
2+
armbian:
3+
remote: MainsailOS
4+
path: src/modules/armbian
5+
6+
armbian_net:
7+
remote: MainsailOS
8+
path: src/modules/armbian_net
9+
10+
crowsnest:
11+
remote: MainsailOS
12+
path: src/modules/crowsnest
13+
14+
is_req_preinstall:
15+
remote: MainsailOS
16+
path: src/modules/is_req_preinstall
17+
18+
klipper:
19+
remote: MainsailOS
20+
path: src/modules/klipper
21+
22+
mainsail:
23+
remote: MainsailOS
24+
path: src/modules/mainsail
25+
26+
mainsailos:
27+
remote: MainsailOS
28+
path: src/modules/mainsailos
29+
30+
moonraker:
31+
remote: MainsailOS
32+
path: src/modules/moonraker
33+
34+
orangepi:
35+
remote: MainsailOS
36+
path: src/modules/orangepi
37+
38+
orangepi_net:
39+
remote: MainsailOS
40+
path: src/modules/orangepi_net
41+
42+
piconfig:
43+
remote: MainsailOS
44+
path: src/modules/piconfig
45+
46+
postrename:
47+
remote: MainsailOS
48+
path: src/modules/postrename
49+
50+
sonar:
51+
remote: MainsailOS
52+
path: src/modules/sonar
53+
54+
timelapse:
55+
remote: MainsailOS
56+
path: src/modules/timelapse
57+
58+
udev_fix:
59+
remote: MainsailOS
60+
path: src/modules/udev_fix
61+
62+
remotes:
63+
MainsailOS:
64+
repo: https://github.com/mainsail-crew/MainsailOS.git
65+
type: git
66+
tag: develop

src/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GitPython

0 commit comments

Comments
 (0)