initial changes to pip releases and dependency cleanup

ls-pip-adjustments
dj2ls 2026-07-24 22:59:40 +02:00
parent 6835390793
commit 7ea4f0796c
9 changed files with 139 additions and 34 deletions

View File

@ -53,12 +53,11 @@ jobs:
sudo apt update
sudo apt install -y portaudio19-dev libhamlib-dev libhamlib-utils build-essential cmake patchelf
- name: Install MacOS pyAudio
- name: Install MacOS dependencies
if: ${{startsWith(matrix.os, 'macos')}}
run: |
brew install portaudio
python -m pip install --upgrade pip
pip3 install pyaudio
- name: Install Python dependencies
run: |

View File

@ -1,5 +1,8 @@
name: Deploy Python Package
on: [push]
on:
push:
tags:
- "v*"
jobs:
deploy:
@ -17,16 +20,64 @@ jobs:
with:
node-version: 24
- name: Install Linux dependencies
run: |
sudo apt update
sudo apt install -y portaudio19-dev libhamlib-dev libhamlib-utils build-essential cmake patchelf
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install .[build]
- name: Set package version from tag
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
python3 - <<'EOF'
import os
import re
import sys
from packaging.version import InvalidVersion, Version
tag = os.environ["RELEASE_TAG"]
version = tag.removeprefix("v")
if not re.fullmatch(r"\d+\.\d+\.\d+(-[0-9A-Za-z.]+)?", version):
print(
f"::error::Tag '{tag}' does not look like a release version. "
f"Use e.g. v1.2.3, v1.2.3-beta, v1.2.3-rc1 or v1.2.3-alpha.1."
)
sys.exit(1)
try:
Version(version)
except InvalidVersion:
print(
f"::error::Tag '{tag}' has an unrecognized pre-release suffix "
f"('{version}' is not valid PEP 440). Use a standard suffix such "
f"as -alpha, -alpha.1, -beta, -rc1 or -dev."
)
sys.exit(1)
print(f"Releasing version {version} (from tag {tag})")
path = "freedata_server/constants.py"
with open(path) as f:
content = f.read()
new_content, count = re.subn(
r'^MODEM_VERSION = .*$',
f'MODEM_VERSION = "{version}"',
content,
count=1,
flags=re.MULTILINE,
)
if count != 1:
print("::error::Could not find MODEM_VERSION in freedata_server/constants.py")
sys.exit(1)
with open(path, "w") as f:
f.write(new_content)
EOF
grep "^MODEM_VERSION" freedata_server/constants.py
- name: Build GUI
working-directory: freedata_gui
run: |
@ -39,7 +90,7 @@ jobs:
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@v1.14.0
if: startsWith(github.ref, 'refs/tags/v')
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
skip-existing: true

View File

@ -18,7 +18,7 @@ ARG HAMLIB_VERSION=4.5.5
ENV HAMLIB_VERSION=${HAMLIB_VERSION}
RUN apt-get update && \
apt-get install --upgrade -y fonts-noto-color-emoji git build-essential cmake portaudio19-dev python3-pyaudio python3-colorama wget && \
apt-get install --upgrade -y fonts-noto-color-emoji git build-essential cmake portaudio19-dev python3-colorama wget && \
mkdir -p /app/FreeDATA
WORKDIR /src

View File

@ -1,6 +1,32 @@
# Module for saving some constants
import os
import sys
def _default_app_dir() -> str:
"""
Per-user directory for config, database and log file, following each
OS's own convention rather than forcing a single layout everywhere:
- Windows: %APPDATA%\\FreeDATA
- macOS: ~/Library/Application Support/FreeDATA
- Linux: $XDG_CONFIG_HOME/FreeDATA or ~/.config/FreeDATA
Used only when FREEDATA_CONFIG / FREEDATA_DATABASE are not set (e.g. a
plain `pip install freedata` run). Keeping this outside the installed
package directory means it survives package upgrades/reinstalls.
"""
home = os.path.expanduser("~")
if sys.platform == "win32":
base = os.getenv("APPDATA") or home
elif sys.platform == "darwin":
base = os.path.join(home, "Library", "Application Support")
else:
base = os.getenv("XDG_CONFIG_HOME") or os.path.join(home, ".config")
return os.path.join(base, "FreeDATA")
CONFIG_ENV_VAR = "FREEDATA_CONFIG"
DEFAULT_CONFIG_FILE = "config.ini"
DEFAULT_APP_DIR = _default_app_dir()
MODEM_VERSION = "0.18.1"
API_VERSION = 4
ARQ_PROTOCOL_VERSION = 1

