Files
pulse/scripts/capture-release-note-visuals.sh
T
rcourtman 150042fe98 Improve release highlights and reusable screenshot evidence
Put published highlights and optional screenshot comparisons in the
post-update dialog, with grouped details available on demand.

Reuse inspected images from merged pull requests or capture fresh release
views, then publish versioned PNG sidecars. Serve screenshots through the
authenticated release endpoint to preserve the existing image policy.
2026-10-15 11:42:25 +01:00

134 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
PLAN_FILE=${1:-}
COMPARISON_TAG=${2:-}
OUTPUT_DIR=${3:-}
if [ -z "$PLAN_FILE" ] || [ -z "$COMPARISON_TAG" ] || [ -z "$OUTPUT_DIR" ]; then
echo "Usage: $0 <visual-plan.json> <comparison-tag> <output-directory>" >&2
exit 2
fi
ROOT_DIR=$(git rev-parse --show-toplevel)
PLAN_FILE=$(cd "$(dirname "$PLAN_FILE")" && pwd)/$(basename "$PLAN_FILE")
mkdir -p "$OUTPUT_DIR"
OUTPUT_DIR=$(cd "$OUTPUT_DIR" && pwd)
python3 "$ROOT_DIR/scripts/release_control/release_note_visuals.py" \
validate --plan "$PLAN_FILE" >/dev/null
CAPTURE_COUNT=$(python3 "$ROOT_DIR/scripts/release_control/release_note_visuals.py" \
count --plan "$PLAN_FILE")
if [ "$CAPTURE_COUNT" = "0" ]; then
exit 0
fi
BEFORE_CAPTURE_COUNT=$(python3 "$ROOT_DIR/scripts/release_control/release_note_visuals.py" \
runtime-count --plan "$PLAN_FILE" --side before)
AFTER_CAPTURE_COUNT=$(python3 "$ROOT_DIR/scripts/release_control/release_note_visuals.py" \
runtime-count --plan "$PLAN_FILE" --side after)
if ! git rev-parse -q --verify "${COMPARISON_TAG}^{commit}" >/dev/null; then
echo "Comparison tag ${COMPARISON_TAG} does not exist" >&2
exit 1
fi
TEMP_ROOT=$(mktemp -d)
PREVIOUS_TREE="$TEMP_ROOT/previous"
RUN_KEY="${GITHUB_RUN_ID:-$$}-${GITHUB_RUN_ATTEMPT:-1}"
BEFORE_IMAGE="pulse-release-visual-before-${RUN_KEY}"
AFTER_IMAGE="pulse-release-visual-after-${RUN_KEY}"
BEFORE_CONTAINER="pulse-release-visual-before-${RUN_KEY}"
AFTER_CONTAINER="pulse-release-visual-after-${RUN_KEY}"
BEFORE_PORT=${PULSE_RELEASE_VISUAL_BEFORE_PORT:-17655}
AFTER_PORT=${PULSE_RELEASE_VISUAL_AFTER_PORT:-17656}
cleanup() {
docker rm -f "$BEFORE_CONTAINER" "$AFTER_CONTAINER" >/dev/null 2>&1 || true
docker image rm -f "$BEFORE_IMAGE" "$AFTER_IMAGE" >/dev/null 2>&1 || true
git -C "$ROOT_DIR" worktree remove --force "$PREVIOUS_TREE" >/dev/null 2>&1 || true
rm -rf "$TEMP_ROOT"
}
trap cleanup EXIT
build_visual_image() {
local source_dir=$1
local image_name=$2
docker build \
--target e2e_runtime \
--build-arg BUILD_AGENT=0 \
--build-arg GO_BUILD_TAGS= \
--tag "$image_name" \
"$source_dir"
}
if [ "$BEFORE_CAPTURE_COUNT" -gt 0 ]; then
git -C "$ROOT_DIR" worktree add --detach "$PREVIOUS_TREE" "$COMPARISON_TAG"
build_visual_image "$PREVIOUS_TREE" "$BEFORE_IMAGE"
fi
if [ "$AFTER_CAPTURE_COUNT" -gt 0 ]; then
build_visual_image "$ROOT_DIR" "$AFTER_IMAGE"
fi
start_visual_container() {
local image_name=$1
local container_name=$2
local port=$3
docker run --detach --name "$container_name" \
--publish "127.0.0.1:${port}:7655" \
--env PULSE_MOCK_MODE=true \
--env PULSE_MOCK_RANDOM_METRICS=false \
--env PULSE_MOCK_TRENDS_SEED_DURATION=24h \
--env PULSE_AUTH_USER=admin \
--env PULSE_AUTH_PASS=adminadminadmin \
--env PULSE_DEV=true \
"$image_name" >/dev/null
}
wait_for_runtime() {
local port=$1
local container_name=$2
local attempts=60
while [ "$attempts" -gt 0 ]; do
if curl -fsS "http://127.0.0.1:${port}/api/health" >/dev/null 2>&1; then
return 0
fi
if [ "$(docker inspect --format '{{.State.Running}}' "$container_name" 2>/dev/null || true)" != "true" ]; then
docker logs "$container_name" >&2 || true
return 1
fi
sleep 2
attempts=$((attempts - 1))
done
docker logs "$container_name" >&2 || true
echo "Timed out waiting for ${container_name}" >&2
return 1
}
if [ "$BEFORE_CAPTURE_COUNT" -gt 0 ]; then
start_visual_container "$BEFORE_IMAGE" "$BEFORE_CONTAINER" "$BEFORE_PORT"
fi
if [ "$AFTER_CAPTURE_COUNT" -gt 0 ]; then
start_visual_container "$AFTER_IMAGE" "$AFTER_CONTAINER" "$AFTER_PORT"
fi
if [ "$BEFORE_CAPTURE_COUNT" -gt 0 ]; then
wait_for_runtime "$BEFORE_PORT" "$BEFORE_CONTAINER"
fi
if [ "$AFTER_CAPTURE_COUNT" -gt 0 ]; then
wait_for_runtime "$AFTER_PORT" "$AFTER_CONTAINER"
fi
node "$ROOT_DIR/scripts/release_control/capture_release_note_visuals.mjs" \
"$PLAN_FILE" \
"http://127.0.0.1:${BEFORE_PORT}" \
"http://127.0.0.1:${AFTER_PORT}" \
"$OUTPUT_DIR" \
"$COMPARISON_TAG"
while IFS= read -r asset_name; do
[ -f "$OUTPUT_DIR/$asset_name" ] || {
echo "Expected visual asset was not produced: $asset_name" >&2
exit 1
}
done < <(python3 "$ROOT_DIR/scripts/release_control/release_note_visuals.py" \
assets --plan "$PLAN_FILE")