djaploy 1.2.7__tar.gz → 1.2.8__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.
- {djaploy-1.2.7 → djaploy-1.2.8}/PKG-INFO +1 -1
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/systemd/infra/djaploy_hooks.py +5 -5
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/infra/djaploy_hooks.py +45 -11
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy.egg-info/PKG-INFO +1 -1
- {djaploy-1.2.7 → djaploy-1.2.8}/pyproject.toml +1 -1
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_bluegreen.py +1 -1
- {djaploy-1.2.7 → djaploy-1.2.8}/LICENSE +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/README.md +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/app.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/borg/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/borg/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/borg/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/janitor/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/janitor/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/janitor/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/janitor/infra/commands/createjanitoruser.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/janitor/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/nginx/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/nginx/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/nginx/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/nginx/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/rclone/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/rclone/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/rclone/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/rclone/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/sync_certs/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/sync_certs/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/sync_certs/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/sync_certs/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/systemd/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/systemd/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/systemd/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/tailscale/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/tailscale/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/tailscale/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/tailscale/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/versioning/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/versioning/apps.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/versioning/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/apps/versioning/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/artifact.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/bin/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/bin/django_pyinfra.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/bin/gunicornherder.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/builtin_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/certificates.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/changelog.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/_utils.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/activate.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/configure.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/deploy.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/restore.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/rollback.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/status.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/commands/sync_certs.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/config.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/deploy.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/discovery.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/infra/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/infra/bluegreen.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/infra/templates.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/infra/utils.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/commands/__init__.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/commands/djaploy.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/commands/restore_backup.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/commands/sync_certs.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/commands/update_certs.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/commands/verify.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/management/utils.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/notifications.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/utils.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/version.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy/versioning.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy.egg-info/SOURCES.txt +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy.egg-info/dependency_links.txt +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy.egg-info/entry_points.txt +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy.egg-info/requires.txt +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/djaploy.egg-info/top_level.txt +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/setup.cfg +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_borg.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_config.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_deploy_scripts.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_discovery.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_gunicornherder.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_hooks.py +0 -0
- {djaploy-1.2.7 → djaploy-1.2.8}/tests/test_versioning.py +0 -0
|
@@ -40,13 +40,13 @@ def start_services(host_data, artifact_path):
|
|
|
40
40
|
|
|
41
41
|
# Only start if the service unit file exists — custom services
|
|
42
42
|
# (e.g. streaming) may be rendered by project hooks, not djaploy core.
|
|
43
|
+
# Uses if/else so enable/restart failures still surface as errors.
|
|
43
44
|
server.shell(
|
|
44
45
|
name=f"Start {slot_service} if unit exists",
|
|
45
46
|
commands=[
|
|
46
|
-
f"
|
|
47
|
-
f"systemctl enable {slot_service} && "
|
|
48
|
-
f"
|
|
49
|
-
f"echo 'Unit {slot_service}.service not found, skipping'",
|
|
47
|
+
f"if [ -f /etc/systemd/system/{slot_service}.service ]; then "
|
|
48
|
+
f"systemctl enable {slot_service} && systemctl restart {slot_service}; "
|
|
49
|
+
f"else echo 'Unit {slot_service}.service not found, skipping'; fi",
|
|
50
50
|
],
|
|
51
51
|
_sudo=True,
|
|
52
52
|
)
|
|
@@ -61,7 +61,7 @@ def start_services(host_data, artifact_path):
|
|
|
61
61
|
f"systemctl stop {app_name}.service 2>/dev/null || true",
|
|
62
62
|
f"systemctl disable {app_name}.service 2>/dev/null || true",
|
|
63
63
|
f"rm -f /etc/systemd/system/{app_name}.service",
|
|
64
|
-
|
|
64
|
+
"systemctl daemon-reload 2>/dev/null || true",
|
|
65
65
|
],
|
|
66
66
|
_sudo=True,
|
|
67
67
|
)
|
|
@@ -760,6 +760,38 @@ def rollback_release(host_data, release=None):
|
|
|
760
760
|
active_slot = (active_slot or "").strip()
|
|
761
761
|
target_slot = other_slot(active_slot) if active_slot else "blue"
|
|
762
762
|
|
|
763
|
+
# Verify the target slot has a deployment before switching
|
|
764
|
+
server.shell(
|
|
765
|
+
name=f"Verify {target_slot} slot has a deployment for rollback",
|
|
766
|
+
commands=[
|
|
767
|
+
f"python3 -c \""
|
|
768
|
+
f"import json, sys; s=json.load(open('{state_file}')); "
|
|
769
|
+
f"sys.exit(0 if s['slots'].get('{target_slot}') else 1)"
|
|
770
|
+
f"\" || (echo 'ROLLBACK ABORTED: slot {target_slot} has no deployment.' && exit 1)",
|
|
771
|
+
],
|
|
772
|
+
_sudo=True,
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
# Health check: verify the target slot is running before rolling back.
|
|
776
|
+
# Retries up to 3 times with 2s delay.
|
|
777
|
+
socket_path = get_slot_socket_path(app_name, target_slot)
|
|
778
|
+
server.shell(
|
|
779
|
+
name=f"Health check: verify {target_slot} slot is healthy for rollback",
|
|
780
|
+
commands=[
|
|
781
|
+
f"for attempt in 1 2 3; do "
|
|
782
|
+
f"HTTP_CODE=$(curl -o /dev/null -w '%{{http_code}}' --max-time 5 --unix-socket {socket_path} http://localhost/ 2>/dev/null); "
|
|
783
|
+
f"if [ \"$HTTP_CODE\" != \"000\" ]; then "
|
|
784
|
+
f"echo \"Health check passed: {target_slot} slot responding (HTTP $HTTP_CODE)\"; exit 0; fi; "
|
|
785
|
+
f"echo \"Health check attempt $attempt/3: no response, retrying in 2s...\"; "
|
|
786
|
+
f"sleep 2; "
|
|
787
|
+
f"done; "
|
|
788
|
+
f"echo 'ROLLBACK ABORTED: {target_slot} slot is not responding on {socket_path}' && "
|
|
789
|
+
f"echo 'Check: systemctl status {app_name}-{target_slot}.service' && "
|
|
790
|
+
f"exit 1",
|
|
791
|
+
],
|
|
792
|
+
_sudo=True,
|
|
793
|
+
)
|
|
794
|
+
|
|
763
795
|
# Store for activate:post hooks (timers, streaming, custom nginx)
|
|
764
796
|
host.data._bluegreen_activated_slot = target_slot
|
|
765
797
|
|
|
@@ -913,20 +945,22 @@ def activate_bluegreen(host_data):
|
|
|
913
945
|
_sudo=True,
|
|
914
946
|
)
|
|
915
947
|
|
|
916
|
-
# Extra gunicorn-specific check: verify the socket
|
|
948
|
+
# Extra gunicorn-specific check: verify the socket accepts HTTP requests.
|
|
949
|
+
# Retries up to 3 times with 2s delay to allow freshly started gunicorn to boot.
|
|
917
950
|
socket_path = get_slot_socket_path(app_name, target_slot)
|
|
918
951
|
server.shell(
|
|
919
952
|
name=f"Health check: verify {target_slot} gunicorn socket responds",
|
|
920
953
|
commands=[
|
|
921
|
-
f"
|
|
922
|
-
f"
|
|
923
|
-
f"
|
|
924
|
-
f"echo
|
|
925
|
-
f"
|
|
926
|
-
f"
|
|
927
|
-
f"
|
|
928
|
-
f"
|
|
929
|
-
f"
|
|
954
|
+
f"for attempt in 1 2 3 4 5; do "
|
|
955
|
+
f"HTTP_CODE=$(curl -o /dev/null -w '%{{http_code}}' --max-time 5 --unix-socket {socket_path} http://localhost/ 2>/dev/null); "
|
|
956
|
+
f"if [ \"$HTTP_CODE\" != \"000\" ]; then "
|
|
957
|
+
f"echo \"Health check passed: gunicorn responding (HTTP $HTTP_CODE)\"; exit 0; fi; "
|
|
958
|
+
f"echo \"Health check attempt $attempt/5: no response, retrying in 3s...\"; "
|
|
959
|
+
f"sleep 3; "
|
|
960
|
+
f"done; "
|
|
961
|
+
f"echo 'ACTIVATION ABORTED: gunicorn not responding on {socket_path}' && "
|
|
962
|
+
f"echo 'Check: systemctl status {app_name}-{target_slot}.service' && "
|
|
963
|
+
f"exit 1",
|
|
930
964
|
],
|
|
931
965
|
_sudo=True,
|
|
932
966
|
)
|
|
@@ -981,7 +1015,7 @@ def activate_bluegreen(host_data):
|
|
|
981
1015
|
print(f" Venv: {old_venv} -> {new_venv}")
|
|
982
1016
|
|
|
983
1017
|
print(f"\n Previous slot ({p_slot}) is still running.")
|
|
984
|
-
print(
|
|
1018
|
+
print(" To rollback: manage.py djaploy rollback --env <env>\n")
|
|
985
1019
|
|
|
986
1020
|
python_op.call(
|
|
987
1021
|
name="Print activation summary",
|
|
@@ -527,7 +527,7 @@ class TestBluegreenHealthCheck(unittest.TestCase):
|
|
|
527
527
|
)
|
|
528
528
|
|
|
529
529
|
# Must curl the correct socket
|
|
530
|
-
self.assertIn(
|
|
530
|
+
self.assertIn("--unix-socket /run/testapp-green/testapp.sock", cmd)
|
|
531
531
|
# Must check the correct service on fallback
|
|
532
532
|
self.assertIn("systemctl is-active testapp-green.service", cmd)
|
|
533
533
|
# Must abort with exit 1 on failure
|
|
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
|
|
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
|