Flatten to a single package directory

The checkout is now astrape/astrape-oracle, not astrape/astrape-oracle/<package>. The package
contents move to the repository root and pyproject maps the package name to
'.' via package-dir, with the subpackages listed explicitly.

The systemd units move out to /srv/astrape/deploy/systemd, outside every
repository, because they describe this deployment rather than any one
component.

Verified both install modes: editable, which is how the deployment runs, and
a plain wheel install, which is what the container build does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
rpotter6298
2026-08-30 15:05:17 +02:00
parent 09e7155b47
commit 9a4ee8e0cf
27 changed files with 6 additions and 117 deletions
-25
View File
@@ -1,25 +0,0 @@
[Unit]
Description=Astrape Energy Oracle Forecast Snapshots
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=gibil
Group=gibil
WorkingDirectory=/srv/astrape
Environment=PYTHONUNBUFFERED=1
Environment=PYTHONDONTWRITEBYTECODE=1
# WorkingDirectory is /srv/astrape, which also holds the gibil checkout. Without
# this, Python prepends the working directory to sys.path and the repo root
# /srv/astrape/gibil shadows the installed package as a namespace portion --
# gibil.__file__ becomes None and imports work only by accident. All code is
# installed into the venv, so nothing needs the working directory on sys.path.
Environment=PYTHONSAFEPATH=1
Environment=ASTRAPE_ORACLE_POLL_SECONDS=300
ExecStart=/srv/astrape/.venv/bin/python -m astrape_oracle.scripts.daemons.oracle_daemon
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
@@ -1,28 +0,0 @@
[Unit]
Description=Astrape Usage TCN Adaptive Training
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=gibil
Group=gibil
WorkingDirectory=/srv/astrape
Environment=PYTHONUNBUFFERED=1
Environment=PYTHONDONTWRITEBYTECODE=1
# WorkingDirectory is /srv/astrape, which also holds the gibil checkout. Without
# this, Python prepends the working directory to sys.path and the repo root
# /srv/astrape/gibil shadows the installed package as a namespace portion --
# gibil.__file__ becomes None and imports work only by accident. All code is
# installed into the venv, so nothing needs the working directory on sys.path.
Environment=PYTHONSAFEPATH=1
# This host is a Xeon E5-2650 v2: no AVX2, no FMA. Torch dispatches kernels it
# cannot execute and dies with SIGILL inside Adam, so force the scalar path.
# The real fix is external_tcn on a GPU host; this keeps the local run alive
# until the scheduler is pointed at it.
Environment=ATEN_CPU_CAPABILITY=default
# Training competes with a 5-second Modbus poll on 8 cores. Leave headroom.
Environment=OMP_NUM_THREADS=4
Environment=MKL_NUM_THREADS=4
Nice=10
ExecStart=/srv/astrape/.venv/bin/python -m astrape_oracle.scripts.tcn.schedule
@@ -1,11 +0,0 @@
[Unit]
Description=Run Astrape Usage TCN Adaptive Training Nightly
[Timer]
OnCalendar=*-*-* 03:20:00
Persistent=true
RandomizedDelaySec=20m
Unit=astrape-usage-tcn-training.service
[Install]
WantedBy=timers.target
@@ -1,25 +0,0 @@
[Unit]
Description=Astrape Weather Forecast Ingest
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=gibil
Group=gibil
WorkingDirectory=/srv/astrape
Environment=PYTHONUNBUFFERED=1
Environment=PYTHONDONTWRITEBYTECODE=1
# WorkingDirectory is /srv/astrape, which also holds the gibil checkout. Without
# this, Python prepends the working directory to sys.path and the repo root
# /srv/astrape/gibil shadows the installed package as a namespace portion --
# gibil.__file__ becomes None and imports work only by accident. All code is
# installed into the venv, so nothing needs the working directory on sys.path.
Environment=PYTHONSAFEPATH=1
ExecStart=/srv/astrape/.venv/bin/python -m astrape_oracle.scripts.daemons.weather_forecast_daemon
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
@@ -1,26 +0,0 @@
[Unit]
Description=Astrape Local Weather Station Ingest
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=gibil
Group=gibil
WorkingDirectory=/srv/astrape
Environment=PYTHONUNBUFFERED=1
Environment=PYTHONDONTWRITEBYTECODE=1
# WorkingDirectory is /srv/astrape, which also holds the gibil checkout. Without
# this, Python prepends the working directory to sys.path and the repo root
# /srv/astrape/gibil shadows the installed package as a namespace portion --
# gibil.__file__ becomes None and imports work only by accident. All code is
# installed into the venv, so nothing needs the working directory on sys.path.
Environment=PYTHONSAFEPATH=1
Environment=ECOWITT_GW3000_HOST=10.24.88.176
Environment=ECOWITT_GW3000_POLL_SECONDS=60
ExecStart=/srv/astrape/.venv/bin/python -m astrape_oracle.scripts.daemons.weather_station_daemon
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
+6 -2
View File
@@ -9,5 +9,9 @@ description = "Forecasting runtime: weather ingest, oracle snapshots, TCN traini
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["gibil", "astrape-ems", "psycopg[binary]>=3.2,<4"] dependencies = ["gibil", "astrape-ems", "psycopg[binary]>=3.2,<4"]
[tool.setuptools.packages.find] # Flat layout: this repository *is* the package. package-dir maps astrape_oracle to the
include = ["astrape_oracle*"] # repository root rather than to a nested astrape_oracle/ directory, so the checkout is
# astrape/astrape-oracle and not astrape/astrape-oracle/astrape_oracle.
[tool.setuptools]
packages = ["astrape_oracle", "astrape_oracle.classes", "astrape_oracle.classes.weather", "astrape_oracle.scripts", "astrape_oracle.scripts.daemons", "astrape_oracle.scripts.debug", "astrape_oracle.scripts.tcn"]
package-dir = {"astrape_oracle" = "."}