fujin-cli 0.26.0__tar.gz → 0.27.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 (155) hide show
  1. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/CHANGELOG.md +12 -0
  2. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/PKG-INFO +1 -1
  3. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-1password/pyproject.toml +2 -2
  4. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-bitwarden/pyproject.toml +2 -2
  5. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-doppler/pyproject.toml +2 -2
  6. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-env/pyproject.toml +2 -2
  7. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/pyproject.toml +2 -2
  8. fujin_cli-0.27.0/src/fujin/__init__.py +1 -0
  9. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/_installer.py +37 -10
  10. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/init.py +11 -90
  11. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/uv.lock +5 -5
  12. fujin_cli-0.26.0/src/fujin/__init__.py +0 -1
  13. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/.github/FUNDING.yml +0 -0
  14. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/.github/workflows/publish.yml +0 -0
  15. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/.github/workflows/test.yml +0 -0
  16. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/.gitignore +0 -0
  17. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/.pre-commit-config.yaml +0 -0
  18. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/.readthedocs.yaml +0 -0
  19. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/CLAUDE.md +0 -0
  20. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/LICENSE.txt +0 -0
  21. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/README.md +0 -0
  22. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/Vagrantfile +0 -0
  23. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-cat-help.png +0 -0
  24. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-exec-help.png +0 -0
  25. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-help.png +0 -0
  26. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-logs-help.png +0 -0
  27. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-restart-help.png +0 -0
  28. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-scale-help.png +0 -0
  29. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-shell-help.png +0 -0
  30. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-start-help.png +0 -0
  31. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-status-help.png +0 -0
  32. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/app-stop-help.png +0 -0
  33. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/audit-help.png +0 -0
  34. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/deploy-help.png +0 -0
  35. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/down-help.png +0 -0
  36. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/fa-help.png +0 -0
  37. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/fujin-help.png +0 -0
  38. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/init-help.png +0 -0
  39. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/migrate-help.png +0 -0
  40. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/new-help.png +0 -0
  41. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/prune-help.png +0 -0
  42. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/rollback-help.png +0 -0
  43. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/server-bootstrap-help.png +0 -0
  44. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/server-create-user-help.png +0 -0
  45. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/server-exec-help.png +0 -0
  46. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/server-help.png +0 -0
  47. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/server-setup-ssh-help.png +0 -0
  48. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/server-status-help.png +0 -0
  49. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/_static/images/help/up-help.png +0 -0
  50. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/changelog.rst +0 -0
  51. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/app.rst +0 -0
  52. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/audit.rst +0 -0
  53. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/deploy.rst +0 -0
  54. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/down.rst +0 -0
  55. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/index.rst +0 -0
  56. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/init.rst +0 -0
  57. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/migrate.rst +0 -0
  58. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/new.rst +0 -0
  59. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/prune.rst +0 -0
  60. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/rollback.rst +0 -0
  61. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/server.rst +0 -0
  62. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/commands/up.rst +0 -0
  63. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/conf.py +0 -0
  64. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/configuration.rst +0 -0
  65. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/guides/django-complete.rst +0 -0
  66. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/guides/index.rst +0 -0
  67. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/guides/templates.rst +0 -0
  68. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/howtos/binary.rst +0 -0
  69. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/howtos/django.rst +0 -0
  70. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/howtos/index.rst +0 -0
  71. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/index.rst +0 -0
  72. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/installation.rst +0 -0
  73. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/integrations.rst +0 -0
  74. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/requirements.txt +0 -0
  75. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/secrets.rst +0 -0
  76. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/docs/troubleshooting.rst +0 -0
  77. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/.fujin/Caddyfile +0 -0
  78. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/.fujin/systemd/health.service +0 -0
  79. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/.fujin/systemd/health.timer +0 -0
  80. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/.fujin/systemd/web.service +0 -0
  81. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/.fujin/systemd/worker@.service +0 -0
  82. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/README.md +0 -0
  83. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/__init__.py +0 -0
  84. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/__main__.py +0 -0
  85. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/asgi.py +0 -0
  86. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/management/commands/health.py +0 -0
  87. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/settings.py +0 -0
  88. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/urls.py +0 -0
  89. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/bookstore/wsgi.py +0 -0
  90. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/fujin.toml +0 -0
  91. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/manage.py +0 -0
  92. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/pyproject.toml +0 -0
  93. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/examples/django/bookstore/requirements.txt +0 -0
  94. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/justfile +0 -0
  95. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-1password/README.md +0 -0
  96. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-1password/src/fujin_secrets_1password/__init__.py +0 -0
  97. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-1password/src/fujin_secrets_1password/py.typed +0 -0
  98. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-bitwarden/README.md +0 -0
  99. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-bitwarden/src/fujin_secrets_bitwarden/__init__.py +0 -0
  100. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-bitwarden/src/fujin_secrets_bitwarden/py.typed +0 -0
  101. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-doppler/README.md +0 -0
  102. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-doppler/src/fujin_secrets_doppler/__init__.py +0 -0
  103. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-doppler/src/fujin_secrets_doppler/py.typed +0 -0
  104. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-env/README.md +0 -0
  105. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/plugins/fujin-secrets-env/src/fujin_secrets_env/__init__.py +0 -0
  106. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/__main__.py +0 -0
  107. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/audit.py +0 -0
  108. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/caddy.py +0 -0
  109. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/__init__.py +0 -0
  110. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/_base.py +0 -0
  111. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/app.py +0 -0
  112. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/audit.py +0 -0
  113. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/deploy.py +0 -0
  114. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/down.py +0 -0
  115. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/migrate.py +0 -0
  116. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/new.py +0 -0
  117. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/prune.py +0 -0
  118. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/rollback.py +0 -0
  119. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/server.py +0 -0
  120. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/showenv.py +0 -0
  121. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/commands/up.py +0 -0
  122. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/config.py +0 -0
  123. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/connection.py +0 -0
  124. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/discovery.py +0 -0
  125. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/errors.py +0 -0
  126. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/fa.py +0 -0
  127. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/formatting.py +0 -0
  128. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/secrets.py +0 -0
  129. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/src/fujin/templates.py +0 -0
  130. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/__init__.py +0 -0
  131. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/conftest.py +0 -0
  132. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/Dockerfile +0 -0
  133. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/__init__.py +0 -0
  134. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/conftest.py +0 -0
  135. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/helpers.py +0 -0
  136. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/test_app_management.py +0 -0
  137. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/test_full_deploy.py +0 -0
  138. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/test_installation.py +0 -0
  139. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/integration/test_server_bootstrap.py +0 -0
  140. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_app.py +0 -0
  141. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_audit.py +0 -0
  142. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_caddy_domain.py +0 -0
  143. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_config.py +0 -0
  144. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_connection.py +0 -0
  145. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_deploy.py +0 -0
  146. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_discovery.py +0 -0
  147. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_down.py +0 -0
  148. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_init.py +0 -0
  149. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_new.py +0 -0
  150. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_prune.py +0 -0
  151. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_rollback.py +0 -0
  152. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_scale.py +0 -0
  153. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_secrets.py +0 -0
  154. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/tests/test_server.py +0 -0
  155. {fujin_cli-0.26.0 → fujin_cli-0.27.0}/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
