Files
computer-playbook/roles/web-app-xwiki/tasks/04_extensions.yml
kevinveenbirkenbachandClaude Opus 5.5 e142d07f97 fix(web-app-xwiki): upgrade an add-on that is not on its pinned version
The extension installer returned as soon as an add-on was installed, in
whatever version. A new pin in meta/addons/ therefore reached a fresh wiki
only, and an existing wiki kept the version it was first deployed with.

The installer now skips an add-on only when the installed version equals
the pin. Otherwise it calls install(id, version, namespace), which the
Extension Manager resolves as an upgrade, and it reports an error when the
add-on is not on the pin afterwards.

The check that follows the installer compared presence only. It takes the
pin as well, so a deploy fails when an add-on is installed in another
version.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 09:17:15 +02:00

105 lines
4.4 KiB
YAML

---
# Installs OIDC / LDAP using a temporary Groovy page that calls the
# Extension Script Service (services.extension.install).
# Avoids REST job API and any Namespace class import for portability.
#
# Flow:
# - Bootstrap config renders with both auth backends OFF (already in your role).
# - This file installs required extensions on the current wiki.
# - Final config later turns auth ON (already in your role).
#
# Notes:
# - We print machine-readable markers so Ansible can assert deterministically.
- name: "🔨 XWIKI | Build Groovy installer code from static file (base64 payload)"
vars:
_wanted_b64: "{{ XWIKI_PLUGINS | to_json | b64encode }}"
set_fact:
_install_code: >-
{{ lookup('file', 'roles/web-app-xwiki/files/extension_installer_b64.groovy')
| regex_replace('__WANTED_B64__', _wanted_b64) }}
- name: "⬇️ XWIKI | Fetch REST session + form token (extensions write ops)"
include_tasks: utils/rest_session.yml
- name: "⬆️ XWIKI | PUT installer page Main.InstallExtensions"
uri_retry:
url: "{{ [XWIKI_REST_XWIKI_PAGES, 'InstallExtensions'] | url_join }}"
method: PUT
url_username: "{{ XWIKI_SUPERADMIN_USERNAME }}"
url_password: "{{ XWIKI_SUPERADMIN_PASSWORD }}"
force_basic_auth: true
status_code: [200, 201, 202, 204]
headers: "{{ xwiki_rest_headers_write }}"
body: "{{ lookup('template', 'xml/install_extensions_page.xml.j2') }}"
register: _put_page
- block:
- name: "📂 XWIKI | Load installer-page execution + assertion routines"
include_tasks: 04_extensions/extensions_execute.yml
rescue:
- name: "🌿 XWiki | Probe extension repos + dump xwiki.properties" # nocheck: rescue-extra-diagnostics extension-repo reachability and xwiki.properties are xwiki-specific and not in any log the generic rescue script captures
ansible.builtin.script: "{{ lookup('path_absolute', 'roles/web-app-xwiki/files/shell/rescue_extensions.sh') }}"
environment:
XWIKI_CONTAINER_ADDRESS: "{{ XWIKI_CONTAINER_ADDRESS }}"
changed_when: false
failed_when: false
- name: "🛑 XWiki | fail after extension diagnostics"
ansible.builtin.fail:
msg: "Extension install failed; repo reachability + xwiki.properties were dumped into the diagnostics folder (the workflow-level rescue capture collects the
full snapshot)"
always:
- name: "🗑️ XWIKI | Delete installer page (best effort)"
uri_retry:
url: "{{ [XWIKI_REST_XWIKI_PAGES, 'InstallExtensions'] | url_join }}"
method: DELETE
url_username: "{{ XWIKI_SUPERADMIN_USERNAME }}"
url_password: "{{ XWIKI_SUPERADMIN_PASSWORD }}"
force_basic_auth: true
status_code: [204, 200, 202, 404]
headers: "{{ xwiki_rest_headers_write | default({}) }}"
register: _delete_page_best_effort
changed_when: _delete_page_best_effort.status != 404
failed_when: false
- name: "✅ XWIKI | Verify requested extensions via Groovy checker"
include_tasks: _check_extension_via_groovy.yml
loop: "{{ XWIKI_PLUGINS }}"
loop_control:
loop_var: plugin
label: "{{ plugin.id }}"
vars:
ext_id: "{{ plugin.id }}"
ext_version: "{{ plugin.version }}"
result_var: "probe_{{ plugin.id | regex_replace('[^A-Za-z0-9_]', '_') }}"
- name: "📊 XWIKI | Collect probe results"
set_fact:
_xwiki_probe_results: "{{ _xwiki_probe_results | default([]) + [ { 'id': plugin.id, 'status': ( (hostvars[inventory_hostname]['probe_' ~ (plugin.id | regex_replace('[^A-Za-z0-9_]',
'_'))] | default({})).status | default(404) | int ) } ] }}"
loop: "{{ XWIKI_PLUGINS }}"
loop_control:
loop_var: plugin
changed_when: false
- name: "✅ XWIKI | Assert all requested extensions are on their pinned version"
vars:
missing: "{{ (_xwiki_probe_results | default([])) | selectattr('status','equalto',404) | map(attribute='id') | list }}"
fail:
msg: "Extensions missing or not on their pinned version: {{ missing | join(', ') }}"
when: missing | length > 0
- name: "🗑️ XWIKI | Delete installer page"
uri_retry:
url: "{{ [XWIKI_REST_XWIKI_PAGES, 'InstallExtensions'] | url_join }}"
method: DELETE
url_username: "{{ XWIKI_SUPERADMIN_USERNAME }}"
url_password: "{{ XWIKI_SUPERADMIN_PASSWORD }}"
force_basic_auth: true
status_code: [204, 200, 202, 404]
headers: "{{ xwiki_rest_headers_write }}"
register: _delete_page
changed_when: _delete_page.status != 404