djaploy 1.3.0__tar.gz → 1.3.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.
Files changed (92) hide show
  1. {djaploy-1.3.0 → djaploy-1.3.2}/PKG-INFO +1 -1
  2. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/rclone/infra/djaploy_hooks.py +48 -23
  3. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/bin/django_pyinfra.py +17 -0
  4. djaploy-1.3.2/djaploy/version.py +1 -0
  5. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy.egg-info/PKG-INFO +1 -1
  6. {djaploy-1.3.0 → djaploy-1.3.2}/pyproject.toml +1 -1
  7. djaploy-1.3.0/djaploy/version.py +0 -1
  8. {djaploy-1.3.0 → djaploy-1.3.2}/LICENSE +0 -0
  9. {djaploy-1.3.0 → djaploy-1.3.2}/README.md +0 -0
  10. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/__init__.py +0 -0
  11. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/app.py +0 -0
  12. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/__init__.py +0 -0
  13. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/borg/__init__.py +0 -0
  14. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/borg/infra/__init__.py +0 -0
  15. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/borg/infra/djaploy_hooks.py +0 -0
  16. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/janitor/__init__.py +0 -0
  17. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/janitor/apps.py +0 -0
  18. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/janitor/infra/__init__.py +0 -0
  19. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/janitor/infra/commands/createjanitoruser.py +0 -0
  20. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/janitor/infra/djaploy_hooks.py +0 -0
  21. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/nginx/__init__.py +0 -0
  22. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/nginx/apps.py +0 -0
  23. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/nginx/infra/__init__.py +0 -0
  24. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/nginx/infra/djaploy_hooks.py +0 -0
  25. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/rclone/__init__.py +0 -0
  26. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/rclone/apps.py +0 -0
  27. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/rclone/infra/__init__.py +0 -0
  28. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/sync_certs/__init__.py +0 -0
  29. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/sync_certs/apps.py +0 -0
  30. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/sync_certs/infra/__init__.py +0 -0
  31. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/sync_certs/infra/djaploy_hooks.py +0 -0
  32. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/systemd/__init__.py +0 -0
  33. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/systemd/apps.py +0 -0
  34. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/systemd/infra/__init__.py +0 -0
  35. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/systemd/infra/djaploy_hooks.py +0 -0
  36. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/tailscale/__init__.py +0 -0
  37. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/tailscale/apps.py +0 -0
  38. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/tailscale/infra/__init__.py +0 -0
  39. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/tailscale/infra/djaploy_hooks.py +0 -0
  40. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/versioning/__init__.py +0 -0
  41. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/versioning/apps.py +0 -0
  42. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/versioning/infra/__init__.py +0 -0
  43. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/apps/versioning/infra/djaploy_hooks.py +0 -0
  44. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/artifact.py +0 -0
  45. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/bin/__init__.py +0 -0
  46. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/bin/gunicornherder.py +0 -0
  47. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/builtin_hooks.py +0 -0
  48. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/certificates.py +0 -0
  49. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/changelog.py +0 -0
  50. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/__init__.py +0 -0
  51. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/_utils.py +0 -0
  52. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/activate.py +0 -0
  53. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/configure.py +0 -0
  54. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/deploy.py +0 -0
  55. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/restore.py +0 -0
  56. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/rollback.py +0 -0
  57. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/status.py +0 -0
  58. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/commands/sync_certs.py +0 -0
  59. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/config.py +0 -0
  60. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/deploy.py +0 -0
  61. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/discovery.py +0 -0
  62. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/hooks.py +0 -0
  63. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/infra/__init__.py +0 -0
  64. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/infra/bluegreen.py +0 -0
  65. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/infra/djaploy_hooks.py +0 -0
  66. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/infra/templates.py +0 -0
  67. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/infra/utils.py +0 -0
  68. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/__init__.py +0 -0
  69. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/commands/__init__.py +0 -0
  70. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/commands/djaploy.py +0 -0
  71. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/commands/restore_backup.py +0 -0
  72. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/commands/sync_certs.py +0 -0
  73. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/commands/update_certs.py +0 -0
  74. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/commands/verify.py +0 -0
  75. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/management/utils.py +0 -0
  76. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/notifications.py +0 -0
  77. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/utils.py +0 -0
  78. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy/versioning.py +0 -0
  79. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy.egg-info/SOURCES.txt +0 -0
  80. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy.egg-info/dependency_links.txt +0 -0
  81. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy.egg-info/entry_points.txt +0 -0
  82. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy.egg-info/requires.txt +0 -0
  83. {djaploy-1.3.0 → djaploy-1.3.2}/djaploy.egg-info/top_level.txt +0 -0
  84. {djaploy-1.3.0 → djaploy-1.3.2}/setup.cfg +0 -0
  85. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_bluegreen.py +0 -0
  86. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_borg.py +0 -0
  87. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_config.py +0 -0
  88. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_deploy_scripts.py +0 -0
  89. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_discovery.py +0 -0
  90. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_gunicornherder.py +0 -0
  91. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_hooks.py +0 -0
  92. {djaploy-1.3.0 → djaploy-1.3.2}/tests/test_versioning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: djaploy
