Compare commits
11 commits
b3f9566b8b
...
3062378a7b
| Author | SHA1 | Date | |
|---|---|---|---|
| 3062378a7b | |||
|
|
bac55c65f8 |
||
|
|
63672ea38b |
||
|
|
4b751e79e1 |
||
|
|
3ea83163b0 | ||
|
|
0e8dd923f8 | ||
|
|
2b1c70a9e1 | ||
|
|
d7df2876dd | ||
|
|
3e6378fbc6 | ||
|
|
bdeddcabad | ||
| 82ce1ff26f |
12 changed files with 185 additions and 145 deletions
51
.github/workflows/publish_assets.yaml
vendored
51
.github/workflows/publish_assets.yaml
vendored
|
|
@ -1,51 +0,0 @@
|
||||||
# CI Code for generating and publishing beta assets
|
|
||||||
|
|
||||||
name: publish_assets
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
jobs:
|
|
||||||
generate_assets:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout Moonraker
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
ref: ${{ github.ref }}
|
|
||||||
path: moonraker
|
|
||||||
|
|
||||||
- name: Checkout Klipper
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
repository: Klipper3d/klipper
|
|
||||||
path: klipper
|
|
||||||
|
|
||||||
- name: Build Beta Assets
|
|
||||||
if: ${{ github.event.release.prerelease }}
|
|
||||||
run: >
|
|
||||||
./moonraker/scripts/build-zip-release.sh -b
|
|
||||||
-o ${{ github.workspace }}
|
|
||||||
-k ${{ github.workspace }}/klipper
|
|
||||||
|
|
||||||
- name: Build Stable Assets
|
|
||||||
if: ${{ !github.event.release.prerelease }}
|
|
||||||
run: >
|
|
||||||
./moonraker/scripts/build-zip-release.sh
|
|
||||||
-o ${{ github.workspace }}
|
|
||||||
-k ${{ github.workspace }}/klipper
|
|
||||||
|
|
||||||
- name: Upload assets
|
|
||||||
run: |
|
|
||||||
cd moonraker
|
|
||||||
gh release upload ${{ env.TAG }} ${{ env.FILES }}
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
FILES: >
|
|
||||||
${{ github.workspace }}/moonraker.zip
|
|
||||||
${{ github.workspace }}/klipper.zip
|
|
||||||
${{ github.workspace }}/RELEASE_INFO
|
|
||||||
${{ github.workspace }}/COMMIT_LOG
|
|
||||||
TAG: ${{ github.event.release.tag_name }}
|
|
||||||
|
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -8,7 +8,13 @@ __pycache__/
|
||||||
venv
|
venv
|
||||||
start_moonraker
|
start_moonraker
|
||||||
*.env
|
*.env
|
||||||
|
*.egg-info
|
||||||
.pdm-python
|
.pdm-python
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
share
|
share
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
.mypy_cache
|
||||||
|
.pdm-build
|
||||||
|
.pytest_cache
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ from ..common import (
|
||||||
KlippyState
|
KlippyState
|
||||||
)
|
)
|
||||||
from ..utils import json_wrapper as jsonw
|
from ..utils import json_wrapper as jsonw
|
||||||
|
try:
|
||||||
|
from paho.mqtt.reasoncodes import ReasonCode
|
||||||
|
except ImportError:
|
||||||
|
from paho.mqtt.reasoncodes import ReasonCodes as ReasonCode
|
||||||
|
|
||||||
# Annotation imports
|
# Annotation imports
|
||||||
from typing import (
|
from typing import (
|
||||||
|
|
@ -41,6 +45,7 @@ if TYPE_CHECKING:
|
||||||
from ..common import JsonRPC, APIDefinition
|
from ..common import JsonRPC, APIDefinition
|
||||||
from ..eventloop import FlexTimer
|
from ..eventloop import FlexTimer
|
||||||
from .klippy_apis import KlippyAPI
|
from .klippy_apis import KlippyAPI
|
||||||
|
from paho.mqtt.properties import Properties
|
||||||
FlexCallback = Callable[[bytes], Optional[Coroutine]]
|
FlexCallback = Callable[[bytes], Optional[Coroutine]]
|
||||||
RPCCallback = Callable[..., Coroutine]
|
RPCCallback = Callable[..., Coroutine]
|
||||||
|
|
||||||
|
|
@ -73,7 +78,7 @@ class ExtPahoClient(paho_mqtt.Client):
|
||||||
"remaining_count": [],
|
"remaining_count": [],
|
||||||
"remaining_mult": 1,
|
"remaining_mult": 1,
|
||||||
"remaining_length": 0,
|
"remaining_length": 0,
|
||||||
"packet": b"",
|
"packet": b"", # type: ignore
|
||||||
"to_process": 0,
|
"to_process": 0,
|
||||||
"pos": 0
|
"pos": 0
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +105,7 @@ class ExtPahoClient(paho_mqtt.Client):
|
||||||
self._last_msg_out = paho_mqtt.time_func()
|
self._last_msg_out = paho_mqtt.time_func()
|
||||||
|
|
||||||
self._ping_t = 0
|
self._ping_t = 0
|
||||||
self._state = paho_mqtt.mqtt_cs_new
|
self._state = paho_mqtt.mqtt_cs_new # type: ignore
|
||||||
|
|
||||||
self._sock_close()
|
self._sock_close()
|
||||||
|
|
||||||
|
|
@ -149,16 +154,16 @@ class ExtPahoClient(paho_mqtt.Client):
|
||||||
|
|
||||||
if self._transport == "websockets":
|
if self._transport == "websockets":
|
||||||
sock.settimeout(self._keepalive)
|
sock.settimeout(self._keepalive)
|
||||||
sock = paho_mqtt.WebsocketWrapper(
|
sock = paho_mqtt.WebsocketWrapper( # type: ignore
|
||||||
sock, self._host, self._port, self._ssl,
|
sock, self._host, self._port, self._ssl,
|
||||||
self._websocket_path, self._websocket_extra_headers
|
self._websocket_path, self._websocket_extra_headers
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
|
|
||||||
self._sock = sock
|
self._sock = sock # type: ignore
|
||||||
assert self._sock is not None
|
assert self._sock is not None
|
||||||
self._sock.setblocking(False)
|
self._sock.setblocking(False)
|
||||||
self._registered_write = False
|
self._registered_write = False
|
||||||
self._call_socket_open()
|
self._call_socket_open() # type: ignore
|
||||||
|
|
||||||
return self._send_connect(self._keepalive)
|
return self._send_connect(self._keepalive)
|
||||||
|
|
||||||
|
|
@ -233,7 +238,7 @@ class BrokerAckLogger:
|
||||||
|
|
||||||
def __call__(self, fut: asyncio.Future) -> None:
|
def __call__(self, fut: asyncio.Future) -> None:
|
||||||
if self.action == "subscribe":
|
if self.action == "subscribe":
|
||||||
res: Union[List[int], List[paho_mqtt.ReasonCodes]]
|
res: Union[List[int], List[ReasonCode]]
|
||||||
res = fut.result()
|
res = fut.result()
|
||||||
log_msg = "MQTT Subscriptions Acknowledged"
|
log_msg = "MQTT Subscriptions Acknowledged"
|
||||||
if len(res) != len(self.topics):
|
if len(res) != len(self.topics):
|
||||||
|
|
@ -243,7 +248,7 @@ class BrokerAckLogger:
|
||||||
else:
|
else:
|
||||||
for topic, qos in zip(self.topics, res):
|
for topic, qos in zip(self.topics, res):
|
||||||
log_msg += f"\n Topic: {topic} | "
|
log_msg += f"\n Topic: {topic} | "
|
||||||
if isinstance(qos, paho_mqtt.ReasonCodes):
|
if isinstance(qos, ReasonCode):
|
||||||
log_msg += qos.getName()
|
log_msg += qos.getName()
|
||||||
else:
|
else:
|
||||||
log_msg += f"Granted QoS {qos}"
|
log_msg += f"Granted QoS {qos}"
|
||||||
|
|
@ -265,40 +270,43 @@ class AIOHelper:
|
||||||
self.client.on_socket_open = self._on_socket_open
|
self.client.on_socket_open = self._on_socket_open
|
||||||
self.client.on_socket_close = self._on_socket_close
|
self.client.on_socket_close = self._on_socket_close
|
||||||
self.client._on_socket_register_write = self._on_socket_register_write
|
self.client._on_socket_register_write = self._on_socket_register_write
|
||||||
self.client._on_socket_unregister_write = \
|
self.client._on_socket_unregister_write = self._on_socket_unregister_write
|
||||||
self._on_socket_unregister_write
|
|
||||||
self.misc_task: Optional[asyncio.Task] = None
|
self.misc_task: Optional[asyncio.Task] = None
|
||||||
|
|
||||||
def _on_socket_open(self,
|
def _on_socket_open(
|
||||||
|
self,
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
userdata: Any,
|
userdata: Any,
|
||||||
sock: socket.socket
|
sock: socket.socket | paho_mqtt.SocketLike
|
||||||
) -> None:
|
) -> None:
|
||||||
logging.info("MQTT Socket Opened")
|
logging.info("MQTT Socket Opened")
|
||||||
self.loop.add_reader(sock, client.loop_read)
|
self.loop.add_reader(sock, client.loop_read)
|
||||||
self.misc_task = self.loop.create_task(self.misc_loop())
|
self.misc_task = self.loop.create_task(self.misc_loop())
|
||||||
|
|
||||||
def _on_socket_close(self,
|
def _on_socket_close(
|
||||||
|
self,
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
userdata: Any,
|
userdata: Any,
|
||||||
sock: socket.socket
|
sock: socket.socket | paho_mqtt.SocketLike
|
||||||
) -> None:
|
) -> None:
|
||||||
logging.info("MQTT Socket Closed")
|
logging.info("MQTT Socket Closed")
|
||||||
self.loop.remove_reader(sock)
|
self.loop.remove_reader(sock)
|
||||||
if self.misc_task is not None:
|
if self.misc_task is not None:
|
||||||
self.misc_task.cancel()
|
self.misc_task.cancel()
|
||||||
|
|
||||||
def _on_socket_register_write(self,
|
def _on_socket_register_write(
|
||||||
|
self,
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
userdata: Any,
|
userdata: Any,
|
||||||
sock: socket.socket
|
sock: socket.socket | paho_mqtt.SocketLike
|
||||||
) -> None:
|
) -> None:
|
||||||
self.loop.add_writer(sock, client.loop_write)
|
self.loop.add_writer(sock, client.loop_write)
|
||||||
|
|
||||||
def _on_socket_unregister_write(self,
|
def _on_socket_unregister_write(
|
||||||
|
self,
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
userdata: Any,
|
userdata: Any,
|
||||||
sock: socket.socket
|
sock: socket.socket | paho_mqtt.SocketLike
|
||||||
) -> None:
|
) -> None:
|
||||||
self.loop.remove_writer(sock)
|
self.loop.remove_writer(sock)
|
||||||
|
|
||||||
|
|
@ -354,7 +362,9 @@ class MQTTClient(APITransport):
|
||||||
config.getboolean("publish_split_status", False)
|
config.getboolean("publish_split_status", False)
|
||||||
client_id: str = config.get("client_id", "")
|
client_id: str = config.get("client_id", "")
|
||||||
if PAHO_MQTT_VERSION < (2, 0):
|
if PAHO_MQTT_VERSION < (2, 0):
|
||||||
self.client = ExtPahoClient(client_id, protocol=self.protocol)
|
self.client = ExtPahoClient(
|
||||||
|
client_id, protocol=self.protocol # type: ignore
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.client = ExtPahoClient(
|
self.client = ExtPahoClient(
|
||||||
paho_mqtt.CallbackAPIVersion.VERSION1, client_id, # type: ignore
|
paho_mqtt.CallbackAPIVersion.VERSION1, client_id, # type: ignore
|
||||||
|
|
@ -472,7 +482,7 @@ class MQTTClient(APITransport):
|
||||||
self._publish_status_update(payload, self.last_status_time)
|
self._publish_status_update(payload, self.last_status_time)
|
||||||
|
|
||||||
def _on_message(self,
|
def _on_message(self,
|
||||||
client: str,
|
client: str | paho_mqtt.Client,
|
||||||
user_data: Any,
|
user_data: Any,
|
||||||
message: paho_mqtt.MQTTMessage
|
message: paho_mqtt.MQTTMessage
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
@ -491,8 +501,8 @@ class MQTTClient(APITransport):
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
user_data: Any,
|
user_data: Any,
|
||||||
flags: Dict[str, Any],
|
flags: Dict[str, Any],
|
||||||
reason_code: Union[int, paho_mqtt.ReasonCodes],
|
reason_code: Union[int, ReasonCode],
|
||||||
properties: Optional[paho_mqtt.Properties] = None
|
properties: Optional[Properties] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
logging.info("MQTT Client Connected")
|
logging.info("MQTT Client Connected")
|
||||||
if reason_code == 0:
|
if reason_code == 0:
|
||||||
|
|
@ -521,7 +531,7 @@ class MQTTClient(APITransport):
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
user_data: Any,
|
user_data: Any,
|
||||||
reason_code: int,
|
reason_code: int,
|
||||||
properties: Optional[paho_mqtt.Properties] = None
|
properties: Optional[Properties] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
if self.disconnect_evt is not None:
|
if self.disconnect_evt is not None:
|
||||||
self.disconnect_evt.set()
|
self.disconnect_evt.set()
|
||||||
|
|
@ -547,8 +557,8 @@ class MQTTClient(APITransport):
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
user_data: Any,
|
user_data: Any,
|
||||||
msg_id: int,
|
msg_id: int,
|
||||||
flex: Union[List[int], List[paho_mqtt.ReasonCodes]],
|
flex: Union[List[int], List[ReasonCode]],
|
||||||
properties: Optional[paho_mqtt.Properties] = None
|
properties: Optional[Properties] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
sub_fut = self.pending_acks.pop(msg_id, None)
|
sub_fut = self.pending_acks.pop(msg_id, None)
|
||||||
if sub_fut is not None and not sub_fut.done():
|
if sub_fut is not None and not sub_fut.done():
|
||||||
|
|
@ -558,8 +568,8 @@ class MQTTClient(APITransport):
|
||||||
client: paho_mqtt.Client,
|
client: paho_mqtt.Client,
|
||||||
user_data: Any,
|
user_data: Any,
|
||||||
msg_id: int,
|
msg_id: int,
|
||||||
properties: Optional[paho_mqtt.Properties] = None,
|
properties: Optional[Properties] = None,
|
||||||
reasoncodes: Optional[paho_mqtt.ReasonCodes] = None
|
reasoncodes: Optional[ReasonCode] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
unsub_fut = self.pending_acks.pop(msg_id, None)
|
unsub_fut = self.pending_acks.pop(msg_id, None)
|
||||||
if unsub_fut is not None and not unsub_fut.done():
|
if unsub_fut is not None and not unsub_fut.done():
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import base64
|
||||||
import tornado.websocket as tornado_ws
|
import tornado.websocket as tornado_ws
|
||||||
|
from tornado.httpclient import HTTPRequest
|
||||||
from tornado import version_info as tornado_version
|
from tornado import version_info as tornado_version
|
||||||
from ..common import RequestType, HistoryFieldData
|
from ..common import RequestType, HistoryFieldData
|
||||||
from ..utils import json_wrapper as jsonw
|
from ..utils import json_wrapper as jsonw
|
||||||
|
|
@ -86,6 +88,23 @@ class SpoolManager:
|
||||||
self.spoolman_url = f"{scheme}://{host}/api"
|
self.spoolman_url = f"{scheme}://{host}/api"
|
||||||
self.ws_url = f"{ws_scheme}://{host}/api/v1/spool"
|
self.ws_url = f"{ws_scheme}://{host}/api/v1/spool"
|
||||||
|
|
||||||
|
headers_raw = config.get("headers", "")
|
||||||
|
self.http_headers = {}
|
||||||
|
for c in headers_raw.split(";"):
|
||||||
|
c = c.strip()
|
||||||
|
if not c:
|
||||||
|
continue
|
||||||
|
c_parts = c.split(":", 1)
|
||||||
|
if len(c_parts) != 2:
|
||||||
|
raise config.error(f"Section [spoolman], Option headers: {c}: Invalid header format")
|
||||||
|
self.http_headers[c_parts[0]] = c_parts[1]
|
||||||
|
|
||||||
|
username = config.get("http_username", None)
|
||||||
|
password = config.get("http_password", None)
|
||||||
|
if username and password:
|
||||||
|
creds = base64.b64encode(f"{username}:{password}".encode()).decode()
|
||||||
|
self.http_headers["Authorization"] = "Basic " + creds
|
||||||
|
|
||||||
def _register_notifications(self):
|
def _register_notifications(self):
|
||||||
self.server.register_notification("spoolman:active_spool_set")
|
self.server.register_notification("spoolman:active_spool_set")
|
||||||
self.server.register_notification("spoolman:spoolman_status_changed")
|
self.server.register_notification("spoolman:spoolman_status_changed")
|
||||||
|
|
@ -130,10 +149,10 @@ class SpoolManager:
|
||||||
if log_connect:
|
if log_connect:
|
||||||
logging.info(f"Connecting To Spoolman: {self.ws_url}")
|
logging.info(f"Connecting To Spoolman: {self.ws_url}")
|
||||||
log_connect = False
|
log_connect = False
|
||||||
|
request = HTTPRequest(url=self.ws_url, headers=self.http_headers, connect_timeout=5.)
|
||||||
try:
|
try:
|
||||||
self.spoolman_ws = await tornado_ws.websocket_connect(
|
self.spoolman_ws = await tornado_ws.websocket_connect(
|
||||||
self.ws_url,
|
request,
|
||||||
connect_timeout=5.,
|
|
||||||
ping_interval=None if tornado_version < (6, 5) else 20.
|
ping_interval=None if tornado_version < (6, 5) else 20.
|
||||||
)
|
)
|
||||||
setattr(self.spoolman_ws, "on_ping", self._on_ws_ping)
|
setattr(self.spoolman_ws, "on_ping", self._on_ws_ping)
|
||||||
|
|
@ -217,7 +236,7 @@ class SpoolManager:
|
||||||
if self.spool_id is not None:
|
if self.spool_id is not None:
|
||||||
response = await self.http_client.get(
|
response = await self.http_client.get(
|
||||||
f"{self.spoolman_url}/v1/spool/{self.spool_id}",
|
f"{self.spoolman_url}/v1/spool/{self.spool_id}",
|
||||||
connect_timeout=1., request_timeout=2.
|
connect_timeout=1., request_timeout=2., headers=self.http_headers
|
||||||
)
|
)
|
||||||
if response.status_code == 404:
|
if response.status_code == 404:
|
||||||
logging.info(f"Spool ID {self.spool_id} not found, setting to None")
|
logging.info(f"Spool ID {self.spool_id} not found, setting to None")
|
||||||
|
|
@ -308,7 +327,8 @@ class SpoolManager:
|
||||||
response = await self.http_client.request(
|
response = await self.http_client.request(
|
||||||
method="PUT",
|
method="PUT",
|
||||||
url=f"{self.spoolman_url}/v1/spool/{spool_id}/use",
|
url=f"{self.spoolman_url}/v1/spool/{spool_id}/use",
|
||||||
body={"use_length": used_length}
|
body={"use_length": used_length},
|
||||||
|
headers=self.http_headers
|
||||||
)
|
)
|
||||||
if response.has_error():
|
if response.has_error():
|
||||||
if response.status_code == 404:
|
if response.status_code == 404:
|
||||||
|
|
@ -370,6 +390,7 @@ class SpoolManager:
|
||||||
method=method,
|
method=method,
|
||||||
url=full_url,
|
url=full_url,
|
||||||
body=body,
|
body=body,
|
||||||
|
headers=self.http_headers
|
||||||
)
|
)
|
||||||
if not use_v2_response:
|
if not use_v2_response:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,9 @@ class PackageKitTransaction:
|
||||||
summary: str
|
summary: str
|
||||||
) -> None:
|
) -> None:
|
||||||
info = PkEnum.Info.from_index(info_code & 0xFFFF)
|
info = PkEnum.Info.from_index(info_code & 0xFFFF)
|
||||||
|
severity = PkEnum.Info.from_index((info_code >> 16) & 0xFFFF)
|
||||||
|
if info == PkEnum.Info.UNKNOWN:
|
||||||
|
info = severity
|
||||||
if self._role in self.GET_PKG_ROLES:
|
if self._role in self.GET_PKG_ROLES:
|
||||||
pkg_data = {
|
pkg_data = {
|
||||||
'package_id': package_id,
|
'package_id': package_id,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#
|
#
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license
|
# This file may be distributed under the terms of the GNU GPLv3 license
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import re
|
import re
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
@ -61,15 +62,22 @@ class SysDepsParser:
|
||||||
version = distro_info.get("distro_version")
|
version = distro_info.get("distro_version")
|
||||||
if version:
|
if version:
|
||||||
self.distro_version = _convert_version(version)
|
self.distro_version = _convert_version(version)
|
||||||
self.vendor: str = ""
|
self.vendor: str = os.getenv("MOONRAKER_VENDOR", "")
|
||||||
if pathlib.Path("/etc/rpi-issue").is_file():
|
if not self.vendor and pathlib.Path("/etc/rpi-issue").is_file():
|
||||||
self.vendor = "raspberry-pi"
|
self.vendor = "raspberry-pi"
|
||||||
|
exclusions = os.getenv("MOONRAKER_EXCLUDED_PKGS", "")
|
||||||
|
self.exclusions: List[str] = [
|
||||||
|
excl.strip() for excl in exclusions.split() if excl.strip()
|
||||||
|
]
|
||||||
|
|
||||||
def _parse_spec(self, full_spec: str) -> str | None:
|
def _parse_spec(self, full_spec: str) -> str | None:
|
||||||
parts = full_spec.split(";", maxsplit=1)
|
parts = full_spec.split(";", maxsplit=1)
|
||||||
if len(parts) == 1:
|
|
||||||
return full_spec
|
|
||||||
pkg_name = parts[0].strip()
|
pkg_name = parts[0].strip()
|
||||||
|
if pkg_name in self.exclusions or not pkg_name:
|
||||||
|
logging.info(f"Package '{full_spec}' excluded by environment")
|
||||||
|
return None
|
||||||
|
if len(parts) == 1:
|
||||||
|
return pkg_name
|
||||||
expressions = re.split(r"( and | or )", parts[1].strip())
|
expressions = re.split(r"( and | or )", parts[1].strip())
|
||||||
if not len(expressions) & 1:
|
if not len(expressions) & 1:
|
||||||
# There should always be an odd number of expressions. Each
|
# There should always be an odd number of expressions. Each
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,22 @@ authors = [
|
||||||
{name = "Eric Callahan", email = "arksine.code@gmail.com"},
|
{name = "Eric Callahan", email = "arksine.code@gmail.com"},
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tornado>=6.2.0, <=6.5.1",
|
"tornado>=6.2.0, <=6.5.4",
|
||||||
"pyserial==3.4",
|
"pyserial==3.4",
|
||||||
"pillow>=9.5.0, <=11.1.0",
|
"pillow>=9.5.0, <=12.1.0",
|
||||||
"streaming-form-data>=1.11.0, <=1.19.1",
|
"streaming-form-data>=1.11.0, <=1.19.1",
|
||||||
"distro==1.9.0",
|
"distro==1.9.0",
|
||||||
"inotify-simple==1.3.5",
|
"inotify-simple==2.0.1",
|
||||||
"libnacl==2.1.0",
|
"libnacl==2.1.0",
|
||||||
"paho-mqtt==1.6.1",
|
"paho-mqtt==2.1.0",
|
||||||
"zeroconf==0.131.0",
|
"zeroconf>=0.131.0, <=0.148.0",
|
||||||
"preprocess-cancellation==0.2.1",
|
"preprocess-cancellation==0.2.1",
|
||||||
"jinja2==3.1.5",
|
"jinja2==3.1.6",
|
||||||
"dbus-fast>=2.21.3, <=2.44.1",
|
"dbus-fast>=2.21.3, <=3.1.2",
|
||||||
"apprise==1.9.2",
|
"apprise>=1.9.3, <=1.9.6",
|
||||||
"ldap3==2.9.1",
|
"ldap3==2.9.1",
|
||||||
"python-periphery==2.4.1",
|
"python-periphery==2.4.1",
|
||||||
"importlib_metadata==6.7.0 ; python_version=='3.7'",
|
"importlib_metadata>=6.7.0, <=8.7.1"
|
||||||
"importlib_metadata==8.2.0 ; python_version>='3.8'"
|
|
||||||
]
|
]
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.7"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -38,6 +37,8 @@ classifiers = [
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Programming Language :: Python :: 3.14"
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|
@ -53,7 +54,7 @@ speedups = [
|
||||||
"msgspec>=0.18.4 ; python_version>='3.8'",
|
"msgspec>=0.18.4 ; python_version>='3.8'",
|
||||||
"uvloop>=0.17.0"
|
"uvloop>=0.17.0"
|
||||||
]
|
]
|
||||||
dev = ["pre-commit"]
|
dev = ["pre-commit", "build", "mypy", "ruff", "twine"]
|
||||||
|
|
||||||
[tool.pdm.version]
|
[tool.pdm.version]
|
||||||
source = "scm"
|
source = "scm"
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ LOG_PATH="${MOONRAKER_LOG_PATH}"
|
||||||
DATA_PATH="${MOONRAKER_DATA_PATH}"
|
DATA_PATH="${MOONRAKER_DATA_PATH}"
|
||||||
INSTANCE_ALIAS="${MOONRAKER_ALIAS:-moonraker}"
|
INSTANCE_ALIAS="${MOONRAKER_ALIAS:-moonraker}"
|
||||||
SPEEDUPS="${MOONRAKER_SPEEDUPS:-n}"
|
SPEEDUPS="${MOONRAKER_SPEEDUPS:-n}"
|
||||||
|
DEV_INSTALL="${MOONRAKER_DEV_INSTALL:-n}"
|
||||||
|
PY_INST_TYPE="${MOONRAKER_PYTHON_INSTALL_TYPE:-venv}"
|
||||||
SERVICE_VERSION="1"
|
SERVICE_VERSION="1"
|
||||||
DISTRIBUTION=""
|
DISTRIBUTION=""
|
||||||
DISTRO_VERSION=""
|
DISTRO_VERSION=""
|
||||||
|
|
@ -25,6 +27,14 @@ if [ ! -z "${MOONRAKER_FORCE_DEFAULTS}" ]; then
|
||||||
FORCE_SYSTEM_INSTALL=$MOONRAKER_FORCE_DEFAULTS
|
FORCE_SYSTEM_INSTALL=$MOONRAKER_FORCE_DEFAULTS
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if this is a dev container, apply dev defaults if not set by environment
|
||||||
|
if [ "${MOONRAKER_VENDOR}" = "vscode-dev" ]; then
|
||||||
|
echo "VSCode Dev Container detected..."
|
||||||
|
[ -z "${MOONRAKER_DEV_INSTALL}" ] && DEV_INSTALL="y"
|
||||||
|
[ -z "${MOONRAKER_PYTHON_INSTALL_TYPE}" ] && PY_INST_TYPE="user"
|
||||||
|
[ -z "${MOONRAKER_DISABLE_SYSTEMCTL}" ] && DISABLE_SYSTEMCTL="y"
|
||||||
|
fi
|
||||||
|
|
||||||
# Force script to exit if an error occurs
|
# Force script to exit if an error occurs
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
|
@ -34,6 +44,7 @@ SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||||
# Determine if Moonraker is to be installed from source
|
# Determine if Moonraker is to be installed from source
|
||||||
if [ -f "${SRCDIR}/moonraker/__init__.py" ]; then
|
if [ -f "${SRCDIR}/moonraker/__init__.py" ]; then
|
||||||
echo "Installing from Moonraker source..."
|
echo "Installing from Moonraker source..."
|
||||||
|
cd $SRCDIR
|
||||||
IS_SRC_DIST="y"
|
IS_SRC_DIST="y"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -48,6 +59,7 @@ detect_distribution() {
|
||||||
# *** AUTO GENERATED OS PACKAGE SCRIPT START ***
|
# *** AUTO GENERATED OS PACKAGE SCRIPT START ***
|
||||||
get_pkgs_script=$(cat << EOF
|
get_pkgs_script=$(cat << EOF
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import re
|
import re
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
@ -101,15 +113,22 @@ class SysDepsParser:
|
||||||
version = distro_info.get("distro_version")
|
version = distro_info.get("distro_version")
|
||||||
if version:
|
if version:
|
||||||
self.distro_version = _convert_version(version)
|
self.distro_version = _convert_version(version)
|
||||||
self.vendor: str = ""
|
self.vendor: str = os.getenv("MOONRAKER_VENDOR", "")
|
||||||
if pathlib.Path("/etc/rpi-issue").is_file():
|
if not self.vendor and pathlib.Path("/etc/rpi-issue").is_file():
|
||||||
self.vendor = "raspberry-pi"
|
self.vendor = "raspberry-pi"
|
||||||
|
exclusions = os.getenv("MOONRAKER_EXCLUDED_PKGS", "")
|
||||||
|
self.exclusions: List[str] = [
|
||||||
|
excl.strip() for excl in exclusions.split() if excl.strip()
|
||||||
|
]
|
||||||
|
|
||||||
def _parse_spec(self, full_spec: str) -> str | None:
|
def _parse_spec(self, full_spec: str) -> str | None:
|
||||||
parts = full_spec.split(";", maxsplit=1)
|
parts = full_spec.split(";", maxsplit=1)
|
||||||
if len(parts) == 1:
|
|
||||||
return full_spec
|
|
||||||
pkg_name = parts[0].strip()
|
pkg_name = parts[0].strip()
|
||||||
|
if pkg_name in self.exclusions or not pkg_name:
|
||||||
|
logging.info(f"Package '{full_spec}' excluded by environment")
|
||||||
|
return None
|
||||||
|
if len(parts) == 1:
|
||||||
|
return pkg_name
|
||||||
expressions = re.split(r"( and | or )", parts[1].strip())
|
expressions = re.split(r"( and | or )", parts[1].strip())
|
||||||
if not len(expressions) & 1:
|
if not len(expressions) & 1:
|
||||||
logging.info(
|
logging.info(
|
||||||
|
|
@ -137,7 +156,7 @@ class SysDepsParser:
|
||||||
continue
|
continue
|
||||||
elif last_logical_op is None:
|
elif last_logical_op is None:
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Requirement specifier contains two seqential expressions "
|
f"Requirement specifier contains two sequential expressions "
|
||||||
f"without a logical operator: {full_spec}")
|
f"without a logical operator: {full_spec}")
|
||||||
return None
|
return None
|
||||||
dep_parts = re.split(r"(==|!=|<=|>=|<|>)", exp.strip())
|
dep_parts = re.split(r"(==|!=|<=|>=|<|>)", exp.strip())
|
||||||
|
|
@ -270,6 +289,12 @@ install_packages()
|
||||||
# Step 4: Create python virtual environment
|
# Step 4: Create python virtual environment
|
||||||
create_virtualenv()
|
create_virtualenv()
|
||||||
{
|
{
|
||||||
|
if [ $PY_INST_TYPE = "system" ]; then
|
||||||
|
pip_inst="pip install"
|
||||||
|
elif [ $PY_INST_TYPE = "user" ]; then
|
||||||
|
pip_inst="pip install --user"
|
||||||
|
else
|
||||||
|
pip_inst="${PYTHONDIR}/bin/pip install"
|
||||||
report_status "Installing python virtual environment..."
|
report_status "Installing python virtual environment..."
|
||||||
|
|
||||||
# If venv exists and user prompts a rebuild, then do so
|
# If venv exists and user prompts a rebuild, then do so
|
||||||
|
|
@ -279,29 +304,37 @@ create_virtualenv()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d ${PYTHONDIR} ]; then
|
if [ ! -d ${PYTHONDIR} ]; then
|
||||||
virtualenv -p /usr/bin/python3 ${PYTHONDIR}
|
virtualenv -p python3 ${PYTHONDIR}
|
||||||
#GET_PIP="${HOME}/get-pip.py"
|
#GET_PIP="${HOME}/get-pip.py"
|
||||||
#curl https://bootstrap.pypa.io/pip/3.6/get-pip.py -o ${GET_PIP}
|
#curl https://bootstrap.pypa.io/pip/3.6/get-pip.py -o ${GET_PIP}
|
||||||
#${PYTHONDIR}/bin/python ${GET_PIP}
|
#${PYTHONDIR}/bin/python ${GET_PIP}
|
||||||
#rm ${GET_PIP}
|
#rm ${GET_PIP}
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
echo "Using pip install command '${pip_inst}'..."
|
||||||
# Install/update dependencies
|
# Install/update dependencies
|
||||||
export SKIP_CYTHON=1
|
export SKIP_CYTHON=1
|
||||||
if [ $IS_SRC_DIST = "y" ]; then
|
if [ $IS_SRC_DIST = "y" ]; then
|
||||||
report_status "Installing Moonraker python dependencies..."
|
report_status "Installing Moonraker python dependencies..."
|
||||||
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-requirements.txt
|
$pip_inst -r ${SRCDIR}/scripts/moonraker-requirements.txt
|
||||||
|
|
||||||
if [ ${SPEEDUPS} = "y" ]; then
|
if [ $DEV_INSTALL = "y" ]; then
|
||||||
|
report_status "Installing dev requirements..."
|
||||||
|
$pip_inst -r ${SRCDIR}/scripts/moonraker-speedups.txt
|
||||||
|
$pip_inst -r ${SRCDIR}/scripts/moonraker-dev-reqs.txt
|
||||||
|
$pip_inst -r ${SRCDIR}/docs/doc-requirements.txt
|
||||||
|
elif [ $SPEEDUPS = "y" ]; then
|
||||||
report_status "Installing Speedups..."
|
report_status "Installing Speedups..."
|
||||||
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-speedups.txt
|
$pip_inst -r ${SRCDIR}/scripts/moonraker-speedups.txt
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
report_status "Installing Moonraker package via Pip..."
|
report_status "Installing Moonraker package via Pip..."
|
||||||
if [ ${SPEEDUPS} = "y" ]; then
|
if [ $DEV_INSTALL = "y" ]; then
|
||||||
${PYTHONDIR}/bin/pip install -U moonraker[speedups]
|
$pip_inst -U moonraker[speedups,dev]
|
||||||
|
elif [ $SPEEDUPS = "y" ]; then
|
||||||
|
$pip_inst -U moonraker[speedups]
|
||||||
else
|
else
|
||||||
${PYTHONDIR}/bin/pip install -U moonraker
|
$pip_inst -U moonraker
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
@ -349,9 +382,13 @@ EOF
|
||||||
# Step 6: Install startup script
|
# Step 6: Install startup script
|
||||||
install_script()
|
install_script()
|
||||||
{
|
{
|
||||||
|
if [ ! -d $SYSTEMDDIR ]; then
|
||||||
|
report_status "Systemd not detected, aborting service installation"
|
||||||
|
fi
|
||||||
# Create systemd service file
|
# Create systemd service file
|
||||||
ENV_FILE="${DATA_PATH}/systemd/moonraker.env"
|
ENV_FILE="${DATA_PATH}/systemd/moonraker.env"
|
||||||
if [ ! -f $ENV_FILE ] || [ $FORCE_SYSTEM_INSTALL = "y" ]; then
|
if [ ! -f $ENV_FILE ] || [ $FORCE_SYSTEM_INSTALL = "y" ]; then
|
||||||
|
report_status "Creating systemd environment file ${ENV_FILE}..."
|
||||||
rm -f $ENV_FILE
|
rm -f $ENV_FILE
|
||||||
env_vars="MOONRAKER_DATA_PATH=\"${DATA_PATH}\""
|
env_vars="MOONRAKER_DATA_PATH=\"${DATA_PATH}\""
|
||||||
[ -n "${CONFIG_PATH}" ] && env_vars="${env_vars}\nMOONRAKER_CONFIG_PATH=\"${CONFIG_PATH}\""
|
[ -n "${CONFIG_PATH}" ] && env_vars="${env_vars}\nMOONRAKER_CONFIG_PATH=\"${CONFIG_PATH}\""
|
||||||
|
|
@ -361,7 +398,9 @@ install_script()
|
||||||
echo -e $env_vars > $ENV_FILE
|
echo -e $env_vars > $ENV_FILE
|
||||||
fi
|
fi
|
||||||
[ -f $SERVICE_FILE ] && [ $FORCE_SYSTEM_INSTALL = "n" ] && return
|
[ -f $SERVICE_FILE ] && [ $FORCE_SYSTEM_INSTALL = "n" ] && return
|
||||||
report_status "Installing system start script..."
|
report_status "Installing systemd service unit..."
|
||||||
|
python_bin="${PYTHONDIR}/bin/python"
|
||||||
|
[ $PY_INST_TYPE != "venv" ] && python_bin="python3"
|
||||||
sudo groupadd -f moonraker-admin
|
sudo groupadd -f moonraker-admin
|
||||||
sudo /bin/sh -c "cat > ${SERVICE_FILE}" << EOF
|
sudo /bin/sh -c "cat > ${SERVICE_FILE}" << EOF
|
||||||
# systemd service file for moonraker
|
# systemd service file for moonraker
|
||||||
|
|
@ -379,7 +418,7 @@ User=$USER
|
||||||
SupplementaryGroups=moonraker-admin
|
SupplementaryGroups=moonraker-admin
|
||||||
RemainAfterExit=yes
|
RemainAfterExit=yes
|
||||||
EnvironmentFile=${ENV_FILE}
|
EnvironmentFile=${ENV_FILE}
|
||||||
ExecStart=${PYTHONDIR}/bin/python \$MOONRAKER_ARGS
|
ExecStart=${python_bin} \$MOONRAKER_ARGS
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=10
|
RestartSec=10
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
|
|
@ -1 +1,5 @@
|
||||||
pre-commit
|
pre-commit
|
||||||
|
build
|
||||||
|
mypy
|
||||||
|
ruff
|
||||||
|
twine
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
# Python dependencies for Moonraker
|
# Python dependencies for Moonraker
|
||||||
--find-links=python_wheels
|
--find-links=python_wheels
|
||||||
tornado>=6.2.0, <=6.5.1
|
tornado>=6.2.0, <=6.5.4
|
||||||
pyserial==3.4
|
pyserial==3.4
|
||||||
pillow>=9.5.0, <=11.1.0
|
pillow>=9.5.0, <=12.1.0
|
||||||
streaming-form-data>=1.11.0, <=1.19.1
|
streaming-form-data>=1.11.0, <=1.19.1
|
||||||
distro==1.9.0
|
distro==1.9.0
|
||||||
inotify-simple==1.3.5
|
inotify-simple==2.0.1
|
||||||
libnacl==2.1.0
|
libnacl==2.1.0
|
||||||
paho-mqtt==1.6.1
|
paho-mqtt==2.1.0
|
||||||
zeroconf==0.131.0
|
zeroconf>=0.131.0, <=0.148.0
|
||||||
preprocess-cancellation==0.2.1
|
preprocess-cancellation==0.2.1
|
||||||
jinja2==3.1.5
|
jinja2==3.1.6
|
||||||
dbus-fast>=2.21.3, <=2.44.1
|
dbus-fast>=2.21.3, <=3.1.2
|
||||||
apprise==1.9.2
|
apprise>=1.9.3, <=1.9.6
|
||||||
ldap3==2.9.1
|
ldap3==2.9.1
|
||||||
python-periphery==2.4.1
|
python-periphery==2.4.1
|
||||||
importlib_metadata==6.7.0 ; python_version=='3.7'
|
importlib_metadata>=6.7.0, <=8.7.1
|
||||||
importlib_metadata==8.2.0 ; python_version>='3.8'
|
|
||||||
|
|
|
||||||
BIN
scripts/python_wheels/zeroconf-0.136.2-py3-none-any.whl
Normal file
BIN
scripts/python_wheels/zeroconf-0.136.2-py3-none-any.whl
Normal file
Binary file not shown.
BIN
scripts/python_wheels/zeroconf-0.148.0-py3-none-any.whl
Normal file
BIN
scripts/python_wheels/zeroconf-0.148.0-py3-none-any.whl
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue