feat: add reproducible imgproxy runtime release

This commit is contained in:
john
2026-07-15 11:25:32 +08:00
parent 87b03f7848
commit 3eee44ef5b
6 changed files with 171 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
# This digest selects the linux/arm64 manifest of darthsim/imgproxy:latest as
# verified on 2026-07-15. Do not replace it with a mutable tag in production.
FROM darthsim/imgproxy@sha256:3819ccd5ee26b83b27f8184b17a0a0c6f0eb21052efa9f554b23a7c39a3e6a1f
EXPOSE 8080
+1
View File
@@ -36,6 +36,7 @@
"enrich:plaza": "node scripts/enrich-plaza-posts.mjs",
"build": "vite build",
"build:portal-runtime": "node scripts/build-portal-runtime.mjs",
"build:imgproxy-runtime": "bash scripts/build-imgproxy-runtime-image.sh",
"build:mindspace-service-runtime": "node scripts/build-mindspace-service-runtime.mjs",
"check:mindspace-public-links": "node scripts/check-mindspace-public-links.mjs --downloads-only",
"check:mindspace-public-links:all": "node scripts/check-mindspace-public-links.mjs --all-links",
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
IMAGE_REF="${IMGPROXY_IMAGE_REF:-darthsim/imgproxy@sha256:3819ccd5ee26b83b27f8184b17a0a0c6f0eb21052efa9f554b23a7c39a3e6a1f}"
IMAGE_TAG="${IMGPROXY_IMAGE_TAG:-tkmind/imgproxy-runtime:20260715-3819ccd5}"
OUT_DIR="${IMGPROXY_RUNTIME_OUT_DIR:-${ROOT}/.runtime/imgproxy}"
DRY_RUN=0
[[ "${1:-}" != "--dry-run" ]] || DRY_RUN=1
mkdir -p "$OUT_DIR"
if [[ "$DRY_RUN" == 1 ]]; then
printf 'image_ref=%s\nimage_tag=%s\nout_dir=%s\n' "$IMAGE_REF" "$IMAGE_TAG" "$OUT_DIR"
exit 0
fi
command -v docker >/dev/null || { echo 'docker is required to build the imgproxy runtime image' >&2; exit 1; }
docker pull --platform linux/arm64 "$IMAGE_REF"
docker tag "$IMAGE_REF" "$IMAGE_TAG"
docker save "$IMAGE_TAG" | gzip -c > "$OUT_DIR/imgproxy-runtime-image.tar.gz"
shasum -a 256 "$OUT_DIR/imgproxy-runtime-image.tar.gz" > "$OUT_DIR/imgproxy-runtime-image.tar.gz.sha256"
printf '%s\n' "$IMAGE_TAG" > "$OUT_DIR/image-tag.txt"
+28
View File
@@ -0,0 +1,28 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import test from 'node:test';
const root = path.resolve(import.meta.dirname, '..');
const read = (name) => fs.readFileSync(path.join(root, name), 'utf8');
test('imgproxy runtime pins an arm64 image digest', () => {
const dockerfile = read('docker/imgproxy-runtime/Dockerfile');
assert.match(dockerfile, /FROM darthsim\/imgproxy@sha256:[a-f0-9]{64}/);
});
test('installer uses a read-only storage mount and both required ports', () => {
const installer = read('scripts/install-imgproxy-runtime-prod.sh');
assert.match(installer, /-p 127\.0\.0\.1:20082:8080/);
assert.match(installer, /\$MINDSPACE_STORAGE_ROOT:\/mnt\/images:ro/);
assert.match(installer, /10\.10\.0\.2/);
assert.match(installer, /IMGPROXY_UPSTREAM/);
});
test('release verifies checksums before loading the image and backs up launch state', () => {
const release = read('scripts/release-imgproxy-runtime-prod.sh');
assert.match(release, /shasum -a 256 -c/);
assert.match(release, /docker load/);
assert.match(release, /imgproxy-compat-\$RELEASE_ID\.plist/);
assert.match(release, /img\.tkmind\.cn\/health/);
});
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Runs only on 103 from a verified release directory. It never downloads images.
set -euo pipefail
ROOT="${ROOT:-/Users/john/Project/Memind}"
RUNTIME_DIR="${IMGPROXY_RUNTIME_DIR:?IMGPROXY_RUNTIME_DIR is required}"
IMAGE_TAG="${IMGPROXY_IMAGE_TAG:?IMGPROXY_IMAGE_TAG is required}"
CONTAINER="${IMGPROXY_CONTAINER:-memind-imgproxy}"
NODE_BIN="${NODE_BIN:-/opt/homebrew/opt/node@24/bin/node}"
PLIST="$HOME/Library/LaunchAgents/cn.tkmind.imgproxy-compat.plist"
GUI="gui/$(id -u)"
set -a
source "$ROOT/.env"
set +a
: "${IMGPROXY_SIGNING_KEY:?missing IMGPROXY_SIGNING_KEY}"
: "${IMGPROXY_SIGNING_SALT:?missing IMGPROXY_SIGNING_SALT}"
: "${MINDSPACE_STORAGE_ROOT:?missing MINDSPACE_STORAGE_ROOT}"
docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
docker run -d --name "$CONTAINER" --restart unless-stopped \
-p 127.0.0.1:20082:8080 \
-v "$MINDSPACE_STORAGE_ROOT:/mnt/images:ro" \
-e IMGPROXY_LOCAL_FILESYSTEM_ROOT=/mnt/images \
-e IMGPROXY_KEY="$IMGPROXY_SIGNING_KEY" \
-e IMGPROXY_SALT="$IMGPROXY_SIGNING_SALT" \
-e IMGPROXY_USE_ETAG=true \
-e IMGPROXY_ENABLE_WEBP_DETECTION=true \
"$IMAGE_TAG" >/dev/null
for _ in $(seq 1 20); do
curl -fsS --max-time 2 http://127.0.0.1:20082/health >/dev/null && break
sleep 1
done
curl -fsS --max-time 3 http://127.0.0.1:20082/health >/dev/null
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>cn.tkmind.imgproxy-compat</string>
<key>ProgramArguments</key><array><string>$NODE_BIN</string><string>$RUNTIME_DIR/imgproxy-compat-proxy.mjs</string></array>
<key>WorkingDirectory</key><string>$RUNTIME_DIR</string>
<key>EnvironmentVariables</key><dict><key>IMGPROXY_COMPAT_HOST</key><string>10.10.0.2</string><key>IMGPROXY_COMPAT_PORT</key><string>20081</string><key>IMGPROXY_UPSTREAM</key><string>http://127.0.0.1:20082</string></dict>
<key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>ThrottleInterval</key><integer>10</integer>
<key>StandardOutPath</key><string>$HOME/Library/Logs/imgproxy-compat.log</string><key>StandardErrorPath</key><string>$HOME/Library/Logs/imgproxy-compat.log</string>
</dict></plist>
EOF
plutil -lint "$PLIST" >/dev/null
launchctl bootout "$GUI/cn.tkmind.imgproxy" 2>/dev/null || true
launchctl disable "$GUI/cn.tkmind.imgproxy" 2>/dev/null || true
launchctl bootout "$GUI/cn.tkmind.imgproxy-compat" 2>/dev/null || true
launchctl bootstrap "$GUI" "$PLIST"
launchctl enable "$GUI/cn.tkmind.imgproxy-compat"
launchctl kickstart -k "$GUI/cn.tkmind.imgproxy-compat"
curl -fsS --max-time 3 http://10.10.0.2:20081/health >/dev/null
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# Release the independently versioned imgproxy runtime. Never run this from a
# feature branch; it is intentionally separate from the Portal runtime bundle.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HOST="${STUDIO_HOST:-john@58.38.22.103}"
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
RUNTIME_BASE="$REMOTE_ROOT/imgproxy-runtime"
INCOMING="$REMOTE_ROOT/incoming/imgproxy-runtime"
RELEASE_ID="$(date +%Y%m%d-%H%M%S)-$(git -C "$ROOT" rev-parse --short HEAD)"
TMP="$(mktemp -d "${TMPDIR:-/tmp}/imgproxy-runtime-release.XXXXXX")"
cleanup() { rm -rf "$TMP"; }
trap cleanup EXIT
[[ "$(git -C "$ROOT" branch --show-current)" == "main" ]] || { echo 'imgproxy production release must run from main' >&2; exit 1; }
[[ -z "$(git -C "$ROOT" status --porcelain)" ]] || { echo 'worktree must be clean' >&2; exit 1; }
git -C "$ROOT" fetch origin main --quiet
[[ "$(git -C "$ROOT" rev-parse HEAD)" == "$(git -C "$ROOT" rev-parse origin/main)" ]] || { echo 'main must match origin/main' >&2; exit 1; }
bash "$ROOT/scripts/check-release-ready.sh"
bash "$ROOT/scripts/build-imgproxy-runtime-image.sh"
IMAGE_DIR="$ROOT/.runtime/imgproxy"
for file in imgproxy-runtime-image.tar.gz imgproxy-runtime-image.tar.gz.sha256 image-tag.txt; do
[[ -f "$IMAGE_DIR/$file" ]] || { echo "missing image artifact: $file" >&2; exit 1; }
done
mkdir -p "$TMP/runtime"
cp "$IMAGE_DIR"/* "$TMP/runtime/"
cp "$ROOT/scripts/install-imgproxy-runtime-prod.sh" "$ROOT/scripts/imgproxy-compat-proxy.mjs" "$TMP/runtime/"
chmod 755 "$TMP/runtime/install-imgproxy-runtime-prod.sh"
tar -C "$TMP" -czf "$TMP/imgproxy-runtime-$RELEASE_ID.tar.gz" runtime
shasum -a 256 "$TMP/imgproxy-runtime-$RELEASE_ID.tar.gz" > "$TMP/imgproxy-runtime-$RELEASE_ID.tar.gz.sha256"
ssh -o BatchMode=yes "$HOST" "mkdir -p '$INCOMING'"
scp -q "$TMP/imgproxy-runtime-$RELEASE_ID.tar.gz" "$TMP/imgproxy-runtime-$RELEASE_ID.tar.gz.sha256" "$HOST:$INCOMING/"
ssh -o BatchMode=yes "$HOST" "RELEASE_ID='$RELEASE_ID' RUNTIME_BASE='$RUNTIME_BASE' INCOMING='$INCOMING' bash -s" <<'REMOTE'
set -euo pipefail
archive="$INCOMING/imgproxy-runtime-$RELEASE_ID.tar.gz"
sha="$archive.sha256"
cd "$INCOMING"
shasum -a 256 -c "$sha"
release_dir="$RUNTIME_BASE/releases/$RELEASE_ID"
mkdir -p "$release_dir" "$RUNTIME_BASE/backups"
cp "$HOME/Library/LaunchAgents/cn.tkmind.imgproxy-compat.plist" "$RUNTIME_BASE/backups/imgproxy-compat-$RELEASE_ID.plist" 2>/dev/null || true
tar -xzf "$archive" -C "$release_dir" --strip-components=1
cd "$release_dir"
shasum -a 256 -c imgproxy-runtime-image.tar.gz.sha256
gunzip -c imgproxy-runtime-image.tar.gz | docker load
image_tag="$(cat image-tag.txt)"
IMGPROXY_RUNTIME_DIR="$release_dir" IMGPROXY_IMAGE_TAG="$image_tag" bash ./install-imgproxy-runtime-prod.sh
ln -sfn "$release_dir" "$RUNTIME_BASE/current"
curl -fsS --max-time 5 http://127.0.0.1:20082/health >/dev/null
curl -fsS --max-time 5 http://10.10.0.2:20081/health >/dev/null
REMOTE
curl -kfsS --max-time 15 https://img.tkmind.cn/health >/dev/null
printf 'imgproxy release verified: %s\n' "$RELEASE_ID"