-
Notifications
You must be signed in to change notification settings - Fork 4
Ship projectMM as a container, and give each instance an identity #98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b94a833
Ship projectMM as a container, and give each instance an identity
ewowi 3ac229a
Run the container rehearsal from a push on its own branch
ewowi 3727e63
Build the container rehearsal at a real version
ewowi b8bbf95
Give the rehearsal container a name Docker accepts
ewowi cde478b
Merge main, and harden the container identity
ewowi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| # projectMM as a container: the desktop firmware, which is the whole system without an ESP32. | ||
| # | ||
| # The desktop build is not a simulator. It runs the same effect pipeline, the same web UI and the | ||
| # same driver stack as a board, and it drives real fixtures over Art-Net, DDP and E1.31, so a | ||
| # container is a complete live installation for anyone whose fixtures are on the network. | ||
| # | ||
| # It INSTALLS the released .deb rather than building from source, deliberately. The release already | ||
| # produces that package; building here would be a second build path to keep working, and the two | ||
| # would drift. The image is packaging, not a build. | ||
| # | ||
| # docker build -t projectmm . # the rolling prerelease (default) | ||
| # docker build --build-arg RELEASE=stable -t projectmm . # the newest stable release | ||
| # docker build --build-arg RELEASE=v4.0.0 -t projectmm . # a specific tag | ||
| # docker run --rm -p 8080:8080 -v projectmm:/data projectmm | ||
| # | ||
| # Then open http://localhost:8080/. | ||
| # | ||
| # **Ports.** 8080 is the web UI, and the only port needed to try it out. Driving fixtures is | ||
| # OUTBOUND: Art-Net on UDP 6454, DDP on 4048, E1.31/sACN on 5568. Those are L3 and reach a unicast | ||
| # fixture address through ordinary bridge networking. | ||
| # | ||
| # **When L2 matters.** mDNS discovery (finding boards, being found by them) is multicast and does | ||
| # not cross a bridge network, and Art-Net's broadcast mode has the same problem. For those, attach | ||
| # the container to the host's network directly (`--network host`, or an L2 CNI on Kubernetes). | ||
| # Unicast output needs none of it. NOT verified on a Linux host yet: on macOS and Windows, Docker | ||
| # Desktop runs a Linux VM, so `--network host` joins the VM rather than the machine's LAN and the | ||
| # question cannot be answered there. | ||
| # | ||
| # **Capabilities.** None. It binds 8080 as an ordinary process and needs no added capability. | ||
| # | ||
| # **amd64 only.** The release ships no arm64 LINUX binary (macOS arm64 is a different target), so | ||
| # an arm64 image needs an arm64 build in the release pipeline first, not a change here. | ||
|
|
||
| # --- stage 1: fetch the release and unpack it ------------------------------------------------- | ||
| # A full Debian image, used only to resolve and extract the .deb. None of it reaches the result. | ||
| FROM debian:trixie-slim AS fetch | ||
|
|
||
| # WHICH release to install, and the default is the ROLLING PRERELEASE, matching what the installer | ||
| # page offers rather than the last tagged version: projectMM ships from `main` continuously, so a | ||
| # tagged release can be months behind what a board would be flashed with, and an image that lagged | ||
| # the firmware would be the wrong thing to test against. | ||
| # | ||
| # `latest` here is a real git TAG carrying that rolling build, not GitHub's "latest release" idea. | ||
| # `stable` is the special value asking for GitHub's newest NON-prerelease, and anything else is | ||
| # taken as a literal tag. The two words genuinely differ, which is why both exist. | ||
| ARG RELEASE=latest | ||
| ARG REPO=MoonModules/projectMM | ||
|
|
||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends ca-certificates curl \ | ||
| && if [ "$RELEASE" = "stable" ]; then \ | ||
| api="https://api.github.com/repos/${REPO}/releases/latest"; \ | ||
| else \ | ||
| api="https://api.github.com/repos/${REPO}/releases/tags/${RELEASE}"; \ | ||
| fi \ | ||
| && url=$(curl -fsSL "$api" | grep -o 'https://[^"]*_amd64\.deb' | head -1) \ | ||
| && test -n "$url" || { echo "no amd64 .deb in release ${RELEASE}" >&2; exit 1; } \ | ||
| && curl -fsSL -o /tmp/projectmm.deb "$url" \ | ||
| && dpkg-deb -x /tmp/projectmm.deb /rootfs | ||
|
|
||
| # --- stage 2: the image that ships ------------------------------------------------------------ | ||
| # Distroless: the binary plus its four shared libraries, with no shell and no package manager, so | ||
| # the attack surface is the application rather than a distribution. `ldd` on the release binary | ||
| # lists exactly libstdc++, libm, libgcc_s and libc, which is the whole reason this fits: nothing | ||
| # else has to come along. 45 MB against 140 MB for the full-Debian form. | ||
| # | ||
| # **debian13, NOT debian12**, and this is load-bearing. The release is built on ubuntu-24.04 | ||
| # (glibc 2.39), so the binary requires glibc >= 2.38. The debian12/bookworm images ship 2.36, where | ||
| # it installs cleanly and then dies at startup with "GLIBC_2.38 not found" from libc and libm. | ||
| # Verified both ways on the bench. If the release ever moves to an older builder, this can too. | ||
| # Pinned by digest, not by tag: `cc-debian13` is mutable, so an unpinned base means two | ||
| # builds of the same commit can ship different runtimes. Re-pin deliberately when picking | ||
| # up base updates (docker buildx imagetools inspect gcr.io/distroless/cc-debian13:latest). | ||
| FROM gcr.io/distroless/cc-debian13@sha256:9b615fff20e1a4fad29c2b30562580b212c7dd5e2225236735cca0070ed11c78 | ||
|
|
||
| COPY --from=fetch /rootfs/usr/bin/projectMM /usr/bin/projectMM | ||
|
|
||
| # WHERE THE CONFIG LIVES, and why this line is required rather than a convenience. The desktop | ||
| # build resolves its data directory from the environment (platform_desktop.cpp, userDataDir): on | ||
| # Linux XDG_DATA_HOME first, then HOME/.local/share. A container has NEITHER, and the function then | ||
| # returns empty, so without this the app has nowhere defined to write. Setting it explicitly also | ||
| # gives the volume one documented path instead of a guess: config lands in /data/projectMM/.config. | ||
| ENV XDG_DATA_HOME=/data | ||
| VOLUME /data | ||
|
|
||
| EXPOSE 8080 | ||
|
|
||
| ENTRYPOINT ["/usr/bin/projectMM"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # projectMM in a container: the desktop firmware, which is the whole system without an ESP32. | ||
| # | ||
| # docker compose up -d start it, in the background | ||
| # docker compose logs -f watch it | ||
| # docker compose down stop it (the volume, and your config, survive) | ||
| # | ||
| # Then open http://localhost:8081/. | ||
| # | ||
| # Everything worth changing is one line, marked CHANGE ME. | ||
|
|
||
| services: | ||
| projectmm: | ||
| build: . | ||
| # The released binary is amd64 only, so this line is REQUIRED on an arm64 host (an Apple-silicon | ||
| # Mac, an ARM server). Without it compose builds for the host's own architecture and the | ||
| # container dies with "rosetta error: failed to open elf" the moment it starts. It is harmless | ||
| # on an amd64 host, where it is what would have happened anyway. It goes when the release grows | ||
| # an arm64 Linux build. | ||
| platform: linux/amd64 | ||
| # Or, once images are published, drop `build:` and use one of these instead: | ||
| # image: ghcr.io/moonmodules/projectmm:latest # the rolling prerelease | ||
| # image: ghcr.io/moonmodules/projectmm:4.0.0 # a fixed version | ||
| container_name: projectmm | ||
|
|
||
| ports: | ||
| # CHANGE ME: the port YOU open in the browser is the left one. 8081 rather than 8080 so a | ||
| # container never fights a projectMM already installed on the machine. The right side is the | ||
| # port inside the container and does not change: containers do not share a port space, so | ||
| # several instances can all listen on 8080 internally with different left-hand numbers. | ||
| - "8081:8080" | ||
|
|
||
| volumes: | ||
| # Config, presets and scripts. Everything the app writes lands in /data/projectMM, so this one | ||
| # mount is the whole of its state: remove it and a restart comes up factory-fresh. | ||
| - projectmm-data:/data | ||
|
|
||
| # The container has no browser to open, and an unknown argument is a hard error rather than | ||
| # something ignored, so this is the one flag worth passing by default. The other is --port, | ||
| # which only matters under host networking (see the bottom of this file). | ||
| command: ["--no-browser"] | ||
|
|
||
| # NAMING an instance happens in the UI (System > deviceName), not on the command line: it is | ||
| # persisted state, so it lives in the volume above and survives a restart. It is not a hostname | ||
| # either, because the desktop build advertises no mDNS. Instances address each other by IP, | ||
| # which is what the network drivers take. | ||
|
|
||
| restart: unless-stopped | ||
|
|
||
| # CPU is the real limit when running SEVERAL instances, and the reason is worth knowing: the | ||
| # desktop build renders as fast as the machine allows (measured at ~83,000 fps in a container), | ||
| # because nothing paces it the way an LED refresh paces a board. One instance therefore takes a | ||
| # whole core, and three take three. Uncomment to cap it; 0.5 is ample for output over the | ||
| # network, where the wire rate is what actually matters. | ||
| # deploy: | ||
| # resources: | ||
| # limits: | ||
| # cpus: "0.5" | ||
|
|
||
| # For DISCOVERY and BROADCAST output, replace the `ports:` block above with host networking: | ||
| # | ||
| # network_mode: host | ||
| # | ||
| # Unicast Art-Net (UDP 6454), DDP (4048) and E1.31 (5568) reach a fixture perfectly well through | ||
| # the ordinary port mapping above; it is broadcast and multicast that a bridge network does not | ||
| # carry. With host networking there is no port mapping, so add `--port 8081` to `command:` above to | ||
| # keep clear of anything already on 8080. | ||
|
|
||
| volumes: | ||
| projectmm-data: |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🤖 get_repo_knowledge executed:
get_repo_knowledge MoonModules/projectMM /tmp/coderabbit-repo-knowledge/moonmodules-projectmm-df788503Length of output: 2931
🏁 Script executed:
Repository: MoonModules/projectMM
Length of output: 19687
🏁 Script executed:
Repository: MoonModules/projectMM
Length of output: 19536
🏁 Script executed:
Repository: MoonModules/projectMM
Length of output: 12658
Pin the listed GitHub Actions to reviewed full commit SHAs.
These actions run in jobs with
packages: write;container-test.ymlalso hascontents: write. Mutable version tags can therefore introduce changed action code into publishing workflows. Apply this to the five references in.github/workflows/release.ymland the six references in.github/workflows/container-test.yml.🧰 Tools
🪛 zizmor (1.29.0)
[error] 613-613: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
📍 Affects 2 files
.github/workflows/release.yml#L613-L613(this comment).github/workflows/release.yml#L618-L618.github/workflows/release.yml#L623-L623.github/workflows/release.yml#L625-L625.github/workflows/release.yml#L668-L668.github/workflows/container-test.yml#L31-L31.github/workflows/container-test.yml#L37-L37.github/workflows/container-test.yml#L50-L50.github/workflows/container-test.yml#L52-L52.github/workflows/container-test.yml#L87-L87.github/workflows/container-test.yml#L136-L136🤖 Prompt for AI Agents
Source: Linters/SAST tools