djaploy 1.2.3__tar.gz → 1.2.4__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.3 → djaploy-1.2.4}/PKG-INFO +1 -1
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/rollback.py +10 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/infra/djaploy_hooks.py +84 -65
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy.egg-info/PKG-INFO +1 -1
- {djaploy-1.2.3 → djaploy-1.2.4}/pyproject.toml +1 -1
- {djaploy-1.2.3 → djaploy-1.2.4}/LICENSE +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/README.md +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/app.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/borg/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/borg/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/borg/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/janitor/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/janitor/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/janitor/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/janitor/infra/commands/createjanitoruser.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/janitor/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/nginx/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/nginx/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/nginx/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/nginx/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/rclone/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/rclone/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/rclone/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/rclone/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/sync_certs/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/sync_certs/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/sync_certs/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/sync_certs/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/systemd/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/systemd/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/systemd/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/systemd/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/tailscale/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/tailscale/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/tailscale/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/tailscale/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/versioning/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/versioning/apps.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/versioning/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/apps/versioning/infra/djaploy_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/artifact.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/bin/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/bin/django_pyinfra.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/bin/gunicornherder.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/builtin_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/certificates.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/changelog.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/_utils.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/activate.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/configure.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/deploy.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/restore.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/status.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/commands/sync_certs.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/config.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/deploy.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/discovery.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/infra/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/infra/bluegreen.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/infra/templates.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/infra/utils.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/commands/__init__.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/commands/djaploy.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/commands/restore_backup.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/commands/sync_certs.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/commands/update_certs.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/commands/verify.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/management/utils.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/notifications.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/utils.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/version.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy/versioning.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy.egg-info/SOURCES.txt +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy.egg-info/dependency_links.txt +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy.egg-info/entry_points.txt +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy.egg-info/requires.txt +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/djaploy.egg-info/top_level.txt +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/setup.cfg +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_bluegreen.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_borg.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_config.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_deploy_scripts.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_discovery.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_gunicornherder.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_hooks.py +0 -0
- {djaploy-1.2.3 → djaploy-1.2.4}/tests/test_versioning.py +0 -0
|
@@ -24,3 +24,13 @@ for hook in registry.get_remote_hooks("rollback"):
|
|
|
24
24
|
_deploy_decorator(hook.function.__name__)(hook.function)(
|
|
25
25
|
host.data, release
|
|
26
26
|
)
|
|
27
|
+
|
|
28
|
+
# For bluegreen rollback, run activate:post hooks to update
|
|
29
|
+
# custom nginx upstream, timers, etc.
|
|
30
|
+
from djaploy.infra.utils import is_bluegreen
|
|
31
|
+
if is_bluegreen(host.data):
|
|
32
|
+
for phase in ("activate:post",):
|
|
33
|
+
for hook in registry.get_remote_hooks(phase):
|
|
34
|
+
_deploy_decorator(hook.function.__name__)(hook.function)(
|
|
35
|
+
host.data
|
|
36
|
+
)
|
|
@@ -712,61 +712,74 @@ def rollback_release(host_data, release=None):
|
|
|
712
712
|
app_path = get_app_path(host_data)
|
|
713
713
|
|
|
714
714
|
if is_bluegreen(host_data):
|
|
715
|
+
from pyinfra import host
|
|
716
|
+
from pyinfra.facts.server import Command
|
|
715
717
|
from djaploy.infra.bluegreen import (
|
|
716
718
|
read_active_slot_cmd, set_active_slot_cmd, other_slot,
|
|
717
719
|
)
|
|
718
720
|
from djaploy.infra.utils import get_slot_socket_path
|
|
719
721
|
from io import StringIO
|
|
720
722
|
from djaploy.infra.templates import NGINX_UPSTREAM_BLUEGREEN, build_template_context
|
|
723
|
+
from jinja2 import Environment
|
|
721
724
|
|
|
722
725
|
app_name = getattr(host_data, 'app_name', None)
|
|
723
726
|
state_file = f"{app_path}/state.json"
|
|
724
727
|
|
|
725
|
-
# Read active slot and
|
|
728
|
+
# Read active slot and compute target
|
|
729
|
+
active_slot = host.get_fact(Command, read_active_slot_cmd(state_file))
|
|
730
|
+
active_slot = (active_slot or "").strip()
|
|
731
|
+
target_slot = other_slot(active_slot) if active_slot else "blue"
|
|
732
|
+
|
|
733
|
+
# Store for activate:post hooks (timers, streaming, custom nginx)
|
|
734
|
+
host.data._bluegreen_activated_slot = target_slot
|
|
735
|
+
|
|
736
|
+
# Update nginx upstream for non-custom setups
|
|
737
|
+
ctx = build_template_context(host_data)
|
|
738
|
+
ctx["active_slot"] = target_slot
|
|
739
|
+
upstream_content = Environment().from_string(NGINX_UPSTREAM_BLUEGREEN).render(**ctx)
|
|
740
|
+
|
|
741
|
+
nginx_cfg = getattr(host_data, 'nginx_conf', None) or {}
|
|
742
|
+
if not nginx_cfg.get("custom"):
|
|
743
|
+
from pyinfra.operations import files
|
|
744
|
+
files.put(
|
|
745
|
+
name=f"Update nginx upstream to {target_slot} slot",
|
|
746
|
+
src=StringIO(upstream_content),
|
|
747
|
+
dest=f"/etc/nginx/sites-available/{app_name}-upstream.conf",
|
|
748
|
+
_sudo=True,
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
# Reload nginx
|
|
726
752
|
server.shell(
|
|
727
|
-
name="
|
|
728
|
-
commands=[
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
753
|
+
name="Test and reload nginx for rollback",
|
|
754
|
+
commands=["nginx -t && nginx -s reload"],
|
|
755
|
+
_sudo=True,
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
# Update state.json
|
|
759
|
+
server.shell(
|
|
760
|
+
name=f"Set active slot to {target_slot} (rollback)",
|
|
761
|
+
commands=[set_active_slot_cmd(state_file, target_slot)],
|
|
734
762
|
_sudo=True,
|
|
735
763
|
_sudo_user=app_user,
|
|
736
764
|
)
|
|
737
765
|
|
|
738
|
-
#
|
|
739
|
-
|
|
740
|
-
for target_slot in ("blue", "green"):
|
|
741
|
-
ctx = build_template_context(host_data)
|
|
742
|
-
ctx["active_slot"] = target_slot
|
|
743
|
-
from jinja2 import Environment
|
|
744
|
-
upstream_content = Environment().from_string(NGINX_UPSTREAM_BLUEGREEN).render(**ctx)
|
|
766
|
+
# Print summary
|
|
767
|
+
from pyinfra.operations import python as python_op
|
|
745
768
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
f'fi',
|
|
754
|
-
],
|
|
755
|
-
_sudo=True,
|
|
756
|
-
)
|
|
769
|
+
def _print_rollback_summary(state_f, t_slot, p_slot):
|
|
770
|
+
info = _read_slot_info_from_remote(host, t_slot, state_f)
|
|
771
|
+
print(f"\n=== Blue-Green Rollback ===")
|
|
772
|
+
print(f" Switched: {p_slot} -> {t_slot}")
|
|
773
|
+
print(f" Release: {info.get('release', 'unknown')}")
|
|
774
|
+
print(f" Python: {info.get('python_interpreter', 'unknown')}")
|
|
775
|
+
print()
|
|
757
776
|
|
|
758
|
-
|
|
759
|
-
name="
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
+ set_active_slot_cmd(state_file, '" + "$TARGET" + "').replace(
|
|
765
|
-
"'" + '"' + " + " + '"' + "$TARGET" + '"' + " + " + '"' + "'",
|
|
766
|
-
"'\"$TARGET\"'"
|
|
767
|
-
),
|
|
768
|
-
],
|
|
769
|
-
_sudo=True,
|
|
777
|
+
python_op.call(
|
|
778
|
+
name="Print rollback summary",
|
|
779
|
+
function=_print_rollback_summary,
|
|
780
|
+
state_f=state_file,
|
|
781
|
+
t_slot=target_slot,
|
|
782
|
+
p_slot=active_slot or "none",
|
|
770
783
|
)
|
|
771
784
|
return
|
|
772
785
|
|
|
@@ -822,37 +835,41 @@ def activate_bluegreen(host_data):
|
|
|
822
835
|
app_path = get_app_path(host_data)
|
|
823
836
|
state_file = f"{app_path}/state.json"
|
|
824
837
|
|
|
825
|
-
#
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
838
|
+
# If called from deploy --activate, the target slot is already known
|
|
839
|
+
# and we can skip the stale-fact verification.
|
|
840
|
+
deploy_target = getattr(host.data, '_bluegreen_target_slot', None)
|
|
841
|
+
if deploy_target:
|
|
842
|
+
# deploy --activate: we just deployed to this slot, activate it
|
|
843
|
+
active_slot_raw = host.get_fact(
|
|
844
|
+
Command,
|
|
845
|
+
read_active_slot_cmd(state_file),
|
|
846
|
+
)
|
|
847
|
+
active_slot = (active_slot_raw or "").strip()
|
|
848
|
+
target_slot = deploy_target
|
|
849
|
+
else:
|
|
850
|
+
# Standalone activate: read state and switch to the other slot
|
|
851
|
+
active_slot_raw = host.get_fact(
|
|
852
|
+
Command,
|
|
853
|
+
read_active_slot_cmd(state_file),
|
|
854
|
+
)
|
|
855
|
+
active_slot = (active_slot_raw or "").strip()
|
|
856
|
+
target_slot = other_slot(active_slot) if active_slot else "blue"
|
|
857
|
+
|
|
858
|
+
# Verify target slot has a deployment (use server.shell to avoid
|
|
859
|
+
# fact caching issues — the check runs at execution time, not plan time)
|
|
860
|
+
current_display = active_slot or "none"
|
|
844
861
|
server.shell(
|
|
845
|
-
name="
|
|
862
|
+
name=f"Verify {target_slot} slot has a deployment",
|
|
846
863
|
commands=[
|
|
847
|
-
f
|
|
848
|
-
f
|
|
849
|
-
f
|
|
850
|
-
f
|
|
851
|
-
f
|
|
864
|
+
f"python3 -c \""
|
|
865
|
+
f"import json, sys; s=json.load(open('{state_file}')); "
|
|
866
|
+
f"sys.exit(0 if s['slots'].get('{target_slot}') else 1)"
|
|
867
|
+
f"\" || (echo 'Nothing to activate: slot {target_slot} has no deployment.' && "
|
|
868
|
+
f"echo 'Current active slot: {current_display}' && "
|
|
869
|
+
f"echo 'Run djaploy deploy first to stage a new version.' && exit 1)",
|
|
852
870
|
],
|
|
853
871
|
_sudo=True,
|
|
854
872
|
)
|
|
855
|
-
return
|
|
856
873
|
|
|
857
874
|
# Store on host.data so activate:post hooks can read it
|
|
858
875
|
host.data._bluegreen_activated_slot = target_slot
|
|
@@ -866,9 +883,11 @@ def activate_bluegreen(host_data):
|
|
|
866
883
|
ctx["active_slot"] = target_slot
|
|
867
884
|
upstream_content = Environment().from_string(NGINX_UPSTREAM_BLUEGREEN).render(**ctx)
|
|
868
885
|
|
|
886
|
+
# Write the upstream config. Custom nginx setups (e.g. bostad) handle
|
|
887
|
+
# their own upstream file via activate:post hooks.
|
|
888
|
+
from pyinfra.operations import files
|
|
869
889
|
nginx_cfg = getattr(host_data, 'nginx_conf', None) or {}
|
|
870
890
|
if not nginx_cfg.get("custom"):
|
|
871
|
-
from pyinfra.operations import files
|
|
872
891
|
files.put(
|
|
873
892
|
name=f"Update nginx upstream to {target_slot} slot",
|
|
874
893
|
src=StringIO(upstream_content),
|
|
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
|
|
File without changes
|