loop-onprem-charts/airgap/build-bundle.sh
2026-03-02 14:57:56 +03:00

212 lines
6.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Defaults
WITH_LIVEKIT=false
VERSION=""
usage() {
cat <<EOF
Usage: $0 [OPTIONS]
Build air-gapped bundle for Loop on-premises deployment.
Run this on a machine WITH internet access.
Options:
--with-livekit Include LiveKit server and Redis images/charts
--version VER Override app version (default: read from Chart.yaml)
-h, --help Show this help
EOF
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
--with-livekit) WITH_LIVEKIT=true; shift ;;
--version) [[ -z "${2:-}" ]] && { echo "ERROR: --version requires an argument"; exit 1; }; VERSION="$2"; shift 2 ;;
-h|--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
# Read version from Chart.yaml if not overridden
if [[ -z "$VERSION" ]]; then
VERSION=$(grep '^appVersion:' "$REPO_ROOT/loop-enterprise-stack/Chart.yaml" | awk '{print $2}')
fi
if [[ -z "$VERSION" ]]; then
echo "ERROR: Could not determine version. Set --version or check Chart.yaml"
exit 1
fi
BUNDLE_NAME="loop-airgap-bundle-${VERSION}"
BUNDLE_DIR="$SCRIPT_DIR/$BUNDLE_NAME"
echo "==> Building air-gapped bundle v${VERSION}"
echo " LiveKit: ${WITH_LIVEKIT}"
# ---- Image lists ----
# Core Helm images (used by loop-enterprise-stack)
HELM_IMAGES=(
"registry.loop.ru/loop/server:${VERSION}"
"appropriate/curl:latest"
)
# Core Docker Compose images
COMPOSE_IMAGES=(
"registry.loop.ru/loop/server:${VERSION}"
"nginx:latest"
"postgres:15-alpine3.21"
"minio/minio:RELEASE.2022-08-26T19-53-15Z"
"minio/mc:RELEASE.2022-08-28T20-08-11Z"
)
# LiveKit images (optional)
LIVEKIT_IMAGES=()
echo "==> Image lists prepared"
# ---- Prerequisites check ----
for cmd in docker helm tar gzip; do
if ! command -v "$cmd" &>/dev/null; then
echo "ERROR: '$cmd' is required but not found in PATH"
exit 1
fi
done
# ---- Vendor Helm dependencies ----
echo "==> Vendoring Helm dependencies for loop-enterprise-stack..."
helm dependency update "$REPO_ROOT/loop-enterprise-stack/"
echo "==> Extracting Bitnami image references via helm template..."
BITNAMI_IMAGES=$(helm template test "$REPO_ROOT/loop-enterprise-stack/" 2>/dev/null \
| grep 'image:' \
| sed -E 's/.*image:[[:space:]]*"?([^"[:space:]]+)"?.*/\1/' \
| sort -u \
| grep -v 'registry.loop.ru' \
| grep -v 'appropriate/curl')
while IFS= read -r img; do
[[ -n "$img" ]] && HELM_IMAGES+=("$img")
done <<< "$BITNAMI_IMAGES"
if [[ "$WITH_LIVEKIT" == "true" ]]; then
echo "==> Vendoring Helm dependencies for loop-livekit-server..."
helm dependency update "$REPO_ROOT/loop-livekit-server/"
echo "==> Extracting LiveKit image references via helm template..."
LK_IMAGES=$(helm template test "$REPO_ROOT/loop-livekit-server/" 2>/dev/null \
| grep 'image:' \
| sed -E 's/.*image:[[:space:]]*"?([^"[:space:]]+)"?.*/\1/' \
| sort -u)
while IFS= read -r img; do
[[ -n "$img" ]] && LIVEKIT_IMAGES+=("$img")
done <<< "$LK_IMAGES"
fi
echo "==> Helm images to bundle:"
printf ' %s\n' "${HELM_IMAGES[@]}"
if [[ "$WITH_LIVEKIT" == "true" ]]; then
echo "==> LiveKit images to bundle:"
printf ' %s\n' "${LIVEKIT_IMAGES[@]}"
fi
echo "==> Compose images to bundle:"
printf ' %s\n' "${COMPOSE_IMAGES[@]}"
# ---- Prepare bundle directory ----
rm -rf "$BUNDLE_DIR"
mkdir -p "$BUNDLE_DIR/images"
mkdir -p "$BUNDLE_DIR/helm"
mkdir -p "$BUNDLE_DIR/docker-compose"
if [[ "$WITH_LIVEKIT" == "true" ]]; then
mkdir -p "$BUNDLE_DIR/images/livekit"
fi
# ---- Pull and save Helm images ----
echo "==> Pulling and saving Helm images..."
for img in "${HELM_IMAGES[@]}"; do
echo " Pulling: $img"
docker pull "$img"
safe_name=$(echo "$img" | tr '/:' '_')
echo " Saving: $img -> images/${safe_name}.tar"
docker save "$img" -o "$BUNDLE_DIR/images/${safe_name}.tar"
done
# ---- Pull and save Compose-only images (skip duplicates) ----
echo "==> Pulling and saving Compose-only images..."
for img in "${COMPOSE_IMAGES[@]}"; do
safe_name=$(echo "$img" | tr '/:' '_')
if [[ -f "$BUNDLE_DIR/images/${safe_name}.tar" ]]; then
echo " Skipping (already saved): $img"
continue
fi
echo " Pulling: $img"
docker pull "$img"
echo " Saving: $img -> images/${safe_name}.tar"
docker save "$img" -o "$BUNDLE_DIR/images/${safe_name}.tar"
done
# ---- Pull and save LiveKit images ----
if [[ "$WITH_LIVEKIT" == "true" ]] && [[ ${#LIVEKIT_IMAGES[@]} -gt 0 ]]; then
echo "==> Pulling and saving LiveKit images..."
for img in "${LIVEKIT_IMAGES[@]}"; do
echo " Pulling: $img"
docker pull "$img"
safe_name=$(echo "$img" | tr '/:' '_')
echo " Saving: $img -> images/livekit/${safe_name}.tar"
docker save "$img" -o "$BUNDLE_DIR/images/livekit/${safe_name}.tar"
done
fi
# ---- Copy Helm charts (with vendored dependencies) ----
echo "==> Copying Helm charts..."
cp -r "$REPO_ROOT/loop-enterprise-stack" "$BUNDLE_DIR/helm/loop-enterprise-stack"
rm -rf "$BUNDLE_DIR/helm/loop-enterprise-stack/.git"
if [[ "$WITH_LIVEKIT" == "true" ]]; then
cp -r "$REPO_ROOT/loop-livekit-server" "$BUNDLE_DIR/helm/loop-livekit-server"
rm -rf "$BUNDLE_DIR/helm/loop-livekit-server/.git"
fi
# ---- Copy docker-compose files ----
echo "==> Copying docker-compose files..."
cp -r "$REPO_ROOT/docker-compose/"* "$BUNDLE_DIR/docker-compose/"
# ---- Copy air-gapped values overrides ----
echo "==> Copying air-gapped values overrides..."
if [[ -f "$SCRIPT_DIR/values-airgap-stack.yaml" ]]; then
cp "$SCRIPT_DIR/values-airgap-stack.yaml" "$BUNDLE_DIR/helm/loop-enterprise-stack/values-airgap.yaml"
fi
if [[ -f "$SCRIPT_DIR/docker-compose-airgap.override.yml" ]]; then
cp "$SCRIPT_DIR/docker-compose-airgap.override.yml" "$BUNDLE_DIR/docker-compose/docker-compose-airgap.override.yml"
fi
if [[ "$WITH_LIVEKIT" == "true" ]] && [[ -f "$SCRIPT_DIR/values-airgap-livekit.yaml" ]]; then
cp "$SCRIPT_DIR/values-airgap-livekit.yaml" "$BUNDLE_DIR/helm/loop-livekit-server/values-airgap.yaml"
fi
# ---- Copy install script ----
echo "==> Copying install script..."
if [[ -f "$SCRIPT_DIR/install.sh" ]]; then
cp "$SCRIPT_DIR/install.sh" "$BUNDLE_DIR/install.sh"
chmod +x "$BUNDLE_DIR/install.sh"
fi
# ---- Copy README ----
if [[ -f "$SCRIPT_DIR/README-airgap.md" ]]; then
cp "$SCRIPT_DIR/README-airgap.md" "$BUNDLE_DIR/README.md"
fi
# ---- Package bundle ----
echo "==> Packaging bundle..."
tar -czf "$SCRIPT_DIR/${BUNDLE_NAME}.tar.gz" -C "$SCRIPT_DIR" "$BUNDLE_NAME"
rm -rf "$BUNDLE_DIR"
BUNDLE_SIZE=$(du -sh "$SCRIPT_DIR/${BUNDLE_NAME}.tar.gz" | awk '{print $1}')
echo ""
echo "==> Bundle created: ${BUNDLE_NAME}.tar.gz (${BUNDLE_SIZE})"
echo " Transfer this file to the air-gapped environment and run install.sh"