diff --git a/.changeset/docker-custom-image-hooks.md b/.changeset/docker-custom-image-hooks.md new file mode 100644 index 0000000..bda892a --- /dev/null +++ b/.changeset/docker-custom-image-hooks.md @@ -0,0 +1,5 @@ +--- +"@jmfederico/pi-web": patch +--- + +Add Docker custom image hooks so local installs can add optional CLIs without bloating the default image. diff --git a/.dockerignore b/.dockerignore index 34f60e9..db14171 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,3 +7,7 @@ dev-plugins *.log .env .DS_Store + +docker/custom-image.d/* +!docker/custom-image.d/.gitkeep +!docker/custom-image.d/*.sh diff --git a/.gitignore b/.gitignore index 052ea5e..afb4be8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,9 @@ dist/ # Local plugin development sandboxes. Symlink these into ~/.pi-web/plugins/. /dev-plugins/ +# Local Docker image build hooks for development containers. +/docker/custom-image.d/* +!/docker/custom-image.d/.gitkeep + # Local runtime attachment uploads (created by the chat composer "save to folder" mode). .pi-web/ diff --git a/README.md b/README.md index 61ad3ba..b924b0c 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ curl -fsSL https://raw.githubusercontent.com/jmfederico/pi-web/main/docker/insta It builds a local image from npm, runs split `sessiond` and `web` services, binds the browser UI to `127.0.0.1:8504` by default, and uses the same command as the update path. The Docker setup intentionally mounts the Docker socket and selected host paths; treat it as root-equivalent host access, do not expose it directly to the public internet, and use an SSH tunnel, VPN, or authenticated reverse proxy for remote access. -See the [Docker guide](https://github.com/jmfederico/pi-web/blob/main/docker/README.md) for trust warnings, version pinning, host command examples, and development Compose usage. +See the [Docker guide](https://github.com/jmfederico/pi-web/blob/main/docker/README.md) for trust warnings, version pinning, custom image hooks for optional CLIs, host command examples, and development Compose usage. PI WEB is also published as a Pi package. Installing it through Pi exposes a `/pi-web` command inside Pi: diff --git a/docker/.dockerignore b/docker/.dockerignore index 1bb8da0..872b011 100644 --- a/docker/.dockerignore +++ b/docker/.dockerignore @@ -3,3 +3,6 @@ !Dockerfile !bin/ !bin/hostexec +!custom-image.d/ +!custom-image.d/.gitkeep +!custom-image.d/*.sh diff --git a/docker/Dockerfile b/docker/Dockerfile index 787aad0..02bde6d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -52,6 +52,16 @@ RUN apt-get update \ && mkdir -p /data/home /data/config /data/pi-web /data/pi-agent /workspace \ && chown -R node:node /data /workspace +COPY custom-image.d/ /tmp/pi-web-custom-image.d/ +RUN bash -euxo pipefail -c '\ + shopt -s nullglob; \ + for script in /tmp/pi-web-custom-image.d/*.sh; do \ + echo "Running PI WEB custom image hook: ${script}"; \ + bash "${script}"; \ + done; \ + rm -rf /tmp/pi-web-custom-image.d /var/lib/apt/lists/* \ +' + COPY --from=package /usr/local/lib/node_modules /usr/local/lib/node_modules COPY --from=package /usr/local/bin /usr/local/bin COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index cdd5afc..bcdcd31 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -46,6 +46,16 @@ RUN npm ci \ COPY --from=docker-cli /usr/local/bin/docker /usr/local/bin/docker COPY --chmod=0755 docker/bin/hostexec /usr/local/bin/hostexec +COPY docker/custom-image.d/ /tmp/pi-web-custom-image.d/ +RUN bash -euxo pipefail -c '\ + shopt -s nullglob; \ + for script in /tmp/pi-web-custom-image.d/*.sh; do \ + echo "Running PI WEB custom image hook: ${script}"; \ + bash "${script}"; \ + done; \ + rm -rf /tmp/pi-web-custom-image.d /var/lib/apt/lists/* \ +' + EXPOSE 8504 8505 ENTRYPOINT ["tini", "--"] diff --git a/docker/README.md b/docker/README.md index f66606f..419128a 100644 --- a/docker/README.md +++ b/docker/README.md @@ -102,6 +102,34 @@ Common environment variables written to `.env`: Host-derived IDs are refreshed on rerun unless you explicitly override them. User-facing values such as data directory, bind address, port, image names, upload limit, and version pins are preserved from an existing `.env` unless you pass a flag or environment override. +### Custom image hooks + +The runtime image can be extended without changing PI WEB's Dockerfile. Put local Bash scripts ending in `.sh` under: + +```text +~/.local/share/pi-web-docker/custom-image.d/ +``` + +The installer preserves that directory, includes the `*.sh` files in the Docker build context, and runs each script as `root` during the image build in lexical order. Use this for optional tools such as `gh`, `glab`, `kubectl`, or cloud CLIs that you do not want in the default image. + +Example: + +```bash +mkdir -p ~/.local/share/pi-web-docker/custom-image.d +$EDITOR ~/.local/share/pi-web-docker/custom-image.d/10-github-cli.sh +curl -fsSL https://raw.githubusercontent.com/jmfederico/pi-web/main/docker/install.sh | sh +``` + +Keep credentials out of these scripts. Authenticate tools after the container starts so secrets live in the persistent `/data` mount, for example through `/data/home` and `/data/config`. + +For Docker development from this checkout, use the equivalent local directory: + +```text +docker/custom-image.d/ +``` + +Files in that development hook directory are ignored by Git except for the placeholder that keeps the directory available to Docker builds. + ### Version pinning Pin npm package versions when you want repeatable rebuilds: diff --git a/docker/custom-image.d/.gitkeep b/docker/custom-image.d/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker/install.sh b/docker/install.sh index 6800042..27fc2fb 100755 --- a/docker/install.sh +++ b/docker/install.sh @@ -306,6 +306,12 @@ write_asset .dockerignore 0644 write_asset install.sh 0755 write_asset bin/hostexec 0755 +custom_image_hooks_dir=$install_dir/custom-image.d +mkdir -p "$custom_image_hooks_dir" || die "could not create custom image hooks directory: $custom_image_hooks_dir" +if [ ! -e "$custom_image_hooks_dir/.gitkeep" ]; then + : >"$custom_image_hooks_dir/.gitkeep" || die "could not initialize custom image hooks directory: $custom_image_hooks_dir" +fi + pi_web_uid=$(value_from_env_or_default PI_WEB_UID "$(id -u)") pi_web_gid=$(value_from_env_or_default PI_WEB_GID "$(id -g)") docker_gid=$(value_from_env_or_default DOCKER_GID "$(detect_docker_gid)") @@ -364,6 +370,7 @@ mv "$temp_env" "$env_file" log "Wrote Docker assets to $install_dir" log "Wrote runtime environment to $env_file" log "Persistent PI WEB Docker data: $data_dir" +log "Custom image hooks: $custom_image_hooks_dir" if [ "${PI_WEB_DOCKER_SKIP_COMPOSE:-0}" = 1 ]; then log "Skipping Docker build/recreate because PI_WEB_DOCKER_SKIP_COMPOSE=1"