From c09bf58ea7091527d7506bd7e1e602cd0e92829d Mon Sep 17 00:00:00 2001 From: Horacio Duran Date: Thu, 15 Jan 2026 22:26:27 +0100 Subject: [PATCH] Backport fixes from version in production Per file detail: backup.yml - Added role defaults loading with proper precedence (inventory > vars > defaults) - Fixed pg_dump permissions: now dumps to /tmp first, then moves to backup directory update.yml - Added role defaults loading with proper precedence - Fixed docker exec commands to use --user {{ forgejo_user }} - Added monitoring compose file detection and handling restore.yml - Added role defaults loading with proper precedence - Added monitoring compose file detection and handling - Fixed docker exec for doctor command to use --user {{ forgejo_user }} Makefile - Updated .PHONY with new targets - Replaced auto-generated help with structured categorized help - Added backup-cron and backup-cron-s3 targets for non-interactive backups - Added cron job example in help output --- Makefile | 88 +++++++++++++++++++++++++++++++---- ansible/playbooks/backup.yml | 21 ++++++++- ansible/playbooks/restore.yml | 25 ++++++++-- ansible/playbooks/update.yml | 29 ++++++++++-- 4 files changed, 143 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 3acc506..661736d 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Forgejo Self-Hosting Automation Makefile # Provides convenient commands for deployment, updates, backups, and maintenance -.PHONY: help install deploy update backup restore ssh terraform-init terraform-plan terraform-apply terraform-destroy ansible-ping ansible-setup check-deps +.PHONY: help install deploy update backup backup-cron backup-to-s3 backup-cron-s3 restore ssh terraform-init terraform-plan terraform-apply terraform-destroy ansible-ping ansible-setup check-deps # Default target .DEFAULT_GOAL := help @@ -28,20 +28,80 @@ NC := \033[0m # No Color # Help target help: ## Show this help message - @echo "$(GREEN)Forgejo Self-Hosting - Available Commands$(NC)" + @echo "$(GREEN)════════════════════════════════════════════════════════════════════$(NC)" + @echo "$(GREEN) Forgejo Self-Hosting - Available Commands$(NC)" + @echo "$(GREEN)════════════════════════════════════════════════════════════════════$(NC)" @echo "" @echo "$(YELLOW)First time? Run:$(NC) ./setup-wizard.sh" @echo "" - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(YELLOW)%-20s$(NC) %s\n", $$1, $$2}' + @echo "$(GREEN)── SETUP & INSTALLATION ──$(NC)" + @echo " $(YELLOW)wizard$(NC) Run interactive setup wizard (first-time setup)" + @echo " $(YELLOW)check-deps$(NC) Verify all required tools are installed" + @echo " $(YELLOW)install$(NC) Full install: create infrastructure + deploy Forgejo" @echo "" + @echo "$(GREEN)── DEPLOYMENT ──$(NC)" + @echo " $(YELLOW)deploy$(NC) Deploy/update Forgejo configuration (full)" + @echo " $(YELLOW)deploy-quick$(NC) Quick deploy without dependency checks" + @echo " $(YELLOW)deploy-tags$(NC) Deploy specific components (TAGS=forgejo,caddy,ufw)" + @echo " $(YELLOW)deploy-check$(NC) Dry-run deployment (no changes made)" + @echo "" + @echo "$(GREEN)── INFRASTRUCTURE (Terraform) ──$(NC)" + @echo " $(YELLOW)terraform-init$(NC) Initialize Terraform/Terragrunt" + @echo " $(YELLOW)terraform-plan$(NC) Preview infrastructure changes" + @echo " $(YELLOW)terraform-apply$(NC) Create/update cloud infrastructure" + @echo " $(YELLOW)terraform-output$(NC) Show infrastructure details (IPs, etc.)" + @echo " $(YELLOW)terraform-destroy$(NC) Destroy all infrastructure (DANGEROUS!)" + @echo "" + @echo "$(GREEN)── BACKUP & RESTORE ──$(NC)" + @echo " $(YELLOW)backup$(NC) Create backup (interactive)" + @echo " $(YELLOW)backup-cron$(NC) Create backup (non-interactive, for cron jobs)" + @echo " $(YELLOW)backup-to-s3$(NC) Create backup and upload to S3 (interactive)" + @echo " $(YELLOW)backup-cron-s3$(NC) Create backup and upload to S3 (for cron jobs)" + @echo " $(YELLOW)restore$(NC) Restore from backup (interactive)" + @echo " $(YELLOW)restore-from-s3$(NC) Restore from S3 backup" + @echo "" + @echo "$(GREEN)── UPDATES ──$(NC)" + @echo " $(YELLOW)update$(NC) Update Forgejo to latest version (with backup)" + @echo " $(YELLOW)update-no-backup$(NC) Update Forgejo without creating backup" + @echo "" + @echo "$(GREEN)── MONITORING & MAINTENANCE ──$(NC)" + @echo " $(YELLOW)status$(NC) Show Forgejo container status" + @echo " $(YELLOW)health$(NC) Check Forgejo health endpoint" + @echo " $(YELLOW)logs$(NC) View Forgejo logs (live, Ctrl+C to exit)" + @echo " $(YELLOW)logs-caddy$(NC) View Caddy/HTTPS logs (live)" + @echo " $(YELLOW)restart$(NC) Restart Forgejo container" + @echo "" + @echo "$(GREEN)── SSH & CONNECTIVITY ──$(NC)" + @echo " $(YELLOW)ssh$(NC) SSH into the Forgejo server" + @echo " $(YELLOW)ssh-agent-start$(NC) Start SSH agent and load key" + @echo " $(YELLOW)ssh-agent-check$(NC) Verify SSH agent has keys loaded" + @echo " $(YELLOW)ansible-ping$(NC) Test Ansible connection to server" + @echo "" + @echo "$(GREEN)── SECRETS & CONFIGURATION ──$(NC)" + @echo " $(YELLOW)ansible-vault-edit$(NC) Edit encrypted secrets file" + @echo " $(YELLOW)ansible-vault-create$(NC) Create new encrypted secrets file" + @echo " $(YELLOW)validate$(NC) Validate all configuration files" + @echo "" + @echo "$(GREEN)── INFORMATION ──$(NC)" + @echo " $(YELLOW)info$(NC) Show current project configuration" + @echo " $(YELLOW)version$(NC) Show versions of all tools" + @echo " $(YELLOW)clean$(NC) Remove temporary/cache files" + @echo "" + @echo "$(GREEN)── DANGEROUS OPERATIONS ──$(NC)" + @echo " $(YELLOW)rebuild$(NC) Destroy and recreate everything (DATA LOSS!)" + @echo " $(YELLOW)terraform-destroy$(NC) Destroy all cloud infrastructure (DATA LOSS!)" + @echo "" + @echo "$(GREEN)════════════════════════════════════════════════════════════════════$(NC)" @echo "$(GREEN)Examples:$(NC)" - @echo " ./setup-wizard.sh # Interactive first-time setup" - @echo " make check-deps # Check all dependencies" - @echo " make terraform-apply # Create infrastructure" - @echo " make deploy # Deploy Forgejo" - @echo " make update # Update Forgejo" - @echo " make backup # Create backup" - @echo " make PROVIDER=hetzner deploy # Deploy on Hetzner" + @echo " ./setup-wizard.sh # First-time interactive setup" + @echo " make deploy # Deploy configuration changes" + @echo " make deploy-tags TAGS=forgejo # Update only Forgejo config" + @echo " make backup-cron # Backup (for cron jobs)" + @echo " make PROVIDER=hetzner terraform-plan # Plan Hetzner infrastructure" + @echo "" + @echo "$(GREEN)Cron example (daily backup at 2 AM):$(NC)" + @echo " 0 2 * * * cd /path/to/forgejo-selfhosting && make backup-cron >> /var/log/forgejo-backup.log 2>&1" + @echo "" ## Setup Wizard wizard: ## Run interactive setup wizard (recommended for first-time setup) @@ -157,10 +217,18 @@ backup: ssh-agent-check ## Create backup of Forgejo @ansible-playbook -i $(INVENTORY) $(PLAYBOOK_DIR)/backup.yml --ask-vault-pass @echo "$(GREEN)✓ Backup complete$(NC)" +backup-cron: ## Create backup non-interactively (for cron jobs) + @test -f $(VAULT_PASSWORD_FILE) || { echo "$(RED)Error: Vault password file not found at $(VAULT_PASSWORD_FILE)$(NC)"; echo "Create it with: echo 'your-vault-password' > $(VAULT_PASSWORD_FILE) && chmod 600 $(VAULT_PASSWORD_FILE)"; exit 1; } + @ansible-playbook -i $(INVENTORY) $(PLAYBOOK_DIR)/backup.yml --vault-password-file $(VAULT_PASSWORD_FILE) + backup-to-s3: ssh-agent-check ## Create backup and upload to S3 @echo "$(GREEN)Creating backup and uploading to S3...$(NC)" @ansible-playbook -i $(INVENTORY) $(PLAYBOOK_DIR)/backup.yml --ask-vault-pass --extra-vars "upload_to_s3=true" +backup-cron-s3: ## Create backup and upload to S3 (for cron jobs) + @test -f $(VAULT_PASSWORD_FILE) || { echo "$(RED)Error: Vault password file not found at $(VAULT_PASSWORD_FILE)$(NC)"; exit 1; } + @ansible-playbook -i $(INVENTORY) $(PLAYBOOK_DIR)/backup.yml --vault-password-file $(VAULT_PASSWORD_FILE) --extra-vars "upload_to_s3=true" + ## Restore Commands restore: ssh-agent-check ## Restore Forgejo from backup @echo "$(RED)WARNING: This will restore from backup$(NC)" diff --git a/ansible/playbooks/backup.yml b/ansible/playbooks/backup.yml index dd39397..1d6c7ef 100644 --- a/ansible/playbooks/backup.yml +++ b/ansible/playbooks/backup.yml @@ -17,6 +17,18 @@ upload_to_s3: "{{ forgejo_backup_to_s3 | default(false) }}" pre_tasks: + - name: Load role defaults as fallback + ansible.builtin.include_vars: + file: ../roles/forgejo/defaults/main.yml + name: role_defaults + + - name: Apply all role defaults for undefined variables + ansible.builtin.set_fact: + "{{ item.key }}": "{{ vars[item.key] | default(item.value) }}" + loop: "{{ role_defaults | dict2items }}" + loop_control: + label: "{{ item.key }}" + - name: Display backup information ansible.builtin.debug: msg: | @@ -37,10 +49,15 @@ community.postgresql.postgresql_db: name: "{{ forgejo_db_name }}" state: dump - target: "{{ forgejo_backup_path }}/database-{{ backup_timestamp }}.sql" + target: "/tmp/database-{{ backup_timestamp }}.sql" become_user: postgres when: forgejo_db_type == 'postgres' - + + - name: Move database backup to backup directory + ansible.builtin.command: + cmd: mv /tmp/database-{{ backup_timestamp }}.sql {{ forgejo_backup_path }}/database-{{ backup_timestamp }}.sql + when: forgejo_db_type == 'postgres' + - name: Compress database backup community.general.archive: path: "{{ forgejo_backup_path }}/database-{{ backup_timestamp }}.sql" diff --git a/ansible/playbooks/restore.yml b/ansible/playbooks/restore.yml index 32ef8c7..aa09555 100644 --- a/ansible/playbooks/restore.yml +++ b/ansible/playbooks/restore.yml @@ -18,6 +18,18 @@ force_restore: false pre_tasks: + - name: Load role defaults as fallback + ansible.builtin.include_vars: + file: ../roles/forgejo/defaults/main.yml + name: role_defaults + + - name: Apply all role defaults for undefined variables + ansible.builtin.set_fact: + "{{ item.key }}": "{{ vars[item.key] | default(item.value) }}" + loop: "{{ role_defaults | dict2items }}" + loop_control: + label: "{{ item.key }}" + - name: Validate backup timestamp ansible.builtin.fail: msg: "Please provide backup_timestamp via --extra-vars 'backup_timestamp=20240115T120000'" @@ -100,11 +112,17 @@ register: db_backup failed_when: not db_backup.stat.exists + - name: Check if monitoring compose file exists + ansible.builtin.stat: + path: "{{ forgejo_base_path }}/docker-compose.monitoring.yml" + register: monitoring_compose + - name: Stop Forgejo service community.docker.docker_compose_v2: project_src: "{{ forgejo_base_path }}" + files: "{{ ['docker-compose.yml', 'docker-compose.monitoring.yml'] if monitoring_compose.stat.exists else ['docker-compose.yml'] }}" state: stopped - + - name: Restore PostgreSQL database when: forgejo_db_type == 'postgres' block: @@ -180,8 +198,9 @@ - name: Start Forgejo service community.docker.docker_compose_v2: project_src: "{{ forgejo_base_path }}" + files: "{{ ['docker-compose.yml', 'docker-compose.monitoring.yml'] if monitoring_compose.stat.exists else ['docker-compose.yml'] }}" state: present - + - name: Wait for Forgejo to be ready ansible.builtin.uri: url: "http://localhost:{{ forgejo_http_port }}" @@ -193,7 +212,7 @@ - name: Run integrity checks ansible.builtin.command: - cmd: docker exec forgejo forgejo doctor check --all + cmd: docker exec --user {{ forgejo_user }} forgejo forgejo doctor check --all register: integrity_check failed_when: false diff --git a/ansible/playbooks/update.yml b/ansible/playbooks/update.yml index 6439776..04964bf 100644 --- a/ansible/playbooks/update.yml +++ b/ansible/playbooks/update.yml @@ -16,15 +16,27 @@ skip_backup: false # Override with --extra-vars "skip_backup=true" pre_tasks: + - name: Load role defaults as fallback + ansible.builtin.include_vars: + file: ../roles/forgejo/defaults/main.yml + name: role_defaults + + - name: Apply all role defaults for undefined variables + ansible.builtin.set_fact: + "{{ item.key }}": "{{ vars[item.key] | default(item.value) }}" + loop: "{{ role_defaults | dict2items }}" + loop_control: + label: "{{ item.key }}" + - name: Display update information ansible.builtin.debug: msg: | Updating Forgejo from {{ forgejo_version }} Backup will be created: {{ backup_before_update and not skip_backup }} - + - name: Check current Forgejo version ansible.builtin.command: - cmd: docker exec forgejo forgejo --version + cmd: docker exec --user {{ forgejo_user }} forgejo forgejo --version register: current_version changed_when: false failed_when: false @@ -46,11 +58,17 @@ ansible.builtin.debug: msg: "Backup completed: {{ backup_result.stdout_lines[-1] if backup_result.stdout_lines else 'No output' }}" + - name: Check if monitoring compose file exists + ansible.builtin.stat: + path: "{{ forgejo_base_path }}/docker-compose.monitoring.yml" + register: monitoring_compose + - name: Stop Forgejo service community.docker.docker_compose_v2: project_src: "{{ forgejo_base_path }}" + files: "{{ ['docker-compose.yml', 'docker-compose.monitoring.yml'] if monitoring_compose.stat.exists else ['docker-compose.yml'] }}" state: stopped - + - name: Pull latest Forgejo image community.docker.docker_image: name: "{{ forgejo_docker_image }}:{{ forgejo_version }}" @@ -68,6 +86,7 @@ - name: Start Forgejo service community.docker.docker_compose_v2: project_src: "{{ forgejo_base_path }}" + files: "{{ ['docker-compose.yml', 'docker-compose.monitoring.yml'] if monitoring_compose.stat.exists else ['docker-compose.yml'] }}" state: present pull: always @@ -82,7 +101,7 @@ - name: Check updated version ansible.builtin.command: - cmd: docker exec forgejo forgejo --version + cmd: docker exec --user {{ forgejo_user }} forgejo forgejo --version register: updated_version changed_when: false @@ -92,7 +111,7 @@ - name: Run database migrations ansible.builtin.command: - cmd: docker exec forgejo forgejo migrate + cmd: docker exec --user {{ forgejo_user }} forgejo forgejo migrate register: migrate_result changed_when: "'No migration needed' not in migrate_result.stdout"