fujin-cli 0.26.1__tar.gz → 0.27.1__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.
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/CHANGELOG.md +18 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/PKG-INFO +1 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-1password/pyproject.toml +2 -2
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-bitwarden/pyproject.toml +2 -2
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-doppler/pyproject.toml +2 -2
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-env/pyproject.toml +2 -2
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/pyproject.toml +2 -2
- fujin_cli-0.27.1/src/fujin/__init__.py +1 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/_installer.py +20 -26
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/_base.py +1 -9
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/app.py +8 -7
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/audit.py +2 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/deploy.py +3 -3
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/down.py +2 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/init.py +11 -90
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/prune.py +2 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/rollback.py +2 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/server.py +9 -8
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/connection.py +23 -8
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/conftest.py +1 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_audit.py +5 -5
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_down.py +6 -6
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_init.py +1 -5
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_prune.py +6 -6
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_rollback.py +7 -7
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_server.py +9 -6
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/uv.lock +5 -5
- fujin_cli-0.26.1/src/fujin/__init__.py +0 -1
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/.github/FUNDING.yml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/.github/workflows/publish.yml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/.github/workflows/test.yml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/.gitignore +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/.pre-commit-config.yaml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/.readthedocs.yaml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/CLAUDE.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/LICENSE.txt +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/README.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/Vagrantfile +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-cat-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-exec-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-logs-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-restart-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-scale-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-shell-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-start-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-status-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/app-stop-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/audit-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/deploy-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/down-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/fa-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/fujin-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/init-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/migrate-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/new-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/prune-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/rollback-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/server-bootstrap-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/server-create-user-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/server-exec-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/server-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/server-setup-ssh-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/server-status-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/_static/images/help/up-help.png +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/changelog.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/app.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/audit.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/deploy.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/down.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/index.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/init.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/migrate.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/new.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/prune.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/rollback.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/server.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/commands/up.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/conf.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/configuration.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/guides/django-complete.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/guides/index.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/guides/templates.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/howtos/binary.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/howtos/django.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/howtos/index.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/index.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/installation.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/integrations.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/requirements.txt +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/secrets.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/docs/troubleshooting.rst +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/.fujin/Caddyfile +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/.fujin/systemd/health.service +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/.fujin/systemd/health.timer +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/.fujin/systemd/web.service +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/.fujin/systemd/worker@.service +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/README.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/__main__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/asgi.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/management/commands/health.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/settings.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/urls.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/bookstore/wsgi.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/fujin.toml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/manage.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/pyproject.toml +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/examples/django/bookstore/requirements.txt +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/justfile +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-1password/README.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-1password/src/fujin_secrets_1password/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-1password/src/fujin_secrets_1password/py.typed +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-bitwarden/README.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-bitwarden/src/fujin_secrets_bitwarden/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-bitwarden/src/fujin_secrets_bitwarden/py.typed +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-doppler/README.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-doppler/src/fujin_secrets_doppler/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-doppler/src/fujin_secrets_doppler/py.typed +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-env/README.md +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/plugins/fujin-secrets-env/src/fujin_secrets_env/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/__main__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/audit.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/caddy.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/migrate.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/new.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/showenv.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/commands/up.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/config.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/discovery.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/errors.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/fa.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/formatting.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/secrets.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/src/fujin/templates.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/Dockerfile +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/__init__.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/conftest.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/helpers.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/test_app_management.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/test_full_deploy.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/test_installation.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/integration/test_server_bootstrap.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_app.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_caddy_domain.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_config.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_connection.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_deploy.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_discovery.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_new.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_scale.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_secrets.py +0 -0
- {fujin_cli-0.26.1 → fujin_cli-0.27.1}/tests/test_up.py +0 -0
|
@@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.27.1] - 2026-06-25
|
|
8
|
+
|
|
9
|
+
### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- Remove falco profile from test parametrization
|
|
12
|
+
|
|
13
|
+
### ⚡ Performance
|
|
14
|
+
|
|
15
|
+
- Simplify installer
|
|
16
|
+
- Improve ssh connection handling
|
|
17
|
+
|
|
18
|
+
## [0.27.0] - 2026-06-13
|
|
19
|
+
|
|
20
|
+
### ⚙️ Miscellaneous Tasks
|
|
21
|
+
|
|
22
|
+
- Release v0.27.0
|
|
23
|
+
- Remove falco preset from init command
|
|
24
|
+
|
|
7
25
|
## [0.26.1] - 2026-06-09
|
|
8
26
|
|
|
9
27
|
### 🐛 Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fujin-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.27.1
|
|
4
4
|
Summary: Get your project up and running in a few minutes on your own vps.
|
|
5
5
|
Project-URL: Documentation, https://github.com/Tobi-De/fujin#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/Tobi-De/fujin/issues
|
|
@@ -4,7 +4,7 @@ requires = [ "uv-build>=0.9.18,<0.10" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fujin-secrets-1password"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.27.1"
|
|
8
8
|
description = "1Password secret adapter for Fujin"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.14",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"fujin-cli>=0.
|
|
23
|
+
"fujin-cli>=0.27.1",
|
|
24
24
|
"python-dotenv>=1.0.1",
|
|
25
25
|
]
|
|
26
26
|
|
|
@@ -4,7 +4,7 @@ requires = [ "uv-build>=0.9.18,<0.10" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fujin-secrets-bitwarden"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.27.1"
|
|
8
8
|
description = "Bitwarden secret adapter for Fujin"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.14",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"fujin-cli>=0.
|
|
23
|
+
"fujin-cli>=0.27.1",
|
|
24
24
|
"python-dotenv>=1.0.1",
|
|
25
25
|
]
|
|
26
26
|
|
|
@@ -4,7 +4,7 @@ requires = [ "uv-build>=0.9.18,<0.10" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fujin-secrets-doppler"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.27.1"
|
|
8
8
|
description = "Doppler secret adapter for Fujin"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.14",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"fujin-cli>=0.
|
|
23
|
+
"fujin-cli>=0.27.1",
|
|
24
24
|
"python-dotenv>=1.0.1",
|
|
25
25
|
]
|
|
26
26
|
|
|
@@ -4,7 +4,7 @@ requires = [ "uv-build>=0.9.18,<0.10" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fujin-secrets-env"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.27.1"
|
|
8
8
|
description = "Environment variable secret adapter for Fujin"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -20,7 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.14",
|
|
21
21
|
]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"fujin-cli>=0.
|
|
23
|
+
"fujin-cli>=0.27.1",
|
|
24
24
|
"python-dotenv>=1.0.1",
|
|
25
25
|
]
|
|
26
26
|
|
|
@@ -5,7 +5,7 @@ requires = [ "hatchling" ]
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "fujin-cli"
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.27.1"
|
|
9
9
|
description = "Get your project up and running in a few minutes on your own vps."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = [
|
|
@@ -151,7 +151,7 @@ markers = [
|
|
|
151
151
|
]
|
|
152
152
|
|
|
153
153
|
[tool.bumpversion]
|
|
154
|
-
current_version = "0.
|
|
154
|
+
current_version = "0.27.1"
|
|
155
155
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
156
156
|
serialize = [ "{major}.{minor}.{patch}" ]
|
|
157
157
|
search = "{current_version}"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.27.1"
|
|
@@ -203,15 +203,15 @@ export -f {config.app_name}
|
|
|
203
203
|
logger.debug("Virtual environment already exists")
|
|
204
204
|
|
|
205
205
|
logger.debug("Installing package: %s", config.distfile_name)
|
|
206
|
-
|
|
206
|
+
# Combine distfile + requirements into a single uv invocation.
|
|
207
|
+
# --no-deps applies to the distfile; -r installs deps from requirements.
|
|
208
|
+
install_cmd = (
|
|
209
|
+
f"UV_COMPILE_BYTECODE=1 {uv_python_install_dir} "
|
|
210
|
+
f"{config.uv_path} pip install {distfile_path} --no-deps"
|
|
211
|
+
)
|
|
207
212
|
if config.requirements:
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
run(
|
|
211
|
-
f"{dist_install} --no-deps && {config.uv_path} pip install -r {requirements_path} ",
|
|
212
|
-
)
|
|
213
|
-
else:
|
|
214
|
-
run(dist_install)
|
|
213
|
+
install_cmd += f" -r {bundle_dir / 'requirements.txt'}"
|
|
214
|
+
run(install_cmd)
|
|
215
215
|
|
|
216
216
|
else:
|
|
217
217
|
logger.debug("Installation mode: binary")
|
|
@@ -387,35 +387,29 @@ export -f {config.app_name}
|
|
|
387
387
|
if unit["template_timer_name"]:
|
|
388
388
|
active_units.append(unit["template_timer_name"])
|
|
389
389
|
|
|
390
|
-
# Validate all unit files before enabling/starting (catch errors early)
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if
|
|
396
|
-
|
|
390
|
+
# Validate all unit files before enabling/starting (catch errors early).
|
|
391
|
+
# systemd-analyze accepts multiple paths and reports which files have issues.
|
|
392
|
+
unit_paths = [
|
|
393
|
+
SYSTEMD_SYSTEM_DIR / unit["template_service_name"]
|
|
394
|
+
for unit in config.deployed_units
|
|
395
|
+
if (SYSTEMD_SYSTEM_DIR / unit["template_service_name"]).exists()
|
|
396
|
+
]
|
|
397
|
+
if unit_paths:
|
|
398
|
+
logger.info("Validating systemd unit files...")
|
|
399
|
+
paths_arg = " ".join(shlex.quote(str(p)) for p in unit_paths)
|
|
397
400
|
result = subprocess.run(
|
|
398
|
-
f"systemd-analyze verify {
|
|
401
|
+
f"systemd-analyze verify {paths_arg}",
|
|
399
402
|
shell=True,
|
|
400
403
|
capture_output=True,
|
|
401
404
|
text=True,
|
|
402
405
|
)
|
|
403
406
|
if result.returncode != 0 or result.stderr.strip():
|
|
404
|
-
validation_issues += 1
|
|
405
407
|
logger.warning(
|
|
406
|
-
"Validation issues
|
|
407
|
-
unit_path.name,
|
|
408
|
-
result.returncode,
|
|
408
|
+
"Validation issues detected (exit code %d):", result.returncode
|
|
409
409
|
)
|
|
410
410
|
for line in result.stderr.splitlines():
|
|
411
411
|
logger.warning(" %s", line)
|
|
412
412
|
|
|
413
|
-
if validation_issues:
|
|
414
|
-
logger.warning(
|
|
415
|
-
"%d unit(s) have validation warnings - review above before continuing",
|
|
416
|
-
validation_issues,
|
|
417
|
-
)
|
|
418
|
-
|
|
419
413
|
if not active_units:
|
|
420
414
|
run("systemctl daemon-reload")
|
|
421
415
|
restart_result = run("true")
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from contextlib import contextmanager
|
|
5
4
|
from dataclasses import dataclass
|
|
6
5
|
from functools import cached_property
|
|
7
|
-
from typing import Annotated
|
|
6
|
+
from typing import Annotated
|
|
8
7
|
|
|
9
8
|
import cappa
|
|
10
9
|
|
|
11
10
|
from fujin.config import Config, HostConfig
|
|
12
|
-
from fujin.connection import SSH2Connection
|
|
13
|
-
from fujin.connection import connection as host_connection
|
|
14
11
|
|
|
15
12
|
_logging_configured = False
|
|
16
13
|
|
|
@@ -74,11 +71,6 @@ class BaseCommand:
|
|
|
74
71
|
def output(self) -> MessageFormatter:
|
|
75
72
|
return MessageFormatter(cappa.Output())
|
|
76
73
|
|
|
77
|
-
@contextmanager
|
|
78
|
-
def connection(self) -> Generator[SSH2Connection, None, None]:
|
|
79
|
-
with host_connection(host=self.selected_host) as conn:
|
|
80
|
-
yield conn
|
|
81
|
-
|
|
82
74
|
|
|
83
75
|
class MessageFormatter:
|
|
84
76
|
"""Enhanced output with built-in color formatting for consistent CLI messaging."""
|
|
@@ -12,6 +12,7 @@ from rich.table import Table
|
|
|
12
12
|
|
|
13
13
|
from fujin.commands import BaseCommand
|
|
14
14
|
from fujin.config import tomllib
|
|
15
|
+
from fujin import connection
|
|
15
16
|
from fujin.discovery import DeployedUnit
|
|
16
17
|
|
|
17
18
|
|
|
@@ -54,7 +55,7 @@ class App(BaseCommand):
|
|
|
54
55
|
if timer_name:
|
|
55
56
|
names.append(timer_name)
|
|
56
57
|
|
|
57
|
-
with
|
|
58
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
58
59
|
shlex.quote(self.config.app_dir)
|
|
59
60
|
fujin_dir = shlex.quote(self.config.install_dir)
|
|
60
61
|
remote_version, _ = conn.run(
|
|
@@ -177,7 +178,7 @@ class App(BaseCommand):
|
|
|
177
178
|
self.output.output(f"[bold]Replicas:[/bold] {deployed_unit.replicas}")
|
|
178
179
|
|
|
179
180
|
# Get status from server
|
|
180
|
-
with
|
|
181
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
181
182
|
# Get detailed status for each instance
|
|
182
183
|
self.output.output("\n[bold]Status:[/bold]")
|
|
183
184
|
for unit_name in deployed_unit.service_instances():
|
|
@@ -257,7 +258,7 @@ class App(BaseCommand):
|
|
|
257
258
|
self,
|
|
258
259
|
command: Annotated[str, cappa.Arg(help="Command to execute")],
|
|
259
260
|
):
|
|
260
|
-
with
|
|
261
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
261
262
|
cmd = f"cd {self.config.app_dir} && source .install/.appenv && {self.config.app_bin} {command}"
|
|
262
263
|
conn.run(
|
|
263
264
|
f"sudo -u {self.config.app_user} bash -c {shlex.quote(cmd)}",
|
|
@@ -307,7 +308,7 @@ class App(BaseCommand):
|
|
|
307
308
|
self._run_service_command("stop", names)
|
|
308
309
|
|
|
309
310
|
def _run_service_command(self, command: str, names: list[str] | None):
|
|
310
|
-
with
|
|
311
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
311
312
|
# Use instances for start/stop/restart (operates on running services)
|
|
312
313
|
units = self._get_runtime_units(names)
|
|
313
314
|
if not units:
|
|
@@ -385,7 +386,7 @@ class App(BaseCommand):
|
|
|
385
386
|
"""
|
|
386
387
|
Show last 50 lines for web process (default)
|
|
387
388
|
"""
|
|
388
|
-
with
|
|
389
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
389
390
|
# Use instances for logs (shows logs from running services)
|
|
390
391
|
units = self._get_runtime_units(names)
|
|
391
392
|
|
|
@@ -425,7 +426,7 @@ class App(BaseCommand):
|
|
|
425
426
|
self.output.output(self._get_available_options())
|
|
426
427
|
return
|
|
427
428
|
|
|
428
|
-
with
|
|
429
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
429
430
|
if name == "caddy" and self.config.caddyfile_exists:
|
|
430
431
|
self.output.output(f"[cyan]# {self.config.caddy_config_path}[/cyan]")
|
|
431
432
|
print()
|
|
@@ -657,7 +658,7 @@ class App(BaseCommand):
|
|
|
657
658
|
self.output.info(f"\nService already at {count} replica(s)")
|
|
658
659
|
|
|
659
660
|
def _scale_on_server(self, service: str, old_count: int, new_count: int):
|
|
660
|
-
with
|
|
661
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
661
662
|
app_name = self.config.app_name
|
|
662
663
|
|
|
663
664
|
if new_count > old_count:
|
|
@@ -11,6 +11,7 @@ from rich.markup import escape
|
|
|
11
11
|
|
|
12
12
|
from fujin.audit import read_logs
|
|
13
13
|
from fujin.commands import BaseCommand
|
|
14
|
+
from fujin import connection
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
@cappa.command(
|
|
@@ -28,7 +29,7 @@ class Audit(BaseCommand):
|
|
|
28
29
|
] = 20
|
|
29
30
|
|
|
30
31
|
def __call__(self):
|
|
31
|
-
with
|
|
32
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
32
33
|
records = read_logs(
|
|
33
34
|
connection=conn,
|
|
34
35
|
app_name=self.config.app_name,
|
|
@@ -24,6 +24,7 @@ from fujin.audit import log_operation
|
|
|
24
24
|
from fujin.commands import BaseCommand
|
|
25
25
|
from fujin.commands.rollback import Rollback
|
|
26
26
|
from fujin.config import get_git_short_hash
|
|
27
|
+
from fujin import connection
|
|
27
28
|
from fujin.connection import SSH2Connection
|
|
28
29
|
from fujin.errors import (
|
|
29
30
|
BuildError,
|
|
@@ -577,12 +578,11 @@ class Deploy(BaseCommand):
|
|
|
577
578
|
Acquires the deploy lock on the remote server before yielding the
|
|
578
579
|
connection, and releases it on exit (even on error).
|
|
579
580
|
"""
|
|
580
|
-
with
|
|
581
|
+
with connection.connection(host=self.selected_host, compress=False) as conn:
|
|
581
582
|
install_dir_q = shlex.quote(self.config.install_dir)
|
|
582
583
|
lock_file_q = shlex.quote(f"{self.config.install_dir}/.deploy_lock")
|
|
583
|
-
conn.run(f"mkdir -p {install_dir_q}", hide=True)
|
|
584
584
|
_, acquired = conn.run(
|
|
585
|
-
f"(set -C; echo $$ > {lock_file_q}) 2>/dev/null",
|
|
585
|
+
f"mkdir -p {install_dir_q} && (set -C; echo $$ > {lock_file_q}) 2>/dev/null",
|
|
586
586
|
warn=True,
|
|
587
587
|
hide=True,
|
|
588
588
|
)
|
|
@@ -10,6 +10,7 @@ from rich.prompt import Confirm
|
|
|
10
10
|
from fujin import caddy
|
|
11
11
|
from fujin.audit import log_operation
|
|
12
12
|
from fujin.commands import BaseCommand
|
|
13
|
+
from fujin import connection
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
@cappa.command(
|
|
@@ -48,7 +49,7 @@ class Down(BaseCommand):
|
|
|
48
49
|
if not confirm:
|
|
49
50
|
return
|
|
50
51
|
|
|
51
|
-
with
|
|
52
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
52
53
|
self.output.info("Tearing down project...")
|
|
53
54
|
|
|
54
55
|
app_dir = shlex.quote(self.config.app_dir)
|
|
@@ -24,7 +24,7 @@ class Init(BaseCommand):
|
|
|
24
24
|
profile: Annotated[
|
|
25
25
|
str,
|
|
26
26
|
cappa.Arg(
|
|
27
|
-
choices=["simple", "
|
|
27
|
+
choices=["simple", "binary", "django"],
|
|
28
28
|
short="-p",
|
|
29
29
|
long="--profile",
|
|
30
30
|
help="Configuration profile to use",
|
|
@@ -54,7 +54,6 @@ class Init(BaseCommand):
|
|
|
54
54
|
profile_generators = {
|
|
55
55
|
"simple": self._generate_simple,
|
|
56
56
|
"django": self._generate_django,
|
|
57
|
-
"falco": self._generate_falco,
|
|
58
57
|
"binary": self._generate_binary,
|
|
59
58
|
}
|
|
60
59
|
|
|
@@ -172,11 +171,20 @@ class Init(BaseCommand):
|
|
|
172
171
|
"# Main command - adjust to match your application\n", exec_start_pre_lines
|
|
173
172
|
)
|
|
174
173
|
|
|
175
|
-
# Customize ExecStart for gunicorn
|
|
174
|
+
# Customize ExecStart for gunicorn and add reload with migrations
|
|
176
175
|
service_content = service_content.replace(
|
|
177
176
|
"ExecStart={install_dir}/.venv/bin/python -m myapp.web",
|
|
178
177
|
f"ExecStart={{install_dir}}/.venv/bin/gunicorn {app_name}.wsgi:application --bind 0.0.0.0:8000",
|
|
179
178
|
)
|
|
179
|
+
exec_reload_lines = (
|
|
180
|
+
f"ExecReload={{install_dir}}/.venv/bin/{app_name} migrate\n"
|
|
181
|
+
f"ExecReload={{install_dir}}/.venv/bin/{app_name} collectstatic --no-input\n"
|
|
182
|
+
f"ExecReload=/bin/kill -s HUP $MAINPID\n"
|
|
183
|
+
)
|
|
184
|
+
service_content = service_content.replace(
|
|
185
|
+
"ExecStartPre=/bin/bash -c 'rsync -a --delete staticfiles/",
|
|
186
|
+
f"{exec_reload_lines}ExecStartPre=/bin/bash -c 'rsync -a --delete staticfiles/",
|
|
187
|
+
)
|
|
180
188
|
web_service.write_text(service_content)
|
|
181
189
|
self.output.success(f" Created {web_service}")
|
|
182
190
|
|
|
@@ -184,93 +192,6 @@ class Init(BaseCommand):
|
|
|
184
192
|
fujin_dir, app_name, "localhost:8000", static_path="{app_dir}/staticfiles/"
|
|
185
193
|
)
|
|
186
194
|
|
|
187
|
-
def _generate_falco(self, app_name: str, fujin_dir: Path):
|
|
188
|
-
"""Generate Falco profile: web + worker services."""
|
|
189
|
-
systemd_dir = fujin_dir / "systemd"
|
|
190
|
-
dropin_dir = systemd_dir / "common.d"
|
|
191
|
-
dropin_dir.mkdir(parents=True, exist_ok=True)
|
|
192
|
-
|
|
193
|
-
dropin = dropin_dir / "base.conf"
|
|
194
|
-
dropin.write_text(
|
|
195
|
-
"""
|
|
196
|
-
[Service]
|
|
197
|
-
User={{app_user}}
|
|
198
|
-
WorkingDirectory={{app_dir}}
|
|
199
|
-
EnvironmentFile={{install_dir}}/.env
|
|
200
|
-
Restart=on-failure
|
|
201
|
-
RestartSec=5s
|
|
202
|
-
|
|
203
|
-
# Security Hardening
|
|
204
|
-
NoNewPrivileges=true
|
|
205
|
-
PrivateTmp=true
|
|
206
|
-
ProtectSystem=strict
|
|
207
|
-
ProtectHome=true
|
|
208
|
-
ReadWritePaths={{app_dir}}
|
|
209
|
-
"""
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
web_service = systemd_dir / "web.service"
|
|
213
|
-
web_service.write_text(
|
|
214
|
-
f"""
|
|
215
|
-
[Unit]
|
|
216
|
-
Description={app_name} web
|
|
217
|
-
After=network.target
|
|
218
|
-
Wants={{setup}}
|
|
219
|
-
After={{setup}}
|
|
220
|
-
|
|
221
|
-
[Service]
|
|
222
|
-
UMask=0002
|
|
223
|
-
RuntimeDirectory={app_name}
|
|
224
|
-
RuntimeDirectoryMode=0755
|
|
225
|
-
ExecStart={{install_dir}}/.venv/bin/{app_name} prodserver --uds /run/{app_name}/web.sock
|
|
226
|
-
ExecReload=/bin/kill -s HUP $MAINPID
|
|
227
|
-
KillMode=mixed
|
|
228
|
-
TimeoutStopSec=5
|
|
229
|
-
|
|
230
|
-
[Install]
|
|
231
|
-
WantedBy=multi-user.target
|
|
232
|
-
"""
|
|
233
|
-
)
|
|
234
|
-
self.output.success(f" Created {web_service}")
|
|
235
|
-
|
|
236
|
-
setup_service = systemd_dir / "setup.service"
|
|
237
|
-
setup_service.write_text(
|
|
238
|
-
f"""
|
|
239
|
-
[Unit]
|
|
240
|
-
Description={app_name} setup
|
|
241
|
-
After=network-online.target
|
|
242
|
-
|
|
243
|
-
[Service]
|
|
244
|
-
Type=oneshot
|
|
245
|
-
RemainAfterExit=yes
|
|
246
|
-
ExecStart={{install_dir}}/.venv/bin/{app_name} setup
|
|
247
|
-
|
|
248
|
-
[Install]
|
|
249
|
-
WantedBy=multi-user.target
|
|
250
|
-
"""
|
|
251
|
-
)
|
|
252
|
-
self.output.success(f" Created {setup_service}")
|
|
253
|
-
|
|
254
|
-
worker_service = systemd_dir / "worker.service"
|
|
255
|
-
worker_service.write_text(
|
|
256
|
-
f"""
|
|
257
|
-
[Unit]
|
|
258
|
-
Description={app_name} worker
|
|
259
|
-
Wants={{setup}}
|
|
260
|
-
After={{setup}}
|
|
261
|
-
After=network.target
|
|
262
|
-
|
|
263
|
-
[Service]
|
|
264
|
-
ExecStart={{install_dir}}/.venv/bin/{app_name} db_worker
|
|
265
|
-
|
|
266
|
-
[Install]
|
|
267
|
-
WantedBy=multi-user.target
|
|
268
|
-
"""
|
|
269
|
-
)
|
|
270
|
-
self.output.success(f" Created {worker_service}")
|
|
271
|
-
|
|
272
|
-
self._create_caddyfile(fujin_dir, app_name, f"unix//run/{app_name}/web.sock")
|
|
273
|
-
|
|
274
195
|
def _generate_binary(self, app_name: str, fujin_dir: Path):
|
|
275
196
|
"""Generate binary profile: single binary deployment."""
|
|
276
197
|
systemd_dir = fujin_dir / "systemd"
|
|
@@ -5,6 +5,7 @@ import cappa
|
|
|
5
5
|
from rich.prompt import Confirm
|
|
6
6
|
|
|
7
7
|
from fujin.commands import BaseCommand
|
|
8
|
+
from fujin import connection
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@cappa.command(
|
|
@@ -30,7 +31,7 @@ class Prune(BaseCommand):
|
|
|
30
31
|
raise cappa.Exit("The minimum value for the --keep option is 1", code=1)
|
|
31
32
|
|
|
32
33
|
versions_dir = f"{self.config.install_dir}/.versions"
|
|
33
|
-
with
|
|
34
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
34
35
|
_, success = conn.run(f"test -d {versions_dir}", warn=True, hide=True)
|
|
35
36
|
if not success:
|
|
36
37
|
self.output.info("No versions directory found. Nothing to prune.")
|
|
@@ -10,6 +10,7 @@ from rich.prompt import Confirm, IntPrompt
|
|
|
10
10
|
|
|
11
11
|
from fujin.audit import log_operation
|
|
12
12
|
from fujin.commands import BaseCommand
|
|
13
|
+
from fujin import connection
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
@cappa.command(
|
|
@@ -35,7 +36,7 @@ class Rollback(BaseCommand):
|
|
|
35
36
|
] = False
|
|
36
37
|
|
|
37
38
|
def __call__(self):
|
|
38
|
-
with
|
|
39
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
39
40
|
shlex.quote(self.config.app_dir)
|
|
40
41
|
fujin_dir = shlex.quote(self.config.install_dir)
|
|
41
42
|
result, _ = conn.run(
|
|
@@ -15,6 +15,7 @@ from rich.table import Table
|
|
|
15
15
|
from fujin import caddy
|
|
16
16
|
from fujin.commands import BaseCommand
|
|
17
17
|
from fujin.config import tomllib
|
|
18
|
+
from fujin import connection
|
|
18
19
|
from fujin.errors import SSHKeyError
|
|
19
20
|
|
|
20
21
|
|
|
@@ -32,7 +33,7 @@ class Server(BaseCommand):
|
|
|
32
33
|
|
|
33
34
|
@cappa.command(help="Display information about the host system")
|
|
34
35
|
def status(self):
|
|
35
|
-
with
|
|
36
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
36
37
|
_, result_ok = conn.run("command -v fastfetch", warn=True, hide=True)
|
|
37
38
|
if result_ok:
|
|
38
39
|
conn.run("fastfetch", pty=True)
|
|
@@ -41,7 +42,7 @@ class Server(BaseCommand):
|
|
|
41
42
|
|
|
42
43
|
@cappa.command(help="Setup uv, web proxy, and install necessary dependencies")
|
|
43
44
|
def bootstrap(self):
|
|
44
|
-
with
|
|
45
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
45
46
|
self.output.info("Bootstrapping server...")
|
|
46
47
|
|
|
47
48
|
self._upgrade_system(conn, packages="sqlite3 curl rsync")
|
|
@@ -91,7 +92,7 @@ class Server(BaseCommand):
|
|
|
91
92
|
help="Upgrade server components (Caddy, uv, Python, system packages)"
|
|
92
93
|
)
|
|
93
94
|
def upgrade(self):
|
|
94
|
-
with
|
|
95
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
95
96
|
self.output.info("Upgrading server components...")
|
|
96
97
|
all_success = True
|
|
97
98
|
self._upgrade_system(conn)
|
|
@@ -204,7 +205,7 @@ class Server(BaseCommand):
|
|
|
204
205
|
bool, cappa.Arg(long="--with-password")
|
|
205
206
|
] = False, # no short arg to force explicitness
|
|
206
207
|
):
|
|
207
|
-
with
|
|
208
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
208
209
|
commands = [
|
|
209
210
|
f"sudo adduser --disabled-password --gecos '' {name}",
|
|
210
211
|
f"sudo mkdir -p /home/{name}/.ssh",
|
|
@@ -336,7 +337,7 @@ class Server(BaseCommand):
|
|
|
336
337
|
),
|
|
337
338
|
] = False,
|
|
338
339
|
):
|
|
339
|
-
with
|
|
340
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
340
341
|
if not appenv:
|
|
341
342
|
return conn.run(command, pty=True)
|
|
342
343
|
cmd = f"cd {self.config.app_dir} && source .install/.appenv && {command}"
|
|
@@ -353,7 +354,7 @@ class Server(BaseCommand):
|
|
|
353
354
|
cappa.Arg(long="--user", help="List keys for a specific user"),
|
|
354
355
|
] = None,
|
|
355
356
|
):
|
|
356
|
-
with
|
|
357
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
357
358
|
cat = "sudo cat" if user else "cat"
|
|
358
359
|
path = (
|
|
359
360
|
f"/home/{user}/.ssh/authorized_keys"
|
|
@@ -401,7 +402,7 @@ class Server(BaseCommand):
|
|
|
401
402
|
cappa.Arg(long="--user", help="Add key for a specific user"),
|
|
402
403
|
] = None,
|
|
403
404
|
):
|
|
404
|
-
with
|
|
405
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
405
406
|
comment = f"{name} (fujin@{date.today().isoformat()})"
|
|
406
407
|
temp_key = f"/tmp/fujin_key_{secrets.token_hex(8)}"
|
|
407
408
|
|
|
@@ -460,7 +461,7 @@ class Server(BaseCommand):
|
|
|
460
461
|
cappa.Arg(long="--force", short="-f", help="Skip confirmation prompt"),
|
|
461
462
|
] = False,
|
|
462
463
|
):
|
|
463
|
-
with
|
|
464
|
+
with connection.connection(host=self.selected_host) as conn:
|
|
464
465
|
cat = "sudo cat" if user else "cat"
|
|
465
466
|
path = (
|
|
466
467
|
f"/home/{user}/.ssh/authorized_keys"
|