Files
scene-actor-extraction/scripts/artifacts/push_artifacts.sh
T
dtourolleandClaude Opus 5 05f30c51fc feat(artifacts): push and pull the VR-013 corpus
The cross-source study needs two 4K recordings and a hand-sorted set of
face crops, neither of which belongs in git. Adds an xsource target to
both artifact scripts.

Push uploads the clips as-is (already compressed) and zips labelling/.
Pull fetches both and regenerates frames with ffmpeg rather than
downloading them: ~320 MB of PNG that is deterministic from the clips.
The extraction settings are pinned in the script, not left to the
caller, because the manifests key on frame filenames and on detection
order within each frame — verify_labels.py runs afterwards and fails
loudly if they drift.

Pull refuses to overwrite an existing labelling/. It is human ground
truth: somebody looked at 167 crops and placed each one, and silently
replacing that with a remote copy would destroy the expensive half of
the study.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

TRACES: VR-013
2026-07-31 15:58:24 +02:00

157 lines
6.5 KiB
Bash
Executable File

#!/bin/bash
# push_artifacts.sh — upload benchmark artifacts (galleries, montage frames) to the
# Gitea generic package registry, decoupled from git history entirely. These are
# NOT needed to run the main app — only for benchmarks/experiments/reports.
#
# Requires GITEA_TOKEN in the environment (a Gitea access token with package
# read/write scope). Never hardcode the token; export it in your shell:
# export GITEA_TOKEN=...
#
# Usage:
# scripts/artifacts/push_artifacts.sh galleries
# scripts/artifacts/push_artifacts.sh montage-frames
# scripts/artifacts/push_artifacts.sh experiment-data
# scripts/artifacts/push_artifacts.sh report-highlights
# scripts/artifacts/push_artifacts.sh xsource
# scripts/artifacts/push_artifacts.sh galleries montage-frames experiment-data report-highlights
#
# Package layout (owner=dtourolle, repo=scene-actor-extraction):
# generic/galleries/<version>/gallery_<model>.h5 (one file per model)
# generic/montage-frames/<version>/<film-slug>.zip (zipped per-film frames)
# generic/experiment-data/<version>/experiment-data.zip (manifests/trajectories/results)
# generic/report-highlights/<version>/<name>.jpg (individual, hand-picked
# illustrative frames referenced by docs/*.md that aren't reproducible via
# the automated best/worst montage selection — see scripts/docs/build_site.sh)
# version = current git short SHA, so artifacts are traceable to the code that
# produced them. Re-running with the same SHA overwrites that version's files.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OWNER="dtourolle"
PKG_REPO="scene-actor-extraction"
# NOTE: the generic package registry lives under /api/packages/ (no /v1/) —
# distinct from the regular REST API under /api/v1/packages/ used for listing.
BASE_URL="https://gitea.tourolle.paris/api/packages/${OWNER}"
VERSION="$(git -C "$REPO_ROOT" rev-parse --short HEAD)"
if [ -z "${GITEA_TOKEN:-}" ]; then
echo "error: GITEA_TOKEN is not set. export GITEA_TOKEN=<your token> and retry." >&2
exit 1
fi
upload() {
local package="$1" filename="$2" filepath="$3"
local url="${BASE_URL}/generic/${package}/${VERSION}/${filename}"
echo " uploading ${filename} -> ${package}/${VERSION}..."
curl -sf -X PUT "$url" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--upload-file "$filepath" \
-o /dev/null -w " HTTP %{http_code}\n"
}
push_galleries() {
echo "=== galleries (version ${VERSION}) ==="
local dir="${REPO_ROOT}/experiments/galleries"
shopt -s nullglob
for f in "$dir"/gallery_*.h5; do
upload "galleries" "$(basename "$f")" "$f"
done
shopt -u nullglob
}
push_montage_frames() {
echo "=== montage-frames (version ${VERSION}) ==="
local root="${REPO_ROOT}/experiments/results/holdout/montage_bestworst"
if [ ! -d "$root" ]; then
echo " no montage_bestworst dir found, skipping" >&2
return
fi
local tmp
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' RETURN
for filmdir in "$root"/*/; do
[ -d "$filmdir" ] || continue
local slug ascii_slug zipfile
slug="$(basename "$filmdir")"
# Gitea's generic package registry rejects non-ASCII filenames (verified:
# a bare é in the name 400s). Transliterate for the upload name only —
# the local directory name (with accents) is untouched.
ascii_slug="$(echo "$slug" | iconv -f utf-8 -t ascii//translit 2>/dev/null || echo "$slug")"
zipfile="${tmp}/${ascii_slug}.zip"
(cd "$filmdir" && zip -qr "$zipfile" .)
upload "montage-frames" "${ascii_slug}.zip" "$zipfile"
done
}
push_experiment_data() {
echo "=== experiment-data (version ${VERSION}) ==="
# manifests/trajectories/results are all small text/JSON — no source paths
# (manifests never carry a "movie" field; see experiments/file-lut.json)
# so this is safe to share as one bundle.
local tmp; tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' RETURN
local zipfile="${tmp}/experiment-data.zip"
(cd "$REPO_ROOT" && zip -qr "$zipfile" \
experiments/manifests experiments/trajectories experiments/results \
-x '*.log' -x '*/holdout/montage_bestworst/*' -x '*/holdout/frames/*' \
-x '*/holdout/montage/*' -x '*/holdout/*.jsonl' -x '*/holdout/pred_*.json' \
-x '*/holdout/raw_*.jsonl')
upload "experiment-data" "experiment-data.zip" "$zipfile"
}
push_report_highlights() {
echo "=== report-highlights (version ${VERSION}) ==="
local src="${REPO_ROOT}/experiments/results/holdout/montage/many_saints/out_of_cast_fpi/10_t003641.jpg"
if [ ! -f "$src" ]; then
echo " [warn] $src not found, skipping" >&2
return
fi
upload "report-highlights" "germar_beats_xray.jpg" "$src"
}
push_xsource() {
echo "=== xsource (version ${VERSION}) ==="
local root="${REPO_ROOT}/experiments/xsource"
if [ ! -d "$root/labelling" ]; then
echo " no experiments/xsource/labelling found, skipping" >&2
return
fi
# Source recordings. Already compressed, so uploaded as-is rather than zipped.
shopt -s nullglob
for f in "$root"/clips/*.mp4; do
upload "xsource" "$(basename "$f")" "$f"
done
shopt -u nullglob
# The hand-sorted crops and their manifests. This is human ground truth and
# the expensive part of the study — a person looked at every crop and put it
# in a folder. Frames are deliberately NOT pushed: they are deterministic
# from the clips, and pulling regenerates them.
local tmp; tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' RETURN
local zipfile="${tmp}/labelling.zip"
(cd "$root" && zip -qr "$zipfile" labelling -x 'labelling/*.html' -x 'labelling/review_*.jpg' \
-x 'labelling/verify_*.jpg')
upload "xsource" "labelling.zip" "$zipfile"
}
if [ $# -eq 0 ]; then
echo "usage: $0 <galleries|montage-frames|experiment-data|report-highlights|xsource> [...]" >&2
exit 1
fi
for target in "$@"; do
case "$target" in
galleries) push_galleries ;;
montage-frames) push_montage_frames ;;
experiment-data) push_experiment_data ;;
report-highlights) push_report_highlights ;;
xsource) push_xsource ;;
*) echo "unknown target: $target (expected galleries, montage-frames, experiment-data, report-highlights, or xsource)" >&2; exit 1 ;;
esac
done
echo "Done. Browse at: https://gitea.tourolle.paris/${OWNER}/${PKG_REPO}/packages"