feat: Implement 'genome-raw-commit' automation script
This commit is contained in:
parent
add1cea732
commit
a3acabb88f
1 changed files with 101 additions and 0 deletions
101
deploy/nexus/genome-raw-commit.sh
Normal file
101
deploy/nexus/genome-raw-commit.sh
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#!/bin/bash
|
||||
# genome-raw-commit <genome>
|
||||
#
|
||||
# Commits raw files synchronized by Syncthing into the vault and pushes them to origin/<base>.
|
||||
# - Committer = n8n-bot (robotic identity responsible for pushing)
|
||||
# - Author = deduced from the Syncthing device ID (modifiedBy field), resolved via .authors.json.
|
||||
# Falls back to default values if unknown.
|
||||
# - One commit per author/device to ensure clear attribution.
|
||||
# - No-op if no changes are present. Excludes infrastructure files and private folders.
|
||||
|
||||
set -euo pipefail
|
||||
genome="${1:?usage: genome-raw-commit <genome>}"
|
||||
|
||||
# Input validation to prevent path or URL traversal inside the script
|
||||
[[ "$genome" =~ ^[a-z0-9][a-z0-9-]{0,63}$ ]] || { echo '{"status":"error","reason":"invalid genome name"}'; exit 1; }
|
||||
|
||||
set -a; . "${HOME}/.config/knowledge-genome.env"; set +a
|
||||
: "${GENOME_VAULTS_ROOT:=/srv/genome-vaults}"
|
||||
: "${GENOME_BASE:=develop}"
|
||||
: "${FORGEJO_USER:=n8n-bot}"
|
||||
: "${FORGEJO_HOST:=127.0.0.1:3001}"
|
||||
: "${FORGEJO_OWNER:=Keru}"
|
||||
: "${SYNCTHING_URL:=http://127.0.0.1:8384}"
|
||||
: "${COMMITTER_NAME:=n8n-bot}"
|
||||
: "${COMMITTER_EMAIL:=n8n-bot@homelab}"
|
||||
: "${DEFAULT_AUTHOR_NAME:=Unknown}"
|
||||
: "${DEFAULT_AUTHOR_EMAIL:=unknown@syncthing}"
|
||||
|
||||
vault="${GENOME_VAULTS_ROOT}/${genome}"
|
||||
fid="${genome}-public"
|
||||
authors_map="${GENOME_VAULTS_ROOT}/.authors.json"
|
||||
clone_url="http://${FORGEJO_USER}@${FORGEJO_HOST}/${FORGEJO_OWNER}/${genome}.git"
|
||||
export GIT_ASKPASS=/usr/local/bin/genome-askpass
|
||||
|
||||
[[ -d "${vault}/.git" ]] || { printf '{"status":"error","reason":"vault absent","genome":"%s"}\n' "$genome"; exit 1; }
|
||||
cd "$vault"
|
||||
git config user.name "$COMMITTER_NAME"
|
||||
git config user.email "$COMMITTER_EMAIL"
|
||||
git config commit.gpgsign false
|
||||
|
||||
# Scope restricted to raw/ directory. raw/.stignore is omitted via .git/info/exclude
|
||||
git add -A -- raw/
|
||||
git reset -q -- raw/.stignore 2>/dev/null || true
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
printf '{"status":"noop","genome":"%s"}\n' "$genome"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Map Syncthing device ID to author information (name, email)
|
||||
resolve_dev() {
|
||||
# $1 = file path relative to the vault root (e.g., raw/file.txt)
|
||||
[[ -z "${SYNCTHING_API_KEY:-}" ]] && return 0
|
||||
curl -fsS -H "X-API-Key: ${SYNCTHING_API_KEY}" --get "${SYNCTHING_URL}/rest/db/file" \
|
||||
--data-urlencode "folder=${fid}" --data-urlencode "file=${1#raw/}" 2>/dev/null \
|
||||
| jq -r '.local.modifiedBy // empty' 2>/dev/null || true
|
||||
}
|
||||
|
||||
author_for_dev() {
|
||||
# $1 = device ID
|
||||
local dev="$1" name="$DEFAULT_AUTHOR_NAME" email="$DEFAULT_AUTHOR_EMAIL"
|
||||
if [[ -n "$dev" && -f "$authors_map" ]] && jq -e --arg d "$dev" '.[$d]' "$authors_map" >/dev/null 2>&1; then
|
||||
name="$(jq -r --arg d "$dev" '.[$d].name' "$authors_map")"
|
||||
email="$(jq -r --arg d "$dev" '.[$d].email' "$authors_map")"
|
||||
fi
|
||||
printf '%s\t%s\t%s' "$name" "$email" "${dev:-unknown}"
|
||||
}
|
||||
|
||||
# Group staged files by author identity
|
||||
declare -A G_FILES G_NAME G_EMAIL G_DEV
|
||||
while IFS= read -r f; do
|
||||
[[ -z "$f" ]] && continue
|
||||
dev="$(resolve_dev "$f")"
|
||||
IFS=$'\t' read -r aname aemail adev <<< "$(author_for_dev "$dev")"
|
||||
key="${aname} <${aemail}>"
|
||||
G_FILES["$key"]+="${f}"$'\n'
|
||||
G_NAME["$key"]="$aname"; G_EMAIL["$key"]="$aemail"; G_DEV["$key"]="$adev"
|
||||
done < <(git diff --cached --name-only -- raw/)
|
||||
|
||||
ts="$(date +%Y-%m-%dT%H:%M:%S%z)"
|
||||
commits=0; summary=""
|
||||
for key in "${!G_FILES[@]}"; do
|
||||
mapfile -t files < <(printf '%s' "${G_FILES[$key]}")
|
||||
short="$(printf '%s\n' "${files[@]}" | sed 's#^raw/##' | paste -sd, -)"
|
||||
msg="$(printf 'raw(%s): sync %s\n\nAdded-by-device: %s\nSyncthing-device-id: %s\nSource: syncthing-autocommit\nSynced-at: %s\n' \
|
||||
"$genome" "$short" "${G_DEV[$key]}" "${G_DEV[$key]}" "$ts")"
|
||||
git commit -q --author="$key" -m "$msg" -- "${files[@]}"
|
||||
commits=$((commits+1))
|
||||
summary="${summary}${summary:+; }${G_NAME[$key]}:${short}"
|
||||
done
|
||||
|
||||
# Fetch updates from origin to merge upstream modifications before pushing
|
||||
git fetch -q origin
|
||||
if git show-ref --verify --quiet "refs/remotes/origin/${GENOME_BASE}"; then
|
||||
git rebase -q "origin/${GENOME_BASE}" \
|
||||
|| { git rebase --abort 2>/dev/null || true; printf '{"status":"error","reason":"rebase-conflict","genome":"%s"}\n' "$genome"; exit 1; }
|
||||
fi
|
||||
git push -q "$clone_url" "HEAD:${GENOME_BASE}"
|
||||
|
||||
printf '{"status":"ok","genome":"%s","base":"%s","commits":%d","head":"%s","summary":"%s"}\n' \
|
||||
"$genome" "$GENOME_BASE" $commits "$(git rev-parse --short HEAD)" "$summary"
|
||||
Loading…
Add table
Reference in a new issue