packaging: build a self-contained distribution bundle by default

- make_dist_bundle.sh: assemble dist/coderai-docker-dist.tar containing the image
  tarball (docker save | pigz), the coderai-docker runner (run_oci.sh, image tag
  pinned), install.sh and README. Stages under dist/ (not tiny /tmp) and hardlinks
  the multi-GB image tarball instead of copying it.
- dist-bundle/install.sh: docker-load the image (sudo-fallback for daemon access)
  then install the coderai-docker runner to /usr/local/bin (root) or
  ~/.local/usr/bin (user, added to ~/.bashrc PATH if missing).
- build_oci_image.sh: after a successful build, export + bundle for distribution
  by default (--no-dist to skip).
- run_oci.sh: default image tag -> coderai:dist (matches what's shipped/loaded).
Co-Authored-By: 's avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 766fef3c
......@@ -309,6 +309,17 @@ Multi-stage `Dockerfile.oci-venv`:
Full build (slow, ~15 min — rebuilds the bundle):
packaging/linux/build_oci_image.sh # tags coderai:dist
- By DEFAULT it then exports the image and assembles the final distribution
bundle (dist/coderai-docker-dist.tar). Pass --no-dist to just build.
Distribution bundle (packaging/linux/make_dist_bundle.sh → dist/coderai-docker-dist.tar):
Expands to coderai-docker-dist/{install.sh, coderai-docker, coderai-dist.tar.gz,
README.txt}. Staged UNDER dist/ (not /tmp — too small) and the 12 GB image
tarball is HARDLINKED into the stage, not copied. install.sh: `docker load`s the
image (sudo-fallback for daemon access), then installs the coderai-docker runner
to /usr/local/bin (root) or ~/.local/usr/bin (user, added to ~/.bashrc PATH if
missing). The runner is run_oci.sh with its default image tag pinned (sed) to
whatever was shipped, so it matches the loaded tag.
Smoke test (no weights, checks services + every bundled binary):
DOCKER="sudo docker" GPU="--gpus all" PORT=18082 \
packaging/linux/smoke_test_services.sh coderai:dist
......
......@@ -33,6 +33,10 @@ LIPSYNC_VENV="${CODERAI_LIPSYNC_VENV:-$HOME/.coderai/lipsync_venv}"
WAV2LIP_DIR="${CODERAI_WAV2LIP_SRC:-$HOME/.coderai/Wav2Lip}"
SADTALKER_DIR="${CODERAI_SADTALKER_SRC:-$HOME/.coderai/SadTalker}"
DS4_DIR="${CODERAI_DS4_DIR:-$HOME/.coderai/ds4}"
# After a successful build, export the image and assemble the final distribution
# bundle (image tarball + install.sh + coderai-docker runner). Disable with
# --no-dist (just builds the image).
MAKE_DIST=1
usage() {
cat <<'EOF'
......@@ -58,6 +62,8 @@ Options:
--no-parler Do not bundle the Parler-TTS venv overlay.
--no-tools Do not bundle the lip-sync (wav2lip/sadtalker) venvs or ds4.
-t, --tag TAG Image tag to create (default: coderai:local or OCI_IMAGE from versions.env).
--no-dist Just build the image; skip exporting + bundling for distribution.
--dist Force building the distribution bundle (default ON).
-h, --help Show this help.
Examples:
......@@ -141,6 +147,14 @@ while [[ $# -gt 0 ]]; do
IMAGE_TAG="$2"
shift 2
;;
--no-dist)
MAKE_DIST=0
shift
;;
--dist)
MAKE_DIST=1
shift
;;
-h|--help)
usage
exit 0
......@@ -527,3 +541,12 @@ Non-root: add --user "\$(id -u):\$(id -g)" (mounts must be owned by that UID),
or use rootless/userns-remap Docker with no extra flags.
See packaging/linux/README-RUN.txt (also at /opt/coderai/README-RUN.txt in the image).
EOF
# Export the image + assemble the final distribution bundle (image tarball +
# install.sh + coderai-docker runner) unless disabled with --no-dist.
if [[ "$MAKE_DIST" == "1" ]]; then
echo ""
echo "=== Building distribution bundle (use --no-dist to skip) ==="
DOCKER="$DOCKER_BIN" IMAGE="$IMAGE_TAG" REFRESH=1 \
"$ROOT_DIR/packaging/linux/make_dist_bundle.sh"
fi
CoderAI — Docker distribution
==============================
This bundle contains everything needed to run CoderAI from a prebuilt
all-in-one Docker image (server + API + admin UI + video editor + videogen
studio + township fighters, all behind one nginx port).
Contents
--------
install.sh Loads the image into Docker and installs the runner.
coderai-docker The run wrapper (installed to your PATH by install.sh).
coderai-dist.tar.gz The Docker image (gzip-compressed `docker save`).
README.txt This file.
Requirements
------------
- Docker (or Podman: set CONTAINER_ENGINE=podman).
- For GPU: NVIDIA Container Toolkit (--nvidia) or /dev/dri access (--vulkan).
Install
-------
./install.sh
Installs `coderai-docker` to:
- /usr/local/bin when run as root
- ~/.local/usr/bin when run as a normal user (added to PATH via
~/.bashrc if it isn't there already)
Run
---
coderai-docker --nvidia # CUDA; or --vulkan (AMD/Intel) / --cpu
coderai-docker --help # all options
Then open http://127.0.0.1:8776/admin
Useful options (see --help)
---------------------------
-p, --port PORT Host port (default 8776).
--data-dir PATH Where config/models/cache live (default ./coderai-runtime).
--local Run against your existing ~/.coderai config.
--map HOST[:CONT] Bind-mount a host dir at the same path (for absolute
model paths in models.json), e.g. --map /AI/guffcache.
--debug[=engine,ws,...] Run with coderai debug flags + a host-tailable file log.
--log-file PATH In-container log path (default /cache/logs/coderai.log,
visible on the host under the cache mount).
-d, --detach Run in the background.
Manual image load (without install.sh)
---------------------------------------
docker load < coderai-dist.tar.gz # restores the coderai:dist tag
#!/usr/bin/env bash
# CoderAI Docker distribution installer.
#
# Expand the distribution tarball and run this script. It:
# 1. Loads the bundled image into Docker (docker load).
# 2. Installs the `coderai-docker` runner:
# - root -> /usr/local/bin/coderai-docker
# - user -> ~/.local/usr/bin/coderai-docker (and ensures it's on PATH,
# adding it to ~/.bashrc if missing).
#
# Usage: ./install.sh
# Env: CONTAINER_ENGINE=docker|podman (default docker)
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
IMAGE_TAR="${IMAGE_TAR:-$HERE/coderai-dist.tar.gz}"
RUNNER_SRC="${RUNNER_SRC:-$HERE/coderai-docker}"
ENGINE="${CONTAINER_ENGINE:-docker}"
say(){ printf '%s\n' "$*"; }
die(){ printf 'Error: %s\n' "$*" >&2; exit 1; }
command -v "$ENGINE" >/dev/null 2>&1 || die "'$ENGINE' not found in PATH — install Docker (or set CONTAINER_ENGINE=podman) first."
[ -f "$IMAGE_TAR" ] || die "image tarball not found: $IMAGE_TAR"
[ -f "$RUNNER_SRC" ] || die "runner script not found: $RUNNER_SRC"
# Decide whether the engine needs sudo for daemon access.
DK=("$ENGINE")
if ! "$ENGINE" info >/dev/null 2>&1; then
if command -v sudo >/dev/null 2>&1; then
DK=(sudo "$ENGINE")
say "[install] Docker daemon needs elevated access — using sudo (you may be prompted)."
else
die "cannot reach the Docker daemon (no permission and no sudo available)."
fi
fi
say "[install] loading image from $IMAGE_TAR — this is large, please wait…"
"${DK[@]}" load -i "$IMAGE_TAR"
# Pick the install dir by privilege.
if [ "$(id -u)" -eq 0 ]; then
BIN_DIR="/usr/local/bin"
else
BIN_DIR="$HOME/.local/usr/bin"
fi
mkdir -p "$BIN_DIR"
install -m 0755 "$RUNNER_SRC" "$BIN_DIR/coderai-docker"
say "[install] installed runner: $BIN_DIR/coderai-docker"
# Ensure a user install dir is on PATH (root's /usr/local/bin already is).
if [ "$(id -u)" -ne 0 ]; then
case ":${PATH:-}:" in
*":$BIN_DIR:"*)
say "[install] $BIN_DIR is already on PATH." ;;
*)
RC="${HOME}/.bashrc"
if [ -f "$RC" ] && grep -Fqs "$BIN_DIR" "$RC"; then
say "[install] $RC already references $BIN_DIR; open a new shell or 'source $RC'."
else
printf '\n# Added by the CoderAI Docker installer\nexport PATH="%s:$PATH"\n' "$BIN_DIR" >> "$RC"
say "[install] added $BIN_DIR to PATH in $RC."
say "[install] run: source \"$RC\" (or open a new terminal) to pick it up now."
fi
;;
esac
fi
say ""
say "Done. Quick start:"
say " coderai-docker --nvidia # or --vulkan / --cpu"
say " coderai-docker --help # all options (local config, --map, --debug, file log)"
#!/usr/bin/env bash
# Assemble a self-contained distribution bundle the recipient can expand and
# install with no repo checkout:
#
# <NAME>/
# install.sh loads the image + installs the runner (see dist-bundle/)
# coderai-docker the run wrapper (run_oci.sh, image tag pinned)
# coderai-dist.tar.gz the image (gzip-compressed `docker save`)
# README.txt
#
# Output: $OUT_DIR/<NAME>.tar (not re-gzipped — the image is already compressed).
#
# Usage:
# [DOCKER="sudo docker"] [IMAGE=coderai:dist] packaging/linux/make_dist_bundle.sh
# REFRESH=1 ... # force re-export of the image tarball even if present
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
DOCKER_BIN="${DOCKER:-docker}"; read -r -a DK <<< "$DOCKER_BIN"
IMAGE="${IMAGE:-coderai:dist}"
OUT_DIR="${OUT_DIR:-$REPO/dist}"
NAME="${NAME:-coderai-docker-dist}"
IMAGE_TAR="$OUT_DIR/coderai-dist.tar.gz"
command -v "${DK[0]}" >/dev/null 2>&1 || { echo "Error: '${DK[0]}' not found" >&2; exit 1; }
"${DK[@]}" image inspect "$IMAGE" >/dev/null 2>&1 || {
echo "Error: image '$IMAGE' not found — build it first (build_oci_image.sh)." >&2; exit 1; }
mkdir -p "$OUT_DIR"
# 1. Export the image tarball (skip if present unless REFRESH=1).
if [[ ! -f "$IMAGE_TAR" || "${REFRESH:-0}" == "1" ]]; then
if command -v pigz >/dev/null 2>&1; then COMP=pigz; else COMP=gzip; fi
echo "[bundle] exporting $IMAGE -> $IMAGE_TAR (via $COMP)…"
"${DK[@]}" save "$IMAGE" | "$COMP" > "$IMAGE_TAR"
else
echo "[bundle] reusing existing $IMAGE_TAR (set REFRESH=1 to re-export)"
fi
# 2. Stage the bundle tree. Stage UNDER $OUT_DIR (not /tmp, which is often a
# small tmpfs) and hardlink the multi-GB image tarball when possible so we don't
# duplicate it on disk.
TMP="$(mktemp -d "$OUT_DIR/.bundle.XXXXXX")"
STAGE="$TMP/$NAME"
mkdir -p "$STAGE"
ln "$IMAGE_TAR" "$STAGE/coderai-dist.tar.gz" 2>/dev/null \
|| cp "$IMAGE_TAR" "$STAGE/coderai-dist.tar.gz"
install -m 0755 "$HERE/run_oci.sh" "$STAGE/coderai-docker"
install -m 0755 "$HERE/dist-bundle/install.sh" "$STAGE/install.sh"
cp "$HERE/dist-bundle/README.txt" "$STAGE/README.txt"
# Pin the runner's default image tag to exactly what we shipped, so it matches
# the tag restored by `docker load` regardless of the build's tag.
sed -i "s|\${OCI_IMAGE:-coderai:dist}|\${OCI_IMAGE:-$IMAGE}|" "$STAGE/coderai-docker"
# 3. Tar it (store, no extra gzip — coderai-dist.tar.gz is already compressed).
BUNDLE="$OUT_DIR/$NAME.tar"
tar -C "$TMP" -cf "$BUNDLE" "$NAME"
rm -rf "$TMP"
echo "[bundle] wrote $BUNDLE ($(du -h "$BUNDLE" | cut -f1))"
echo "[bundle] recipient: tar -xf $(basename "$BUNDLE") && cd $NAME && ./install.sh"
......@@ -9,7 +9,7 @@ if [[ -f "$VERSIONS_FILE" ]]; then
fi
ENGINE="${CONTAINER_ENGINE:-docker}"
IMAGE_TAG="${OCI_IMAGE:-coderai:local}"
IMAGE_TAG="${OCI_IMAGE:-coderai:dist}"
MODE="cpu"
PORT="${CODERAI_PORT:-8776}"
DATA_ROOT="$PWD/coderai-runtime"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment