Initial commit: nult - Ansible deployment toolkit
Merged from veridion-gitea and veridion-act-runner-gitea repos. nult (Null-T) - instant teleportation from Strugatsky's Noon Universe. Like Null-T, this toolkit instantly deploys infrastructure. Roles: - gitea: Gitea server with PostgreSQL (Docker Compose) - act_runner: Gitea Actions runner Playbooks: - gitea.yml: Deploy Gitea server - act-runner.yml: Deploy Act Runner - site.yml: Deploy all services Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
138
roles/gitea/tasks/backup.yml
Normal file
138
roles/gitea/tasks/backup.yml
Normal file
@@ -0,0 +1,138 @@
|
||||
---
|
||||
# =============================================================================
|
||||
# Backup Tasks - Using Official Gitea Dump
|
||||
# =============================================================================
|
||||
#
|
||||
# Uses the official `gitea dump` command for comprehensive backup.
|
||||
# Reference: https://docs.gitea.com/administration/backup-and-restore
|
||||
#
|
||||
# The dump creates a ZIP containing:
|
||||
# - app.ini and custom/ directory (configuration)
|
||||
# - All git repositories
|
||||
# - Database SQL dump (gitea-db.sql)
|
||||
# - Attachments, avatars, LFS objects
|
||||
#
|
||||
# Note: We run the dump while Gitea is running. The official docs recommend
|
||||
# stopping Gitea for perfect consistency, but docker exec requires a running
|
||||
# container. The consistency risk during the brief dump operation is minimal.
|
||||
# =============================================================================
|
||||
|
||||
# Set up variables for this backup run.
|
||||
# gitea_backup_timestamp: Used in filename for unique identification (e.g., 20260114T143022)
|
||||
# gitea_backup_dir_path: Where backups are stored on the host filesystem
|
||||
- name: Set backup variables
|
||||
ansible.builtin.set_fact:
|
||||
gitea_backup_timestamp: "{{ ansible_facts['date_time']['iso8601_basic_short'] }}"
|
||||
gitea_backup_dir_path: "{{ gitea_install_dir }}/{{ gitea_backup_dir }}"
|
||||
|
||||
# Ensure the backup directory exists on the host.
|
||||
# Mode 0750: owner read/write/execute, group read/execute, others no access
|
||||
- name: Ensure backup directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ gitea_backup_dir_path }}"
|
||||
state: directory
|
||||
mode: "0750"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Create Backup Using Official Gitea Dump
|
||||
# -----------------------------------------------------------------------------
|
||||
# Run the official gitea dump command inside the running container.
|
||||
#
|
||||
# Command breakdown:
|
||||
# docker exec {{ container }} - Execute command inside the container
|
||||
# /usr/local/bin/gitea dump - The gitea binary with dump subcommand
|
||||
# -c /data/gitea/conf/app.ini - Path to config file (tells gitea where data is)
|
||||
# -w /tmp - Working directory for temporary files
|
||||
# -f /tmp/gitea-backup-*.zip - Output filename for the backup archive
|
||||
#
|
||||
# Note: We write to /tmp inside the container first, then copy out.
|
||||
# This avoids permission issues with mounted volumes.
|
||||
|
||||
- name: Run gitea dump command
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
docker exec -u git {{ gitea_container_name }}
|
||||
/usr/local/bin/gitea dump
|
||||
-c /data/gitea/conf/app.ini
|
||||
-w /tmp
|
||||
-f /tmp/gitea-backup-{{ gitea_backup_timestamp }}.zip
|
||||
register: gitea_dump_result
|
||||
changed_when: true
|
||||
|
||||
# Copy backup from container to host filesystem
|
||||
# docker cp copies files between container and host.
|
||||
# Format: docker cp container:/path/in/container /path/on/host
|
||||
- name: Copy backup from container to host
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
docker cp
|
||||
{{ gitea_container_name }}:/tmp/gitea-backup-{{ gitea_backup_timestamp }}.zip
|
||||
{{ gitea_backup_dir_path }}/gitea-backup-{{ gitea_backup_timestamp }}.zip
|
||||
changed_when: true
|
||||
|
||||
# Clean up the backup file inside the container
|
||||
# We don't want to leave large files in the container's /tmp
|
||||
# failed_when: false - Don't fail if cleanup fails (backup already succeeded)
|
||||
- name: Remove backup file from container
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
docker exec -u git {{ gitea_container_name }}
|
||||
rm /tmp/gitea-backup-{{ gitea_backup_timestamp }}.zip
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Backup Verification
|
||||
# -----------------------------------------------------------------------------
|
||||
# Verify the backup was actually created and is not empty.
|
||||
# This catches cases where the command succeeded but produced no output.
|
||||
|
||||
- name: Verify backup file exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ gitea_backup_dir_path }}/gitea-backup-{{ gitea_backup_timestamp }}.zip"
|
||||
register: gitea_backup_file
|
||||
|
||||
# Display backup info for operator visibility
|
||||
# Size calculation: bytes / 1048576 = megabytes (1024*1024)
|
||||
- name: Display backup info
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
Backup completed: {{ gitea_backup_dir_path }}/gitea-backup-{{ gitea_backup_timestamp }}.zip
|
||||
Size: {{ (gitea_backup_file.stat.size / 1048576) | round(2) }}MB
|
||||
when: gitea_backup_file.stat.exists | default(false)
|
||||
|
||||
- name: Fail if backup file is missing or empty
|
||||
ansible.builtin.fail:
|
||||
msg: "Backup file is missing or empty. Cannot proceed."
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- not gitea_backup_file.stat.exists or gitea_backup_file.stat.size == 0
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Backup Rotation
|
||||
# -----------------------------------------------------------------------------
|
||||
# Keep only the N most recent backups (controlled by gitea_backup_retention).
|
||||
# This prevents disk space from being exhausted by old backups.
|
||||
|
||||
# Find all existing backup files matching our naming pattern
|
||||
- name: Find old backup files
|
||||
ansible.builtin.find:
|
||||
paths: "{{ gitea_backup_dir_path }}"
|
||||
patterns: "gitea-backup-*.zip"
|
||||
file_type: file
|
||||
register: gitea_old_backup_files
|
||||
|
||||
# Delete old backups, keeping only the most recent ones.
|
||||
# Logic breakdown:
|
||||
# gitea_old_backup_files.files | sort(attribute='mtime') - Sort by modification time (oldest first)
|
||||
# [:-gitea_backup_retention] - Slice: all except last N items
|
||||
# (Python slice notation: [:-5] = all but last 5)
|
||||
# Example: If we have 7 backups and retention=5, this deletes the 2 oldest.
|
||||
- name: Remove old backups beyond retention limit
|
||||
ansible.builtin.file:
|
||||
path: "{{ item.path }}"
|
||||
state: absent
|
||||
loop: "{{ (gitea_old_backup_files.files | sort(attribute='mtime'))[:-gitea_backup_retention] }}"
|
||||
loop_control:
|
||||
label: "{{ item.path | basename }}"
|
||||
when: gitea_old_backup_files.files | length > gitea_backup_retention
|
||||
Reference in New Issue
Block a user