diff --git a/DEPLOYMENT_GUIDE.md b/DEPLOYMENT_GUIDE.md index 87ac23c..ef536de 100644 --- a/DEPLOYMENT_GUIDE.md +++ b/DEPLOYMENT_GUIDE.md @@ -117,7 +117,31 @@ scp -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS:~/backup_gitea.db ./ ### Updating Forgejo -To update Forgejo to a newer version: +Forgejo is configured to automatically update to the latest version on a weekly basis. + +#### Automatic Updates + +The system includes an automatic update mechanism that: +- Checks for new Forgejo versions every Sunday at 3:00 AM +- Creates a backup before updating +- Updates the Docker image and restarts the service +- Logs all operations to `/opt/forgejo/logs/update.log` + +To manually trigger an update check: + +```bash +ssh -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS "sudo /opt/forgejo/scripts/update-forgejo.sh" +``` + +To view update logs: + +```bash +ssh -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS "sudo cat /opt/forgejo/logs/update.log" +``` + +#### Manual Updates + +If you prefer to update manually: 1. Edit `ansible/templates/docker-compose.yml.j2` to update the image version 2. Run the Ansible playbook again: diff --git a/README.md b/README.md index 63f19e2..3f63c6b 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,9 @@ SQLITE_JOURNAL_MODE = WAL - **Terraform State**: The Terraform state is stored locally in `terraform/terraform.tfstate` - **VM Updates**: SSH into the VM and run standard Debian update commands -- **Forgejo Updates**: Update the Docker image version in `ansible/templates/docker-compose.yml.j2` +- **Forgejo Updates**: + - Automatic updates: Forgejo is configured to automatically check for and install updates weekly + - Manual updates: Update the Docker image version in `ansible/templates/docker-compose.yml.j2` - **SSL Certificate Renewal**: Certificates are automatically renewed weekly via a cron job - **Database Backups**: To back up the Forgejo database, copy `/opt/forgejo/data/gitea/gitea.db*` from the VM - **Monitoring**: Check the status of services with: @@ -124,6 +126,28 @@ SQLITE_JOURNAL_MODE = WAL ssh -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS "sudo docker ps" ``` +## Auto-Update System + +The deployment includes an automatic update system for Forgejo with the following features: + +- **Weekly Updates**: Checks for and installs new Forgejo versions every Sunday at 3:00 AM +- **Pre-Update Backups**: Creates a backup of critical data before performing updates +- **Rollback Capability**: Maintains backups to allow manual rollback if needed +- **Update Logs**: Detailed logs of update operations at `/opt/forgejo/logs/update.log` +- **Failure Handling**: Proper error detection and reporting + +To manually trigger an update check: + +```bash +ssh -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS "sudo /opt/forgejo/scripts/update-forgejo.sh" +``` + +To view update logs: + +```bash +ssh -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS "sudo cat /opt/forgejo/logs/update.log" +``` + ## Troubleshooting - **SSH Access**: `ssh -i ~/.ssh/id_rsa debian@VM_IP_ADDRESS` diff --git a/ansible/forgejo.yml b/ansible/forgejo.yml index ec82689..f5f0eb3 100644 --- a/ansible/forgejo.yml +++ b/ansible/forgejo.yml @@ -122,3 +122,49 @@ group: root mode: '0755' when: admin_email is defined + + - name: Set up Forgejo auto-update functionality + block: + - name: Create scripts directory + file: + path: "{{ forgejo_data_dir }}/scripts" + state: directory + owner: "{{ forgejo_user_uid }}" + group: "{{ forgejo_user_gid }}" + mode: '0755' + + - name: Create logs directory + file: + path: "{{ forgejo_data_dir }}/logs" + state: directory + owner: "{{ forgejo_user_uid }}" + group: "{{ forgejo_user_gid }}" + mode: '0755' + + - name: Create backups directory + file: + path: "{{ forgejo_data_dir }}/backups" + state: directory + owner: "{{ forgejo_user_uid }}" + group: "{{ forgejo_user_gid }}" + mode: '0755' + + - name: Copy Forgejo auto-update script + template: + src: templates/update-forgejo.sh.j2 + dest: "{{ forgejo_data_dir }}/scripts/update-forgejo.sh" + owner: "{{ forgejo_user_uid }}" + group: "{{ forgejo_user_gid }}" + mode: '0755' + tags: + - update_script + + - name: Set up cron job for Forgejo auto-update + cron: + name: "Forgejo auto-update" + weekday: "0" + hour: "3" + minute: "0" + user: "debian" + job: "{{ forgejo_data_dir }}/scripts/update-forgejo.sh >> {{ forgejo_data_dir }}/logs/cron-update.log 2>&1" + cron_file: "forgejo-update" diff --git a/ansible/templates/forgejo-update-cron.j2 b/ansible/templates/forgejo-update-cron.j2 new file mode 100644 index 0000000..d6ac831 --- /dev/null +++ b/ansible/templates/forgejo-update-cron.j2 @@ -0,0 +1,3 @@ +# Cron job for automatically updating Forgejo +# Run weekly at 3:00 AM on Sunday +0 3 * * 0 {{ forgejo_user }} {{ forgejo_data_dir }}/scripts/update-forgejo.sh >> {{ forgejo_data_dir }}/logs/cron-update.log 2>&1 diff --git a/ansible/templates/update-forgejo.sh.j2 b/ansible/templates/update-forgejo.sh.j2 new file mode 100644 index 0000000..9a1185b --- /dev/null +++ b/ansible/templates/update-forgejo.sh.j2 @@ -0,0 +1,140 @@ +#!/bin/bash +# Script to automatically update Forgejo to the latest version +# Created: {{ ansible_date_time.date }} +# This script is managed by Ansible - manual changes will be overwritten + +set -e +LOG_FILE="{{ forgejo_data_dir }}/logs/update.log" +COMPOSE_FILE="{{ forgejo_data_dir }}/docker-compose.yml" + +# Create log directory if it doesn't exist +mkdir -p "{{ forgejo_data_dir }}/logs" + +# Function for logging +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +# Function to check if Forgejo is running +is_forgejo_running() { + if docker ps | grep -q forgejo; then + return 0 + else + return 1 + fi +} + +# Function to backup Forgejo data +backup_forgejo() { + local backup_dir="{{ forgejo_data_dir }}/backups" + local backup_file="forgejo_backup_$(date '+%Y%m%d_%H%M%S').tar.gz" + + log "Creating backup of Forgejo data..." + mkdir -p "$backup_dir" + + # Backup the data directory (excluding large objects if space is a concern) + tar -czf "$backup_dir/$backup_file" -C "{{ forgejo_data_dir }}" \ + --exclude="data/gitea-repositories" \ + --exclude="data/avatars" \ + --exclude="data/attachments" \ + --exclude="data/lfs" \ + data/ + + log "Backup created at $backup_dir/$backup_file" +} + +# Get the latest Forgejo version +get_latest_version() { + # Redirect log output to stderr so it doesn't get captured + log "Checking for the latest Forgejo version..." >&2 + + local response + local latest_version + + response=$(curl -s --max-time 10 https://codeberg.org/api/v1/repos/forgejo/forgejo/releases/latest) + + # Check if response contains expected field (also redirect to stderr) + if ! echo "$response" | jq -e '.tag_name' > /dev/null; then + log "ERROR: Invalid response from Codeberg API." >&2 + log "Response was: $response" >&2 + exit 1 + fi + + latest_version=$(echo "$response" | jq -r '.tag_name' | sed 's/^v//') + + if [ -z "$latest_version" ]; then + log "ERROR: Failed to retrieve the latest version from Codeberg API." >&2 + exit 1 + fi + + # Only output the version number + echo "$latest_version" +} + +# Get current version +get_current_version() { + docker inspect --format='{% raw %}{{.Config.Image}}{% endraw %}' forgejo | cut -d':' -f2 +} + +# Update Forgejo to the latest version +update_forgejo() { + local latest_version=$1 + local image_name="codeberg.org/forgejo/forgejo" + + log "Current image: $image_name:$(get_current_version)" + + # Check if Forgejo is running + if ! is_forgejo_running; then + log "ERROR: Forgejo is not running. Please start it before updating." + exit 1 + fi + + # Create a backup before updating + backup_forgejo + + # Pull the latest image + log "Pulling the latest Forgejo image... $image_name:$latest_version" + docker pull "$image_name:$latest_version" + + # Update the docker-compose file with the new version + log "Updating docker-compose.yml with the new version..." + sed -i "s|codeberg.org/forgejo/forgejo:[0-9.]*\+*|$image_name:$latest_version|g" "$COMPOSE_FILE" + + # Restart Forgejo with the new version + log "Restarting Forgejo with the new version..." + cd "{{ forgejo_data_dir }}" && docker-compose down && docker-compose up -d + + # Wait for container to restart and verify + sleep 15 + + # Verify the update + NEW_VERSION=$(docker inspect --format='{% raw %}{{.Config.Image}}{% endraw %}' forgejo | cut -d':' -f2) + + if [ "$NEW_VERSION" = "$latest_version" ]; then + log "Forgejo successfully updated to version $latest_version" + else + log "ERROR: Update verification failed. Current version: $NEW_VERSION, Expected: $latest_version" + log "Please check the container logs for more information." + exit 1 + fi +} + +# Main execution +log "=== Forgejo Auto-Update Script Started ===" + +# Get versions +CURRENT_VERSION=$(get_current_version) +LATEST_VERSION=$(get_latest_version) + +log "Current version: $CURRENT_VERSION" +log "Latest version: $LATEST_VERSION" + +# Compare versions and update if needed +if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ]; then + log "A new version is available. Updating from $CURRENT_VERSION to $LATEST_VERSION..." + update_forgejo "$LATEST_VERSION" +else + log "Forgejo is already at the latest version ($CURRENT_VERSION). No update needed." +fi + +log "=== Forgejo Auto-Update Script Completed ==="