Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docker/Dockerfile.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Local-testing variant of docker/Dockerfile.
# Instead of installing the published @flowfuse/device-agent from npm, it
# installs a tarball built from the local working copy via `npm pack`.
#
# Build context MUST be the device-agent repo root so the .tgz is reachable:
# cd packages/device-agent
# npm pack
# docker build -f docker/Dockerfile.local -t flowfuse/device-agent:local .
ARG NODE_VERSION=24
FROM node:${NODE_VERSION}-alpine

ARG FF_UID=2000
ARG FF_GID=2000

RUN apk add --no-cache --virtual buildtools build-base linux-headers udev python3 openssl

RUN addgroup -g ${FF_GID} -S flowfuse \
&& adduser -u ${FF_UID} -S -G flowfuse -h /opt/flowfuse-device flowfuse \
&& mkdir -p /opt/flowfuse-device \
&& chown -R "${FF_UID}":"${FF_GID}" /opt/flowfuse-device

# Copy the locally-built package tarball into the image and install it globally.
COPY flowfuse-device-agent-*.tgz /tmp/device-agent.tgz
RUN npm install -g npm \
&& npm config set cache /opt/flowfuse-device/.npm --global \
&& npm install -g /tmp/device-agent.tgz --omit=dev \
&& rm -f /tmp/device-agent.tgz \
&& chown -R ${FF_UID}:${FF_GID} /opt/flowfuse-device

EXPOSE 1880

ENV HOME=/opt/flowfuse-device

USER flowfuse

CMD ["flowfuse-device-agent"]
51 changes: 51 additions & 0 deletions docker/build-local.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## Local feature branch testing

The image published to a registry installs the released `@flowfuse/device-agent`
package from npm. When testing **local code changes** (e.g. on a feature branch),
you can use the provided helper script to build an image from your branch instead.

The script uses [`Dockerfile.local`](./Dockerfile.local), which packages the local
source with `npm pack` and installs that tarball — mirroring how the production
image installs the published package, but with your changes.

### Building

From the device-agent repo root, run the script:

```bash
# Linux / macOS / Windows Subsystem for Linux (WSL)
# optional: make the script executable:
sudo chmod +x ./docker/build-local.sh
# then run:
./docker/build-local.sh
```

The script will:

- remove any stale `flowfuse-device-agent-*.tgz`
- run `npm pack` to package the local working copy
- build and tag the image based on the current git branch:
- on `main` → `flowfuse/device-agent:local-build`
- on any other branch &rarr; `flowfuse/device-agent:<branch>-local-build`
(branch lowercased, illegal tag characters replaced with `-`)

You can override the defaults:

```bash
IMAGE_NAME=myorg/device-agent ./docker/build-local.sh # override repo name
TAG=custom-tag ./docker/build-local.sh # override the whole tag
```

### Running

Run exactly as the published image, just using the locally-built tag and
mounting your `device.yml`:

```bash
docker run --rm -it -v /path/to/device.yml:/opt/flowfuse-device/device.yml -p 1880:1880 flowfuse/device-agent:local-build
```

Re-running a build script reassigns the tag to the freshly built image; the
previous build becomes a dangling (`<none>`) image. A running container is not
updated automatically — stop it and `docker run` again to pick up a rebuild.
Clean up old dangling images occasionally with `docker image prune -f`.
69 changes: 69 additions & 0 deletions docker/build-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# Builds a local-testing image of the device-agent from the current working copy.
#
# - clears any stale flowfuse-device-agent-*.tgz
# - runs `npm pack` to package the local code
# - tags the image based on the current git branch:
# main -> flowfuse/device-agent:local-build
# <other-branch> -> flowfuse/device-agent:<branch>-local-build
#
# Usage (from anywhere):
# ./docker/build-local.sh
# IMAGE_NAME=myorg/device-agent ./docker/build-local.sh # override repo name
# TAG=custom-tag ./docker/build-local.sh # override the whole tag

# Fail fast: -e exits on any command error, -u errors on unset variables,
# -o pipefail makes a pipeline fail if any stage (not just the last) fails.
set -euo pipefail

# Generate a default image name and tag if not supplied by the caller.
IMAGE_NAME="${IMAGE_NAME:-flowfuse/device-agent}"
TAG="${TAG:-}"

# Resolve the device-agent repo root (parent of this script's /docker dir) and work from there.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
cd "$REPO_ROOT"

# 1. Clear any old tarballs so only the fresh pack remains.
for f in flowfuse-device-agent-*.tgz; do
if [ -e "$f" ]; then
echo "Removing old pack: $f"
rm -f "$f"
fi
done

# 2. Package the local working copy.
echo "Running npm pack..."
npm pack

# 3. Work out the tag from the branch name (unless one was supplied).
if [ -z "$TAG" ]; then
branch="$(git rev-parse --abbrev-ref HEAD)"
if [ "$branch" = "main" ]; then
TAG="local-build"
else
# Sanitise: lowercase, and replace anything not allowed in a docker tag with '-'
safe_branch="$(echo "$branch" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_.-]/-/g')"
TAG="${safe_branch}-local-build"
fi
fi

FULL_IMAGE="${IMAGE_NAME}:${TAG}"

# 4. Build the image (context = repo root, so the .tgz is reachable by COPY).
echo "Building image: $FULL_IMAGE"
docker build -f docker/Dockerfile.local -t "$FULL_IMAGE" .

echo ""
echo "Built $FULL_IMAGE"
echo ""
echo "Enter the following command to run the device-agent:"
echo " docker run --rm -it \\"
echo " -v /opt/flowfuse-device-docker-local/device.yml:/opt/flowfuse-device/device.yml \\"
echo " -p 1888:1880 \\"
echo " $FULL_IMAGE"
echo ""
echo "NOTE:"
echo "Entering the above command will run the device-agent on port 1888"
echo "using your local device.yml (edit as needed before hitting enter)"
Loading