xcorecli 1.1.0__tar.gz → 2.0.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.
- {xcorecli-1.1.0 → xcorecli-2.0.0}/PKG-INFO +2 -2
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/index.md +7 -2
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/reference.md +5 -26
- {xcorecli-1.1.0 → xcorecli-2.0.0}/mkdocs.yml +0 -2
- {xcorecli-1.1.0 → xcorecli-2.0.0}/pyproject.toml +2 -2
- {xcorecli-1.1.0 → xcorecli-2.0.0}/uv.lock +1 -1
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/main.py +0 -2
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/manager/cli.py +74 -0
- xcorecli-1.1.0/docs/deploy/index.md +0 -406
- xcorecli-1.1.0/xcli/deploy/cli.py +0 -538
- xcorecli-1.1.0/xcli/deploy/config.py +0 -289
- xcorecli-1.1.0/xcli/deploy/generate.py +0 -122
- xcorecli-1.1.0/xcli/deploy/runner.py +0 -692
- xcorecli-1.1.0/xcli/worker/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.github/workflows/publish.yml +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.gitignore +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.python-version +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.vscode/configurationCache.log +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.vscode/dryrun.log +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.vscode/settings.json +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/.vscode/targets.log +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/README.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/commands/health.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/commands/init.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/config/index.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/getting-started/auth.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/getting-started/configuration.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/getting-started/install.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/manager/index.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/manager/monitoring.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/manager/services.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/migration/index.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/index.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/install.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/local.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/marketplace.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/runtime.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/security.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/plugin/update.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/sandbox/index.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/worker/index.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/docs/worker/process.md +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/makefile +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/_credentials.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/_run.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/_xcore.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/config/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/config/cli.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/config/runtime.py +0 -0
- {xcorecli-1.1.0/xcli/deploy → xcorecli-2.0.0/xcli/init}/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/init/manager.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/init/upgrade.py +0 -0
- {xcorecli-1.1.0/xcli/init → xcorecli-2.0.0/xcli/manager}/__init__.py +0 -0
- {xcorecli-1.1.0/xcli/manager → xcorecli-2.0.0/xcli/marketplace}/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/marketplace/cli.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/migrations/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/migrations/cli.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/migrations/runtime.py +0 -0
- {xcorecli-1.1.0/xcli/marketplace → xcorecli-2.0.0/xcli/plugin}/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/cli.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/install_commands.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/local_commands.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/marketplace_commands.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/runtime_commands.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/scaffold.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/security_commands.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/shared.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/plugin/update_commands.py +0 -0
- {xcorecli-1.1.0/xcli/plugin → xcorecli-2.0.0/xcli/sandbox}/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/sandbox/cli.py +0 -0
- {xcorecli-1.1.0/xcli/sandbox → xcorecli-2.0.0/xcli/worker}/__init__.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/worker/cli.py +0 -0
- {xcorecli-1.1.0 → xcorecli-2.0.0}/xcli/worker/worker.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: xcorecli
|
|
3
|
-
Version:
|
|
4
|
-
Summary: CLI for xcore — configuration, plugins,
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: CLI for xcore — configuration, plugins, monitoring
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Requires-Dist: alembic>=1.18.0
|
|
7
7
|
Requires-Dist: httpx>=0.27
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
- **Project Initialization**: Seamlessly scaffold new projects and manage `integration.yaml`.
|
|
11
11
|
- **Plugin Lifecycle**: Full control over installing, signing, and updating plugins.
|
|
12
|
-
- **Production Deploy**: Deploy plugins to remote servers via SSH with pre/post hooks.
|
|
13
12
|
- **Real-time Monitoring**: Integrated dashboard for service health and resource usage.
|
|
14
13
|
- **Worker Orchestration**: Manage Celery/XWorker processes with ease.
|
|
15
14
|
- **Security & Sandboxing**: Resource isolation and AST-based whitelisting for plugins.
|
|
@@ -30,5 +29,11 @@ make install
|
|
|
30
29
|
|
|
31
30
|
- [Installation Guide](getting-started/install.md)
|
|
32
31
|
- [Authentication Setup](getting-started/auth.md)
|
|
33
|
-
- [Deploy to Production](deploy/index.md)
|
|
34
32
|
- [Core Commands Reference](reference.md)
|
|
33
|
+
|
|
34
|
+
!!! info "Production deployment"
|
|
35
|
+
Deploying plugin bundles to remote servers is no longer part of
|
|
36
|
+
`xcorecli` — it's fully handled by the standalone
|
|
37
|
+
[`xcore-agent`](https://github.com/traoreera/xcore-agent) deployment
|
|
38
|
+
agent (Hub artifact fetch, signature verification, install/rollback,
|
|
39
|
+
systemd/Docker/Kubernetes supervisors, CI/CD watch loop).
|
|
@@ -21,32 +21,11 @@ A comprehensive list of all commands available in `xcorecli`.
|
|
|
21
21
|
- `manager services reload`: Reconnect a service.
|
|
22
22
|
- `manager services unload`: Shutdown a service.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
- `deploy status <target>`: Show plugin state on a remote server via the XCore API.
|
|
30
|
-
|
|
31
|
-
**Key options for `deploy <target>`:**
|
|
32
|
-
|
|
33
|
-
| Option | Description |
|
|
34
|
-
|---|---|
|
|
35
|
-
| `--plugin <name>` | Deploy only one plugin (skips extensions + integration) |
|
|
36
|
-
| `--dry-run` | Simulate without sending anything |
|
|
37
|
-
| `--no-reload` | Transfer files without triggering reload/restart |
|
|
38
|
-
| `--file <path>` | Use an alternate config file |
|
|
39
|
-
|
|
40
|
-
**Deploy order:** `integration.yaml` → extensions (services) → plugins
|
|
41
|
-
|
|
42
|
-
**Sources:** each plugin/extension accepts `source: ./local/path` OR `repo: https://github.com/org/repo` (public), with optional `token: "${GITHUB_TOKEN}"` for private repos or `git@github.com:...` SSH URLs.
|
|
43
|
-
|
|
44
|
-
**Hook levels:**
|
|
45
|
-
|
|
46
|
-
| Level | Keys | When |
|
|
47
|
-
|---|---|---|
|
|
48
|
-
| Global | `hooks.pre_deploy` / `hooks.post_deploy` | Once per full deploy run |
|
|
49
|
-
| Per-plugin/extension | `hooks.pre_deploy` / `post_deploy` | Before/after each item |
|
|
24
|
+
!!! info "Production deployment moved"
|
|
25
|
+
`deploy` (init/list/status, SSH transfer, pre/post hooks) is no
|
|
26
|
+
longer part of `xcorecli` — it's fully replaced by the standalone
|
|
27
|
+
[`xcore-agent`](https://github.com/traoreera/xcore-agent) deployment
|
|
28
|
+
agent.
|
|
50
29
|
|
|
51
30
|
---
|
|
52
31
|
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "xcorecli"
|
|
7
|
-
version = "
|
|
8
|
-
description = "CLI for xcore — configuration, plugins,
|
|
7
|
+
version = "2.0.0"
|
|
8
|
+
description = "CLI for xcore — configuration, plugins, monitoring"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
11
11
|
dependencies = [
|
|
@@ -13,7 +13,6 @@ _console = Console()
|
|
|
13
13
|
from xcli.init.manager import _DB_URLS, create_project
|
|
14
14
|
from xcli.init.upgrade import run_upgrade as _run_upgrade
|
|
15
15
|
from xcli.config.cli import app as config_app
|
|
16
|
-
from xcli.deploy.cli import app as deploy_app
|
|
17
16
|
from xcli.manager.cli import app as manager_app
|
|
18
17
|
from xcli.migrations.cli import app as migrations_app
|
|
19
18
|
from xcli.plugin.cli import app as plugin_app
|
|
@@ -150,7 +149,6 @@ def services() -> None:
|
|
|
150
149
|
# ── sub-apps ──────────────────────────────────────────────────
|
|
151
150
|
|
|
152
151
|
app.add_typer(config_app, name="config")
|
|
153
|
-
app.add_typer(deploy_app, name="deploy")
|
|
154
152
|
app.add_typer(plugin_app, name="plugin")
|
|
155
153
|
app.add_typer(sandbox_app, name="sandbox")
|
|
156
154
|
app.add_typer(worker_app, name="worker")
|
|
@@ -138,6 +138,57 @@ def _build_resources_table() -> Table:
|
|
|
138
138
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
139
139
|
pass
|
|
140
140
|
|
|
141
|
+
# Plugins en mode "trusted"/"sandboxed(threaded)" tournent DANS le
|
|
142
|
+
# process API principal (pas de sous-process dédié) — donc pas de PID
|
|
143
|
+
# à eux. Plutôt que d'afficher des tirets, on retombe sur le process
|
|
144
|
+
# API (retrouvé via le PID file écrit par `xcli manager start`, ou à
|
|
145
|
+
# défaut par scan cmdline) et on l'affiche comme ressource *partagée*.
|
|
146
|
+
main_proc: psutil.Process | None = None
|
|
147
|
+
try:
|
|
148
|
+
from xcli.worker.worker import PID_API, _is_running, _read_pid
|
|
149
|
+
|
|
150
|
+
main_pid = _read_pid(PID_API)
|
|
151
|
+
if main_pid and _is_running(main_pid):
|
|
152
|
+
main_proc = psutil.Process(main_pid)
|
|
153
|
+
except Exception:
|
|
154
|
+
main_proc = None
|
|
155
|
+
|
|
156
|
+
if main_proc is None:
|
|
157
|
+
# Pas de PID file (démarrage en foreground sans --detach, ou
|
|
158
|
+
# process lancé hors xcli) — on cherche le process uvicorn qui
|
|
159
|
+
# sert l'app, en excluant les sandbox workers déjà identifiés.
|
|
160
|
+
sandboxed_pids = {p.pid for p in sandbox_pids.values()}
|
|
161
|
+
for proc in psutil.process_iter(["pid", "cmdline"]):
|
|
162
|
+
if proc.pid in sandboxed_pids:
|
|
163
|
+
continue
|
|
164
|
+
try:
|
|
165
|
+
cmd = proc.info["cmdline"] or []
|
|
166
|
+
if any("uvicorn" in str(c) for c in cmd):
|
|
167
|
+
main_proc = proc
|
|
168
|
+
break
|
|
169
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
main_cpu_str = main_mem_str = main_conn_str = "—"
|
|
173
|
+
main_pid_str = "—"
|
|
174
|
+
if main_proc is not None:
|
|
175
|
+
try:
|
|
176
|
+
main_pid_str = str(main_proc.pid)
|
|
177
|
+
cpu = main_proc.cpu_percent(interval=0.05)
|
|
178
|
+
cpu_color = "red" if cpu > 80 else ("yellow" if cpu > 40 else "green")
|
|
179
|
+
main_cpu_str = f"[{cpu_color}]{cpu:.1f}[/{cpu_color}]"
|
|
180
|
+
|
|
181
|
+
mem = main_proc.memory_info().rss / 1024**2
|
|
182
|
+
mem_color = "red" if mem > 400 else ("yellow" if mem > 200 else "magenta")
|
|
183
|
+
main_mem_str = f"[{mem_color}]{mem:.1f}[/{mem_color}]"
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
main_conn_str = str(len(main_proc.net_connections()))
|
|
187
|
+
except (psutil.AccessDenied, AttributeError):
|
|
188
|
+
main_conn_str = "?"
|
|
189
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
190
|
+
main_proc = None
|
|
191
|
+
|
|
141
192
|
table = Table(title=f"Plugin Resources [dim]{plugins_root}[/dim]", show_lines=True)
|
|
142
193
|
table.add_column("Plugin", style="cyan", no_wrap=True)
|
|
143
194
|
table.add_column("Mode", justify="center")
|
|
@@ -196,9 +247,25 @@ def _build_resources_table() -> Table:
|
|
|
196
247
|
state_str = "[green]running[/green]" if state == "running" else f"[yellow]{state}[/yellow]"
|
|
197
248
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
198
249
|
state_str = "[red]gone[/red]"
|
|
250
|
+
elif main_proc is not None:
|
|
251
|
+
# Plugin en process partagé (trusted / non-sandboxé) : on
|
|
252
|
+
# affiche les métriques du process API, avec un marqueur "~"
|
|
253
|
+
# pour signaler que c'est une valeur partagée entre plugins,
|
|
254
|
+
# pas exclusive à celui-ci.
|
|
255
|
+
pid_str = f"~{main_pid_str}"
|
|
256
|
+
cpu_str = main_cpu_str
|
|
257
|
+
mem_str = main_mem_str
|
|
258
|
+
conn_str = main_conn_str
|
|
259
|
+
state_str = "[cyan]in-process[/cyan] [dim](partagé)[/dim]"
|
|
199
260
|
|
|
200
261
|
table.add_row(name, mode, pid_str, cpu_str, mem_str, disk_str, conn_str, state_str)
|
|
201
262
|
|
|
263
|
+
if main_proc is not None and any(name not in sandbox_pids for name in plugin_names):
|
|
264
|
+
table.caption = (
|
|
265
|
+
"[dim]~PID = process API partagé entre plugins \"in-process (partagé)\" — "
|
|
266
|
+
"CPU/Mem/Net Conn reflètent le process entier, pas ce plugin seul.[/dim]"
|
|
267
|
+
)
|
|
268
|
+
|
|
202
269
|
return table
|
|
203
270
|
|
|
204
271
|
|
|
@@ -595,12 +662,19 @@ def start(
|
|
|
595
662
|
|
|
596
663
|
console.print(f"[dim]→ uvicorn {app_path} --host {resolved_host} --port {resolved_port}[/dim]")
|
|
597
664
|
console.print("[dim]Ctrl+C to stop[/dim]\n")
|
|
665
|
+
PID_DIR.mkdir(parents=True, exist_ok=True)
|
|
598
666
|
try:
|
|
599
667
|
proc = subprocess.Popen(cmd)
|
|
668
|
+
# Écrit le PID même en foreground : `xcli manager resources`/`top`
|
|
669
|
+
# dans un autre terminal en a besoin pour retrouver le process API
|
|
670
|
+
# (plugins "trusted" partagent ce process, pas de PID dédié).
|
|
671
|
+
_write_pid(PID_API, proc.pid)
|
|
600
672
|
proc.wait()
|
|
601
673
|
except KeyboardInterrupt:
|
|
602
674
|
proc.terminate()
|
|
603
675
|
proc.wait()
|
|
676
|
+
finally:
|
|
677
|
+
PID_API.unlink(missing_ok=True)
|
|
604
678
|
|
|
605
679
|
|
|
606
680
|
@app.command("stop")
|
|
@@ -1,406 +0,0 @@
|
|
|
1
|
-
# Deploy — Déploiement en production
|
|
2
|
-
|
|
3
|
-
Le module `deploy` permet de déployer des plugins, extensions (services), fichiers de configuration (.env, certificats…) et le fichier `integration.yaml` vers des serveurs distants via SSH/SFTP. Il supporte les sources **locales** et les **repos GitHub** (publics et privés), avec un système de hooks pre/post.
|
|
4
|
-
|
|
5
|
-
## Quickstart
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# 1. Scan automatique du projet (détecte plugins, extensions, integration.yaml)
|
|
9
|
-
xcli deploy generate
|
|
10
|
-
|
|
11
|
-
# 2. Éditer xcore-deploy.yaml (targets, tweaks)
|
|
12
|
-
|
|
13
|
-
# 3. Simuler sans rien envoyer
|
|
14
|
-
xcli deploy run production --dry-run
|
|
15
|
-
|
|
16
|
-
# 4. Déployer
|
|
17
|
-
xcli deploy run production
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Structure complète de `xcore-deploy.yaml`
|
|
23
|
-
|
|
24
|
-
```yaml
|
|
25
|
-
version: "1"
|
|
26
|
-
|
|
27
|
-
# ── Serveurs ─────────────────────────────────────────────────────────────────
|
|
28
|
-
targets:
|
|
29
|
-
production:
|
|
30
|
-
host: prod.monapp.com
|
|
31
|
-
port: 22
|
|
32
|
-
user: deploy
|
|
33
|
-
ssh_key: ~/.ssh/id_ed25519
|
|
34
|
-
xcore_url: https://api.monapp.com
|
|
35
|
-
xcore_token: "${XCORE_ADMIN_TOKEN}"
|
|
36
|
-
plugins_root: /opt/xcore/app/plugins
|
|
37
|
-
extensions_root: /opt/xcore/app/extensions
|
|
38
|
-
|
|
39
|
-
staging:
|
|
40
|
-
host: staging.monapp.com
|
|
41
|
-
user: deploy
|
|
42
|
-
ssh_key: ~/.ssh/id_ed25519
|
|
43
|
-
xcore_url: https://staging.monapp.com
|
|
44
|
-
xcore_token: "${XCORE_STAGING_TOKEN}"
|
|
45
|
-
plugins_root: /opt/xcore/app/plugins
|
|
46
|
-
|
|
47
|
-
# ── Hooks globaux ─────────────────────────────────────────────────────────────
|
|
48
|
-
hooks:
|
|
49
|
-
pre_deploy:
|
|
50
|
-
- cmd: "uv run xcli plugin security validate --save"
|
|
51
|
-
- cmd: "uv run pytest tests/ -x -q"
|
|
52
|
-
ignore_errors: false
|
|
53
|
-
|
|
54
|
-
post_deploy:
|
|
55
|
-
- cmd: "uv run xcli deploy status production"
|
|
56
|
-
ignore_errors: true
|
|
57
|
-
|
|
58
|
-
# ── Config XCore (integration.yaml) ──────────────────────────────────────────
|
|
59
|
-
integration:
|
|
60
|
-
source: ./integration.yaml
|
|
61
|
-
remote_path: /opt/xcore/integration.yaml
|
|
62
|
-
restart_xcore: true
|
|
63
|
-
|
|
64
|
-
# ── Fichiers (config, .env, certificats) ──────────────────────────────────────
|
|
65
|
-
# Copiés vers le serveur avant les extensions et plugins.
|
|
66
|
-
files:
|
|
67
|
-
- source: ./.env
|
|
68
|
-
dest: /opt/xcore/.env
|
|
69
|
-
# only: [production]
|
|
70
|
-
|
|
71
|
-
- source: ./conf/private.pem
|
|
72
|
-
dest: /opt/xcore/conf/private.pem
|
|
73
|
-
only: [production]
|
|
74
|
-
|
|
75
|
-
- source: ./conf/public.pem
|
|
76
|
-
dest: /opt/xcore/conf/public.pem
|
|
77
|
-
|
|
78
|
-
# ── Extensions (services) ─────────────────────────────────────────────────────
|
|
79
|
-
extensions:
|
|
80
|
-
- name: mail
|
|
81
|
-
source: ./extensions/mail
|
|
82
|
-
restart: true
|
|
83
|
-
only: [production]
|
|
84
|
-
|
|
85
|
-
- name: pubsub
|
|
86
|
-
repo: https://github.com/org/xcore-pubsub
|
|
87
|
-
ref: v1.0.0
|
|
88
|
-
restart: true
|
|
89
|
-
|
|
90
|
-
# ── Plugins ───────────────────────────────────────────────────────────────────
|
|
91
|
-
plugins:
|
|
92
|
-
- name: auth
|
|
93
|
-
source: ./app/auth
|
|
94
|
-
sign: true
|
|
95
|
-
reload: true
|
|
96
|
-
|
|
97
|
-
- name: billing
|
|
98
|
-
repo: https://github.com/org/billing-plugin
|
|
99
|
-
ref: main
|
|
100
|
-
sign: true
|
|
101
|
-
reload: true
|
|
102
|
-
|
|
103
|
-
- name: pdf-generator
|
|
104
|
-
source: ./app/pdf-generator
|
|
105
|
-
reload: true
|
|
106
|
-
only: [production]
|
|
107
|
-
hooks:
|
|
108
|
-
pre_deploy:
|
|
109
|
-
- cmd: "uv run xcli plugin security validate ./app/pdf-generator"
|
|
110
|
-
post_deploy:
|
|
111
|
-
- cmd: "echo 'pdf-generator déployé'"
|
|
112
|
-
ignore_errors: true
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## Section `files:` — Copie de fichiers
|
|
118
|
-
|
|
119
|
-
Les fichiers déclarés dans `files:` sont copiés vers le serveur **avant** les extensions et plugins. Utile pour `.env`, certificats, fichiers de config, etc.
|
|
120
|
-
|
|
121
|
-
```yaml
|
|
122
|
-
files:
|
|
123
|
-
- source: ./.env
|
|
124
|
-
dest: /opt/xcore/.env
|
|
125
|
-
- source: ./conf/private.pem
|
|
126
|
-
dest: /opt/xcore/conf/private.pem
|
|
127
|
-
only: [production]
|
|
128
|
-
- source: ./config/prod.yaml
|
|
129
|
-
dest: /opt/xcore/config/prod.yaml
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
| Champ | Description |
|
|
133
|
-
|---|---|
|
|
134
|
-
| `source` | Chemin local du fichier (relatif à la racine du projet) |
|
|
135
|
-
| `dest` | Chemin distant de destination |
|
|
136
|
-
| `only` | (optionnel) Restreindre à certains targets |
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Sources : local vs GitHub
|
|
141
|
-
|
|
142
|
-
Chaque plugin et extension accepte soit `source:` (chemin local), soit `repo:` (dépôt Git).
|
|
143
|
-
|
|
144
|
-
### Source locale
|
|
145
|
-
|
|
146
|
-
```yaml
|
|
147
|
-
plugins:
|
|
148
|
-
- name: mon-plugin
|
|
149
|
-
source: ./app/mon-plugin
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### GitHub — repo public
|
|
153
|
-
|
|
154
|
-
```yaml
|
|
155
|
-
plugins:
|
|
156
|
-
- name: mon-plugin
|
|
157
|
-
repo: https://github.com/org/mon-plugin
|
|
158
|
-
ref: v2.1.0
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### GitHub — repo privé via token
|
|
162
|
-
|
|
163
|
-
```yaml
|
|
164
|
-
plugins:
|
|
165
|
-
- name: mon-plugin
|
|
166
|
-
repo: https://github.com/org/mon-plugin-prive
|
|
167
|
-
ref: main
|
|
168
|
-
token: "${GITHUB_TOKEN}"
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### GitHub — repo privé via SSH
|
|
172
|
-
|
|
173
|
-
```yaml
|
|
174
|
-
plugins:
|
|
175
|
-
- name: mon-plugin
|
|
176
|
-
repo: git@github.com:org/mon-plugin-prive.git
|
|
177
|
-
ref: main
|
|
178
|
-
# utilise ssh_key du target
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Plugin dans un sous-dossier du repo
|
|
182
|
-
|
|
183
|
-
```yaml
|
|
184
|
-
plugins:
|
|
185
|
-
- name: mon-plugin
|
|
186
|
-
repo: https://github.com/org/monorepo
|
|
187
|
-
ref: main
|
|
188
|
-
subdirectory: packages/mon-plugin
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
!!! note "Clone automatique"
|
|
192
|
-
xcli fait un `git clone --depth 1 --branch <ref>` dans un dossier temporaire, puis emballe et transfère le code. Le clone est supprimé après le déploiement.
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## Variables d'environnement
|
|
197
|
-
|
|
198
|
-
Les valeurs `${VAR}` ou `{VAR}` dans le YAML sont interpolées depuis l'environnement système.
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
export XCORE_ADMIN_TOKEN="mon-token-secret"
|
|
202
|
-
export XCORE_STAGING_TOKEN="staging-token"
|
|
203
|
-
export GITHUB_TOKEN="ghp_..."
|
|
204
|
-
export PROD_HOST="prod.monapp.com"
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## Ordre d'exécution complet
|
|
210
|
-
|
|
211
|
-
```
|
|
212
|
-
hooks.pre_deploy (global)
|
|
213
|
-
│
|
|
214
|
-
├─ integration.yaml (si défini)
|
|
215
|
-
│ → sftp integration.yaml
|
|
216
|
-
│ → POST /config/reload (ou restart service)
|
|
217
|
-
│
|
|
218
|
-
├─ Fichiers (section files:)
|
|
219
|
-
│ → sftp chaque fichier
|
|
220
|
-
│
|
|
221
|
-
├─ Pour chaque extension :
|
|
222
|
-
│ extension.hooks.pre_deploy
|
|
223
|
-
│ → archive tar.gz
|
|
224
|
-
│ → sftp + extraction
|
|
225
|
-
│ → POST /services/{name}/restart
|
|
226
|
-
│ extension.hooks.post_deploy
|
|
227
|
-
│
|
|
228
|
-
├─ Pour chaque plugin :
|
|
229
|
-
│ plugin.hooks.pre_deploy
|
|
230
|
-
│ → signature HMAC (si sign: true)
|
|
231
|
-
│ → archive tar.gz
|
|
232
|
-
│ → sftp + extraction
|
|
233
|
-
│ → POST /plugins/{name}/reload
|
|
234
|
-
│ plugin.hooks.post_deploy
|
|
235
|
-
│
|
|
236
|
-
hooks.post_deploy (global)
|
|
237
|
-
└─ Rapport tableau final
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Hooks
|
|
243
|
-
|
|
244
|
-
### Niveau global
|
|
245
|
-
|
|
246
|
-
Définis sous `hooks:` à la racine.
|
|
247
|
-
|
|
248
|
-
| Moment | Quand |
|
|
249
|
-
|---|---|
|
|
250
|
-
| `hooks.pre_deploy` | Avant integration.yaml, fichiers, extensions et plugins |
|
|
251
|
-
| `hooks.post_deploy` | Après tous les plugins |
|
|
252
|
-
|
|
253
|
-
### Niveau plugin / extension
|
|
254
|
-
|
|
255
|
-
Définis sous `plugins[].hooks` ou `extensions[].hooks`.
|
|
256
|
-
|
|
257
|
-
| Moment | Quand |
|
|
258
|
-
|---|---|
|
|
259
|
-
| `pre_deploy` | Avant l'archivage |
|
|
260
|
-
| `post_deploy` | Après le hot-reload / restart |
|
|
261
|
-
|
|
262
|
-
### Options d'un hook
|
|
263
|
-
|
|
264
|
-
```yaml
|
|
265
|
-
hooks:
|
|
266
|
-
pre_deploy:
|
|
267
|
-
- cmd: "ma-commande --option valeur"
|
|
268
|
-
cwd: "./sous-dossier" # répertoire d'exécution (défaut : racine projet)
|
|
269
|
-
ignore_errors: true # continue même si exit code != 0 (défaut : false)
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
!!! warning "Hooks bloquants"
|
|
273
|
-
Par défaut (`ignore_errors: false`), un hook qui échoue **annule le déploiement**. Utilise `ignore_errors: true` pour les commandes non-critiques (notifications, rapports...).
|
|
274
|
-
|
|
275
|
-
---
|
|
276
|
-
|
|
277
|
-
## Commandes
|
|
278
|
-
|
|
279
|
-
### `xcli deploy generate`
|
|
280
|
-
|
|
281
|
-
Scanne le projet et génère `xcore-deploy.yaml` automatiquement.
|
|
282
|
-
|
|
283
|
-
Détecte les plugins (dossiers avec `plugin.yaml`), les extensions (`__init__.py`) et `integration.yaml`. Si le fichier existe déjà, les **targets et hooks sont conservés**.
|
|
284
|
-
|
|
285
|
-
```bash
|
|
286
|
-
xcli deploy generate
|
|
287
|
-
xcli deploy generate --output deploy/prod.yaml
|
|
288
|
-
xcli deploy generate --plugins-dir ./custom-plugins --dry-run
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
| Option | Description |
|
|
292
|
-
|---|---|
|
|
293
|
-
| `--output`, `-o` | Fichier de sortie (défaut: `xcore-deploy.yaml`) |
|
|
294
|
-
| `--plugins-dir`, `-p` | Répertoire des plugins (défaut: `./app`) |
|
|
295
|
-
| `--extensions-dir`, `-e` | Répertoire des extensions (défaut: `./extensions`) |
|
|
296
|
-
| `--dry-run` | Affiche le résultat sans écrire le fichier |
|
|
297
|
-
|
|
298
|
-
---
|
|
299
|
-
|
|
300
|
-
### `xcli deploy init`
|
|
301
|
-
|
|
302
|
-
Génère un fichier `xcore-deploy.yaml` d'exemple complet.
|
|
303
|
-
|
|
304
|
-
```bash
|
|
305
|
-
xcli deploy init
|
|
306
|
-
xcli deploy init --output deploy/prod.yaml
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
### `xcli deploy list`
|
|
312
|
-
|
|
313
|
-
Liste les targets, plugins, extensions, fichiers et hooks déclarés.
|
|
314
|
-
|
|
315
|
-
```bash
|
|
316
|
-
xcli deploy list
|
|
317
|
-
xcli deploy list --file deploy/prod.yaml
|
|
318
|
-
```
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
### `xcli deploy run <target>`
|
|
323
|
-
|
|
324
|
-
Déploie l'intégralité (integration.yaml + fichiers + extensions + plugins) vers un target.
|
|
325
|
-
|
|
326
|
-
```bash
|
|
327
|
-
xcli deploy run production
|
|
328
|
-
xcli deploy run staging
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
**Options :**
|
|
332
|
-
|
|
333
|
-
| Option | Description |
|
|
334
|
-
|---|---|
|
|
335
|
-
| `--plugin <nom>` | Déploie **uniquement** ce plugin (skip extensions, fichiers et integration) |
|
|
336
|
-
| `--dry-run` | Simule sans envoyer ni exécuter de commandes réelles |
|
|
337
|
-
| `--no-reload` | Transfère les fichiers sans déclencher reload/restart |
|
|
338
|
-
| `--file <chemin>` | Fichier de configuration alternatif |
|
|
339
|
-
|
|
340
|
-
```bash
|
|
341
|
-
# Déployer un seul plugin (skip extensions + fichiers + integration)
|
|
342
|
-
xcli deploy run production --plugin auth
|
|
343
|
-
|
|
344
|
-
# Simuler le déploiement complet
|
|
345
|
-
xcli deploy run production --dry-run
|
|
346
|
-
|
|
347
|
-
# Transférer sans redémarrer
|
|
348
|
-
xcli deploy run production --no-reload
|
|
349
|
-
|
|
350
|
-
# Fichier alternatif
|
|
351
|
-
xcli deploy run production --file deploy/prod.yaml
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
---
|
|
355
|
-
|
|
356
|
-
### `xcli deploy copy <target> <source> <dest>`
|
|
357
|
-
|
|
358
|
-
Copie un fichier vers un serveur distant via SFTP (sans passer par le déploiement complet).
|
|
359
|
-
|
|
360
|
-
```bash
|
|
361
|
-
xcli deploy copy production .env /opt/xcore/.env
|
|
362
|
-
xcli deploy copy staging config.yaml /opt/xcore/config.yaml --dry-run
|
|
363
|
-
xcli deploy copy production docker-compose.yml /opt/xcore/ --file deploy/prod.yaml
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
### `xcli deploy status <target>`
|
|
369
|
-
|
|
370
|
-
Affiche l'état des plugins sur le serveur via l'API XCore.
|
|
371
|
-
|
|
372
|
-
```bash
|
|
373
|
-
xcli deploy status production
|
|
374
|
-
xcli deploy status staging --file deploy/prod.yaml
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
---
|
|
378
|
-
|
|
379
|
-
## Dépendances requises
|
|
380
|
-
|
|
381
|
-
```bash
|
|
382
|
-
pip install paramiko # SSH/SFTP
|
|
383
|
-
pip install httpx # Appels API XCore (reload/restart)
|
|
384
|
-
# git doit être disponible dans le PATH pour les sources repo
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
---
|
|
388
|
-
|
|
389
|
-
## Exemples de hooks courants
|
|
390
|
-
|
|
391
|
-
```yaml
|
|
392
|
-
hooks:
|
|
393
|
-
pre_deploy:
|
|
394
|
-
- cmd: "uv run xcli plugin security validate --save"
|
|
395
|
-
- cmd: "uv run xcli plugin security validate --check-breaking"
|
|
396
|
-
- cmd: "uv run pytest tests/ -x -q"
|
|
397
|
-
ignore_errors: false
|
|
398
|
-
|
|
399
|
-
post_deploy:
|
|
400
|
-
- cmd: "uv run xcli deploy status production"
|
|
401
|
-
- cmd: >
|
|
402
|
-
curl -s -X POST https://hooks.slack.com/services/XXX
|
|
403
|
-
-H 'Content-type: application/json'
|
|
404
|
-
-d '{"text": "✅ Déploiement production terminé"}'
|
|
405
|
-
ignore_errors: true
|
|
406
|
-
```
|