diff --git a/deploy/cleura/portfolio-sync.sh b/deploy/cleura/portfolio-sync.sh index 97f2c2f..8c68b5c 100644 --- a/deploy/cleura/portfolio-sync.sh +++ b/deploy/cleura/portfolio-sync.sh @@ -8,8 +8,14 @@ # # Installed by deploy/cleura/install-portfolio-sync.sh as /opt/portal/site/portfolio-sync.sh, run by # portfolio-sync.timer every 20 minutes. Secrets: /etc/portal/portfolio-sync.env (root 600) with -# IMMICH_URL, PORTFOLIO_TAG_ID, IMMICH_API_KEY (read-only Immich key) and GIT_URL (Gitea URL that may push main). +# IMMICH_URL, PORTFOLIO_TAG_ID, IMMICH_API_KEY (read-only Immich key) and GIT_URL (Gitea URL that may push main, +# with user:token@ in it, as on Nurem). +# +# The Gitea token never lands on disk outside that env file: the clone's remote URL is stored without it, git gets +# it from a credential helper at fetch/push time, and the Node container (npm install runs third-party code) gets +# an empty tmpfs over .git and only the Immich variables. set -euo pipefail +umask 077 ENVF=${PORTFOLIO_SYNC_ENV:-/etc/portal/portfolio-sync.env} DIR=${PORTFOLIO_SYNC_DIR:-/opt/portal/site/sync/repo} NODE=node:22.22.0-alpine@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13363826e34 @@ -23,27 +29,41 @@ export PORTFOLIO_TAG_ID="${PORTFOLIO_TAG_ID:-67d16b6c-0dea-42ef-a289-2c9e57fa5c5 [ -n "${IMMICH_API_KEY:-}" ] || { echo "FATAL: IMMICH_API_KEY missing in $ENVF"; exit 2; } [ -n "${GIT_URL:-}" ] || { echo "FATAL: GIT_URL missing in $ENVF"; exit 2; } +# Split GIT_URL into the clean URL (stored in .git/config) and user/token (kept in this process only). +read -r CLEAN_URL GIT_USER GIT_TOKEN < <(python3 - "$GIT_URL" <<'PY' +import sys, urllib.parse as u +p = u.urlsplit(sys.argv[1]) +host = p.hostname + (":%d" % p.port if p.port else "") +print(u.urlunsplit((p.scheme, host, p.path, "", "")), u.unquote(p.username or "-"), u.unquote(p.password or "-")) +PY +) +unset GIT_URL +export GIT_USER GIT_TOKEN +# shellcheck disable=SC2016 # $GIT_USER/$GIT_TOKEN are expanded by the helper's shell, not here. +G=(git -c credential.helper= -c 'credential.helper=!f() { echo "username=$GIT_USER"; echo "password=$GIT_TOKEN"; }; f') + if [ ! -d "$DIR/.git" ]; then mkdir -p "$(dirname "$DIR")" - git clone -q "$GIT_URL" "$DIR" + "${G[@]}" clone -q "$CLEAN_URL" "$DIR" fi -git -C "$DIR" remote set-url origin "$GIT_URL" +chmod 700 "$(dirname "$DIR")" "$DIR" +git -C "$DIR" remote set-url origin "$CLEAN_URL" echo "[sync] fetch + reset main" -git -C "$DIR" fetch -q origin main +"${G[@]}" -C "$DIR" fetch -q origin main git -C "$DIR" checkout -q -B main origin/main git -C "$DIR" reset -q --hard origin/main echo "[sync] scripts/portfolio-sync.mjs" docker run --rm --memory 2g --cpus 2 --network bridge \ -e IMMICH_URL -e IMMICH_API_KEY -e PORTFOLIO_TAG_ID \ - -v "$DIR":/app -w /app "$NODE" \ + -v "$DIR":/app --mount type=tmpfs,destination=/app/.git -w /app "$NODE" \ sh -c 'npm install --no-audit --no-fund --loglevel=error >/dev/null && node scripts/portfolio-sync.mjs' if git -C "$DIR" status --porcelain -- src/assets/images/portfolio | grep -q .; then git -C "$DIR" add -A src/assets/images/portfolio git -C "$DIR" -c user.name="portfolio-sync (Cleura)" -c user.email="Admin@imagesbyolofsson.se" \ commit -q -m "chore(portfolio): sync $(date -u +%Y-%m-%d)" - git -C "$DIR" push -q origin main + "${G[@]}" -C "$DIR" push -q origin main echo "[sync] pushed $(git -C "$DIR" rev-parse --short HEAD), rebuilding the live site" /opt/portal/site/build-live.sh main else