Fix
ci / checks (push) Has been cancelled

This commit is contained in:
khodowany
2026-09-19 08:17:25 -05:00
parent e1410b0fdc
commit cee9f45b0c
28 changed files with 860 additions and 1234 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"heartbeat_path": "build/build_x86_debug/runtime/heartbeat.json"
},
"database": {
"path": "build/build_x86_debug/runtime/vamp.db"
"path": "build/build_x86_debug/generated-assets/catalogue.db"
},
"control": {
"endpoint": {
+1 -1
View File
@@ -18,7 +18,7 @@
"heartbeat_path": "/run/product-vamp/heartbeat.json"
},
"database": {
"path": "/var/lib/product-vamp/vamp.db"
"path": "/usr/share/product-vamp/catalogue.db"
},
"control": {
"endpoint": {
+2
View File
@@ -1,5 +1,7 @@
FROM debian:trixie-slim
LABEL org.khodo.product=product-vamp-zero
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
+141 -10
View File
@@ -8,6 +8,49 @@ config_dir=/etc/product-vamp
state_dir=/var/lib/product-vamp
run_dir=/run/product-vamp
share_dir=/usr/share/product-vamp
image_tag=
connection=product-vamp-control
replacement_started=false
image_loaded=false
previous_image=
stage_dir=
cleanup() {
status=$?
trap - EXIT
if [ "$status" -ne 0 ] && [ "$replacement_started" = true ]; then
systemctl stop product-vamp-zero.service product-vamp-ap.service >/dev/null 2>&1 || true
docker rm -f product-vamp-zero >/dev/null 2>&1 || true
nmcli connection down "$connection" >/dev/null 2>&1 || true
echo "Installation failed; Product Vamp services are stopped. Rerun the installer." >&2
elif [ "$status" -ne 0 ] && [ "$image_loaded" = true ] && [ -n "$previous_image" ]; then
docker tag "$previous_image" "$image_tag" || echo "Failed to restore the previous image tag." >&2
fi
if [ -n "$stage_dir" ]; then
rm -rf -- "$stage_dir"
fi
exit "$status"
}
trap cleanup EXIT
validate_target() {
target=$1
if [ "$(realpath -m -- "$target")" != "$target" ] || [ -L "$target" ]; then
echo "Refusing unsafe installation target: $target" >&2
exit 1
fi
if [ -e "$target" ] && [ -d "$target" ]; then
if [ -n "$(find "$target" -type l -print -quit)" ]; then
echo "Refusing installation target containing symlinks: $target" >&2
exit 1
fi
mounts=$(find "$target" -xdev -type d -exec mountpoint -q {} ';' -print)
if [ -n "$mounts" ]; then
echo "Refusing mounted installation target: $target" >&2
exit 1
fi
fi
}
required_packages=(
docker.io
docker-cli
@@ -51,25 +94,113 @@ else
fi
systemctl enable --now docker NetworkManager
install -d -m 0755 "$config_dir" "$state_dir" "$run_dir" "$share_dir"
cp -R "$zero_dir/share/product-vamp/." "$share_dir/"
image_details=$(python3 - "$bundle_dir/manifest.json" "$image_tar" <<'PY'
import hashlib
import json
import re
import sys
import tarfile
with open(sys.argv[1], encoding='utf-8') as source:
tag = json.load(source)['zero']['image_tag']
if not isinstance(tag, str) or not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._:/-]*', tag):
raise ValueError('invalid bundle image tag')
with tarfile.open(sys.argv[2]) as archive:
with archive.extractfile('manifest.json') as source:
images = [image for image in json.load(source) if tag in (image.get('RepoTags') or [])]
if len(images) != 1:
raise ValueError('Docker archive does not contain exactly one matching bundle image')
with archive.extractfile(images[0]['Config']) as source:
digest = hashlib.file_digest(source, 'sha256').hexdigest()
print(tag)
print('sha256:' + digest)
PY
)
mapfile -t image_fields <<< "$image_details"
image_tag=${image_fields[0]}
expected_image=${image_fields[1]}
if [ ! -f "$config_dir/vamp.json" ]; then
cp "$zero_dir/config/vamp.example.json" "$config_dir/vamp.json"
fi
for target in "$config_dir" "$state_dir" "$run_dir" "$share_dir" \
/usr/local/bin/product-vamp-start-ap /usr/local/bin/product-vamp-start-zero \
/etc/systemd/system/product-vamp-ap.service.d /etc/systemd/system/product-vamp-zero.service.d \
/run/systemd/system/product-vamp-ap.service.d /run/systemd/system/product-vamp-zero.service.d \
/etc/systemd/system/product-vamp-ap.service /etc/systemd/system/product-vamp-zero.service; do
validate_target "$target"
done
stage_dir=$(mktemp -d /tmp/product-vamp-install-XXXXXX)
install -m 0644 "$zero_dir/config/vamp.example.json" "$stage_dir/vamp.json"
install -m 0755 "$zero_dir/start-ap.sh" "$stage_dir/start-ap.sh"
install -m 0755 "$zero_dir/start-zero.sh" "$stage_dir/start-zero.sh"
install -m 0644 "$zero_dir/systemd/product-vamp-ap.service" "$stage_dir/product-vamp-ap.service"
install -m 0644 "$zero_dir/systemd/product-vamp-zero.service" "$stage_dir/product-vamp-zero.service"
bash -n "$stage_dir/start-ap.sh"
bash -n "$stage_dir/start-zero.sh"
previous_image=$(docker image inspect --format '{{.Id}}' "$image_tag" 2>/dev/null || true)
previous_images=$(docker image ls --quiet --no-trunc --filter label=org.khodo.product=product-vamp-zero)
image_loaded=true
docker load -i "$image_tar"
new_image=$(docker image inspect --format '{{.Id}}' "$image_tag")
if [ "$new_image" != "$expected_image" ]; then
echo "Loaded Docker image does not match the incoming archive." >&2
exit 1
fi
# Probe the actual image and staged configuration without touching host state or hardware.
docker run --rm --network none --mount "type=bind,src=$stage_dir,dst=/install,readonly" \
"$new_image" /usr/bin/vamp-zero --config /install/vamp.json --once
install -m 0755 "$zero_dir/start-ap.sh" /usr/local/bin/product-vamp-start-ap
install -m 0755 "$zero_dir/start-zero.sh" /usr/local/bin/product-vamp-start-zero
install -m 0644 "$zero_dir/systemd/product-vamp-ap.service" /etc/systemd/system/product-vamp-ap.service
install -m 0644 "$zero_dir/systemd/product-vamp-zero.service" /etc/systemd/system/product-vamp-zero.service
echo "Resetting all Product Vamp data and settings, including control Wi-Fi credentials."
echo "A connection through the control Wi-Fi may disconnect."
replacement_started=true
for service in product-vamp-zero.service product-vamp-ap.service; do
if systemctl cat "$service" >/dev/null 2>&1; then
systemctl stop "$service"
fi
done
if docker container inspect product-vamp-zero >/dev/null 2>&1; then
docker rm -f product-vamp-zero
fi
for target in "$config_dir" "$state_dir" "$run_dir" "$share_dir" \
/etc/systemd/system/product-vamp-ap.service.d /etc/systemd/system/product-vamp-zero.service.d \
/run/systemd/system/product-vamp-ap.service.d /run/systemd/system/product-vamp-zero.service.d; do
validate_target "$target"
rm -rf --one-file-system -- "$target"
done
install -d -m 0755 "$config_dir" "$run_dir"
install -m 0644 "$stage_dir/vamp.json" "$config_dir/vamp.json"
printf '%s\n' "$new_image" > "$config_dir/image-id"
install -m 0755 "$stage_dir/start-ap.sh" /usr/local/bin/product-vamp-start-ap
install -m 0755 "$stage_dir/start-zero.sh" /usr/local/bin/product-vamp-start-zero
install -m 0644 "$stage_dir/product-vamp-ap.service" /etc/systemd/system/product-vamp-ap.service
install -m 0644 "$stage_dir/product-vamp-zero.service" /etc/systemd/system/product-vamp-zero.service
if nmcli -t -f NAME connection show | grep -Fxq "$connection"; then
nmcli connection delete "$connection"
fi
systemctl daemon-reload
systemctl disable --now getty@tty1.service >/dev/null 2>&1 || true
systemctl enable --now getty@tty2.service >/dev/null 2>&1 || true
systemctl enable --now product-vamp-ap.service
systemctl enable product-vamp-zero.service
systemctl restart product-vamp-zero.service
healthy=false
for attempt in $(seq 1 60); do
if docker exec product-vamp-zero /usr/bin/vamp-zero --config /etc/product-vamp/vamp.json --health-check; then
healthy=true
break
fi
sleep 1
done
if [ "$healthy" != true ]; then
echo "Product Vamp did not become healthy after installation." >&2
exit 1
fi
for image in $(printf '%s\n' "$previous_image" "$previous_images" | sort -u); do
if [ "$image" != "$new_image" ]; then
if [ -z "$(docker ps -aq --filter "ancestor=$image")" ]; then
docker image rm "$image" || echo "Warning: could not remove old Product Vamp image $image" >&2
else
echo "Keeping old Product Vamp image $image: another container still uses it." >&2
fi
fi
done
echo "Product Vamp Zero installed. Control AP defaults to SSID 'Vamp Control' at http://192.168.42.1:8080."
+4 -3
View File
@@ -2,9 +2,11 @@
set -euo pipefail
container_name=${PRODUCT_VAMP_ZERO_CONTAINER:-product-vamp-zero}
image_tag=${PRODUCT_VAMP_ZERO_IMAGE:-product-vamp-zero:dev}
config_dir=${PRODUCT_VAMP_CONFIG_DIR:-/etc/product-vamp}
state_dir=${PRODUCT_VAMP_STATE_DIR:-/var/lib/product-vamp}
image_tag=${PRODUCT_VAMP_ZERO_IMAGE:-}
if [ -z "$image_tag" ]; then
read -r image_tag < "$config_dir/image-id"
fi
run_dir=${PRODUCT_VAMP_RUN_DIR:-/run/product-vamp}
display_tty=${PRODUCT_VAMP_DISPLAY_TTY:-/dev/tty1}
@@ -36,7 +38,6 @@ echo "Starting Product Vamp Zero container '$container_name' from '$image_tag' o
exec docker run --rm --name "$container_name" --network host --privileged \
--env PRODUCT_VAMP_DISPLAY_TTY="$display_tty" \
-v "$config_dir:/etc/product-vamp:ro" \
-v "$state_dir:/var/lib/product-vamp" \
-v "$run_dir:/run/product-vamp" \
"$image_tag" \
sh -lc 'display_tty=${PRODUCT_VAMP_DISPLAY_TTY:-/dev/tty1}; exec setsid --wait --ctty /usr/bin/vamp-zero --config /etc/product-vamp/vamp.json < "$display_tty"'