fix(console): comprehensive TUI rendering fixes
- Fix Enter key detection: handle multiple Enter key formats (\n, \r, \r\n) - Reduce flickering: lower render frequency from 60 FPS to 30 FPS - Fix menu bar visibility: re-render menu bar after content to prevent overwriting - Fix content positioning: explicit line positioning for categories and commands - Fix line shifting: clear lines before writing, control newlines manually - Limit visible items: prevent overflow with maxVisibleCategories/Commands - Improve CPU usage: increase sleep interval when no events processed This fixes: - Enter key not working for selection - Strong flickering of the application - Menu bar not visible or being overwritten - Top half of selection list not displayed - Lines being shifted/misaligned
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
---
|
||||
# Backup Before Redeploy
|
||||
# Creates comprehensive backup of Gitea data, SSL certificates, and configurations
|
||||
# before redeploying Traefik and Gitea stacks
|
||||
|
||||
- name: Backup Before Redeploy
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: no
|
||||
vars:
|
||||
gitea_stack_path: "{{ stacks_base_path }}/gitea"
|
||||
traefik_stack_path: "{{ stacks_base_path }}/traefik"
|
||||
backup_base_path: "{{ backups_path | default('/home/deploy/backups') }}"
|
||||
backup_name: "redeploy-backup-{{ ansible_date_time.epoch }}"
|
||||
|
||||
tasks:
|
||||
- name: Display backup plan
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
BACKUP BEFORE REDEPLOY
|
||||
================================================================================
|
||||
|
||||
This playbook will backup:
|
||||
1. Gitea data (volumes)
|
||||
2. SSL certificates (acme.json)
|
||||
3. Gitea configuration (app.ini)
|
||||
4. Traefik configuration
|
||||
5. PostgreSQL data (if applicable)
|
||||
|
||||
Backup location: {{ backup_base_path }}/{{ backup_name }}
|
||||
|
||||
================================================================================
|
||||
|
||||
- name: Ensure backup directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ backup_base_path }}/{{ backup_name }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
become: yes
|
||||
|
||||
- name: Create backup timestamp file
|
||||
ansible.builtin.copy:
|
||||
content: |
|
||||
Backup created: {{ ansible_date_time.iso8601 }}
|
||||
Backup name: {{ backup_name }}
|
||||
Purpose: Before Traefik/Gitea redeploy
|
||||
dest: "{{ backup_base_path }}/{{ backup_name }}/backup-info.txt"
|
||||
mode: '0644'
|
||||
become: yes
|
||||
|
||||
# ========================================
|
||||
# Backup Gitea Data
|
||||
# ========================================
|
||||
- name: Check Gitea volumes
|
||||
ansible.builtin.shell: |
|
||||
docker volume ls --filter name=gitea --format "{{ '{{' }}.Name{{ '}}' }}"
|
||||
register: gitea_volumes
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Backup Gitea volumes
|
||||
ansible.builtin.shell: |
|
||||
for volume in {{ gitea_volumes.stdout_lines | join(' ') }}; do
|
||||
if [ -n "$volume" ]; then
|
||||
echo "Backing up volume: $volume"
|
||||
docker run --rm \
|
||||
-v "$volume:/source:ro" \
|
||||
-v "{{ backup_base_path }}/{{ backup_name }}:/backup" \
|
||||
alpine tar czf "/backup/gitea-volume-${volume}.tar.gz" -C /source .
|
||||
fi
|
||||
done
|
||||
when: gitea_volumes.stdout_lines | length > 0
|
||||
register: gitea_volumes_backup
|
||||
changed_when: gitea_volumes_backup.rc == 0
|
||||
|
||||
# ========================================
|
||||
# Backup SSL Certificates
|
||||
# ========================================
|
||||
- name: Check if acme.json exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ traefik_stack_path }}/acme.json"
|
||||
register: acme_json_stat
|
||||
|
||||
- name: Backup acme.json
|
||||
ansible.builtin.copy:
|
||||
src: "{{ traefik_stack_path }}/acme.json"
|
||||
dest: "{{ backup_base_path }}/{{ backup_name }}/acme.json"
|
||||
remote_src: yes
|
||||
mode: '0600'
|
||||
when: acme_json_stat.stat.exists
|
||||
register: acme_backup
|
||||
changed_when: acme_backup.changed | default(false)
|
||||
|
||||
# ========================================
|
||||
# Backup Gitea Configuration
|
||||
# ========================================
|
||||
- name: Backup Gitea app.ini
|
||||
ansible.builtin.shell: |
|
||||
cd {{ gitea_stack_path }}
|
||||
docker compose exec -T gitea cat /data/gitea/conf/app.ini > "{{ backup_base_path }}/{{ backup_name }}/gitea-app.ini" 2>/dev/null || echo "Could not read app.ini"
|
||||
register: gitea_app_ini_backup
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Backup Gitea docker-compose.yml
|
||||
ansible.builtin.copy:
|
||||
src: "{{ gitea_stack_path }}/docker-compose.yml"
|
||||
dest: "{{ backup_base_path }}/{{ backup_name }}/gitea-docker-compose.yml"
|
||||
remote_src: yes
|
||||
mode: '0644'
|
||||
register: gitea_compose_backup
|
||||
changed_when: gitea_compose_backup.changed | default(false)
|
||||
|
||||
# ========================================
|
||||
# Backup Traefik Configuration
|
||||
# ========================================
|
||||
- name: Backup Traefik configuration files
|
||||
ansible.builtin.shell: |
|
||||
cd {{ traefik_stack_path }}
|
||||
tar czf "{{ backup_base_path }}/{{ backup_name }}/traefik-config.tar.gz" \
|
||||
traefik.yml \
|
||||
docker-compose.yml \
|
||||
dynamic/ 2>/dev/null || echo "Some files may be missing"
|
||||
register: traefik_config_backup
|
||||
changed_when: traefik_config_backup.rc == 0
|
||||
failed_when: false
|
||||
|
||||
# ========================================
|
||||
# Backup PostgreSQL Data (if applicable)
|
||||
# ========================================
|
||||
- name: Check if PostgreSQL stack exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ stacks_base_path }}/postgresql/docker-compose.yml"
|
||||
register: postgres_compose_exists
|
||||
|
||||
- name: Backup PostgreSQL database (if running)
|
||||
ansible.builtin.shell: |
|
||||
cd {{ stacks_base_path }}/postgresql
|
||||
if docker compose ps postgres | grep -q "Up"; then
|
||||
docker compose exec -T postgres pg_dumpall -U postgres | gzip > "{{ backup_base_path }}/{{ backup_name }}/postgresql-all-{{ ansible_date_time.epoch }}.sql.gz"
|
||||
echo "PostgreSQL backup created"
|
||||
else
|
||||
echo "PostgreSQL not running, skipping backup"
|
||||
fi
|
||||
when: postgres_compose_exists.stat.exists
|
||||
register: postgres_backup
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
# ========================================
|
||||
# Verify Backup
|
||||
# ========================================
|
||||
- name: List backup contents
|
||||
ansible.builtin.shell: |
|
||||
ls -lh "{{ backup_base_path }}/{{ backup_name }}/"
|
||||
register: backup_contents
|
||||
changed_when: false
|
||||
|
||||
- name: Calculate backup size
|
||||
ansible.builtin.shell: |
|
||||
du -sh "{{ backup_base_path }}/{{ backup_name }}" | awk '{print $1}'
|
||||
register: backup_size
|
||||
changed_when: false
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
BACKUP SUMMARY
|
||||
================================================================================
|
||||
|
||||
Backup location: {{ backup_base_path }}/{{ backup_name }}
|
||||
Backup size: {{ backup_size.stdout }}
|
||||
|
||||
Backed up:
|
||||
- Gitea volumes: {% if gitea_volumes_backup.changed %}✅{% else %}ℹ️ No volumes found{% endif %}
|
||||
- SSL certificates (acme.json): {% if acme_backup.changed | default(false) %}✅{% else %}ℹ️ Not found{% endif %}
|
||||
- Gitea app.ini: {% if gitea_app_ini_backup.rc == 0 %}✅{% else %}⚠️ Could not read{% endif %}
|
||||
- Gitea docker-compose.yml: {% if gitea_compose_backup.changed | default(false) %}✅{% else %}ℹ️ Not found{% endif %}
|
||||
- Traefik configuration: {% if traefik_config_backup.rc == 0 %}✅{% else %}⚠️ Some files may be missing{% endif %}
|
||||
- PostgreSQL data: {% if postgres_backup.rc == 0 and 'created' in postgres_backup.stdout %}✅{% else %}ℹ️ Not running or not found{% endif %}
|
||||
|
||||
Backup contents:
|
||||
{{ backup_contents.stdout }}
|
||||
|
||||
================================================================================
|
||||
NEXT STEPS
|
||||
================================================================================
|
||||
|
||||
Backup completed successfully. You can now proceed with redeploy:
|
||||
|
||||
ansible-playbook -i inventory/production.yml playbooks/setup/redeploy-traefik-gitea-clean.yml \
|
||||
--vault-password-file secrets/.vault_pass \
|
||||
-e "backup_name={{ backup_name }}"
|
||||
|
||||
================================================================================
|
||||
|
||||
216
deployment/ansible/playbooks/maintenance/cleanup.yml
Normal file
216
deployment/ansible/playbooks/maintenance/cleanup.yml
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
- name: Cleanup All Containers and Networks on Production Server
|
||||
hosts: production
|
||||
become: no
|
||||
gather_facts: yes
|
||||
|
||||
vars:
|
||||
cleanup_volumes: true # Set to false to preserve volumes
|
||||
|
||||
tasks:
|
||||
- name: Set stacks_base_path if not defined
|
||||
set_fact:
|
||||
stacks_base_path: "{{ stacks_base_path | default('/home/deploy/deployment/stacks') }}"
|
||||
|
||||
- name: Display cleanup warning
|
||||
debug:
|
||||
msg:
|
||||
- "=== WARNING: This will stop and remove ALL containers ==="
|
||||
- "Volumes will be removed: {{ cleanup_volumes }}"
|
||||
- "This will cause downtime for all services"
|
||||
- "Stacks path: {{ stacks_base_path }}"
|
||||
- ""
|
||||
|
||||
- name: List all running containers before cleanup
|
||||
command: docker ps --format 'table {{ "{{" }}.Names{{ "}}" }}\t{{ "{{" }}.Status{{ "}}" }}\t{{ "{{" }}.Ports{{ "}}" }}'
|
||||
register: containers_before
|
||||
changed_when: false
|
||||
|
||||
- name: Display running containers
|
||||
debug:
|
||||
msg: "{{ containers_before.stdout_lines }}"
|
||||
|
||||
# Stop all Docker Compose stacks
|
||||
- name: Stop Traefik stack
|
||||
command: docker compose -f {{ stacks_base_path }}/traefik/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/traefik"
|
||||
ignore_errors: yes
|
||||
register: traefik_stop
|
||||
|
||||
- name: Stop Gitea stack
|
||||
command: docker compose -f {{ stacks_base_path }}/gitea/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/gitea"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop PostgreSQL Production stack
|
||||
command: docker compose -f {{ stacks_base_path }}/postgresql-production/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/postgresql-production"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop PostgreSQL Staging stack
|
||||
command: docker compose -f {{ stacks_base_path }}/postgresql-staging/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/postgresql-staging"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop Redis stack
|
||||
command: docker compose -f {{ stacks_base_path }}/redis/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/redis"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop Docker Registry stack
|
||||
command: docker compose -f {{ stacks_base_path }}/registry/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/registry"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop MinIO stack
|
||||
command: docker compose -f {{ stacks_base_path }}/minio/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/minio"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop Monitoring stack
|
||||
command: docker compose -f {{ stacks_base_path }}/monitoring/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/monitoring"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop Production stack
|
||||
command: docker compose -f {{ stacks_base_path }}/production/docker-compose.base.yml -f {{ stacks_base_path }}/production/docker-compose.production.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/production"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop Staging stack
|
||||
command: docker compose -f {{ stacks_base_path }}/staging/docker-compose.base.yml -f {{ stacks_base_path }}/staging/docker-compose.staging.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/staging"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Stop WireGuard stack
|
||||
command: docker compose -f {{ stacks_base_path }}/wireguard/docker-compose.yml down
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/wireguard"
|
||||
ignore_errors: yes
|
||||
|
||||
# Remove all containers (including stopped ones)
|
||||
- name: Get all container IDs
|
||||
command: docker ps -a -q
|
||||
register: all_containers
|
||||
changed_when: false
|
||||
|
||||
- name: Remove all containers
|
||||
command: docker rm -f {{ item }}
|
||||
loop: "{{ all_containers.stdout_lines }}"
|
||||
when: all_containers.stdout_lines | length > 0
|
||||
ignore_errors: yes
|
||||
|
||||
# Check for port conflicts
|
||||
- name: Check what's using port 80
|
||||
command: sudo ss -tlnp 'sport = :80'
|
||||
register: port_80_check
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display port 80 status
|
||||
debug:
|
||||
msg: "{{ port_80_check.stdout_lines if port_80_check.rc == 0 else 'Port 80 is free or cannot be checked' }}"
|
||||
|
||||
- name: Check what's using port 443
|
||||
command: sudo ss -tlnp 'sport = :443'
|
||||
register: port_443_check
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display port 443 status
|
||||
debug:
|
||||
msg: "{{ port_443_check.stdout_lines if port_443_check.rc == 0 else 'Port 443 is free or cannot be checked' }}"
|
||||
|
||||
# Clean up networks
|
||||
- name: Remove traefik-public network
|
||||
community.docker.docker_network:
|
||||
name: traefik-public
|
||||
state: absent
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Remove app-internal network
|
||||
community.docker.docker_network:
|
||||
name: app-internal
|
||||
state: absent
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Get all custom networks
|
||||
command: docker network ls --format '{{ "{{" }}.Name{{ "}}" }}'
|
||||
register: all_networks
|
||||
changed_when: false
|
||||
|
||||
- name: Remove custom networks (except default ones)
|
||||
community.docker.docker_network:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ all_networks.stdout_lines }}"
|
||||
when:
|
||||
- item not in ['bridge', 'host', 'none']
|
||||
- item not in ['traefik-public', 'app-internal'] # Already removed above
|
||||
ignore_errors: yes
|
||||
|
||||
# Clean up volumes (if requested)
|
||||
- name: Get all volumes
|
||||
command: docker volume ls -q
|
||||
register: all_volumes
|
||||
changed_when: false
|
||||
when: cleanup_volumes | bool
|
||||
|
||||
- name: Remove all volumes
|
||||
command: docker volume rm {{ item }}
|
||||
loop: "{{ all_volumes.stdout_lines }}"
|
||||
when:
|
||||
- cleanup_volumes | bool
|
||||
- all_volumes.stdout_lines | length > 0
|
||||
ignore_errors: yes
|
||||
|
||||
# Final verification
|
||||
- name: List remaining containers
|
||||
command: docker ps -a
|
||||
register: containers_after
|
||||
changed_when: false
|
||||
|
||||
- name: Display remaining containers
|
||||
debug:
|
||||
msg: "{{ containers_after.stdout_lines }}"
|
||||
|
||||
- name: List remaining networks
|
||||
command: docker network ls
|
||||
register: networks_after
|
||||
changed_when: false
|
||||
|
||||
- name: Display remaining networks
|
||||
debug:
|
||||
msg: "{{ networks_after.stdout_lines }}"
|
||||
|
||||
- name: Verify ports 80 and 443 are free
|
||||
command: sudo ss -tlnp 'sport = :{{ item }}'
|
||||
register: port_check
|
||||
changed_when: false
|
||||
failed_when: port_check.rc == 0 and port_check.stdout_lines | length > 0
|
||||
loop:
|
||||
- 80
|
||||
- 443
|
||||
|
||||
- name: Display cleanup summary
|
||||
debug:
|
||||
msg:
|
||||
- "=== Cleanup Complete ==="
|
||||
- "All containers stopped and removed"
|
||||
- "Networks cleaned up"
|
||||
- "Volumes removed: {{ cleanup_volumes }}"
|
||||
- ""
|
||||
- "Next steps:"
|
||||
- "1. Run sync-stacks.yml to sync configurations"
|
||||
- "2. Run setup-infrastructure.yml to deploy fresh infrastructure"
|
||||
|
||||
255
deployment/ansible/playbooks/maintenance/rollback-redeploy.yml
Normal file
255
deployment/ansible/playbooks/maintenance/rollback-redeploy.yml
Normal file
@@ -0,0 +1,255 @@
|
||||
---
|
||||
# Rollback Redeploy
|
||||
# Restores Traefik and Gitea from backup created before redeploy
|
||||
#
|
||||
# Usage:
|
||||
# ansible-playbook -i inventory/production.yml playbooks/maintenance/rollback-redeploy.yml \
|
||||
# --vault-password-file secrets/.vault_pass \
|
||||
# -e "backup_name=redeploy-backup-1234567890"
|
||||
|
||||
- name: Rollback Redeploy
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: no
|
||||
vars:
|
||||
traefik_stack_path: "{{ stacks_base_path }}/traefik"
|
||||
gitea_stack_path: "{{ stacks_base_path }}/gitea"
|
||||
backup_base_path: "{{ backups_path | default('/home/deploy/backups') }}"
|
||||
backup_name: "{{ backup_name | default('') }}"
|
||||
|
||||
tasks:
|
||||
- name: Validate backup name
|
||||
ansible.builtin.fail:
|
||||
msg: "backup_name is required. Use: -e 'backup_name=redeploy-backup-1234567890'"
|
||||
when: backup_name == ""
|
||||
|
||||
- name: Check if backup directory exists
|
||||
ansible.builtin.stat:
|
||||
path: "{{ backup_base_path }}/{{ backup_name }}"
|
||||
register: backup_dir_stat
|
||||
|
||||
- name: Fail if backup not found
|
||||
ansible.builtin.fail:
|
||||
msg: "Backup directory not found: {{ backup_base_path }}/{{ backup_name }}"
|
||||
when: not backup_dir_stat.stat.exists
|
||||
|
||||
- name: Display rollback plan
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
ROLLBACK REDEPLOY
|
||||
================================================================================
|
||||
|
||||
This playbook will restore from backup: {{ backup_base_path }}/{{ backup_name }}
|
||||
|
||||
Steps:
|
||||
1. Stop Traefik and Gitea stacks
|
||||
2. Restore Gitea volumes
|
||||
3. Restore SSL certificates (acme.json)
|
||||
4. Restore Gitea configuration (app.ini)
|
||||
5. Restore Traefik configuration
|
||||
6. Restore PostgreSQL data (if applicable)
|
||||
7. Restart stacks
|
||||
8. Verify
|
||||
|
||||
⚠️ WARNING: This will overwrite current state!
|
||||
|
||||
================================================================================
|
||||
|
||||
# ========================================
|
||||
# 1. STOP STACKS
|
||||
# ========================================
|
||||
- name: Stop Traefik stack
|
||||
ansible.builtin.shell: |
|
||||
cd {{ traefik_stack_path }}
|
||||
docker compose down
|
||||
register: traefik_stop
|
||||
changed_when: traefik_stop.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Stop Gitea stack
|
||||
ansible.builtin.shell: |
|
||||
cd {{ gitea_stack_path }}
|
||||
docker compose down
|
||||
register: gitea_stop
|
||||
changed_when: gitea_stop.rc == 0
|
||||
failed_when: false
|
||||
|
||||
# ========================================
|
||||
# 2. RESTORE GITEA VOLUMES
|
||||
# ========================================
|
||||
- name: List Gitea volume backups
|
||||
ansible.builtin.shell: |
|
||||
ls -1 "{{ backup_base_path }}/{{ backup_name }}/gitea-volume-"*.tar.gz 2>/dev/null || echo ""
|
||||
register: gitea_volume_backups
|
||||
changed_when: false
|
||||
|
||||
- name: Restore Gitea volumes
|
||||
ansible.builtin.shell: |
|
||||
for backup_file in {{ backup_base_path }}/{{ backup_name }}/gitea-volume-*.tar.gz; do
|
||||
if [ -f "$backup_file" ]; then
|
||||
volume_name=$(basename "$backup_file" .tar.gz | sed 's/gitea-volume-//')
|
||||
echo "Restoring volume: $volume_name"
|
||||
docker volume create "$volume_name" 2>/dev/null || true
|
||||
docker run --rm \
|
||||
-v "$volume_name:/target" \
|
||||
-v "{{ backup_base_path }}/{{ backup_name }}:/backup:ro" \
|
||||
alpine sh -c "cd /target && tar xzf /backup/$(basename $backup_file)"
|
||||
fi
|
||||
done
|
||||
when: gitea_volume_backups.stdout != ""
|
||||
register: gitea_volumes_restore
|
||||
changed_when: gitea_volumes_restore.rc == 0
|
||||
|
||||
# ========================================
|
||||
# 3. RESTORE SSL CERTIFICATES
|
||||
# ========================================
|
||||
- name: Restore acme.json
|
||||
ansible.builtin.copy:
|
||||
src: "{{ backup_base_path }}/{{ backup_name }}/acme.json"
|
||||
dest: "{{ traefik_stack_path }}/acme.json"
|
||||
remote_src: yes
|
||||
mode: '0600'
|
||||
register: acme_restore
|
||||
changed_when: acme_restore.rc == 0
|
||||
|
||||
# ========================================
|
||||
# 4. RESTORE CONFIGURATIONS
|
||||
# ========================================
|
||||
- name: Restore Gitea docker-compose.yml
|
||||
ansible.builtin.copy:
|
||||
src: "{{ backup_base_path }}/{{ backup_name }}/gitea-docker-compose.yml"
|
||||
dest: "{{ gitea_stack_path }}/docker-compose.yml"
|
||||
remote_src: yes
|
||||
mode: '0644'
|
||||
register: gitea_compose_restore
|
||||
changed_when: gitea_compose_restore.rc == 0
|
||||
failed_when: false
|
||||
|
||||
- name: Restore Traefik configuration
|
||||
ansible.builtin.shell: |
|
||||
cd {{ traefik_stack_path }}
|
||||
tar xzf "{{ backup_base_path }}/{{ backup_name }}/traefik-config.tar.gz" 2>/dev/null || echo "Some files may be missing"
|
||||
register: traefik_config_restore
|
||||
changed_when: traefik_config_restore.rc == 0
|
||||
failed_when: false
|
||||
|
||||
# ========================================
|
||||
# 5. RESTORE POSTGRESQL DATA
|
||||
# ========================================
|
||||
- name: Find PostgreSQL backup
|
||||
ansible.builtin.shell: |
|
||||
ls -1 "{{ backup_base_path }}/{{ backup_name }}/postgresql-all-"*.sql.gz 2>/dev/null | head -1 || echo ""
|
||||
register: postgres_backup_file
|
||||
changed_when: false
|
||||
|
||||
- name: Restore PostgreSQL database
|
||||
ansible.builtin.shell: |
|
||||
cd {{ stacks_base_path }}/postgresql
|
||||
if docker compose ps postgres | grep -q "Up"; then
|
||||
gunzip -c "{{ postgres_backup_file.stdout }}" | docker compose exec -T postgres psql -U postgres
|
||||
echo "PostgreSQL restored"
|
||||
else
|
||||
echo "PostgreSQL not running, skipping restore"
|
||||
fi
|
||||
when: postgres_backup_file.stdout != ""
|
||||
register: postgres_restore
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
# ========================================
|
||||
# 6. RESTART STACKS
|
||||
# ========================================
|
||||
- name: Deploy Traefik stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ traefik_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: traefik_deploy
|
||||
|
||||
- name: Wait for Traefik to be ready
|
||||
ansible.builtin.shell: |
|
||||
cd {{ traefik_stack_path }}
|
||||
docker compose ps traefik | grep -Eiq "Up|running"
|
||||
register: traefik_ready
|
||||
changed_when: false
|
||||
until: traefik_ready.rc == 0
|
||||
retries: 12
|
||||
delay: 5
|
||||
failed_when: traefik_ready.rc != 0
|
||||
|
||||
- name: Deploy Gitea stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ gitea_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: gitea_deploy
|
||||
|
||||
- name: Restore Gitea app.ini
|
||||
ansible.builtin.shell: |
|
||||
if [ -f "{{ backup_base_path }}/{{ backup_name }}/gitea-app.ini" ]; then
|
||||
cd {{ gitea_stack_path }}
|
||||
docker compose exec -T gitea sh -c "cat > /data/gitea/conf/app.ini" < "{{ backup_base_path }}/{{ backup_name }}/gitea-app.ini"
|
||||
docker compose restart gitea
|
||||
echo "app.ini restored and Gitea restarted"
|
||||
else
|
||||
echo "No app.ini backup found"
|
||||
fi
|
||||
register: gitea_app_ini_restore
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: Wait for Gitea to be ready
|
||||
ansible.builtin.shell: |
|
||||
cd {{ gitea_stack_path }}
|
||||
docker compose ps gitea | grep -Eiq "Up|running"
|
||||
register: gitea_ready
|
||||
changed_when: false
|
||||
until: gitea_ready.rc == 0
|
||||
retries: 12
|
||||
delay: 5
|
||||
failed_when: gitea_ready.rc != 0
|
||||
|
||||
# ========================================
|
||||
# 7. VERIFY
|
||||
# ========================================
|
||||
- name: Wait for Gitea to be healthy
|
||||
ansible.builtin.shell: |
|
||||
cd {{ gitea_stack_path }}
|
||||
docker compose exec -T gitea curl -f http://localhost:3000/api/healthz 2>&1 | grep -q "status.*pass" && echo "HEALTHY" || echo "NOT_HEALTHY"
|
||||
register: gitea_health
|
||||
changed_when: false
|
||||
until: gitea_health.stdout == "HEALTHY"
|
||||
retries: 30
|
||||
delay: 2
|
||||
failed_when: false
|
||||
|
||||
- name: Summary
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
================================================================================
|
||||
ROLLBACK SUMMARY
|
||||
================================================================================
|
||||
|
||||
Restored from backup: {{ backup_base_path }}/{{ backup_name }}
|
||||
|
||||
Restored:
|
||||
- Gitea volumes: {% if gitea_volumes_restore.changed %}✅{% else %}ℹ️ No volumes to restore{% endif %}
|
||||
- SSL certificates: {% if acme_restore.changed %}✅{% else %}ℹ️ Not found{% endif %}
|
||||
- Gitea docker-compose.yml: {% if gitea_compose_restore.changed %}✅{% else %}ℹ️ Not found{% endif %}
|
||||
- Traefik configuration: {% if traefik_config_restore.rc == 0 %}✅{% else %}⚠️ Some files may be missing{% endif %}
|
||||
- PostgreSQL data: {% if postgres_restore.rc == 0 and 'restored' in postgres_restore.stdout %}✅{% else %}ℹ️ Not restored{% endif %}
|
||||
- Gitea app.ini: {% if gitea_app_ini_restore.rc == 0 and 'restored' in gitea_app_ini_restore.stdout %}✅{% else %}ℹ️ Not found{% endif %}
|
||||
|
||||
Status:
|
||||
- Traefik: {% if traefik_ready.rc == 0 %}✅ Running{% else %}❌ Not running{% endif %}
|
||||
- Gitea: {% if gitea_ready.rc == 0 %}✅ Running{% else %}❌ Not running{% endif %}
|
||||
- Gitea Health: {% if gitea_health.stdout == 'HEALTHY' %}✅ Healthy{% else %}❌ Not healthy{% endif %}
|
||||
|
||||
Next steps:
|
||||
1. Test Gitea: curl -k https://{{ gitea_domain }}/api/healthz
|
||||
2. Check logs if issues: cd {{ gitea_stack_path }} && docker compose logs gitea --tail=50
|
||||
|
||||
================================================================================
|
||||
|
||||
|
||||
Reference in New Issue
Block a user