fujin-cli 0.22.0__tar.gz → 0.22.2__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.22.0 → fujin_cli-0.22.2}/CHANGELOG.md +12 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/PKG-INFO +1 -1
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-1password/pyproject.toml +2 -2
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-bitwarden/pyproject.toml +2 -2
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-doppler/pyproject.toml +2 -2
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/pyproject.toml +2 -2
- fujin_cli-0.22.2/src/fujin/__init__.py +1 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/_installer.py +9 -3
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/config.py +1 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/connection.py +27 -41
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/uv.lock +5 -5
- fujin_cli-0.22.0/src/fujin/__init__.py +0 -1
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/.github/FUNDING.yml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/.github/workflows/publish.yml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/.github/workflows/test.yml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/.gitignore +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/.pre-commit-config.yaml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/.readthedocs.yaml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/CLAUDE.md +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/LICENSE.txt +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/README.md +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/Vagrantfile +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-cat-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-exec-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-logs-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-restart-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-scale-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-shell-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-start-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-status-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/app-stop-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/audit-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/deploy-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/down-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/fa-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/fujin-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/init-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/migrate-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/new-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/prune-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/rollback-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/server-bootstrap-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/server-create-user-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/server-exec-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/server-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/server-setup-ssh-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/server-status-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/_static/images/help/up-help.png +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/changelog.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/app.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/audit.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/deploy.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/down.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/index.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/init.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/migrate.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/new.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/prune.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/rollback.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/server.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/commands/up.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/conf.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/configuration.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/guides/django-complete.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/guides/index.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/guides/templates.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/howtos/binary.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/howtos/django.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/howtos/index.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/index.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/installation.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/integrations.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/requirements.txt +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/secrets.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/docs/troubleshooting.rst +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/Caddyfile +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/systemd/health.service +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/systemd/health.timer +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/systemd/web.service +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/systemd/worker@.service +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/README.md +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/__main__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/asgi.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/management/commands/health.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/settings.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/urls.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/bookstore/wsgi.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/fujin.toml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/manage.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/pyproject.toml +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/requirements.txt +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/justfile +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-1password/README.md +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-1password/src/fujin_secrets_1password/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-1password/src/fujin_secrets_1password/py.typed +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-bitwarden/README.md +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-bitwarden/src/fujin_secrets_bitwarden/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-bitwarden/src/fujin_secrets_bitwarden/py.typed +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-doppler/README.md +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-doppler/src/fujin_secrets_doppler/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/plugins/fujin-secrets-doppler/src/fujin_secrets_doppler/py.typed +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/__main__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/audit.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/caddy.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/_base.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/app.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/audit.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/deploy.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/down.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/init.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/migrate.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/new.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/prune.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/rollback.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/server.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/showenv.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/commands/up.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/discovery.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/errors.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/fa.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/formatting.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/secrets.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/src/fujin/templates.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/conftest.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/Dockerfile +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/__init__.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/conftest.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/helpers.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/test_app_management.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/test_full_deploy.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/test_installation.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/integration/test_server_bootstrap.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_app.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_audit.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_caddy_domain.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_config.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_connection.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_deploy.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_discovery.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_down.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_init.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_new.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_prune.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_rollback.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_scale.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_secrets.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_server.py +0 -0
- {fujin_cli-0.22.0 → fujin_cli-0.22.2}/tests/test_up.py +0 -0
|
@@ -4,6 +4,18 @@ 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.22.2] - 2026-03-21
|
|
8
|
+
|
|
9
|
+
### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- Handle pre-existing group when creating app user
|
|
12
|
+
|
|
13
|
+
## [0.22.1] - 2026-03-21
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Use native ssh auth first when needed
|
|
18
|
+
|
|
7
19
|
## [0.22.0] - 2026-03-20
|
|
8
20
|
|
|
9
21
|
### 🚀 Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fujin-cli
|
|
3
|
-
Version: 0.22.
|
|
3
|
+
Version: 0.22.2
|
|
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.22.
|
|
7
|
+
version = "0.22.2"
|
|
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.22",
|
|
23
|
+
"fujin-cli>=0.22.2",
|
|
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.22.
|
|
7
|
+
version = "0.22.2"
|
|
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.22",
|
|
23
|
+
"fujin-cli>=0.22.2",
|
|
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.22.
|
|
7
|
+
version = "0.22.2"
|
|
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.22",
|
|
23
|
+
"fujin-cli>=0.22.2",
|
|
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.22.
|
|
8
|
+
version = "0.22.2"
|
|
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 = [
|
|
@@ -149,7 +149,7 @@ markers = [
|
|
|
149
149
|
]
|
|
150
150
|
|
|
151
151
|
[tool.bumpversion]
|
|
152
|
-
current_version = "0.22.
|
|
152
|
+
current_version = "0.22.2"
|
|
153
153
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
|
154
154
|
serialize = [ "{major}.{minor}.{patch}" ]
|
|
155
155
|
search = "{current_version}"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.22.2"
|
|
@@ -118,9 +118,15 @@ def install(
|
|
|
118
118
|
logger.debug("User %s already exists", config.app_user)
|
|
119
119
|
except KeyError:
|
|
120
120
|
logger.debug("Creating system user: %s", config.app_user)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
# Check if group already exists (e.g., from a previous partial install)
|
|
122
|
+
try:
|
|
123
|
+
grp.getgrnam(config.app_user)
|
|
124
|
+
# Group exists, use it instead of creating a new one
|
|
125
|
+
useradd_cmd = f"useradd --system --no-create-home --shell /usr/sbin/nologin --no-user-group -g {config.app_user} {config.app_user}"
|
|
126
|
+
except KeyError:
|
|
127
|
+
# No existing group, let useradd create one
|
|
128
|
+
useradd_cmd = f"useradd --system --no-create-home --shell /usr/sbin/nologin {config.app_user}"
|
|
129
|
+
run(useradd_cmd, check=True)
|
|
124
130
|
|
|
125
131
|
app_dir = Path(config.app_dir)
|
|
126
132
|
app_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -212,6 +212,7 @@ class HostConfig(msgspec.Struct, kw_only=True):
|
|
|
212
212
|
port: int = 22
|
|
213
213
|
_key_filename: str | None = msgspec.field(name="key_filename", default=None)
|
|
214
214
|
key_passphrase_env: str | None = None
|
|
215
|
+
native_ssh_auth: bool = False # Use native ssh for auth (for Tailscale, etc.)
|
|
215
216
|
|
|
216
217
|
def __post_init__(self):
|
|
217
218
|
if self._env_file and self.env_content:
|
|
@@ -344,8 +344,34 @@ def connection(host: HostConfig) -> Generator[SSH2Connection, None, None]:
|
|
|
344
344
|
auth_methods_tried = []
|
|
345
345
|
authenticated = False
|
|
346
346
|
|
|
347
|
+
# For hosts that require native SSH auth (Tailscale, etc.), authenticate via ssh first
|
|
348
|
+
if host.native_ssh_auth:
|
|
349
|
+
logger.info("Using native SSH for authentication...")
|
|
350
|
+
ssh_cmd = [
|
|
351
|
+
"ssh",
|
|
352
|
+
"-p",
|
|
353
|
+
str(host.port),
|
|
354
|
+
"-o",
|
|
355
|
+
"StrictHostKeyChecking=accept-new",
|
|
356
|
+
f"{host.user}@{host.address}",
|
|
357
|
+
"exit",
|
|
358
|
+
]
|
|
359
|
+
result = subprocess.run(ssh_cmd)
|
|
360
|
+
if result.returncode == 0:
|
|
361
|
+
# Reconnect after successful auth - session is now authorized
|
|
362
|
+
sock.close()
|
|
363
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
364
|
+
sock.settimeout(30)
|
|
365
|
+
sock.connect((host.address, host.port))
|
|
366
|
+
sock.settimeout(None)
|
|
367
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
368
|
+
session = Session()
|
|
369
|
+
session.handshake(sock)
|
|
370
|
+
auth_methods_tried.append("native ssh")
|
|
371
|
+
# Continue to normal auth methods below (they should work now)
|
|
372
|
+
|
|
347
373
|
# Method 1: Explicit key file (if specified)
|
|
348
|
-
if host.key_filename:
|
|
374
|
+
if not authenticated and host.key_filename:
|
|
349
375
|
try:
|
|
350
376
|
key_path = Path(host.key_filename).expanduser()
|
|
351
377
|
logger.debug(f"Trying explicit key: {key_path}")
|
|
@@ -416,46 +442,6 @@ def connection(host: HostConfig) -> Generator[SSH2Connection, None, None]:
|
|
|
416
442
|
logger.debug(f"Password auth failed: {e}")
|
|
417
443
|
auth_methods_tried.append("password (failed)")
|
|
418
444
|
|
|
419
|
-
# Method 5: Keyboard-interactive via native SSH (for Tailscale, etc.)
|
|
420
|
-
if not authenticated:
|
|
421
|
-
try:
|
|
422
|
-
available_methods = session.userauth_list(host.user)
|
|
423
|
-
if available_methods and "keyboard-interactive" in available_methods:
|
|
424
|
-
logger.info(
|
|
425
|
-
"Trying keyboard-interactive auth via native SSH (Tailscale, etc.)..."
|
|
426
|
-
)
|
|
427
|
-
# Spawn native ssh to handle interactive auth (shows URL, waits for browser)
|
|
428
|
-
ssh_cmd = [
|
|
429
|
-
"ssh",
|
|
430
|
-
"-p",
|
|
431
|
-
str(host.port),
|
|
432
|
-
"-o",
|
|
433
|
-
"StrictHostKeyChecking=accept-new",
|
|
434
|
-
f"{host.user}@{host.address}",
|
|
435
|
-
"exit",
|
|
436
|
-
]
|
|
437
|
-
result = subprocess.run(ssh_cmd)
|
|
438
|
-
if result.returncode == 0:
|
|
439
|
-
# Auth succeeded via native SSH, retry with ssh2-python
|
|
440
|
-
# Need to create a new session since the old one may be in a bad state
|
|
441
|
-
sock.close()
|
|
442
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
443
|
-
sock.settimeout(30)
|
|
444
|
-
sock.connect((host.address, host.port))
|
|
445
|
-
sock.settimeout(None)
|
|
446
|
-
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
447
|
-
session = Session()
|
|
448
|
-
session.handshake(sock)
|
|
449
|
-
# Retry agent auth (Tailscale adds credentials to agent after browser auth)
|
|
450
|
-
session.agent_auth(host.user)
|
|
451
|
-
authenticated = session.userauth_authenticated()
|
|
452
|
-
if authenticated:
|
|
453
|
-
logger.info("✓ Authenticated after keyboard-interactive flow")
|
|
454
|
-
auth_methods_tried.append("keyboard-interactive (native ssh)")
|
|
455
|
-
except Exception as e:
|
|
456
|
-
logger.debug(f"Keyboard-interactive auth failed: {e}")
|
|
457
|
-
auth_methods_tried.append("keyboard-interactive (failed)")
|
|
458
|
-
|
|
459
445
|
if not authenticated:
|
|
460
446
|
sock.close()
|
|
461
447
|
methods_str = ", ".join(auth_methods_tried) if auth_methods_tried else "none"
|
|
@@ -336,7 +336,7 @@ name = "exceptiongroup"
|
|
|
336
336
|
version = "1.3.1"
|
|
337
337
|
source = { registry = "https://pypi.org/simple" }
|
|
338
338
|
dependencies = [
|
|
339
|
-
{ name = "typing-extensions", marker = "python_full_version < '3.
|
|
339
|
+
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
|
340
340
|
]
|
|
341
341
|
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
342
342
|
wheels = [
|
|
@@ -345,7 +345,7 @@ wheels = [
|
|
|
345
345
|
|
|
346
346
|
[[package]]
|
|
347
347
|
name = "fujin-cli"
|
|
348
|
-
version = "0.
|
|
348
|
+
version = "0.22.0"
|
|
349
349
|
source = { editable = "." }
|
|
350
350
|
dependencies = [
|
|
351
351
|
{ name = "cappa" },
|
|
@@ -401,7 +401,7 @@ docs = [
|
|
|
401
401
|
|
|
402
402
|
[[package]]
|
|
403
403
|
name = "fujin-secrets-1password"
|
|
404
|
-
version = "0.
|
|
404
|
+
version = "0.22.0"
|
|
405
405
|
source = { editable = "plugins/fujin-secrets-1password" }
|
|
406
406
|
dependencies = [
|
|
407
407
|
{ name = "fujin-cli" },
|
|
@@ -416,7 +416,7 @@ requires-dist = [
|
|
|
416
416
|
|
|
417
417
|
[[package]]
|
|
418
418
|
name = "fujin-secrets-bitwarden"
|
|
419
|
-
version = "0.
|
|
419
|
+
version = "0.22.0"
|
|
420
420
|
source = { editable = "plugins/fujin-secrets-bitwarden" }
|
|
421
421
|
dependencies = [
|
|
422
422
|
{ name = "fujin-cli" },
|
|
@@ -431,7 +431,7 @@ requires-dist = [
|
|
|
431
431
|
|
|
432
432
|
[[package]]
|
|
433
433
|
name = "fujin-secrets-doppler"
|
|
434
|
-
version = "0.
|
|
434
|
+
version = "0.22.0"
|
|
435
435
|
source = { editable = "plugins/fujin-secrets-doppler" }
|
|
436
436
|
dependencies = [
|
|
437
437
|
{ name = "fujin-cli" },
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.22.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/systemd/health.service
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fujin_cli-0.22.0 → fujin_cli-0.22.2}/examples/django/bookstore/.fujin/systemd/worker@.service
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|