From bca331802dd9d0bc7e8c57f03b8c8a4f20ce63a6 Mon Sep 17 00:00:00 2001 From: yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Thu, 21 May 2026 13:24:39 -0400 Subject: [PATCH 1/3] docker: pin core in published repeater images --- .github/workflows/docker-publish.yml | 93 +++++++++++++++++++++------- README.md | 7 ++- docker-compose.yml | 10 +-- dockerfile | 12 +++- 4 files changed, 91 insertions(+), 31 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c96b1a3..5d1d86c 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -5,18 +5,23 @@ on: branches: - main - dev + repository_dispatch: + types: + - rebuild-from-core workflow_dispatch: inputs: image_repository: description: "Docker image repository to publish to" required: false - default: "pymcdev/pymc-repeater" + default: "" jobs: docker: if: | github.event_name == 'workflow_dispatch' || + github.event_name == 'repository_dispatch' || github.repository == 'pyMC-dev/pyMC_Repeater' || + github.repository == 'rightup/pyMC_Repeater' || github.repository == 'yellowcooln/pyMC_Repeater' runs-on: ubuntu-latest permissions: @@ -27,6 +32,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + ref: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.channel || github.ref_name }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -39,6 +45,58 @@ jobs: with: python-version: "3.12" + - name: Determine build context + id: context + shell: bash + env: + INPUT_IMAGE_REPOSITORY: ${{ inputs.image_repository }} + PAYLOAD_CHANNEL: ${{ github.event.client_payload.channel }} + PAYLOAD_CORE_REF: ${{ github.event.client_payload.core_ref }} + PAYLOAD_CORE_SHA: ${{ github.event.client_payload.core_sha }} + run: | + set -euo pipefail + + channel="${PAYLOAD_CHANNEL:-${GITHUB_REF_NAME}}" + case "${channel}" in + main|dev) ;; + *) + echo "Unsupported repeater channel: ${channel}" >&2 + exit 1 + ;; + esac + + core_ref="${PAYLOAD_CORE_REF:-${channel}}" + if [ -n "${PAYLOAD_CORE_SHA}" ]; then + core_sha="${PAYLOAD_CORE_SHA}" + else + core_sha="$(git ls-remote https://github.com/pyMC-dev/pyMC_core.git "refs/heads/${core_ref}" | cut -f1)" + fi + + if [ -z "${core_sha}" ]; then + echo "Failed to resolve pyMC_core ref ${core_ref}" >&2 + exit 1 + fi + + if [ -n "${INPUT_IMAGE_REPOSITORY}" ]; then + image_repository="${INPUT_IMAGE_REPOSITORY}" + elif [ "${{ github.repository }}" = "yellowcooln/pyMC_Repeater" ]; then + image_repository="yellowcooln/pymc-repeater" + else + image_repository="pymcdev/pymc-repeater" + fi + + repeater_sha="$(git rev-parse HEAD)" + repeater_short_sha="$(git rev-parse --short=7 HEAD)" + + echo "Using image repository: ${image_repository}" + echo "Building repeater ${channel} against pyMC_core ${core_ref} @ ${core_sha}" + echo "channel=${channel}" >> "$GITHUB_OUTPUT" + echo "core_ref=${core_ref}" >> "$GITHUB_OUTPUT" + echo "core_sha=${core_sha}" >> "$GITHUB_OUTPUT" + echo "image_repository=${image_repository}" >> "$GITHUB_OUTPUT" + echo "repeater_sha=${repeater_sha}" >> "$GITHUB_OUTPUT" + echo "repeater_short_sha=${repeater_short_sha}" >> "$GITHUB_OUTPUT" + - name: Compute package version id: package_version run: | @@ -52,30 +110,19 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Determine image repository - id: image_repo - shell: bash - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.image_repository }}" ]; then - image_repository="${{ inputs.image_repository }}" - elif [ "${{ github.repository }}" = "yellowcooln/pyMC_Repeater" ]; then - image_repository="yellowcooln/pymc-repeater" - else - image_repository="pymcdev/pymc-repeater" - fi - - echo "Using image repository: ${image_repository}" - echo "image_repository=${image_repository}" >> "$GITHUB_OUTPUT" - - name: Extract Docker metadata id: meta uses: docker/metadata-action@v5 with: - images: ${{ steps.image_repo.outputs.image_repository }} + images: ${{ steps.context.outputs.image_repository }} tags: | - type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }} - type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }} - type=sha,format=short + type=raw,value=${{ steps.context.outputs.channel }} + type=raw,value=sha-${{ steps.context.outputs.repeater_short_sha }} + labels: | + org.opencontainers.image.revision=${{ steps.context.outputs.repeater_sha }} + io.pymc.repeater.channel=${{ steps.context.outputs.channel }} + io.pymc.core.ref=${{ steps.context.outputs.core_ref }} + io.pymc.core.sha=${{ steps.context.outputs.core_sha }} - name: Build and push uses: docker/build-push-action@v6 @@ -86,6 +133,8 @@ jobs: push: true build-args: | PACKAGE_VERSION=${{ steps.package_version.outputs.version }} + PYMC_CORE_REF=${{ steps.context.outputs.core_ref }} + PYMC_CORE_SHA=${{ steps.context.outputs.core_sha }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha @@ -95,8 +144,8 @@ jobs: if: github.repository == 'pyMC-dev/pyMC_Repeater' env: DISPATCH_TOKEN: ${{ secrets.HA_ADDON_REPO_DISPATCH_TOKEN }} - CHANNEL: ${{ github.ref_name }} - REVISION: ${{ github.sha }} + CHANNEL: ${{ steps.context.outputs.channel }} + REVISION: ${{ steps.context.outputs.repeater_sha }} run: | if [ -z "${DISPATCH_TOKEN}" ]; then echo "HA_ADDON_REPO_DISPATCH_TOKEN is not set" >&2 diff --git a/README.md b/README.md index b048689..83f5305 100644 --- a/README.md +++ b/README.md @@ -365,10 +365,13 @@ sudo bash ./setup-radio-config.sh 4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. -5. Build and start the container. +5. Pull and start the container. The example compose file defaults to + `pymcdev/pymc-repeater:dev`. Override `PYMC_REPEATER_IMAGE` if you want a + different channel or a fork image. ```bash -docker compose up -d --force-recreate --build +docker compose pull +docker compose up -d --force-recreate ``` ## Roadmap / Planned Features diff --git a/docker-compose.yml b/docker-compose.yml index 647db59..d0aabcb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,12 @@ services: pymc-repeater: - build: - context: . - args: - PUID: ${PUID:-1000} - PGID: ${PGID:-1000} + image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:dev} + pull_policy: always container_name: pymc-repeater restart: unless-stopped + environment: + PUID: ${PUID:-1000} + PGID: ${PGID:-1000} ports: - 8000:8000 devices: diff --git a/dockerfile b/dockerfile index a2e1218..6042367 100644 --- a/dockerfile +++ b/dockerfile @@ -5,6 +5,8 @@ ARG USER=repeater ARG GROUP=repeater ARG PUID=15888 ARG PGID=15888 +ARG PYMC_CORE_REF=dev +ARG PYMC_CORE_SHA ARG TARGETARCH ARG YQ_VERSION=v4.40.5 @@ -64,8 +66,14 @@ 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 ".[rrd]" +# Install the matching pyMC_core ref by resolved commit SHA, then install repeater +# without re-resolving dependencies back to a cached branch tip. +RUN test -n "${PYMC_CORE_SHA}" \ + && echo "Installing pyMC_core ${PYMC_CORE_REF} @ ${PYMC_CORE_SHA}" \ + && pip install --no-cache-dir "pymc_core[hardware] @ git+https://github.com/pyMC-dev/pyMC_core.git@${PYMC_CORE_SHA}" \ + && python -c "import pathlib, tomllib; data = tomllib.loads(pathlib.Path('pyproject.toml').read_text()); deps = [d for d in data['project']['dependencies'] if not d.startswith('pymc_core')]; deps += data['project']['optional-dependencies']['rrd']; pathlib.Path('/tmp/pymc_repeater_requirements.txt').write_text('\n'.join(deps) + '\n')" \ + && pip install --no-cache-dir -r /tmp/pymc_repeater_requirements.txt \ + && pip install --no-cache-dir --no-deps . USER root From 8355d5454ef036b23edde5f914bd97c755cf77bf Mon Sep 17 00:00:00 2001 From: yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Thu, 21 May 2026 13:29:24 -0400 Subject: [PATCH 2/3] ci: drop legacy repeater repo guards --- .github/workflows/docker-publish.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 5d1d86c..faac29b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -21,7 +21,6 @@ jobs: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch' || github.repository == 'pyMC-dev/pyMC_Repeater' || - github.repository == 'rightup/pyMC_Repeater' || github.repository == 'yellowcooln/pyMC_Repeater' runs-on: ubuntu-latest permissions: From 9cc893c2badd00e06c98139f657e0367d8031234 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Wed, 27 May 2026 10:44:20 -0400 Subject: [PATCH 3/3] docker: fix core fallback and compose docs --- README.md | 19 ++++++++++++++----- docker-compose.yml | 4 ++-- dockerfile | 10 +++++++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 83f5305..f3753aa 100644 --- a/README.md +++ b/README.md @@ -349,23 +349,32 @@ You can now run pyMC Repeater from within a [Docker Container](https://www.docke Here is what you'll need to do in order to get the container running: -1. Copy the `config.yaml.example` to `config.yaml` +1. Create the bind mount directories and copy the example config into the + Docker config directory. ```bash -cp ./config.yaml.example ./config.yaml +mkdir -p ./config ./data +cp ./config.yaml.example ./config/config.yaml ``` 2. Run the configuration script and follow the prompts. ```bash -sudo bash ./setup-radio-config.sh +sudo bash ./setup-radio-config.sh ./config ``` -3. Modify the `config.yaml` file with a unique web UI password. This allows you to bypass the `/setup` page when logging for the first time. You can find the value under `repeater.security.admin_password`. Change to _anything_ besides the default of `admin123`. +3. Modify `./config/config.yaml` with a unique web UI password. This allows you to bypass the `/setup` page when logging for the first time. You can find the value under `repeater.security.admin_password`. Change to _anything_ besides the default of `admin123`. 4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. -5. Pull and start the container. The example compose file defaults to +5. Make the bind mount directories writable by the container user. The + published image runs as UID/GID `15888` by default. + +```bash +sudo chown -R 15888:15888 ./config ./data +``` + +6. Pull and start the container. The example compose file defaults to `pymcdev/pymc-repeater:dev`. Override `PYMC_REPEATER_IMAGE` if you want a different channel or a fork image. diff --git a/docker-compose.yml b/docker-compose.yml index d0aabcb..8ffc90b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,8 +5,8 @@ services: container_name: pymc-repeater restart: unless-stopped environment: - PUID: ${PUID:-1000} - PGID: ${PGID:-1000} + PUID: ${PUID:-15888} + PGID: ${PGID:-15888} ports: - 8000:8000 devices: diff --git a/dockerfile b/dockerfile index 6042367..b98f972 100644 --- a/dockerfile +++ b/dockerfile @@ -68,9 +68,13 @@ USER ${USER} # Install the matching pyMC_core ref by resolved commit SHA, then install repeater # without re-resolving dependencies back to a cached branch tip. -RUN test -n "${PYMC_CORE_SHA}" \ - && echo "Installing pyMC_core ${PYMC_CORE_REF} @ ${PYMC_CORE_SHA}" \ - && pip install --no-cache-dir "pymc_core[hardware] @ git+https://github.com/pyMC-dev/pyMC_core.git@${PYMC_CORE_SHA}" \ +RUN core_sha="${PYMC_CORE_SHA}" \ + && if [ -z "${core_sha}" ]; then \ + core_sha="$(git ls-remote https://github.com/pyMC-dev/pyMC_core.git "refs/heads/${PYMC_CORE_REF}" | cut -f1)"; \ + fi \ + && test -n "${core_sha}" \ + && echo "Installing pyMC_core ${PYMC_CORE_REF} @ ${core_sha}" \ + && pip install --no-cache-dir "pymc_core[hardware] @ git+https://github.com/pyMC-dev/pyMC_core.git@${core_sha}" \ && python -c "import pathlib, tomllib; data = tomllib.loads(pathlib.Path('pyproject.toml').read_text()); deps = [d for d in data['project']['dependencies'] if not d.startswith('pymc_core')]; deps += data['project']['optional-dependencies']['rrd']; pathlib.Path('/tmp/pymc_repeater_requirements.txt').write_text('\n'.join(deps) + '\n')" \ && pip install --no-cache-dir -r /tmp/pymc_repeater_requirements.txt \ && pip install --no-cache-dir --no-deps .