Compare commits
10 Commits
6f2c79f985
...
30ac5dfae9
Author | SHA1 | Date |
---|---|---|
Eric Callahan | 30ac5dfae9 | |
Eric Callahan | 0a8590643f | |
Eric Callahan | 3a42dac02b | |
Eric Callahan | 863d0c1e4b | |
Eric Callahan | b40751ba9d | |
Eric Callahan | c2409e813e | |
Eric Callahan | 83371cf135 | |
Eric Callahan | 247640cc27 | |
Eric Callahan | 63578aefd0 | |
Eric Callahan | 8debbf8ba4 |
|
@ -11,3 +11,4 @@ start_moonraker
|
||||||
.pdm-python
|
.pdm-python
|
||||||
build
|
build
|
||||||
dist
|
dist
|
||||||
|
share
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
repos:
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: sync-requirements
|
||||||
|
name: sync python requirements
|
||||||
|
language: system
|
||||||
|
entry: python3 scripts/sync_dependencies.py
|
||||||
|
files: ^pyproject.toml$
|
||||||
|
- id: sync-os-packages
|
||||||
|
name: sync packages
|
||||||
|
language: system
|
||||||
|
entry: python3 scripts/sync_dependencies.py
|
||||||
|
files: ^scripts/system-dependencies.json$
|
|
@ -6,6 +6,38 @@ The format is based on [Keep a Changelog].
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **server**: Use `asyncio.run` to launch the server as recommended by the
|
||||||
|
official Python documentation.
|
||||||
|
- **announcements**: Look for xml files at `<data_path>/development/announcements`
|
||||||
|
when `dev_mode` is set to True.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **confighelper**: Don't resolve symbolic links to the main configuration file.
|
||||||
|
|
||||||
|
|
||||||
|
## [0.9.2] - 2024-07-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **install**: Add support for installing Moonraker's python package via pip.
|
||||||
|
- **scripts**: Add script to sync python and system dependencies from
|
||||||
|
`pyproject.toml` and `system-dependencies.json` respectively.
|
||||||
|
- **dev**: Add pre-commit hook to call `sync_dependencies.py`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **build**: Build from sdist now correctly includes share data.
|
||||||
|
- **build**: Remove stray `.gitignore` from Python Wheel.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **install**: The `MOONRAKER_FORCE_DEFAULTS` environment variable has changed
|
||||||
|
to `MOONRAKER_FORCE_SYSTEM_INSTALL`.
|
||||||
|
|
||||||
|
## [0.9.1] - 2024-07-25
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **source_info**: Fixed `importlib.metadata` compatibility issues with python
|
||||||
|
versions 3.9 or older.
|
||||||
|
|
||||||
## [0.9.0] - 2024-07-25
|
## [0.9.0] - 2024-07-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -184,7 +216,9 @@ The format is based on [Keep a Changelog].
|
||||||
[api_changes.md]: api_changes.md
|
[api_changes.md]: api_changes.md
|
||||||
|
|
||||||
<!-- Versions -->
|
<!-- Versions -->
|
||||||
[unreleased]: https://github.com/Arksine/moonraker/compare/v0.9.0...HEAD
|
[unreleased]: https://github.com/Arksine/moonraker/compare/v0.9.2...HEAD
|
||||||
|
[0.9.2]: https://github.com/Arksine/moonraker/compare/v0.9.1...v0.9.2
|
||||||
|
[0.9.1]: https://github.com/Arksine/moonraker/compare/v0.9.0...v0.9.1
|
||||||
[0.9.0]: https://github.com/Arksine/moonraker/compare/v0.8.0...v0.9.0
|
[0.9.0]: https://github.com/Arksine/moonraker/compare/v0.8.0...v0.9.0
|
||||||
[0.8.0]: https://github.com/Arksine/moonraker/compare/v0.7.1...v0.8.0
|
[0.8.0]: https://github.com/Arksine/moonraker/compare/v0.7.1...v0.8.0
|
||||||
[0.7.1]: https://github.com/Arksine/moonraker/releases/tag/v0.7.1
|
[0.7.1]: https://github.com/Arksine/moonraker/releases/tag/v0.7.1
|
|
@ -2222,7 +2222,7 @@ folder that provides supplemental information for the application. The
|
||||||
Moonraker uses the [PDM backend](https://backend.pdm-project.org/) to perform
|
Moonraker uses the [PDM backend](https://backend.pdm-project.org/) to perform
|
||||||
its package builds. An example of a pdm build script that generates a
|
its package builds. An example of a pdm build script that generates a
|
||||||
`release_info` file may be found
|
`release_info` file may be found
|
||||||
[here](https://github.com/Arksine/moonraker/blob/master/scripts/pdm_build_dist.py).
|
[here](https://github.com/Arksine/moonraker/blob/master/pdm_build.py).
|
||||||
|
|
||||||
#### The System Dependencies File Format
|
#### The System Dependencies File Format
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
This document provides a guide on how to install Moonraker on a Raspberry
|
This document provides a guide on how to install Moonraker on a Debian
|
||||||
Pi running Raspian/Rasperry Pi OS. Other SBCs and/or linux distributions
|
based Linux Distributions. Other linux distributions may work, however
|
||||||
may work, however they may need a custom install script. Moonraker
|
they may need a custom install script. Moonraker requires Python 3.7 or
|
||||||
requires Python 3.7 or greater, verify that your distribution's
|
greater, verify that your distribution's Python 3 packages meet this
|
||||||
Python 3 packages meet this requirement.
|
requirement.
|
||||||
|
|
||||||
### Installing Klipper
|
### Installing Klipper
|
||||||
|
|
||||||
Klipper should be installed prior to installing Moonraker. Please see
|
Klipper should be installed prior to installing Moonraker. Please see
|
||||||
[Klipper's Documention](https://klipper3d.com/Overview.html) for details.
|
[Klipper's Documentation](https://klipper3d.com/Overview.html) for details.
|
||||||
After installing Klipper you should make sure to add Moonraker's
|
After installing Klipper you should make sure to add Moonraker's
|
||||||
[configuration requirements](#klipper-configuration-requirements).
|
[configuration requirements](#klipper-configuration-requirements).
|
||||||
|
|
||||||
### Klipper Configuration Requirements
|
#### Klipper Configuration Requirements
|
||||||
|
|
||||||
Moonraker depends on the following Klippy extras for full functionality:
|
Moonraker depends on the following Klippy extras for full functionality:
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ missing one or both, you can simply add the bare sections to `printer.cfg`:
|
||||||
path: ~/printer_data/gcodes
|
path: ~/printer_data/gcodes
|
||||||
```
|
```
|
||||||
|
|
||||||
### Enabling Klipper's Unix Domain Socket Server
|
#### Enabling Klipper's Unix Domain Socket Server
|
||||||
|
|
||||||
After Klipper is installed it may be necessary to modify its `defaults` file in
|
After Klipper is installed it may be necessary to modify its `defaults` file in
|
||||||
order to enable the Unix Domain Socket. Begin by opening the file in your
|
order to enable the Unix Domain Socket. Begin by opening the file in your
|
||||||
|
@ -53,7 +53,7 @@ KLIPPY_EXEC=/home/pi/klippy-env/bin/python
|
||||||
KLIPPY_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer.cfg -l /tmp/klippy.log"
|
KLIPPY_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer.cfg -l /tmp/klippy.log"
|
||||||
```
|
```
|
||||||
|
|
||||||
Add `-a /tmp/klippy_uds` to KLIPPY_ARGS:
|
Add `-a /home/pi/printer_data/comms/klippy.sock` to KLIPPY_ARGS:
|
||||||
```
|
```
|
||||||
# Configuration for /etc/init.d/klipper
|
# Configuration for /etc/init.d/klipper
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ KLIPPY_USER=pi
|
||||||
|
|
||||||
KLIPPY_EXEC=/home/pi/klippy-env/bin/python
|
KLIPPY_EXEC=/home/pi/klippy-env/bin/python
|
||||||
|
|
||||||
KLIPPY_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer.cfg -l /tmp/klippy.log -a /tmp/klippy_uds"
|
KLIPPY_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer.cfg -l /tmp/klippy.log -a /home/pi/printer_data/comms/klippy.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! note
|
!!! note
|
||||||
|
@ -79,7 +79,7 @@ KLIPPY_USER=pi
|
||||||
|
|
||||||
KLIPPY_EXEC=/home/pi/klippy-env/bin/python
|
KLIPPY_EXEC=/home/pi/klippy-env/bin/python
|
||||||
|
|
||||||
KLIPPY_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer_data/config/printer.cfg -l /home/pi/printer_data/logs/klippy.log -a /tmp/klippy_uds"
|
KLIPPY_ARGS="/home/pi/klipper/klippy/klippy.py /home/pi/printer_data/config/printer.cfg -l /home/pi/printer_data/logs/klippy.log -a /home/pi/printer_data/comms/klippy.sock"
|
||||||
```
|
```
|
||||||
|
|
||||||
Moonraker's install script will create the data folder, however you
|
Moonraker's install script will create the data folder, however you
|
||||||
|
@ -94,13 +94,52 @@ mv printer.cfg ~/printer_data/config
|
||||||
|
|
||||||
### Installing Moonraker
|
### Installing Moonraker
|
||||||
|
|
||||||
Begin by cloning the git respository:
|
Moonraker provides an install script that can be used to facilitate
|
||||||
|
installation. The type of installation depends on where the install
|
||||||
|
script is located on the host file system. If the install script is
|
||||||
|
downloaded and run individually, the script will install Moonraker as
|
||||||
|
a Python Package using pip. If the script is run from Moonraker's
|
||||||
|
original source it will install Moonraker from source.
|
||||||
|
|
||||||
|
Prior to installation it is necessary to open a terminal on the host
|
||||||
|
machine, or SSH into it. It is recommended to read this entire
|
||||||
|
section before proceeding with the installation.
|
||||||
|
|
||||||
|
#### Installing the Moonraker Python package
|
||||||
|
|
||||||
|
The Python Package version of Moonraker will receive fewer updates, and
|
||||||
|
should generally be more stable. This is intended for users that do
|
||||||
|
not need to run unofficial 3rd party extensions, such as
|
||||||
|
[Moonraker-Timelapse](https://github.com/mainsail-crew/moonraker-timelapse),
|
||||||
|
and do not desire to run the "bleeding edge" version of Moonraker.
|
||||||
|
|
||||||
|
To perform this installation, download Moonraker's
|
||||||
|
[install script](https://raw.githubusercontent.com/Arksine/moonraker/master/scripts/install-moonraker.sh)
|
||||||
|
from Github, then run the installer:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd ~
|
||||||
|
wget https://raw.githubusercontent.com/Arksine/moonraker/master/scripts/install-moonraker.sh
|
||||||
|
./install-moonraker.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Installing Moonraker from source
|
||||||
|
|
||||||
|
Moonraker can be run directly from source. This method of installation will
|
||||||
|
clone Moonraker's git repository, and may receive frequent bleeding edge
|
||||||
|
updates. Users who want to test Moonraker, run unofficial modifications, and
|
||||||
|
do not mind SSHing into the host to correct issues should choose this option.
|
||||||
|
|
||||||
|
To install Moonraker from source, clone its git repository then run the installer:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd ~
|
cd ~
|
||||||
git clone https://github.com/Arksine/moonraker.git
|
git clone https://github.com/Arksine/moonraker.git
|
||||||
|
~/moonraker/scripts/install-moonraker.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Customizing the installation
|
||||||
|
|
||||||
The install script will attempt to create a basic configuration if
|
The install script will attempt to create a basic configuration if
|
||||||
`moonraker.conf` does not exist at the expected location, however if you
|
`moonraker.conf` does not exist at the expected location, however if you
|
||||||
prefer to have Moonraker start with a robust configuration you may create
|
prefer to have Moonraker start with a robust configuration you may create
|
||||||
|
@ -109,20 +148,17 @@ it now. By default the configuration file should be located at
|
||||||
data path may be configured using the script's command line options.
|
data path may be configured using the script's command line options.
|
||||||
The [sample moonraker.conf](./moonraker.conf) may be used as a starting
|
The [sample moonraker.conf](./moonraker.conf) may be used as a starting
|
||||||
point, full details can be found in the
|
point, full details can be found in the
|
||||||
[confguration documentation](./configuration.md).
|
[configuration documentation](./configuration.md).
|
||||||
|
|
||||||
For a default installation run the following commands:
|
|
||||||
```
|
|
||||||
cd ~/moonraker/scripts
|
|
||||||
./install-moonraker.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
The install script has a few command line options that may be useful,
|
The install script has several command line options that may be useful,
|
||||||
particularly for those upgrading:
|
particularly for those upgrading:
|
||||||
|
|
||||||
- `-f`:
|
- `-f`:
|
||||||
Force an overwrite of Moonraker's systemd script. By default the
|
Force an overwrite of Moonraker's systemd script. In addition, a
|
||||||
the systemd script will not be modified if it exists.
|
new `moonraker.env` file will be created, and the PolKit rules will
|
||||||
|
be re-installed. By default these items will not be modified if
|
||||||
|
they exist.
|
||||||
- `-a <alias>`:
|
- `-a <alias>`:
|
||||||
The installer uses this option to determine the name of the service
|
The installer uses this option to determine the name of the service
|
||||||
to install. If `-d` is not provided then this options will also be
|
to install. If `-d` is not provided then this options will also be
|
||||||
|
@ -133,7 +169,7 @@ particularly for those upgrading:
|
||||||
files and directories used by moonraker. See the `Data Folder Structure`
|
files and directories used by moonraker. See the `Data Folder Structure`
|
||||||
section for details. If omitted this defaults to `$HOME/printer_data`.
|
section for details. If omitted this defaults to `$HOME/printer_data`.
|
||||||
- `-c <path to configuration file>`
|
- `-c <path to configuration file>`
|
||||||
Specifies the path to Moonraker's configuation file. By default the
|
Specifies the path to Moonraker's configuration file. By default the
|
||||||
configuration is expected at `<data_folder>/config/moonraker.conf`. ie:
|
configuration is expected at `<data_folder>/config/moonraker.conf`. ie:
|
||||||
`/home/pi/printer_data/config/moonraker.conf`.
|
`/home/pi/printer_data/config/moonraker.conf`.
|
||||||
- `-l <path to log file>`
|
- `-l <path to log file>`
|
||||||
|
@ -157,25 +193,26 @@ variables:
|
||||||
|
|
||||||
- `MOONRAKER_VENV`
|
- `MOONRAKER_VENV`
|
||||||
- `MOONRAKER_REBUILD_ENV`
|
- `MOONRAKER_REBUILD_ENV`
|
||||||
- `MOONRAKER_FORCE_DEFAULTS`
|
- `MOONRAKER_FORCE_SYSTEM_INSTALL`
|
||||||
- `MOONRAKER_DISABLE_SYSTEMCTL`
|
- `MOONRAKER_DISABLE_SYSTEMCTL`
|
||||||
- `MOONRAKER_SKIP_POLKIT`
|
- `MOONRAKER_SKIP_POLKIT`
|
||||||
- `MOONRAKER_CONFIG_PATH`
|
- `MOONRAKER_CONFIG_PATH`
|
||||||
- `MOONAKER_LOG_PATH`
|
- `MOONRAKER_LOG_PATH`
|
||||||
- `MOONRAKER_DATA_PATH`
|
- `MOONRAKER_DATA_PATH`
|
||||||
- `MOONRAKER_SPEEDUPS`
|
- `MOONRAKER_SPEEDUPS`
|
||||||
|
|
||||||
When the script completes it should start both Moonraker and Klipper. In
|
When the script completes it should start the Moonraker system service. If Klipper
|
||||||
`klippy.log` you should find the following entry:
|
is running and Moonraker is able to establish a connection the following log entry
|
||||||
|
should be available in `klippy.log`:
|
||||||
|
|
||||||
`webhooks client <uid>: Client info {'program': 'Moonraker', 'version': '<version>'}`
|
`webhooks client <uid>: Client info {'program': 'Moonraker', 'version': '<version>'}`
|
||||||
|
|
||||||
Now you may install a client, such as
|
Now you may wish to install a frontend, such as
|
||||||
[Mainsail](https://github.com/mainsail-crew/mainsail) or
|
[Mainsail](https://github.com/mainsail-crew/mainsail) or
|
||||||
[Fluidd](https://github.com/fluidd-core/fluidd).
|
[Fluidd](https://github.com/fluidd-core/fluidd).
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
Moonraker's install script no longer includes the nginx dependency.
|
Moonraker's installer does not include nginx as a dependency.
|
||||||
If you want to install one of the above clients on the local machine,
|
If you want to install one of the above clients on the local machine,
|
||||||
you may want to first install nginx (`sudo apt install nginx` on
|
you may want to first install nginx (`sudo apt install nginx` on
|
||||||
debian/ubuntu distros).
|
debian/ubuntu distros).
|
||||||
|
@ -299,7 +336,7 @@ Following are some items to take note of:
|
||||||
- The `EnvironmentFile` field contains Moonraker's arguments. See the
|
- The `EnvironmentFile` field contains Moonraker's arguments. See the
|
||||||
[environment file section](#the-environment-file) for details.
|
[environment file section](#the-environment-file) for details.
|
||||||
- The `ExecStart` field begins with the python executable, followed by
|
- The `ExecStart` field begins with the python executable, followed by
|
||||||
by the enviroment variable `MOONRAKER_ARGS`. This variable is set in
|
by the environment variable `MOONRAKER_ARGS`. This variable is set in
|
||||||
the environment file.
|
the environment file.
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,7 +399,7 @@ If is necessary to run Moonraker without logging to a file the
|
||||||
While moonraker will still log to stdout, all requests for support
|
While moonraker will still log to stdout, all requests for support
|
||||||
must be accompanied by `moonraker.log`.
|
must be accompanied by `moonraker.log`.
|
||||||
|
|
||||||
Each command line argument has an associated enviroment variable that may
|
Each command line argument has an associated environment variable that may
|
||||||
be used to specify options in place of the command line.
|
be used to specify options in place of the command line.
|
||||||
|
|
||||||
- `MOONRAKER_DATA_PATH="<data path>"`: equivalent to `-d <data path>`
|
- `MOONRAKER_DATA_PATH="<data path>"`: equivalent to `-d <data path>`
|
||||||
|
@ -460,7 +497,7 @@ with the `sudo` prefix. This has significant downsides:
|
||||||
wants to poll information about the system.
|
wants to poll information about the system.
|
||||||
|
|
||||||
Moonraker now supports communicating with system services via D-Bus.
|
Moonraker now supports communicating with system services via D-Bus.
|
||||||
Operations that require elevated privileges are authrorized through
|
Operations that require elevated privileges are authorized through
|
||||||
PolicyKit. On startup Moonraker will check for the necessary privileges
|
PolicyKit. On startup Moonraker will check for the necessary privileges
|
||||||
and warn users if they are not available. Warnings are presented in
|
and warn users if they are not available. Warnings are presented in
|
||||||
`moonraker.log` and directly to the user through some clients.
|
`moonraker.log` and directly to the user through some clients.
|
||||||
|
|
|
@ -422,9 +422,13 @@ class RssFeed:
|
||||||
self.etag: Optional[str] = None
|
self.etag: Optional[str] = None
|
||||||
self.dev_xml_path: Optional[pathlib.Path] = None
|
self.dev_xml_path: Optional[pathlib.Path] = None
|
||||||
if dev_mode:
|
if dev_mode:
|
||||||
res_dir = pathlib.Path(__file__).parent.parent.parent.resolve()
|
data_path = pathlib.Path(self.server.get_app_arg("data_path"))
|
||||||
res_path = res_dir.joinpath(".devel/announcement_xml")
|
dev_folder = data_path.joinpath("development/announcements")
|
||||||
self.dev_xml_path = res_path.joinpath(self.xml_file)
|
dev_folder.mkdir(parents=True, exist_ok=True)
|
||||||
|
self.dev_xml_path = dev_folder.joinpath(self.xml_file)
|
||||||
|
logging.info(
|
||||||
|
f"Announcement Feed {name}: Dev XML path set to {self.dev_xml_path}"
|
||||||
|
)
|
||||||
|
|
||||||
async def initialize(self) -> None:
|
async def initialize(self) -> None:
|
||||||
self.etag = await self.database.get_item(
|
self.etag = await self.database.get_item(
|
||||||
|
|
|
@ -531,7 +531,7 @@ class ConfigHelper:
|
||||||
|
|
||||||
def create_backup(self) -> None:
|
def create_backup(self) -> None:
|
||||||
cfg_path = self.server.get_app_args()["config_file"]
|
cfg_path = self.server.get_app_args()["config_file"]
|
||||||
cfg = pathlib.Path(cfg_path).expanduser().resolve()
|
cfg = pathlib.Path(cfg_path).expanduser()
|
||||||
backup = cfg.parent.joinpath(f".{cfg.name}.bkp")
|
backup = cfg.parent.joinpath(f".{cfg.name}.bkp")
|
||||||
backup_fp: Optional[TextIO] = None
|
backup_fp: Optional[TextIO] = None
|
||||||
try:
|
try:
|
||||||
|
@ -1108,7 +1108,10 @@ class FileSourceWrapper(ConfigSourceWrapper):
|
||||||
def get_configuration(
|
def get_configuration(
|
||||||
server: Server, app_args: Dict[str, Any]
|
server: Server, app_args: Dict[str, Any]
|
||||||
) -> ConfigHelper:
|
) -> ConfigHelper:
|
||||||
start_path = pathlib.Path(app_args['config_file']).expanduser().resolve()
|
cfg_file = app_args["config_file"]
|
||||||
|
if app_args["is_backup_config"]:
|
||||||
|
cfg_file = app_args["backup_config"]
|
||||||
|
start_path = pathlib.Path(cfg_file).expanduser().absolute()
|
||||||
source = FileSourceWrapper(server)
|
source = FileSourceWrapper(server)
|
||||||
source.read_file(start_path)
|
source.read_file(start_path)
|
||||||
if not source.config.has_section('server'):
|
if not source.config.has_section('server'):
|
||||||
|
@ -1116,7 +1119,7 @@ def get_configuration(
|
||||||
return ConfigHelper(server, source, 'server', {})
|
return ConfigHelper(server, source, 'server', {})
|
||||||
|
|
||||||
def find_config_backup(cfg_path: str) -> Optional[str]:
|
def find_config_backup(cfg_path: str) -> Optional[str]:
|
||||||
cfg = pathlib.Path(cfg_path).expanduser().resolve()
|
cfg = pathlib.Path(cfg_path).expanduser()
|
||||||
backup = cfg.parent.joinpath(f".{cfg.name}.bkp")
|
backup = cfg.parent.joinpath(f".{cfg.name}.bkp")
|
||||||
if backup.is_file():
|
if backup.is_file():
|
||||||
return str(backup)
|
return str(backup)
|
||||||
|
|
|
@ -27,7 +27,7 @@ _uvl_var = os.getenv("MOONRAKER_ENABLE_UVLOOP", "y").lower()
|
||||||
_uvl_enabled = False
|
_uvl_enabled = False
|
||||||
if _uvl_var in ["y", "yes", "true"]:
|
if _uvl_var in ["y", "yes", "true"]:
|
||||||
with contextlib.suppress(ImportError):
|
with contextlib.suppress(ImportError):
|
||||||
import uvloop
|
import uvloop # type: ignore
|
||||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||||
_uvl_enabled = True
|
_uvl_enabled = True
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class EventLoop:
|
||||||
return self.aioloop
|
return self.aioloop
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.aioloop = self._create_new_loop()
|
self.aioloop = asyncio.get_running_loop()
|
||||||
self.add_signal_handler = self.aioloop.add_signal_handler
|
self.add_signal_handler = self.aioloop.add_signal_handler
|
||||||
self.remove_signal_handler = self.aioloop.remove_signal_handler
|
self.remove_signal_handler = self.aioloop.remove_signal_handler
|
||||||
self.add_reader = self.aioloop.add_reader
|
self.add_reader = self.aioloop.add_reader
|
||||||
|
@ -126,7 +126,7 @@ class EventLoop:
|
||||||
host, port, family=0, type=socket.SOCK_STREAM
|
host, port, family=0, type=socket.SOCK_STREAM
|
||||||
)
|
)
|
||||||
for res in ainfo:
|
for res in ainfo:
|
||||||
af, socktype, proto, canonname, sa = res
|
af, socktype, proto, _cannon_name, _sa = res
|
||||||
sock = None
|
sock = None
|
||||||
try:
|
try:
|
||||||
sock = socket.socket(af, socktype, proto)
|
sock = socket.socket(af, socktype, proto)
|
||||||
|
@ -152,12 +152,6 @@ class EventLoop:
|
||||||
else:
|
else:
|
||||||
raise socket.error("getaddrinfo returns an empty list")
|
raise socket.error("getaddrinfo returns an empty list")
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self.aioloop.run_forever()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.aioloop.stop()
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.aioloop.close()
|
self.aioloop.close()
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,8 @@ class LogManager:
|
||||||
return eventloop.run_in_thread(self.file_hdlr.doRollover)
|
return eventloop.run_in_thread(self.file_hdlr.doRollover)
|
||||||
|
|
||||||
def stop_logging(self):
|
def stop_logging(self):
|
||||||
self.listener.stop()
|
if self.listener is not None:
|
||||||
|
self.listener.stop()
|
||||||
|
|
||||||
async def _handle_log_rollover(
|
async def _handle_log_rollover(
|
||||||
self, web_request: WebRequest
|
self, web_request: WebRequest
|
||||||
|
|
|
@ -89,6 +89,7 @@ class Server:
|
||||||
self.ssl_port: int = config.getint('ssl_port', 7130)
|
self.ssl_port: int = config.getint('ssl_port', 7130)
|
||||||
self.exit_reason: str = ""
|
self.exit_reason: str = ""
|
||||||
self.server_running: bool = False
|
self.server_running: bool = False
|
||||||
|
self.app_running_evt = asyncio.Event()
|
||||||
self.pip_recovery_attempted: bool = False
|
self.pip_recovery_attempted: bool = False
|
||||||
|
|
||||||
# Configure Debug Logging
|
# Configure Debug Logging
|
||||||
|
@ -198,10 +199,8 @@ class Server:
|
||||||
await self.event_loop.run_in_thread(self.config.create_backup)
|
await self.event_loop.run_in_thread(self.config.create_backup)
|
||||||
|
|
||||||
machine: Machine = self.lookup_component("machine")
|
machine: Machine = self.lookup_component("machine")
|
||||||
if await machine.validate_installation():
|
restarting = await machine.validate_installation()
|
||||||
return
|
if not restarting and start_server:
|
||||||
|
|
||||||
if start_server:
|
|
||||||
await self.start_server()
|
await self.start_server()
|
||||||
|
|
||||||
async def start_server(self, connect_to_klippy: bool = True) -> None:
|
async def start_server(self, connect_to_klippy: bool = True) -> None:
|
||||||
|
@ -218,6 +217,9 @@ class Server:
|
||||||
if connect_to_klippy:
|
if connect_to_klippy:
|
||||||
self.klippy_connection.connect()
|
self.klippy_connection.connect()
|
||||||
|
|
||||||
|
async def run_until_exit(self) -> None:
|
||||||
|
await self.app_running_evt.wait()
|
||||||
|
|
||||||
def add_log_rollover_item(
|
def add_log_rollover_item(
|
||||||
self, name: str, item: str, log: bool = True
|
self, name: str, item: str, log: bool = True
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -488,7 +490,7 @@ class Server:
|
||||||
|
|
||||||
self.exit_reason = exit_reason
|
self.exit_reason = exit_reason
|
||||||
self.event_loop.remove_signal_handler(signal.SIGTERM)
|
self.event_loop.remove_signal_handler(signal.SIGTERM)
|
||||||
self.event_loop.stop()
|
self.app_running_evt.set()
|
||||||
|
|
||||||
async def _handle_server_restart(self, web_request: WebRequest) -> str:
|
async def _handle_server_restart(self, web_request: WebRequest) -> str:
|
||||||
self.event_loop.register_callback(self._stop_server)
|
self.event_loop.register_callback(self._stop_server)
|
||||||
|
@ -540,6 +542,45 @@ class Server:
|
||||||
'files': cfg_file_list
|
'files': cfg_file_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def launch_server(
|
||||||
|
log_manager: LogManager, app_args: Dict[str, Any]
|
||||||
|
) -> Optional[int]:
|
||||||
|
eventloop = EventLoop()
|
||||||
|
startup_warnings: List[str] = app_args["startup_warnings"]
|
||||||
|
try:
|
||||||
|
server = Server(app_args, log_manager, eventloop)
|
||||||
|
server.load_components()
|
||||||
|
except confighelper.ConfigError as e:
|
||||||
|
logging.exception("Server Config Error")
|
||||||
|
backup_cfg: Optional[str] = app_args["backup_config"]
|
||||||
|
if app_args["is_backup_config"] or backup_cfg is None:
|
||||||
|
return 1
|
||||||
|
app_args["is_backup_config"] = True
|
||||||
|
startup_warnings.append(
|
||||||
|
f"Server configuration error: {e}\n"
|
||||||
|
f"Loading most recent working configuration: '{backup_cfg}'\n"
|
||||||
|
f"Please fix the issue in moonraker.conf and restart the server."
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Moonraker Error")
|
||||||
|
return 1
|
||||||
|
try:
|
||||||
|
await server.server_init()
|
||||||
|
await server.run_until_exit()
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Server Running Error")
|
||||||
|
return 1
|
||||||
|
if server.exit_reason == "terminate":
|
||||||
|
return 0
|
||||||
|
# Restore the original config and clear the warning
|
||||||
|
# before the server restarts
|
||||||
|
if app_args["is_backup_config"]:
|
||||||
|
startup_warnings.pop()
|
||||||
|
app_args["is_backup_config"] = False
|
||||||
|
del server
|
||||||
|
return None
|
||||||
|
|
||||||
def main(from_package: bool = True) -> None:
|
def main(from_package: bool = True) -> None:
|
||||||
def get_env_bool(key: str) -> bool:
|
def get_env_bool(key: str) -> bool:
|
||||||
return os.getenv(key, "").lower() in ["y", "yes", "true"]
|
return os.getenv(key, "").lower() in ["y", "yes", "true"]
|
||||||
|
@ -635,6 +676,7 @@ def main(from_package: bool = True) -> None:
|
||||||
"data_path": str(data_path),
|
"data_path": str(data_path),
|
||||||
"is_default_data_path": cmd_line_args.datapath is None,
|
"is_default_data_path": cmd_line_args.datapath is None,
|
||||||
"config_file": cfg_file,
|
"config_file": cfg_file,
|
||||||
|
"backup_config": confighelper.find_config_backup(cfg_file),
|
||||||
"startup_warnings": startup_warnings,
|
"startup_warnings": startup_warnings,
|
||||||
"verbose": cmd_line_args.verbose,
|
"verbose": cmd_line_args.verbose,
|
||||||
"debug": cmd_line_args.debug,
|
"debug": cmd_line_args.debug,
|
||||||
|
@ -661,60 +703,14 @@ def main(from_package: bool = True) -> None:
|
||||||
log_manager = LogManager(app_args, startup_warnings)
|
log_manager = LogManager(app_args, startup_warnings)
|
||||||
|
|
||||||
# Start asyncio event loop and server
|
# Start asyncio event loop and server
|
||||||
event_loop = EventLoop()
|
|
||||||
alt_config_loaded = False
|
|
||||||
estatus = 0
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
estatus = asyncio.run(launch_server(log_manager, app_args))
|
||||||
server = Server(app_args, log_manager, event_loop)
|
if estatus is not None:
|
||||||
server.load_components()
|
|
||||||
except confighelper.ConfigError as e:
|
|
||||||
backup_cfg = confighelper.find_config_backup(cfg_file)
|
|
||||||
logging.exception("Server Config Error")
|
|
||||||
if alt_config_loaded or backup_cfg is None:
|
|
||||||
estatus = 1
|
|
||||||
break
|
|
||||||
app_args["config_file"] = backup_cfg
|
|
||||||
app_args["is_backup_config"] = True
|
|
||||||
warn_list = list(startup_warnings)
|
|
||||||
app_args["startup_warnings"] = warn_list
|
|
||||||
warn_list.append(
|
|
||||||
f"Server configuration error: {e}\n"
|
|
||||||
f"Loaded server from most recent working configuration:"
|
|
||||||
f" '{app_args['config_file']}'\n"
|
|
||||||
f"Please fix the issue in moonraker.conf and restart "
|
|
||||||
f"the server."
|
|
||||||
)
|
|
||||||
alt_config_loaded = True
|
|
||||||
continue
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Moonraker Error")
|
|
||||||
estatus = 1
|
|
||||||
break
|
break
|
||||||
try:
|
|
||||||
event_loop.register_callback(server.server_init)
|
|
||||||
event_loop.start()
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Server Running Error")
|
|
||||||
estatus = 1
|
|
||||||
break
|
|
||||||
if server.exit_reason == "terminate":
|
|
||||||
break
|
|
||||||
# Restore the original config and clear the warning
|
|
||||||
# before the server restarts
|
|
||||||
if alt_config_loaded:
|
|
||||||
app_args["config_file"] = cfg_file
|
|
||||||
app_args["startup_warnings"] = startup_warnings
|
|
||||||
app_args["is_backup_config"] = False
|
|
||||||
alt_config_loaded = False
|
|
||||||
event_loop.close()
|
|
||||||
# Since we are running outside of the the server
|
# Since we are running outside of the the server
|
||||||
# it is ok to use a blocking sleep here
|
# it is ok to use a blocking sleep here
|
||||||
time.sleep(.5)
|
time.sleep(.5)
|
||||||
logging.info("Attempting Server Restart...")
|
logging.info("Attempting Server Restart...")
|
||||||
del server
|
|
||||||
event_loop.reset()
|
|
||||||
event_loop.close()
|
|
||||||
logging.info("Server Shutdown")
|
logging.info("Server Shutdown")
|
||||||
log_manager.stop_logging()
|
log_manager.stop_logging()
|
||||||
exit(estatus)
|
exit(estatus)
|
||||||
|
|
|
@ -13,10 +13,7 @@ import re
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
if sys.version_info < (3, 8):
|
from importlib_metadata import Distribution, PathDistribution, PackageMetadata
|
||||||
from importlib_metadata import Distribution, PathDistribution, PackageMetadata
|
|
||||||
else:
|
|
||||||
from importlib.metadata import Distribution, PathDistribution, PackageMetadata
|
|
||||||
from .exceptions import ServerError
|
from .exceptions import ServerError
|
||||||
|
|
||||||
# Annotation imports
|
# Annotation imports
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
# Wheel Setup Script for generating metadata
|
||||||
|
#
|
||||||
|
# Copyright (C) 2023 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Dict, Any, TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pdm.backend.hooks.base import Context
|
||||||
|
|
||||||
|
__package_name__ = "moonraker"
|
||||||
|
__dependencies__ = "scripts/system-dependencies.json"
|
||||||
|
|
||||||
|
def _run_git_command(cmd: str) -> str:
|
||||||
|
prog = shlex.split(cmd)
|
||||||
|
process = subprocess.Popen(
|
||||||
|
prog, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
|
)
|
||||||
|
ret, err = process.communicate()
|
||||||
|
retcode = process.wait()
|
||||||
|
if retcode == 0:
|
||||||
|
return ret.strip().decode()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_commit_sha(source_path: pathlib.Path) -> str:
|
||||||
|
cmd = f"git -C {source_path} rev-parse HEAD"
|
||||||
|
return _run_git_command(cmd)
|
||||||
|
|
||||||
|
def retrieve_git_version(source_path: pathlib.Path) -> str:
|
||||||
|
cmd = f"git -C {source_path} describe --always --tags --long --dirty"
|
||||||
|
return _run_git_command(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def pdm_build_clean(context: Context) -> None:
|
||||||
|
share_path: pathlib.Path = context.root.joinpath("share")
|
||||||
|
if share_path.exists():
|
||||||
|
shutil.rmtree(str(share_path))
|
||||||
|
|
||||||
|
def pdm_build_initialize(context: Context) -> None:
|
||||||
|
context.ensure_build_dir()
|
||||||
|
proj_name: str = context.config.metadata['name']
|
||||||
|
build_dir = pathlib.Path(context.build_dir)
|
||||||
|
data_path = context.root.joinpath(f"share/{proj_name}")
|
||||||
|
pkg_path = build_dir.joinpath(__package_name__)
|
||||||
|
pkg_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
rinfo_path: pathlib.Path = pkg_path.joinpath("release_info")
|
||||||
|
rinfo_data: str = ""
|
||||||
|
if context.root.joinpath(".git").exists():
|
||||||
|
build_ver: str = context.config.metadata['version']
|
||||||
|
build_time = datetime.now(timezone.utc)
|
||||||
|
urls: Dict[str, str] = context.config.metadata['urls']
|
||||||
|
release_info: Dict[str, Any] = {
|
||||||
|
"project_name": proj_name,
|
||||||
|
"package_name": __package_name__,
|
||||||
|
"urls": {key.lower(): val for key, val in urls.items()},
|
||||||
|
"package_version": build_ver,
|
||||||
|
"git_version": retrieve_git_version(context.root),
|
||||||
|
"commit_sha": get_commit_sha(context.root),
|
||||||
|
"build_time": datetime.isoformat(build_time, timespec="seconds")
|
||||||
|
}
|
||||||
|
if __dependencies__:
|
||||||
|
deps = pathlib.Path(context.root).joinpath(__dependencies__)
|
||||||
|
if deps.is_file():
|
||||||
|
dep_info: Dict[str, Any] = json.loads(deps.read_bytes())
|
||||||
|
release_info["system_dependencies"] = dep_info
|
||||||
|
# Write the release info to both the package and the data path
|
||||||
|
rinfo_data = json.dumps(release_info, indent=4)
|
||||||
|
rinfo_path.write_text(rinfo_data)
|
||||||
|
else:
|
||||||
|
rinfo_path = context.root.joinpath(f"{proj_name}/release_info")
|
||||||
|
if rinfo_path.is_file():
|
||||||
|
rinfo_data = rinfo_path.read_text()
|
||||||
|
else:
|
||||||
|
rinfo_data = ""
|
||||||
|
data_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
if rinfo_data:
|
||||||
|
data_path.joinpath("release_info").write_text(rinfo_data)
|
||||||
|
scripts_path: pathlib.Path = context.root.joinpath("scripts")
|
||||||
|
scripts_dest: pathlib.Path = data_path.joinpath("scripts")
|
||||||
|
scripts_dest.mkdir()
|
||||||
|
for item in scripts_path.iterdir():
|
||||||
|
if item.name in ("__pycache__", "python_wheels"):
|
||||||
|
continue
|
||||||
|
if item.is_dir():
|
||||||
|
shutil.copytree(str(item), str(scripts_dest.joinpath(item.name)))
|
||||||
|
else:
|
||||||
|
shutil.copy2(str(item), str(scripts_dest))
|
||||||
|
git_ignore = build_dir.joinpath(".gitignore")
|
||||||
|
if git_ignore.is_file():
|
||||||
|
git_ignore.unlink()
|
||||||
|
|
||||||
|
def pdm_build_finalize(context: Context, artifact: pathlib.Path) -> None:
|
||||||
|
share_path: pathlib.Path = context.root.joinpath("share")
|
||||||
|
if share_path.exists():
|
||||||
|
shutil.rmtree(str(share_path))
|
|
@ -25,7 +25,8 @@ dependencies = [
|
||||||
"apprise==1.8.0",
|
"apprise==1.8.0",
|
||||||
"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 ; python_version=='3.7'",
|
||||||
|
"importlib_metadata==8.2.0 ; python_version>='3.8'"
|
||||||
]
|
]
|
||||||
requires-python = ">=3.7"
|
requires-python = ">=3.7"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -50,9 +51,13 @@ documentation = "https://moonraker.readthedocs.io"
|
||||||
changelog = "https://moonraker.readthedocs.io/en/latest/changelog/"
|
changelog = "https://moonraker.readthedocs.io/en/latest/changelog/"
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
msgspec=["msgspec>=0.18.4 ; python_version>='3.8'"]
|
msgspec = ["msgspec>=0.18.4 ; python_version>='3.8'"]
|
||||||
uvloop=["uvloop>=0.17.0"]
|
uvloop = ["uvloop>=0.17.0"]
|
||||||
speedups = ["moonraker[msgspec,uvloop]"]
|
speedups = [
|
||||||
|
"msgspec>=0.18.4 ; python_version>='3.8'",
|
||||||
|
"uvloop>=0.17.0"
|
||||||
|
]
|
||||||
|
dev = ["pre-commit"]
|
||||||
|
|
||||||
[tool.pdm.version]
|
[tool.pdm.version]
|
||||||
source = "scm"
|
source = "scm"
|
||||||
|
@ -62,8 +67,12 @@ write_template = "__version__ = '{}'\n"
|
||||||
[tool.pdm.build]
|
[tool.pdm.build]
|
||||||
excludes = ["./**/.git", "moonraker/moonraker.py"]
|
excludes = ["./**/.git", "moonraker/moonraker.py"]
|
||||||
includes = ["moonraker"]
|
includes = ["moonraker"]
|
||||||
|
source-includes = ["scripts"]
|
||||||
editable-backend = "path"
|
editable-backend = "path"
|
||||||
custom-hook = "scripts/pdm_build_dist.py"
|
custom-hook = "pdm_build.py"
|
||||||
|
|
||||||
|
[tool.pdm.build.wheel-data]
|
||||||
|
data = [{path = "share/moonraker/**/*", relative-to = "."}]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
moonraker = "moonraker.server:main"
|
moonraker = "moonraker.server:main"
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# This script installs Moonraker on a Raspberry Pi machine running
|
# This script installs Moonraker on Debian based Linux distros.
|
||||||
# Raspbian/Raspberry Pi OS based distributions.
|
|
||||||
|
|
||||||
|
SUPPORTED_DISTROS="debian"
|
||||||
PYTHONDIR="${MOONRAKER_VENV:-${HOME}/moonraker-env}"
|
PYTHONDIR="${MOONRAKER_VENV:-${HOME}/moonraker-env}"
|
||||||
SYSTEMDDIR="/etc/systemd/system"
|
SYSTEMDDIR="/etc/systemd/system"
|
||||||
REBUILD_ENV="${MOONRAKER_REBUILD_ENV:-n}"
|
REBUILD_ENV="${MOONRAKER_REBUILD_ENV:-n}"
|
||||||
FORCE_DEFAULTS="${MOONRAKER_FORCE_DEFAULTS:-n}"
|
FORCE_SYSTEM_INSTALL="${MOONRAKER_FORCE_SYSTEM_INSTALL:-n}"
|
||||||
DISABLE_SYSTEMCTL="${MOONRAKER_DISABLE_SYSTEMCTL:-n}"
|
DISABLE_SYSTEMCTL="${MOONRAKER_DISABLE_SYSTEMCTL:-n}"
|
||||||
SKIP_POLKIT="${MOONRAKER_SKIP_POLKIT:-n}"
|
SKIP_POLKIT="${MOONRAKER_SKIP_POLKIT:-n}"
|
||||||
CONFIG_PATH="${MOONRAKER_CONFIG_PATH}"
|
CONFIG_PATH="${MOONRAKER_CONFIG_PATH}"
|
||||||
|
@ -14,17 +14,77 @@ DATA_PATH="${MOONRAKER_DATA_PATH}"
|
||||||
INSTANCE_ALIAS="${MOONRAKER_ALIAS:-moonraker}"
|
INSTANCE_ALIAS="${MOONRAKER_ALIAS:-moonraker}"
|
||||||
SPEEDUPS="${MOONRAKER_SPEEDUPS:-n}"
|
SPEEDUPS="${MOONRAKER_SPEEDUPS:-n}"
|
||||||
SERVICE_VERSION="1"
|
SERVICE_VERSION="1"
|
||||||
|
DISTRIBUTION=""
|
||||||
|
IS_SRC_DIST="n"
|
||||||
|
PACKAGES=""
|
||||||
|
|
||||||
package_decode_script=$( cat << EOF
|
# Check deprecated FORCE_DEFAULTS environment variable
|
||||||
import sys
|
if [ ! -z "${MOONRAKER_FORCE_DEFAULTS}" ]; then
|
||||||
import json
|
echo "Deprecated MOONRAKER_FORCE_DEFAULTS environment variable"
|
||||||
try:
|
echo -e "detected. Please use MOONRAKER_FORCE_SYSTEM_INSTALL\n"
|
||||||
ret = json.load(sys.stdin)
|
FORCE_SYSTEM_INSTALL=$MOONRAKER_FORCE_DEFAULTS
|
||||||
except Exception:
|
fi
|
||||||
exit(0)
|
|
||||||
sys.stdout.write(' '.join(ret['debian']))
|
# Force script to exit if an error occurs
|
||||||
EOF
|
set -e
|
||||||
)
|
|
||||||
|
# Find source director from the pathname of this script
|
||||||
|
SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
||||||
|
|
||||||
|
# Determine if Moonraker is to be installed from source
|
||||||
|
if [ -f "${SRCDIR}/moonraker/__init__.py" ]; then
|
||||||
|
echo "Installing from Moonraker source..."
|
||||||
|
IS_SRC_DIST="y"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect Current Distribution
|
||||||
|
detect_distribution() {
|
||||||
|
distro_list=""
|
||||||
|
if [ -f "/etc/os-release" ]; then
|
||||||
|
distro_list="$( grep -Po "^ID=\K.+" /etc/os-release || true )"
|
||||||
|
like_str="$( grep -Po "^ID_LIKE=\K.+" /etc/os-release || true )"
|
||||||
|
if [ ! -z "${like_str}" ]; then
|
||||||
|
distro_list="${distro_list} ${like_str}"
|
||||||
|
fi
|
||||||
|
if [ ! -z "${distro_list}" ]; then
|
||||||
|
echo "Found Linux distribution IDs: ${distro_list}"
|
||||||
|
else
|
||||||
|
echo "Unable to detect Linux Distribution."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
distro_id=""
|
||||||
|
while [ "$distro_list" != "$distro_id" ]; do
|
||||||
|
distro_id="${distro_list%% *}"
|
||||||
|
distro_list="${distro_list#$distro_id }"
|
||||||
|
supported_dists=$SUPPORTED_DISTROS
|
||||||
|
supported_id=""
|
||||||
|
while [ "$supported_dists" != "$supported_id" ]; do
|
||||||
|
supported_id="${supported_dists%% *}"
|
||||||
|
supported_dists="${supported_dists#$supported_id }"
|
||||||
|
if [ "$distro_id" = "$supported_id" ]; then
|
||||||
|
DISTRIBUTION=$distro_id
|
||||||
|
echo "Distribution detected: $DISTRIBUTION"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ ! -z "$DISTRIBUTION" ] && break
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$DISTRIBUTION" ] && [ -x "$( which apt-get || true )" ]; then
|
||||||
|
# Fall back to debian if apt-get is deteted
|
||||||
|
echo "Found apt-get, falling back to debian distribution"
|
||||||
|
DISTRIBUTION="debian"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# *** AUTO GENERATED OS PACKAGE DEPENDENCES START ***
|
||||||
|
if [ ${DISTRIBUTION} = "debian" ]; then
|
||||||
|
PACKAGES="python3-virtualenv python3-dev libopenjp2-7 libsodium-dev zlib1g-dev"
|
||||||
|
PACKAGES="${PACKAGES} libjpeg-dev packagekit wireless-tools curl"
|
||||||
|
PACKAGES="${PACKAGES} build-essential"
|
||||||
|
fi
|
||||||
|
# *** AUTO GENERATED OS PACKAGE DEPENDENCES END ***
|
||||||
|
}
|
||||||
|
|
||||||
# Step 2: Clean up legacy installation
|
# Step 2: Clean up legacy installation
|
||||||
cleanup_legacy() {
|
cleanup_legacy() {
|
||||||
|
@ -41,30 +101,19 @@ cleanup_legacy() {
|
||||||
# Step 3: Install packages
|
# Step 3: Install packages
|
||||||
install_packages()
|
install_packages()
|
||||||
{
|
{
|
||||||
|
if [ -z "${PACKAGES}" ]; then
|
||||||
|
echo "Unsupported Linux Distribution ${DISTRIBUTION}. "
|
||||||
|
echo "Bypassing system package installation."
|
||||||
|
return
|
||||||
|
fi
|
||||||
# Update system package info
|
# Update system package info
|
||||||
report_status "Running apt-get update..."
|
report_status "Running apt-get update..."
|
||||||
sudo apt-get update --allow-releaseinfo-change
|
sudo apt-get update --allow-releaseinfo-change
|
||||||
|
|
||||||
system_deps="${SRCDIR}/scripts/system-dependencies.json"
|
|
||||||
if [ -f "${system_deps}" ]; then
|
|
||||||
if [ ! -x "$(command -v python3)" ]; then
|
|
||||||
report_status "Installing python3 base package..."
|
|
||||||
sudo apt-get install --yes python3
|
|
||||||
fi
|
|
||||||
PKGS="$( cat ${system_deps} | python3 -c "${package_decode_script}" )"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Error: system-dependencies.json not found, falling back to legacy pacakge list"
|
|
||||||
PKGLIST="${PKGLIST} python3-virtualenv python3-dev"
|
|
||||||
PKGLIST="${PKGLIST} libopenjp2-7 libsodium-dev zlib1g-dev libjpeg-dev"
|
|
||||||
PKGLIST="${PKGLIST} packagekit wireless-tools curl"
|
|
||||||
PKGS=${PKGLIST}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install desired packages
|
# Install desired packages
|
||||||
report_status "Installing Moonraker Dependencies:"
|
report_status "Installing Moonraker Dependencies:"
|
||||||
report_status "${PKGS}"
|
report_status "${PACKAGES}"
|
||||||
sudo apt-get install --yes ${PKGS}
|
sudo apt-get install --yes ${PACKAGES}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Step 4: Create python virtual environment
|
# Step 4: Create python virtual environment
|
||||||
|
@ -88,11 +137,21 @@ create_virtualenv()
|
||||||
|
|
||||||
# Install/update dependencies
|
# Install/update dependencies
|
||||||
export SKIP_CYTHON=1
|
export SKIP_CYTHON=1
|
||||||
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-requirements.txt
|
if [ $IS_SRC_DIST = "y" ]; then
|
||||||
|
report_status "Installing Moonraker python dependencies..."
|
||||||
|
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-requirements.txt
|
||||||
|
|
||||||
if [ ${SPEEDUPS} = "y" ]; then
|
if [ ${SPEEDUPS} = "y" ]; then
|
||||||
report_status "Installing Speedups..."
|
report_status "Installing Speedups..."
|
||||||
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-speedups.txt
|
${PYTHONDIR}/bin/pip install -r ${SRCDIR}/scripts/moonraker-speedups.txt
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
report_status "Installing Moonraker package via Pip..."
|
||||||
|
if [ ${SPEEDUPS} = "y" ]; then
|
||||||
|
${PYTHONDIR}/bin/pip install -U moonraker[speedups]
|
||||||
|
else
|
||||||
|
${PYTHONDIR}/bin/pip install -U moonraker
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,16 +200,16 @@ install_script()
|
||||||
{
|
{
|
||||||
# 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_DEFAULTS = "y" ]; then
|
if [ ! -f $ENV_FILE ] || [ $FORCE_SYSTEM_INSTALL = "y" ]; then
|
||||||
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}\""
|
||||||
[ -n "${LOG_PATH}" ] && env_vars="${env_vars}\nMOONRAKER_LOG_PATH=\"${LOG_PATH}\""
|
[ -n "${LOG_PATH}" ] && env_vars="${env_vars}\nMOONRAKER_LOG_PATH=\"${LOG_PATH}\""
|
||||||
env_vars="${env_vars}\nMOONRAKER_ARGS=\"-m moonraker\""
|
env_vars="${env_vars}\nMOONRAKER_ARGS=\"-m moonraker\""
|
||||||
env_vars="${env_vars}\nPYTHONPATH=\"${SRCDIR}\"\n"
|
[ $IS_SRC_DIST = "y" ] && env_vars="${env_vars}\nPYTHONPATH=\"${SRCDIR}\"\n"
|
||||||
echo -e $env_vars > $ENV_FILE
|
echo -e $env_vars > $ENV_FILE
|
||||||
fi
|
fi
|
||||||
[ -f $SERVICE_FILE ] && [ $FORCE_DEFAULTS = "n" ] && return
|
[ -f $SERVICE_FILE ] && [ $FORCE_SYSTEM_INSTALL = "n" ] && return
|
||||||
report_status "Installing system start script..."
|
report_status "Installing system start script..."
|
||||||
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
|
||||||
|
@ -184,32 +243,51 @@ EOF
|
||||||
check_polkit_rules()
|
check_polkit_rules()
|
||||||
{
|
{
|
||||||
if [ ! -x "$(command -v pkaction || true)" ]; then
|
if [ ! -x "$(command -v pkaction || true)" ]; then
|
||||||
|
echo "PolKit not installed"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [ "${SKIP_POLKIT}" = "y" ]; then
|
||||||
|
echo "Skipping PolKit rules installation"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
POLKIT_VERSION="$( pkaction --version | grep -Po "(\d+\.?\d*)" )"
|
POLKIT_VERSION="$( pkaction --version | grep -Po "(\d+\.?\d*)" )"
|
||||||
NEED_POLKIT_INSTALL="n"
|
NEED_POLKIT_INSTALL="n"
|
||||||
if [ "$POLKIT_VERSION" = "0.105" ]; then
|
if [ $FORCE_SYSTEM_INSTALL = "n" ]; then
|
||||||
POLKIT_LEGACY_FILE="/etc/polkit-1/localauthority/50-local.d/10-moonraker.pkla"
|
if [ "$POLKIT_VERSION" = "0.105" ]; then
|
||||||
# legacy policykit rules don't give users other than root read access
|
POLKIT_LEGACY_FILE="/etc/polkit-1/localauthority/50-local.d/10-moonraker.pkla"
|
||||||
if sudo [ ! -f $POLKIT_LEGACY_FILE ]; then
|
# legacy policykit rules don't give users other than root read access
|
||||||
NEED_POLKIT_INSTALL="y"
|
if sudo [ ! -f $POLKIT_LEGACY_FILE ]; then
|
||||||
|
NEED_POLKIT_INSTALL="y"
|
||||||
|
else
|
||||||
|
echo "PolKit rules file found at ${POLKIT_LEGACY_FILE}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
POLKIT_FILE="/etc/polkit-1/rules.d/moonraker.rules"
|
||||||
|
POLKIT_USR_FILE="/usr/share/polkit-1/rules.d/moonraker.rules"
|
||||||
|
if sudo [ -f $POLKIT_FILE ]; then
|
||||||
|
echo "PolKit rules file found at ${POLKIT_FILE}"
|
||||||
|
elif sudo [ -f $POLKIT_USR_FILE ]; then
|
||||||
|
echo "PolKit rules file found at ${POLKIT_USR_FILE}"
|
||||||
|
else
|
||||||
|
NEED_POLKIT_INSTALL="y"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
POLKIT_FILE="/etc/polkit-1/rules.d/moonraker.rules"
|
NEED_POLKIT_INSTALL="y"
|
||||||
POLKIT_USR_FILE="/usr/share/polkit-1/rules.d/moonraker.rules"
|
|
||||||
if [ ! -f $POLKIT_FILE ] && [ ! -f $POLKIT_USR_FILE ]; then
|
|
||||||
NEED_POLKIT_INSTALL="y"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
if [ "${NEED_POLKIT_INSTALL}" = "y" ]; then
|
if [ "${NEED_POLKIT_INSTALL}" = "y" ]; then
|
||||||
if [ "${SKIP_POLKIT}" = "y" ]; then
|
report_status "Installing PolKit Rules"
|
||||||
echo -e "\n*** No PolicyKit Rules detected, run 'set-policykit-rules.sh'"
|
polkit_script="${SRCDIR}/scripts/set-policykit-rules.sh"
|
||||||
echo "*** if you wish to grant Moonraker authorization to manage"
|
if [ $IS_SRC_DIST != "y" ]; then
|
||||||
echo "*** system services, reboot/shutdown the system, and update"
|
polkit_script="${PYTHONDIR}/share/moonraker"
|
||||||
echo "*** packages."
|
polkit_script="${polkit_script}/scripts/set-policykit-rules.sh"
|
||||||
|
fi
|
||||||
|
if [ -f "$polkit_script" ]; then
|
||||||
|
set +e
|
||||||
|
$polkit_script -z
|
||||||
|
set -e
|
||||||
else
|
else
|
||||||
report_status "Installing PolKit Rules"
|
echo "PolKit rule install script not found at $polkit_script"
|
||||||
${SRCDIR}/scripts/set-policykit-rules.sh -z
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
@ -235,17 +313,11 @@ verify_ready()
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Force script to exit if an error occurs
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Find SRCDIR from the pathname of this script
|
|
||||||
SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/.. && pwd )"
|
|
||||||
|
|
||||||
# Parse command line arguments
|
# Parse command line arguments
|
||||||
while getopts "rfzxsc:l:d:a:" arg; do
|
while getopts "rfzxsc:l:d:a:" arg; do
|
||||||
case $arg in
|
case $arg in
|
||||||
r) REBUILD_ENV="y";;
|
r) REBUILD_ENV="y";;
|
||||||
f) FORCE_DEFAULTS="y";;
|
f) FORCE_SYSTEM_INSTALL="y";;
|
||||||
z) DISABLE_SYSTEMCTL="y";;
|
z) DISABLE_SYSTEMCTL="y";;
|
||||||
x) SKIP_POLKIT="y";;
|
x) SKIP_POLKIT="y";;
|
||||||
s) SPEEDUPS="y";;
|
s) SPEEDUPS="y";;
|
||||||
|
@ -273,6 +345,7 @@ SERVICE_FILE="${SYSTEMDDIR}/${INSTANCE_ALIAS}.service"
|
||||||
|
|
||||||
# Run installation steps defined above
|
# Run installation steps defined above
|
||||||
verify_ready
|
verify_ready
|
||||||
|
detect_distribution
|
||||||
cleanup_legacy
|
cleanup_legacy
|
||||||
install_packages
|
install_packages
|
||||||
create_virtualenv
|
create_virtualenv
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pre-commit
|
|
@ -20,3 +20,4 @@ apprise==1.8.0
|
||||||
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 ; python_version=='3.7'
|
||||||
|
importlib_metadata==8.2.0 ; python_version>='3.8'
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
# Wheel Setup Script for generating metadata
|
|
||||||
#
|
|
||||||
# Copyright (C) 2023 Eric Callahan <arksine.code@gmail.com>
|
|
||||||
#
|
|
||||||
# This file may be distributed under the terms of the GNU GPLv3 license
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
import pathlib
|
|
||||||
import subprocess
|
|
||||||
import shlex
|
|
||||||
import json
|
|
||||||
import shutil
|
|
||||||
from datetime import datetime, timezone
|
|
||||||
from typing import Dict, Any, TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from pdm.backend.hooks.base import Context
|
|
||||||
|
|
||||||
__package_name__ = "moonraker"
|
|
||||||
__dependencies__ = "scripts/system-dependencies.json"
|
|
||||||
|
|
||||||
def _run_git_command(cmd: str) -> str:
|
|
||||||
prog = shlex.split(cmd)
|
|
||||||
process = subprocess.Popen(
|
|
||||||
prog, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
ret, err = process.communicate()
|
|
||||||
retcode = process.wait()
|
|
||||||
if retcode == 0:
|
|
||||||
return ret.strip().decode()
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_commit_sha(source_path: pathlib.Path) -> str:
|
|
||||||
cmd = f"git -C {source_path} rev-parse HEAD"
|
|
||||||
return _run_git_command(cmd)
|
|
||||||
|
|
||||||
def retrieve_git_version(source_path: pathlib.Path) -> str:
|
|
||||||
cmd = f"git -C {source_path} describe --always --tags --long --dirty"
|
|
||||||
return _run_git_command(cmd)
|
|
||||||
|
|
||||||
def pdm_build_initialize(context: Context) -> None:
|
|
||||||
context.ensure_build_dir()
|
|
||||||
build_ver: str = context.config.metadata['version']
|
|
||||||
proj_name: str = context.config.metadata['name']
|
|
||||||
urls: Dict[str, str] = context.config.metadata['urls']
|
|
||||||
build_dir = pathlib.Path(context.build_dir)
|
|
||||||
rel_dpath = f"{__package_name__}-{build_ver}.data/data/share/{proj_name}"
|
|
||||||
data_path = build_dir.joinpath(rel_dpath)
|
|
||||||
pkg_path = build_dir.joinpath(__package_name__)
|
|
||||||
build_time = datetime.now(timezone.utc)
|
|
||||||
release_info: Dict[str, Any] = {
|
|
||||||
"project_name": proj_name,
|
|
||||||
"package_name": __package_name__,
|
|
||||||
"urls": {key.lower(): val for key, val in urls.items()},
|
|
||||||
"package_version": build_ver,
|
|
||||||
"git_version": retrieve_git_version(context.root),
|
|
||||||
"commit_sha": get_commit_sha(context.root),
|
|
||||||
"build_time": datetime.isoformat(build_time, timespec="seconds")
|
|
||||||
}
|
|
||||||
if __dependencies__:
|
|
||||||
deps = pathlib.Path(context.root).joinpath(__dependencies__)
|
|
||||||
if deps.is_file():
|
|
||||||
dep_info: Dict[str, Any] = json.loads(deps.read_bytes())
|
|
||||||
release_info["system_dependencies"] = dep_info
|
|
||||||
# Write the release info to both the package and the data path
|
|
||||||
rinfo_data = json.dumps(release_info, indent=4)
|
|
||||||
data_path.mkdir(parents=True, exist_ok=True)
|
|
||||||
pkg_path.mkdir(parents=True, exist_ok=True)
|
|
||||||
data_path.joinpath("release_info").write_text(rinfo_data)
|
|
||||||
pkg_path.joinpath("release_info").write_text(rinfo_data)
|
|
||||||
scripts_path = context.root.joinpath("scripts")
|
|
||||||
scripts_dest = data_path.joinpath("scripts")
|
|
||||||
scripts_dest.mkdir()
|
|
||||||
for item in scripts_path.iterdir():
|
|
||||||
if item.name == "__pycache__":
|
|
||||||
continue
|
|
||||||
if item.is_dir():
|
|
||||||
shutil.copytree(str(item), str(scripts_dest.joinpath(item.name)))
|
|
||||||
else:
|
|
||||||
shutil.copy2(str(item), str(scripts_dest))
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
#! /usr/bin/python3
|
||||||
|
# Script for syncing package dependencies and python reqs
|
||||||
|
#
|
||||||
|
# Copyright (C) 2024 Eric Callahan <arksine.code@gmail.com>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import argparse
|
||||||
|
import pathlib
|
||||||
|
import tomllib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
MAX_LINE_LENGTH = 88
|
||||||
|
SCRIPTS_PATH = pathlib.Path(__file__).parent
|
||||||
|
INST_PKG_HEADER = "# *** AUTO GENERATED OS PACKAGE DEPENDENCES START ***"
|
||||||
|
INST_PKG_FOOTER = "# *** AUTO GENERATED OS PACKAGE DEPENDENCES END ***"
|
||||||
|
|
||||||
|
def gen_multline_var(var_name: str, values: List[str], indent: int = 0) -> str:
|
||||||
|
idt = " " * indent
|
||||||
|
if not values:
|
||||||
|
return f'{idt}{var_name}=""'
|
||||||
|
line_list: List[str] = []
|
||||||
|
current_line = f"{idt}{var_name}=\"{values.pop(0)}"
|
||||||
|
for val in values:
|
||||||
|
if len(current_line) + len(val) + 2 > MAX_LINE_LENGTH:
|
||||||
|
line_list.append(f'{current_line}"')
|
||||||
|
current_line = (f"{idt}{var_name}=\"${{{var_name}}} {val}")
|
||||||
|
else:
|
||||||
|
current_line += f" {val}"
|
||||||
|
line_list.append(f'{current_line}"')
|
||||||
|
return "\n".join(line_list)
|
||||||
|
|
||||||
|
def sync_packages() -> int:
|
||||||
|
inst_script = SCRIPTS_PATH.joinpath("install-moonraker.sh")
|
||||||
|
sys_deps_file = SCRIPTS_PATH.joinpath("system-dependencies.json")
|
||||||
|
new_deps: Dict[str, List[str]] = json.loads(sys_deps_file.read_bytes())
|
||||||
|
# Copy install script in memory.
|
||||||
|
install_data: List[str] = []
|
||||||
|
prev_deps: Dict[str, List[str]] = {}
|
||||||
|
distro_name = ""
|
||||||
|
skip_data = False
|
||||||
|
with inst_script.open("r") as inst_file:
|
||||||
|
for line in inst_file:
|
||||||
|
cur_line = line.strip()
|
||||||
|
if not skip_data:
|
||||||
|
install_data.append(line)
|
||||||
|
else:
|
||||||
|
# parse current dependencies
|
||||||
|
distro_match = re.match(
|
||||||
|
r"(?:el)?if \[ \$\{DISTRIBUTION\} = \"([a-z0-9._-]+)\" \]; then",
|
||||||
|
cur_line
|
||||||
|
)
|
||||||
|
if distro_match is not None:
|
||||||
|
distro_name = distro_match.group(1)
|
||||||
|
prev_deps[distro_name] = []
|
||||||
|
elif cur_line.startswith("PACKAGES"):
|
||||||
|
pkgs = cur_line.split("=", maxsplit=1)[1].strip('"')
|
||||||
|
pkg_list = pkgs.split()
|
||||||
|
if pkg_list and pkg_list[0] == "${PACKAGES}":
|
||||||
|
pkg_list.pop(0)
|
||||||
|
prev_deps[distro_name].extend(pkg_list)
|
||||||
|
if cur_line == INST_PKG_HEADER:
|
||||||
|
skip_data = True
|
||||||
|
elif cur_line == INST_PKG_FOOTER:
|
||||||
|
skip_data = False
|
||||||
|
install_data.append(line)
|
||||||
|
# Check if an update is necessary
|
||||||
|
if set(prev_deps.keys()) == set(new_deps.keys()):
|
||||||
|
for distro, pkg_list in prev_deps.items():
|
||||||
|
new_pkgs = new_deps[distro]
|
||||||
|
if set(pkg_list) != set(new_pkgs):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Dependencies match, exit
|
||||||
|
print("System package dependencies match")
|
||||||
|
return 0
|
||||||
|
print("Writing new system dependencies to install script...")
|
||||||
|
with inst_script.open("w+") as inst_file:
|
||||||
|
# Find and replace old package defs
|
||||||
|
for line in install_data:
|
||||||
|
inst_file.write(line)
|
||||||
|
if line.strip() == INST_PKG_HEADER:
|
||||||
|
indent_count = len(line) - len(line.lstrip())
|
||||||
|
idt = " " * indent_count
|
||||||
|
# Write Package data
|
||||||
|
first_distro = True
|
||||||
|
for distro, packages in new_deps.items():
|
||||||
|
prefix = f"{idt}if" if first_distro else f"{idt}elif"
|
||||||
|
first_distro = False
|
||||||
|
inst_file.write(
|
||||||
|
f'{prefix} [ ${{DISTRIBUTION}} = "{distro}" ]; then\n'
|
||||||
|
)
|
||||||
|
pkg_var = gen_multline_var("PACKAGES", packages, indent_count + 4)
|
||||||
|
inst_file.write(pkg_var)
|
||||||
|
inst_file.write("\n")
|
||||||
|
inst_file.write(f"{idt}fi\n")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def check_reqs_changed(reqs_file: pathlib.Path, new_reqs: List[str]) -> bool:
|
||||||
|
req_list = []
|
||||||
|
for requirement in reqs_file.read_text().splitlines():
|
||||||
|
requirement = requirement.strip()
|
||||||
|
if not requirement or requirement[0] in ("-", "#"):
|
||||||
|
continue
|
||||||
|
req_list.append(requirement)
|
||||||
|
return set(new_reqs) != set(req_list)
|
||||||
|
|
||||||
|
def sync_requirements() -> int:
|
||||||
|
ret: int = 0
|
||||||
|
src_path = SCRIPTS_PATH.parent
|
||||||
|
proj_file = src_path.joinpath("pyproject.toml")
|
||||||
|
with proj_file.open("rb") as f:
|
||||||
|
data = tomllib.load(f)
|
||||||
|
python_deps = data["project"]["dependencies"]
|
||||||
|
optional_deps = data["project"]["optional-dependencies"]
|
||||||
|
reqs_path = SCRIPTS_PATH.joinpath("moonraker-requirements.txt")
|
||||||
|
if check_reqs_changed(reqs_path, python_deps):
|
||||||
|
print("Syncing Moonraker Python Requirements...")
|
||||||
|
ret = 1
|
||||||
|
with reqs_path.open("w+") as req_file:
|
||||||
|
req_file.write("# Python dependencies for Moonraker\n")
|
||||||
|
req_file.write("--find-links=python_wheels\n")
|
||||||
|
for requirement in python_deps:
|
||||||
|
req_file.write(f"{requirement}\n")
|
||||||
|
else:
|
||||||
|
print("Moonraker Python requirements match")
|
||||||
|
# sync speedups
|
||||||
|
speedups_path = SCRIPTS_PATH.joinpath("moonraker-speedups.txt")
|
||||||
|
speedup_deps = optional_deps["speedups"]
|
||||||
|
if check_reqs_changed(speedups_path, speedup_deps):
|
||||||
|
print("Syncing speedup requirements...")
|
||||||
|
ret = 1
|
||||||
|
with speedups_path.open("w+") as req_file:
|
||||||
|
for requirement in speedup_deps:
|
||||||
|
req_file.write(f"{requirement}\n")
|
||||||
|
else:
|
||||||
|
print("Speedup sequirements match")
|
||||||
|
# sync dev dependencies
|
||||||
|
dev_reqs_path = SCRIPTS_PATH.joinpath("moonraker-dev-reqs.txt")
|
||||||
|
dev_deps = optional_deps["dev"]
|
||||||
|
if check_reqs_changed(dev_reqs_path, dev_deps):
|
||||||
|
print("Syncing dev requirements")
|
||||||
|
ret = 1
|
||||||
|
with dev_reqs_path.open("r+") as req_file:
|
||||||
|
for requirement in dev_deps:
|
||||||
|
req_file.write(f"{requirement}\n")
|
||||||
|
else:
|
||||||
|
print("Dev requirements match")
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"filename", default="", nargs="?",
|
||||||
|
help="The name of the dependency file to sync"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
fname: str = args.filename
|
||||||
|
if not fname:
|
||||||
|
ret = sync_requirements()
|
||||||
|
ret += sync_packages()
|
||||||
|
return 1 if ret > 0 else 0
|
||||||
|
elif fname == "pyproject.toml":
|
||||||
|
return sync_requirements()
|
||||||
|
elif fname == "scripts/system-dependencies.json":
|
||||||
|
return sync_packages()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
|
@ -8,6 +8,7 @@
|
||||||
"libjpeg-dev",
|
"libjpeg-dev",
|
||||||
"packagekit",
|
"packagekit",
|
||||||
"wireless-tools",
|
"wireless-tools",
|
||||||
"curl"
|
"curl",
|
||||||
|
"build-essential"
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Reference in New Issue