309 lines
11 KiB
YAML
309 lines
11 KiB
YAML
---
|
|
# =============================================================================
|
|
# Gitea Migration Playbook
|
|
# =============================================================================
|
|
# Migrates Gitea from stuslab.cc to code.stuslab.cc with Authentik OAuth
|
|
#
|
|
# Prerequisites:
|
|
# 1. Authentik deployed at auth.stuslab.cc
|
|
# 2. DNS record: code.stuslab.cc -> 94.130.181.201
|
|
# 3. group_vars/vault.yml with authentik bootstrap token
|
|
#
|
|
# Usage:
|
|
# ansible-playbook -i inventory.yml playbook.yml --ask-vault-pass
|
|
#
|
|
# What this playbook does:
|
|
# 1. Validates existing Gitea installation
|
|
# 2. Creates backup of gitea_data/
|
|
# 3. Updates app.ini with new domain settings
|
|
# 4. Deploys updated Caddyfile with redirect
|
|
# 5. Creates OAuth application in Authentik
|
|
# 6. Verifies migration success
|
|
# =============================================================================
|
|
|
|
- name: Migrate Gitea to code.stuslab.cc
|
|
hosts: gitea_servers
|
|
become: true
|
|
vars_files:
|
|
- group_vars/gitea_servers.yml
|
|
- group_vars/vault.yml
|
|
|
|
# ===========================================================================
|
|
# Pre-flight Validation
|
|
# ===========================================================================
|
|
pre_tasks:
|
|
- name: Validate required variables
|
|
ansible.builtin.assert:
|
|
that:
|
|
- gitea_domain is defined
|
|
- gitea_old_domain is defined
|
|
- authentik_domain is defined
|
|
- vault_authentik_bootstrap_token is defined
|
|
- vault_authentik_bootstrap_token != "PASTE_AUTHENTIK_BOOTSTRAP_TOKEN_HERE"
|
|
fail_msg: |
|
|
Required variables not configured!
|
|
1. Copy vault.yml.example to vault.yml
|
|
2. Add your Authentik bootstrap token
|
|
3. Encrypt with: ansible-vault encrypt group_vars/vault.yml
|
|
|
|
- name: Check existing Gitea installation
|
|
ansible.builtin.stat:
|
|
path: "{{ gitea_base_dir }}/gitea_data/gitea/conf/app.ini"
|
|
register: existing_gitea
|
|
|
|
- name: Fail if no existing Gitea found
|
|
ansible.builtin.fail:
|
|
msg: |
|
|
No existing Gitea installation found at {{ gitea_base_dir }}
|
|
Expected app.ini at: {{ gitea_base_dir }}/gitea_data/gitea/conf/app.ini
|
|
when: not existing_gitea.stat.exists
|
|
|
|
- name: Display pre-flight status
|
|
ansible.builtin.debug:
|
|
msg: |
|
|
============================================
|
|
Gitea Migration Pre-flight Check
|
|
============================================
|
|
Current domain: {{ gitea_old_domain }}
|
|
Target domain: {{ gitea_domain }}
|
|
Authentik: {{ authentik_domain }}
|
|
Base dir: {{ gitea_base_dir }}
|
|
============================================
|
|
|
|
- name: Verify Authentik is reachable
|
|
ansible.builtin.uri:
|
|
url: "https://{{ authentik_domain }}/api/v3/core/brands/"
|
|
method: GET
|
|
headers:
|
|
Authorization: "Bearer {{ vault_authentik_bootstrap_token }}"
|
|
status_code: 200
|
|
timeout: 30
|
|
register: authentik_check
|
|
ignore_errors: true
|
|
|
|
- name: Warn if Authentik not reachable
|
|
ansible.builtin.debug:
|
|
msg: |
|
|
WARNING: Authentik not reachable at https://{{ authentik_domain }}
|
|
OAuth setup will be skipped. You can run it manually later.
|
|
when: authentik_check.failed
|
|
|
|
tasks:
|
|
# =========================================================================
|
|
# Stage 1: Backup (skip if recent backup exists)
|
|
# =========================================================================
|
|
- name: Create backup directory
|
|
ansible.builtin.file:
|
|
path: "{{ gitea_backup_dir }}"
|
|
state: directory
|
|
mode: "0755"
|
|
when: gitea_create_backup
|
|
|
|
- name: Check for existing backup from today
|
|
ansible.builtin.find:
|
|
paths: "{{ gitea_backup_dir }}"
|
|
patterns: "gitea-backup-{{ ansible_date_time.date | replace('-', '') }}*.tar.gz"
|
|
register: existing_backups
|
|
when: gitea_create_backup
|
|
|
|
- name: Set backup needed flag
|
|
ansible.builtin.set_fact:
|
|
backup_needed: "{{ gitea_create_backup and (existing_backups.files | length == 0) }}"
|
|
|
|
- name: Skip backup message
|
|
ansible.builtin.debug:
|
|
msg: "Backup already exists from today: {{ existing_backups.files[0].path | basename }}. Skipping backup."
|
|
when:
|
|
- gitea_create_backup
|
|
- existing_backups.files | length > 0
|
|
|
|
- name: Stop Gitea container for consistent backup
|
|
ansible.builtin.command:
|
|
cmd: docker compose stop gitea
|
|
chdir: "{{ gitea_base_dir }}"
|
|
when: backup_needed
|
|
changed_when: true
|
|
|
|
- name: Generate backup timestamp
|
|
ansible.builtin.set_fact:
|
|
backup_timestamp: "{{ ansible_date_time.iso8601_basic_short }}"
|
|
when: backup_needed
|
|
|
|
- name: Create backup archive
|
|
ansible.builtin.archive:
|
|
path: "{{ gitea_data_dir }}"
|
|
dest: "{{ gitea_backup_dir }}/gitea-backup-{{ backup_timestamp }}.tar.gz"
|
|
format: gz
|
|
when: backup_needed
|
|
|
|
- name: Display backup status
|
|
ansible.builtin.debug:
|
|
msg: "Backup created: {{ gitea_backup_dir }}/gitea-backup-{{ backup_timestamp }}.tar.gz"
|
|
when: backup_needed
|
|
|
|
- name: Upload backup to GDrive (if configured)
|
|
ansible.builtin.command:
|
|
cmd: "rclone copy {{ gitea_backup_dir }}/gitea-backup-{{ backup_timestamp }}.tar.gz GDrive:backups/gitea/"
|
|
when:
|
|
- backup_needed
|
|
- gitea_backup_to_gdrive
|
|
ignore_errors: true
|
|
register: gdrive_upload
|
|
changed_when: gdrive_upload.rc == 0
|
|
|
|
# =========================================================================
|
|
# Stage 2: Domain Migration (app.ini)
|
|
# =========================================================================
|
|
- name: Update app.ini ROOT_URL
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ gitea_data_dir }}/gitea/conf/app.ini"
|
|
regexp: '^ROOT_URL\s*='
|
|
line: "ROOT_URL = https://{{ gitea_domain }}/"
|
|
backup: true
|
|
|
|
- name: Update app.ini SSH_DOMAIN
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ gitea_data_dir }}/gitea/conf/app.ini"
|
|
regexp: '^SSH_DOMAIN\s*='
|
|
line: "SSH_DOMAIN = {{ gitea_ssh_domain }}"
|
|
|
|
- name: Update app.ini DOMAIN
|
|
ansible.builtin.lineinfile:
|
|
path: "{{ gitea_data_dir }}/gitea/conf/app.ini"
|
|
regexp: '^DOMAIN\s*='
|
|
line: "DOMAIN = {{ gitea_domain }}"
|
|
|
|
# =========================================================================
|
|
# Stage 3: Deploy Updated Configuration
|
|
# =========================================================================
|
|
- name: Deploy docker-compose.yml
|
|
ansible.builtin.template:
|
|
src: templates/docker-compose.yml.j2
|
|
dest: "{{ gitea_base_dir }}/docker-compose.yml"
|
|
mode: "0644"
|
|
backup: true
|
|
|
|
- name: Deploy Caddyfile with domain redirect
|
|
ansible.builtin.template:
|
|
src: templates/Caddyfile.j2
|
|
dest: "{{ gitea_base_dir }}/Caddyfile"
|
|
mode: "0644"
|
|
backup: true
|
|
|
|
- name: Start services
|
|
ansible.builtin.command:
|
|
cmd: docker compose up -d
|
|
chdir: "{{ gitea_base_dir }}"
|
|
changed_when: true
|
|
|
|
- name: Wait for Caddy to start (port 443)
|
|
ansible.builtin.wait_for:
|
|
port: 443
|
|
host: 127.0.0.1
|
|
delay: 5
|
|
timeout: 60
|
|
|
|
- name: Wait for Gitea container to be healthy
|
|
ansible.builtin.command:
|
|
cmd: docker compose ps gitea --format json
|
|
chdir: "{{ gitea_base_dir }}"
|
|
register: gitea_container
|
|
until: "'running' in gitea_container.stdout"
|
|
retries: 12
|
|
delay: 5
|
|
changed_when: false
|
|
|
|
# =========================================================================
|
|
# Stage 4: Authentik OAuth Setup
|
|
# =========================================================================
|
|
- name: Install jq for OAuth setup script
|
|
ansible.builtin.apt:
|
|
name: jq
|
|
state: present
|
|
update_cache: true
|
|
when: not authentik_check.failed
|
|
|
|
- name: Deploy Gitea OAuth setup script
|
|
ansible.builtin.template:
|
|
src: templates/setup-gitea-oauth.sh.j2
|
|
dest: "{{ gitea_base_dir }}/setup-gitea-oauth.sh"
|
|
mode: "0755"
|
|
when: not authentik_check.failed
|
|
|
|
- name: Run Gitea OAuth setup on Authentik
|
|
ansible.builtin.command:
|
|
cmd: "{{ gitea_base_dir }}/setup-gitea-oauth.sh"
|
|
register: oauth_setup
|
|
when: not authentik_check.failed
|
|
changed_when: true
|
|
|
|
- name: Display OAuth setup output
|
|
ansible.builtin.debug:
|
|
var: oauth_setup.stdout_lines
|
|
when:
|
|
- not authentik_check.failed
|
|
- oauth_setup is defined
|
|
|
|
# =========================================================================
|
|
# Stage 5: Verification
|
|
# =========================================================================
|
|
- name: Wait for Gitea to be healthy on new domain
|
|
ansible.builtin.uri:
|
|
url: "https://{{ gitea_domain }}/api/v1/version"
|
|
method: GET
|
|
status_code: 200
|
|
timeout: 30
|
|
validate_certs: true
|
|
register: gitea_health
|
|
until: gitea_health.status == 200
|
|
retries: 12
|
|
delay: 10
|
|
ignore_errors: true
|
|
|
|
- name: Check old domain redirect
|
|
ansible.builtin.uri:
|
|
url: "https://{{ gitea_old_domain }}/"
|
|
method: GET
|
|
follow_redirects: none
|
|
status_code: [301, 302, 308]
|
|
validate_certs: true
|
|
register: redirect_check
|
|
ignore_errors: true
|
|
|
|
- name: Display migration status
|
|
ansible.builtin.debug:
|
|
msg: |
|
|
============================================
|
|
Gitea Migration Complete!
|
|
============================================
|
|
|
|
New URL: https://{{ gitea_domain }}
|
|
Old URL: https://{{ gitea_old_domain }} (redirects)
|
|
|
|
Health check: {{ 'PASSED' if gitea_health.status == 200 else 'PENDING - may need DNS propagation' }}
|
|
Redirect check: {{ 'PASSED' if redirect_check.status in [301, 302, 308] else 'PENDING' }}
|
|
|
|
Backup: {{ gitea_backup_dir }}/gitea-backup-{{ backup_timestamp | default('N/A') }}.tar.gz
|
|
|
|
============================================
|
|
MANUAL STEPS REQUIRED:
|
|
============================================
|
|
|
|
1. DNS (if not done):
|
|
Add A record: code.stuslab.cc -> 94.130.181.201
|
|
|
|
2. OAuth Configuration in Gitea UI:
|
|
- Go to: https://{{ gitea_domain }}/admin/auths/new
|
|
- See credentials: cat /tmp/gitea-oauth-credentials.json
|
|
|
|
3. Test git operations:
|
|
ssh -T git@{{ gitea_ssh_domain }} -p {{ gitea_ssh_port }}
|
|
git clone git@{{ gitea_ssh_domain }}:user/repo.git
|
|
|
|
============================================
|
|
|
|
View logs:
|
|
ssh root@{{ ansible_host }} "cd {{ gitea_base_dir }} && docker compose logs -f"
|
|
|
|
============================================
|