Compare commits

..

1 commit

Author SHA1 Message Date
n8n-bot
4e81f650e2 feat: Adds vm101 ingest wrapper and deploy tooling 2026-06-21 14:20:44 +02:00
2 changed files with 27 additions and 39 deletions

View file

@ -1,5 +1,5 @@
# ============================================================================= # =============================================================================
# Knowledge Genome - Makefile v. 1.6.0 # Knowledge Genome - Makefile v. 1.5.0
# Orchestrates the setup and management of the knowledge base. # Orchestrates the setup and management of the knowledge base.
# ============================================================================= # =============================================================================

View file

@ -1,11 +1,13 @@
#!/bin/bash #!/bin/bash
# genome-raw-commit <genome> # genome-raw-commit <genome>
# #
# Commit the raw files that Syncthing has placed in the vault and push them to origin/<base>. # Commits raw files synchronized by Syncthing into the vault and pushes them to origin/<base>.
# - Committer = n8n-bot (sole pusher); Author = the person who wrote it (Syncthing modifiedBy -> .authors.json) # - Committer = n8n-bot (robotic identity responsible for pushing)
# - One commit per author (single-device => one commit). No-op if there is nothing. # - Author = deduced from the Syncthing device ID (modifiedBy field), resolved via .authors.json.
# - JSON output built with jq (safe escaping), with a `files` array: # Falls back to default values if unknown.
# for each raw -> file, author, local_path, local_url (file://), remote_url (Forgejo web). # - 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 set -euo pipefail
genome="${1:?usage: genome-raw-commit <genome>}" genome="${1:?usage: genome-raw-commit <genome>}"
@ -18,7 +20,6 @@ set -a; . "${HOME}/.config/knowledge-genome.env"; set +a
: "${FORGEJO_USER:=n8n-bot}" : "${FORGEJO_USER:=n8n-bot}"
: "${FORGEJO_HOST:=127.0.0.1:3001}" : "${FORGEJO_HOST:=127.0.0.1:3001}"
: "${FORGEJO_OWNER:=Keru}" : "${FORGEJO_OWNER:=Keru}"
: "${FORGEJO_WEB_BASE:=https://git.keruhomelab.com}" # human-facing URL for remote links (not the loopback)
: "${SYNCTHING_URL:=http://127.0.0.1:8384}" : "${SYNCTHING_URL:=http://127.0.0.1:8384}"
: "${COMMITTER_NAME:=n8n-bot}" : "${COMMITTER_NAME:=n8n-bot}"
: "${COMMITTER_EMAIL:=n8n-bot@homelab}" : "${COMMITTER_EMAIL:=n8n-bot@homelab}"
@ -37,42 +38,43 @@ git config user.name "$COMMITTER_NAME"
git config user.email "$COMMITTER_EMAIL" git config user.email "$COMMITTER_EMAIL"
git config commit.gpgsign false git config commit.gpgsign false
grep -qxF 'raw/.stignore' "${vault}/.git/info/exclude" 2>/dev/null || echo 'raw/.stignore' >> "${vault}/.git/info/exclude" # Scope restricted to raw/ directory. raw/.stignore is omitted via .git/info/exclude
grep -qxF 'raw/.stfolder' "${vault}/.git/info/exclude" 2>/dev/null || echo 'raw/.stfolder' >> "${vault}/.git/info/exclude"
git add -A -- raw/ git add -A -- raw/
git reset -q -- raw/.stignore raw/.stfolder 2>/dev/null || true git reset -q -- raw/.stignore 2>/dev/null || true
if git diff --cached --quiet; then if git diff --cached --quiet; then
printf '{"status":"noop","genome":"%s"}\n' "$genome" printf '{"status":"noop","genome":"%s"}\n' "$genome"
exit 0 exit 0
fi fi
resolve_dev() { # $1 = path relative to the vault (raw/...) -> prints the short device id, or empty # 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 [[ -z "${SYNCTHING_API_KEY:-}" ]] && return 0
curl -fsS -H "X-API-Key: ${SYNCTHING_API_KEY}" --get "${SYNCTHING_URL}/rest/db/file" \ 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 \ --data-urlencode "folder=${fid}" --data-urlencode "file=${1#raw/}" 2>/dev/null \
| jq -r '.local.modifiedBy // empty' 2>/dev/null || true | jq -r '.local.modifiedBy // empty' 2>/dev/null || true
} }
author_for_dev() { # $1 = device id -> prints "name\temail"
author_for_dev() {
# $1 = device ID
local dev="$1" name="$DEFAULT_AUTHOR_NAME" email="$DEFAULT_AUTHOR_EMAIL" 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 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")" name="$(jq -r --arg d "$dev" '.[$d].name' "$authors_map")"
email="$(jq -r --arg d "$dev" '.[$d].email' "$authors_map")" email="$(jq -r --arg d "$dev" '.[$d].email' "$authors_map")"
fi fi
printf '%s\t%s' "$name" "$email" printf '%s\t%s\t%s' "$name" "$email" "${dev:-unknown}"
} }
# Collect per-file (relpath, author) and group by author for committing # Group staged files by author identity
declare -A G_FILES G_NAME G_EMAIL declare -A G_FILES G_NAME G_EMAIL G_DEV
declare -a ROWS
while IFS= read -r f; do while IFS= read -r f; do
[[ -z "$f" ]] && continue [[ -z "$f" ]] && continue
dev="$(resolve_dev "$f")" dev="$(resolve_dev "$f")"
IFS=$'\t' read -r aname aemail <<< "$(author_for_dev "$dev")" IFS=$'\t' read -r aname aemail adev <<< "$(author_for_dev "$dev")"
ROWS+=("${f}"$'\t'"${aname}")
key="${aname} <${aemail}>" key="${aname} <${aemail}>"
G_FILES["$key"]+="${f}"$'\n' G_FILES["$key"]+="${f}"$'\n'
G_NAME["$key"]="$aname"; G_EMAIL["$key"]="$aemail" G_NAME["$key"]="$aname"; G_EMAIL["$key"]="$aemail"; G_DEV["$key"]="$adev"
done < <(git diff --cached --name-only -- raw/) done < <(git diff --cached --name-only -- raw/)
ts="$(date +%Y-%m-%dT%H:%M:%S%z)" ts="$(date +%Y-%m-%dT%H:%M:%S%z)"
@ -80,34 +82,20 @@ commits=0; summary=""
for key in "${!G_FILES[@]}"; do for key in "${!G_FILES[@]}"; do
mapfile -t files < <(printf '%s' "${G_FILES[$key]}") mapfile -t files < <(printf '%s' "${G_FILES[$key]}")
short="$(printf '%s\n' "${files[@]}" | sed 's#^raw/##' | paste -sd, -)" short="$(printf '%s\n' "${files[@]}" | sed 's#^raw/##' | paste -sd, -)"
msg="$(printf 'raw(%s): sync %s\n\nAdded-by: %s\nSource: syncthing-autocommit\nSynced-at: %s\n' \ 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_NAME[$key]}" "$ts")" "$genome" "$short" "${G_DEV[$key]}" "${G_DEV[$key]}" "$ts")"
git commit -q --author="$key" -m "$msg" -- "${files[@]}" git commit -q --author="$key" -m "$msg" -- "${files[@]}"
commits=$((commits+1)) commits=$((commits+1))
summary="${summary}${summary:+; }${G_NAME[$key]}:${short}" summary="${summary}${summary:+; }${G_NAME[$key]}:${short}"
done done
# Pull in any remote advances (e.g. a merged wiki PR), then push # Fetch updates from origin to merge upstream modifications before pushing
git fetch -q origin git fetch -q origin
if git show-ref --verify --quiet "refs/remotes/origin/${GENOME_BASE}"; then if git show-ref --verify --quiet "refs/remotes/origin/${GENOME_BASE}"; then
git rebase -q "origin/${GENOME_BASE}" \ 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; } || { git rebase --abort 2>/dev/null || true; printf '{"status":"error","reason":"rebase-conflict","genome":"%s"}\n' "$genome"; exit 1; }
fi fi
git push -q "$clone_url" "HEAD:${GENOME_BASE}" git push -q "$clone_url" "HEAD:${GENOME_BASE}"
head="$(git rev-parse --short HEAD)"
# `files` array: local (file://) and remote (Forgejo web) link for each committed raw printf '{"status":"ok","genome":"%s","base":"%s","commits":%d","head":"%s","summary":"%s"}\n' \
files_json="$( "$genome" "$GENOME_BASE" $commits "$(git rev-parse --short HEAD)" "$summary"
for row in "${ROWS[@]}"; do
IFS=$'\t' read -r rel aname <<< "$row"
jq -n --arg file "$rel" --arg author "$aname" \
--arg lpath "${vault}/${rel}" \
--arg lurl "file://${vault}/${rel}" \
--arg rurl "${FORGEJO_WEB_BASE}/${FORGEJO_OWNER}/${genome}/src/branch/${GENOME_BASE}/${rel}" \
'{file:$file, author:$author, local_path:$lpath, local_url:$lurl, remote_url:$rurl}'
done | jq -s '.'
)"
jq -n --arg genome "$genome" --arg base "$GENOME_BASE" --argjson commits "$commits" \
--arg head "$head" --arg summary "$summary" --argjson files "$files_json" \
'{status:"ok", genome:$genome, base:$base, commits:$commits, head:$head, summary:$summary, files:$files}'