From c77a2b02b966b9ae238dc9241fd6715ddaec708c Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Thu, 25 Jun 2026 12:55:42 +0200 Subject: [PATCH 1/6] deploy: nexus: Add Syncthing folder marker for raw vault --- deploy/nexus/ensure-genome-vault.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/deploy/nexus/ensure-genome-vault.sh b/deploy/nexus/ensure-genome-vault.sh index 42ed105..265c349 100644 --- a/deploy/nexus/ensure-genome-vault.sh +++ b/deploy/nexus/ensure-genome-vault.sh @@ -91,6 +91,14 @@ EOF grep -qxF 'raw/.stignore' "${vault}/.git/info/exclude" 2>/dev/null \ || echo 'raw/.stignore' >> "${vault}/.git/info/exclude" +# Syncthing folder marker: must exist on disk (locally, NOT on Git). +# Without it, Syncthing refuses to scan (“folder marker missing”). +mkdir -p "${vault}/raw/.stfolder" + +# .stfolder must not be included in genome commits +grep -qxF 'raw/.stfolder' "${vault}/.git/info/exclude" 2>/dev/null \ +|| echo 'raw/.stfolder' >> "${vault}/.git/info/exclude" + # ── 3. Idempotent Syncthing folder configuration (best-effort, does not block the vault) ──────── folder_state="skipped(no api key)" if [[ -n "${SYNCTHING_API_KEY:-}" ]]; then From 4462d1886652826a0abec1cb642adb8c04f93c7b Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Thu, 25 Jun 2026 12:59:29 +0200 Subject: [PATCH 2/6] feat(n8n): Add manual Genome ingest workflow (scratch) --- .../n8n/Genome_ ingest MANUALE (scratch).json | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 deploy/n8n/Genome_ ingest MANUALE (scratch).json diff --git a/deploy/n8n/Genome_ ingest MANUALE (scratch).json b/deploy/n8n/Genome_ ingest MANUALE (scratch).json new file mode 100644 index 0000000..7f16a50 --- /dev/null +++ b/deploy/n8n/Genome_ ingest MANUALE (scratch).json @@ -0,0 +1,170 @@ +{ + "name": "Genome: ingest MANUALE (scratch)", + "nodes": [ + { + "parameters": {}, + "type": "n8n-nodes-base.manualTrigger", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "2101e704-6275-419d-9963-29a142e5811c", + "name": "Esegui manualmente" + }, + { + "parameters": { + "authentication": "privateKey", + "command": "ssh vm101 'pi ingest genome-test raw/articles/il-grano-saraceno.md'" + }, + "type": "n8n-nodes-base.ssh", + "typeVersion": 1, + "position": [ + 224, + 0 + ], + "id": "8ade2def-2d53-4860-88a5-2ca734c6e54a", + "name": "SSH: pi ingest (manuale)", + "credentials": { + "sshPrivateKey": { + "id": "GJQjKzte7Hjdfz89", + "name": "n8n container -> n8n-runner@nexus" + } + } + }, + { + "parameters": { + "mode": "runOnceForEachItem", + "jsCode": "// ultima riga JSON di run-ingest.sh (ha 'run_id=' davanti)\nconst out = ($json.stdout || '').trim();\nconst line = out.split('\\n').filter(l => l.trim().startsWith('{')).pop();\nif (!line) return { status: 'error', reason: 'nessuna riga JSON run-ingest', raw: out };\ntry { return JSON.parse(line); } catch (e) { return { status: 'error', reason: 'JSON non parsabile', raw: line }; }" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 448, + 0 + ], + "id": "d84cdeaf-612a-454c-8b4d-31824ae6d71e", + "name": "Parse ingest" + }, + { + "parameters": { + "mode": "runOnceForEachItem", + "jsCode": "const d=$json;let n;\nif (d.status==='ok'){\n n={title:`Ingest ${d.slug}: PR aperta`,priority:'default',tags:'inbox_tray',\n body:`\\u2705 ${d.slug}: PR aperta (lint ${d.lint_clean?'clean':'KO'}${d.conflict?', CONFLITTO':''})\\n\\n\\ud83d\\udd17 ${d.pr_url}`};\n} else if (d.status==='pr_failed'){\n n={title:`Ingest ${d.slug}: PR FALLITA`,priority:'high',tags:'warning',\n body:`\\u26a0\\ufe0f ${d.slug}: semantic/lint ok ma PR non aperta.\\n\\n${(d.detail||'').split('\\n')[0]}`};\n} else {\n n={title:'Ingest: ERRORE',priority:'high',tags:'rotating_light',\n body:`\\u274c ${d.reason||'errore'}\\n\\n${(d.raw||'').slice(0,300)}`};\n}\nreturn n;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 672, + 0 + ], + "id": "eadd9275-b38c-416b-b15e-0999f70a05fb", + "name": "Build ntfy" + }, + { + "parameters": { + "method": "POST", + "url": "http://ntfy/homelab-genome", + "authentication": "genericCredentialType", + "genericAuthType": "httpBearerAuth", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Title", + "value": "={{ $json.title }}" + }, + { + "name": "Priority", + "value": "={{ $json.priority }}" + }, + { + "name": "Tags", + "value": "={{ $json.tags }}" + } + ] + }, + "sendBody": true, + "contentType": "raw", + "rawContentType": "Raw / Text", + "body": "={{ $json.body }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 880, + 0 + ], + "id": "63ab577b-893a-4b3d-8f13-b377be778099", + "name": "ntfy: send notification", + "credentials": { + "httpHeaderAuth": { + "id": "TBPXSWOF63k9mvm8", + "name": "ntfy-token" + }, + "httpBearerAuth": { + "id": "nCv4CUN7Ef086Ewj", + "name": "Bearer Auth account" + } + } + } + ], + "pinData": {}, + "connections": { + "Esegui manualmente": { + "main": [ + [ + { + "node": "SSH: pi ingest (manuale)", + "type": "main", + "index": 0 + } + ] + ] + }, + "SSH: pi ingest (manuale)": { + "main": [ + [ + { + "node": "Parse ingest", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse ingest": { + "main": [ + [ + { + "node": "Build ntfy", + "type": "main", + "index": 0 + } + ] + ] + }, + "Build ntfy": { + "main": [ + [ + { + "node": "ntfy: send notification", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": false, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "df06ce3b-1ea8-43be-91ff-02c77972cfe2", + "meta": { + "instanceId": "96b2f0ec76a4400bbd481c617b24b3b87024cc7a913efacccaf9fc85722e7417" + }, + "id": "RNoSaRLYG9vcMn6M", + "tags": [] +} \ No newline at end of file From 2e557ad48f6e75330095ebf94fe5a5ebf66eae0f Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Thu, 25 Jun 2026 12:59:29 +0200 Subject: [PATCH 3/6] feat(n8n): Implement automated Genome ingest workflow --- deploy/n8n/Genome_ ingest.json | 405 +++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 deploy/n8n/Genome_ ingest.json diff --git a/deploy/n8n/Genome_ ingest.json b/deploy/n8n/Genome_ ingest.json new file mode 100644 index 0000000..142f3b4 --- /dev/null +++ b/deploy/n8n/Genome_ ingest.json @@ -0,0 +1,405 @@ +{ + "name": "Genome: ingest", + "nodes": [ + { + "parameters": { + "httpMethod": "POST", + "path": "forgejo-push", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 2.1, + "position": [ + 1040, + 240 + ], + "id": "9cc1b02e-6885-4a19-af34-ed2783ae99bf", + "name": "Webhook", + "webhookId": "bb518834-da85-46bb-bb72-97ba21a78faa" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "loose", + "version": 2 + }, + "conditions": [ + { + "id": "cc000000-0000-4000-8000-000000000001", + "leftValue": "={{ $json.body.ref }}", + "rightValue": "refs/heads/develop", + "operator": { + "type": "string", + "operation": "equals" + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + 1264, + 240 + ], + "id": "b2dd46aa-cdc3-4103-ad05-c728d9bd14ee", + "name": "IF: ref == develop" + }, + { + "parameters": { + "workflowId": { + "__rl": true, + "value": "zbtRXWsLt56nEIfz", + "mode": "list", + "cachedResultUrl": "/workflow/zbtRXWsLt56nEIfz", + "cachedResultName": "Power Manager" + }, + "workflowInputs": { + "mappingMode": "defineBelow", + "value": { + "mode": "ensure-on" + }, + "matchingColumns": [ + "mode" + ], + "schema": [ + { + "id": "mode", + "displayName": "mode", + "required": false, + "defaultMatch": false, + "display": true, + "canBeUsedToMatch": true, + "type": "string", + "removed": false + } + ], + "attemptToConvertTypes": false, + "convertFieldsToString": true + }, + "options": {} + }, + "type": "n8n-nodes-base.executeWorkflow", + "typeVersion": 1.3, + "position": [ + 1488, + 240 + ], + "id": "e10f6af4-73ac-4689-b9f4-9c656d7c0cc4", + "name": "Power Manager - ensure-on" + }, + { + "parameters": { + "authentication": "privateKey", + "command": "=ssh vm101 'pi changed-raw {{ $('Webhook').item.json.body.repository.name }} {{ $('Webhook').item.json.body.before }} {{ $('Webhook').item.json.body.after }}'" + }, + "type": "n8n-nodes-base.ssh", + "typeVersion": 1, + "position": [ + 1712, + 240 + ], + "id": "479d2e9d-0fde-417a-9122-d9780cc5dcba", + "name": "SSH: changed-raw", + "executeOnce": true, + "credentials": { + "sshPrivateKey": { + "id": "GJQjKzte7Hjdfz89", + "name": "n8n container -> n8n-runner@nexus" + } + } + }, + { + "parameters": { + "jsCode": "// run-once-for-all: parse changed-raw (JSON intero) -> 1 item per raw.\n// I nomi raw con spazi o caratteri non sicuri romperebbero il trasporto SSH\n// (lo spazio e' separatore di token nel comando 'pi ingest g raw'). Quindi qui\n// valido i nomi: quelli problematici NON vengono ingeriti, ma emettono un item\n// di tipo 'badname' che a valle diventa un ntfy 'rinomina il file'.\nconst out = ($input.first().json.stdout || '').toString().trim();\nlet d;\ntry { d = JSON.parse(out); }\ncatch (e) { return [{ json: { _kind: 'error', reason: 'changed-raw non parsabile', raw: out } }]; }\nif (!d.files || d.files.length === 0) return []; // niente raw -> stop silenzioso\n\n// regola 'non rompicoglioni': consentiti lettere, numeri, punto, slash, trattino, underscore.\n// VIETATI: spazi e tutto il resto (che spezzano SSH o gli slug downstream).\nconst SAFE = /^[A-Za-z0-9._\\/-]+$/;\nconst out_items = [];\nfor (const raw of d.files) {\n if (SAFE.test(raw)) {\n out_items.push({ json: { _kind: 'ingest', genome: d.genome, raw } });\n } else {\n out_items.push({ json: { _kind: 'badname', genome: d.genome, raw,\n hint: raw.replace(/[^A-Za-z0-9._\\/-]+/g, '-').toLowerCase() } });\n }\n}\nreturn out_items;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1920, + 240 + ], + "id": "d540e454-4648-475c-8dce-5111ef876f75", + "name": "Split raw files" + }, + { + "parameters": { + "authentication": "privateKey", + "command": "=ssh vm101 'pi ingest {{ $json.genome }} {{ $json.raw }}'" + }, + "type": "n8n-nodes-base.ssh", + "typeVersion": 1, + "position": [ + 2144, + 240 + ], + "id": "7e30e055-7bc5-484a-a405-d29ea06ff175", + "name": "SSH: pi ingest", + "credentials": { + "sshPrivateKey": { + "id": "GJQjKzte7Hjdfz89", + "name": "n8n container -> n8n-runner@nexus" + } + } + }, + { + "parameters": { + "mode": "runOnceForEachItem", + "jsCode": "// per-item: ultima riga JSON di run-ingest.sh\nconst out = ($json.stdout || '').trim();\nconst line = out.split('\\n').filter(l => l.trim().startsWith('{')).pop();\nif (!line) return { status: 'error', reason: 'nessuna riga JSON run-ingest', raw: out };\ntry { return JSON.parse(line); } catch (e) { return { status: 'error', reason: 'JSON non parsabile', raw: line }; }" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2368, + 240 + ], + "id": "f60878f4-8cca-43d0-b8b3-0aa1a422237b", + "name": "Parse ingest" + }, + { + "parameters": { + "mode": "runOnceForEachItem", + "jsCode": "const d = $json;\nlet n;\nif (d.status === 'ok') {\n n = { title: `Ingest ${d.slug}: PR aperta`, priority: 'default', tags: 'inbox_tray',\n body: `\\u2705 ${d.slug}: PR aperta (lint ${d.lint_clean ? 'clean' : 'KO'}${d.conflict ? ', CONFLITTO' : ''})\\n\\n\\ud83d\\udd17 ${d.pr_url}` };\n} else if (d.status === 'pr_failed') {\n n = { title: `Ingest ${d.slug}: PR FALLITA`, priority: 'high', tags: 'warning',\n body: `\\u26a0\\ufe0f ${d.slug}: semantic/lint ok ma PR non aperta.\\n\\n${(d.detail || '').split('\\n')[0]}` };\n} else {\n n = { title: 'Ingest: ERRORE', priority: 'high', tags: 'rotating_light',\n body: `\\u274c ${d.reason || 'errore'}\\n\\n${(d.raw || '').slice(0,300)}` };\n}\nreturn n;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 2592, + 240 + ], + "id": "9018dff6-b314-4ca8-b8ff-fd5423818816", + "name": "Build ntfy" + }, + { + "parameters": { + "method": "POST", + "url": "http://ntfy/homelab-genome", + "authentication": "genericCredentialType", + "genericAuthType": "httpBearerAuth", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Title", + "value": "={{ $json.title }}" + }, + { + "name": "Priority", + "value": "={{ $json.priority }}" + }, + { + "name": "Tags", + "value": "={{ $json.tags }}" + } + ] + }, + "sendBody": true, + "contentType": "raw", + "rawContentType": "Raw / Text", + "body": "={{ $json.body }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 2800, + 240 + ], + "id": "1f572cb3-741b-46bc-87fa-1e23ade5a9be", + "name": "ntfy: send notification", + "credentials": { + "httpHeaderAuth": { + "id": "TBPXSWOF63k9mvm8", + "name": "ntfy-token" + }, + "httpBearerAuth": { + "id": "nCv4CUN7Ef086Ewj", + "name": "Bearer Auth account" + } + } + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "loose", + "version": 2 + }, + "conditions": [ + { + "id": "dd000000-0000-4000-8000-000000000001", + "leftValue": "={{ $json._kind }}", + "rightValue": "ingest", + "operator": { + "type": "string", + "operation": "equals" + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + 3168, + 240 + ], + "id": "d2d2e2b2-9bd7-446b-b6b8-0a865d49c601", + "name": "IF: nome valido" + }, + { + "parameters": { + "mode": "runOnceForEachItem", + "jsCode": "const d=$json;\nreturn {\n title: 'Ingest: nome file non valido',\n priority: 'high',\n tags: 'warning',\n body: `\\u26a0\\ufe0f \"${d.raw}\" ha spazi o caratteri non ammessi e non e' stato ingerito.\\n\\nRinominalo in: ${d.hint}`\n};" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 3392, + 400 + ], + "id": "3b69aa97-170a-4666-8b0e-4b51b48b2817", + "name": "Build ntfy badname" + } + ], + "pinData": {}, + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "IF: ref == develop", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF: ref == develop": { + "main": [ + [ + { + "node": "Power Manager - ensure-on", + "type": "main", + "index": 0 + } + ] + ] + }, + "Power Manager - ensure-on": { + "main": [ + [ + { + "node": "SSH: changed-raw", + "type": "main", + "index": 0 + } + ] + ] + }, + "SSH: changed-raw": { + "main": [ + [ + { + "node": "Split raw files", + "type": "main", + "index": 0 + } + ] + ] + }, + "Split raw files": { + "main": [ + [ + { + "node": "IF: nome valido", + "type": "main", + "index": 0 + } + ] + ] + }, + "SSH: pi ingest": { + "main": [ + [ + { + "node": "Parse ingest", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse ingest": { + "main": [ + [ + { + "node": "Build ntfy", + "type": "main", + "index": 0 + } + ] + ] + }, + "Build ntfy": { + "main": [ + [ + { + "node": "ntfy: send notification", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF: nome valido": { + "main": [ + [ + { + "node": "SSH: pi ingest", + "type": "main", + "index": 0 + } + ], + [ + { + "node": "Build ntfy badname", + "type": "main", + "index": 0 + } + ] + ] + }, + "Build ntfy badname": { + "main": [ + [ + { + "node": "ntfy: send notification", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "2115dd9f-e2b6-4acb-8de0-4a166eb9729a", + "meta": { + "instanceId": "96b2f0ec76a4400bbd481c617b24b3b87024cc7a913efacccaf9fc85722e7417" + }, + "id": "mUJUuQxcDiiPWcUE", + "tags": [] +} \ No newline at end of file From 13b6d47574cdbc17bfa6ee0b61c60ef8e8d8afe4 Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Thu, 25 Jun 2026 12:59:30 +0200 Subject: [PATCH 4/6] feat(n8n): Add scheduled Genome raw file committer --- deploy/n8n/Genome_ raw → commit.json | 222 +++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 deploy/n8n/Genome_ raw → commit.json diff --git a/deploy/n8n/Genome_ raw → commit.json b/deploy/n8n/Genome_ raw → commit.json new file mode 100644 index 0000000..d0dffe2 --- /dev/null +++ b/deploy/n8n/Genome_ raw → commit.json @@ -0,0 +1,222 @@ +{ + "name": "Genome: raw → commit", + "nodes": [ + { + "parameters": { + "rule": { + "interval": [ + { + "field": "cronExpression", + "expression": "*/2 * * * *" + } + ] + } + }, + "type": "n8n-nodes-base.scheduleTrigger", + "typeVersion": 1.3, + "position": [ + 384, + 1056 + ], + "id": "520c79c8-76e6-41c0-8836-4d8d8f4ed236", + "name": "Schedule: ogni 2 min" + }, + { + "parameters": { + "authentication": "privateKey", + "command": "sudo -u homelab -H /usr/local/bin/genome-raw-commit genome-test" + }, + "type": "n8n-nodes-base.ssh", + "typeVersion": 1, + "position": [ + 608, + 1056 + ], + "id": "fe89a85f-d63e-47d9-a7b4-08222f2635d0", + "name": "SSH: genome-raw-commit", + "executeOnce": true, + "credentials": { + "sshPrivateKey": { + "id": "GJQjKzte7Hjdfz89", + "name": "n8n container -> n8n-runner@nexus" + } + } + }, + { + "parameters": { + "jsCode": "// Lo script ora stampa JSON multilinea (jq -n). git manda i progressi su stderr,\n// quindi stdout e' SOLO il JSON: si parsa per intero.\nconst out = ($input.first().json.stdout || '').trim();\nlet data;\ntry {\n data = JSON.parse(out);\n} catch (e) {\n data = { status: 'error', reason: 'output non parsabile', genome: 'genome-test', raw: out };\n}\nreturn [{ json: data }];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 832, + 1056 + ], + "id": "74051cc5-5760-453d-80e4-0696d31bfc15", + "name": "Parse result" + }, + { + "parameters": { + "conditions": { + "options": { + "caseSensitive": true, + "leftValue": "", + "typeValidation": "loose", + "version": 2 + }, + "conditions": [ + { + "id": "c0000000-0000-4000-8000-000000000001", + "leftValue": "={{ $json.status }}", + "rightValue": "noop", + "operator": { + "type": "string", + "operation": "notEquals" + } + } + ], + "combinator": "and" + }, + "options": {} + }, + "type": "n8n-nodes-base.if", + "typeVersion": 2.2, + "position": [ + 1056, + 1056 + ], + "id": "5813753d-f015-4a4e-b386-9d60659077c3", + "name": "IF: non noop" + }, + { + "parameters": { + "jsCode": "const d = $input.first().json;\nlet n;\nif (d.status === 'ok') {\n const f = d.files && d.files[0];\n n = {\n title: `Genome: ${d.commits} raw -> ${d.base}`,\n priority: 'default',\n tags: 'inbox_tray',\n body: `✅ ${d.genome}: ${d.commits} commit su ${d.base} (HEAD ${d.head})\\n\\n${d.summary || ''}`\n + (f ? `\\n\\n🔗 Forgejo: ${f.remote_url}\\n📂 Locale: ${f.local_url}` : '')\n };\n} else {\n n = {\n title: 'Genome raw commit: ERRORE',\n priority: 'high',\n tags: 'warning',\n body: `\\u274C ${d.genome || 'genome-test'}: ${d.reason || 'errore sconosciuto'}`\n };\n}\nreturn [{ json: n }];" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 1264, + 976 + ], + "id": "29eee748-4c2d-4e1e-8013-a64bc9cbf816", + "name": "Build ntfy" + }, + { + "parameters": { + "method": "POST", + "url": "http://ntfy/homelab-genome", + "authentication": "genericCredentialType", + "genericAuthType": "httpBearerAuth", + "sendHeaders": true, + "headerParameters": { + "parameters": [ + { + "name": "Title", + "value": "={{ $json.title }}" + }, + { + "name": "Priority", + "value": "={{ $json.priority }}" + }, + { + "name": "Tags", + "value": "={{ $json.tags }}" + } + ] + }, + "sendBody": true, + "contentType": "raw", + "rawContentType": "Raw / Text", + "body": "={{ $json.body }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.4, + "position": [ + 1488, + 976 + ], + "id": "d9b6ca21-59ef-44cf-a4f7-a75dcc7eeab4", + "name": "ntfy: send notification", + "credentials": { + "httpHeaderAuth": { + "id": "TBPXSWOF63k9mvm8", + "name": "ntfy-token" + }, + "httpBearerAuth": { + "id": "nCv4CUN7Ef086Ewj", + "name": "Bearer Auth account" + } + } + } + ], + "pinData": {}, + "connections": { + "Schedule: ogni 2 min": { + "main": [ + [ + { + "node": "SSH: genome-raw-commit", + "type": "main", + "index": 0 + } + ] + ] + }, + "SSH: genome-raw-commit": { + "main": [ + [ + { + "node": "Parse result", + "type": "main", + "index": 0 + } + ] + ] + }, + "Parse result": { + "main": [ + [ + { + "node": "IF: non noop", + "type": "main", + "index": 0 + } + ] + ] + }, + "IF: non noop": { + "main": [ + [ + { + "node": "Build ntfy", + "type": "main", + "index": 0 + } + ] + ] + }, + "Build ntfy": { + "main": [ + [ + { + "node": "ntfy: send notification", + "type": "main", + "index": 0 + } + ] + ] + } + }, + "active": true, + "settings": { + "executionOrder": "v1", + "binaryMode": "separate" + }, + "versionId": "9607be0b-cd8c-4e7a-9ddb-63b6ec22b65d", + "meta": { + "instanceId": "96b2f0ec76a4400bbd481c617b24b3b87024cc7a913efacccaf9fc85722e7417" + }, + "id": "whyxMpvJMYQ55J1M", + "tags": [] +} \ No newline at end of file From 940eb49a9e6bb6a902ca99e6973db6c7c35d929c Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Thu, 25 Jun 2026 13:08:24 +0200 Subject: [PATCH 5/6] refactor: Standardize n8n workflow file naming to kebab-case --- ..._ ingest MANUALE (scratch).json => genome-ingest-manuale.json} | 0 deploy/n8n/{Genome_ ingest.json => genome-ingest.json} | 0 deploy/n8n/{Genome_ raw → commit.json => genome-raw-commit.json} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename deploy/n8n/{Genome_ ingest MANUALE (scratch).json => genome-ingest-manuale.json} (100%) rename deploy/n8n/{Genome_ ingest.json => genome-ingest.json} (100%) rename deploy/n8n/{Genome_ raw → commit.json => genome-raw-commit.json} (100%) diff --git a/deploy/n8n/Genome_ ingest MANUALE (scratch).json b/deploy/n8n/genome-ingest-manuale.json similarity index 100% rename from deploy/n8n/Genome_ ingest MANUALE (scratch).json rename to deploy/n8n/genome-ingest-manuale.json diff --git a/deploy/n8n/Genome_ ingest.json b/deploy/n8n/genome-ingest.json similarity index 100% rename from deploy/n8n/Genome_ ingest.json rename to deploy/n8n/genome-ingest.json diff --git a/deploy/n8n/Genome_ raw → commit.json b/deploy/n8n/genome-raw-commit.json similarity index 100% rename from deploy/n8n/Genome_ raw → commit.json rename to deploy/n8n/genome-raw-commit.json From 1cb3da41c35844ddfb225ddac1fbeea3c7aa36c4 Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Thu, 25 Jun 2026 13:09:10 +0200 Subject: [PATCH 6/6] Update version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1177c8c..214cf3b 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # ============================================================================= -# Knowledge Genome - Makefile v. 1.7.0 +# Knowledge Genome - Makefile v. 1.8.0 # Orchestrates the setup and management of the knowledge base. # =============================================================================