+ ## [unreleased]
8
+
9
+ ### ⚙️ Miscellaneous Tasks
10
+
11
+ - Remove falco preset from init command
12
+
13
+ ## [0.26.1] - 2026-06-09
14
+
15
+ ### 🐛 Bug Fixes
16
+
17
+ - *(deploy)* Skip reload-or-restart for Type=oneshot services
18
+
7
19
  ## [0.26.0] - 2026-06-05
8
20
 
9
21
  ### 🚀 Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fujin-cli
3
- Version: 0.26.0
3
+ Version: 0.27.0
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.26.0"
7
+ version = "0.26.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.26",
23
+ "fujin-cli>=0.26.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.26.0"
7
+ version = "0.26.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.26",
23
+ "fujin-cli>=0.26.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.26.0"
7
+ version = "0.26.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.26",
23
+ "fujin-cli>=0.26.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.26.0"
7
+ version = "0.26.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.26",
23
+ "fujin-cli>=0.26.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.26.0"
8
+ version = "0.27.0"
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.26.0"
154
+ current_version = "0.27.0"
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.26.1"
@@ -373,8 +373,15 @@ export -f {config.app_name}
373
373
 
374
374
  logger.info("Restarting services...")
375
375
  active_units = []
376
+ oneshot_units = _get_oneshot_units(config.deployed_units)
377
+ if oneshot_units:
378
+ logger.info(
379
+ "Skipping restart of Type=oneshot units: %s",
380
+ ", ".join(sorted(oneshot_units)),
381
+ )
376
382
  for unit in config.deployed_units:
377
- active_units.extend(unit["service_instances"])
383
+ if unit["template_service_name"] not in oneshot_units:
384
+ active_units.extend(unit["service_instances"])
378
385
  if unit["template_socket_name"]:
379
386
  active_units.append(unit["template_socket_name"])
380
387
  if unit["template_timer_name"]:
