diff --git a/plugins/lookup/compose_file_args.py b/plugins/lookup/compose_file_args.py index 7b9e0de235..3628946662 100644 --- a/plugins/lookup/compose_file_args.py +++ b/plugins/lookup/compose_file_args.py @@ -29,6 +29,7 @@ from ansible.errors import AnsibleError from ansible.plugins.loader import lookup_loader from ansible.plugins.lookup import LookupBase +from plugins.filter.role_path_by_app_id import abs_role_path_by_application_id from utils.docker.paths_utils import get_docker_paths from utils.templating.jinja import render_strict @@ -58,7 +59,7 @@ def _maybe_template(templar: Any, value: Any) -> Any: return value -def _role_provides_override(*, application_id: str, templar: Any) -> bool: +def _role_provides_override(*, application_id: str) -> bool: """ Mirror tasks/04_files.yml "with_first_found" logic: @@ -67,22 +68,10 @@ def _role_provides_override(*, application_id: str, templar: Any) -> bool: Only if one of these exists, we append "-f ". """ - tpl = getattr(templar, "template", None) - if not callable(tpl): - raise AnsibleError( - "compose_file_args: templar is required to resolve abs_role_path_by_application_id" - ) - - role_base = _as_str(tpl("{{ application_id | abs_role_path_by_application_id }}")) - if not role_base: - raise AnsibleError( - "compose_file_args: abs_role_path_by_application_id resolved to empty" - ) - - c1 = str(Path(role_base) / "templates" / "compose.override.yml.j2") - c2 = str(Path(role_base) / "files" / "compose.override.yml") - - return Path(c1).is_file() or Path(c2).is_file() + role_base = Path(abs_role_path_by_application_id(application_id)) + return (role_base / "templates" / "compose.override.yml.j2").is_file() or ( + role_base / "files" / "compose.override.yml" + ).is_file() class LookupModule(LookupBase): @@ -152,7 +141,7 @@ class LookupModule(LookupBase): parts = [f"{flag} {base}"] - if _role_provides_override(application_id=application_id, templar=templar): + if _role_provides_override(application_id=application_id): if not _as_str(override): raise AnsibleError( "compose_file_args: compose.files.compose_override is required " diff --git a/roles/web-app-bigbluebutton/README.md b/roles/web-app-bigbluebutton/README.md index e3a195ed83..0d0cd89ef4 100644 --- a/roles/web-app-bigbluebutton/README.md +++ b/roles/web-app-bigbluebutton/README.md @@ -21,7 +21,7 @@ By default, BigBlueButton is deployed with best-practice hardening, modular secr - ๐Ÿ›  Volume patching and Docker Compose customization - ๐Ÿ“ฌ SMTP integration and Greenlight admin creation: the administrator is verified and active, and its password converges on every deploy - ๐ŸŽจ Corporate design for Greenlight: brand color and logo through the Greenlight site settings, the configured title through the design script, dark mode through the role stylesheet; the meeting client (`/html5client`) takes the palette and the configured title through its own `customStyleUrl` and `clientTitle` settings -- ๐Ÿงช Workarounds for known Docker Compose or Etherpad issues +- ๐Ÿงช Workarounds for known Docker Compose issues ## Single Sign-On (SSO) diff --git a/roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js b/roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js deleted file mode 100644 index 47fcdd9c15..0000000000 --- a/roles/web-app-bigbluebutton/files/javascript/ep_redis_publisher_index.js +++ /dev/null @@ -1,148 +0,0 @@ -// nocheck: mirrored-unit-test - requires ep_etherpad-lite's Settings module at load -// time and reads settings.ep_redis_publisher, so it cannot be imported outside an -// Etherpad install -const redis = require('redis'); -const settings = require('ep_etherpad-lite/node/utils/Settings'); -const logger = { error: (...args) => console.error('[ep_redis_publisher]', ...args) }; - -const CHANNEL = settings.ep_redis_publisher.channel || 'from-etherpad-redis-channel'; - -const sanitizeMessageBody = (body) => { - let response = { ...body }; - - if (response.pad && response.pad._db) { - response = { - ...response, - pad: { - ...response.pad, - _db: undefined, - }, - }; - } - - if (body?.pad?.id) { body.pad.padId = body.pad.id; } - - return response; -}; - -const getCircularReplacer = () => { - const seen = new WeakSet(); - - return (key, value) => { - if (typeof value === "object" && value !== null) { - if (seen.has(value)) return; - - seen.add(value); - } - - return value; - }; -}; - -const retry = (options) => { - if (options.error && options.error.code === 'ECONNREFUSED') { - logger.error('redis', 'connection refused'); - - return new Error('refused'); - } - - if (options.total_retry_time > 1000 * 60 * 60) { - logger.error('redis', 'retry exhausted'); - - return new Error('exhausted'); - } - - if (options.attempt > 10) { - logger.error('redis', 'attempt limit'); - - return undefined; - } - - return Math.min(options.attempt * 100, 3000); -}; - -const options = { - host: settings.ep_redis_publisher.host || '127.0.0.1', - port: settings.ep_redis_publisher.port || 6379, - retry_strategy: retry, -}; - -if (settings.ep_redis_publisher.password) { - options.password = settings.ep_redis_publisher.password; -} - -const publisher = redis.createClient(options); - -const buildEnvelope = (name) => { - - return { - name, - routing: { sender: 'etherpad' }, - timestamp: (new Date()).getTime(), - }; -}; - -const buildCore = (name, body) => { - - return { - header: { name }, - body, - }; -}; - -const build = (name, body) => { - - return JSON.stringify({ - envelope: buildEnvelope(name), - core: buildCore(name, sanitizeMessageBody(body)), - }, getCircularReplacer()); -}; - -exports.publish = (event, body) => { - let message; - switch (event) { - case 'loadSettings': - message = build('PadLoadSettingsSysMsg', body); - break; - case 'shutdown': - message = build('PadShutdownSysMsg', body); - break; - case 'expressConfigure': - message = build('PadConfigureSysMsg', body); - break; - case 'expressCreateServer': - message = build('PadCreateServerSysMsg', body); - break; - case 'expressCloseServer': - message = build('PadCloseServerSysMsg', body); - break; - case 'onAccessCheck': - message = build('PadAccessCheckSysMsg', body); - break; - case 'padCreate': - message = build('PadCreateSysMsg', body); - break; - case 'padLoad': - message = build('PadLoadSysMsg', body); - break; - case 'padUpdate': - message = build('PadUpdateSysMsg', body); - break; - case 'padCopy': - message = build('PadCopySysMsg', body); - break; - case 'padRemove': - message = build('PadRemoveSysMsg', body); - break; - case 'userLeave': - message = build('PadUserLeaveSysMsg', body); - break; - - default: - return; - } - - if (message) { - publisher.publish(CHANNEL, message); - } -}; diff --git a/roles/web-app-bigbluebutton/files/shell/etherpad_pg_entrypoint.sh b/roles/web-app-bigbluebutton/files/shell/etherpad_pg_entrypoint.sh deleted file mode 100644 index 64ede8aef2..0000000000 --- a/roles/web-app-bigbluebutton/files/shell/etherpad_pg_entrypoint.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -set -e - -sed -i \ - -e 's#"dbType": "redis"#"dbType": "postgres"#' \ - -e "s#\"url\": \"redis://redis:6379\"#\"host\": \"${ETHERPAD_DB_HOST}\", \"port\": ${ETHERPAD_DB_PORT}, \"database\": \"${ETHERPAD_DB_NAME}\", \"user\": \"${ETHERPAD_DB_USER}\", \"password\": \"${ETHERPAD_DB_PASSWORD}\"#" \ - /opt/etherpad-lite/settings.json - -exec /entrypoint.sh diff --git a/roles/web-app-bigbluebutton/files/shell/postgres_initdb_etherpad.sh b/roles/web-app-bigbluebutton/files/shell/postgres_initdb_etherpad.sh deleted file mode 100644 index 11364c0015..0000000000 --- a/roles/web-app-bigbluebutton/files/shell/postgres_initdb_etherpad.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -set -eu - -: "${POSTGRES_USER:?set by the upstream compose file}" -: "${ETHERPAD_DB_NAME:?set in env.j2 and broadcast to every service}" - -if ! psql --username "${POSTGRES_USER}" --dbname postgres -tAc \ - "SELECT 1 FROM pg_database WHERE datname = '${ETHERPAD_DB_NAME}'" | grep -q 1; then - psql --username "${POSTGRES_USER}" --dbname postgres \ - -c "CREATE DATABASE \"${ETHERPAD_DB_NAME}\"" -fi diff --git a/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py b/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py index 24d0413510..fb7e5a8514 100644 --- a/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py +++ b/roles/web-app-bigbluebutton/filter_plugins/compose_mods.py @@ -3,7 +3,9 @@ import re from utils.cache.yaml import dump_yaml_str, load_yaml_str -def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): +def compose_mods( + yml_text, compose_repository_path, env_file, extra_hosts=None, pinned=None +): """Normalize the upstream-generated bigbluebutton compose file. Args: @@ -14,6 +16,9 @@ def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): env_file: env file broadcast to every service via ``env_file``. extra_hosts: optional host mappings added to every service that is not on the host network. + pinned: names of services whose image the role pins in + compose.override.yml; their upstream ``build`` is dropped even + where the upstream file names no image. Returns: Transformed compose YAML text. Structural deltas (named data @@ -32,7 +37,7 @@ def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): data = load_yaml_str(yml_text) or {} services = data.get("services", {}) or {} - for svc in services.values(): + for name, svc in services.items(): if not isinstance(svc, dict): continue @@ -49,7 +54,7 @@ def compose_mods(yml_text, compose_repository_path, env_file, extra_hosts=None): hosts.append(entry) svc["extra_hosts"] = hosts - if "build" in svc and svc.get("image"): + if "build" in svc and (svc.get("image") or name in (pinned or [])): svc.pop("build") data["services"] = services diff --git a/roles/web-app-bigbluebutton/meta/secrets.yml b/roles/web-app-bigbluebutton/meta/secrets.yml index 8a14f2b6f1..e3683a6e83 100644 --- a/roles/web-app-bigbluebutton/meta/secrets.yml +++ b/roles/web-app-bigbluebutton/meta/secrets.yml @@ -4,10 +4,6 @@ credentials: description: Shared secret for BigBlueButton API authentication algorithm: sha256 validation: ^[a-f0-9]{64}$ - etherpad_api_key: - description: API key for Etherpad integration - algorithm: random_hex_32 - validation: ^[a-zA-Z0-9]{32}$ rails_secret: description: Secret key for Rails backend algorithm: random_hex diff --git a/roles/web-app-bigbluebutton/meta/services.yml b/roles/web-app-bigbluebutton/meta/services.yml index 3b08329ad8..37fc17db53 100644 --- a/roles/web-app-bigbluebutton/meta/services.yml +++ b/roles/web-app-bigbluebutton/meta/services.yml @@ -146,26 +146,13 @@ nginx: enabled: false # nocheck: playwright-service-flag: prebuilt upstream-compose image, mirror pre-pull only # nocheck: dynamic-flag: fixed upstream-compose sub-service, not group-gated -etherpad: - image: alangecker/bbb-docker-etherpad - version: "2.4.2-s8328b77-p88f3f6b" - update: - type: http_regex - url: - - https://raw.githubusercontent.com/bigbluebutton/docker/develop/repos/tags - - https://raw.githubusercontent.com/bigbluebutton/docker/develop/docker-compose.tmpl.yml - pattern: "repos/bbb-etherpad-plugin (?P[0-9a-f]+)\\nrepos/bbb-etherpad-skin (?P[0-9a-f]+)\\n(?:.*\\n)*?.*bbb-docker-etherpad:(?P[0-9.]+)-s" - template: "{base}-s{skin}-p{plugin}" - enabled: false -# nocheck: playwright-service-flag: prebuilt upstream-compose image, mirror pre-pull only -# nocheck: dynamic-flag: fixed upstream-compose sub-service, not group-gated -bbb-pads: - image: alangecker/bbb-docker-pads - version: "v1.5.8" +bbb-shared-notes-server: + image: alangecker/bbb-shared-notes-server + version: "v3.0.39" update: type: http_regex url: https://raw.githubusercontent.com/bigbluebutton/docker/develop/repos/tags - pattern: "repos/bbb-pads (v[0-9.]+)" + pattern: "repos/bigbluebutton (v[0-9.]+)" enabled: false # nocheck: playwright-service-flag: prebuilt upstream-compose image, mirror pre-pull only # nocheck: dynamic-flag: fixed upstream-compose sub-service, not group-gated diff --git a/roles/web-app-bigbluebutton/meta/volumes.yml b/roles/web-app-bigbluebutton/meta/volumes.yml index 01fa16bad9..1ef0128b59 100644 --- a/roles/web-app-bigbluebutton/meta/volumes.yml +++ b/roles/web-app-bigbluebutton/meta/volumes.yml @@ -70,25 +70,3 @@ html5_settings: mounts: - service: apps-akka target: /etc/bigbluebutton/bbb-html5.yml -ep_redis_publisher_patch: - type: bind - source: "{{ [lookup('container', 'web-app-bigbluebutton', 'directories.instance'), 'ep_redis_publisher_index.js'] | path_join }}" - read_only: true - mounts: - - service: etherpad - target: /opt/etherpad-lite/src/plugin_packages/.versions/ep_redis_publisher@0.0.3/index.js -etherpad_pg_entrypoint: - type: config - source: "{{ [lookup('container', 'web-app-bigbluebutton', 'directories.instance'), 'etherpad_pg_entrypoint.sh'] | path_join }}" - mode: "0444" - mounts: - - service: etherpad - target: /etherpad_pg_entrypoint.sh - -postgres_initdb_etherpad: - type: config - source: "{{ [lookup('container', 'web-app-bigbluebutton', 'directories.instance'), 'postgres_initdb_etherpad.sh'] | path_join }}" - mode: "0555" - mounts: - - service: postgres - target: /docker-entrypoint-initdb.d/zz-etherpad.sh diff --git a/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml b/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml index ec8782a989..237134a106 100644 --- a/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml +++ b/roles/web-app-bigbluebutton/tasks/00_core/manager_ops.yml @@ -31,27 +31,6 @@ dest: "{{ [lookup('container', application_id, 'directories.instance'), 'compose.override.yml'] | path_join }}" notify: compose-up -- name: "๐Ÿฉน Vendor the ep_redis_publisher fix mounted over the etherpad plugin" - stack_host_copy: - src: javascript/ep_redis_publisher_index.js - dest: "{{ [lookup('container', application_id, 'directories.instance'), 'ep_redis_publisher_index.js'] | path_join }}" - mode: "0444" - notify: compose-up - -- name: "๐Ÿฉน Vendor the etherpad postgres-DB entrypoint wrapper" - stack_host_copy: - src: shell/etherpad_pg_entrypoint.sh - dest: "{{ [lookup('container', application_id, 'directories.instance'), 'etherpad_pg_entrypoint.sh'] | path_join }}" - mode: "0444" - notify: compose-up - -- name: "๐Ÿฉน Vendor the initdb hook that creates the etherpad database" - stack_host_copy: - src: shell/postgres_initdb_etherpad.sh - dest: "{{ [lookup('container', application_id, 'directories.instance'), 'postgres_initdb_etherpad.sh'] | path_join }}" - mode: "0555" - notify: compose-up - - name: "๐ŸŽจ Render the meeting client settings of '{{ application_id }}'" stack_host_template: src: bbb-html5.yml.j2 diff --git a/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml b/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml index 31542c38c4..58e6f60662 100644 --- a/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml +++ b/roles/web-app-bigbluebutton/tasks/01_compose/bootstrap_compose_project.yml @@ -27,7 +27,8 @@ extra_hosts=( [lookup('domain', 'web-app-keycloak') ~ ':host-gateway'] if 'web-app-keycloak' in group_names else [] - ) + ), + pinned=BBB_PINNED_SERVICES ) }} dest: "{{ BBB_COMPOSE_FILE_FINAL }}" notify: diff --git a/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml b/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml index d36a6d7a0a..960638dd8f 100644 --- a/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml +++ b/roles/web-app-bigbluebutton/tasks/01_compose/postgres_password_sync.yml @@ -30,26 +30,3 @@ delay: 3 until: bbb_pg_alter.rc == 0 changed_when: false - -- name: "๐Ÿ”Ž Check whether the etherpad database exists" - ansible.builtin.shell: > # nocheck: container-address-quote compose_mode_force role - container exec -i {{ BBB_POSTGRES_CONTAINER_ADDRESS | quote }} - psql -U postgres -d postgres -tAc - "SELECT 1 FROM pg_database WHERE datname = 'etherpad'" - register: bbb_etherpad_db - retries: 20 - delay: 3 - until: bbb_etherpad_db.rc == 0 - changed_when: false - -- name: "๐Ÿ—„๏ธ Create the etherpad database" - ansible.builtin.shell: > # nocheck: container-address-quote compose_mode_force role - container exec -i {{ BBB_POSTGRES_CONTAINER_ADDRESS | quote }} - psql -U postgres -d postgres -c "CREATE DATABASE etherpad" - register: bbb_etherpad_create - retries: 20 - delay: 3 - until: bbb_etherpad_create.rc == 0 or 'already exists' in bbb_etherpad_create.stderr - failed_when: bbb_etherpad_create.rc != 0 and 'already exists' not in bbb_etherpad_create.stderr - when: (bbb_etherpad_db.stdout | trim) != '1' - changed_when: bbb_etherpad_create.rc == 0 diff --git a/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 b/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 index 53ced3bb76..e2504853de 100644 --- a/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 +++ b/roles/web-app-bigbluebutton/templates/compose.override.yml.j2 @@ -5,7 +5,7 @@ services: {{ lookup('container_volumes', service_name) | indent(4) }} {{ lookup('container_extra_hosts') | indent(4) }} {% if BBB_COLLABORA_ENABLED | bool %} - {{ lookup('depends_on', ['redis', 'etherpad', 'bbb-pads']) }} + {{ lookup('depends_on', ['redis']) }} {% endif %} {% set service_name = 'freeswitch' %} {{ service_name }}: @@ -15,19 +15,10 @@ services: {{ service_name }}: {{ lookup('container_image', application_id, service_name) }} {{ lookup('container_volumes', service_name) | indent(4) }} -{% set service_name = 'etherpad' %} - {{ service_name }}: - {{ lookup('container_image', application_id, service_name) }} - {{ lookup('container_volumes', service_name) | indent(4) }} - entrypoint: ["sh", "{{ lookup('volume', 'web-app-bigbluebutton', 'etherpad_pg_entrypoint').target }}"] -{% if BBB_COLLABORA_ENABLED | bool %} - {{ lookup('depends_on', ['redis', 'postgres']) }} -{% else %} - {{ lookup('depends_on', ['postgres']) }} -{% endif %} -{% set service_name = 'bbb-pads' %} +{% set service_name = 'bbb-shared-notes-server' %} {{ service_name }}: {{ lookup('container_image', application_id, service_name) }} + {{ lookup('compose_restart') }} {% set service_name = 'bbb-export-annotations' %} {{ service_name }}: {{ lookup('container_image', application_id, service_name) }} @@ -68,6 +59,8 @@ services: {% set service_name = 'greenlight' %} {{ service_name }}: {{ lookup('container_volumes', service_name) | indent(4) }} + environment: + URL_HOST: "{{ domain }}" {% endif %} {% if BBB_RECORDING_ENABLED | bool %} {% set service_name = 'recordings' %} diff --git a/roles/web-app-bigbluebutton/templates/env.j2 b/roles/web-app-bigbluebutton/templates/env.j2 index caae691d10..c62f34887e 100644 --- a/roles/web-app-bigbluebutton/templates/env.j2 +++ b/roles/web-app-bigbluebutton/templates/env.j2 @@ -23,12 +23,6 @@ REMOVE_OLD_RECORDING={{ BBB_RECORDING_CLEANUP_ENABLED | lower }} RECORDING_MAX_AGE_DAYS={{ BBB_RECORDING_MAX_AGE_DAYS }} SHARED_SECRET={{ BBB_SHARED_SECRET }} -ETHERPAD_API_KEY={{ BBB_ETHERPAD_API_KEY }} -ETHERPAD_DB_HOST=postgres -ETHERPAD_DB_PORT=5432 -ETHERPAD_DB_NAME=etherpad -ETHERPAD_DB_USER=postgres -ETHERPAD_DB_PASSWORD={{ BBB_POSTGRESQL_SECRET | dotenv_quote }} RAILS_SECRET={{ BBB_RAILS_SECRET }} POSTGRESQL_SECRET={{ BBB_POSTGRESQL_SECRET }} FSESL_PASSWORD={{ BBB_FSESL_PASSWORD | dotenv_quote }} diff --git a/roles/web-app-bigbluebutton/vars/main.yml b/roles/web-app-bigbluebutton/vars/main.yml index ce34047e57..65184b19b4 100644 --- a/roles/web-app-bigbluebutton/vars/main.yml +++ b/roles/web-app-bigbluebutton/vars/main.yml @@ -9,11 +9,11 @@ http_port: "{{ lookup('config', application_id, 'services.bigbluebutton.ports.lo BBB_REPOSITORY: "{{ lookup('config', 'web-app-bigbluebutton', 'services.' ~ ('web-app-bigbluebutton' | entity_name) ~ '.repository') }}" BBB_REF: "{{ lookup('config', 'web-app-bigbluebutton', 'services.' ~ ('web-app-bigbluebutton' | entity_name) ~ '.ref') }}" +BBB_PINNED_SERVICES: "{{ lookup('config', application_id, 'services') | dict2items | selectattr('value.image', 'defined') | map(attribute='key') | list }}" _BBB_COTURN_ROLE: "web-svc-coturn" BBB_SHARED_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.shared_secret') }}" -BBB_ETHERPAD_API_KEY: "{{ lookup('config', application_id, 'secrets.credentials.etherpad_api_key') }}" BBB_RAILS_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.rails_secret') }}" BBB_POSTGRESQL_SECRET: "{{ lookup('config', application_id, 'secrets.credentials.postgresql_secret') }}" BBB_FSESL_PASSWORD: "{{ lookup('config', application_id, 'secrets.credentials.fsesl_password') }}" diff --git a/roles/web-app-penpot/tasks/01_administrator.yml b/roles/web-app-penpot/tasks/01_administrator.yml index 4dc1c6d59a..d1fc727a80 100644 --- a/roles/web-app-penpot/tasks/01_administrator.yml +++ b/roles/web-app-penpot/tasks/01_administrator.yml @@ -7,14 +7,13 @@ resolve_compose_cid: "{{ PENPOT_BACKEND_CONTAINER_ADDRESS }}" - name: "๐Ÿฅพ Bootstrap the Penpot administrator's local account (idempotent)" - ansible.builtin.shell: > # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node - container exec -i -e PENPOT_ADMIN_PASSWORD {{ PENPOT_BACKEND_LOCAL_CID | quote }} - sh -lc 'python3 manage.py create-profile - -n "{{ PENPOT_ADMIN_FULLNAME }}" - -e "{{ PENPOT_ADMIN_EMAIL }}" - -p "$PENPOT_ADMIN_PASSWORD"' - environment: - PENPOT_ADMIN_PASSWORD: "{{ PENPOT_ADMIN_PASSWORD }}" + ansible.builtin.shell: + cmd: >- # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node + container exec -i {{ PENPOT_BACKEND_LOCAL_CID | quote }} + python3 manage.py create-profile + -n {{ PENPOT_ADMIN_FULLNAME | quote }} + -e {{ PENPOT_ADMIN_EMAIL | quote }} + stdin: "{{ PENPOT_ADMIN_PASSWORD }}" register: penpot_admin_create retries: 30 delay: 5 @@ -29,13 +28,12 @@ delegate_to: "{{ PENPOT_BACKEND_HOST_NODE }}" - name: "๐Ÿ”ง Set the Penpot administrator's local password when the profile already exists" - ansible.builtin.shell: > # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node - container exec -i -e PENPOT_ADMIN_PASSWORD {{ PENPOT_BACKEND_LOCAL_CID | quote }} - sh -lc 'python3 manage.py update-profile - -e "{{ PENPOT_ADMIN_EMAIL }}" - -p "$PENPOT_ADMIN_PASSWORD"' - environment: - PENPOT_ADMIN_PASSWORD: "{{ PENPOT_ADMIN_PASSWORD }}" + ansible.builtin.shell: + cmd: >- # nocheck: container-exec-resolver runtime-resolved CID, delegated to the hosting node + container exec -i {{ PENPOT_BACKEND_LOCAL_CID | quote }} + python3 manage.py update-profile + -e {{ PENPOT_ADMIN_EMAIL | quote }} + stdin: "{{ PENPOT_ADMIN_PASSWORD }}" when: "'already exists' in (penpot_admin_create.stdout | default('') ~ penpot_admin_create.stderr | default(''))" register: penpot_admin_update changed_when: "'Updated' in penpot_admin_update.stdout" diff --git a/roles/web-app-socialhome/meta/services.yml b/roles/web-app-socialhome/meta/services.yml index efa6759777..b29ff3da46 100644 --- a/roles/web-app-socialhome/meta/services.yml +++ b/roles/web-app-socialhome/meta/services.yml @@ -14,7 +14,7 @@ socialhome: placement: manager name: socialhome image: ghcr.io/social-home-io/socialhome - version: "2026.10.20" + version: "2026.10.22" ports: local: http: 8089 diff --git a/tests/unit/python/plugins/lookup/test_compose_file_args.py b/tests/unit/python/plugins/lookup/test_compose_file_args.py index 38116e0d0b..0a8deafb6e 100644 --- a/tests/unit/python/plugins/lookup/test_compose_file_args.py +++ b/tests/unit/python/plugins/lookup/test_compose_file_args.py @@ -1,5 +1,7 @@ import importlib.util +import tempfile import unittest +from pathlib import Path from unittest.mock import patch from ansible.errors import AnsibleError @@ -206,6 +208,16 @@ class TestComposeFArgs(unittest.TestCase): self.assertEqual(out, "-f /x/compose.yml") + def test_role_provides_override_reads_the_role_of_the_given_application(self): + with ( + tempfile.TemporaryDirectory() as role, + patch.object(self.m, "abs_role_path_by_application_id", return_value=role), + ): + self.assertFalse(self.m._role_provides_override(application_id="x")) + (Path(role) / "templates").mkdir() + (Path(role) / "templates" / "compose.override.yml.j2").write_text("") + self.assertTrue(self.m._role_provides_override(application_id="x")) + def test_requires_one_term(self): with self.assertRaises(AnsibleError): self.lookup.run([], variables=self.vars) diff --git a/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py b/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py index d5822c4e59..28084185c2 100644 --- a/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py +++ b/tests/unit/python/roles/web-app-bigbluebutton/filter_plugins/test_compose_mods.py @@ -197,6 +197,26 @@ services: self.compose_repository_path + "/mod/html5-dev", ) + def test_build_stripped_for_a_pinned_service_without_upstream_image(self): + original = """services: + bbb-shared-notes-server: + build: + context: mod/bbb-shared-notes-server + html5-dev: + build: + context: mod/html5-dev +""" + services = load_yaml_str( + compose_mods( + original, + self.compose_repository_path, + self.env_file, + pinned=["bbb-shared-notes-server"], + ) + )["services"] + self.assertNotIn("build", services["bbb-shared-notes-server"]) + self.assertIn("build", services["html5-dev"]) + if __name__ == "__main__": unittest.main()