From aea6dec081bded95c4efe3bc6ac3d9b37a6983a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=A4rlocher?= Date: Tue, 26 May 2026 14:04:17 +0200 Subject: [PATCH] fix(nextcloud): make occ-driven config tasks idempotent Every `occ config:app:set` / `ldap:set-config` / `notify_push:setup` call previously fired on every play, marking changed even when the stored value already matched. Now we read the current value first and only invoke the setter when it differs: * richdocuments (collabora): pre-read wopi_url, public_wopi_url, disable_certificate_verification, wopi_allowlist into a fact map; guard each `config:app:set` and tag `richdocuments:activate-config` with `changed_when: false` since it's a discovery refresh. * drawio: same pattern for DrawioUrl, DrawioTheme, DrawioOffline, comparing as strings (occ stores booleans as "1"/"0"). * user_ldap: pre-read `ldap:show-config s01 --output=json`, parse JSON defensively (occ logs interleave on stderr), and skip per-key `ldap:set-config` calls when the stored value already equals the desired one. * notify_push: skip `notify_push:setup` when the stored base_endpoint already matches the computed URL. * plugins: `app:install`/`app:enable` were treating "already installed/ enabled" output as a change. Add the negative match to `changed_when` so re-runs of a fully-provisioned site report ok rather than changed. --- roles/nextcloud/tasks/collabora.yml | 31 +++++++++++++++++++++++++-- roles/nextcloud/tasks/drawio.yml | 27 +++++++++++++++++++++-- roles/nextcloud/tasks/ldap.yml | 19 ++++++++++++++++ roles/nextcloud/tasks/notify_push.yml | 11 +++++++++- roles/nextcloud/tasks/plugins.yml | 8 +++++-- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/roles/nextcloud/tasks/collabora.yml b/roles/nextcloud/tasks/collabora.yml index 2a7bd82..d9d4f62 100644 --- a/roles/nextcloud/tasks/collabora.yml +++ b/roles/nextcloud/tasks/collabora.yml @@ -1,28 +1,55 @@ #SPDX-License-Identifier: MIT-0 --- # tasks file for configuring Collabora in Nextcloud +- name: Read current richdocuments config values + community.docker.docker_container_exec: + container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" + command: php /var/www/html/occ config:app:get richdocuments {{ item }} + loop: + - wopi_url + - public_wopi_url + - disable_certificate_verification + - wopi_allowlist + register: _richdocuments_current + changed_when: false + failed_when: false + +- name: Build map of current richdocuments config + ansible.builtin.set_fact: + _richdocuments_cfg: "{{ _richdocuments_cfg | default({}) | combine({item.item: (item.stdout | default('')).strip()}) }}" + loop: "{{ _richdocuments_current.results }}" + loop_control: + label: "{{ item.item }}" + - name: Configure Collabora WOPI URL (server-to-server) community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" command: php /var/www/html/occ config:app:set richdocuments wopi_url --value=https://{{ nextcloud_collabora_domain }} + when: _richdocuments_cfg.wopi_url != ('https://' ~ nextcloud_collabora_domain) - name: Configure Collabora public WOPI URL (browser-facing) community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" command: php /var/www/html/occ config:app:set richdocuments public_wopi_url --value=https://{{ nextcloud_collabora_public_domain }} - when: nextcloud_collabora_public_domain is defined and nextcloud_collabora_public_domain != nextcloud_collabora_domain + when: + - nextcloud_collabora_public_domain is defined + - nextcloud_collabora_public_domain != nextcloud_collabora_domain + - _richdocuments_cfg.public_wopi_url != ('https://' ~ nextcloud_collabora_public_domain) - name: Configure certificate verification for Collabora community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" command: php /var/www/html/occ config:app:set richdocuments disable_certificate_verification --value={{ nextcloud_collabora_disable_cert_verification | ternary('yes', 'no') }} + when: _richdocuments_cfg.disable_certificate_verification != (nextcloud_collabora_disable_cert_verification | ternary('yes', 'no')) - name: Set Collabora WOPI allowlist community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" command: php /var/www/html/occ config:app:set richdocuments wopi_allowlist --value='' + when: _richdocuments_cfg.wopi_allowlist | default('') != '' - name: Activate richdocuments configuration (fetch discovery from Collabora) community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" - command: php /var/www/html/occ richdocuments:activate-config \ No newline at end of file + command: php /var/www/html/occ richdocuments:activate-config + changed_when: false \ No newline at end of file diff --git a/roles/nextcloud/tasks/drawio.yml b/roles/nextcloud/tasks/drawio.yml index bd2e17e..e693862 100644 --- a/roles/nextcloud/tasks/drawio.yml +++ b/roles/nextcloud/tasks/drawio.yml @@ -2,18 +2,41 @@ --- # tasks file for configuring draw.io in Nextcloud +- name: Read current drawio config values + community.docker.docker_container_exec: + container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" + command: php /var/www/html/occ config:app:get drawio {{ item }} + loop: + - DrawioUrl + - DrawioTheme + - DrawioOffline + register: _drawio_current + changed_when: false + failed_when: false + +- name: Build map of current drawio config + ansible.builtin.set_fact: + _drawio_cfg: "{{ _drawio_cfg | default({}) | combine({item.item: (item.stdout | default('')).strip()}) }}" + loop: "{{ _drawio_current.results }}" + loop_control: + label: "{{ item.item }}" + - name: Configure draw.io URL community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" command: php /var/www/html/occ config:app:set drawio DrawioUrl --value={{ nextcloud_drawio_url }} - when: nextcloud_drawio_url | length > 0 + when: + - nextcloud_drawio_url | length > 0 + - _drawio_cfg.DrawioUrl != nextcloud_drawio_url - name: Configure draw.io theme community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" command: php /var/www/html/occ config:app:set drawio DrawioTheme --value={{ nextcloud_drawio_theme }} + when: _drawio_cfg.DrawioTheme != (nextcloud_drawio_theme | string) - name: Configure draw.io offline mode community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" - command: php /var/www/html/occ config:app:set drawio DrawioOffline --value={{ nextcloud_drawio_offline }} \ No newline at end of file + command: php /var/www/html/occ config:app:set drawio DrawioOffline --value={{ nextcloud_drawio_offline }} + when: _drawio_cfg.DrawioOffline != (nextcloud_drawio_offline | string) \ No newline at end of file diff --git a/roles/nextcloud/tasks/ldap.yml b/roles/nextcloud/tasks/ldap.yml index dcb2392..89618d5 100644 --- a/roles/nextcloud/tasks/ldap.yml +++ b/roles/nextcloud/tasks/ldap.yml @@ -15,6 +15,24 @@ command: php /var/www/html/occ ldap:create-empty-config when: "'s01' not in ldap_show_config.stdout" +- name: Read current LDAP config for s01 + community.docker.docker_container_exec: + container: "{{ nextcloud_service_name }}-nextcloud-1" + command: php /var/www/html/occ ldap:show-config s01 --output=json + register: _ldap_show_s01 + changed_when: false + failed_when: false + +- name: Parse current LDAP config + ansible.builtin.set_fact: + _ldap_current: >- + {{ + (_ldap_show_s01.stdout | from_json) if ( + (_ldap_show_s01.stdout | default('') | trim) is match('^[\\[{]') + ) else {} + }} + when: _ldap_show_s01.rc | default(1) == 0 + - name: Configure LDAP settings community.docker.docker_container_exec: container: "{{ nextcloud_service_name }}-nextcloud-1" @@ -29,6 +47,7 @@ loop_control: label: "{{ item.key }}" no_log: true + when: ((_ldap_current | default({})).get(item.key) | default(none) | string) != (item.value | string) - name: Test LDAP configuration community.docker.docker_container_exec: diff --git a/roles/nextcloud/tasks/notify_push.yml b/roles/nextcloud/tasks/notify_push.yml index 1497c68..2fba4d9 100644 --- a/roles/nextcloud/tasks/notify_push.yml +++ b/roles/nextcloud/tasks/notify_push.yml @@ -2,7 +2,16 @@ --- # tasks file for configuring notify_push in Nextcloud +- name: Read current notify_push base endpoint + community.docker.docker_container_exec: + container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" + command: php /var/www/html/occ config:app:get notify_push base_endpoint + register: _notify_push_current + changed_when: false + failed_when: false + - name: Configure notify_push base endpoint community.docker.docker_container_exec: container: "{{ nextcloud_docker_compose_dir | basename }}-nextcloud-1" - command: php /var/www/html/occ notify_push:setup https://{{ nextcloud_notify_push_domain | default(nextcloud_domains[0]) }}/push \ No newline at end of file + command: php /var/www/html/occ notify_push:setup https://{{ nextcloud_notify_push_domain | default(nextcloud_domains[0]) }}/push + when: (_notify_push_current.stdout | default('') | trim) != ('https://' ~ (nextcloud_notify_push_domain | default(nextcloud_domains[0])) ~ '/push') \ No newline at end of file diff --git a/roles/nextcloud/tasks/plugins.yml b/roles/nextcloud/tasks/plugins.yml index 2a6d8a5..a93e37c 100644 --- a/roles/nextcloud/tasks/plugins.yml +++ b/roles/nextcloud/tasks/plugins.yml @@ -8,7 +8,9 @@ chdir: "{{ nextcloud_docker_compose_dir }}" loop: "{{ nextcloud_apps_to_install }}" register: app_install_result - changed_when: "'installed' in app_install_result.stdout" + changed_when: + - "'already installed' not in app_install_result.stdout" + - "'installed' in app_install_result.stdout" failed_when: - app_install_result.rc != 0 - "'already installed' not in app_install_result.stdout" @@ -19,7 +21,9 @@ chdir: "{{ nextcloud_docker_compose_dir }}" loop: "{{ nextcloud_apps_to_install }}" register: app_enable_result - changed_when: "'enabled' in app_enable_result.stdout" + changed_when: + - "'already enabled' not in app_enable_result.stdout" + - "'enabled' in app_enable_result.stdout" failed_when: - app_enable_result.rc != 0 - "'already enabled' not in app_enable_result.stdout"