mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-08-07 09:23:06 +02:00
Merge pull request #252 from yellowcooln/patch-2
docker: bootstrap config on startup and include rootless container fixes
This commit is contained in:
@@ -16,7 +16,7 @@ jobs:
|
||||
docker:
|
||||
if: |
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
github.repository == 'rightup/pyMC_Repeater' ||
|
||||
github.repository == 'pyMC-dev/pyMC_Repeater' ||
|
||||
github.repository == 'yellowcooln/pyMC_Repeater'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Notify Home Assistant add-on repository
|
||||
if: github.repository == 'rightup/pyMC_Repeater'
|
||||
if: github.repository == 'pyMC-dev/pyMC_Repeater'
|
||||
env:
|
||||
DISPATCH_TOKEN: ${{ secrets.HA_ADDON_REPO_DISPATCH_TOKEN }}
|
||||
CHANNEL: ${{ github.ref_name }}
|
||||
|
||||
+5
-1
@@ -1,6 +1,10 @@
|
||||
services:
|
||||
pymc-repeater:
|
||||
build: .
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
PUID: ${PUID:-1000}
|
||||
PGID: ${PGID:-1000}
|
||||
container_name: pymc-repeater
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/pymc_repeater}"
|
||||
CONFIG_DIR="${CONFIG_DIR:-/etc/pymc_repeater}"
|
||||
CONFIG_PATH="${PYMC_REPEATER_CONFIG:-${CONFIG_DIR}/config.yaml}"
|
||||
EXAMPLE_PATH="${CONFIG_DIR}/config.yaml.example"
|
||||
BUNDLED_EXAMPLE_PATH="${INSTALL_DIR}/config.yaml.example"
|
||||
RUNTIME_USER="${USER:-repeater}"
|
||||
RUNTIME_UID="${PUID:-unknown}"
|
||||
RUNTIME_GID="${PGID:-unknown}"
|
||||
YQ_CMD="${YQ_CMD:-/usr/local/bin/yq}"
|
||||
|
||||
mkdir -p "${CONFIG_DIR}"
|
||||
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
merge_config_from_example() {
|
||||
config_path="$1"
|
||||
|
||||
if [ ! -f "${config_path}" ] || [ ! -f "${EXAMPLE_PATH}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -x "${YQ_CMD}" ] || ! "${YQ_CMD}" --version 2>&1 | grep -q "mikefarah/yq"; then
|
||||
echo "Skipping config merge: mikefarah yq is not available at ${YQ_CMD}." >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
stripped_user="${tmpdir}/config.stripped.yaml"
|
||||
merged_config="${tmpdir}/config.merged.yaml"
|
||||
|
||||
cleanup_merge() {
|
||||
rm -rf "${tmpdir}"
|
||||
}
|
||||
trap cleanup_merge EXIT HUP INT TERM
|
||||
|
||||
# Keep only the example's comments to avoid comment duplication across upgrades.
|
||||
"${YQ_CMD}" eval '... comments=""' "${config_path}" > "${stripped_user}" 2>/dev/null || cp "${config_path}" "${stripped_user}"
|
||||
|
||||
if ! "${YQ_CMD}" eval-all '. as $item ireduce ({}; . * $item)' "${EXAMPLE_PATH}" "${stripped_user}" > "${merged_config}" 2>/dev/null; then
|
||||
echo "Failed to merge ${config_path} with ${EXAMPLE_PATH}; keeping the existing config." >&2
|
||||
cleanup_merge
|
||||
trap - EXIT HUP INT TERM
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! "${YQ_CMD}" eval '.' "${merged_config}" >/dev/null 2>&1; then
|
||||
echo "Merged config for ${config_path} is invalid; keeping the existing config." >&2
|
||||
cleanup_merge
|
||||
trap - EXIT HUP INT TERM
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! cmp -s "${config_path}" "${merged_config}"; then
|
||||
copy_or_die "${merged_config}" "${config_path}"
|
||||
fi
|
||||
|
||||
cleanup_merge
|
||||
trap - EXIT HUP INT TERM
|
||||
}
|
||||
|
||||
if [ ! -f "${EXAMPLE_PATH}" ] && [ -f "${BUNDLED_EXAMPLE_PATH}" ]; then
|
||||
copy_or_die "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}"
|
||||
fi
|
||||
|
||||
if [ -d "${CONFIG_PATH}" ]; then
|
||||
if [ ! -s "${CONFIG_PATH}/config.yaml" ] && [ -f "${EXAMPLE_PATH}" ]; then
|
||||
copy_or_die "${EXAMPLE_PATH}" "${CONFIG_PATH}/config.yaml"
|
||||
fi
|
||||
CONFIG_PATH="${CONFIG_PATH}/config.yaml"
|
||||
elif [ ! -s "${CONFIG_PATH}" ] && [ -f "${EXAMPLE_PATH}" ]; then
|
||||
copy_or_die "${EXAMPLE_PATH}" "${CONFIG_PATH}"
|
||||
fi
|
||||
|
||||
merge_config_from_example "${CONFIG_PATH}"
|
||||
|
||||
exec python3 -m repeater.main --config "${CONFIG_PATH}"
|
||||
+41
-4
@@ -1,15 +1,25 @@
|
||||
FROM python:3.12-slim-bookworm
|
||||
|
||||
ARG PACKAGE_VERSION=1.0.5
|
||||
ARG USER=repeater
|
||||
ARG GROUP=repeater
|
||||
ARG PUID=15888
|
||||
ARG PGID=15888
|
||||
ARG TARGETARCH
|
||||
ARG YQ_VERSION=v4.40.5
|
||||
|
||||
ENV INSTALL_DIR=/opt/pymc_repeater \
|
||||
CONFIG_DIR=/etc/pymc_repeater \
|
||||
DATA_DIR=/var/lib/pymc_repeater \
|
||||
HOME_DIR=/home/${USER} \
|
||||
PATH=/home/${USER}/.local/bin:${PATH} \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER=${PACKAGE_VERSION}
|
||||
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER=${PACKAGE_VERSION} \
|
||||
PUID=${PUID} \
|
||||
PGID=${PGID}
|
||||
|
||||
# Install runtime dependencies only
|
||||
RUN apt-get update && apt-get install -y \
|
||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
|
||||
libffi-dev \
|
||||
python3-rrdtool \
|
||||
jq \
|
||||
@@ -21,20 +31,47 @@ RUN apt-get update && apt-get install -y \
|
||||
python3-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN arch="${TARGETARCH:-}" \
|
||||
&& if [ -z "${arch}" ]; then arch="$(uname -m)"; fi \
|
||||
&& case "${arch}" in \
|
||||
amd64|x86_64) YQ_BINARY="yq_linux_amd64" ;; \
|
||||
arm64|aarch64) YQ_BINARY="yq_linux_arm64" ;; \
|
||||
arm|armv7|armv7l) YQ_BINARY="yq_linux_arm" ;; \
|
||||
*) echo "Unsupported architecture for yq: ${arch}" >&2; exit 1 ;; \
|
||||
esac \
|
||||
&& wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY}" \
|
||||
&& chmod +x /usr/local/bin/yq
|
||||
|
||||
# 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"
|
||||
|
||||
# Create runtime directories
|
||||
RUN mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR}
|
||||
RUN mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} \
|
||||
&& chown -R "$USER":"$GROUP" ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} ${HOME_DIR}
|
||||
|
||||
WORKDIR ${INSTALL_DIR}
|
||||
|
||||
# Copy source
|
||||
COPY repeater ./repeater
|
||||
COPY pyproject.toml .
|
||||
COPY config.yaml.example .
|
||||
COPY radio-presets.json .
|
||||
COPY radio-settings.json .
|
||||
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
# Switch to the unprivileged runtime user
|
||||
USER ${USER}
|
||||
|
||||
# Install package
|
||||
RUN pip install --no-cache-dir .
|
||||
|
||||
USER root
|
||||
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
USER ${USER}
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENTRYPOINT ["python3", "-m", "repeater.main", "--config", "/etc/pymc_repeater/config.yaml"]
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
|
||||
Reference in New Issue
Block a user