Merge branch 'pr-285' into dev

This commit is contained in:
Lloyd
2026-06-09 08:54:41 +01:00
6 changed files with 109 additions and 14 deletions
+31
View File
@@ -0,0 +1,31 @@
# Copy this file to .env before running Docker Compose:
# cp .env.example .env
# Published image to run. Use a different repository or tag if you are testing
# a fork or a specific release.
PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:main
# Storage defaults to Docker named volumes. This is the safest option for
# Portainer and fresh installs because Docker preserves the image ownership.
# To use host bind mounts instead, create the folders first and make them
# writable by UID/GID 15888:
# sudo mkdir -p /opt/pymc-repeater/config /opt/pymc-repeater/data
# sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data
# Then uncomment and adjust these paths:
# PYMC_CONFIG_VOLUME=/opt/pymc-repeater/config
# PYMC_DATA_VOLUME=/opt/pymc-repeater/data
# SPI/GPIO access uses the host's numeric group IDs. Check your host with:
# getent group gpio
# getent group spi
# Example output:
# gpio:x:997:
# spi:x:999:
# Put the third field from each output line below.
GPIO_GID=986
SPI_GID=989
# Local build only. These are used by docker-compose.build.yml if you build the
# image yourself instead of pulling PYMC_REPEATER_IMAGE.
PUID=15888
PGID=15888
+1
View File
@@ -51,6 +51,7 @@ htmlcov/
*~
# Config
.env
config.yaml
config.yaml.backup
identity.json
+11
View File
@@ -0,0 +1,11 @@
services:
pymc-repeater:
image: pymc-repeater:local
build:
context: .
dockerfile: dockerfile
args:
PUID: ${PUID:-15888}
PGID: ${PGID:-15888}
GPIO_GID: ${GPIO_GID:-986}
SPI_GID: ${SPI_GID:-989}
+11 -9
View File
@@ -1,10 +1,6 @@
services:
pymc-repeater:
build:
context: .
args:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:main}
container_name: pymc-repeater
restart: unless-stopped
ports:
@@ -18,9 +14,15 @@ services:
# SPI DEVICES PERMISSIONS
cap_add:
- SYS_RAWIO
# USB DEVICSE PERMISSIONS
# USB DEVICE PERMISSIONS
group_add:
- plugdev
- "${GPIO_GID:-986}"
- "${SPI_GID:-989}"
- plugdev
volumes:
- ./config:/etc/pymc_repeater
- ./data:/var/lib/pymc_repeater
- ${PYMC_CONFIG_VOLUME:-pymc-repeater-config}:/etc/pymc_repeater
- ${PYMC_DATA_VOLUME:-pymc-repeater-data}:/var/lib/pymc_repeater
volumes:
pymc-repeater-config:
pymc-repeater-data:
+46 -3
View File
@@ -13,16 +13,48 @@ YQ_CMD="${YQ_CMD:-/usr/local/bin/yq}"
mkdir -p "${CONFIG_DIR}"
print_permission_help() {
echo "If you are bind-mounting ./config or ./data, ensure the host paths are writable by ${RUNTIME_USER} (${RUNTIME_UID}:${RUNTIME_GID})." >&2
echo "For the default image user, run: sudo chown -R ${RUNTIME_UID}:${RUNTIME_GID} ./config ./data" >&2
}
fail_bad_config_mount() {
echo "Invalid Docker config mount: ${CONFIG_PATH} is a directory, but it must be the config file." >&2
echo "This usually happens when ./config.yaml is bind-mounted before that host file exists." >&2
echo "Use the supported folder mount instead:" >&2
echo " - ./config:/etc/pymc_repeater" >&2
echo "Then place the config at ./config/config.yaml." >&2
print_permission_help
exit 1
}
copy_or_die() {
src="$1"
dest="$2"
if ! cp "${src}" "${dest}"; then
echo "Failed to initialize ${dest} from ${src}." >&2
echo "If you are bind-mounting ./config.yaml, ensure the host path is writable by ${RUNTIME_USER} (${RUNTIME_UID}:${RUNTIME_GID})." >&2
print_permission_help
exit 1
fi
}
use_runtime_merged_config() {
src="$1"
runtime_dir="$(mktemp -d /tmp/pymc-repeater-config.XXXXXX)"
runtime_config="${runtime_dir}/config.yaml"
if ! cp "${src}" "${runtime_config}"; then
echo "Failed to prepare temporary merged config at ${runtime_config}; keeping the existing config." >&2
return 1
fi
CONFIG_PATH="${runtime_config}"
echo "Using merged config from ${CONFIG_PATH} for this container start only." >&2
echo "Fix the bind-mounted config ownership so future upgrades can persist merged config changes." >&2
print_permission_help
return 0
}
merge_config_from_example() {
config_path="$1"
@@ -62,15 +94,26 @@ merge_config_from_example() {
fi
if ! cmp -s "${config_path}" "${merged_config}"; then
copy_or_die "${merged_config}" "${config_path}"
if ! cp "${merged_config}" "${config_path}"; then
echo "Failed to update ${config_path} from merged config; the bind-mounted config is not writable." >&2
use_runtime_merged_config "${merged_config}" || true
fi
fi
cleanup_merge
trap - EXIT HUP INT TERM
}
if [ -d "${CONFIG_PATH}" ] && [ "$(basename "${CONFIG_PATH}")" = "config.yaml" ]; then
fail_bad_config_mount
fi
if [ ! -f "${EXAMPLE_PATH}" ] && [ -f "${BUNDLED_EXAMPLE_PATH}" ]; then
copy_or_die "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}"
if ! cp "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}"; then
echo "Could not copy bundled example config to ${EXAMPLE_PATH}; using bundled example for config merge only." >&2
print_permission_help
EXAMPLE_PATH="${BUNDLED_EXAMPLE_PATH}"
fi
fi
if [ -d "${CONFIG_PATH}" ]; then
+9 -2
View File
@@ -5,6 +5,8 @@ ARG USER=repeater
ARG GROUP=repeater
ARG PUID=15888
ARG PGID=15888
ARG GPIO_GID=986
ARG SPI_GID=989
ARG TARGETARCH
ARG YQ_VERSION=v4.40.5
@@ -16,7 +18,9 @@ ENV INSTALL_DIR=/opt/pymc_repeater \
PYTHONUNBUFFERED=1 \
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER=${PACKAGE_VERSION} \
PUID=${PUID} \
PGID=${PGID}
PGID=${PGID} \
GPIO_GID=${GPIO_GID} \
SPI_GID=${SPI_GID}
# Install runtime dependencies only
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
@@ -45,7 +49,10 @@ RUN arch="${TARGETARCH:-}" \
# Create the group and user in order to run without root privileges
RUN groupadd --gid "$PGID" "$GROUP" \
&& useradd --uid "$PUID" --gid "$PGID" --home-dir "$HOME_DIR" --create-home --shell /usr/bin/bash "$USER"
&& groupadd --gid "$GPIO_GID" gpio \
&& groupadd --gid "$SPI_GID" spi \
&& useradd --uid "$PUID" --gid "$PGID" --home-dir "$HOME_DIR" --create-home --shell /usr/bin/bash "$USER" \
&& usermod -a -G gpio,spi "$USER"
# Create runtime directories
RUN mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} \