@@ -409,16 +416,20 @@ export -f {config.app_name}
409
416
  validation_issues,
410
417
  )
411
418
 
412
- units_str = " ".join(active_units)
413
- run(
414
- f"systemctl daemon-reload && systemctl enable {units_str}",
415
- check=True,
416
- )
419
+ if not active_units:
420
+ run("systemctl daemon-reload")
421
+ restart_result = run("true")
422
+ else:
423
+ units_str = " ".join(active_units)
424
+ run(
425
+ f"systemctl daemon-reload && systemctl enable {units_str}",
426
+ check=True,
427
+ )
417
428
 
418
- restart_cmd = "restart" if full_restart else "reload-or-restart"
419
- restart_result = run(
420
- f"systemctl {restart_cmd} {units_str}",
421
- )
429
+ restart_cmd = "restart" if full_restart else "reload-or-restart"
430
+ restart_result = run(
431
+ f"systemctl {restart_cmd} {units_str}",
432
+ )
422
433
 
423
434
  # Check if services are actually running (not just restart command succeeded)
424
435
  # Only check services with no timer or socket - they should run immediately
@@ -426,6 +437,7 @@ export -f {config.app_name}
426
437
  unit["service_instances"]
427
438
  for unit in config.deployed_units
428
439
  if not (unit["template_socket_name"] or unit["template_timer_name"])
440
+ and unit["template_service_name"] not in oneshot_units
429
441
  ]
430
442
  units_to_check = list(chain.from_iterable(units_to_check))
431
443
 
@@ -607,6 +619,21 @@ def uninstall(config: InstallConfig, bundle_dir: Path) -> None:
607
619
  logger.info("Uninstall completed.")
608
620
 
609
621
 
622
+ def _get_oneshot_units(deployed_units: list[DeployedUnit]) -> set[str]:
623
+ """Identify units with Type=oneshot that should be skipped during restart.
624
+
625
+ Oneshot services have no long-running process to reload or restart — they
626
+ complete and exit. Restarting them during deploy is unnecessary and can
627
+ cause spurious failures that trigger rollbacks.
628
+ """
629
+ oneshot = set()
630
+ for unit in deployed_units:
631
+ unit_path = SYSTEMD_SYSTEM_DIR / unit["template_service_name"]
632
+ if unit_path.exists() and "Type=oneshot" in unit_path.read_text():
633
+ oneshot.add(unit["template_service_name"])
634
+ return oneshot
635
+
636
+
610
637
  def run(
611
638
  cmd: str,
612
639
  *,
@@ -24,7 +24,7 @@ class Init(BaseCommand):
24
24
  profile: Annotated[
25
25
  str,
26
26
  cappa.Arg(
27
- choices=["simple", "falco", "binary", "django"],
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"
@@ -346,7 +346,7 @@ wheels = [
346
346
 
347
347
  [[package]]
348
348
  name = "fujin-cli"
349
- version = "0.25.2"
349
+ version = "0.26.0"
350
350
  source = { editable = "." }
351
351
  dependencies = [
352
352
  { name = "cappa" },
@@ -402,7 +402,7 @@ docs = [
402
402
 
403
403
  [[package]]
404
404
  name = "fujin-secrets-1password"
405
- version = "0.25.2"
405
+ version = "0.26.0"
406
406
  source = { editable = "plugins/fujin-secrets-1password" }
407
407
  dependencies = [
408
408
  { name = "fujin-cli" },
@@ -417,7 +417,7 @@ requires-dist = [
417
417
 
418
418
  [[package]]
419
419
  name = "fujin-secrets-bitwarden"
420
- version = "0.25.2"
420
+ version = "0.26.0"
421
421
  source = { editable = "plugins/fujin-secrets-bitwarden" }
422
422
  dependencies = [
423
423
  { name = "fujin-cli" },
@@ -432,7 +432,7 @@ requires-dist = [
432
432
 
433
433
  [[package]]
434
434
  name = "fujin-secrets-doppler"
435
- version = "0.25.2"
435
+ version = "0.26.0"
436
436
  source = { editable = "plugins/fujin-secrets-doppler" }
437
437
  dependencies = [
438
438
  { name = "fujin-cli" },
@@ -447,7 +447,7 @@ requires-dist = [
447
447
 
448
448
  [[package]]
449
449
  name = "fujin-secrets-env"
450
- version = "0.25.2"
450
+ version = "0.26.0"
451
451
  source = { editable = "plugins/fujin-secrets-env" }
452
452
  dependencies = [
453
453
  { name = "fujin-cli" },
@@ -1 +0,0 @@
1
- __version__ = "0.26.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