3
- Version: 1.3.0
3
+ Version: 1.3.2
4
4
  Summary: Modular Django deployment system based on pyinfra
5
5
  Author-email: Johanna Mae Dimayuga <johanna@techco.fi>
6
6
  License: MIT License
@@ -162,9 +162,17 @@ cleanup() {{
162
162
  # Set trap to cleanup on exit
163
163
  trap cleanup EXIT
164
164
 
165
+ # Retention enforcement (defined here, run before the dump so it still
166
+ # executes even if a later backup step fails)
167
+ {_generate_retention_function(retention_days, host_name)}
168
+
165
169
  # Start backup
166
170
  log_message "Starting backup for {host_name}"
167
171
 
172
+ # Enforce retention up front so stale backups are pruned on every run,
173
+ # independent of whether the dump/upload below succeeds.
174
+ enforce_retention || true
175
+
168
176
  # Create temporary backup directory
169
177
  mkdir -p "$TEMP_BACKUP_DIR"
170
178
 
@@ -174,6 +182,23 @@ log_message "Creating consistent database backups"
174
182
  # List of databases to backup
175
183
  DATABASES=({db_array})
176
184
 
185
+ # Fail fast (with a clear message) if there isn't enough scratch space. VACUUM
186
+ # INTO writes a full copy of every DB, then those are tarred — so we need room
187
+ # for roughly the total DB size plus the archive. The threshold scales from the
188
+ # live DB sizes (1.5x), so it is correct on any host without a hard-coded value.
189
+ REQUIRED_KB=0
190
+ for DB in "${{DATABASES[@]}}"; do
191
+ if [ -f "$DB_DIR/$DB" ]; then
192
+ REQUIRED_KB=$((REQUIRED_KB + $(du -k "$DB_DIR/$DB" | cut -f1)))
193
+ fi
194
+ done
195
+ REQUIRED_KB=$((REQUIRED_KB * 3 / 2))
196
+ AVAIL_KB=$(df --output=avail "$(dirname "$TEMP_BACKUP_DIR")" | tail -1)
197
+ if [ "${{AVAIL_KB:-0}}" -lt "$REQUIRED_KB" ]; then
198
+ log_message "ERROR: only $((AVAIL_KB/1024))MB free, need ~$((REQUIRED_KB/1024))MB for backup scratch space — aborting DB dump"
199
+ exit 1
200
+ fi
201
+
177
202
  for DB in "${{DATABASES[@]}}"; do
178
203
  if [ -f "$DB_DIR/$DB" ]; then
179
204
  log_message "Backing up database: $DB"
@@ -296,36 +321,36 @@ else
296
321
  log_message "Media directory not found or empty at $MEDIA_DIR, skipping media backup"
297
322
  fi
298
323
 
299
- # Clean up old backups based on retention policy
300
- {_generate_retention_cleanup(retention_days, host_name)}
324
+ # (Retention was already enforced at the start of this run via enforce_retention.)
301
325
 
302
326
  log_message "Backup completed successfully for {host_name}"
303
327
  '''
304
328
 
305
329
 
306
- def _generate_retention_cleanup(retention_days: int, host_name: str) -> str:
307
- """Generate the retention cleanup section of the backup script."""
330
+ def _generate_retention_function(retention_days: int, host_name: str) -> str:
331
+ """Generate an ``enforce_retention()`` shell function for the backup script.
332
+
333
+ Defined near the top of the script and invoked *before* the database dump
334
+ so retention runs on every invocation even if a later step fails. Previously
335
+ the cleanup was the final step, guarded by ``set -e`` and multiple ``exit 1``
336
+ branches, so any dump/upload failure skipped it and old backups accumulated
337
+ without bound (this is what filled the linnahovi disk).
338
+ """
308
339
  if retention_days <= 0:
309
- return ""
340
+ return "enforce_retention() { :; } # retention disabled (retention_days<=0)"
310
341
  return (
311
- 'log_message "Cleaning up backup folders older than {days} days"\n'
312
- '\n'
313
- '# Calculate cutoff date\n'
314
- 'CUTOFF_DATE=$(date -d "{days} days ago" +"%Y-%m-%d")\n'
315
- '\n'
316
- '# List all date folders and delete those older than cutoff\n'
317
- 'rclone lsf "${{REMOTE_NAME}}:{host}/" --dirs-only --config "$RCLONE_CONFIG" | while read -r folder; do\n'
318
- ' # Remove trailing slash\n'
319
- ' folder_name="${{folder%/}}"\n'
320
- '\n'
321
- ' # Check if folder name matches date format and is older than cutoff\n'
322
- ' if [[ "$folder_name" =~ ^[0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}}$ ]] && [[ "$folder_name" < "$CUTOFF_DATE" ]]; then\n'
323
- ' log_message "Deleting old backup folder: $folder_name"\n'
324
- ' rclone purge "${{REMOTE_NAME}}:{host}/$folder_name" --config "$RCLONE_CONFIG" --quiet\n'
325
- ' fi\n'
326
- 'done\n'
327
- '\n'
328
- 'log_message "Cleanup completed"'
342
+ 'enforce_retention() {{\n'
343
+ ' log_message "Enforcing retention: removing remote folders older than {days} days"\n'
344
+ ' local cutoff; cutoff=$(date -d "{days} days ago" +"%Y-%m-%d")\n'
345
+ ' rclone lsf "${{REMOTE_NAME}}:{host}/" --dirs-only --config "$RCLONE_CONFIG" 2>>"$LOG_FILE" | while read -r folder; do\n'
346
+ ' local name="${{folder%/}}"\n'
347
+ ' if [[ "$name" =~ ^[0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}}$ ]] && [[ "$name" < "$cutoff" ]]; then\n'
348
+ ' log_message "Deleting old backup folder: $name"\n'
349
+ ' rclone purge "${{REMOTE_NAME}}:{host}/$name" --config "$RCLONE_CONFIG" --quiet 2>>"$LOG_FILE" \\\n'
350
+ ' || log_message "WARN: purge failed for $name"\n'
351
+ ' fi\n'
352
+ ' done\n'
353
+ '}}'
329
354
  ).format(days=retention_days, host=host_name)
330
355
 
331
356
 
@@ -7,6 +7,23 @@ This script sets up Django environment before running pyinfra,
7
7
  allowing inventory files to use Django models and settings.
8
8
  """
9
9
 
10
+ # Monkey-patch before anything else is imported. pyinfra_cli does this in its
11
+ # own __init__, but by then Django has been set up and ssl/threading are already
12
+ # imported — gevent warns about exactly that ("Monkey-patching ssl after ssl has
13
+ # already been imported ... may silently lead to incorrect behaviour"), which is
14
+ # not a warning to live with in a tool that exists to open SSH connections.
15
+ #
16
+ # It also breaks Django projects outright. Patching walks every live object and
17
+ # isinstance()-checks it; that resolves any LazyObject it touches. Django's
18
+ # `django.contrib.admin.sites.site` is one, and it exists whenever anything has
19
+ # imported `django.contrib.admin` — Wagtail does, via
20
+ # `wagtail/admin/admin_url_finder.py`. If the project doesn't have the admin app
21
+ # in INSTALLED_APPS, resolving it raises `LookupError: No installed app with
22
+ # label 'admin'` before pyinfra has connected to anything.
23
+ from gevent import monkey
24
+
25
+ monkey.patch_all()
26
+
10
27
  import os
11
28
  import sys
12
29
 
@@ -0,0 +1 @@
1
+ __version__ = "1.3.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: djaploy
3
- Version: 1.3.0
3
+ Version: 1.3.2
4
4
  Summary: Modular Django deployment system based on pyinfra
5
5
  Author-email: Johanna Mae Dimayuga <johanna@techco.fi>
6
6
  License: MIT License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "djaploy"
3
- version = "1.3.0"
3
+ version = "1.3.2"
4
4
  description = "Modular Django deployment system based on pyinfra"
5
5
  authors = [
6
6
  {name = "Johanna Mae Dimayuga", email = "johanna@techco.fi"}
@@ -1 +0,0 @@
1
- __version__ = "1.3.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