n8n-local-sync 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. n8n_local_sync-0.1.0/.env.example +8 -0
  2. n8n_local_sync-0.1.0/.github/workflows/ci.yml +40 -0
  3. n8n_local_sync-0.1.0/.github/workflows/publish.yml +30 -0
  4. n8n_local_sync-0.1.0/.gitignore +56 -0
  5. n8n_local_sync-0.1.0/.pre-commit-hooks.yaml +7 -0
  6. n8n_local_sync-0.1.0/AGENTS.md +40 -0
  7. n8n_local_sync-0.1.0/LICENSE +21 -0
  8. n8n_local_sync-0.1.0/Makefile +24 -0
  9. n8n_local_sync-0.1.0/PKG-INFO +258 -0
  10. n8n_local_sync-0.1.0/README.md +217 -0
  11. n8n_local_sync-0.1.0/docker-compose.yml +19 -0
  12. n8n_local_sync-0.1.0/n8n/.keep +0 -0
  13. n8n_local_sync-0.1.0/pyproject.toml +46 -0
  14. n8n_local_sync-0.1.0/src/n8n_local_sync/__init__.py +2 -0
  15. n8n_local_sync-0.1.0/src/n8n_local_sync/api.py +93 -0
  16. n8n_local_sync-0.1.0/src/n8n_local_sync/cli.py +185 -0
  17. n8n_local_sync-0.1.0/src/n8n_local_sync/config.py +78 -0
  18. n8n_local_sync-0.1.0/src/n8n_local_sync/diff.py +130 -0
  19. n8n_local_sync-0.1.0/src/n8n_local_sync/export.py +91 -0
  20. n8n_local_sync-0.1.0/src/n8n_local_sync/import_.py +180 -0
  21. n8n_local_sync-0.1.0/src/n8n_local_sync/models.py +18 -0
  22. n8n_local_sync-0.1.0/src/n8n_local_sync/normalization.py +62 -0
  23. n8n_local_sync-0.1.0/src/n8n_local_sync/state.py +70 -0
  24. n8n_local_sync-0.1.0/src/n8n_local_sync/status.py +86 -0
  25. n8n_local_sync-0.1.0/src/n8n_local_sync/sync.py +118 -0
  26. n8n_local_sync-0.1.0/src/n8n_local_sync/validation.py +76 -0
  27. n8n_local_sync-0.1.0/tests/test_api.py +41 -0
  28. n8n_local_sync-0.1.0/tests/test_import.py +73 -0
  29. n8n_local_sync-0.1.0/tests/test_sync.py +70 -0
  30. n8n_local_sync-0.1.0/tests/test_validation.py +59 -0
