--- # Git-based Rollback Playbook # Rolls back to the previous release by switching the symlink # # Usage: # ansible-playbook -i inventories/production/hosts.yml playbooks/rollback-git-based.yml # ansible-playbook -i inventories/production/hosts.yml playbooks/rollback-git-based.yml --extra-vars "rollback_to=20241025123456" - name: Rollback Custom PHP Framework (Git-based) hosts: web_servers become: true vars: app_name: michaelschiemer app_user: deploy app_group: deploy app_base_path: "/var/www/{{ app_name }}" releases_path: "{{ app_base_path }}/releases" current_path: "{{ app_base_path }}/current" pre_tasks: - name: Check if deployment lock exists stat: path: "{{ app_base_path }}/.deploy.lock" register: deploy_lock - name: Fail if deployment is in progress fail: msg: "Cannot rollback - deployment in progress" when: deploy_lock.stat.exists - name: Create rollback lock file: path: "{{ app_base_path }}/.rollback.lock" state: touch owner: "{{ app_user }}" group: "{{ app_group }}" tasks: - name: Get current release stat: path: "{{ current_path }}" register: current_release - name: Fail if no current release exists fail: msg: "No current release found at {{ current_path }}" when: not current_release.stat.exists - name: Get list of all releases find: paths: "{{ releases_path }}" file_type: directory register: all_releases - name: Sort releases by creation time (newest first) set_fact: sorted_releases: "{{ all_releases.files | sort(attribute='ctime', reverse=true) }}" - name: Determine target release for rollback set_fact: target_release: "{{ rollback_to if rollback_to is defined else sorted_releases[1].path }}" - name: Verify target release exists stat: path: "{{ target_release }}" register: target_release_stat - name: Fail if target release doesn't exist fail: msg: "Target release not found: {{ target_release }}" when: not target_release_stat.stat.exists - name: Display rollback information debug: msg: - "Current release: {{ current_release.stat.lnk_source }}" - "Rolling back to: {{ target_release }}" - name: Switch symlink to previous release file: src: "{{ target_release }}" dest: "{{ current_path }}" state: link owner: "{{ app_user }}" group: "{{ app_group }}" force: yes - name: Wait for application to be ready wait_for: timeout: 5 delegate_to: localhost - name: Health check after rollback uri: url: "http://{{ ansible_host }}/health/summary" method: GET return_content: yes status_code: 200 register: health_check retries: 3 delay: 5 until: health_check.status == 200 ignore_errors: yes - name: Log rollback lineinfile: path: "{{ app_base_path }}/deploy.log" line: "[{{ ansible_date_time.iso8601 }}] ROLLBACK: {{ current_release.stat.lnk_source }} -> {{ target_release }} - Health: {{ health_check.status | default('FAILED') }}" create: yes - name: Display rollback result debug: msg: - "==========================================" - "Rollback completed" - "Previous: {{ current_release.stat.lnk_source }}" - "Current: {{ target_release }}" - "Health check: {{ health_check.status | default('FAILED') }}" - "==========================================" post_tasks: - name: Remove rollback lock file: path: "{{ app_base_path }}/.rollback.lock" state: absent rescue: - name: Remove rollback lock on failure file: path: "{{ app_base_path }}/.rollback.lock" state: absent - name: Log rollback failure lineinfile: path: "{{ app_base_path }}/deploy.log" line: "[{{ ansible_date_time.iso8601 }}] ROLLBACK FAILED" create: yes - name: Fail with error message fail: msg: "Rollback failed"