ARG ARCH=%%BALENA_ARCH%% ARG FATRW_VERSION=0.2.21 ARG NODE="nodejs~=20" ARG NPM="npm~=10" ARG ALPINE_VERSION="3.19" ################################################### # Build the supervisor dependencies ################################################### FROM alpine:${ALPINE_VERSION} AS build-base ARG ARCH ARG NODE ARG NPM ARG FATRW_VERSION ARG FATRW_RELEASES="https://github.com/balena-os/fatrw/releases/download/v${FATRW_VERSION}" WORKDIR /usr/src/app RUN apk add --update --no-cache \ build-base \ python3 \ curl \ $NODE \ $NPM \ libuv \ sqlite-dev \ cargo \ rust COPY package*.json ./ COPY ./build-utils/rust-arch.sh / RUN strip "$(which node)" # Install fatrw RUN FATRW_ARCHIVE="fatrw-$(/rust-arch.sh).tar.gz" && \ FATRW_LOCATION="${FATRW_RELEASES}/${FATRW_ARCHIVE}" && \ curl -SLO "${FATRW_LOCATION}" && \ ls -la "${FATRW_ARCHIVE}" && \ tar -xzf "${FATRW_ARCHIVE}" -C /usr/local/bin && \ rm -f "${FATRW_ARCHIVE}" # Just install dev dependencies first RUN npm ci --build-from-source=sqlite3 --sqlite=/usr/lib ################################################################### # Journal access. # The supervisor is built on an alpine image but still needs # to use journalctl (from systemd) which cannot be built for # musl. We hack around this by copying the binary and its library # dependencies to the final image ################################################################### FROM debian:bookworm-slim AS journal RUN apt-get update && apt-get install -y --no-install-recommends systemd COPY ./build-utils/setup-journal.sh / RUN /setup-journal.sh ################################################### # Extra dependencies. This uses alpine 3.11 as the # procmail package was removed on 3.12 ################################################### FROM alpine:3.11 AS extra RUN apk add --update --no-cache procmail ################################################### # Image with the final production dependencies. # This image will also be be used for testing ################################################### FROM alpine:${ALPINE_VERSION} AS runtime-base ARG NODE WORKDIR /usr/src/app # Also copy the fatrw binary COPY --from=build-base /usr/local/bin/fatrw /usr/local/bin/fatrw # Similarly, from the procmail package we just need the lockfile binary COPY --from=extra /usr/bin/lockfile /usr/bin/lockfile # Copy journalctl and library dependecies to the final image COPY --from=journal /sysroot / # Copy mount script for mounting host partitions into container COPY mount-partitions.sh . # Runtime dependencies RUN apk add --update --no-cache \ $NODE \ rsync \ dbus \ dmidecode \ sqlite-libs \ lsblk \ kmod \ device-mapper # Iptables should be pinned to 1.8.9 (legacy) as balenaOS still uses iptables-legacy RUN apk add --update --no-cache \ --repository=http://dl-cdn.alpinelinux.org/alpine/v3.18/main \ iptables~=1.8.9 \ ip6tables~=1.8.9 ARG ARCH ARG VERSION=master ENV LED_FILE=/dev/null \ SUPERVISOR_IMAGE=balena/$ARCH-supervisor \ VERSION=$VERSION ############################################################### # Use the base image to run integration tests and for livepush ############################################################### FROM runtime-base AS test ARG NPM ARG ARCH # We want to use as close to the final image when running tests # but we need npm so we install it here again RUN apk add --update --no-cache $NPM WORKDIR /usr/src/app # Copy build dependencies COPY --from=build-base /usr/src/app/package.json ./ COPY --from=build-base /usr/src/app/node_modules ./node_modules # Run livepush here #dev-copy=entry.sh . #dev-cmd-live=LIVEPUSH=1 ./entry.sh # Copy build files COPY entry.sh . COPY build-utils ./build-utils COPY webpack.config.js tsconfig.json tsconfig.release.json tsconfig.js.json .mochapodrc.yml ./ COPY typings ./typings COPY src ./src COPY test ./test # Fail-safe, check the architecture used by apk against the expected architecture # from the device type RUN APK_ARCH=$(./build-utils/apk-print-arch.sh); [ "$APK_ARCH" = "$ARCH" ] || (echo "Image architecture ($APK_ARCH) does not match the target architecture ($ARCH)" && exit 1) # Run type checking and unit tests here # to prevent setting up a test environment that will # most likely fail. RUN npm run test # When running tests from a container built from this stage, # skip the mocha-pod setup ENV MOCHAPOD_SKIP_SETUP=1 # This command will be used by default when running integration tests # from this stage CMD npm run test:integration ################################################### # Build the production package ################################################### FROM build-base AS build-prod WORKDIR /usr/src/app # Copy build files COPY build-utils ./build-utils COPY webpack.config.js tsconfig.json tsconfig.release.json ./ COPY src ./src COPY typings ./typings # Compile the sources using the dev # dependencies RUN npm run build # Run the production install here, to avoid the npm dependency on # the later stage RUN npm ci \ --omit=dev \ --omit=optional \ --unsafe-perm \ --build-from-source=sqlite3 \ --sqlite=/usr/lib \ && npm cache clean --force \ # For some reason this doesn't get cleared with the other # cache && rm -rf node_modules/.cache \ # Remove various uneeded filetypes in order to reduce space # We also remove the spurious node.dtps, see https://github.com/mapbox/node-sqlite3/issues/861 && find . -path '*/coverage/*' -o -path '*/test/*' -o -path '*/.nyc_output/*' \ -o -name '*.tar.*' -o -name '*.in' -o -name '*.cc' \ -o -name '*.c' -o -name "*.ts" -o -name '*.eslintrc' \ -o -name '*.h' -o -name '*.html' -o -name '*.markdown' \ -o -name '*.md' -o -name '*.patch' -o -name '*.png' \ -o -name '*.yml' \ -delete \ && find . -type f -path '*/node_modules/sqlite3/deps*' -delete \ && find . -type f -path '*/node_modules/knex/build*' -delete \ && rm -rf node_modules/sqlite3/node.dtps ################################################### # Build the production image ################################################### FROM runtime-base WORKDIR /usr/src/app COPY --from=build-prod /usr/src/app/dist ./dist COPY --from=build-prod /usr/src/app/package.json ./ COPY --from=build-prod /usr/src/app/node_modules ./node_modules COPY entry.sh . HEALTHCHECK --interval=5m --start-period=1m --timeout=30s --retries=3 \ CMD wget http://127.0.0.1:${LISTEN_PORT:-48484}/v1/healthy -O - -q CMD ["/usr/src/app/entry.sh"]