@@ -0,0 +1,8 @@
1
+ # n8n API Key for authenticating with the n8n REST API
2
+ # You can generate this in your n8n instance under Settings > API
3
+ N8N_API_KEY=your_api_key_here
4
+
5
+ # (Optional) Base URL of your n8n instance.
6
+ # If provided, this overrides the URL specified in .n8n-sync.yaml.
7
+ # Example: http://localhost:5678
8
+ N8N_BASE_URL=
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main" ]
6
+ pull_request:
7
+ branches: [ "main" ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install hatchling pytest ruff build
28
+ pip install -e .[dev]
29
+
30
+ - name: Lint with ruff
31
+ run: |
32
+ ruff check src/ tests/
33
+
34
+ - name: Test with pytest
35
+ run: |
36
+ pytest tests/
37
+
38
+ - name: Build package
39
+ run: |
40
+ python -m build
@@ -0,0 +1,30 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build-and-publish:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write
12
+ contents: read
13
+
14
+ steps:
15
+ - name: Check out repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.10"
22
+
23
+ - name: Install build tool
24
+ run: pip install build
25
+
26
+ - name: Build package
27
+ run: python -m build
28
+
29
+ - name: Publish to PyPI
30
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,56 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # Environments
30
+ .env
31
+ .venv
32
+ env/
33
+ venv/
34
+ ENV/
35
+ env.bak/
36
+ venv.bak/
37
+
38
+ # Project specific
39
+ .n8n/
40
+ database.sqlite
41
+ volumes/
42
+ logs/
43
+ executions/
44
+ credentials/
45
+
46
+ # OS generated files
47
+ .DS_Store
48
+ .DS_Store?
49
+ ._*
50
+ .Spotlight-V100
51
+ .Trashes
52
+ ehthumbs.db
53
+ Thumbs.db
54
+
55
+ n8n/*
56
+ !n8n/.keep
@@ -0,0 +1,7 @@
1
+ - id: validate-n8n-workflows
2
+ name: Validate n8n Workflows
3
+ description: Validates all n8n workflows in the directory to ensure schema correctness and prevents committing embedded secrets.
4
+ entry: n8n-sync validate
5
+ language: python
6
+ types: [json]
7
+ pass_filenames: false
@@ -0,0 +1,40 @@
1
+ # Instruções para Agentes de IA
2
+
3
+ Este documento (AGENTS.md) fornece diretrizes fundamentais para qualquer Agente de IA que venha a interagir com, dar manutenção ou expandir o código deste repositório.
4
+
5
+ ## 1. Objetivo e Escopo
6
+
7
+ O `n8n-local-sync` é uma ferramenta CLI (Command Line Interface) open source.
8
+ * **Propósito:** Versionar, validar, exportar, importar e sincronizar workflows de n8n.
9
+ * **Não é:** Um servidor, uma aplicação web, ou um sistema distribuído complexo. Não faça overengineering.
10
+ * **Foco Inicial:** Interface CLI simples em Python que interaja com o Git e instâncias n8n.
11
+
12
+ ## 2. Arquitetura e Integração com n8n
13
+
14
+ * O `n8n-local-sync` é uma ferramenta externa e independente. Não a embuta dentro da stack Docker do n8n de forma intrusiva (como um sidecar obrigatório).
15
+ * **Comunicação com n8n:** **Antes de utilizar qualquer comando, API, parâmetro ou comportamento específico do n8n, consulte a documentação oficial correspondente à versão fixada.** Utilize preferencialmente a **Public REST API** do n8n (introduzida na versão 0.164.0+) como meio de exportar/importar workflows, requerendo a passagem de API Keys (via `.env`), ao invés de hacks usando a CLI legada local.
16
+ * **Fonte da Verdade:** O Git (arquivos `.json` no repositório) é a única fonte da verdade. O banco de dados do n8n é considerado efêmero em relação aos workflows gerenciados pela ferramenta.
17
+
18
+ ## 3. Padrões e Convenções de Código
19
+
20
+ * **Python:** Utilizar `Typer` para a CLI, `Pydantic` e `PyYAML` para as configurações e validações.
21
+ * **Scripts Shell:** Quando usar bash para automação ou CI, sempre inicie com `#!/usr/bin/env bash` seguido de `set -euo pipefail`. Use `ShellCheck`! Não use `eval`.
22
+ * **Packaging:** Utilize `pyproject.toml` (`hatchling`) para definir metadados do pacote e entrypoints CLI (`n8n-sync`).
23
+
24
+ ## 4. Segurança (Criticamente Importante)
25
+
26
+ * Nunca adicione lógicas que cometam credenciais ou secrets no repositório.
27
+ * Arquivos `.env` devem sempre estar no `.gitignore`.
28
+ * A CLI deve alertar se tentar fazer parsing de um `.json` exportado que contenha credenciais embedded.
29
+
30
+ ## 5. Testes e CI
31
+
32
+ * Python: Utilize `pytest`. Testes unitários para a configuração, parse do JSON e diffing.
33
+ * A integração contínua (GitHub Actions) deve rodar o Pytest e lint (Ruff) antes de autorizar publicações no PyPI.
34
+
35
+ ## 6. Alterações na CLI
36
+
37
+ A CLI é a API primária da ferramenta para o usuário.
38
+ * Mantenha os comandos padronizados: `init`, `validate`, `export`, `import`, `sync`, `status`, `diff`.
39
+ * **Não invente comandos** que não estão planejados, a não ser que extremamente justificado por um roadmap.
40
+ * Garanta códigos de saída (`exit code`) apropriados: `0` para sucesso, não-`0` para erros, o que é fundamental para CI/CD e pre-commit hooks.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel Dias Pereira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ .PHONY: setup test lint format docker-up docker-down build package
2
+
3
+ setup:
4
+ pip install -e ".[dev]"
5
+
6
+ test:
7
+ pytest tests/
8
+
9
+ lint:
10
+ ruff check src/ tests/
11
+
12
+ format:
13
+ ruff format src/ tests/
14
+
15
+ docker-up:
16
+ docker compose up -d
17
+
18
+ docker-down:
19
+ docker compose down
20
+
21
+ build:
22
+ hatch build
23
+
24
+ package: build
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.5
2
+ Name: n8n-local-sync
3
+ Version: 0.1.0
4
+ Summary: A CLI tool to version, validate, export, import, and sync workflows from self-hosted n8n instances using Git.
5
+ Author: DanielDPereira
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Daniel Dias Pereira
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ License-File: LICENSE
28
+ Requires-Python: >=3.9
29
+ Requires-Dist: deepdiff>=6.7.1
30
+ Requires-Dist: httpx>=0.27.0
31
+ Requires-Dist: pydantic>=2.0
32
+ Requires-Dist: python-dotenv>=1.0.0
33
+ Requires-Dist: pyyaml>=6.0.1
34
+ Requires-Dist: tenacity>=8.2.3
35
+ Requires-Dist: typer>=0.9.0
36
+ Provides-Extra: dev
37
+ Requires-Dist: build>=1.0.3; extra == 'dev'
38
+ Requires-Dist: pytest>=7.0; extra == 'dev'
39
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
40
+ Description-Content-Type: text/markdown
41
+
42
+ <div align="center">
43
+ <h1>🚀 n8n-local-sync</h1>
44
+ <p><i>A lightweight, developer-first GitOps CLI for versioning, validating, and synchronizing n8n workflows.</i></p>
45
+
46
+ [![Python Version](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
48
+ </div>
49
+
50
+ ---
51
+
52
+ ## What is it?
53
+
54
+ **n8n-local-sync** bridges the gap between your [n8n](https://n8n.io/) instances and Git. By treating your n8n workflows as code, you can leverage standard software engineering practices—like version control, peer reviews, CI/CD, and automated deployments—for your automations.
55
+
56
+ ## Why?
57
+
58
+ Managing n8n workflows through the UI is great for building, but challenging for lifecycle management.
59
+ Simple backups aren't enough. You need **version-controlled workflow management**.
60
+
61
+ With `n8n-local-sync`, you can:
62
+ - Track changes meaningfully using deterministic hashing.
63
+ - Review workflow changes in Git pull requests with clean diffs.
64
+ - Safely promote workflows across environments (dev → staging → prod) via CI/CD pipelines.
65
+
66
+ ## Features
67
+
68
+ * 🔄 **Bidirectional Syncing:** Seamlessly pull (`sync` / `export`) and push (`import`) workflows using the official n8n REST API.
69
+ * 🧠 **Smart State Tracking:** Detects `LOCAL_MODIFIED`, `REMOTE_MODIFIED`, and `CONFLICT` states before destructive actions.
70
+ * 🛡️ **Validation & Security:** Catch invalid JSON structures and detect potential hardcoded secrets (heuristically).
71
+ * 🌲 **Git-Native:** Cleans workflow metadata and normalizes node order for deterministic, clean `git diff` outputs.
72
+ * 🧪 **Dry-Run Mode:** Simulate changes (`--dry-run`) across all destructive commands before applying them.
73
+ * 🏷️ **Tag Filtering:** Target specific environments or modules during export using the `--tag` flag.
74
+ * 🤖 **CI/CD Ready:** Configure your environment dynamically using `.env` files or environment variables.
75
+
76
+ ## Architecture
77
+
78
+ ```mermaid
79
+ graph TD
80
+ A[n8n Instance] <-->|REST API| B(n8n-local-sync)
81
+ B -->|Export / Pull| C[Local Git Repository]
82
+ B -->|Validate / Diff| C
83
+ C -->|Import / Push| B
84
+ C -->|Commit / Push| D[GitHub / GitLab]
85
+ D -->|CI/CD Actions| B
86
+ ```
87
+
88
+ ## Installation
89
+
90
+ You can install `n8n-local-sync` directly from PyPI (once published) or from the source:
91
+
92
+ ```bash
93
+ pip install n8n-local-sync
94
+ ```
95
+
96
+ ## Quick Start
97
+
98
+ 1. **Initialize and export:**
99
+ ```bash
100
+ # Initialize project configuration
101
+ n8n-sync init
102
+
103
+ # Set your API credentials in the generated .env file
104
+ # Export workflows from your n8n instance
105
+ n8n-sync export
106
+
107
+ # Version control the results
108
+ git add n8n/workflows/ .n8n-sync.yaml
109
+ git commit -m "chore: initial workflow export"
110
+ git push
111
+ ```
112
+
113
+ 2. **Synchronize changes:**
114
+ ```bash
115
+ # See what changed between local and remote
116
+ n8n-sync diff
117
+ n8n-sync status
118
+
119
+ # Pull remote changes safely (won't overwrite local modifications)
120
+ n8n-sync sync
121
+
122
+ # Push local changes back to n8n
123
+ n8n-sync push
124
+ ```
125
+
126
+ ## Configuration
127
+
128
+ Configuration resolves in the following priority:
129
+ 1. **CLI Arguments** (`--force`, `--dry-run`)
130
+ 2. **Environment Variables** (`N8N_API_KEY`, `N8N_BASE_URL`)
131
+ 3. **Config File** (`.n8n-sync.yaml`)
132
+
133
+ > [!IMPORTANT]
134
+ > Never store your `N8N_API_KEY` in the `.n8n-sync.yaml` file. Always use environment variables or a `.env` file (which is ignored by Git).
135
+
136
+ ### 🔄 Synching (Pulling) Remote Changes
137
+
138
+ Update your local repository with changes made directly in the n8n UI. The sync command evaluates the state of each workflow and avoids overwriting local modifications unless forced.
139
+
140
+ ```bash
141
+ n8n-sync sync # or n8n-sync pull
142
+ ```
143
+
144
+ **GitOps States Handled During Pull:**
145
+ - `UNCHANGED`: Skipped safely.
146
+ - `REMOTE_MODIFIED`: Remote changes are pulled, updating the local file.
147
+ - `LOCAL_MODIFIED`: Skipped with a warning (to protect local unpushed work). Use `--force` to overwrite local changes.
148
+ - `CONFLICT` (both changed): Skipped with a warning. Use `--force` to overwrite local with remote.
149
+ - `REMOTE_ONLY`: New remote workflows are pulled and saved locally.
150
+ - `LOCAL_ONLY`: Ignored by pull (use `push` to upload them).
151
+
152
+ *Note: Deletions are not automatically synced in either direction to prevent accidental data loss. If you delete a workflow in n8n, delete the local file manually.*
153
+
154
+ ### 📤 Pushing Local Changes
155
+
156
+ Upload your local Git-versioned workflows to the remote n8n instance. Like pull, push is state-aware.
157
+
158
+ ```bash
159
+ n8n-sync import # or n8n-sync push
160
+ ```
161
+
162
+ **GitOps States Handled During Push:**
163
+ - `UNCHANGED`: Skipped safely.
164
+ - `LOCAL_MODIFIED`: Pushed to remote, updating the n8n workflow.
165
+ - `REMOTE_MODIFIED`: Skipped with a warning (to protect remote changes). Use `--force` to overwrite remote changes.
166
+ - `CONFLICT` (both changed): Skipped with a warning. Use `--force` to overwrite remote with local.
167
+ - `LOCAL_ONLY`: Creates a new workflow in n8n. The local file is automatically updated with the new ID assigned by n8n.
168
+ - `REMOTE_ONLY`: Ignored by push (use `pull` to download them).
169
+
170
+ ## CLI Reference
171
+
172
+ - `n8n-sync init`: Initializes a project, creating `.n8n-sync.yaml` and `.env.example`.
173
+ - `n8n-sync sync` (alias `pull`): Safely synchronizes remote workflows to local files. Warns on conflicts.
174
+ - `n8n-sync import` (alias `push`): Pushes local workflows to the remote n8n instance.
175
+ - `n8n-sync export`: Forces an export of all (or tagged) remote workflows to local files.
176
+ - `n8n-sync diff`: Shows a granular, structural diff between local and remote workflows.
177
+ - `n8n-sync status`: Displays a summary table of workflow synchronization states (e.g., `LOCAL_MODIFIED`, `CONFLICT`).
178
+ - `n8n-sync validate`: Runs structural and security heuristic validations against local workflow JSON files.
179
+
180
+ **Common Flags:**
181
+ - `--dry-run`: Simulate operations without modifying local files or the remote n8n instance.
182
+ - `--force`: Force overwrite conflicts or local modifications during `sync`.
183
+
184
+ ## Git Workflow
185
+
186
+ The typical GitOps flow looks like this:
187
+
188
+ 1. Build a workflow in your Dev n8n instance.
189
+ 2. Run `n8n-sync sync` to pull it down locally.
190
+ 3. Review the structural changes using `git diff`.
191
+ 4. Create a Pull Request.
192
+ 5. On merge, a CI/CD pipeline runs `n8n-sync validate` and `n8n-sync push` to deploy the workflow to Production.
193
+
194
+ ## Security
195
+
196
+ - **Heuristic Secret Scanning:** The `validate` command detects potential hardcoded secrets (`api_key`, `token`, `password`, etc.) in workflow nodes. *Note: This is a heuristic detection, not a strict guarantee.*
197
+ - **No Credentials Exposed:** The CLI is designed to never output API keys or authorization headers in error logs or standard output.
198
+
199
+ ## n8n Compatibility
200
+
201
+ - **Supported/Tested Versions:** n8n `0.164.0` and above.
202
+ - **API Requirements:** Requires the n8n Public REST API (v1) to be enabled and accessible. Legacy CLI hacks are not supported.
203
+
204
+ ## Development
205
+
206
+ ```bash
207
+ # Clone the repository
208
+ git clone https://github.com/DanielDPereira/n8n-local-sync.git
209
+ cd n8n-local-sync
210
+
211
+ # Install with development dependencies
212
+ pip install -e .[dev]
213
+
214
+ # Run linting
215
+ ruff check src/ tests/
216
+ ```
217
+
218
+ We bundle a pre-commit hook that runs `n8n-sync validate`.
219
+ ```bash
220
+ pre-commit install
221
+ ```
222
+
223
+ ## Testing
224
+
225
+ ```bash
226
+ # Run unit tests
227
+ pytest tests/
228
+ ```
229
+
230
+ ### Integration Testing
231
+ You can use the provided `docker-compose.yml` to spin up an ephemeral n8n instance for testing:
232
+ ```bash
233
+ docker compose up -d
234
+ ```
235
+ The instance will be available at `http://localhost:5678`.
236
+
237
+ ## CI/CD
238
+
239
+ This project uses GitHub Actions for CI/CD:
240
+ - **CI**: Runs `pytest`, `ruff`, and `python -m build` on all PRs and pushes to `main`.
241
+ - **Publish**: Uses PyPI Trusted Publishing (OIDC) to securely publish new releases on tag.
242
+
243
+ ## Roadmap
244
+
245
+ - [x] CLI foundation
246
+ - [x] Export/import
247
+ - [x] Git-friendly workflow files (canonicalization)
248
+ - [x] Validation (Heuristic secret scanning)
249
+ - [x] Diff & Status (State tracking)
250
+ - [x] Dry-run safe mode
251
+ - [x] CI and PyPI packaging readiness
252
+ - [ ] Integration test suite with Docker
253
+ - [ ] Multi-environment support (`--env prod`)
254
+ - [ ] Workflow promotion logic
255
+
256
+ ## License
257
+
258
+ This project is licensed under the MIT License.