Automatisation GitHub avec n8n : gestion de fichiers en temps réel
Ce workflow n8n permet d'automatiser la gestion des fichiers sur GitHub, en facilitant la création et l'édition de fichiers selon leur statut. Dans un contexte où la collaboration et la mise à jour des documents sont essentielles, ce workflow s'adresse aux équipes de développement et aux professionnels qui souhaitent optimiser leur flux de travail. Par exemple, il peut être utilisé pour synchroniser des fichiers de projet ou pour gérer des mises à jour de documentation. Le flux commence par un déclencheur manuel, permettant à l'utilisateur de lancer le processus à tout moment. Ensuite, le workflow récupère un fichier depuis GitHub via une requête HTTP. Si le fichier est trop volumineux, il passe par une condition pour gérer ce cas. Les fichiers sont ensuite analysés pour déterminer s'ils sont nouveaux ou modifiés, ce qui permet de décider s'il faut créer un nouveau fichier ou éditer un fichier existant. En utilisant des noeuds comme 'Merge Items' et 'Execute Command', le workflow assure une gestion efficace des données. Les bénéfices incluent une réduction significative du temps passé sur la gestion manuelle des fichiers, une meilleure collaboration entre les équipes et une automatisation des tâches répétitives, ce qui permet aux utilisateurs de se concentrer sur des tâches à plus forte valeur ajoutée.
Workflow n8n GitHub, gestion de fichiers : vue d'ensemble
Schéma des nœuds et connexions de ce workflow n8n, généré à partir du JSON n8n.
Workflow n8n GitHub, gestion de fichiers : détail des nœuds
Inscris-toi pour voir l'intégralité du workflow
Inscription gratuite
S'inscrire gratuitementBesoin d'aide ?{
"meta": {
"instanceId": "d6b502dfa4d9dd072cdc5c2bb763558661053f651289291352a84403e01b3d1b",
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "4377c764-07f3-4304-8105-d3f009925917",
"name": "On clicking 'execute'",
"type": "n8n-nodes-base.manualTrigger",
"position": [
1780,
520
],
"parameters": {},
"typeVersion": 1
},
{
"id": "10f6ea70-c2cb-4463-972c-e2fdef3e837a",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1339.5461279763795,
900
],
"parameters": {
"color": 6,
"width": 2086.845881354743,
"height": 750.8363163824032,
"content": "## Subworkflow"
},
"typeVersion": 1
},
{
"id": "d22236c2-578c-400b-b3e5-354498620c39",
"name": "Return",
"type": "n8n-nodes-base.set",
"position": [
3220,
1100
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "8d513345-6484-431f-afb7-7cf045c90f4f",
"name": "Done",
"type": "boolean",
"value": true
}
]
}
},
"typeVersion": 3.3
},
{
"id": "943eed85-d4cd-4ec5-b278-d143b0f6bd15",
"name": "Get File",
"type": "n8n-nodes-base.httpRequest",
"position": [
2320,
980
],
"parameters": {
"url": "={{ $json.download_url }}",
"options": {}
},
"typeVersion": 4.2
},
{
"id": "124ebdd7-c2c1-4fec-89d3-596f034e0fe7",
"name": "If file too large",
"type": "n8n-nodes-base.if",
"position": [
2120,
1000
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 1,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "45ce825e-9fa6-430c-8931-9aaf22c42585",
"operator": {
"type": "string",
"operation": "empty",
"singleValue": true
},
"leftValue": "={{ $json.content }}",
"rightValue": ""
},
{
"id": "9619a55f-7fb1-4f24-b1a7-7aeb82365806",
"operator": {
"type": "string",
"operation": "notExists",
"singleValue": true
},
"leftValue": "={{ $json.error }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "751621b4-4f99-4178-a691-40fc7488874b",
"name": "Merge Items",
"type": "n8n-nodes-base.merge",
"position": [
2120,
1260
],
"parameters": {},
"typeVersion": 2
},
{
"id": "8892eb02-0e8e-4617-85e6-e6f188361f95",
"name": "isDiffOrNew",
"type": "n8n-nodes-base.code",
"position": [
2320,
1260
],
"parameters": {
"jsCode": "const orderJsonKeys = (jsonObj) => {\n const ordered = {};\n Object.keys(jsonObj).sort().forEach(key => {\n ordered[key] = jsonObj[key];\n });\n return ordered;\n}\n\n// Check if file returned with content\nif (Object.keys($input.all()[0].json).includes(\"content\")) {\n // Decode base64 content and parse JSON\n const origWorkflow = JSON.parse(Buffer.from($input.all()[0].json.content, 'base64').toString());\n const n8nWorkflow = $input.all()[1].json;\n \n // Order JSON objects\n const orderedOriginal = orderJsonKeys(origWorkflow);\n const orderedActual = orderJsonKeys(n8nWorkflow);\n\n // Determine difference\n if (JSON.stringify(orderedOriginal) === JSON.stringify(orderedActual)) {\n $input.all()[0].json.github_status = \"same\";\n } else {\n $input.all()[0].json.github_status = \"different\";\n $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n }\n $input.all()[0].json.content_decoded = orderedOriginal;\n// No file returned / new workflow\n} else if (Object.keys($input.all()[0].json).includes(\"data\")) {\n const origWorkflow = JSON.parse($input.all()[0].json.data);\n const n8nWorkflow = $input.all()[1].json;\n \n // Order JSON objects\n const orderedOriginal = orderJsonKeys(origWorkflow);\n const orderedActual = orderJsonKeys(n8nWorkflow);\n\n // Determine difference\n if (JSON.stringify(orderedOriginal) === JSON.stringify(orderedActual)) {\n $input.all()[0].json.github_status = \"same\";\n } else {\n $input.all()[0].json.github_status = \"different\";\n $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n }\n $input.all()[0].json.content_decoded = orderedOriginal;\n\n} else {\n // Order JSON object\n const n8nWorkflow = $input.all()[1].json;\n const orderedActual = orderJsonKeys(n8nWorkflow);\n \n // Proper formatting\n $input.all()[0].json.github_status = \"new\";\n $input.all()[0].json.n8n_data_stringy = JSON.stringify(orderedActual, null, 2);\n}\n\n// Return items\nreturn $input.all();"
},
"typeVersion": 1
},
{
"id": "bfddb2a2-c149-4710-bd77-b368d641114d",
"name": "Check Status",
"type": "n8n-nodes-base.switch",
"position": [
2540,
1260
],
"parameters": {
"rules": {
"rules": [
{
"value2": "same"
},
{
"output": 1,
"value2": "different"
},
{
"output": 2,
"value2": "new"
}
]
},
"value1": "={{$json.github_status}}",
"dataType": "string"
},
"typeVersion": 1
},
{
"id": "681e54af-b916-416d-9801-ac38a5882bcf",
"name": "Same file - Do nothing",
"type": "n8n-nodes-base.noOp",
"position": [
2760,
1100
],
"parameters": {},
"typeVersion": 1
},
{
"id": "38b2041d-1d56-436f-aa04-79d7241dcc74",
"name": "File is different",
"type": "n8n-nodes-base.noOp",
"position": [
2760,
1260
],
"parameters": {},
"typeVersion": 1
},
{
"id": "ae33280d-10d5-4882-be9b-7972394357e1",
"name": "File is new",
"type": "n8n-nodes-base.noOp",
"position": [
2760,
1420
],
"parameters": {},
"typeVersion": 1
},
{
"id": "bea3995f-9f34-4119-a6cf-20281e70d685",
"name": "Create new file",
"type": "n8n-nodes-base.github",
"position": [
2980,
1420
],
"parameters": {
"owner": {
"__rl": true,
"mode": "name",
"value": "={{ $('Globals').item.json.repo.owner }}"
},
"filePath": "={{ $('Globals').item.json.repo.path }}{{$('Execute Workflow Trigger').first().json.id}}.json",
"resource": "file",
"repository": {
"__rl": true,
"mode": "name",
"value": "={{ $('Globals').item.json.repo.name }}"
},
"fileContent": "={{$('isDiffOrNew').item.json[\"n8n_data_stringy\"]}}",
"commitMessage": "={{$('Execute Workflow Trigger').first().json.name}} ({{$json.github_status}})"
},
"credentials": {
"githubApi": {
"id": "3mfzXcMjoqNHsujs",
"name": "GitHub account"
}
},
"typeVersion": 1
},
{
"id": "d9172af3-55f8-4b99-b462-3e6e718b5a77",
"name": "Edit existing file",
"type": "n8n-nodes-base.github",
"position": [
2980,
1240
],
"parameters": {
"owner": {
"__rl": true,
"mode": "name",
"value": "={{ $('Globals').item.json.repo.owner }}"
},
"filePath": "={{ $('Globals').item.json.repo.path }}{{$('Execute Workflow Trigger').first().json.id}}.json",
"resource": "file",
"operation": "edit",
"repository": {
"__rl": true,
"mode": "name",
"value": "={{ $('Globals').item.json.repo.name }}"
},
"fileContent": "={{$('isDiffOrNew').item.json[\"n8n_data_stringy\"]}}",
"commitMessage": "={{$('Execute Workflow Trigger').first().json.name}} ({{$json.github_status}})"
},
"credentials": {
"githubApi": {
"id": "3mfzXcMjoqNHsujs",
"name": "GitHub account"
}
},
"typeVersion": 1
},
{
"id": "d9589e32-ed20-46e7-9427-1680c6222406",
"name": "Loop Over Items",
"type": "n8n-nodes-base.splitInBatches",
"position": [
2380,
620
],
"parameters": {
"options": {}
},
"typeVersion": 3
},
{
"id": "e1530650-aa76-4ab3-b5bb-cd6b805ea656",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
1780,
720
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 2
}
]
}
},
"typeVersion": 1.2
},
{
"id": "79910589-f40f-46fa-a704-eaa65157a17a",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
1340,
278.28654385738866
],
"parameters": {
"color": 4,
"width": 365.19481715599653,
"height": 596.4810912485963,
"content": "## Backup to GitHub \nThis workflow will backup all instance credentials to GitHub.\n\nThe files are saved `ID.json` for the filename.\n\n### Setup\nOpen `Globals` node and update the values below 👇\n\n- **repo.owner:** your Github username\n- **repo.name:** the name of your repository\n- **repo.path:** the folder to use within the repository. If it doesn't exist it will be created.\n\n\nIf your username was `john-doe` and your repository was called `n8n-backups` and you wanted the credentials to go into a `credentials` folder you would set:\n\n- repo.owner - john-doe\n- repo.name - n8n-backups\n- repo.path - credentials/\n\n\nThe workflow calls itself using a subworkflow, to help reduce memory usage."
},
"typeVersion": 1
},
{
"id": "e16c9874-1a35-41c4-8410-0c42efe17770",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
1740,
440
],
"parameters": {
"color": 7,
"width": 1028.7522287279464,
"height": 434.88564057365943,
"content": "## Main workflow loop"
},
"typeVersion": 1
},
{
"id": "a1464b91-516a-4fd9-9235-20de50e74cb2",
"name": "Get file data",
"type": "n8n-nodes-base.github",
"position": [
1920,
1000
],
"parameters": {
"owner": {
"__rl": true,
"mode": "name",
"value": "={{ $json.repo.owner }}"
},
"filePath": "={{ $json.repo.path }}{{ $('Execute Workflow Trigger').item.json.id }}.json",
"resource": "file",
"operation": "get",
"repository": {
"__rl": true,
"mode": "name",
"value": "={{ $json.repo.name }}"
},
"asBinaryProperty": false,
"additionalParameters": {}
},
"credentials": {
"githubApi": {
"id": "3mfzXcMjoqNHsujs",
"name": "GitHub account"
}
},
"typeVersion": 1,
"continueOnFail": true,
"alwaysOutputData": true
},
{
"id": "eb2fe87f-f3af-4215-ac1f-7c2b45e8aff6",
"name": "Globals",
"type": "n8n-nodes-base.set",
"position": [
1720,
1160
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "6cf546c5-5737-4dbd-851b-17d68e0a3780",
"name": "repo.owner",
"type": "string",
"value": "john-doe"
},
{
"id": "452efa28-2dc6-4ea3-a7a2-c35d100d0382",
"name": "repo.name",
"type": "string",
"value": "n8n-backup"
},
{
"id": "81c4dc54-86bf-4432-a23f-22c7ea831e74",
"name": "repo.path",
"type": "string",
"value": "credentials/"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f4498ab4-1760-4849-9fe1-ecfcd7baa9f3",
"name": "Execute Command",
"type": "n8n-nodes-base.executeCommand",
"position": [
2000,
620
],
"parameters": {
"command": "npx n8n export:credentials --all --decrypted"
},
"typeVersion": 1
},
{
"id": "d453a000-40ef-43f5-b108-5eb30422d1a3",
"name": "JSON formatting",
"type": "n8n-nodes-base.code",
"position": [
2180,
620
],
"parameters": {
"jsCode": "// Function to beautify JSON\nfunction beautifyJson(jsonString) {\n try {\n // Parse the JSON string\n const jsonObject = JSON.parse(jsonString);\n\n // Format the JSON with indentation\n return jsonObject; // Return the parsed object directly\n } catch (error) {\n // Return the error message if JSON is invalid\n return `Invalid JSON: ${error.message}`;\n }\n}\n\n// Retrieve the JSON object from the input data\nconst input = $input.all()[0].json;\n\n// Extract the JSON string from the stdout field\nconst jsonString = input.stdout.match(/\\[{.*}\\]/s);\n\n// Check if a valid JSON string is found\nif (!jsonString) {\n return {\n json: {\n error: \"No valid JSON string found in stdout.\"\n }\n };\n}\n\n// Beautify the JSON\nconst beautifiedJson = beautifyJson(jsonString[0]);\n\n// Output the beautified JSON, ensuring each entry is in an object with a 'json' key\nconst output = beautifiedJson.map(entry => ({ json: entry }));\n\n// Return the output\nreturn output;\n"
},
"typeVersion": 2
},
{
"id": "49dbf875-7345-4241-a7fc-f42e53aef64e",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
1680,
1060
],
"parameters": {
"color": 4,
"width": 150,
"height": 80,
"content": "## Edit this node 👇"
},
"typeVersion": 1
},
{
"id": "98158f3e-7aca-456b-994c-4c795d31c18c",
"name": "Execute Workflow",
"type": "n8n-nodes-base.executeWorkflow",
"position": [
2600,
620
],
"parameters": {
"mode": "each",
"options": {},
"workflowId": "={{ $workflow.id }}"
},
"typeVersion": 1
},
{
"id": "d8c52eb7-bcb0-49e7-bb32-7499b1ca22cd",
"name": "Execute Workflow Trigger",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"position": [
1440,
1280
],
"parameters": {
"inputSource": "passthrough"
},
"typeVersion": 1.1
}
],
"pinData": {},
"connections": {
"Globals": {
"main": [
[
{
"node": "Get file data",
"type": "main",
"index": 0
}
]
]
},
"Get File": {
"main": [
[
{
"node": "Merge Items",
"type": "main",
"index": 0
}
]
]
},
"File is new": {
"main": [
[
{
"node": "Create new file",
"type": "main",
"index": 0
}
]
]
},
"Merge Items": {
"main": [
[
{
"node": "isDiffOrNew",
"type": "main",
"index": 0
}
]
]
},
"isDiffOrNew": {
"main": [
[
{
"node": "Check Status",
"type": "main",
"index": 0
}
]
]
},
"Check Status": {
"main": [
[
{
"node": "Same file - Do nothing",
"type": "main",
"index": 0
}
],
[
{
"node": "File is different",
"type": "main",
"index": 0
}
],
[
{
"node": "File is new",
"type": "main",
"index": 0
}
]
]
},
"Get file data": {
"main": [
[
{
"node": "If file too large",
"type": "main",
"index": 0
}
]
]
},
"Create new file": {
"main": [
[
{
"node": "Return",
"type": "main",
"index": 0
}
]
]
},
"Execute Command": {
"main": [
[
{
"node": "JSON formatting",
"type": "main",
"index": 0
}
]
]
},
"JSON formatting": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Loop Over Items": {
"main": [
[],
[
{
"node": "Execute Workflow",
"type": "main",
"index": 0
},
{
"node": "Execute Workflow",
"type": "main",
"index": 0
}
]
]
},
"Execute Workflow": {
"main": [
[
{
"node": "Loop Over Items",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Execute Command",
"type": "main",
"index": 0
}
]
]
},
"File is different": {
"main": [
[
{
"node": "Edit existing file",
"type": "main",
"index": 0
}
]
]
},
"If file too large": {
"main": [
[
{
"node": "Get File",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge Items",
"type": "main",
"index": 0
}
]
]
},
"Edit existing file": {
"main": [
[
{
"node": "Return",
"type": "main",
"index": 0
}
]
]
},
"On clicking 'execute'": {
"main": [
[
{
"node": "Execute Command",
"type": "main",
"index": 0
}
]
]
},
"Same file - Do nothing": {
"main": [
[
{
"node": "Return",
"type": "main",
"index": 0
}
]
]
},
"Execute Workflow Trigger": {
"main": [
[
{
"node": "Globals",
"type": "main",
"index": 0
},
{
"node": "Merge Items",
"type": "main",
"index": 1
}
]
]
}
}
}Workflow n8n GitHub, gestion de fichiers : pour qui est ce workflow ?
Ce workflow s'adresse principalement aux équipes de développement logiciel, aux chefs de projet et aux professionnels de la gestion de contenu qui utilisent GitHub. Il est conçu pour des utilisateurs ayant un niveau technique intermédiaire, souhaitant automatiser la gestion de fichiers dans un environnement collaboratif.
Workflow n8n GitHub, gestion de fichiers : problème résolu
Ce workflow résout le problème de la gestion manuelle des fichiers sur GitHub, qui peut être chronophage et sujet à des erreurs. En automatisant le processus de création et d'édition de fichiers, il élimine les frustrations liées à la synchronisation des documents et réduit les risques d'erreurs humaines. Les utilisateurs bénéficient d'une mise à jour rapide et efficace de leurs fichiers, ce qui améliore la productivité et la collaboration au sein des équipes.
Workflow n8n GitHub, gestion de fichiers : étapes du workflow
Étape 1 : Le processus commence par un déclencheur manuel qui permet à l'utilisateur d'exécuter le workflow.
- Étape 1 : Le workflow effectue une requête HTTP pour récupérer un fichier depuis GitHub.
- Étape 2 : Une condition vérifie si le fichier est trop volumineux.
- Étape 3 : Le workflow analyse le fichier pour déterminer s'il est nouveau ou modifié.
- Étape 4 : Selon le statut du fichier, il crée un nouveau fichier ou édite un fichier existant sur GitHub.
- Étape 5 : Le workflow utilise des noeuds pour exécuter des commandes et formater les données JSON avant de finaliser le processus.
Workflow n8n GitHub, gestion de fichiers : guide de personnalisation
Pour adapter ce workflow, vous pouvez modifier l'URL de la requête HTTP pour pointer vers le dépôt GitHub souhaité. Il est également possible de personnaliser les paramètres des noeuds 'Create new file' et 'Edit existing file' en fonction des besoins spécifiques de votre projet. Pensez à ajuster les conditions dans le noeud 'If file too large' pour gérer les fichiers selon vos critères. Enfin, vous pouvez intégrer d'autres outils ou services en ajoutant des noeuds supplémentaires dans le workflow, tout en veillant à sécuriser les accès via des tokens d'authentification appropriés.