embystream 0.0.29

Another Emby streaming application (frontend/backend separation) written in Rust.
Documentation
# =========================================================================
# Stage 1: Build the Rust application for a specific target
# =========================================================================
FROM rust:1.88.0-slim AS builder

# BuildKit sets TARGETPLATFORM per --platform (e.g. linux/amd64, linux/arm64).
# Do NOT default TARGETARCH to amd64: multi-platform + cache can otherwise
# compile the wrong triple (e.g. x86_64-musl under an arm64 target), breaking aws-lc-sys (-m64).
ARG TARGETPLATFORM
ARG TARGETARCH

# Resolve musl triple from TARGETPLATFORM (single source of truth for cross/QEMU builds).
RUN set -eu; \
    case "${TARGETPLATFORM:-linux/amd64}" in \
        linux/amd64) echo amd64 > /musl-arch; echo x86_64-unknown-linux-musl > /rust-target ;; \
        linux/arm64) echo arm64 > /musl-arch; echo aarch64-unknown-linux-musl > /rust-target ;; \
        *) echo "unsupported TARGETPLATFORM=${TARGETPLATFORM}" >&2; exit 1 ;; \
    esac; \
    ARCH="$(cat /musl-arch)"; \
    echo "Docker build: TARGETPLATFORM=${TARGETPLATFORM:-} TARGETARCH=${TARGETARCH:-} -> ARCH=${ARCH}"

# aws-lc-sys (rustls) needs CMake + a C/C++ toolchain; musl-tools for *-linux-musl link.
# rust-version in Cargo.toml must stay <= image rustc (see FROM above).
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    cmake \
    musl-tools \
    && \
    ARCH="$(cat /musl-arch)"; \
    case "$ARCH" in \
        amd64) rustup target add x86_64-unknown-linux-musl ;; \
        arm64) rustup target add aarch64-unknown-linux-musl ;; \
        *) echo "unsupported ARCH=$ARCH" >&2; exit 1 ;; \
    esac && \
    rm -rf /var/lib/apt/lists/*

# Point aws-lc-sys / cc-rs at the arch-correct musl GCC (matches CI musl-tools layout).
RUN set -eu; \
    ARCH="$(cat /musl-arch)"; \
    case "$ARCH" in \
        amd64) \
            printf '%s\n' \
                'export CC_x86_64_unknown_linux_musl=x86_64-linux-musl-gcc' \
                'export CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=x86_64-linux-musl-gcc' \
                > /etc/musl-cargo-env.sh \
            ;; \
        arm64) \
            printf '%s\n' \
                'export CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc' \
                'export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc' \
                > /etc/musl-cargo-env.sh \
            ;; \
        *) echo "unsupported ARCH=$ARCH" >&2; exit 1 ;; \
    esac

WORKDIR /usr/src/app

# Create a dummy project to cache dependencies
RUN cargo init --bin .

# Copy dependency manifests
COPY Cargo.toml Cargo.lock ./

# Build only the dependencies for the specified target
# This step is cached as long as Cargo.toml/Cargo.lock don't change.
RUN . /etc/musl-cargo-env.sh && \
    RUST_TARGET="$(cat /rust-target)" && \
    cargo build --release --target "$RUST_TARGET"

# Now, copy the actual application source code and build it
RUN rm src/*.rs
COPY src ./src
# Embedded at compile time via include_str! in src/i18n.rs (../locales/...)
COPY locales ./locales
RUN . /etc/musl-cargo-env.sh && \
    RUST_TARGET="$(cat /rust-target)" && \
    cargo build --release --target "$RUST_TARGET"

# Create a symlink to the final build artifact with a predictable name
# This solves the problem of the COPY command in the next stage not being able to use logic.
RUN RUST_TARGET="$(cat /rust-target)" && \
    ln -s "/usr/src/app/target/${RUST_TARGET}" /usr/src/app/target/final_target

# =========================================================================
# Stage 2: Create the final, minimal production image using Alpine
# =========================================================================
FROM alpine:latest

# Install ca-certificates for making HTTPS requests, a common dependency.
RUN apk --no-cache add ca-certificates

WORKDIR /app

# Create necessary directories
RUN mkdir -p /config/embystream

# Copy the configuration file template
COPY src/config/config.toml.template /config/embystream/config.toml

# Copy the compiled binary from the 'builder' stage using the predictable symlink
COPY --from=builder /usr/src/app/target/final_target/release/embystream /app/embystream

# Set the default command to run when the container starts.
CMD ["/app/embystream", "run", "--config", "/config/embystream/config.toml"]