View File

@ -5,7 +5,7 @@ from freedata_server.message_system_db_model import Base, Config, Station, Statu
import structlog
from freedata_server import helpers
import os
from freedata_server.constants import MESSAGE_SYSTEM_DATABASE_VERSION
from freedata_server.constants import MESSAGE_SYSTEM_DATABASE_VERSION, DEFAULT_APP_DIR
class DatabaseManager:
@ -42,18 +42,19 @@ class DatabaseManager:
This method determines the database file path based on the
environment variable `FREEDATA_DATABASE`. If the variable is set,
its value is used as the path. Otherwise, it defaults to
`freedata-messages.db` in the script directory.
`freedata-messages.db` in the per-user app directory
(DEFAULT_APP_DIR), so a plain `pip install` keeps the database
outside the installed package and it survives upgrades.
Returns:
str: The database file path as a SQLAlchemy URL.
"""
script_directory = os.path.dirname(os.path.abspath(__file__))
if self.DATABASE_ENV_VAR in os.environ:
# db_path = os.getenv(self.DATABASE_ENV_VAR, os.path.join(script_directory, self.DEFAULT_DATABASE_FILE))
db_path = os.getenv(self.DATABASE_ENV_VAR)
else:
db_path = os.path.join(script_directory, self.DEFAULT_DATABASE_FILE)
db_path = os.path.join(DEFAULT_APP_DIR, self.DEFAULT_DATABASE_FILE)
os.makedirs(os.path.dirname(db_path), exist_ok=True)
return "sqlite:///" + db_path
def initialize_default_values(self):

View File

@ -1,4 +1,5 @@
import os
import shutil
import sys
import threading
@ -10,7 +11,7 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from freedata_server.log_handler import setup_logging
from freedata_server.constants import CONFIG_ENV_VAR, DEFAULT_CONFIG_FILE, API_VERSION
from freedata_server.constants import CONFIG_ENV_VAR, DEFAULT_CONFIG_FILE, DEFAULT_APP_DIR, API_VERSION
from freedata_server.context import AppContext
from freedata_server.api.general import router as general_router
@ -27,18 +28,39 @@ import uvicorn
# --- Resolve config path FIRST (no logger needed yet) ---
def resolve_config_path() -> str:
"""
Determine the configuration file to use (env var or default next to this file).
Exits if not found.
Determine the configuration file to use.
Uses FREEDATA_CONFIG if set, otherwise defaults to a per-user config
directory (DEFAULT_APP_DIR). If no config file exists yet at that
location, a fresh one is bootstrapped from the bundled
config.ini.example template so a plain `pip install freedata` followed
by `freedata` works out of the box without any manual setup.
"""
candidate = os.getenv(
CONFIG_ENV_VAR,
os.path.join(os.path.dirname(__file__), DEFAULT_CONFIG_FILE),
candidate = os.path.abspath(
os.getenv(
CONFIG_ENV_VAR,
os.path.join(DEFAULT_APP_DIR, DEFAULT_CONFIG_FILE),
)
)
if not os.path.exists(candidate):
# We cannot log to file yet since we don't know the directory; write to stderr.
sys.stderr.write(f"[FATAL] Config file not found: {candidate}\n")
sys.exit(1)
return os.path.abspath(candidate)
template = os.path.join(os.path.dirname(__file__), "config.ini.example")
try:
os.makedirs(os.path.dirname(candidate), exist_ok=True)
if os.path.isfile(template):
shutil.copyfile(template, candidate)
sys.stderr.write(f"[INFO] No config found - created a default one at: {candidate}\n")
else:
sys.stderr.write(
f"[FATAL] Config file not found and no template available to create one: {candidate}\n"
)
sys.exit(1)
except OSError as e:
sys.stderr.write(f"[FATAL] Could not create config file at {candidate}: {e}\n")
sys.exit(1)
return candidate
config_file = resolve_config_path()
@ -95,11 +117,15 @@ async def nocache(request: Request, call_next):
# Static GUI mounting
# Order matters: prefer paths anchored to this file's location (work no
# matter what the current working directory is) over cwd-relative
# fallbacks kept for backwards compatibility with older layouts.
potential_gui_dirs = [
os.path.join(os.path.dirname(__file__), "gui"), # nuitka standalone build
os.path.join(os.path.dirname(os.path.dirname(__file__)), "freedata_gui", "dist"), # pip install (sibling package)
"../freedata_gui/dist",
"freedata_gui/dist",
"FreeDATA/freedata_gui/dist",
os.path.join(os.path.dirname(__file__), "gui"),
]
gui_dir = next((d for d in potential_gui_dirs if os.path.isdir(d)), None)
if gui_dir:

View File

@ -34,7 +34,6 @@ requires-python = ">=3.10"
dependencies = [
"numpy",
"psutil",
"PyAudio",
"pyserial",
"sounddevice",
"structlog",
@ -78,12 +77,13 @@ nuitka = [
[tool.setuptools.packages.find]
where = [ "." ]
exclude = [
"tools*",
include = [
"freedata_server*",
"freedata_gui",
]
[tool.setuptools.package-data]
freedata_server = [ "lib/**/*" ]
freedata_server = [ "lib/**/*", "config.ini.example" ]
freedata_gui = [ "dist/**/*" ]
[tool.setuptools.dynamic]

View File

@ -1,6 +1,5 @@
numpy
psutil
PyAudio
pyserial
sounddevice
structlog

View File

@ -44,6 +44,9 @@
#
#
# Changelog:
# 2.10: 24 Jul 2026
# Remove python3-pyaudio (unused dependency, FreeDATA uses sounddevice)
#
# 2.9: 10 Jan Sep 2026
# Add Ubuntu 24.10 and 25.04
# Change hamlib default version to 4.6.5
@ -164,7 +167,7 @@ case $osname in
"Debian GNU/Linux")
case $osversion in
"11" | "12" | "13")
sudo apt install --upgrade -y fonts-noto-color-emoji git build-essential cmake python3 portaudio19-dev python3-pyaudio python3-pip python3-colorama python3-venv wget python3-dev
sudo apt install --upgrade -y fonts-noto-color-emoji git build-essential cmake python3 portaudio19-dev python3-pip python3-colorama python3-venv wget python3-dev
;;
*)
@ -182,7 +185,7 @@ case $osname in
"Ubuntu" | "Linux Mint")
case $osversion in
"21.3" | "22.04" | "24.04" | "24.10" | "25.04" )
sudo apt install --upgrade -y fonts-noto-color-emoji git build-essential cmake python3 portaudio19-dev python3-pyaudio python3-pip python3-colorama python3-venv wget python3-dev
sudo apt install --upgrade -y fonts-noto-color-emoji git build-essential cmake python3 portaudio19-dev python3-pip python3-colorama python3-venv wget python3-dev
;;
*)
@ -197,7 +200,7 @@ case $osname in
"Fedora Linux")
case $osversion in
"VERSION_ID=40" | "VERSION_ID=41")
sudo dnf install -y git cmake make automake gcc gcc-c++ kernel-devel wget portaudio-devel python3-pyaudio python3-pip python3-colorama python3-virtualenv google-noto-emoji-fonts python3-devel
sudo dnf install -y git cmake make automake gcc gcc-c++ kernel-devel wget portaudio-devel python3-pip python3-colorama python3-virtualenv google-noto-emoji-fonts python3-devel
;;
esac
;;