diff --git a/.dockerignore b/.dockerignore index d2098ebad7..5cbbce7aee 100644 --- a/.dockerignore +++ b/.dockerignore @@ -20,6 +20,8 @@ build/* .ruff_cache .agents .claude/skills/ +.claude/worktrees/ +.worktrees/ .claude-* collections/ .env diff --git a/.gitignore b/.gitignore index d2098ebad7..5cbbce7aee 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,8 @@ build/* .ruff_cache .agents .claude/skills/ +.claude/worktrees/ +.worktrees/ .claude-* collections/ .env diff --git a/Makefile b/Makefile index e08a2a1888..b400a0d679 100644 --- a/Makefile +++ b/Makefile @@ -260,7 +260,7 @@ compose-system-purge: compose-entity-purge .PHONY: compose-up # Start the development stack. -compose-up: install +compose-up: install fix-dockerignore @"$${PYTHON}" -m cli.administration.deploy.development up .PHONY: console diff --git a/package-lock.json b/package-lock.json index f6f2f7668e..b357c68e7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@playwright/test": "^1.64.1", "eslint": "^10.14.0", "eslint-plugin-playwright": "^2.12.1", - "globals": "^17.13.0", + "globals": "^17.14.0", "stylelint": "^17.16.0" } }, @@ -1233,9 +1233,9 @@ } }, "node_modules/globals": { - "version": "17.13.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", - "integrity": "sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==", + "version": "17.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.14.0.tgz", + "integrity": "sha512-UtdkTVyAJePQv0J2M9Xe4L0ruNSDV314XiIJsADI2Zf40bcdAonE/D/OhAU8AT99oij9mhLByb+NvA3QWZYgCg==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 51b1424985..a27faa405f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@playwright/test": "^1.64.1", "eslint": "^10.14.0", "eslint-plugin-playwright": "^2.12.1", - "globals": "^17.13.0", + "globals": "^17.14.0", "stylelint": "^17.16.0" }, "overrides": { diff --git a/plugins/lookup/tls.py b/plugins/lookup/tls.py index 03de01439a..871986a7b0 100644 --- a/plugins/lookup/tls.py +++ b/plugins/lookup/tls.py @@ -99,7 +99,8 @@ class LookupModule(LookupBase): web_protocol = "https" if enabled else "http" websocket_protocol = "wss" if enabled else "ws" web_port = 443 if enabled else 80 - base_url = f"{web_protocol}://{primary_domain}/" + origin = f"{web_protocol}://{primary_domain}" + base_url = f"{origin}/" resolved: dict[str, Any] = { "application_id": app_id, @@ -109,7 +110,7 @@ class LookupModule(LookupBase): "domains": {"primary": primary_domain, "all": all_domains}, "protocols": {"web": web_protocol, "websocket": websocket_protocol}, "ports": {"web": web_port}, - "url": {"base": base_url}, + "url": {"base": base_url, "origin": origin}, } if want: diff --git a/roles/svc-storage-nfs-server/tasks/01_prepare.yml b/roles/svc-storage-nfs-server/tasks/01_prepare.yml index d265346729..5cfa039600 100644 --- a/roles/svc-storage-nfs-server/tasks/01_prepare.yml +++ b/roles/svc-storage-nfs-server/tasks/01_prepare.yml @@ -18,6 +18,14 @@ group: root mode: "0755" +- name: "📁 Pre-create the {{ DIR_BACKUPS }} mount point in the shared state" + ansible.builtin.file: + path: "{{ [STORAGE_NFS_STATE_PATH, DIR_BACKUPS | relpath(DIR_VAR_LIB)] | path_join }}" + state: directory + owner: root + group: root + mode: "0755" + - name: "✅ Assert the state subdir is strictly nested under the export base" ansible.builtin.assert: that: diff --git a/roles/web-app-fider/templates/env.j2 b/roles/web-app-fider/templates/env.j2 index 30ebb09da2..38abb69ce7 100644 --- a/roles/web-app-fider/templates/env.j2 +++ b/roles/web-app-fider/templates/env.j2 @@ -1,6 +1,6 @@ {% include 'roles/sys-svc-container/templates/env.j2' %} -BASE_URL={{ lookup('tls', application_id, 'url.base') }} +BASE_URL={{ lookup('tls', application_id, 'url.origin') }} DATABASE_URL=postgresql://{{ lookup('database', application_id, 'username') }}:{{ lookup('database', application_id, 'password') }}@{{ lookup('database', application_id, 'host') }}:{{ lookup('database', application_id, 'port') }}/{{ lookup('database', application_id, 'name') }}?sslmode=disable JWT_SECRET={{ (application_id ~ lookup('users', 'administrator').password) | hash('sha256') }} diff --git a/roles/web-app-keycloak/meta/services.yml b/roles/web-app-keycloak/meta/services.yml index 86d2f2f0af..e4ad93e494 100644 --- a/roles/web-app-keycloak/meta/services.yml +++ b/roles/web-app-keycloak/meta/services.yml @@ -51,7 +51,7 @@ keycloak: shared: true provides: sso image: quay.io/keycloak/keycloak - version: "26.8.1" + version: "26.8.2" name: keycloak min_storage: 468MB cpus: "2.0" @@ -91,7 +91,7 @@ keycloak: # nocheck: playwright-service-flag — sidecar shape, no probe-able surface flag sso_proxy: image: quay.io/oauth2-proxy/oauth2-proxy - version: "v7.15.5" + version: "v7.15.6" min_storage: 220MB configuration_file: oauth2-proxy-keycloak.cfg ports: diff --git a/roles/web-app-mattermost/meta/services.yml b/roles/web-app-mattermost/meta/services.yml index fd3b6ef275..e7b08833e8 100644 --- a/roles/web-app-mattermost/meta/services.yml +++ b/roles/web-app-mattermost/meta/services.yml @@ -46,7 +46,7 @@ mattermost: shared: true user_access_token_enabled: "{{ 'web-app-nextcloud' in group_names }}" image: mattermost/mattermost-team-edition - version: 12.0.2 + version: 12.1.0 min_storage: 659MB backup: no_stop_required: true diff --git a/roles/web-app-moodle/meta/addons/auth_oidc.yml b/roles/web-app-moodle/meta/addons/auth_oidc.yml index 5d68112121..88b4da3c08 100644 --- a/roles/web-app-moodle/meta/addons/auth_oidc.yml +++ b/roles/web-app-moodle/meta/addons/auth_oidc.yml @@ -3,7 +3,7 @@ enabled: "{{ lookup('config', 'web-app-moodle', 'services.sso.enabled') | bool } required: false mechanism: plugin source: upstream -version: "v5.2.1" +version: "v5.3.0" bridges: - sso update: diff --git a/roles/web-app-nextcloud/meta/services.yml b/roles/web-app-nextcloud/meta/services.yml index eabc21d99a..ac9c5c55ec 100644 --- a/roles/web-app-nextcloud/meta/services.yml +++ b/roles/web-app-nextcloud/meta/services.yml @@ -141,7 +141,7 @@ context_agent: shared: "{{ lookup('config', 'web-app-nextcloud', 'mcp.enabled') | bool }}" name: nextcloud-context-agent image: ghcr.io/nextcloud/context_agent - version: 2.10.0 + version: 2.11.0 backup: no_stop_required: true # nocheck: single-replica diff --git a/roles/web-app-prometheus/meta/services.yml b/roles/web-app-prometheus/meta/services.yml index 3efbe50131..6b514e2828 100644 --- a/roles/web-app-prometheus/meta/services.yml +++ b/roles/web-app-prometheus/meta/services.yml @@ -55,7 +55,7 @@ prometheus: provides: - prometheus image: prom/prometheus # nocheck: volumes-path-single-spot: the repository path of an image name, not a mount target - version: v3.15.1 + version: v3.16.0 name: prometheus cpus: "0.5" mem_reservation: 512m diff --git a/roles/web-app-stalwart/meta/services.yml b/roles/web-app-stalwart/meta/services.yml index 710bb31351..0e2cd29530 100644 --- a/roles/web-app-stalwart/meta/services.yml +++ b/roles/web-app-stalwart/meta/services.yml @@ -42,7 +42,7 @@ stalwart: # nocheck: default-placement-manager mail edge at a known IP: MX A-record, PTR and SPF bind to the stack host IP (a floating task's egress IP breaks deliverability); the mail store is NFS-hostile and IS the app — same rationale as web-app-mailu placement: manager image: stalwartlabs/stalwart - webui_version: "v2.0.4" + webui_version: "v2.0.5" version: "v1.0.3" update: key: webui_version diff --git a/roles/web-app-wazuh/meta/services.yml b/roles/web-app-wazuh/meta/services.yml index 224974902e..a3ed60f9e1 100644 --- a/roles/web-app-wazuh/meta/services.yml +++ b/roles/web-app-wazuh/meta/services.yml @@ -68,7 +68,7 @@ certs_generator: pids_limit: 128 manager: image: wazuh/wazuh-manager - version: "4.14.9" + version: "4.14.10" name: wazuh.manager # nocheck: min-storage-headroom Reason: budgets room for wazuh-manager's own operational data (var-ossec logs, alerts, agent queue/state) to grow well beyond the base image over the service's lifetime, not just the image itself. min_storage: 10GB @@ -98,7 +98,7 @@ indexer: # nocheck: healthcheck-timing-override covers the indexer's real boot without waiting out the curl flavor's 10m default, which would otherwise stall manager/dashboard's startup order far longer than the indexer actually needs. start_period: 90s image: wazuh/wazuh-indexer - version: "4.14.9" + version: "4.14.10" name: wazuh.indexer # nocheck: min-storage-headroom Reason: budgets room for the OpenSearch index data (security events/alerts Wazuh continuously ingests) to grow well beyond the base image over the service's lifetime, not just the image itself. min_storage: 20GB @@ -116,7 +116,7 @@ indexer: http: 8097 console: image: wazuh/wazuh-dashboard - version: "4.14.9" + version: "4.14.10" name: wazuh.dashboard # nocheck: min-storage-headroom Reason: budgets room for the dashboard's own runtime state (OpenSearch Dashboards bundle/build cache rebuilt on first boot, plus exported reports and saved-object data) to grow beyond the base image, not just the image itself. min_storage: 2GB diff --git a/roles/web-app-xwiki/meta/addons/oidc-authenticator.yml b/roles/web-app-xwiki/meta/addons/oidc-authenticator.yml index dcd823c54c..c4c0021e48 100644 --- a/roles/web-app-xwiki/meta/addons/oidc-authenticator.yml +++ b/roles/web-app-xwiki/meta/addons/oidc-authenticator.yml @@ -5,7 +5,7 @@ mechanism: extension source: upstream bridges: - sso -version: "2.29.1" +version: "2.30.0" update: type: http_regex url: https://maven.xwiki.org/releases/org/xwiki/contrib/oidc/oidc-authenticator/maven-metadata.xml diff --git a/tests/integration/ansible/filters/test_defined.py b/tests/integration/ansible/filters/test_defined.py index cf8cd59d76..4ea402eaa4 100644 --- a/tests/integration/ansible/filters/test_defined.py +++ b/tests/integration/ansible/filters/test_defined.py @@ -90,6 +90,7 @@ BUILTIN_FILTERS: set[str] = { "items2dict", "password_hash", "path_join", + "relpath", "product", "quote", "split", diff --git a/tests/lint/repository/test_dockerignore.py b/tests/lint/repository/test_dockerignore.py new file mode 100644 index 0000000000..c900866f99 --- /dev/null +++ b/tests/lint/repository/test_dockerignore.py @@ -0,0 +1,33 @@ +import re +import unittest + +from utils.cache.files import read_text + +from . import PROJECT_ROOT + +NESTED_CHECKOUTS = (".claude/worktrees/", ".worktrees/") + + +class TestDockerignore(unittest.TestCase): + def test_dockerignore_is_the_gitignore(self) -> None: + self.assertEqual( + read_text(str(PROJECT_ROOT / ".gitignore")), + read_text(str(PROJECT_ROOT / ".dockerignore")), + "Run 'make fix-dockerignore': .dockerignore must be a copy of .gitignore.", + ) + + def test_nested_checkouts_stay_out_of_the_build_context(self) -> None: + lines = read_text(str(PROJECT_ROOT / ".dockerignore")).splitlines() + for entry in NESTED_CHECKOUTS: + with self.subTest(entry=entry): + self.assertIn(entry, lines) + + def test_compose_up_regenerates_the_dockerignore(self) -> None: + makefile = read_text(str(PROJECT_ROOT / "Makefile")) + match = re.search(r"^compose-up:(.*)$", makefile, re.MULTILINE) + self.assertIsNotNone(match, "Makefile has no compose-up target") + self.assertIn("fix-dockerignore", match.group(1).split()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/python/plugins/lookup/test_tls.py b/tests/unit/python/plugins/lookup/test_tls.py index af91c312e7..0cd80fcbb7 100644 --- a/tests/unit/python/plugins/lookup/test_tls.py +++ b/tests/unit/python/plugins/lookup/test_tls.py @@ -85,6 +85,16 @@ class TestTlsResolveLookup(unittest.TestCase): )[0] self.assertEqual(val, "https://a.example/") + def test_url_origin_is_the_base_url_without_its_trailing_slash(self): + origin, base = ( + self.lookup.run(["web-app-a", want], variables=self.base_vars, mode="app")[ + 0 + ] + for want in ("url.origin", "url.base") + ) + self.assertEqual(origin, "https://a.example") + self.assertEqual(f"{origin}/", base) + def test_protocols_web_path_is_http_for_an_onion_only_app(self): onion = dict(self.base_vars) onion["domains"] = {**self.domains, "web-app-onion": "abc123.onion"}