git-site-sync.sh (3791B)
1 # git-site-sync — keep git.daemon-sec.xyz in step with GitLab. 2 # 3 # git-site-sync same as `auto` 4 # git-site-sync auto sync only if a mirrored repo's main moved (the timer runs this) 5 # git-site-sync now sync regardless (after editing the theme or build script) 6 # git-site-sync status last sync, what GitLab has now, the timer 7 # git-site-sync log the last runs, from the journal 8 # 9 # The repos are whatever ~/git/daemon-sec/script/git-site/repos.json lists 10 # (NixDaemon, daemon-sec-cheatsheet, daemon-sec-lotl). Checking is one 11 # `git ls-remote` per repo — a few hundred bytes — so the timer can run 12 # often; the build and upload (`npm run git-site:sync`, which itself only 13 # sends changed files) happen only when a head actually moved. 14 15 SITE_REPO=${GIT_SITE_REPO:-$HOME/git/daemon-sec} 16 CONFIG=$SITE_REPO/script/git-site/repos.json 17 STATE=${XDG_STATE_HOME:-$HOME/.local/state}/git-site 18 mkdir -p "$STATE" 19 20 # The npm script calls `nix shell` and `npx`: give a systemd user service the 21 # system and per-user profiles that an interactive shell would have. 22 export PATH="$PATH:/run/current-system/sw/bin:/etc/profiles/per-user/$USER/bin" 23 24 die() { echo "git-site-sync: $*" >&2; exit 1; } 25 [ -f "$CONFIG" ] || die "no $CONFIG (set GIT_SITE_REPO if the site repo moved)" 26 27 # "<name> <commit>" per repo, from GitLab. Fails loudly if any repo cannot be 28 # read, so a network blip never looks like "nothing changed". 29 heads() { 30 local name url sha 31 jq -r '.repos[] | "\(.name) \(.fetch)"' "$CONFIG" | while read -r name url; do 32 sha=$(git ls-remote "$url" refs/heads/main | cut -f1) || return 1 33 [ -n "$sha" ] || { echo "git-site-sync: no main on $url" >&2; return 1; } 34 printf '%s %s\n' "$name" "$sha" 35 done 36 } 37 38 notify() { 39 command -v notify-send >/dev/null 2>&1 && notify-send -a git-site-sync -u "${2:-normal}" "git.daemon-sec.xyz" "$1" 2>/dev/null || true 40 } 41 42 sync() { 43 local new=$1 44 echo "git-site-sync: syncing…" 45 if (cd "$SITE_REPO" && npm run -s git-site:sync); then 46 printf '%s\n' "$new" > "$STATE/heads" 47 date -Is > "$STATE/last-sync" 48 echo "git-site-sync: done" 49 else 50 notify "Sync failed — git-site-sync log" critical 51 die "sync failed (git-site-sync log for the details)" 52 fi 53 } 54 55 # One run at a time: the timer and a manual `now` must not interleave uploads. 56 exec 9>"$STATE/lock" 57 case "${1:-auto}" in 58 auto | now) 59 flock -n 9 || { echo "git-site-sync: another sync is running"; exit 0; } 60 new=$(heads) || die "could not read GitLab (offline?); nothing changed" 61 if [ "${1:-auto}" = auto ] && [ "$new" = "$(cat "$STATE/heads" 2>/dev/null)" ]; then 62 echo "git-site-sync: up to date" 63 exit 0 64 fi 65 if [ -f "$STATE/heads" ]; then 66 diff <(cat "$STATE/heads") <(printf '%s\n' "$new") | sed -n 's/^> / moved: /p' || true 67 fi 68 sync "$new" 69 ;; 70 status) 71 echo "last sync: $(cat "$STATE/last-sync" 2>/dev/null || echo never)" 72 echo "synced heads:" 73 sed 's/^/ /' "$STATE/heads" 2>/dev/null || echo " (none yet)" 74 if new=$(heads); then 75 if [ "$new" = "$(cat "$STATE/heads" 2>/dev/null)" ]; then echo "GitLab: no changes since"; else 76 echo "GitLab now:"; printf '%s\n' "$new" | sed 's/^/ /'; fi 77 fi 78 systemctl --user list-timers git-site-sync.timer --no-pager 2>/dev/null | head -2 79 ;; 80 log) 81 journalctl --user -u git-site-sync.service -n "${2:-60}" --no-pager 82 ;; 83 -h | --help | help) 84 echo "usage: git-site-sync [auto|now|status|log [n]]" 85 echo " auto sync if a mirrored repo's main moved (what the timer runs)" 86 echo " now sync regardless" 87 echo " status last sync, GitLab's heads, the timer" 88 echo " log the last runs from the journal" 89 ;; 90 *) die "unknown command '$1' (auto, now, status, log)" ;; 91 esac