1105 lines
50 KiB
JSON
1105 lines
50 KiB
JSON
{
|
||
"name": "Git Commit Workflows",
|
||
"nodes": [
|
||
{
|
||
"parameters": {
|
||
"content": "## Точка входа\n\nДва способа запустить workflow:\n\n**Форма** — основной способ. Пользователь заполняет данные, через форму\n\n**Ручной запуск** — для отладки.\n\n**Два режима фильтрации workflows:**\n- `Выгрузить все workflow` — `true` чтобы выгрузить всё, `false` для фильтрации по тегам\n- Список `Tags (через запятую)` — игнорируется если `Выгрузить все workflow: true`",
|
||
"height": 1036,
|
||
"width": 608,
|
||
"color": 7
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
5152,
|
||
176
|
||
],
|
||
"id": "d1421bd7-ad57-436f-93d9-fa72ee99b478",
|
||
"name": "Зона: Точка входа"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "## Подготовка\n\nПарсим данные формы и строим все переменные необходимые для работы:\n- **remoteUrl** — URL с кредами вида `http://user:pat@host/org/repo.git`.\n- **cleanRemoteUrl** — URL без кредов.\n- **projectFolder** — локальная папка репозитория.\n- **commitMessage** — собирается из типа + текста коммита\n- **exportAll** — флаг «выгрузить все workflow». Если `true` — теги игнорируются\n- **tags** — список тегов (игнорируется при `exportAll = true`)\n- **skipWorkflows** — имена workflow которые всегда исключаются из выгрузки\n\n**Два режима сбора:**\n- `exportAll = true` → `Получить все workflow` (один запрос к n8n API)\n- `exportAll = false` → `Получить теги n8n` → `Фильтр тегов` → `Цикл по тегам` → `Получить workflow по тегу`\n\n⚠️Если ни один тег не найден и `exportAll = false` — бросаем ошибку.",
|
||
"height": 1036,
|
||
"width": 604,
|
||
"color": 5
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
5808,
|
||
176
|
||
],
|
||
"id": "d511af8a-d4eb-4b73-9dbc-dac6fd785a09",
|
||
"name": "Зона: Подготовка"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "## Сбор workflow\n\nДва режима в зависимости от флага **Выгрузить все**:\n\n**Режим «Все workflow»:**\nОдин запрос к n8n API без фильтра — получаем полный список. Теги игнорируются.\n\n**Режим «По тегам»:**\nДля каждого совпавшего тега получаем список workflow через n8n API. Один workflow может иметь несколько тегов — после сбора применяем **дедупликацию** по `workflowId`.\n\nЕсли список пуст — показываем форму «Workflow не найдены».",
|
||
"height": 1036,
|
||
"width": 604,
|
||
"color": 5
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
6464,
|
||
176
|
||
],
|
||
"id": "a8cae61d-9f09-470e-bee5-af57075ae2f5",
|
||
"name": "Зона: Сбор workflow"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "## Git Clone / Pull и подготовка репозитория\n\nВыполняется **один раз** перед началом итерации по workflow.\n\nЧто происходит:\n1. Создаём родительскую папку (`exportFolder/org/`) если её нет\n2. **Если папка уже есть** (`projectFolder/.git`):\n - Обновляем remote URL (с кредами текущего пользователя)\n - `git fetch` — получаем актуальные ветки\n - Переключаемся на нужную ветку\n - `git reset --hard origin/branch` — приводим к состоянию remote\n3. **Если папки нет** — `git clone` нужной ветки (или создаём новую если её нет на remote)\nПапка **никогда не удаляется** — каждый запуск только актуализирует.\n\n⚠️ Если нет git `user.email` и `user.name` - настраиваются автоматически",
|
||
"height": 1036,
|
||
"width": 604,
|
||
"color": 4
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
7120,
|
||
176
|
||
],
|
||
"id": "252d213a-25d6-43a9-83d5-f7006d445119",
|
||
"name": "Зона: Git Init"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "## Синхронизация и экспорт файлов\n\n**Синхронизация удалённых workflow:**\nПеред записью сравниваем список `.json` файлов в папке с актуальным списком workflow. Файлы которых больше нет среди собранных workflow — **удаляются**.\n\n**Для каждого workflow из цикла:**\n\n**Записать файл** (`executeCommand`) — экспортируем через CLI прямо в целевой файл:\n`n8n export:workflow --id=<workflowId> --output=<filePath>`\n\nCLI сам создаёт или перезаписывает файл. Проверка изменений не выполняется — git определит diff при коммите.",
|
||
"height": 1028,
|
||
"width": 604,
|
||
"color": 6
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
7776,
|
||
176
|
||
],
|
||
"id": "a93df50a-5755-4a8d-a262-748f89cdd158",
|
||
"name": "Зона: Экспорт файлов"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"content": "## Коммит, пуш и очистка\n\nПосле того как все файлы экспортированы (цикл завершён — выход 0):\n\n1. **Проверить изменения git** — `git add -A` + `git diff --cached HEAD`:\n - `HAS_CHANGES` → делаем коммит\n - `NO_CHANGES` → показываем форму «Нет изменений»\n\n2. **Git Commit** — `git commit -m \"commitMessage\"`\n\n3. **Git Push** — `git push origin branch || git push --set-upstream origin branch`. Для новой ветки автоматически добавляет `--set-upstream`.\n\n4. **Успешный Push?** (IF) — проверяем stdout на наличие `fatal:` или `error:` (AND):\n - Нет ошибок → **Очистить Remote от кредов** → **Форма: Успех**\n - Есть ошибки → **Форма: Ошибка push**\n\n5. **Очистить Remote от кредов**\n\n⚠️ ВАЖНО! Заменяем remote URL на версию БЕЗ кредов. Защищает от случайного использования чужого PAT при следующем запуске.\n\n⚠️ Локальная папка **не удаляется** — остаётся для следующего запуска.",
|
||
"height": 1028,
|
||
"width": 1356,
|
||
"color": 3
|
||
},
|
||
"type": "n8n-nodes-base.stickyNote",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
8448,
|
||
176
|
||
],
|
||
"id": "92723155-adb4-4a8d-92ec-2e7dc0ce7d38",
|
||
"name": "Зона: Коммит и пуш"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"formTitle": "Экспорт Workflow в Git",
|
||
"formDescription": "Заполните данные для коммита workflow в репозиторий",
|
||
"formFields": {
|
||
"values": [
|
||
{
|
||
"fieldLabel": "Username",
|
||
"placeholder": "gitea_username",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Personal Access Token",
|
||
"fieldType": "password",
|
||
"placeholder": "your_personal_access_token",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Repository URL",
|
||
"placeholder": "https://<URL Repo>",
|
||
"defaultValue": "https://vcs.bbr.ru",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Organization",
|
||
"placeholder": "Название организации",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Repository Name",
|
||
"placeholder": "my-project",
|
||
"defaultValue": "workflows",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Branch Type",
|
||
"fieldType": "dropdown",
|
||
"defaultValue": "main",
|
||
"fieldOptions": {
|
||
"values": [
|
||
{
|
||
"option": "main"
|
||
},
|
||
{
|
||
"option": "develop"
|
||
},
|
||
{
|
||
"option": "release/"
|
||
},
|
||
{
|
||
"option": "feature/"
|
||
},
|
||
{
|
||
"option": "hotfix/"
|
||
},
|
||
{
|
||
"option": "custom"
|
||
}
|
||
]
|
||
},
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Custom Branch Name",
|
||
"placeholder": "Для release/feature/hotfix укажите суффикс или полное имя для custom"
|
||
},
|
||
{
|
||
"fieldLabel": "Commit Type",
|
||
"fieldType": "dropdown",
|
||
"defaultValue": "feat: Новая функциональность",
|
||
"fieldOptions": {
|
||
"values": [
|
||
{
|
||
"option": "feat: Новая функциональность"
|
||
},
|
||
{
|
||
"option": "fix: Исправление ошибки"
|
||
},
|
||
{
|
||
"option": "docs: Документация"
|
||
},
|
||
{
|
||
"option": "style: Форматирование"
|
||
},
|
||
{
|
||
"option": "refactor: Рефакторинг"
|
||
},
|
||
{
|
||
"option": "test: Тесты"
|
||
},
|
||
{
|
||
"option": "chore: Служебные изменения"
|
||
},
|
||
{
|
||
"option": "perf: Производительность"
|
||
},
|
||
{
|
||
"option": "ci: CI/CD"
|
||
}
|
||
]
|
||
},
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Commit Message",
|
||
"placeholder": "Описание изменений",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Выгрузить все workflow",
|
||
"fieldType": "radio",
|
||
"defaultValue": "0",
|
||
"fieldOptions": {
|
||
"values": [
|
||
{
|
||
"option": "Да"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
{
|
||
"fieldLabel": "Tags (через запятую)",
|
||
"placeholder": "production,finance,crm",
|
||
"defaultValue": "metric",
|
||
"requiredField": true
|
||
},
|
||
{
|
||
"fieldLabel": "Export Folder",
|
||
"placeholder": "/data/shared",
|
||
"defaultValue": "/data/shared",
|
||
"requiredField": true
|
||
}
|
||
]
|
||
},
|
||
"responseMode": "lastNode",
|
||
"options": {
|
||
"path": "154391ab-386d-42ce-8074-19a28d05544b",
|
||
"customCss": ":root {\n --font-size-body: 13px;\n --font-size-label: 13px;\n --font-size-test-notice: 12px;\n --font-size-input: 13px;\n --font-size-header: 20px;\n --font-size-paragraph: 13px;\n --font-size-link: 12px;\n --font-size-error: 12px;\n --font-size-html-h1: 28px;\n --font-size-html-h2: 20px;\n --font-size-html-h3: 16px;\n --font-size-html-h4: 13px;\n --font-size-html-h5: 12px;\n --font-size-html-h6: 11px;\n --font-size-subheader: 13px;\n --color-background: #1b1f23;\n --color-test-notice-text: #d29922;\n --color-test-notice-bg: #272115;\n --color-test-notice-border: #5a4010;\n --color-card-bg: #22272e;\n --color-card-border: #373e47;\n --color-card-shadow: rgba(0,0,0,0.4);\n --color-link: #539bf5;\n --color-html-link: #539bf5;\n --color-header: #cdd9e5;\n --color-header-subtext: #768390;\n --color-label: #adbac7;\n --color-input-border: #444c56;\n --color-input-text: #cdd9e5;\n --color-input-bg: #1c2128;\n --color-focus-border: #539bf5;\n --color-submit-btn-bg: #347d39;\n --color-submit-btn-text: #cdd9e5;\n --color-error: #e5534b;\n --color-required: #e5534b;\n --color-clear-button-bg: #636e7b;\n --color-html-text: #adbac7;\n --border-radius-card: 6px;\n --border-radius-input: 6px;\n --border-radius-clear-btn: 50%;\n --card-border-radius: 6px;\n --padding-container-top: 24px;\n --padding-card: 24px;\n --padding-test-notice-vertical: 10px;\n --padding-test-notice-horizontal: 16px;\n --margin-bottom-card: 16px;\n --padding-form-input: 12px;\n --card-padding: 24px;\n --card-margin-bottom: 16px;\n --container-width: 448px;\n --submit-btn-height: 44px;\n --checkbox-size: 16px;\n --box-shadow-card: 0px 4px 16px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03);\n --opacity-placeholder: 0.35;\n}"
|
||
}
|
||
},
|
||
"id": "cfe3d2d1-e962-46b0-bd23-7cc9873ce956",
|
||
"name": "Форма запроса",
|
||
"type": "n8n-nodes-base.formTrigger",
|
||
"typeVersion": 2.2,
|
||
"position": [
|
||
5328,
|
||
672
|
||
],
|
||
"webhookId": "154391ab-386d-42ce-8074-19a28d05544b"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const body = $input.first().json;\n\nlet branch;\nconst branchType = body['Branch Type'];\nconst customBranch = (body['Custom Branch Name'] || '').trim();\n\nif (branchType === 'custom') {\n if (!customBranch) throw new Error('Custom Branch Name обязателен при выборе custom');\n branch = customBranch;\n} else if (['release/', 'feature/', 'hotfix/'].includes(branchType)) {\n if (!customBranch) throw new Error(`Укажите суффикс для ветки ${branchType}`);\n branch = branchType + customBranch;\n} else {\n branch = branchType;\n}\n\nconst commitTypeRaw = body['Commit Type'];\nconst commitTypePrefix = commitTypeRaw.split(':')[0].trim();\nconst commitMsg = (body['Commit Message'] || '').trim();\nconst commitMessage = `${commitTypePrefix}: ${commitMsg}`;\n\nconst username = (body['Username']).trim();\nconst pat = (body['Personal Access Token']).trim();\nconst org = (body['Organization'] || '').trim();\nconst repoUrl = (body['Repository URL']).trim().replace(/\\/$/, '');\nconst repoName = (body['Repository Name']).trim();\nconst exportFolder = (body['Export Folder'] || '/data/shared').trim().replace(/\\/$/, '');\n\nconst authUrl = repoUrl.replace(/^(https?:\\/\\/)/, `$1${encodeURIComponent(username)}:${encodeURIComponent(pat)}@`);\nconst remoteUrl = `${authUrl}/${org}/${repoName}.git`;\nconst cleanRemoteUrl = `${repoUrl}/${org}/${repoName}.git`;\n\nconst projectFolder = `${exportFolder}/${org}/${repoName}`;\n\nconst exportAll = !!(body['Выгрузить все workflow']);\nconst tags = (body['Tags (через запятую)'] || '').split(',').map(t => t.trim()).filter(Boolean);\nif (!exportAll && tags.length === 0) throw new Error('Укажите хотя бы один тег или включите \"Выгрузить все workflow\"');\nconst skipWorkflows = [\n 'Git Commit Workflows',\n 'Git Pull Workflows',\n 'Backup Workflows',\n 'Backup Сredentials',\n];\n\n\nreturn [{\n json: {\n username,\n pat,\n repoUrl,\n repoName,\n org,\n remoteUrl,\n cleanRemoteUrl,\n branch,\n commitMessage,\n exportAll,\n tags,\n skipWorkflows,\n projectFolder,\n exportFolder,\n }\n}];"
|
||
},
|
||
"id": "887f1e0f-1b6c-47b7-9644-75e388ece60c",
|
||
"name": "Подготовка переменных",
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
5888,
|
||
672
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"url": "http://n8n:5678/api/v1/tags",
|
||
"authentication": "genericCredentialType",
|
||
"genericAuthType": "httpHeaderAuth",
|
||
"options": {}
|
||
},
|
||
"id": "c3de3ce6-7e43-4790-9091-686628db6874",
|
||
"name": "Получить теги n8n",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"typeVersion": 4.2,
|
||
"position": [
|
||
6064,
|
||
672
|
||
],
|
||
"credentials": {
|
||
"httpHeaderAuth": {
|
||
"id": "VcZvOMLi8tCfxhei",
|
||
"name": "Header n8n local"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const tagsResponse = $input.first().json;\nconst allTags = tagsResponse.data || [];\nconst ctx = $('Подготовка переменных').first().json;\nconst requestedTags = ctx.tags;\n\nif (!requestedTags || requestedTags.length === 0) {\n return [{ json: {} }];\n}\n\nconst matched = allTags.filter(tag => requestedTags.includes(tag.name));\nconst notFound = requestedTags.filter(t => !matched.find(m => m.name === t));\n\nif (notFound.length > 0) {\n console.log(`Предупреждение: теги не найдены: ${notFound.join(', ')}`);\n}\n\nif (matched.length === 0) {\n return [{ json: {} }];\n}\n\nreturn matched.map(tag => ({\n json: {\n matchedTagId: tag.id,\n matchedTagName: tag.name,\n }\n}));"
|
||
},
|
||
"id": "7d93f7fd-a2f3-42dc-8b30-0adef4f8d75d",
|
||
"name": "Фильтр тегов",
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
6240,
|
||
672
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"options": {}
|
||
},
|
||
"id": "d240ec1f-37e8-4d7f-bb7b-af498d499847",
|
||
"name": "Цикл по тегам",
|
||
"type": "n8n-nodes-base.splitInBatches",
|
||
"typeVersion": 3,
|
||
"position": [
|
||
6544,
|
||
672
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"filters": {
|
||
"tags": "={{ $json.matchedTagName }}"
|
||
},
|
||
"requestOptions": {}
|
||
},
|
||
"type": "n8n-nodes-base.n8n",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
6544,
|
||
896
|
||
],
|
||
"id": "88466772-d7d2-4270-a523-db27c82436ae",
|
||
"name": "Получить workflow по тегу",
|
||
"alwaysOutputData": true,
|
||
"credentials": {
|
||
"n8nApi": {
|
||
"id": "aLPzwPxLHLLpPJIw",
|
||
"name": "n8n local"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const variables = $('Подготовка переменных').first().json;\nconst projectFolder = variables.projectFolder;\nconst skipWorkflows = variables.skipWorkflows || [];\n\nconst result = $input.all()\n .filter(item => item.json && item.json.name)\n .filter(item => !skipWorkflows.includes(item.json.name))\n .map(item => {\n const workflow = item.json;\n\n const safeName = workflow.name\n .replace(/[<>:\"/\\\\|?*]/g, '')\n .replace(/\\s+/g, '_')\n .trim();\n\n const filePath = `${projectFolder}/${safeName}.json`;\n\n return {\n json: {\n workflowId: workflow.id,\n workflowName: workflow.name,\n filePath,\n projectFolder,\n }\n };\n });\n\nif (result.length === 0) {\n $execution.customData.set('empty', 'true');\n return [{ json: { empty: true } }];\n}\n\nreturn result;"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
6736,
|
||
896
|
||
],
|
||
"id": "c73e0b04-866a-4007-ad7c-07d2693c87b9",
|
||
"name": "Подготовить пути файлов"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "3db8353a-8212-44b5-b5cd-b172abc862ad",
|
||
"leftValue": "={{ $input.all() }}",
|
||
"rightValue": "",
|
||
"operator": {
|
||
"type": "array",
|
||
"operation": "notEmpty",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
6736,
|
||
672
|
||
],
|
||
"id": "f32586e3-2e1f-4899-9cd8-4e1b2a9aba80",
|
||
"name": "Есть workflow?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"compare": "selectedFields",
|
||
"fieldsToCompare": "workflowId",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.removeDuplicates",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
7184,
|
||
672
|
||
],
|
||
"id": "a16c52a5-e26d-496c-8be6-b820c84dae85",
|
||
"name": "Удалить дубликаты"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"command": "=PROJECT_FOLDER=\"{{ $('Подготовка переменных').first().json.projectFolder }}\"\nREMOTE_URL=\"{{ $('Подготовка переменных').first().json.remoteUrl }}\"\nBRANCH=\"{{ $('Подготовка переменных').first().json.branch }}\"\n\nmkdir -p \"$(dirname \"$PROJECT_FOLDER\")\"\n\nif [ -d \"$PROJECT_FOLDER/.git\" ]; then\n cd \"$PROJECT_FOLDER\"\n git remote set-url origin \"$REMOTE_URL\"\n git fetch origin 2>/dev/null || true\n BRANCH_REMOTE=$(git branch -r --list \"origin/$BRANCH\")\n if git branch --list \"$BRANCH\" | grep -q \"$BRANCH\"; then\n git checkout \"$BRANCH\"\n if [ -n \"$BRANCH_REMOTE\" ]; then\n git reset --hard \"origin/$BRANCH\"\n fi\n elif [ -n \"$BRANCH_REMOTE\" ]; then\n git checkout -b \"$BRANCH\" \"origin/$BRANCH\"\n else\n git checkout -b \"$BRANCH\"\n fi\nelse\n BRANCH_REMOTE_CHECK=$(git ls-remote --heads \"$REMOTE_URL\" \"$BRANCH\" 2>/dev/null | wc -l)\n if [ \"$BRANCH_REMOTE_CHECK\" -gt \"0\" ]; then\n git clone --branch \"$BRANCH\" \"$REMOTE_URL\" \"$PROJECT_FOLDER\"\n cd \"$PROJECT_FOLDER\"\n else\n git clone \"$REMOTE_URL\" \"$PROJECT_FOLDER\" 2>/dev/null || git init \"$PROJECT_FOLDER\"\n cd \"$PROJECT_FOLDER\"\n git remote add origin \"$REMOTE_URL\" 2>/dev/null || git remote set-url origin \"$REMOTE_URL\"\n git checkout -b \"$BRANCH\" 2>/dev/null || git checkout \"$BRANCH\"\n fi\nfi\n\ncd \"$PROJECT_FOLDER\"\ngit config user.email \"n8n@automation.local\"\ngit config user.name \"n8n Automation\"\n\necho \"READY: $BRANCH | $PROJECT_FOLDER\""
|
||
},
|
||
"id": "449d595a-321d-4f16-a8f6-51ca21a0146b",
|
||
"name": "Git Init / Переключить ветку",
|
||
"type": "n8n-nodes-base.executeCommand",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
7376,
|
||
672
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"mode": "combine",
|
||
"combineBy": "combineAll",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.merge",
|
||
"typeVersion": 3.2,
|
||
"position": [
|
||
7568,
|
||
672
|
||
],
|
||
"id": "309246c1-b4eb-47c9-84ca-feabd2ecabd4",
|
||
"name": "Ожидать Git Init"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"options": {}
|
||
},
|
||
"id": "b6fb588d-40ae-4b47-935a-a170aeed1e96",
|
||
"name": "Цикл по workflow",
|
||
"type": "n8n-nodes-base.splitInBatches",
|
||
"typeVersion": 3,
|
||
"position": [
|
||
8032,
|
||
672
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"command": "=mkdir -p \"{{ $json.projectFolder }}\"\nn8n export:workflow --id=\"{{ $json.workflowId }}\" --pretty --output=\"{{ $json.filePath }}\"\necho \"EXPORTED: {{ $json.filePath }}\""
|
||
},
|
||
"type": "n8n-nodes-base.executeCommand",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
8208,
|
||
896
|
||
],
|
||
"id": "628246e2-0dfc-4558-a048-9cfb2c420bbe",
|
||
"name": "Записать файл"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"command": "=cd \"{{ $('Подготовка переменных').first().json.projectFolder }}\"\n\ngit add -A\n\n# Сравниваем с последним коммитом на текущей ветке\n# Если коммитов нет (новая ветка) — считаем что всё новое\nif git rev-parse HEAD 2>/dev/null; then\n if git diff --cached HEAD --quiet; then\n echo \"NO_CHANGES\"\n else\n echo \"HAS_CHANGES\"\n fi\nelse\n # Нет коммитов — новая ветка, всё новое\n if git diff --cached --quiet; then\n echo \"NO_CHANGES\"\n else\n echo \"HAS_CHANGES\"\n fi\nfi"
|
||
},
|
||
"type": "n8n-nodes-base.executeCommand",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
8496,
|
||
672
|
||
],
|
||
"id": "3a621797-0ce2-426e-b789-b5049c656583",
|
||
"name": "Проверить изменения git"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "4673d9eb-d1ff-42e0-a42d-fa377b99dd93",
|
||
"leftValue": "={{ $json.stdout.trim() }}",
|
||
"rightValue": "HAS_CHANGES",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "contains"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
8688,
|
||
672
|
||
],
|
||
"id": "9831a213-3ccd-4d14-bbea-82a38083ca0b",
|
||
"name": "Есть изменения?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"command": "=cd \"{{ $('Подготовка переменных').first().json.projectFolder }}\"\nCOMMIT_MSG=\"{{ $('Подготовка переменных').first().json.commitMessage }}\"\n\ngit add -A\n\nif git diff --cached --quiet; then\n echo \"NO_CHANGES\"\nelse\n git commit -m \"$COMMIT_MSG\"\n echo \"COMMITTED\"\nfi"
|
||
},
|
||
"id": "1b6c77fa-fbe7-4eba-8a27-da23b2bbdf5b",
|
||
"name": "Git Commit",
|
||
"type": "n8n-nodes-base.executeCommand",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
8896,
|
||
672
|
||
]
|
||
},
|
||
{
|
||
"parameters": {
|
||
"command": "=cd \"{{ $('Подготовка переменных').first().json.projectFolder }}\"\nBRANCH=\"{{ $('Подготовка переменных').first().json.branch }}\"\ngit push origin \"$BRANCH\" 2>&1 || git push --set-upstream origin \"$BRANCH\" 2>&1 || true"
|
||
},
|
||
"type": "n8n-nodes-base.executeCommand",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
9072,
|
||
672
|
||
],
|
||
"id": "b8fc50b7-4315-4c9d-8c53-ebfacf3375b3",
|
||
"name": "Git Push"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"command": "=cd \"{{ $('Подготовка переменных').first().json.projectFolder }}\"\nCLEAN_URL=\"{{ $('Подготовка переменных').first().json.cleanRemoteUrl }}\"\n\ngit remote set-url origin \"$CLEAN_URL\"\n\necho \"REMOTE_CLEANED: $CLEAN_URL\""
|
||
},
|
||
"type": "n8n-nodes-base.executeCommand",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
9440,
|
||
672
|
||
],
|
||
"id": "50523ba5-067b-46da-beb6-dee37d34d9cf",
|
||
"name": "Очистить Remote от кредов"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "completion",
|
||
"completionTitle": "Workflow не найдены",
|
||
"completionMessage": "По указанным тегам workflow не найдено. Проверьте теги и попробуйте снова",
|
||
"limitWaitTime": true,
|
||
"resumeUnit": "minutes",
|
||
"options": {
|
||
"customCss": ":root {\n --font-size-body: 13px;\n --font-size-label: 13px;\n --font-size-test-notice: 12px;\n --font-size-input: 13px;\n --font-size-header: 20px;\n --font-size-paragraph: 13px;\n --font-size-link: 12px;\n --font-size-error: 12px;\n --font-size-html-h1: 28px;\n --font-size-html-h2: 20px;\n --font-size-html-h3: 16px;\n --font-size-html-h4: 13px;\n --font-size-html-h5: 12px;\n --font-size-html-h6: 11px;\n --font-size-subheader: 13px;\n --color-background: #1b1f23;\n --color-test-notice-text: #d29922;\n --color-test-notice-bg: #272115;\n --color-test-notice-border: #5a4010;\n --color-card-bg: #22272e;\n --color-card-border: #373e47;\n --color-card-shadow: rgba(0,0,0,0.4);\n --color-link: #539bf5;\n --color-html-link: #539bf5;\n --color-header: #cdd9e5;\n --color-header-subtext: #768390;\n --color-label: #adbac7;\n --color-input-border: #444c56;\n --color-input-text: #cdd9e5;\n --color-input-bg: #1c2128;\n --color-focus-border: #539bf5;\n --color-submit-btn-bg: #347d39;\n --color-submit-btn-text: #cdd9e5;\n --color-error: #e5534b;\n --color-required: #e5534b;\n --color-clear-button-bg: #636e7b;\n --color-html-text: #adbac7;\n --border-radius-card: 6px;\n --border-radius-input: 6px;\n --border-radius-clear-btn: 50%;\n --card-border-radius: 6px;\n --padding-container-top: 24px;\n --padding-card: 24px;\n --padding-test-notice-vertical: 10px;\n --padding-test-notice-horizontal: 16px;\n --margin-bottom-card: 16px;\n --padding-form-input: 12px;\n --card-padding: 24px;\n --card-margin-bottom: 16px;\n --container-width: 448px;\n --submit-btn-height: 44px;\n --checkbox-size: 16px;\n --box-shadow-card: 0px 4px 16px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03);\n --opacity-placeholder: 0.35;\n}"
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.form",
|
||
"typeVersion": 2.5,
|
||
"position": [
|
||
6928,
|
||
896
|
||
],
|
||
"id": "28066c34-7c3f-4c2a-ad69-7f3ee1fbd6d0",
|
||
"name": "Форма: Workflow не найдены",
|
||
"webhookId": "6f77d74e-f7a4-443e-a8c7-7490cecf8ca9"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "completion",
|
||
"completionTitle": "Всё актуально",
|
||
"completionMessage": "Workflow не изменились с последнего коммита",
|
||
"limitWaitTime": true,
|
||
"resumeUnit": "minutes",
|
||
"options": {
|
||
"customCss": ":root {\n --font-size-body: 13px;\n --font-size-label: 13px;\n --font-size-test-notice: 12px;\n --font-size-input: 13px;\n --font-size-header: 20px;\n --font-size-paragraph: 13px;\n --font-size-link: 12px;\n --font-size-error: 12px;\n --font-size-html-h1: 28px;\n --font-size-html-h2: 20px;\n --font-size-html-h3: 16px;\n --font-size-html-h4: 13px;\n --font-size-html-h5: 12px;\n --font-size-html-h6: 11px;\n --font-size-subheader: 13px;\n --color-background: #1b1f23;\n --color-test-notice-text: #d29922;\n --color-test-notice-bg: #272115;\n --color-test-notice-border: #5a4010;\n --color-card-bg: #22272e;\n --color-card-border: #373e47;\n --color-card-shadow: rgba(0,0,0,0.4);\n --color-link: #539bf5;\n --color-html-link: #539bf5;\n --color-header: #cdd9e5;\n --color-header-subtext: #768390;\n --color-label: #adbac7;\n --color-input-border: #444c56;\n --color-input-text: #cdd9e5;\n --color-input-bg: #1c2128;\n --color-focus-border: #539bf5;\n --color-submit-btn-bg: #347d39;\n --color-submit-btn-text: #cdd9e5;\n --color-error: #e5534b;\n --color-required: #e5534b;\n --color-clear-button-bg: #636e7b;\n --color-html-text: #adbac7;\n --border-radius-card: 6px;\n --border-radius-input: 6px;\n --border-radius-clear-btn: 50%;\n --card-border-radius: 6px;\n --padding-container-top: 24px;\n --padding-card: 24px;\n --padding-test-notice-vertical: 10px;\n --padding-test-notice-horizontal: 16px;\n --margin-bottom-card: 16px;\n --padding-form-input: 12px;\n --card-padding: 24px;\n --card-margin-bottom: 16px;\n --container-width: 448px;\n --submit-btn-height: 44px;\n --checkbox-size: 16px;\n --box-shadow-card: 0px 4px 16px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03);\n --opacity-placeholder: 0.35;\n}"
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.form",
|
||
"typeVersion": 2.5,
|
||
"position": [
|
||
8896,
|
||
896
|
||
],
|
||
"id": "e6a68407-6ccc-447d-bc68-b8f6785704b3",
|
||
"name": "Форма: Нет изменений",
|
||
"webhookId": "b2c3d4e5-f6a7-8901-bcde-f01234567890"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "completion",
|
||
"completionTitle": "Готово",
|
||
"completionMessage": "=Workflow успешно экспортированы и отправлены в репозиторий\n\nВетка: {{ $('Подготовка переменных').first().json.branch }}\nРепозиторий: {{ $('Подготовка переменных').first().json.org }}/{{ $('Подготовка переменных').first().json.repoName }}\nКоммит: {{ $('Подготовка переменных').first().json.commitMessage }}\n",
|
||
"limitWaitTime": true,
|
||
"resumeUnit": "minutes",
|
||
"options": {
|
||
"customCss": ":root {\n --font-size-body: 13px;\n --font-size-label: 13px;\n --font-size-test-notice: 12px;\n --font-size-input: 13px;\n --font-size-header: 20px;\n --font-size-paragraph: 13px;\n --font-size-link: 12px;\n --font-size-error: 12px;\n --font-size-html-h1: 28px;\n --font-size-html-h2: 20px;\n --font-size-html-h3: 16px;\n --font-size-html-h4: 13px;\n --font-size-html-h5: 12px;\n --font-size-html-h6: 11px;\n --font-size-subheader: 13px;\n --color-background: #1b1f23;\n --color-test-notice-text: #d29922;\n --color-test-notice-bg: #272115;\n --color-test-notice-border: #5a4010;\n --color-card-bg: #22272e;\n --color-card-border: #373e47;\n --color-card-shadow: rgba(0,0,0,0.4);\n --color-link: #539bf5;\n --color-html-link: #539bf5;\n --color-header: #cdd9e5;\n --color-header-subtext: #768390;\n --color-label: #adbac7;\n --color-input-border: #444c56;\n --color-input-text: #cdd9e5;\n --color-input-bg: #1c2128;\n --color-focus-border: #539bf5;\n --color-submit-btn-bg: #347d39;\n --color-submit-btn-text: #cdd9e5;\n --color-error: #e5534b;\n --color-required: #e5534b;\n --color-clear-button-bg: #636e7b;\n --color-html-text: #adbac7;\n --border-radius-card: 6px;\n --border-radius-input: 6px;\n --border-radius-clear-btn: 50%;\n --card-border-radius: 6px;\n --padding-container-top: 24px;\n --padding-card: 24px;\n --padding-test-notice-vertical: 10px;\n --padding-test-notice-horizontal: 16px;\n --margin-bottom-card: 16px;\n --padding-form-input: 12px;\n --card-padding: 24px;\n --card-margin-bottom: 16px;\n --container-width: 448px;\n --submit-btn-height: 44px;\n --checkbox-size: 16px;\n --box-shadow-card: 0px 4px 16px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03);\n --opacity-placeholder: 0.35;\n}"
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.form",
|
||
"typeVersion": 2.5,
|
||
"position": [
|
||
9616,
|
||
672
|
||
],
|
||
"id": "133dd868-3197-4ac9-a0b9-2f2ca41b9521",
|
||
"name": "Форма: Успех",
|
||
"webhookId": "c3d4e5f6-a7b8-9012-cdef-012345678901"
|
||
},
|
||
{
|
||
"parameters": {},
|
||
"type": "n8n-nodes-base.manualTrigger",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
5328,
|
||
896
|
||
],
|
||
"id": "f8fe696e-8b61-4321-865d-4c9b2b8b30f1",
|
||
"name": "Ручной запуск (отладка)"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"mode": "raw",
|
||
"jsonOutput": "{\n \"Username\": \"gitea\",\n \"Personal Access Token\": \"0f276ee359b823464680037cb9b62f3558796f95\",\n \"Repository URL\": \"http://192.168.1.155:3001\",\n \"Repository Name\": \"workflows\",\n \"Organization\": \"event_forge\",\n \"Branch Type\": \"main\",\n \"Custom Branch Name\": \"\",\n \"Commit Type\": \"feat: Новая функциональность\",\n \"Commit Message\": \"Тестовый коммит\",\n \"Выгрузить все workflow\": false,\n \"Tags (через запятую)\": \"metric,test-git\",\n \"Export Folder\": \"/data/shared\",\n \"submittedAt\": \"2026-04-09T17:24:20.346+03:00\",\n \"formMode\": \"test\"\n}",
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.set",
|
||
"typeVersion": 3.4,
|
||
"position": [
|
||
5520,
|
||
896
|
||
],
|
||
"id": "43ee0bee-f5ea-4841-b840-674a12ed31c6",
|
||
"name": "Тестовые данные"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const fs = require('fs');\nconst path = require('path');\n\nconst variables = $('Подготовка переменных').first().json;\nconst projectFolder = variables.projectFolder;\n\n// Собираем имена файлов которые должны быть\nconst expectedFiles = new Set(\n $input.all()\n .filter(i => i.json && i.json.filePath)\n .map(i => path.basename(i.json.filePath))\n);\n\n// Удаляем лишние файлы (workflow были удалены в n8n)\nlet deleted = [];\nif (fs.existsSync(projectFolder)) {\n const existing = fs.readdirSync(projectFolder).filter(f => f.endsWith('.json'));\n for (const file of existing) {\n if (!expectedFiles.has(file)) {\n fs.unlinkSync(path.join(projectFolder, file));\n deleted.push(file);\n console.log(`DELETED (removed workflow): ${file}`);\n }\n }\n}\n\nconsole.log(`Sync: expected=${expectedFiles.size}, deleted=${deleted.length}`);\nreturn $input.all();"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
7840,
|
||
672
|
||
],
|
||
"id": "90ae57be-52a6-400e-9dc1-80a171e2d381",
|
||
"name": "Синхронизировать удалённые файлы"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "b1c2d3e4-f5a6-7890-abcd-123456789abc",
|
||
"leftValue": "={{ $('Подготовка переменных').item.json.exportAll }}",
|
||
"rightValue": true,
|
||
"operator": {
|
||
"type": "boolean",
|
||
"operation": "true",
|
||
"singleValue": true
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
5888,
|
||
896
|
||
],
|
||
"id": "bdca851d-c812-480b-894e-fb2a81a7b440",
|
||
"name": "Выгрузить все?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"filters": {},
|
||
"requestOptions": {}
|
||
},
|
||
"type": "n8n-nodes-base.n8n",
|
||
"typeVersion": 1,
|
||
"position": [
|
||
6064,
|
||
896
|
||
],
|
||
"id": "fe828c0d-cad0-4137-a4bc-e3b5a2e6ac30",
|
||
"name": "Получить все workflow",
|
||
"alwaysOutputData": true,
|
||
"credentials": {
|
||
"n8nApi": {
|
||
"id": "aLPzwPxLHLLpPJIw",
|
||
"name": "n8n local"
|
||
}
|
||
}
|
||
},
|
||
{
|
||
"parameters": {
|
||
"jsCode": "const variables = $('Подготовка переменных').first().json;\nconst projectFolder = variables.projectFolder;\nconst skipWorkflows = variables.skipWorkflows || [];\n\nconst result = $input.all()\n .filter(item => item.json && item.json.name)\n .filter(item => !skipWorkflows.includes(item.json.name))\n .map(item => {\n const workflow = item.json;\n\n const safeName = workflow.name\n .replace(/[<>:\"/\\\\|?*]/g, '')\n .replace(/\\s+/g, '_')\n .trim();\n\n const filePath = `${projectFolder}/${safeName}.json`;\n\n return {\n json: {\n workflowId: workflow.id,\n workflowName: workflow.name,\n filePath,\n projectFolder,\n }\n };\n });\n\nif (result.length === 0) {\n $execution.customData.set('empty', 'true');\n return [{ json: { empty: true } }];\n}\n\nreturn result;"
|
||
},
|
||
"type": "n8n-nodes-base.code",
|
||
"typeVersion": 2,
|
||
"position": [
|
||
6240,
|
||
896
|
||
],
|
||
"id": "08150d3b-066f-4dc1-ba57-221475c5fe63",
|
||
"name": "Подготовить пути (все)"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {
|
||
"caseSensitive": true,
|
||
"leftValue": "",
|
||
"typeValidation": "strict",
|
||
"version": 3
|
||
},
|
||
"conditions": [
|
||
{
|
||
"id": "f22669db-3bcc-4732-9506-b90b63a81ee3",
|
||
"leftValue": "={{ $json.stdout }}",
|
||
"rightValue": "fatal",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "notContains"
|
||
}
|
||
},
|
||
{
|
||
"id": "ceeb9c23-47a8-4d5e-9ddb-0960fe4f59e4",
|
||
"leftValue": "={{ $json.stdout }}",
|
||
"rightValue": "error",
|
||
"operator": {
|
||
"type": "string",
|
||
"operation": "notContains"
|
||
}
|
||
}
|
||
],
|
||
"combinator": "and"
|
||
},
|
||
"options": {}
|
||
},
|
||
"type": "n8n-nodes-base.if",
|
||
"typeVersion": 2.3,
|
||
"position": [
|
||
9248,
|
||
672
|
||
],
|
||
"id": "cef87e93-feac-4ac2-9f97-8e606fb8be15",
|
||
"name": "Успешный Push?"
|
||
},
|
||
{
|
||
"parameters": {
|
||
"operation": "completion",
|
||
"completionTitle": "Ошибка push",
|
||
"completionMessage": "=Произошла ошибка при push: {{ $json.stdout }}",
|
||
"limitWaitTime": true,
|
||
"resumeUnit": "minutes",
|
||
"options": {
|
||
"customCss": ":root {\n --font-size-body: 13px;\n --font-size-label: 13px;\n --font-size-test-notice: 12px;\n --font-size-input: 13px;\n --font-size-header: 20px;\n --font-size-paragraph: 13px;\n --font-size-link: 12px;\n --font-size-error: 12px;\n --font-size-html-h1: 28px;\n --font-size-html-h2: 20px;\n --font-size-html-h3: 16px;\n --font-size-html-h4: 13px;\n --font-size-html-h5: 12px;\n --font-size-html-h6: 11px;\n --font-size-subheader: 13px;\n --color-background: #1b1f23;\n --color-test-notice-text: #d29922;\n --color-test-notice-bg: #272115;\n --color-test-notice-border: #5a4010;\n --color-card-bg: #22272e;\n --color-card-border: #373e47;\n --color-card-shadow: rgba(0,0,0,0.4);\n --color-link: #539bf5;\n --color-html-link: #539bf5;\n --color-header: #cdd9e5;\n --color-header-subtext: #768390;\n --color-label: #adbac7;\n --color-input-border: #444c56;\n --color-input-text: #cdd9e5;\n --color-input-bg: #1c2128;\n --color-focus-border: #539bf5;\n --color-submit-btn-bg: #347d39;\n --color-submit-btn-text: #cdd9e5;\n --color-error: #e5534b;\n --color-required: #e5534b;\n --color-clear-button-bg: #636e7b;\n --color-html-text: #adbac7;\n --border-radius-card: 6px;\n --border-radius-input: 6px;\n --border-radius-clear-btn: 50%;\n --card-border-radius: 6px;\n --padding-container-top: 24px;\n --padding-card: 24px;\n --padding-test-notice-vertical: 10px;\n --padding-test-notice-horizontal: 16px;\n --margin-bottom-card: 16px;\n --padding-form-input: 12px;\n --card-padding: 24px;\n --card-margin-bottom: 16px;\n --container-width: 448px;\n --submit-btn-height: 44px;\n --checkbox-size: 16px;\n --box-shadow-card: 0px 4px 16px rgba(0,0,0,0.4), 0 0 0 1px rgba(255,255,255,0.03);\n --opacity-placeholder: 0.35;\n}"
|
||
}
|
||
},
|
||
"type": "n8n-nodes-base.form",
|
||
"typeVersion": 2.5,
|
||
"position": [
|
||
9440,
|
||
896
|
||
],
|
||
"id": "edec2ba9-6272-43b4-be06-51b2f47ed273",
|
||
"name": "Форма: Ошибка push",
|
||
"webhookId": "b2c3d4e5-f6a7-8901-bcde-f01234567890"
|
||
}
|
||
],
|
||
"pinData": {},
|
||
"connections": {
|
||
"Форма запроса": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Подготовка переменных",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Ручной запуск (отладка)": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Тестовые данные",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Тестовые данные": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Подготовка переменных",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Подготовка переменных": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Получить теги n8n",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Получить теги n8n": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Выгрузить все?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Фильтр тегов": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Цикл по тегам",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Цикл по тегам": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Есть workflow?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Получить workflow по тегу",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Получить workflow по тегу": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Подготовить пути файлов",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Подготовить пути файлов": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Цикл по тегам",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Есть workflow?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Удалить дубликаты",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Форма: Workflow не найдены",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Удалить дубликаты": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Ожидать Git Init",
|
||
"type": "main",
|
||
"index": 1
|
||
},
|
||
{
|
||
"node": "Git Init / Переключить ветку",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Git Init / Переключить ветку": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Ожидать Git Init",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Ожидать Git Init": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Синхронизировать удалённые файлы",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Цикл по workflow": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Проверить изменения git",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Записать файл",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Записать файл": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Цикл по workflow",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Проверить изменения git": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Есть изменения?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Есть изменения?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Git Commit",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Форма: Нет изменений",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Git Commit": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Git Push",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Git Push": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Успешный Push?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Очистить Remote от кредов": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Форма: Успех",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Синхронизировать удалённые файлы": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Цикл по workflow",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Выгрузить все?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Получить все workflow",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Фильтр тегов",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Получить все workflow": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Подготовить пути (все)",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Подготовить пути (все)": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Есть workflow?",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
},
|
||
"Успешный Push?": {
|
||
"main": [
|
||
[
|
||
{
|
||
"node": "Очистить Remote от кредов",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
],
|
||
[
|
||
{
|
||
"node": "Форма: Ошибка push",
|
||
"type": "main",
|
||
"index": 0
|
||
}
|
||
]
|
||
]
|
||
}
|
||
},
|
||
"active": false,
|
||
"settings": {
|
||
"executionOrder": "v1",
|
||
"binaryMode": "separate"
|
||
},
|
||
"versionId": "c3caa6fa-12a3-414f-91c3-74497aba7106",
|
||
"meta": {
|
||
"instanceId": "e5d2d5dcf3b84baf4a3229099b56ef256963029b1ff1917093a2e26870e31e8f"
|
||
},
|
||
"id": "1K7zE39Cw01WwP8O",
|
||
"tags": []
|
||
} |