Compare commits

...

310 Commits

Author SHA1 Message Date
Mantas Masalskis
7b753d4fb0 Maintenance: Bumped dalli to 5.2.2 (GHSA-3553-vcg5-72jw, GHSA-4qp6-2jcr-596v,... 2026-10-06 23:28:21 +03:00
Dominik Klein
ee95993569 Maintenance: Allow alternative FQDN for WebSocket origins.
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-10-06 18:19:47 +02:00
Florian Liebe
e732fa5107 Fixes #6413 - Drag & Drop (macros / group move) available on all tabs in detail search, but should be limited to tickets.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-10-06 10:40:12 +02:00
Florian Liebe
ee04322a63 Fixes #6386 - Postmaster filter: #{regexp.…} cannot be saved into email/url attributes.
Approved-by: Dominik Klein <dk@zammad.com>
2026-10-06 10:28:33 +02:00
Florian Liebe
d66f7c9d68 Maintenance: Measure the CPU time instead of the wall-clock time in the performance specs of html2text and TextEncoding.
Approved-by: Martin Gruner <mg@zammad.com>
2026-10-06 10:28:23 +02:00
Dusan Vuckovic
9fd8a6ad87 Maintenance: Desktop view - Danger styling on buttons does not reliably signal irrecoverable actions.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-10-06 09:19:54 +01:00
Florian Liebe
416fe41626 Maintenance: Fixed packager.io RHEL scenario skipping the boot wait before systemd is reachable. 2026-10-06 09:57:42 +02:00
renovatebot
9f8f2388e7 Maintenance: Update dependency eslint to ^10.12.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-06 09:22:08 +02:00
renovatebot
911cb27aa6 Maintenance: Update dependency @frsource/cypress-plugin-visual-regression-diff to ^4.2.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-06 09:17:45 +02:00
Florian Liebe
24277638f9 Maintenance: Fixed flaky packager.io RHEL 10 job caused by a dnf lock race.
Approved-by: Martin Gruner <mg@zammad.com>
2026-10-06 09:17:15 +02:00
Florian Liebe
110fa3339c Follow-up 09ca564bfcc72a9d83851ede09621d8b16c62b55 - Maintenance: Stop exposing the session ID in the signshow payload and authenticate the legacy WebSocket login from the session cookie.
Approved-by: Dominik Klein <dk@zammad.com>
2026-10-05 21:41:06 +02:00
Mantas Masalskis
1e486439ef Maintenance: Do not establish a session when verifying a signup email address.
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 21:41:05 +02:00
Florian Liebe
9a1439e7fc Maintenance: Desktop view - Improved rendering of tree select option labels.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-10-05 21:40:26 +02:00
Rene Reimann
713173c2a7 Maintenance: Desktop view - Improved rendering of select option labels
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:26 +02:00
Florian Liebe
0b3f314645 Tidy BREAKING_CHANGES.md file. 2026-10-05 21:40:25 +02:00
Florian Liebe
551d4f7fac Maintenance: Improve the handling of password reset and verification emails.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-10-05 21:40:24 +02:00
Mantas Masalskis
ed181aaa76 Maintenance: Improve authorization scoping for the getting started wizard payload
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 21:40:24 +02:00
Dominik Klein
65b7d0843b Maintenance: Improve notification template handling for some special situations.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:23 +02:00
Dusan Vuckovic
50bdaae0ac Maintenance: Stop exposing the session ID in the signshow payload and authenticate the legacy WebSocket login from the session cookie. 2026-10-05 21:40:22 +02:00
Mantas Masalskis
0228d7dbd6 Maintenance: Render pushed collections for the recipient's assets level
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 21:40:21 +02:00
Benjamin Scharf
465b2a8f43 Maintenance: Missing authorization of the queried object in LinksController#index
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-10-05 21:40:21 +02:00
Mantas Masalskis
1129cc4f03 Maintenance: Give the knowledge base controller policies their own permission declarations.
Approved-by: Rene Reimann <rr@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 21:40:20 +02:00
Florian Liebe
060ea46b1b Maintenance: Improved authorization of activity stream entries.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-10-05 21:40:19 +02:00
Florian Liebe
a74ed5fbba Maintenance: Improve Organization association attribute handling.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-10-05 21:40:19 +02:00
Florian Liebe
98e279a474 Maintenance: Improved handling of online notifications and related assets.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-10-05 21:40:18 +02:00
Florian Liebe
ebe4b7a454 Maintenance: Restrict attribute assignment via GraphQL object attribute values.
Approved-by: Dominik Klein <dk@zammad.com>
2026-10-05 21:40:17 +02:00
Mantas Masalskis
31794c4b56 Maintenance: Inactive sessions remain authorized over ActionCable GraphQL
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 21:40:16 +02:00
Mantas Masalskis
7d7975eee5 Maintenance: Improve consistency of role based knowledge base access checks.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-10-05 21:40:16 +02:00
Florian Liebe
045a91746b Maintenance: Improve handling of templates in desktop view.
Approved-by: Dominik Klein <dk@zammad.com>
2026-10-05 21:40:15 +02:00
Florian Liebe
a26b9ad2de Maintenance: Improved handling of article preferences and attachments.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-10-05 21:40:14 +02:00
Rolf Schmidt
5e1c80bcb0 Maintenance: Improve consistency of role based group access checks.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:14 +02:00
Rolf Schmidt
c0c5aa3d38 Maintenance: Harden overview sorting order direction handling.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:13 +02:00
Silke Jaenichen
6f5ca10dfe Maintenance: Improve the sender format handling in quoted email headers.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:12 +02:00
Rene Reimann
ea9dc41f50 Maintenance: Improve ticket article create endpoint.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:11 +02:00
Dominik Klein
54e9f987eb Maintenance: Ticket update responses return unredacted user and organization assets to customers.
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:11 +02:00
Silke Jaenichen
151f641c1d Maintenance: Align legacy knowledge base reader video marker rendering with the server-side helper.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:10 +02:00
Florian Liebe
b77d668f5d Maintenance: Update translations. 2026-10-05 21:40:09 +02:00
Dominik Klein
2e7daf6b19 Maintenance: Improve authorization scoping for knowledge base reordering and publishing actions.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:09 +02:00
Silke Jaenichen
1200360977 Maintenance: Desktop view - Escape display names in copy-to-clipboard HTML
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:08 +02:00
Dominik Klein
97a2c5e030 Maintenance: Improve audit log facebook channel handling.
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:40:07 +02:00
renovatebot
70587e634d Maintenance: Lock file maintenance (Ruby)
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:26:41 +02:00
Dominik Klein
48efde6c79 Maintenance: Align the Playwright drivers with Selenium and add an on-demand Firefox lane.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:24:38 +02:00
Dusan Vuckovic
c45ccd7e7e Maintenance: Update dependency vite_ruby to v3.11.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 21:22:26 +02:00
Dominik Klein
db1d5ec219 Maintenance: Cancel the running queries on logout.
Approved-by: Benjamin Scharf <bs@zammad.com>
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 20:49:25 +02:00
Mantas Masalskis
bc067ef3f0 Fixes #6399 - Knowledge base search index stores HTML-escaped titles and answer bodies.
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 16:56:15 +03:00
Florian Liebe
5fb14cbae6 Follow-up 6d3d2456ea - Maintenance: No password_change notification when password is set via the user-update endpoint.
Approved-by: Marcel Bialas <mb@zammad.com>
2026-10-05 13:18:51 +02:00
Mantas Masalskis
2731289bf3 Fixes #6396 - Reply actions throw "Cannot read properties of null (reading 'rangeCount')" before the first mouse click.
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-05 13:25:44 +03:00
Benjamin Scharf
579146276f Maintenance: Desktop view - Compact header ignores clicks on detail views too short to scroll the full header away
Approved-by: Marcel Bialas <mb@zammad.com>
2026-10-05 11:11:43 +02:00
Dominik Klein
e4cebad6ca Fixes #6404 - Postmaster filter list in admin UI silently shows only the first 500 filters.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 10:46:19 +02:00
Silke Jaenichen
cec59d3b6b Fixes #6416 - Customer chat "Waiting Customers" and "Chatting Customers" popovers do not show the time.
Approved-by: Marcel Bialas <mb@zammad.com>
2026-10-05 10:33:22 +02:00
Florian Liebe
f51bedcf5e Maintenance: Stop canceling Renovate pipelines on the first failed job.
Approved-by: Martin Gruner <mg@zammad.com>
2026-10-05 10:12:05 +02:00
renovatebot
075a5cc32a Maintenance: Update dependency aws-sdk-s3 to v1.233.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 09:29:49 +02:00
renovatebot
38c00436e2 Maintenance: Update dependency eslint-plugin-security to ^4.2.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 09:11:29 +02:00
renovatebot
9f29391db4 Maintenance: Update dependency vite to ^8.3.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 09:02:50 +02:00
renovatebot
677aef4b1d Maintenance: Update dependency mocha to ^12.0.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 09:01:17 +02:00
renovatebot
945b7369ce Maintenance: Update dependency listen to v3.10.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 08:08:04 +02:00
renovatebot
c3a566e783 Maintenance: Update dependency brakeman to v8.1.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 08:07:41 +02:00
renovatebot
f7048b1308 Maintenance: Update dependency stylelint to ^17.16.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-05 08:07:17 +02:00
Benjamin Scharf
48312937df Maintenance: Double-click in an article body opens the meta information header
Approved-by: Marcel Bialas <mb@zammad.com>
2026-10-05 07:55:27 +02:00
renovatebot
5dfbb30150 Maintenance: Update dependency net-smtp to v0.5.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 14:32:47 +02:00
renovatebot
0577200f87 Maintenance: Update Test related packages to ^5.0.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 14:32:25 +02:00
renovatebot
6e1d0882ef Maintenance: Update dependency selenium-webdriver to v4.50.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 12:10:59 +02:00
renovatebot
6edde8a670 Maintenance: Update Zammad dependencies via updatecli
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: updatecli-bot <updatecli-bot@updatecli.io>
2026-10-04 11:53:07 +02:00
renovatebot
98da351d09 Maintenance: Update dependency altcha to v3.2.4
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 11:51:58 +02:00
renovatebot
bb69454ca8 Maintenance: Update dependency tzinfo-data to v1.2026.5
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 11:51:45 +02:00
renovatebot
8cb978bda1 Maintenance: Update pnpm to v11.28.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 11:51:30 +02:00
renovatebot
e0aad7951c Maintenance: Update Tiptap packages to v3.31.4
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-04 11:51:16 +02:00
renovatebot
9e5237a033 Maintenance: Update dependency vite-plugin-ruby to ^5.2.5
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-03 14:29:30 +02:00
renovatebot
3d2815aecc Maintenance: Update dependency cypress to ^16.1.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-03 14:29:16 +02:00
renovatebot
8935f1b4c3 Maintenance: Update dependency @colbymchenry/codegraph to ^1.6.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-03 14:29:05 +02:00
renovatebot
2e696f91aa Maintenance: Update dependency capybara-playwright-driver to v0.5.12
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-10-02 14:14:44 +02:00
renovatebot
2058e0ee3f Maintenance: Update dependency twilio-ruby to v7.11.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-02 13:17:22 +02:00
renovatebot
023601fc5e Maintenance: Update dependency vue-echarts to ^8.3.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-02 13:17:01 +02:00
renovatebot
3ddf995ed2 Maintenance: Update Oxc packages
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-02 13:16:36 +02:00
Benjamin Scharf
600ab13125 Maintenance: Desktop view - Knowledge base floating toolbar is not clickable
Approved-by: Marcel Bialas <mb@zammad.com>
2026-10-02 12:44:51 +02:00
Mantas Masalskis
b665173bc6 Fixes #6398 - Recurring all-day iCal holidays are stored one day early on hosts east of UTC.
Approved-by: Martin Gruner <mg@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-10-02 11:48:05 +03:00
Dusan Vuckovic
da9cf5813a Follow-up 0291a23eef - Feature: Integrate LibreTranslate as backend translation service
Approved-by: Marcel Bialas <mb@zammad.com>
2026-10-02 08:16:06 +01:00
renovatebot
e5fbbba94e Maintenance: Update dependency aws-sdk-s3 to v1.232.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-02 08:48:23 +02:00
renovatebot
7b834fab61 Maintenance: Update pnpm to v11.28.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-02 08:48:14 +02:00
renovatebot
362a54ab39 Maintenance: Update Typescript/Types packages to ^8.71.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-02 08:47:45 +02:00
Dominik Klein
5970fba843 Maintenance: Fix websocket sessions getting lost on login with the file session store.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-01 20:34:30 +02:00
Dusan Vuckovic
5a5c7650a7 Follow-up 11b1461156 - Maintenance: Desktop view - Improving the visibility of normal body text and muted text in light mode. 2026-10-01 14:16:02 +01:00
Dominik Klein
f71cd98c99 Maintenance: Check for the shown checklist when opening the checklist sidebar in the system tests.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-01 13:52:05 +02:00
Marcel Bialas
d274a3273e Maintenance: Update role requirements for editing users.
Approved-by: Rene Reimann <rr@zammad.com>
2026-10-01 13:20:02 +02:00
Dusan Vuckovic
11b1461156 Maintenance: Desktop view - Improving the visibility of normal body text and muted text in light mode.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-10-01 10:58:41 +01:00
Marcel Bialas
4234be8201 Feature: Desktop-view - Skip auto-translation for agent fluent languages
Approved-by: Benjamin Scharf <bs@zammad.com>
Approved-by: Marcel Bialas <mb@zammad.com>

Co-authored-by: Ben <bs@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-10-01 11:09:46 +02:00
Dominik Klein
409eff8afa Maintenance: Fix stale facts and conflicts in the AI agent instructions.
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-01 09:58:15 +02:00
renovatebot
6437f46e07 Maintenance: Update dependency eslint-plugin-security to ^4.1.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-10-01 08:44:47 +02:00
Dominik Klein
f6b741d5fb Maintenance: Stabilize opening the checklist sidebar in the ticket zoom system tests.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-30 21:46:30 +02:00
Florian Liebe
15fc26ea81 Fixes #6411 - Adding underlines to the overview name causes parts of the string to be underlined in dashboard panel Overview.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-30 21:27:08 +02:00
Dusan Vuckovic
480ca9d4fe Fixes #6408 - Knowledge base answer popover is stuck.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-30 16:07:28 +01:00
Rolf Schmidt
db8590f692 Fixes #6412 - Package precompile hangs on hidden pnpm modules directory purge confirmation.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-30 13:39:00 +02:00
Florian Liebe
17e0e558eb Maintenance: Speed up the CI pipeline and report test results to GitLab.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-30 12:03:58 +02:00
Florian Liebe
0e13236e75 Fixes #6407 - NoMethodError in HtmlSanitizer when an img style attribute contains an empty value (e.g. style="width:;").
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-30 11:10:07 +02:00
Florian Liebe
13c599ad2d Maintenance: Run the Playwright shadow lanes only on demand in merge requests.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-30 10:24:50 +02:00
Rolf Schmidt
7d0d97a065 Fixes #6403 - HTTP log does not record requests with unparsable parameters.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-30 09:49:34 +02:00
renovatebot
434370a1f2 Maintenance: Update dependency eslint-plugin-n to ^18.4.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-30 08:04:09 +02:00
Rolf Schmidt
9a6ec511ed Fixes #6402 - XOAUTH2 token refresh floods the audit log with channel update entries.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-29 17:44:21 +02:00
Martin Gruner
c6d7c338f9 Fixes #6391 - Large report and export downloads are held in memory several times before they are sent.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-29 17:26:04 +02:00
Martin Gruner
d57b643e08 Maintenance: Scan only the new commits for secrets in CI and limit the runtime of the lint jobs.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-29 17:24:17 +02:00
Benjamin Scharf
1e17bef2ab Feature: Desktop view - Design a working indicator for in-progress article translations
Approved-by: Marcel Bialas <mb@zammad.com>

Co-authored-by: Marcel Bialas <mb@zammad.com>
2026-09-29 16:53:51 +02:00
Dusan Vuckovic
db5e805ee0 Follow-up 62163034dd - Feature: Desktop view - Agents miss incoming calls while the browser is in the background.
Approved-by: Benjamin Scharf <bs@zammad.com>

Co-authored-by: Ben <bs@zammad.com>
2026-09-29 15:44:05 +01:00
Dusan Vuckovic
528910f1f6 Fixes #6401 - Mobile view - Error pages show a broken image instead of the illustration.
Approved-by: Marcel Bialas <mb@zammad.com>
2026-09-29 11:23:19 +01:00
Silke Jaenichen
1b8b3894c4 Fixes #6237 - Enforce maintenance mode for import_mode.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-29 12:10:24 +02:00
Mantas Masalskis
0f47ae5359 Fixes #6383 - RAILS_TRUSTED_PROXIES (Docker array format) never trusts the configured proxy — request.remote_ip always returns the proxy's own IP.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-29 12:36:23 +03:00
Florian Liebe
b1dfbc3eb1 Maintenance: Make the OCR integration spec tolerant of varying model output.
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-29 11:33:37 +02:00
Florian Liebe
07889decc9 Maintenance: Fix Telegram reply spec on the Playwright driver.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-29 10:27:22 +02:00
Florian Liebe
8a42e2f2ed Maintenance: Improve Keycloak related tests after json v3 bump.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-29 10:25:51 +02:00
Florian Liebe
d203f43a43 Maintenance: Skip the Nominatim geolocation spec when VCR cassettes are disabled, as Nominatim blocks the CI runners.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-29 09:55:13 +02:00
Florian Liebe
ff5b8af113 Maintenance: Accept the address Clearbit currently returns in the Clearbit integration spec.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-29 09:20:06 +02:00
Benjamin Scharf
6a4baf7c85 Maintenance: Desktop view - Translate menu display
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-29 09:19:00 +02:00
Dusan Vuckovic
62163034dd Feature: Desktop view - Agents miss incoming calls while the browser is in the background.
Approved-by: Benjamin Scharf <bs@zammad.com>
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-29 07:59:22 +01:00
renovatebot
cf60dd7359 Maintenance: Update pnpm to v11.28.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-29 08:32:28 +02:00
renovatebot
bcbc29436a Maintenance: Update Test related packages to ^5.0.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-29 08:32:15 +02:00
Mantas Masalskis
c63f3625ea Fixes #6371 - String#utf8_encode and Object#to_utf8 are deprecated in favour of TextEncoding.utf8_encode
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-09-28 19:41:59 +03:00
Marcel Bialas
18e00d947b Fixes #5175 - Resetting notifications settings does not restore default values for limit groups and sounds
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-28 18:02:05 +02:00
Dusan Vuckovic
914b9f596b Fixes #6377 - WhatsApp/Telegram loses linebreaks when replying to customer-initiated messages.
Approved-by: Marcel Bialas <mb@zammad.com>
2026-09-28 15:29:57 +01:00
Dusan Vuckovic
caae658587 Follow-up 1825040ffe - Fixes #6389 - Caller log entries are not relinked to a newly created customer when the stored number is not a plain digit string.
Approved-by: Silke Jaenichen <sj@zammad.com>
2026-09-28 14:57:58 +01:00
Benjamin Scharf
33e72ba0f4 Maintenance: Use eslint-plugin-better-tailwindcss for tailwind class linting in vue templates
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-28 15:43:23 +02:00
Marcel Bialas
619ef87bb2 Follow up 7b00c113e3 - Fixes #5571 - Add missing mobile view parts.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-28 15:27:14 +02:00
Dominik Klein
68277d9888 Maintenance: Wait for push delivery before the login page specs change settings.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 14:37:22 +02:00
Florian Liebe
6734a65ac9 Maintenance: Detailed search overwrites input typed right after a key release.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-28 14:09:52 +02:00
renovatebot
3ae1fa15e5 Maintenance: Lock file maintenance (Ruby)
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 12:54:01 +02:00
Dusan Vuckovic
9eb6fa00f9 Maintenance: Desktop view - Reinitalize table widths on page reactivation.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-28 11:37:56 +01:00
Martin Gruner
55b0d56dbe Fixes #6390 - Attachment downloads are fully loaded into memory before they are sent.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-28 12:19:56 +02:00
renovatebot
952d9a4a58 Maintenance: Update dependency json to v3
Approved-by: Martin Gruner <mg@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-28 11:30:08 +02:00
Silke Jaenichen
1825040ffe Fixes #6389 - Caller log entries are not relinked to a newly created customer when the stored number is not a plain digit string.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-28 10:43:34 +02:00
Silke Jaenichen
ac9fc5aaac Fixes #3967 - Caller Log filter (previously notify map) mapping shows customers as well.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 09:42:50 +02:00
renovatebot
27072339e0 Maintenance: Update dependency @rails/actioncable to ~8.1.400
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 09:30:32 +02:00
renovatebot
991f9ac871 Maintenance: Update dependency rails to v8.1.4
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 08:13:05 +02:00
Florian Liebe
24eed6dd3f Maintenance: Update clearbit spec.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 08:11:43 +02:00
renovatebot
bdc203b567 Maintenance: Update dependency vite to ^8.3.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 07:56:18 +02:00
renovatebot
c2e1e8f389 Maintenance: Update dependency eslint-plugin-vue to ^10.11.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 07:56:07 +02:00
renovatebot
63940b512f Maintenance: Update dependency doorkeeper to v5.9.9
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 07:40:00 +02:00
renovatebot
e20dd5f95d Maintenance: Update dependency dalli to v5.1.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 06:46:40 +02:00
renovatebot
5db43b3af1 Maintenance: Update dependency jsdom to ^30.1.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 06:46:12 +02:00
renovatebot
cd1640e591 Maintenance: Update dependency eslint-plugin-oxlint to ^1.85.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-28 06:42:16 +02:00
Dusan Vuckovic
6775944657 Feature: Desktop view - Reimplement CTI integration.
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Silke Jänichen <sj@zammad.com>
Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-25 14:39:06 +01:00
Dominik Klein
42ba46bc8a Maintenance: Desktop view - Calendar subscription settings can be changed before they have loaded
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-25 13:47:52 +02:00
Dominik Klein
d2ac472902 Feature: Use hybrid Markdown and markers for AI-powered article translation.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-25 12:59:31 +02:00
Marcel Bialas
71c22ef656 Feature: Read collected translation feedback as an admin
Approved-by: Marcel Bialas <mb@zammad.com>
Approved-by: Benjamin Scharf <bs@zammad.com>

Co-authored-by: Ben <bs@zammad.com>
2026-09-25 12:14:22 +02:00
Benjamin Scharf
aafa1d635e Maintenance: Desktop view - Browsing the knowledge base shows short interim states while loading
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-25 11:54:59 +02:00
renovatebot
950c5b61b6 Maintenance: Update dependency graphql to v2.6.11
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-25 11:46:52 +02:00
Dominik Klein
521e7a6fdb Maintenance: Stabilize full quote header sender format system tests.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-25 10:49:27 +02:00
renovatebot
a924a39905 Maintenance: Update Zammad dependencies via updatecli
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: updatecli-bot <updatecli-bot@updatecli.io>
2026-09-25 10:39:40 +02:00
renovatebot
08582fea8a Maintenance: Update Typescript/Types packages to ^8.70.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-25 10:39:29 +02:00
renovatebot
9cc5fabd53 Maintenance: Update Oxc packages
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-25 10:39:16 +02:00
Florian Liebe
d675cde614 Maintenance: Use precompiled native gems for common platforms.
Approved-by: Martin Gruner <mg@zammad.com>

Co-authored-by: Martin Gruner <mg@zammad.com>
2026-09-25 08:49:37 +02:00
Mantas Masalskis
5b396c16c1 Fixes #6384 - Digits of the auto-generated login are indexed as known caller IDs of a user.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-24 21:41:03 +03:00
Marcel Bialas
aa6e2d44ec Feature: Desktop view - Agent feedback for translation quality
Approved-by: Marcel Bialas <mb@zammad.com>
Approved-by: Benjamin Scharf <bs@zammad.com>

Co-authored-by: Ben <bs@zammad.com>
2026-09-24 15:06:13 +02:00
Benjamin Scharf
d4a704eb84 Fixes #6378 - AI result feedback can be submitted again
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-24 13:11:05 +02:00
Dominik Klein
0c5234a571 Maintenance: Desktop view - Skip subscription updates for the browser tab which caused them.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-24 12:53:20 +02:00
Dominik Klein
b9b1b27f8f Maintenance: Stabilize editor typing in system tests.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-24 12:52:26 +02:00
Dominik Klein
9d92a60807 Maintenance: Unify article translation requests.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-24 12:29:47 +02:00
Rene Reimann
6af6c459a0 Fixes #6376 - Signed mail is displayed in Zammad with no visible content
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-24 11:34:23 +02:00
renovatebot
3a841fbd4c Maintenance: Update dependency altcha to v3.2.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-24 09:43:02 +02:00
renovatebot
447e9c0d95 Maintenance: Update pnpm to v11.27.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-24 08:55:13 +02:00
renovatebot
f5aed5ec81 Maintenance: Update dependency @frsource/cypress-plugin-visual-regression-diff to ^4.2.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-24 08:53:14 +02:00
renovatebot
1ad0d928a4 Maintenance: Update dependency rubocop-rails to v2.38.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-24 08:52:52 +02:00
Marcel Bialas
50afc49814 Maintenance: Facebook article links render without a caption in the new frontend
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-23 18:46:47 +02:00
Dusan Vuckovic
df5b7e16b6 Fixes #6379 - Monitoring reports old AI provider connection errors after the connection is no longer assigned.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-23 16:44:56 +01:00
Marcel Bialas
a766089e94 Feature: Desktop-view - Record translation analytics for non-AI translation services
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-23 17:19:32 +02:00
Florian Liebe
ab9b64d571 Maintenance: Fix the Vitest command in the agent instructions.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-23 15:25:46 +02:00
renovatebot
a7385ec42e Maintenance: Update dependency markdownlint-cli2 to ^0.23.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-23 14:54:44 +02:00
renovatebot
8226a0a2e2 Maintenance: Update Playwright packages to v1.63.0
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-23 13:15:47 +02:00
Florian Liebe
4166a43ae0 Maintenance: Let Renovate keep the Playwright pins in lockstep.
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-23 09:32:42 +02:00
renovatebot
6f6ea1a880 Maintenance: Update memcached Docker tag to v1.6.45
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-23 08:38:50 +02:00
renovatebot
35766eadde Maintenance: Update Zammad dependencies via updatecli
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: updatecli-bot <updatecli-bot@updatecli.io>
2026-09-23 07:50:42 +02:00
Florian Liebe
662245aca9 Maintenance: Update version for Zammad 7.3.x.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-23 07:33:19 +02:00
Mantas Masalskis
9e84eb1b59 Maintenance: Desktop view - Cannot create API token.
Approved-by: Benjamin Scharf <bs@zammad.com>
Approved-by: Dominik Klein <dk@zammad.com>

Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Mantas <mantas@idev.lt>
2026-09-22 22:43:24 +03:00
Benjamin Scharf
ca0015a9fb Feature: Desktop view - Automatically translate ticket articles
Approved-by: Dominik Klein <dk@zammad.com>

Co-authored-by: Dominik Klein <dk@zammad.com>
2026-09-22 17:43:15 +02:00
Dominik Klein
54ee63f620 Maintenance: Cover foreign route precedence in the desktop search input.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-22 16:18:19 +02:00
Martin Gruner
4be72d4ca8 Maintenance: update Rails to 8.1
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-22 13:47:23 +02:00
Dusan Vuckovic
cb0c223f43 Follow-up b3075fe7d5 - Maintenance: Improve readability of HTTP log entries for Webhooks and AI providers.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-22 11:52:32 +01:00
Marcel Bialas
6d3d2456ea Maintenance: No password_change notification when password is set via the user-update endpoint
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-22 10:11:59 +02:00
renovatebot
55da798cbd Maintenance: Update elasticsearch Docker tag to v9.5.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-22 08:44:54 +02:00
renovatebot
0afee62ecf Maintenance: Update dependency type-fest to ^5.10.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-22 08:26:10 +02:00
renovatebot
c76fc69ad2 Maintenance: Update dependency eslint to ^10.11.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-22 08:25:28 +02:00
Weblate
7ece397217 Maintenance: Update translated files and catalog 2026-09-22 06:01:23 +01:00
Weblate
fc761060c3 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Igor Costa <igor_sfx@yahoo.com.br>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Thomas Meijer <tvm@bizconnect.dk>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/da/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/pt_BR/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/uk/
Translation: Zammad/Zammad (development version)
2026-09-22 04:56:08 +00:00
Marcel Bialas
22f7ec2b18 Fixes #6369 - Knowledge Base wildcard search shows escaped special characters in titles.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-21 18:16:10 +02:00
Dusan Vuckovic
3e42f8d238 Fixes #6370 - Core workflow ignores default value if select attribute options are mutated.
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-09-21 13:34:40 +01:00
Mantas Masalskis
1ae9dc3bd5 Fixes #6363 - Desktop view: a date or datetime attribute marks every ticket tab as changed.
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-09-21 14:23:55 +03:00
Benjamin Scharf
3c7d16f786 Fixes #6372 - Ticket draft gets duplicated everytime the Browser back button is used.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-21 10:46:59 +02:00
renovatebot
451268cfcd Maintenance: Update Test related packages
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 10:45:59 +02:00
renovatebot
3a18bb052c Maintenance: Update vueuse monorepo to v15
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-21 10:45:38 +02:00
renovatebot
5018dd3ad2 Maintenance: Update dependency @sa11y/preset-rules to ^8.1.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 10:45:27 +02:00
renovatebot
b57d4cb861 Maintenance: Update dependency vite-plugin-ruby to ^5.2.4
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:29:40 +02:00
renovatebot
4916ff4ede Maintenance: Update dependency cypress to ^16.1.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:29:20 +02:00
renovatebot
0a9e45fc62 Maintenance: Update dependency mocha to ^12.0.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:28:41 +02:00
renovatebot
af8e28daea Maintenance: Update dependency aws-sdk-s3 to v1.232.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:22:17 +02:00
renovatebot
494ba95e20 Maintenance: Update Vue packages
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:22:02 +02:00
renovatebot
e7a30a210a Maintenance: Update Zammad dependencies via updatecli
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: updatecli-bot <updatecli-bot@updatecli.io>
2026-09-21 08:21:44 +02:00
renovatebot
f399d51b27 Maintenance: Update dependency stylelint-scss to ^7.3.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:21:32 +02:00
renovatebot
0ba441c65d Maintenance: Update dependency @sa11y/vitest to ^8.1.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:20:33 +02:00
renovatebot
495daf94f6 Maintenance: Update dependency write_xlsx to v1.15.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-21 08:20:19 +02:00
renovatebot
e9068b270e Maintenance: Update Oxc packages
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-18 15:47:29 +02:00
Rolf Schmidt
c545ff10b2 Maintenance: Cover the staged package check in the pending migrations initializer for Kubernetes deployments.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-18 15:14:32 +02:00
Rolf Schmidt
301f2b11e1 Maintenance: Wait for staged addon packages before serving in container environments.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-18 13:05:15 +02:00
renovatebot
281e8105c3 Maintenance: Update dependency @devcontainers/cli to v0.89.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-18 12:37:50 +02:00
renovatebot
ab8bba1970 Maintenance: Update memcached Docker tag to v1.6.44
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-18 12:37:31 +02:00
renovatebot
9e3da6f931 Maintenance: Update elasticsearch Docker tag to v9.5.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-18 12:37:12 +02:00
renovatebot
c3268f811d Maintenance: Update dependency @vitejs/plugin-vue to ^6.0.9
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-18 12:28:25 +02:00
Rolf Schmidt
847138afaa Maintenance: Core Workflow - Do not show attributes without matching screen permission options.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-18 12:05:15 +02:00
Weblate
8796ac43d5 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/
Translation: Zammad/Zammad (development version)
2026-09-18 05:46:18 +00:00
Weblate
af5b5f702f Maintenance: Update translated files and catalog 2026-09-18 06:28:45 +01:00
Weblate
b4ec46ec8e Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Aivars Miška <allefm@gmail.com>
Co-authored-by: Ales Zabala Alava <shagi@gisa-elkartea.org>
Co-authored-by: Alfredo Sola <alfredo@sola.es>
Co-authored-by: Ali Sa'ad Eddeen <ali.se@alameensoft.com>
Co-authored-by: Alivion <dani-pranger@live.nl>
Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Andi Chandler <andi@gowling.com>
Co-authored-by: Andre <8a9315x8s@mozmail.com>
Co-authored-by: Andre Oli <n0bledeath.117@gmail.com>
Co-authored-by: Andrea <andreacarboni@stepservizi.net>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Antonio Nardella <info@antonionardella.it>
Co-authored-by: Arif Waram <ninearif@hotmail.com>
Co-authored-by: Artem <aasavchuk@gmail.com>
Co-authored-by: Artur Kalagov <Artur.Kalagov@gmail.com>
Co-authored-by: Arturo <r2r0@posteo.de>
Co-authored-by: Atsushi Kojima <bm@exbridge.jp>
Co-authored-by: Azharul Haque <ahaque@limitxyz.com>
Co-authored-by: Background update <noreply-weblate-update@weblate.org>
Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: Benjamin Scharf <bs@zammad.com>
Co-authored-by: Bobby Zhang <my@minthe.cn>
Co-authored-by: Bogdan Lungu <bogdy_lungu_alex@yahoo.co.uk>
Co-authored-by: Borut Baznik <borut.baznik@jhl.si>
Co-authored-by: CZAsterix <admin@upice-net.cz>
Co-authored-by: Cap6991 <an_96_drea@hotmail.it>
Co-authored-by: Cardo FemBloc <cardo@fembloc.cat>
Co-authored-by: Carko <darko@augmentech.si>
Co-authored-by: Carlos Jara <carlos.jara@gmail.com>
Co-authored-by: Carlos Lindo <carloslindo@gmail.com>
Co-authored-by: Carlos Quiros <carlos.quiros@gmail.com>
Co-authored-by: Chang W. Lee <changlee99@gmail.com>
Co-authored-by: Colin Andries <colin.andries@bisdomantwerpen.be>
Co-authored-by: Dag Notland <dag@digiserv.no>
Co-authored-by: Damian Ferrari <damianferrari.ar@gmail.com>
Co-authored-by: Daniel Garcia <gm.dani@gmail.com>
Co-authored-by: Daniel Macías <danielmaciasm@gmail.com>
Co-authored-by: Decio E. Silva <decio@verinatec.com.br>
Co-authored-by: Deleted User <noreply+1023@weblate.org>
Co-authored-by: Deleted User <noreply+139@weblate.org>
Co-authored-by: Deleted User <noreply+169@weblate.org>
Co-authored-by: Deleted User <noreply+475@weblate.org>
Co-authored-by: Deleted User <noreply+621@weblate.org>
Co-authored-by: Denys Kurochkin <denisku@wix.com>
Co-authored-by: DjMagicFingers <amir@srvit.co.il>
Co-authored-by: Dmitriy <pim-dima@ya.ru>
Co-authored-by: Dmjorge <duartemjorge@gmail.com>
Co-authored-by: Dmytro <ramadan2003@ukr.net>
Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Dominik Penyk <penyk1@uniba.sk>
Co-authored-by: Dominikmarek88 <marek47@uniba.sk>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Elias Grinias <elgrinias@gmail.com>
Co-authored-by: Emanuel Saramago <dev@emanuelsaramago.com>
Co-authored-by: Emre ÇAPAN <emrecapanogluu@gmail.com>
Co-authored-by: Falkonio <falkovds@gmail.com>
Co-authored-by: Florian Liebe <dev@florianlie.be>
Co-authored-by: FoW <fow@netspheres.org>
Co-authored-by: Francisco Rocha <francisco@rocha.com.pt>
Co-authored-by: Gerrit <github@gmail.arghs.de>
Co-authored-by: Ginjeira <lmgl1711@gmail.com>
Co-authored-by: Girrese <girrese@gmail.com>
Co-authored-by: Gius4 <giuseppe@socci.eu>
Co-authored-by: GoneTone <p29022716@gmail.com>
Co-authored-by: Goudarz Jafari <goudarz.jafari@gmail.com>
Co-authored-by: Hannesrol <Kontakt@hannesrollmann.de>
Co-authored-by: IOANNIS KALINTIRIS <kalidiris@uop.gr>
Co-authored-by: IT SMBDA <it@smb.gov.az>
Co-authored-by: Igor Costa <igor_sfx@yahoo.com.br>
Co-authored-by: Isla <m4rtu@riseup.net>
Co-authored-by: Ivan Perovic <ip@zammad.com>
Co-authored-by: JPB <JPB@users.noreply.translations.zammad.org>
Co-authored-by: JackYeah <jack11070@gmail.com>
Co-authored-by: Jano Suchal <jan.suchal@gmail.com>
Co-authored-by: JasperE84 <jasper.e@gmail.com>
Co-authored-by: Jc <dottdot.dev@gmail.com>
Co-authored-by: Jennie Gejel <jenniegejel@gmail.com>
Co-authored-by: Jeroen Baten <jbaten@i2rs.nl>
Co-authored-by: Joaquin Diego Perez Gonzalez <joaquinperez@ipglobal.es>
Co-authored-by: Joffre Ayala <ayala.joffre.clinica@gmail.com>
Co-authored-by: Jonas Mandrén <Jonas@mandren.se>
Co-authored-by: JonggwonSeon <jg.seon@sdt.inc>
Co-authored-by: Jorge Peixinho <jorge.peixinho@storesace.cv>
Co-authored-by: Joris Drenth <joris@jorisdrenth.nl>
Co-authored-by: Josep Prada <josepprada@gmail.com>
Co-authored-by: José Fernandes <jose@diasfernandes.pt>
Co-authored-by: Juan Carlos Mendez <jcmendez@alum.mit.edu>
Co-authored-by: Kamil <kserwus@gmail.com>
Co-authored-by: Katrina Laimina <katrina.laimina@paysera.net>
Co-authored-by: Krzysztof <intuicje@gmail.com>
Co-authored-by: Krzysztof Jereczek <krzysztof@pro2web.pl>
Co-authored-by: Laurens Bijker <laurens@laurensbijker-ict.nl>
Co-authored-by: Leif-Jöran Olsson <info@friprogramvarusyndikatet.se>
Co-authored-by: Leonardas <leonardas.konosovas@paysera.net>
Co-authored-by: Leonerd <3ickleonerd@gmail.com>
Co-authored-by: Leroy Aronds <leroy@nimbizz.com>
Co-authored-by: Lerrand <lerrand@gmail.com>
Co-authored-by: Lorenzo Milesi <lorenzo@mile.si>
Co-authored-by: Louise Forsen <louise.forsen@entryevent.se>
Co-authored-by: MBekspert <michal.bosak@ekspert.biz>
Co-authored-by: MacaGrox <1695460597ab@gmail.com>
Co-authored-by: Maciej Olko <maciej.olko@affirm.com>
Co-authored-by: Magdalena Dulęba <magdalena.duleba@pwr.edu.pl>
Co-authored-by: Mantas Masalskis <mantas@idev.lt>
Co-authored-by: Mantikor <nhyuga.r@gmail.com>
Co-authored-by: Marcel Bialas <mtzl.29@proton.me>
Co-authored-by: Marcel Herrguth <github@thehomeofanime.de>
Co-authored-by: Marcin Miłek <marcin.milek@pwr.edu.pl>
Co-authored-by: Marco <revolution.gf@libero.it>
Co-authored-by: Marco Badan <info@marcobadan.com>
Co-authored-by: Marcos Aurélio Silvério <maureliosilverio@gmail.com>
Co-authored-by: Mario Gomes <mafigo74@gmail.com>
Co-authored-by: Mariusz Jamro <mariusz.jamro@gmail.com>
Co-authored-by: Marlon Baez <marlon.baez@barolit.com>
Co-authored-by: Martin Bonde <martin@maboni.dk>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Michal <m.plawsiuk@razit.pl>
Co-authored-by: Michal Kotnowski <michal.kotnowski@ukotka.com>
Co-authored-by: Mikołaj Kaczmarek <mikolaj.s.kaczmarek@outlook.com>
Co-authored-by: Milan Regec <milan.regec@hey.com>
Co-authored-by: Milena Tsarvenkova <milena.tsarvenkova@paysera.net>
Co-authored-by: Misha <misha@association-enoria.org>
Co-authored-by: Mixer <mixer@mixercz.cz>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: Murat Özalp <cunyor@gmail.com>
Co-authored-by: Müslüm Cengiz <muslumcengiz@windowslive.com>
Co-authored-by: Never Min <59808@qq.com>
Co-authored-by: NightFlexer <b111vitalii@gmail.com>
Co-authored-by: Nikita <nyakovlev@iko.tech>
Co-authored-by: Ondrej Pokorny <ondrej.pokorny@entry-cz.com>
Co-authored-by: Onur Alp Akin <onralpakin@gmail.com>
Co-authored-by: Osmar Valdebenito <osmar.valdebenito@uchile.cl>
Co-authored-by: Ozan Şahin <efarduos@outlook.com>
Co-authored-by: PIXELHIZE <pixelhize@gmail.com>
Co-authored-by: PIetro Sacchi <sacchi.pietro@gmail.com>
Co-authored-by: PackyZhang <zhanghongbang@live.com>
Co-authored-by: Panagiotis Strakas <pstrakas@hotmail.com>
Co-authored-by: Paul van Wanrooij <pvwanrooij@hgvt.nl>
Co-authored-by: Paweł Rypień <pawel.rypien@bmw-sikora.pl>
Co-authored-by: Pedro Pereira <pedro.pereira@strongcharon.pt>
Co-authored-by: Peter-Hans de Zwarte <peter-hans@zwarte.no>
Co-authored-by: Petr Daniel Ambrož <paces@paces.cz>
Co-authored-by: Petr Prochazka <prochazkapetr2@gmail.com>
Co-authored-by: Philip Gabrielsen <philip@digno.no>
Co-authored-by: Phoenix <phoenix.yu@vesoft.com>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Rania Amina <reaamina@gmail.com>
Co-authored-by: Ricardo Ribeiro <chacal.ricardo@gmail.com>
Co-authored-by: Ricardo Sandoval Pérez <r.sandoval@itknow.es>
Co-authored-by: Richard <richard.savoie@hotmail.ca>
Co-authored-by: Ricky Tigg <ricky.tigg@gmail.com>
Co-authored-by: Roar Eliassen <roar.eliassen@protonmail.com>
Co-authored-by: Rob <robertborge@outlook.com>
Co-authored-by: Rok Poje <rok.poje@mintpos.si>
Co-authored-by: Rowdy van der Veen <rowdy@rowdyengeesje.nl>
Co-authored-by: Ruben <ruben@bibsyst.no>
Co-authored-by: Ruben Daniel Ribeiro Duarte <ruben.ribeiro.duarte@gmail.com>
Co-authored-by: Rudolfet <hermy.t.m1997@gmail.com>
Co-authored-by: Sajjad Jazini <s.jazini@vatanmail.ir>
Co-authored-by: SamolJacek <samol.jacek@gmail.com>
Co-authored-by: Sarino24 <jaroslav.sujan@gmail.com>
Co-authored-by: Sebastian Diaz <sebdiazf@gmail.com>
Co-authored-by: Servicios Informáticos del Ayuntamiento de Sariegos <informatica@sariegos.es>
Co-authored-by: Sezgin KOC <sezgink059@outlook.com.tr>
Co-authored-by: Shadowdenim <mikebeelen111@gmail.com>
Co-authored-by: Spiteful Grog <kev_2108@outlook.com>
Co-authored-by: Stefan Choraliev <s.choraliev@velox-systems.com>
Co-authored-by: Suat VARLIKLI <suatvarlikli89@gmail.com>
Co-authored-by: Sumonchai Wongphithak <sumonchai@gmail.com>
Co-authored-by: Svein-Erik Larsen <feinom@gmail.com>
Co-authored-by: Svyrydov Semen <pincher2006@gmail.com>
Co-authored-by: Szkolarz <lukasz.szkolarz@gmail.com>
Co-authored-by: Tecsi Aron <aron@edituraedu.ro>
Co-authored-by: Thomas Meijer <tvm@bizconnect.dk>
Co-authored-by: TomAdriaensen <tom.adriaensen@hivset.be>
Co-authored-by: Türk Oyuncu <recentlly@hotmail.com>
Co-authored-by: Vicent Oron <voron@virtualwareco.com>
Co-authored-by: Victor Gallegos <vgallegos@kaliroy.com>
Co-authored-by: Ville Perkkio <vilu@mail.suomi.net>
Co-authored-by: Vincent Handfield <vincent@accestech.com>
Co-authored-by: Vitālijs <vitalijs.gaidelis@nep.lv>
Co-authored-by: Vlad Ilie <vladilie94@gmail.com>
Co-authored-by: Vladimir <sleuths.slews0s@icloud.com>
Co-authored-by: WKobes <github@wouterkobes.nl>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Wim Deprez <wim@tamale.nl>
Co-authored-by: YosefAdPro <yosef@adpronet.com>
Co-authored-by: Zen Ken <likemoue@gmail.com>
Co-authored-by: Zhilin Ruslan <x-th-unicorn@hotmail.com>
Co-authored-by: affarinha <andre.farinha@syswave.pt>
Co-authored-by: ait0rn00b <ait0rn00b@protonmail.com>
Co-authored-by: aiworker-dev <aiworker8889@hotmail.com>
Co-authored-by: ajasimiq <ahmedjaseem@gmail.com>
Co-authored-by: albanobattistella <albanobattistella@gmail.com>
Co-authored-by: albertoarmida <alberto.armida@gmail.com>
Co-authored-by: allan-pm <ju@cicada-speech.com>
Co-authored-by: antonylu0826 <lu-antony@yahoo.com.tw>
Co-authored-by: behrad.b <behroora@yahoo.com>
Co-authored-by: chen <chenchen@4chian.com>
Co-authored-by: chrand818 <can@telenabler.com>
Co-authored-by: craguilera <craguilera@gmail.com>
Co-authored-by: crnfpp <crnfpp@unife.it>
Co-authored-by: danidemis <dani.demichi@gmail.com>
Co-authored-by: doniet <donietvelez@gmail.com>
Co-authored-by: engano <enoceda@puntotec.com>
Co-authored-by: eppupesoladigisys <eero.pesola@digisys.fi>
Co-authored-by: fabiuxxlinuxx <fabiux@gmail.com>
Co-authored-by: fluidum <erkkoisok@gmail.com>
Co-authored-by: francois littel <flittel@rentcompany.nl>
Co-authored-by: fwmats <matsunaga@fairway-corp.co.jp>
Co-authored-by: gfced <gianluca.fadda@comune.oristano.it>
Co-authored-by: grantw <grantw@gmail.com>
Co-authored-by: grtnl <geert.noel@aalst.be>
Co-authored-by: horizonxh <horizonxh@163.com>
Co-authored-by: jason_k <iasonas2001@hotmail.com>
Co-authored-by: jebulol <jebulol@gmail.com>
Co-authored-by: jeysunit <jeysunit@bcamp.net>
Co-authored-by: jingchun.xia <jingchun.xia@vesoft.com>
Co-authored-by: k-fujimaru <k.fujimaru@natsuhara.link>
Co-authored-by: kands-software <admin@kands.pl>
Co-authored-by: kartoshe4ka <gugckaev.oleg@yandex.ru>
Co-authored-by: kdh949 <donghyun.kim@chungbuk.ac.kr>
Co-authored-by: kim jy <elcidme@gmail.com>
Co-authored-by: kirkkak <kirkka.kivilo@gmail.com>
Co-authored-by: kurtjaspaert-stack <kurt.jaspaert@gmail.com>
Co-authored-by: lukenpi <lucapasquini89@gmail.com>
Co-authored-by: mathew-uems <mathew.chen@edgentauems.com.tw>
Co-authored-by: mbatini <mbatini@gmail.com>
Co-authored-by: michal-zila <michal.zila@somi.sk>
Co-authored-by: michelvdriel <michel@vdriel.com>
Co-authored-by: mittkkoo <iankoff@yahoo.com>
Co-authored-by: mohammdhani <mohammdhani@gmail.com>
Co-authored-by: newsam202 <newsam202@naver.com>
Co-authored-by: notkrisu <damasiewiczkrystian@gmail.com>
Co-authored-by: payman Zafar <p.moghadam@asredanesh.com>
Co-authored-by: pedrom20 <pedrom20@gmail.com>
Co-authored-by: rok-poje <rok.poje@gmail.com>
Co-authored-by: salvolp <s.salvo17@gmail.com>
Co-authored-by: sashkashurik <sashkashurik@gmail.com>
Co-authored-by: sesoltani <se.soltani@hotmail.com>
Co-authored-by: sigurbjartur <github@sigurbjartur.com>
Co-authored-by: stomko11 <stefan.tomko@gmail.com>
Co-authored-by: tasagore <tasagore.seibei@gmail.com>
Co-authored-by: tekvsakdan <boris.ivanovic@tekvsakdan.si>
Co-authored-by: timnis <timo@nisula.org>
Co-authored-by: valentin2323 <sttor11@gmail.com>
Co-authored-by: vaxty <marek@ondrej.pro>
Co-authored-by: wenkuan01 <wenkuan@cicada-speech.com>
Co-authored-by: xXxLeVIaFaNxXx <sakovalev@polyplast-nm.ru>
Co-authored-by: yvanjay <181923630@qq.com>
Co-authored-by: Øivind Spro Heggland <oivheg@gmail.com>
Co-authored-by: Šimon Woidig <woidig.simon@gmail.com>
Co-authored-by: Андриан Галкин <galandrix@gmail.com>
Co-authored-by: Вячеслав Карпов <pvd86@mail.ru>
Co-authored-by: 周柏佑 <frank@sydt.com.tw>
Co-authored-by: 황윤성 <hys@osbusiness.net>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ar/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/az/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/bg/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/bn/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ca/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/cs/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/da/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/el/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/en_CA/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/en_GB/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/es/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/es_CA/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/es_CO/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/es_MX/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/et/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/fa/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/fi/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/fr_CA/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/he_IL/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/hi/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/hr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/id/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/is/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ja/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ko/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/lt/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/lv/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ms/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/nb_NO/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/nl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/pl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/pt_BR/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/pt_PT/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ro/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ru/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/rw/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sv/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/th/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/uk/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/vi/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/zh_Hans/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/zh_Hant/
Translation: Zammad/Zammad (development version)
2026-09-18 05:19:53 +00:00
Dusan Vuckovic
6598ed3690 Follow-up for 5f01c16a75 - Feature: Desktop view - Add/Edit/Remove a Knowledge Base Category.
Approved-by: Benjamin Scharf <bs@zammad.com>
Approved-by: Ivan Stamenkovic <is@zammad.com>
2026-09-17 16:44:55 +01:00
Marcel Bialas
c18867f7c8 Maintenance: Stale tooltip text remains after value is cleared
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-17 17:12:47 +02:00
Dominik Klein
f554c40ada Feature: Translate individual ticket articles.
Approved-by: Benjamin Scharf <bs@zammad.com>

Co-authored-by: Ben <bs@zammad.com>
2026-09-17 16:08:18 +02:00
Martin Edenhofer
8ccec554fa Maintenance: Fix publiccode.yml version, languages and links.
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Martin Edenhofer <me@edenhofer.de>
2026-09-17 15:01:47 +02:00
Marcel Bialas
0a6b5454eb Feature: Integrate DeepL as an additional backend translation service
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-09-17 14:42:05 +02:00
Benjamin Scharf
cb35c689d1 Maintenance: Desktop view - Improve subscriptions cleanup during logout
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-17 13:45:32 +02:00
Florian Liebe
6324aacef6 Maintenance: Extend and correct the Renovate configuration.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-17 13:37:26 +02:00
Benjamin Scharf
11ec201fdb Fixes #6367 - Knowledge Base next/previous buttons use original language.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-17 12:48:04 +02:00
Florian Liebe
ec915a3991 Maintenance: Resolve mocked GraphQL union fields by their declared type.
Approved-by: Dominik Klein <dk@zammad.com>

Co-authored-by: Ben <bs@zammad.com>
2026-09-17 11:41:54 +02:00
Benjamin Scharf
eab302d2ee Maintenance: Desktop view - Set z-index on sidebar menus
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-17 08:10:22 +02:00
Mantas Masalskis
556e1c8420 Fixes #6340 - Mail body silently falls back to CharDet when Encoding.find cannot resolve the declared charset label.
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-09-17 07:38:29 +03:00
Dusan Vuckovic
08df09e7c3 Maintenance: Increase the GraphQL maximum aliases count.
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-09-16 14:59:55 +01:00
Weblate
49186ec781 Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Azharul Haque <ahaque@limitxyz.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/bn/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sr_Cyrl/
Translation: Zammad/Zammad (development version)
2026-09-16 16:02:20 +03:00
Florian Liebe
ed740442a2 Maintenance: Document how to debug system tests with the Playwright driver.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-16 13:54:30 +02:00
Mantas Masalskis
76a2bec1f0 Follow-up 670a1e13fe - Fixes #6310 - Audit log: untranslated values in object type column
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-16 13:09:55 +03:00
Marcel Bialas
0291a23eef Feature: Integrate LibreTranslate as backend translation service
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-09-16 09:33:46 +02:00
Ralf Schmid
3e7d711b5f Feature: Desktop view - Show the knowledge base count tooltips on the whole count group.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-16 08:07:07 +02:00
renovatebot
01be3bf121 Maintenance: Update dependency cypress-real-events to ^1.15.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-16 07:51:49 +02:00
renovatebot
e1c8c1c4be Maintenance: Update pnpm to v11.27.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-16 07:51:34 +02:00
Dusan Vuckovic
925bea2505 Feature: Show a read-only package overview in Docker environments.
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-09-15 14:48:09 +01:00
Martin Gruner
7ac0d216e7 Maintenance: Activate the new Bangla translation
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-15 13:41:19 +02:00
Martin Gruner
858e6fcf04 Maintenance: Restore $VERBOSE after em-websocket monkey patches.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-15 12:10:52 +02:00
renovatebot
ba8af9e282 Maintenance: Lock file maintenance (Ruby)
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-15 12:01:32 +02:00
Mantas Masalskis
a2057dcf81 Fixes #6315 - Restrict deleting organizations via API to the admin.organization permission
Approved-by: Rene Reimann <rr@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-09-15 12:45:22 +03:00
Rolf Schmidt
e8902e14f1 Maintenance: Load the models before generating the GraphQL introspection to include dynamic schema parts.
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-15 10:22:23 +02:00
Weblate
e5072ccd46 Maintenance: Update translated files and catalog 2026-09-15 08:25:58 +01:00
Weblate
6a287c5e1a Maintenance: Translations update from translations.zammad.org.
Special thanks to the amazing translation team. You rock! 💪

Co-authored-by: Anael Mobilia <contrib@anael.eu>
Co-authored-by: Anntoo <jdoomm@gmail.com>
Co-authored-by: Atsushi Kojima <bm@exbridge.jp>
Co-authored-by: Azharul Haque <ahaque@limitxyz.com>
Co-authored-by: Balázs Úr <balazs@urbalazs.hu>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
Co-authored-by: Igor Costa <igor_sfx@yahoo.com.br>
Co-authored-by: Marcel Bialas <mtzl.29@proton.me>
Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Murat Duman <mduman@metu.edu.tr>
Co-authored-by: Ralf Schmid <rsc@zammad.com>
Co-authored-by: Random <random_r@users.noreply.translations.zammad.org>
Co-authored-by: Thomas Meijer <tvm@bizconnect.dk>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: albanobattistella <albanobattistella@gmail.com>
Co-authored-by: k-fujimaru <k.fujimaru@natsuhara.link>
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/bn/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/da/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/de/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/fr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/hu/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/it/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/ja/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/pt_BR/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/sr_Cyrl/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/tr/
Translate-URL: https://translations.zammad.org/projects/zammad/zammad-development/uk/
Translation: Zammad/Zammad (development version)
2026-09-15 07:22:40 +00:00
renovatebot
b482ba7463 Maintenance: Update dependency aws-sdk-s3 to v1.232.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-15 08:40:31 +02:00
renovatebot
7e950b7292 Maintenance: Update dependency mocha to ^12.0.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-15 08:40:07 +02:00
Ary Rabelo
02f3349010 Maintenance: Cleanup action job name misses job prefix on payload args
CleanupAction#job_name interpolates `payload_object.args.inspect` as a
bare identifier instead of `job.payload_object.args.inspect`. Lines 53
and 65 of the same private method already call `job.payload_object`
correctly, so this is a missed receiver, not intentional.

Since `payload_object` is not a local variable or method in this
context, Ruby raises NameError (not NoMethodError) as soon as a job's
payload responds to :args, which is the case for the legacy
Delayed::PerformableMethod payload used by delayed_job's non-ActiveJob
enqueue path.
2026-09-14 22:38:23 +02:00
Mantas Masalskis
f44c67a54e Follow up 49f2cd4540 - Fixes #6254 - Update expand and full endpoints to be equal regarding inline attachments.
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>

Co-authored-by: Rolf Schmidt <rolf.schmidt@zammad.com>
Co-authored-by: Mantas <mantas@idev.lt>
2026-09-14 23:09:07 +03:00
Dusan Vuckovic
203b01bd53 Fixes #6355 - SLA times longer than 41 days cannot be edited via GUI.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-14 16:07:56 +01:00
Marcel Bialas
8f6e723119 Maintenance: Mobile view - Long ticket number overlaps headline in mobile view
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-14 14:39:17 +02:00
Florian Liebe
f2d2f3c820 Maintenance: Drop packager.io support for Debian 11.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-14 12:18:23 +02:00
Philipp Reger
08f75cf405 Fixes #6357 - S/MIME decryption fails for forwarded emails when recipient is only present in Delivered-To. 2026-09-14 10:12:50 +01:00
renovatebot
66b3af0705 Maintenance: Update dependency rubocop to v1.91.0
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-14 10:36:04 +02:00
renovatebot
86bdef31b2 Maintenance: Update dependency selenium-webdriver to v4.49.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-14 10:11:57 +02:00
Dusan Vuckovic
5124de7d3e Follow-up f6114a6aca - Maintenance: Allow building container images with preinstalled addon packages.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-14 09:10:03 +01:00
Florian Liebe
71678141d5 Maintenance: Do not fetch artifacts in CI jobs that do not use them.
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-14 09:45:16 +02:00
Florian Liebe
646d6bafc1 Maintenance: Instruct AI agents to add code comments sparingly.
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-14 09:40:52 +02:00
renovatebot
a743fd43b6 Maintenance: Update dependency doorkeeper to v5.9.7
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-14 09:10:01 +02:00
Benjamin Scharf
965435f237 Fixes #6351 - Activity Type field displays with no valid options
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-14 08:32:07 +02:00
renovatebot
67e58fcc64 Maintenance: Update dependency net-imap to v0.6.7
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-14 08:26:07 +02:00
renovatebot
fd2bdd75d2 Maintenance: Update dependency vite to ^8.3.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-14 08:20:14 +02:00
renovatebot
218df29906 Maintenance: Update dependency twilio-ruby to v7.11.2
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-14 08:19:48 +02:00
Dominik Klein
d9c5ba05f3 Maintenance: Migrating an existing multiselect attribute raises an ArgumentError.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-11 22:07:14 +02:00
Mantas Masalskis
1c4bbbcb48 Maintenance: Move the test suite's ports clear of the development stack.
Approved-by: Dusan Vuckovic <dv@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-09-11 17:15:11 +03:00
Ary Rabelo
b3b3b78626 Maintenance: Remove unused OnlineNotificationPolicy#relation
The private method calls object_klass, which does not exist anywhere
in this class or its ancestor chain (ApplicationPolicy, PunditPolicy).
Every call raises NameError. It has no reachable caller: Pundit only
dispatches policy methods via public_send("#{action_name}?") from the
controller/GraphQL layer, or resolve on a Scope class, and this class
has no Scope. relation matches neither pattern.

Dead since it was introduced alongside without_relation_permission_field_scope
in commit b303d31f8 (2023-03-17); only the latter was ever wired up.
2026-09-11 13:32:52 +02:00
renovatebot
37bac7726f Maintenance: Update dependency oxlint to ^1.82.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-11 12:25:53 +02:00
Rene Reimann
5003c8d311 Maintenance: Fix api_client:php job by disabling PHPUnit coverage.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-11 11:42:48 +02:00
renovatebot
147ab7b060 Maintenance: Update dependency oxfmt to ^0.67.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-11 10:20:47 +02:00
renovatebot
bbd992bc55 Maintenance: Update Typescript/Types packages to ^8.70.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-11 10:20:36 +02:00
Marcel Bialas
dee9b72eb9 Feature: Add the admin UI for article translation feature configuration.
Approved-by: Dominik Klein <dk@zammad.com>
Approved-by: Marcel Bialas <mb@zammad.com>

Co-authored-by: Dominik Klein <dk@zammad.com>
2026-09-11 10:11:06 +02:00
renovatebot
01f5ad5717 Maintenance: Update Zammad dependencies via updatecli
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: updatecli-bot <updatecli-bot@updatecli.io>
2026-09-11 09:22:37 +02:00
renovatebot
90e59a98fd Maintenance: Update dependency eslint-plugin-oxlint to ^1.82.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-11 08:49:25 +02:00
Benjamin Scharf
7736d0365f Maintenance: Desktop view - Pasted multiline content is not inserted into the code block
Approved-by: Dusan Vuckovic <dv@zammad.com>
2026-09-11 08:14:26 +02:00
Martin Gruner
2a34cba995 Feature: Exclude the Elasticsearch host from proxy routing on new Docker installations.
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-11 07:23:52 +02:00
Dominik Klein
9e069ada35 Feature: AI-generated translations for articles.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-10 15:54:56 +02:00
Benjamin Scharf
6f382b08ec Maintenance: Remove unreachable ticket_id handling from ChecklistItemsController.
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-09-10 12:55:36 +02:00
Benjamin Scharf
0d86c485bf Fixes #6352 - Checked parameter ignored on POST checklist_items
Approved-by: Rolf Schmidt <rolf.schmidt@zammad.com>
2026-09-10 11:46:58 +02:00
Rolf Schmidt
f6114a6aca Maintenance: Allow building container images with preinstalled addon packages.
Approved-by: Martin Gruner <mg@zammad.com>

Co-authored-by: Martin Gruner <mg@zammad.com>
Co-authored-by: Dusan Vuckovic <dv@zammad.com>
2026-09-10 11:41:29 +02:00
Rene Reimann
ac93615eba Maintenance: Cache Composer and Bundler dependencies in the API client jobs
Approved-by: Martin Gruner <mg@zammad.com>
2026-09-10 10:22:41 +02:00
renovatebot
9b53dd616a Maintenance: Update dependency eslint-plugin-vue to ^10.11.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-10 08:56:10 +02:00
renovatebot
a1a82283f8 Maintenance: Update dependency vue-echarts to ^8.3.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-10 08:56:00 +02:00
renovatebot
303cceece9 Maintenance: Update Zammad dependencies via updatecli
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: updatecli-bot <updatecli-bot@updatecli.io>
2026-09-10 08:55:46 +02:00
renovatebot
70982e5163 Maintenance: Update pnpm to v11.26.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-10 07:46:03 +02:00
Dominik Klein
c2c8fb4c80 Fixes #6309 - Sorting by "Customer" column in detailed search doesn't work as expected.
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-09 20:31:39 +02:00
Ary Rabelo
84e4b27e8c Fix: Wrong number of arguments in FillDb.create_knowledge_base_answers
create_knowledge_base_answers calls create_knowledge_base_categories with
4 positional arguments, but that method takes a single params hash and
reads amount, knowledge_base_id, locale_id and sleep_time from it (see
its definition and the correct call at line 374). The mismatched call
raises ArgumentError: wrong number of arguments (given 4, expected 1).

Fix the call to pass a hash, mirroring the working call at line 374.
2026-09-09 12:19:47 +02:00
Benjamin Scharf
2ff8ac47e6 Fixes #4880 - Adding time accounting support when updating tickets inside the mobile view
Approved-by: Marcel Bialas <mb@zammad.com>
2026-09-09 11:22:34 +02:00
Vaibhav Srivastava
2655bb0f44 docs: fix typo strucutre -> structure
Signed-off-by: Vaibhav Srivastava <vaibhavsri1712@gmail.com>
2026-09-09 08:58:35 +01:00
Benjamin Scharf
8a8df58623 Maintenance: Desktop view - Detail search triggers network requests on every keystroke
Approved-by: Marcel Bialas <mb@zammad.com>
2026-09-09 09:55:36 +02:00
Mantas Masalskis
0d53457279 Feature: Desktop view - Re-organize knowledge base search.
Approved-by: Benjamin Scharf <bs@zammad.com>

Co-authored-by: Mantas <mantas@idev.lt>
2026-09-08 23:38:27 +03:00
Marcel Bialas
daa3ac83e6 Feature: Desktop view - Add a knowledge base answers tab to the global detailed search
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-08 16:33:07 +02:00
Benjamin Scharf
7e03016fe7 Feature: Desktop view - Limit quick search results to 5 per entity type when...
Approved-by: Marcel Bialas <mb@zammad.com>
2026-09-08 14:27:18 +02:00
Benjamin Scharf
1b3b73e0fb Maintenance: Remove truncation logging from KnowledgeBase search
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-08 12:45:04 +02:00
Florian Liebe
8f71883121 Maintenance: Taskbar#set_user reassigns ownership on every save, not only on create.
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-08 11:28:58 +02:00
renovatebot
b605a7f82c Maintenance: Update dependency eslint to ^10.10.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-08 08:42:01 +02:00
renovatebot
64e138c8ec Maintenance: Update dependency stylelint to ^17.15.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-08 08:41:15 +02:00
renovatebot
1192a9c49a Maintenance: Update Tiptap packages to v3.31.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-08 08:39:52 +02:00
Benjamin Scharf
0c22c51356 Feature: Desktop view - Show knowledge base answers in quicksearch results
Approved-by: Mantas Masalskis <mm@zammad.com>
2026-09-08 08:29:00 +02:00
renovatebot
669958389c Maintenance: Lock file maintenance (Ruby)
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 11:41:56 +02:00
Martin Gruner
3959bba0f0 Maintenance: Restructure CI job rules definitions
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 10:55:38 +02:00
renovatebot
8106ada2db Maintenance: Update dependency vue-router to ^5.3.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 10:15:50 +02:00
renovatebot
3551a84b8a Maintenance: Update dependency oxlint to ^1.81.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 09:53:35 +02:00
renovatebot
ad76b10694 Maintenance: Update dependency cypress to v16
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-07 09:52:45 +02:00
renovatebot
5659c1a686 Maintenance: Update Tiptap packages to v3.31.2
Approved-by: Florian Liebe <fl@zammad.com>

Co-authored-by: Florian Liebe <fl@zammad.com>
2026-09-07 09:01:28 +02:00
renovatebot
c21e0c1b3e Maintenance: Update dependency oxfmt to ^0.66.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 08:36:00 +02:00
renovatebot
4c421f0b2c Maintenance: Update dependency postcss to ^8.5.28
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 08:19:57 +02:00
renovatebot
ffa06c0516 Maintenance: Update dependency eslint-plugin-oxlint to ^1.81.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 07:37:06 +02:00
renovatebot
b69c301dec Maintenance: Update dependency simpleidn to v0.3.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-07 07:35:21 +02:00
Zammad Renovate Bot
0eaf215e15 Maintenance: Update dependency @testing-library/user-event to ^14.6.7 2026-09-06 01:10:42 +00:00
Dusan Vuckovic
7e0c22f72e Maintenance: Show warning alert when editing Zammad AI provider in SaaS environment. 2026-09-04 14:53:14 +01:00
Dusan Vuckovic
533acef6bd Fixes #6305 - CSRF token verification failure during login after updating to Zammad 7.1
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-04 14:07:30 +01:00
Dominik Klein
3e31091d7d Maintenance: Repair spec assertions that never ran or depend on the driver.
Approved-by: Marcel Bialas <mb@zammad.com>
2026-09-04 14:57:27 +02:00
Dominik Klein
35667d2599 Maintenance: Move CodeGraph setup instructions into the developer manual.
Approved-by: Benjamin Scharf <bs@zammad.com>
2026-09-04 12:55:48 +02:00
Dusan Vuckovic
d671bf22c1 Follow-up 61e0da447f - Feature: Desktop view - Follow-ups from the knowledge base answer review.
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-04 11:02:23 +01:00
Vladimir Sheremet
4e489121c5 Maintenance: Upgrade to Vitest 5 and make the frontend tests 4 times faster.
Vitest is updated to v5 and the pool is switched to `vmForks`, which cuts the
runtime of the frontend suite from ~250s to ~69s.

The new jsdom no longer allows `window.navigation` to be overridden, so the
tests that need it now go through a small `#shared/utils/navigation.ts`
indirection which can be mocked instead.

Co-authored-by: Vladimir Sheremet <vsheremet@cloudflare.com>
2026-09-04 11:37:22 +02:00
renovatebot
db07311818 Maintenance: Update Tiptap packages to ^3.30.6
Co-authored-by: Ben <bs@zammad.com>
2026-09-04 11:36:59 +02:00
Marcel Bialas
031a514395 Fixes #6294 - Mobile view: Knowledge base answer notifications lead to an error page.
Approved-by: Dominik Klein <dk@zammad.com>
2026-09-04 11:34:16 +02:00
Martin Gruner
96ca490c7f Maintenance: Add CI job guard for deployment jobs
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-04 11:29:30 +02:00
renovatebot
2f45c7b2f1 Maintenance: Update dependency omniauth-google-oauth2 to v1.2.3
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-04 10:59:53 +02:00
renovatebot
01124d9023 Maintenance: Update dependency mocha to v12
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-04 09:09:10 +02:00
renovatebot
8777c6c185 Maintenance: Update Typescript/Types packages to ^8.69.0
Approved-by: Florian Liebe <fl@zammad.com>
2026-09-04 09:08:57 +02:00
1645 changed files with 139681 additions and 37077 deletions

View File

@@ -1,2 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs.git#v365
https://github.com/heroku/heroku-buildpack-nodejs.git#v371
https://github.com/pkgr/heroku-buildpack-ruby.git#v361-1

View File

@@ -78,8 +78,8 @@ make a review look thorough is not.
Also out of scope in every review:
- Style and formatting a linter owns (RuboCop, ESLint, Prettier), unless it actively obscures
meaning or breaks a documented standard.
- Style and formatting a linter or formatter owns (RuboCop, ESLint, oxlint, Oxfmt, Stylelint),
unless it actively obscures meaning or breaks a documented standard.
- Naming preferences not covered by a convention doc.
- Speculative future requirements that are not in the issue.
- Pre-existing code visible in diff context but untouched by the change.
@@ -308,6 +308,19 @@ or translation quality for some users.
other `$t(...)` / `i18n.t(...)` calls or the base locale file. If a near-identical string
exists, flag the new one as a duplicate and suggest reusing it — every distinct string adds
translator workload across all supported languages.
- **Danger styling and confirmations** — `danger` and `remove` (which counts as danger) mark the
click that cannot be undone, so they stay only on controls that act immediately. Flag a `danger`
or `remove` trigger whose handler ends in `waitForConfirmation` / `waitForVariantConfirmation`,
or opens a flyout that asks first. What replaces it depends on the kind of trigger: a `danger`
button becomes `tertiary` with an icon naming the action (`trash3` for delete and discard,
`arrow-counterclockwise` for reset); a menu item carries no variant at all, icon included; a
`remove` icon button loses the variant, keeps its icon, and takes the variant UX decides per
place (`tertiary` so far), so do not assume one. Flag a confirmation for irrecoverable loss
(deleting records, resetting user settings, discarding unsaved changes, clearing filters or
recent lists) whose confirm button is not `danger`: the `delete` and `unsaved` variants of
`CommonConfirmationDialog` already render one, so only the default `confirm` variant needs
`buttonVariant: 'danger'`. A control that asks only in some states (a dirty taskbar tab) follows
the state.
- Never flag a change to `i18n/*.po` as the fix. Those files are managed externally.
### Backend — `[backend]`

View File

@@ -9,7 +9,7 @@ Resolvers are thin wrappers. Business logic lives in `app/services/service/`:
```ruby
def resolve(...)
Service::SomeAction.new(...).execute
Service::SomeAction.execute(...)
end
```

View File

@@ -64,5 +64,13 @@ Do not "fix" these without a decision:
inactive knowledge base that way — the policy for anyone with effective access
to the record (a reader included), the scope for the `knowledge_base.editor`
permission.
- `Gql::Concerns::SearchesKnowledgeBaseAnswers` answers with an **empty result**
instead of raising when no knowledge base is active. It is reached from a
document that asks for more than the knowledge base — the desktop quicksearch
requests every searchable entity in one query — and the search field is
`null: false`, so a raise would null the root and take the ticket, user and
organization groups down with it on every instance that has no knowledge base.
The knowledge base's own queries, `Gql::Queries::KnowledgeBase::Search`
included, do raise: they are asked for on their own.
Each of these carries the reasoning in a code comment; keep the two in sync.

View File

@@ -36,8 +36,8 @@ is not enough.
### Skeleton
Minimal concern following `app/models/mention/assets.rb` and
`app/models/audit_log/assets.rb` (belongs_to user + polymorphic relation):
Minimal concern following `app/models/mention/assets.rb` (belongs_to user +
polymorphic relation):
```ruby
class MyModel
@@ -67,7 +67,6 @@ end
### Reference examples
- `app/models/mention/assets.rb` — user + polymorphic `mentionable`
- `app/models/audit_log/assets.rb` — optional user + polymorphic `auditable`
- `app/models/online_notification/assets.rb` — lookup tables + created/updated by
- `app/models/group/assets.rb` — attribute filtering and per-user authorization

View File

@@ -170,13 +170,13 @@ existing code.
- **UI-6 (proposed)** — Should new UI be required to compose existing design-system components and
tokens, with a new primitive treated as a decision rather than a detail?
- **UI-7 (proposed)** — Should a resumable view offer its own taskbar-behaviour-on-update setting,
stored independently per view? Seen once, in
`zammad/coordination-desktop-view#785`; unclear whether it generalises.
stored independently per view? Seen once, in the story that introduced editing knowledge base
answers; unclear whether it generalises.
- **When is a new setting justified?** `DEFAULT-2` states the preference but not the test. There are
~300 settings today, so a usable criterion would have real leverage.
- **Are bulk or batch variants expected** for single-record actions, or added only on demand?
- **Where does deferred or scheduled state belong** in the interface? `#785` grew a whole scheduled
visibility capability, which suggests there is no settled pattern for it yet.
- **Where does deferred or scheduled state belong** in the interface? The same story grew a whole
scheduled visibility capability, which suggests there is no settled pattern for it yet.
- **How much of the legacy interface does a new feature have to reach**, and when is parity
explicitly not required?
- **How custom file overrides and packages are meant to work in a container**, given `DEPLOY-1`.

View File

@@ -146,7 +146,7 @@ A **layer slice** can never be a story. "Add the service and the mutation" has n
and nothing observable — forcing it into story form produces a fake user story, which is worse than
a large one. Layer slices are tasks. Split stories along capability and surface seams only.
### Worked example — `zammad/coordination-desktop-view#785`
### Worked example — editing knowledge base answers
"Edit an answer of the knowledge base", one story, sixteen tasks, one very large merge request.
Its criteria classified by the test above:

View File

@@ -40,6 +40,19 @@ Selenium opens a real, visible browser window.
SELENIUM_BROWSER_HEADLESS=1 RAILS_ENV=test VITE_TEST_MODE=1 bundle exec rspec spec/system/path/to/file_spec.rb
```
### Ports of a test run
The suite serves Capybara on `CAPYBARA_PORT` (default `3001`) and its websocket
server on `WS_PORT` (default `6043`) — next to, never on, the ports of the
development stack, so a run does not collide with a stack of the same checkout.
Set both to run two suites side by side; they still share the `test` database,
the Redis keys and the Elasticsearch indices, so those (`DATABASE_URL`,
`REDIS_URL`, `ES_INDEX`) need separating as well.
```bash
CAPYBARA_PORT=3101 WS_PORT=6143 bundle exec rspec spec/system/path/to/file_spec.rb
```
### Capybara form helpers (new frontend stack)
System tests for the Vue frontend use custom helpers to interact
@@ -79,11 +92,15 @@ Full reference:
Test files use `.spec.ts` extension and are co-located with source files.
```bash
VITE_TEST_MODE=1 pnpm test -- app/frontend/path/to/file.spec.ts
pnpm test --run app/frontend/path/to/file.spec.ts
```
Always pass `--run` and at least one file path. In an interactive terminal,
watch mode is the default and does not exit. Never put `--` between `pnpm test` and the arguments: pnpm
forwards it, Vitest then ignores everything after it and runs the complete
suite. `VITE_TEST_MODE` is not needed, Vitest enables the test mode itself.
Tests use **Testing Library** (on top of Vue Test Utils).
Watch mode is the default.
### Two levels of frontend tests

View File

@@ -40,7 +40,7 @@ finding, what is out of scope, how to classify findings, and how to write them.
Apply it rather than your own judgement about what matters, and do not restate it
here.
Two things from it deserve emphasis in this position specifically:
Three things from it deserve emphasis in this position specifically:
- **The flagging threshold.** You may be reviewing work in progress, where scaffolding
and half-finished edges are expected. Report what meets the bar and nothing else.

View File

@@ -39,6 +39,26 @@ The CoffeeScript frontend uses REST controllers.
- Never edit translation files (`i18n/*.po`) directly —
translations are managed via translations.zammad.org.
### Code Comments
Comment only what the code cannot say itself: a non-obvious _why_, a
workaround with its reason, a spec/protocol quirk, or a deliberate
deviation from a pattern. Otherwise add no comment — clear naming and
small methods are preferred over explanation.
- One or two short lines, in the style of the surrounding file.
- Never restate what the next line does, summarise a block, label
sections (`# --- setup ---`), or narrate a change (`# new`, `# was …`,
`# fixed`).
- Keep out reasoning that belongs in the commit message, the issue or
the MR description.
- Never reference an issue from an internal repository (`coordination-*`) —
this repository is publicly mirrored. Reference a public `zammad/zammad`
issue, or state the reason without a link.
- Never mark a fix for a security vulnerability as such — this repository
is publicly mirrored, and the comment points at the vulnerable code in
installations that are not updated yet.
### UI Principles
- Icon-only buttons need `v-tooltip` supplying the accessible name.
@@ -47,6 +67,13 @@ The CoffeeScript frontend uses REST controllers.
- Translatable strings: use complete, meaningful source strings,
sentence case for English source strings, and check for existing
near-duplicate strings before adding new ones.
- Red marks the point of no return: `danger` and `remove` stay only on
controls that act immediately. A trigger that only opens a confirmation
dialog or a confirming flyout is not red: a button becomes `tertiary`
with an icon naming the action (`trash3` for delete and discard,
`arrow-counterclockwise` for reset), a menu item carries no variant, a
`remove` icon button keeps its icon and takes the variant UX names per
place. The decisive button in the dialog or flyout is `danger`.
## Essential Commands
@@ -62,12 +89,38 @@ bundle exec rubocop --autocorrect app/path/to/file.rb # L
Always use pnpm for frontend and cross-stack commands.
```bash
pnpm test app/frontend/path/to/file.spec.ts # Run specific Vitest test
pnpm lint # Run all linters
pnpm generate-graphql-api # Regenerate GraphQL types after schema changes
pnpm generate-setting-types # Regenerate Config types after setting changes
pnpm test --run app/frontend/path/to/file.spec.ts # Run specific Vitest test (never add `--`)
pnpm lint # Run all linters
pnpm generate-graphql-api # Regenerate GraphQL types after schema changes
pnpm generate-setting-types # Regenerate Config types after setting changes
```
### Development stack
A browser-clickable instance for this checkout. Start it via `bin/dev` rather
than `forego` directly, and wait for it to answer before using it.
```bash
RAILS_ENV=development bundle exec rake db:create zammad:bootstrap:reset # once per checkout
bin/dev # does not exit; run it in the background
```
It serves on `localhost:3000`, with the websocket server on `6042` and Vite on
`3036`. Export `ZAMMAD_RAILS_PORT`, `ZAMMAD_WEBSOCKET_PORT` and
`VITE_RUBY_PORT` before `bin/dev` to move them — the whole stack has to agree
on them, so export, do not set them per process.
`zammad:bootstrap:reset` migrates, seeds and runs the auto wizard, which creates
`admin@example.com` / `test`. Plain `zammad:db:init` skips the wizard and leaves
the browser on the getting-started screen. Never run `zammad:bootstrap:init` or
`zammad:setup:db_config`: they overwrite `config/database.yml` and prompt on
stdin, which hangs a non-interactive session.
The test suite uses its own ports (`3001` and `6043`) and its own database, so
it can run while the stack is up. Several checkouts, however, share the
development database and Redis — only one stack per machine unless
`DATABASE_URL` and `REDIS_URL` say otherwise.
## Development Lifecycle
Issues are tracked on **GitHub**, code is developed on a self-hosted **GitLab**.
@@ -78,7 +131,7 @@ Stories are refined and broken down into task issues before this lifecycle
starts (`/refine-story` → `/plan-story`). Bugs skip that and enter it directly.
Lifecycle: Understand → Research → Plan → Branch (`/prepare-issue-branch`) →
Implement → Test → Commit → Review → MR (`/create-mr`) → Cherry-pick
Implement → Test → Review → Commit → MR (`/create-mr`) → Cherry-pick
(`/cherry-pick-to-stable`)
See `.dev/agent_docs/development_workflow.md` for the full lifecycle and
@@ -111,7 +164,7 @@ You MUST read the relevant file(s) below before responding when working on that
## CodeGraph
In repositories indexed by CodeGraph (a `.codegraph/` directory exists at the repo
root), reach for it BEFORE grep/find or reading files when you need to understand
root), reach for it before grep/find or reading files when you need to understand
or locate code:
- **MCP tool** (when available): `codegraph_explore` answers most code questions
@@ -120,14 +173,9 @@ or locate code:
in the query to read its current line-numbered source. If it's listed but
deferred, load it by name via tool search.
- **Shell** (always works): `pnpm exec codegraph explore "<symbol names or question>"`
prints the same output — `codegraph` is a local dev dependency, so the bare
command is not on `PATH`.
prints the same output using the repository-pinned CodeGraph version.
Setup is per developer: `pnpm codegraph:install` indexes this checkout and registers
the server with the agent harnesses it finds (Claude Code, Cursor, Codex CLI,
opencode, …). Say yes when it offers to put the CLI on `PATH`: that is how they
launch it. `pnpm codegraph:uninstall` reverts it. Nothing lands in a commit —
`.codegraph/` and `.mcp.json` are gitignored, and the harness registration lives in
each tool's own config outside the repo.
If there is no `.codegraph/` directory, skip CodeGraph entirely — indexing is the user's decision.
If there is no `.codegraph/` directory, skip CodeGraph entirely — indexing is the
user's decision. If they ask to initialize it, run `pnpm codegraph:init`. Developer
setup and removal are documented in
`doc/developer_manual/development_environment/codegraph-setup.md`.

View File

@@ -20,7 +20,14 @@ export default defineConfig({
downloadsFolder: '.dev/cypress/downloads',
screenshotsFolder: '.dev/cypress/screenshots',
videoCompression: false,
env: {
// Cypress 16 defaults to `0`, which types faster than the rich text editor can
// process input events.
keystrokeDelay: 10,
// Cypress 16 defaults to `modern`, which relies on `Element.checkVisibility()`
// and therefore reports elements moved out of the viewport via `transform` as
// visible.
visibilityStrategy: 'legacy',
expose: {
CY_CI: isCYCI,
pluginVisualRegressionDiffConfig: {
threshold: 0.02,

View File

@@ -2,10 +2,10 @@
"private": true,
"type": "module",
"devDependencies": {
"@frsource/cypress-plugin-visual-regression-diff": "^4.1.1",
"@frsource/cypress-plugin-visual-regression-diff": "^4.2.1",
"@testing-library/cypress": "^10.1.3",
"cypress": "^15.21.1",
"cypress-real-events": "^1.15.0"
"cypress": "^16.1.1",
"cypress-real-events": "^1.15.1"
},
"imports": {
"#tests/*": "../../app/frontend/tests/*",

View File

@@ -9,17 +9,17 @@ importers:
.:
devDependencies:
'@frsource/cypress-plugin-visual-regression-diff':
specifier: ^4.1.1
version: 4.1.1(cypress@15.21.1)
specifier: ^4.2.1
version: 4.2.1(cypress@16.1.1)
'@testing-library/cypress':
specifier: ^10.1.3
version: 10.1.3(cypress@15.21.1)
version: 10.1.3(cypress@16.1.1)
cypress:
specifier: ^15.21.1
version: 15.21.1
specifier: ^16.1.1
version: 16.1.1
cypress-real-events:
specifier: ^1.15.0
version: 1.15.0(cypress@15.21.1)
specifier: ^1.15.1
version: 1.15.1(cypress@16.1.1)
packages:
@@ -45,12 +45,12 @@ packages:
'@emnapi/runtime@1.11.3':
resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==}
'@frsource/base64@1.0.213':
resolution: {integrity: sha512-KaW0AbWO5gLtFJ+C9QZjY25P2dKkOFBtKtuyIj+DEA87moaO4cpovazCE8Zps8JP7cVyuZ+rDxkXlQTt8xli+Q==}
'@frsource/base64@1.0.237':
resolution: {integrity: sha512-X0tSa8rrusTEkYNy7aE+M+sZlsdyjMt8OgxWc6RerYefph3EKIVRTaL0qg8iw53iabvM7mJDK8MNc4W1yTAz2w==}
'@frsource/cypress-plugin-visual-regression-diff@4.1.1':
resolution: {integrity: sha512-O/RAf8shfs9UcwzPfR0TmrXnuy0UymjPNrbLhXFsls2bHsnWH++LskqXgkMdi9fnTA7E51XKqptUexNKOeEg7g==}
engines: {node: '>=14'}
'@frsource/cypress-plugin-visual-regression-diff@4.2.1':
resolution: {integrity: sha512-zu+Lfet2mydw+kkGMQ96gN6w8uejPsonKgmaybAMrS3HVXx4V28D961Q8f7ohs9hGa+v+njPJeGbTZuKpnLCkQ==}
engines: {node: '>=20.9.0'}
peerDependencies:
cypress: '>=13.0.0'
@@ -58,160 +58,160 @@ packages:
resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
engines: {node: '>=18'}
'@img/sharp-darwin-arm64@0.35.3':
resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==}
'@img/sharp-darwin-arm64@0.35.4':
resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [darwin]
'@img/sharp-darwin-x64@0.35.3':
resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==}
'@img/sharp-darwin-x64@0.35.4':
resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [darwin]
'@img/sharp-freebsd-wasm32@0.35.3':
resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==}
'@img/sharp-freebsd-wasm32@0.35.4':
resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==}
engines: {node: '>=20.9.0'}
os: [freebsd]
'@img/sharp-libvips-darwin-arm64@1.3.2':
resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==}
'@img/sharp-libvips-darwin-arm64@1.3.3':
resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==}
cpu: [arm64]
os: [darwin]
'@img/sharp-libvips-darwin-x64@1.3.2':
resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==}
'@img/sharp-libvips-darwin-x64@1.3.3':
resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==}
cpu: [x64]
os: [darwin]
'@img/sharp-libvips-linux-arm64@1.3.2':
resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==}
'@img/sharp-libvips-linux-arm64@1.3.3':
resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.3.2':
resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==}
'@img/sharp-libvips-linux-arm@1.3.3':
resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.3.2':
resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==}
'@img/sharp-libvips-linux-ppc64@1.3.3':
resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-riscv64@1.3.2':
resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==}
'@img/sharp-libvips-linux-riscv64@1.3.3':
resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.3.2':
resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==}
'@img/sharp-libvips-linux-s390x@1.3.3':
resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.3.2':
resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==}
'@img/sharp-libvips-linux-x64@1.3.3':
resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.3.2':
resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==}
'@img/sharp-libvips-linuxmusl-arm64@1.3.3':
resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.3.2':
resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==}
'@img/sharp-libvips-linuxmusl-x64@1.3.3':
resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.35.3':
resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==}
'@img/sharp-linux-arm64@0.35.4':
resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.35.3':
resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==}
'@img/sharp-linux-arm@0.35.4':
resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==}
engines: {node: '>=20.9.0'}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.35.3':
resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==}
'@img/sharp-linux-ppc64@0.35.4':
resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==}
engines: {node: '>=20.9.0'}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-riscv64@0.35.3':
resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==}
'@img/sharp-linux-riscv64@0.35.4':
resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==}
engines: {node: '>=20.9.0'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.35.3':
resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==}
'@img/sharp-linux-s390x@0.35.4':
resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==}
engines: {node: '>=20.9.0'}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.35.3':
resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==}
'@img/sharp-linux-x64@0.35.4':
resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.35.3':
resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==}
'@img/sharp-linuxmusl-arm64@0.35.4':
resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.35.3':
resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==}
'@img/sharp-linuxmusl-x64@0.35.4':
resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.35.3':
resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==}
'@img/sharp-wasm32@0.35.4':
resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==}
engines: {node: '>=20.9.0'}
'@img/sharp-webcontainers-wasm32@0.35.3':
resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==}
'@img/sharp-webcontainers-wasm32@0.35.4':
resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==}
engines: {node: '>=20.9.0'}
cpu: [wasm32]
'@img/sharp-win32-arm64@0.35.3':
resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==}
'@img/sharp-win32-arm64@0.35.4':
resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==}
engines: {node: '>=20.9.0'}
cpu: [arm64]
os: [win32]
'@img/sharp-win32-ia32@0.35.3':
resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==}
'@img/sharp-win32-ia32@0.35.4':
resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==}
engines: {node: ^20.9.0}
cpu: [ia32]
os: [win32]
'@img/sharp-win32-x64@0.35.3':
resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==}
'@img/sharp-win32-x64@0.35.4':
resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==}
engines: {node: '>=20.9.0'}
cpu: [x64]
os: [win32]
@@ -386,22 +386,22 @@ packages:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
engines: {node: '>= 8'}
cypress-real-events@1.15.0:
resolution: {integrity: sha512-in98xxTnnM9Z7lZBvvVozm99PBT2eEOjXRG5LKWyYvQnj9mGWXMiPNpfw7e7WiraBFh7XlXIxnE9Cu5o+52kQQ==}
cypress-real-events@1.15.1:
resolution: {integrity: sha512-BiTyqYcNHiIFWfSLRn3H+TgTvpnTGOKfl4Brz51yeSGKMxBbD60wtRQw31nII00yQ67Xg3C18mU8lqUxDN3OUA==}
peerDependencies:
cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x || ^14.x || ^15.x
cypress: ^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x || ^14.x || ^15.x || ^16.x
cypress@15.21.1:
resolution: {integrity: sha512-ogHpHMj0XNlZA5MGzjg8SWHf0eMw9lTwVQRKjpcT1GzNTxNUjwnHA8YCG6nOJ8ThU+XZaUxJgpMYZnJ//8aDaw==}
engines: {node: ^20.1.0 || ^22.0.0 || >=24.0.0}
cypress@16.1.1:
resolution: {integrity: sha512-T09qNpMKssD6eEMqqfywQ8OUub/VrNCvIUFAyQCfWsJ6o4CZ5Yeapce+WoZY9FXly6+3+pk7sjEB5voxduWQlQ==}
engines: {node: ^22.0.0 || ^24.0.0 || >=26.0.0}
hasBin: true
dashdash@1.14.1:
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
engines: {node: '>=0.10'}
dayjs@1.11.21:
resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
dayjs@1.11.23:
resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==}
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
@@ -490,7 +490,6 @@ packages:
extsprintf@1.3.0:
resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==}
engines: {'0': node >=0.6.0}
forever-agent@0.6.1:
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
@@ -625,7 +624,6 @@ packages:
jsprim@2.0.2:
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
engines: {'0': node >=0.6.0}
listr2@9.0.5:
resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==}
@@ -794,8 +792,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
sharp@0.35.3:
resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==}
sharp@0.35.4:
resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==}
engines: {node: '>=20.9.0'}
peerDependencies:
'@types/node': '*'
@@ -930,7 +928,6 @@ packages:
verror@1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
@@ -1004,132 +1001,132 @@ snapshots:
tslib: 2.8.1
optional: true
'@frsource/base64@1.0.213': {}
'@frsource/base64@1.0.237': {}
'@frsource/cypress-plugin-visual-regression-diff@4.1.1(cypress@15.21.1)':
'@frsource/cypress-plugin-visual-regression-diff@4.2.1(cypress@16.1.1)':
dependencies:
'@frsource/base64': 1.0.213
cypress: 15.21.1
'@frsource/base64': 1.0.237
cypress: 16.1.1
glob: 13.0.6
meta-png: 1.0.6
move-file: 4.1.0
pixelmatch: 7.2.0
pngjs: 7.0.0
sharp: 0.35.3
sharp: 0.35.4
transitivePeerDependencies:
- '@types/node'
'@img/colour@1.1.0': {}
'@img/sharp-darwin-arm64@0.35.3':
'@img/sharp-darwin-arm64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.3.2
'@img/sharp-libvips-darwin-arm64': 1.3.3
optional: true
'@img/sharp-darwin-x64@0.35.3':
'@img/sharp-darwin-x64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.3.2
'@img/sharp-libvips-darwin-x64': 1.3.3
optional: true
'@img/sharp-freebsd-wasm32@0.35.3':
'@img/sharp-freebsd-wasm32@0.35.4':
dependencies:
'@img/sharp-wasm32': 0.35.3
'@img/sharp-wasm32': 0.35.4
optional: true
'@img/sharp-libvips-darwin-arm64@1.3.2':
'@img/sharp-libvips-darwin-arm64@1.3.3':
optional: true
'@img/sharp-libvips-darwin-x64@1.3.2':
'@img/sharp-libvips-darwin-x64@1.3.3':
optional: true
'@img/sharp-libvips-linux-arm64@1.3.2':
'@img/sharp-libvips-linux-arm64@1.3.3':
optional: true
'@img/sharp-libvips-linux-arm@1.3.2':
'@img/sharp-libvips-linux-arm@1.3.3':
optional: true
'@img/sharp-libvips-linux-ppc64@1.3.2':
'@img/sharp-libvips-linux-ppc64@1.3.3':
optional: true
'@img/sharp-libvips-linux-riscv64@1.3.2':
'@img/sharp-libvips-linux-riscv64@1.3.3':
optional: true
'@img/sharp-libvips-linux-s390x@1.3.2':
'@img/sharp-libvips-linux-s390x@1.3.3':
optional: true
'@img/sharp-libvips-linux-x64@1.3.2':
'@img/sharp-libvips-linux-x64@1.3.3':
optional: true
'@img/sharp-libvips-linuxmusl-arm64@1.3.2':
'@img/sharp-libvips-linuxmusl-arm64@1.3.3':
optional: true
'@img/sharp-libvips-linuxmusl-x64@1.3.2':
'@img/sharp-libvips-linuxmusl-x64@1.3.3':
optional: true
'@img/sharp-linux-arm64@0.35.3':
'@img/sharp-linux-arm64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.3.2
'@img/sharp-libvips-linux-arm64': 1.3.3
optional: true
'@img/sharp-linux-arm@0.35.3':
'@img/sharp-linux-arm@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.3.2
'@img/sharp-libvips-linux-arm': 1.3.3
optional: true
'@img/sharp-linux-ppc64@0.35.3':
'@img/sharp-linux-ppc64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-ppc64': 1.3.2
'@img/sharp-libvips-linux-ppc64': 1.3.3
optional: true
'@img/sharp-linux-riscv64@0.35.3':
'@img/sharp-linux-riscv64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-riscv64': 1.3.2
'@img/sharp-libvips-linux-riscv64': 1.3.3
optional: true
'@img/sharp-linux-s390x@0.35.3':
'@img/sharp-linux-s390x@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.3.2
'@img/sharp-libvips-linux-s390x': 1.3.3
optional: true
'@img/sharp-linux-x64@0.35.3':
'@img/sharp-linux-x64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.3.2
'@img/sharp-libvips-linux-x64': 1.3.3
optional: true
'@img/sharp-linuxmusl-arm64@0.35.3':
'@img/sharp-linuxmusl-arm64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.3.2
'@img/sharp-libvips-linuxmusl-arm64': 1.3.3
optional: true
'@img/sharp-linuxmusl-x64@0.35.3':
'@img/sharp-linuxmusl-x64@0.35.4':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.3.2
'@img/sharp-libvips-linuxmusl-x64': 1.3.3
optional: true
'@img/sharp-wasm32@0.35.3':
'@img/sharp-wasm32@0.35.4':
dependencies:
'@emnapi/runtime': 1.11.3
optional: true
'@img/sharp-webcontainers-wasm32@0.35.3':
'@img/sharp-webcontainers-wasm32@0.35.4':
dependencies:
'@img/sharp-wasm32': 0.35.3
'@img/sharp-wasm32': 0.35.4
optional: true
'@img/sharp-win32-arm64@0.35.3':
'@img/sharp-win32-arm64@0.35.4':
optional: true
'@img/sharp-win32-ia32@0.35.3':
'@img/sharp-win32-ia32@0.35.4':
optional: true
'@img/sharp-win32-x64@0.35.3':
'@img/sharp-win32-x64@0.35.4':
optional: true
'@testing-library/cypress@10.1.3(cypress@15.21.1)':
'@testing-library/cypress@10.1.3(cypress@16.1.1)':
dependencies:
'@babel/runtime': 7.29.7
'@testing-library/dom': 10.4.1
cypress: 15.21.1
cypress: 16.1.1
'@testing-library/dom@10.4.1':
dependencies:
@@ -1280,11 +1277,11 @@ snapshots:
shebang-command: 2.0.0
which: 2.0.2
cypress-real-events@1.15.0(cypress@15.21.1):
cypress-real-events@1.15.1(cypress@16.1.1):
dependencies:
cypress: 15.21.1
cypress: 16.1.1
cypress@15.21.1:
cypress@16.1.1:
dependencies:
'@cypress/request': 4.0.1
'@cypress/xvfb': 1.2.4(supports-color@8.1.1)
@@ -1302,7 +1299,7 @@ snapshots:
cli-table3: 0.6.1
commander: 6.2.1
common-tags: 1.8.2
dayjs: 1.11.21
dayjs: 1.11.23
debug: 4.4.3(supports-color@8.1.1)
eventemitter2: 6.4.7
execa: 4.1.0
@@ -1333,7 +1330,7 @@ snapshots:
dependencies:
assert-plus: 1.0.0
dayjs@1.11.21: {}
dayjs@1.11.23: {}
debug@3.2.7(supports-color@8.1.1):
dependencies:
@@ -1689,37 +1686,37 @@ snapshots:
semver@7.8.5: {}
sharp@0.35.3:
sharp@0.35.4:
dependencies:
'@img/colour': 1.1.0
detect-libc: 2.1.2
semver: 7.8.5
optionalDependencies:
'@img/sharp-darwin-arm64': 0.35.3
'@img/sharp-darwin-x64': 0.35.3
'@img/sharp-freebsd-wasm32': 0.35.3
'@img/sharp-libvips-darwin-arm64': 1.3.2
'@img/sharp-libvips-darwin-x64': 1.3.2
'@img/sharp-libvips-linux-arm': 1.3.2
'@img/sharp-libvips-linux-arm64': 1.3.2
'@img/sharp-libvips-linux-ppc64': 1.3.2
'@img/sharp-libvips-linux-riscv64': 1.3.2
'@img/sharp-libvips-linux-s390x': 1.3.2
'@img/sharp-libvips-linux-x64': 1.3.2
'@img/sharp-libvips-linuxmusl-arm64': 1.3.2
'@img/sharp-libvips-linuxmusl-x64': 1.3.2
'@img/sharp-linux-arm': 0.35.3
'@img/sharp-linux-arm64': 0.35.3
'@img/sharp-linux-ppc64': 0.35.3
'@img/sharp-linux-riscv64': 0.35.3
'@img/sharp-linux-s390x': 0.35.3
'@img/sharp-linux-x64': 0.35.3
'@img/sharp-linuxmusl-arm64': 0.35.3
'@img/sharp-linuxmusl-x64': 0.35.3
'@img/sharp-webcontainers-wasm32': 0.35.3
'@img/sharp-win32-arm64': 0.35.3
'@img/sharp-win32-ia32': 0.35.3
'@img/sharp-win32-x64': 0.35.3
'@img/sharp-darwin-arm64': 0.35.4
'@img/sharp-darwin-x64': 0.35.4
'@img/sharp-freebsd-wasm32': 0.35.4
'@img/sharp-libvips-darwin-arm64': 1.3.3
'@img/sharp-libvips-darwin-x64': 1.3.3
'@img/sharp-libvips-linux-arm': 1.3.3
'@img/sharp-libvips-linux-arm64': 1.3.3
'@img/sharp-libvips-linux-ppc64': 1.3.3
'@img/sharp-libvips-linux-riscv64': 1.3.3
'@img/sharp-libvips-linux-s390x': 1.3.3
'@img/sharp-libvips-linux-x64': 1.3.3
'@img/sharp-libvips-linuxmusl-arm64': 1.3.3
'@img/sharp-libvips-linuxmusl-x64': 1.3.3
'@img/sharp-linux-arm': 0.35.4
'@img/sharp-linux-arm64': 0.35.4
'@img/sharp-linux-ppc64': 0.35.4
'@img/sharp-linux-riscv64': 0.35.4
'@img/sharp-linux-s390x': 0.35.4
'@img/sharp-linux-x64': 0.35.4
'@img/sharp-linuxmusl-arm64': 0.35.4
'@img/sharp-linuxmusl-x64': 0.35.4
'@img/sharp-webcontainers-wasm32': 0.35.4
'@img/sharp-win32-arm64': 0.35.4
'@img/sharp-win32-ia32': 0.35.4
'@img/sharp-win32-x64': 0.35.4
shebang-command@2.0.0:
dependencies:

View File

@@ -16,7 +16,7 @@ globalThis.process.env = {
Cypress.Screenshot.defaults({ capture: 'viewport' })
if (Cypress.env('CY_CI')) {
if (Cypress.expose('CY_CI')) {
Cypress.config('defaultCommandTimeout', 20000)
}

View File

@@ -4,4 +4,4 @@ corepack enable pnpm
pnpm install --frozen-lockfile --ignore-scripts
pnpm cypress:install
pnpm test:ci:ct --env pluginVisualRegressionUpdateImages=$CYPRESS_UPDATE_SNAPSHOTS --spec '../../**/*-visuals.cy.*'
pnpm test:ci:ct --expose pluginVisualRegressionUpdateImages=$CYPRESS_UPDATE_SNAPSHOTS --spec '../../**/*-visuals.cy.*'

View File

@@ -32,7 +32,7 @@ services:
command: sleep infinity
elasticsearch:
image: elasticsearch:9.3.2
image: elasticsearch:9.5.3
environment:
- "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
- discovery.type=single-node
@@ -56,7 +56,7 @@ services:
memcached:
command: memcached -m 256M
image: memcached:1.6.38-alpine
image: memcached:1.6.45-alpine
restart: unless-stopped
postgresql:

View File

@@ -13,9 +13,9 @@ pnpm config set store-dir "$(pwd)/.pnpm-store" --global
bin/setup --skip-server
echo "== Install CodeGraph index =="
echo "== Initialize CodeGraph index =="
pnpm run codegraph:install
pnpm run codegraph:init
echo "== Precompile Ruby cache =="

View File

@@ -13,9 +13,9 @@ pnpm config set store-dir "$(pwd)/.pnpm-store" --global
bin/setup --skip-server
echo "== Install CodeGraph index =="
echo "== Initialize CodeGraph index =="
pnpm run codegraph:install
pnpm run codegraph:init
echo "== Precompile Ruby cache =="

View File

@@ -13,9 +13,9 @@ pnpm config set store-dir "$(pwd)/.pnpm-store" --global
bin/setup --skip-server
echo "== Install CodeGraph index =="
echo "== Initialize CodeGraph index =="
pnpm run codegraph:install
pnpm run codegraph:init
echo "== Precompile Ruby cache =="

View File

@@ -13,9 +13,9 @@ pnpm config set store-dir "$(pwd)/.pnpm-store" --global
bin/setup --skip-server
echo "== Install CodeGraph index =="
echo "== Initialize CodeGraph index =="
pnpm run codegraph:install
pnpm run codegraph:init
echo "== Precompile Ruby cache =="

View File

@@ -17,10 +17,10 @@
"requireindex": "^1.2.0"
},
"devDependencies": {
"eslint": "^10.9.1",
"eslint": "^10.12.0",
"eslint-plugin-eslint-plugin": "^7.6.2",
"eslint-plugin-n": "^18.3.0",
"mocha": "^11.8.0",
"eslint-plugin-n": "^18.4.0",
"mocha": "^12.0.3",
"vue-eslint-parser": "^10.4.1"
},
"engines": {

View File

@@ -13,23 +13,29 @@ importers:
version: 1.2.0
devDependencies:
eslint:
specifier: ^10.9.1
version: 10.9.1(supports-color@8.1.1)
specifier: ^10.12.0
version: 10.12.0(supports-color@8.1.1)
eslint-plugin-eslint-plugin:
specifier: ^7.6.2
version: 7.6.2(eslint@10.9.1(supports-color@8.1.1))
version: 7.6.2(eslint@10.12.0(supports-color@8.1.1))
eslint-plugin-n:
specifier: ^18.3.0
version: 18.3.0(eslint@10.9.1(supports-color@8.1.1))
specifier: ^18.4.0
version: 18.4.0(eslint@10.12.0(supports-color@8.1.1))
mocha:
specifier: ^11.8.0
version: 11.8.0
specifier: ^12.0.3
version: 12.0.3
vue-eslint-parser:
specifier: ^10.4.1
version: 10.4.1(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1)
version: 10.4.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1)
packages:
'@cacheable/memory@2.2.0':
resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==}
'@cacheable/utils@2.5.0':
resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==}
'@eslint-community/eslint-utils@4.10.1':
resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -56,8 +62,8 @@ packages:
resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@eslint/plugin-kit@0.7.2':
resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
'@eslint/plugin-kit@0.7.3':
resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@humanfs/core@0.19.2':
@@ -80,13 +86,14 @@ packages:
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@keyv/bigmap@1.3.1':
resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==}
engines: {node: '>= 18'}
peerDependencies:
keyv: ^5.6.0
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@keyv/serialize@1.1.1':
resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
'@types/esrecurse@4.3.1':
resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
@@ -110,35 +117,13 @@ packages:
ajv@6.15.0:
resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
ansi-regex@6.3.0:
resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==}
engines: {node: '>=12'}
ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
ansi-styles@6.2.3:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
balanced-match@4.0.4:
resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
engines: {node: 18 || 20 || >=22}
brace-expansion@2.1.4:
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
brace-expansion@5.0.9:
resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
engines: {node: 20 || >=22}
@@ -146,28 +131,12 @@ packages:
browser-stdout@1.3.1:
resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
camelcase@6.3.0:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'}
cacheable@2.5.0:
resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
chokidar@5.0.0:
resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
engines: {node: '>= 20.19.0'}
cross-spawn@7.0.6:
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
@@ -182,34 +151,17 @@ packages:
supports-color:
optional: true
decamelize@4.0.0:
resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
engines: {node: '>=10'}
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
diff@7.0.0:
resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==}
diff@9.0.0:
resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==}
engines: {node: '>=0.3.1'}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
enhanced-resolve@5.24.5:
resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==}
engines: {node: '>=10.13.0'}
escalade@3.2.0:
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
engines: {node: '>=6'}
escape-string-regexp@4.0.0:
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
engines: {node: '>=10'}
@@ -232,8 +184,8 @@ packages:
peerDependencies:
eslint: '>=9.0.0'
eslint-plugin-n@18.3.0:
resolution: {integrity: sha512-cPVguuDe6DrIPb/qUXHf8P89MaVTUmiYWwpt5gX5AILsvRIiZAxMFXcFR6QHYBksqKJpjfUBlL/RleCJUWcD7w==}
eslint-plugin-n@18.4.0:
resolution: {integrity: sha512-TEm6Nqn9+l7EWfJXOL6pmrEdpiM2ZTgo4fNMRDvBTM70jeQ7x3X3Akcx+sRCIRfpcSVwMypH3ZAA33PLyNKmYw==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
peerDependencies:
eslint: '>=8.57.1'
@@ -257,8 +209,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
eslint@10.9.1:
resolution: {integrity: sha512-9VaAkDURekixUQJy0oJYl2DcN6oKMfxay7XzaGYAWQwsb6qfKf+x76R2k1L8kb1boc+FyCAaTA9GmiKaaiaF+A==}
eslint@10.12.0:
resolution: {integrity: sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
hasBin: true
peerDependencies:
@@ -296,33 +248,23 @@ packages:
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
file-entry-cache@11.1.5:
resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==}
find-up-simple@1.0.1:
resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==}
engines: {node: '>=18'}
find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
flat-cache@4.0.1:
resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
engines: {node: '>=16'}
flat@5.0.2:
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
hasBin: true
flat-cache@6.1.23:
resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==}
flatted@3.4.4:
resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
foreground-child@3.3.1:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
get-tsconfig@4.14.2:
resolution: {integrity: sha512-XpwZALwwl/BaKTAyC6+c5T8y6kCg2jk+XGqOVrKIQmW49pNypYLMRjCUXqa28tQgJlhS2RlzP7sc+Rx7W6qsfw==}
@@ -330,10 +272,9 @@ packages:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
glob@10.5.0:
resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@13.0.6:
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
engines: {node: 18 || 20 || >=22}
globals@15.15.0:
resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
@@ -349,9 +290,15 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
hashery@1.5.1:
resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==}
engines: {node: '>=20'}
hookified@1.15.1:
resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==}
hookified@2.2.0:
resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==}
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
@@ -365,21 +312,13 @@ packages:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-path-inside@3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
is-plain-obj@2.1.0:
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
engines: {node: '>=8'}
is-path-inside@4.0.0:
resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==}
engines: {node: '>=12'}
is-unicode-supported@0.1.0:
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
@@ -388,24 +327,18 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
js-yaml@4.3.1:
resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==}
js-yaml@5.4.1:
resolution: {integrity: sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==}
hasBin: true
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
keyv@5.6.0:
resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==}
levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
@@ -415,28 +348,21 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
log-symbols@4.1.0:
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
engines: {node: '>=10'}
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@11.5.2:
resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==}
engines: {node: 20 || >=22}
minimatch@10.2.6:
resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==}
engines: {node: 18 || 20 || >=22}
minimatch@9.0.9:
resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
engines: {node: '>=16 || 14 >=14.17'}
minipass@7.1.3:
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
engines: {node: '>=16 || 14 >=14.17'}
mocha@11.8.0:
resolution: {integrity: sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
mocha@12.0.3:
resolution: {integrity: sha512-beammieDdiWdkjANWeWPpkxUROYGi5FN88fPOkLiticz7HTg3Y+Pho3EYE6SUdcuWnZ3h2WD8EXCA55JF4o6wg==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
ms@2.1.3:
@@ -457,9 +383,6 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -468,9 +391,9 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
path-scurry@2.0.2:
resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==}
engines: {node: 18 || 20 || >=22}
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -483,16 +406,13 @@ packages:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
qified@0.10.1:
resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==}
engines: {node: '>=20'}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
readdirp@5.1.1:
resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==}
engines: {node: '>= 20.19.0'}
requireindex@1.2.0:
resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==}
@@ -501,16 +421,14 @@ packages:
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
semver@7.8.5:
resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==}
engines: {node: '>=10'}
hasBin: true
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
serialize-javascript@7.1.1:
resolution: {integrity: sha512-k3CMsaIvvdSwm8oLB4MXSl0wH2/cwlH7xGcnRd2DaeRmBkbzYmyT8j0tsX60DwD1eRwHTpNpH8ljKu9oUT1MeQ==}
engines: {node: '>=20.0.0'}
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
@@ -520,33 +438,9 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
string-width@5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
strip-ansi@7.2.0:
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
engines: {node: '>=12'}
strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
strip-json-comments@5.0.3:
resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==}
engines: {node: '>=14.16'}
supports-color@8.1.1:
resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
@@ -578,32 +472,8 @@ packages:
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
engines: {node: '>=0.10.0'}
workerpool@9.3.4:
resolution: {integrity: sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==}
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
wrap-ansi@8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
yargs-unparser@2.0.0:
resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
engines: {node: '>=10'}
yargs@17.7.3:
resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
engines: {node: '>=12'}
workerpool@10.0.3:
resolution: {integrity: sha512-6z2Iis68Wqth93/G/wJP9u+R3O+d2XTlgWChGCwuT1qLbBsOYueGRZuJ++v3mtDP5KjYdy+WzvWC+VWETSVXJA==}
yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
@@ -611,9 +481,21 @@ packages:
snapshots:
'@eslint-community/eslint-utils@4.10.1(eslint@10.9.1(supports-color@8.1.1))':
'@cacheable/memory@2.2.0':
dependencies:
eslint: 10.9.1(supports-color@8.1.1)
'@cacheable/utils': 2.5.0
'@keyv/bigmap': 1.3.1(keyv@5.6.0)
hookified: 1.15.1
keyv: 5.6.0
'@cacheable/utils@2.5.0':
dependencies:
hashery: 1.5.1
keyv: 5.6.0
'@eslint-community/eslint-utils@4.10.1(eslint@10.12.0(supports-color@8.1.1))':
dependencies:
eslint: 10.12.0(supports-color@8.1.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -636,7 +518,7 @@ snapshots:
'@eslint/object-schema@3.0.5': {}
'@eslint/plugin-kit@0.7.2':
'@eslint/plugin-kit@0.7.3':
dependencies:
'@eslint/core': 1.2.1
levn: 0.4.1
@@ -657,17 +539,13 @@ snapshots:
'@humanwhocodes/retry@0.4.3': {}
'@isaacs/cliui@8.0.2':
'@keyv/bigmap@1.3.1(keyv@5.6.0)':
dependencies:
string-width: 5.1.2
string-width-cjs: string-width@4.2.3
strip-ansi: 7.2.0
strip-ansi-cjs: strip-ansi@6.0.1
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
hashery: 1.5.1
hookified: 1.15.1
keyv: 5.6.0
'@pkgjs/parseargs@0.11.0':
optional: true
'@keyv/serialize@1.1.1': {}
'@types/esrecurse@4.3.1': {}
@@ -688,54 +566,27 @@ snapshots:
json-schema-traverse: 0.4.1
uri-js: 4.4.1
ansi-regex@5.0.1: {}
ansi-regex@6.3.0: {}
ansi-styles@4.3.0:
dependencies:
color-convert: 2.0.1
ansi-styles@6.2.3: {}
argparse@2.0.1: {}
balanced-match@1.0.2: {}
balanced-match@4.0.4: {}
brace-expansion@2.1.4:
dependencies:
balanced-match: 1.0.2
brace-expansion@5.0.9:
dependencies:
balanced-match: 4.0.4
browser-stdout@1.3.1: {}
camelcase@6.3.0: {}
chalk@4.1.2:
cacheable@2.5.0:
dependencies:
ansi-styles: 4.3.0
supports-color: 7.2.0
'@cacheable/memory': 2.2.0
'@cacheable/utils': 2.5.0
hookified: 1.15.1
keyv: 5.6.0
qified: 0.10.1
chokidar@4.0.3:
chokidar@5.0.0:
dependencies:
readdirp: 4.1.2
cliui@8.0.1:
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
color-name@1.1.4: {}
readdirp: 5.1.1
cross-spawn@7.0.6:
dependencies:
@@ -749,55 +600,46 @@ snapshots:
optionalDependencies:
supports-color: 8.1.1
decamelize@4.0.0: {}
deep-is@0.1.4: {}
diff@7.0.0: {}
eastasianwidth@0.2.0: {}
emoji-regex@8.0.0: {}
emoji-regex@9.2.2: {}
diff@9.0.0: {}
enhanced-resolve@5.24.5:
dependencies:
graceful-fs: 4.2.11
tapable: 2.3.3
escalade@3.2.0: {}
escape-string-regexp@4.0.0: {}
eslint-compat-utils@0.5.1(eslint@10.9.1(supports-color@8.1.1)):
eslint-compat-utils@0.5.1(eslint@10.12.0(supports-color@8.1.1)):
dependencies:
eslint: 10.9.1(supports-color@8.1.1)
eslint: 10.12.0(supports-color@8.1.1)
semver: 7.8.5
eslint-plugin-es-x@7.8.0(eslint@10.9.1(supports-color@8.1.1)):
eslint-plugin-es-x@7.8.0(eslint@10.12.0(supports-color@8.1.1)):
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(supports-color@8.1.1))
'@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(supports-color@8.1.1))
'@eslint-community/regexpp': 4.12.2
eslint: 10.9.1(supports-color@8.1.1)
eslint-compat-utils: 0.5.1(eslint@10.9.1(supports-color@8.1.1))
eslint: 10.12.0(supports-color@8.1.1)
eslint-compat-utils: 0.5.1(eslint@10.12.0(supports-color@8.1.1))
eslint-plugin-eslint-plugin@7.6.2(eslint@10.9.1(supports-color@8.1.1)):
eslint-plugin-eslint-plugin@7.6.2(eslint@10.12.0(supports-color@8.1.1)):
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(supports-color@8.1.1))
eslint: 10.9.1(supports-color@8.1.1)
'@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(supports-color@8.1.1))
eslint: 10.12.0(supports-color@8.1.1)
estraverse: 5.3.0
eslint-plugin-n@18.3.0(eslint@10.9.1(supports-color@8.1.1)):
eslint-plugin-n@18.4.0(eslint@10.12.0(supports-color@8.1.1)):
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(supports-color@8.1.1))
'@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(supports-color@8.1.1))
enhanced-resolve: 5.24.5
eslint: 10.9.1(supports-color@8.1.1)
eslint-plugin-es-x: 7.8.0(eslint@10.9.1(supports-color@8.1.1))
eslint: 10.12.0(supports-color@8.1.1)
eslint-plugin-es-x: 7.8.0(eslint@10.12.0(supports-color@8.1.1))
get-tsconfig: 4.14.2
globals: 15.15.0
globrex: 0.1.2
ignore: 5.3.2
js-yaml: 5.4.1
semver: 7.8.5
eslint-scope@9.1.2:
@@ -811,14 +653,14 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
eslint@10.9.1(supports-color@8.1.1):
eslint@10.12.0(supports-color@8.1.1):
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@10.9.1(supports-color@8.1.1))
'@eslint-community/eslint-utils': 4.10.1(eslint@10.12.0(supports-color@8.1.1))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.5(supports-color@8.1.1)
'@eslint/config-helpers': 0.7.0
'@eslint/core': 1.2.1
'@eslint/plugin-kit': 0.7.2
'@eslint/plugin-kit': 0.7.3
'@humanfs/node': 0.16.8
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3
@@ -833,7 +675,7 @@ snapshots:
esquery: 1.7.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 8.0.0
file-entry-cache: 11.1.5
find-up: 5.0.0
glob-parent: 6.0.2
ignore: 5.3.2
@@ -870,31 +712,25 @@ snapshots:
fast-levenshtein@2.0.6: {}
file-entry-cache@8.0.0:
file-entry-cache@11.1.5:
dependencies:
flat-cache: 4.0.1
flat-cache: 6.1.23
find-up-simple@1.0.1: {}
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
path-exists: 4.0.0
flat-cache@4.0.1:
flat-cache@6.1.23:
dependencies:
cacheable: 2.5.0
flatted: 3.4.4
keyv: 4.5.4
flat@5.0.2: {}
hookified: 1.15.1
flatted@3.4.4: {}
foreground-child@3.3.1:
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
get-caller-file@2.0.5: {}
get-tsconfig@4.14.2:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -903,14 +739,11 @@ snapshots:
dependencies:
is-glob: 4.0.3
glob@10.5.0:
glob@13.0.6:
dependencies:
foreground-child: 3.3.1
jackspeak: 3.4.3
minimatch: 9.0.9
minimatch: 10.2.6
minipass: 7.1.3
package-json-from-dist: 1.0.1
path-scurry: 1.11.1
path-scurry: 2.0.2
globals@15.15.0: {}
@@ -920,7 +753,13 @@ snapshots:
has-flag@4.0.0: {}
he@1.2.0: {}
hashery@1.5.1:
dependencies:
hookified: 1.15.1
hookified@1.15.1: {}
hookified@2.2.0: {}
ignore@5.3.2: {}
@@ -928,39 +767,27 @@ snapshots:
is-extglob@2.1.1: {}
is-fullwidth-code-point@3.0.0: {}
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
is-path-inside@3.0.3: {}
is-plain-obj@2.1.0: {}
is-path-inside@4.0.0: {}
is-unicode-supported@0.1.0: {}
isexe@2.0.0: {}
jackspeak@3.4.3:
dependencies:
'@isaacs/cliui': 8.0.2
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
js-yaml@4.3.1:
js-yaml@5.4.1:
dependencies:
argparse: 2.0.1
json-buffer@3.0.1: {}
json-schema-traverse@0.4.1: {}
json-stable-stringify-without-jsonify@1.0.1: {}
keyv@4.5.4:
keyv@5.6.0:
dependencies:
json-buffer: 3.0.1
'@keyv/serialize': 1.1.1
levn@0.4.1:
dependencies:
@@ -971,46 +798,32 @@ snapshots:
dependencies:
p-locate: 5.0.0
log-symbols@4.1.0:
dependencies:
chalk: 4.1.2
is-unicode-supported: 0.1.0
lru-cache@10.4.3: {}
lru-cache@11.5.2: {}
minimatch@10.2.6:
dependencies:
brace-expansion: 5.0.9
minimatch@9.0.9:
dependencies:
brace-expansion: 2.1.4
minipass@7.1.3: {}
mocha@11.8.0:
mocha@12.0.3:
dependencies:
browser-stdout: 1.3.1
chokidar: 4.0.3
chokidar: 5.0.0
debug: 4.4.3(supports-color@8.1.1)
diff: 7.0.0
escape-string-regexp: 4.0.0
find-up: 5.0.0
glob: 10.5.0
he: 1.2.0
is-path-inside: 3.0.3
js-yaml: 4.3.1
log-symbols: 4.1.0
minimatch: 9.0.9
diff: 9.0.0
find-up-simple: 1.0.1
glob: 13.0.6
is-path-inside: 4.0.0
is-unicode-supported: 0.1.0
js-yaml: 5.4.1
minimatch: 10.2.6
ms: 2.1.3
picocolors: 1.1.1
serialize-javascript: 6.0.2
strip-json-comments: 3.1.1
serialize-javascript: 7.1.1
strip-json-comments: 5.0.3
supports-color: 8.1.1
workerpool: 9.3.4
yargs: 17.7.3
yargs-parser: 21.1.1
yargs-unparser: 2.0.0
workerpool: 10.0.3
ms@2.1.3: {}
@@ -1033,15 +846,13 @@ snapshots:
dependencies:
p-limit: 3.1.0
package-json-from-dist@1.0.1: {}
path-exists@4.0.0: {}
path-key@3.1.1: {}
path-scurry@1.11.1:
path-scurry@2.0.2:
dependencies:
lru-cache: 10.4.3
lru-cache: 11.5.2
minipass: 7.1.3
picocolors@1.1.1: {}
@@ -1050,25 +861,19 @@ snapshots:
punycode@2.3.1: {}
randombytes@2.1.0:
qified@0.10.1:
dependencies:
safe-buffer: 5.2.1
hookified: 2.2.0
readdirp@4.1.2: {}
require-directory@2.1.1: {}
readdirp@5.1.1: {}
requireindex@1.2.0: {}
resolve-pkg-maps@1.0.0: {}
safe-buffer@5.2.1: {}
semver@7.8.5: {}
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
serialize-javascript@7.1.1: {}
shebang-command@2.0.0:
dependencies:
@@ -1076,33 +881,7 @@ snapshots:
shebang-regex@3.0.0: {}
signal-exit@4.1.0: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
string-width@5.1.2:
dependencies:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.2.0
strip-ansi@6.0.1:
dependencies:
ansi-regex: 5.0.1
strip-ansi@7.2.0:
dependencies:
ansi-regex: 6.3.0
strip-json-comments@3.1.1: {}
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
strip-json-comments@5.0.3: {}
supports-color@8.1.1:
dependencies:
@@ -1118,10 +897,10 @@ snapshots:
dependencies:
punycode: 2.3.1
vue-eslint-parser@10.4.1(eslint@10.9.1(supports-color@8.1.1))(supports-color@8.1.1):
vue-eslint-parser@10.4.1(eslint@10.12.0(supports-color@8.1.1))(supports-color@8.1.1):
dependencies:
debug: 4.4.3(supports-color@8.1.1)
eslint: 10.9.1(supports-color@8.1.1)
eslint: 10.12.0(supports-color@8.1.1)
eslint-scope: 9.1.2
eslint-visitor-keys: 5.0.1
espree: 11.2.0
@@ -1136,39 +915,6 @@ snapshots:
word-wrap@1.2.5: {}
workerpool@9.3.4: {}
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi@8.1.0:
dependencies:
ansi-styles: 6.2.3
string-width: 5.1.2
strip-ansi: 7.2.0
y18n@5.0.8: {}
yargs-parser@21.1.1: {}
yargs-unparser@2.0.0:
dependencies:
camelcase: 6.3.0
decamelize: 4.0.0
flat: 5.0.2
is-plain-obj: 2.1.0
yargs@17.7.3:
dependencies:
cliui: 8.0.1
escalade: 3.2.0
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
workerpool@10.0.3: {}
yocto-queue@0.1.0: {}

View File

@@ -22,7 +22,6 @@ jobs:
fail-fast: false
matrix:
target:
- debian:11
- debian:12
- debian:13
- el:9

5
.gitignore vendored
View File

@@ -107,6 +107,7 @@
# Node
**/node_modules
/.vitest
yarn-error.log
.yarn/cache
/.pnpm-store
@@ -123,6 +124,8 @@ schema.graphql
# Packages
*.zpm
!.gitlab/docker/*.zpm
/packages/
# Devcontainer
/.devcontainer/with-mailserver/certs/*
@@ -139,6 +142,6 @@ schema.graphql
/.claude/skills
/.claude/settings.local.json
# Codegraph
# CodeGraph
/.codegraph/
/.mcp.json

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env ruby
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
# Ensures that all precompiled gems in Gemfile.lock are available for every platform
# Zammad is built for (packager.io, Docker, CI, development machines), so that none of them
# falls back to compiling its native extension from source.
#
# .gitlab/check_native_gem_platforms.rb [path/to/Gemfile.lock]
require 'bundler'
class CheckNativeGemPlatforms
# `ruby` is the fallback for all unlisted platforms. Bundler silently drops it when a new gem
# is added to the Gemfile, which would break installations on those platforms.
REQUIRED_PLATFORMS = %w[ruby x86_64-linux-gnu aarch64-linux-gnu x86_64-darwin arm64-darwin].freeze
# An update can leave only the source variant of a gem in the lockfile, e.g. when its precompiled
# gems are not published yet. Listing the gems keeps them checked even then.
PRECOMPILED_GEMS = %w[ffi google-protobuf nokogiri pg sass-embedded].freeze
def self.run(lockfile_path)
new(lockfile_path).run
end
def initialize(lockfile_path)
@lockfile = Bundler::LockfileParser.new(File.read(lockfile_path))
@errors = []
end
def run
puts 'Checking native gem platforms in Gemfile.lock…'
check_required_platforms
check_precompiled_gems
if @errors.any?
puts(@errors.map { |error| " #{error}" })
exit 1
end
puts " #{@lockfile.platforms.size} platforms present, #{precompiled_gems.size} precompiled gems (#{precompiled_gems.keys.sort.join(', ')}) available for all #{binary_platforms.size} binary platforms."
puts 'done.'
end
private
def check_required_platforms
platforms = @lockfile.platforms.map(&:to_s)
(REQUIRED_PLATFORMS - platforms).each do |platform|
@errors << "Platform '#{platform}' is missing, restore it with: bundle lock --add-platform #{platform}"
end
end
def check_precompiled_gems
precompiled_gems.each do |name, variants|
# This script runs without ActiveSupport, so Array#exclude? is not available.
@errors << "#{name} is a precompiled gem, add it to PRECOMPILED_GEMS in #{__FILE__}." if !PRECOMPILED_GEMS.include?(name) # rubocop:disable Rails/NegateInclude
binary_platforms.each do |platform|
next if variants.any? { |spec| binary?(spec) && Gem::Platform.new(spec.platform) === platform } # rubocop:disable Style/CaseEquality
@errors << "#{name} #{variants.first.version} has no precompiled gem for #{platform}#{' and would need a Rust toolchain' if rust?(variants)}."
end
end
end
# A gem counts as precompiled if it is listed, or ships at least one platform variant. Rust-based
# gems count even without either, their source variant is recognizable by its rb_sys dependency.
def precompiled_gems
@precompiled_gems ||= @lockfile.specs.group_by(&:name).select do |name, variants|
PRECOMPILED_GEMS.include?(name) || variants.any? { |spec| binary?(spec) } || rust?(variants)
end
end
def rust?(variants)
variants.any? { |spec| !binary?(spec) && spec.dependencies.any? { |dependency| dependency.name == 'rb_sys' } }
end
def binary?(spec)
spec.platform != Gem::Platform::RUBY
end
def binary_platforms
@binary_platforms ||= @lockfile.platforms.reject { |platform| platform == Gem::Platform::RUBY }
end
end
CheckNativeGemPlatforms.run(ARGV[0] || File.expand_path('../Gemfile.lock', __dir__))

View File

@@ -0,0 +1,5 @@
# Certain jobs use no artifacts of the earlier stages. Without this, GitLab downloads all of
# them, and retrying such a job after the database dumps expired fails it with
# `missing_dependency_failure`.
.no_artifact_dependencies:
dependencies: []

View File

@@ -4,9 +4,6 @@ default:
tags:
- docker
# Most jobs execute ruby commands, so take that as default.
cache: !reference [.cache, read_only_ruby]
# Artifacts are stored for failed jobs
artifacts:
expire_in: 1 week
@@ -18,16 +15,20 @@ default:
interruptible: true
# Initialize application env
before_script:
- !reference [.scripts, source_rvm]
- !reference [.scripts, bundle_install]
- !reference [.scripts, configure_environment]
after_script:
- .gitlab/environment_info.sh > log/environment_info.log
# Copy logs from other containers to store them as artifacts.
- cp /builds/*.log log/ || true
# Print recorded test runtimes for script/build/update_test_timings.rb. The script is
# missing if an older commit is checked out, e.g. in rspec:migration.
- if [ -x .gitlab/print_test_timings.sh ]; then .gitlab/print_test_timings.sh; fi
# Logs and screenshots are only needed for failed jobs, see .artifacts_with_test_report.
- if [ "$CI_JOB_STATUS" = success ]; then rm -rf log/*.log tmp/screenshot* tmp/playwright-traces; fi
# For jobs running the application's Ruby code: read the gem cache and initialize the
# application environment.
.template_ruby:
cache: !reference [.cache, read_only_ruby]
before_script:
- !reference [.scripts, source_rvm]
- !reference [.scripts, bundle_install]
- !reference [.scripts, configure_environment]

View File

@@ -0,0 +1,48 @@
# Building blocks for `needs:`, combined per job:
#
# needs:
# - !reference [.needs, ruby]
# - !reference [.needs, lint]
#
# A job with `needs:` starts as soon as the listed jobs have finished, instead of waiting
# for all jobs of the earlier stages, and downloads the artifacts of the listed jobs only.
#
# Jobs of the test and browser stages need `lint`, so that none of them is started for a
# commit that fails linting. The manual lint jobs are not listed, a job needing them
# would wait for them forever.
#
# Jobs with rules of their own are optional, because filtered pipelines ($JOB_FILTER,
# $BROWSER_TEST_FILTER) do not contain them, and needing a job that is missing from the
# pipeline fails its creation. Without 'write database dumps', .gitlab/init_database.sh
# initializes the database from scratch.
.needs:
ruby:
- job: 'write cache: ruby'
artifacts: false
nodejs:
- job: 'write cache: nodejs'
artifacts: false
database:
- job: 'write database dumps'
optional: true
assets:
- job: 'assets:build'
lint:
- job: 'lint: shellcheck'
artifacts: false
optional: true
- job: 'lint: i18n & rails'
artifacts: false
optional: true
- job: 'lint: ruby & security'
artifacts: false
optional: true
- job: 'lint: markdown, coffee & css'
artifacts: false
optional: true
- job: 'lint: js'
artifacts: false
optional: true
- job: 'lint: secret_detection'
artifacts: false
optional: true

View File

@@ -1,5 +1,65 @@
.job_rules:
# Allow filtering CI pipelines with JOB_FILTER and BROWSER_TEST_FILTER push variabales.
# Rules decide whether a job is added to a pipeline at all, and if so, whether it
# starts automatically, waits for a manual start, and may fail. Entries are
# evaluated from top to bottom, the first matching one wins.
#
# This file provides two kinds of building blocks, which must not be confused:
#
# 1. Fragments below `.rule_fragments` - *partial* rules lists, spliced into the
# list of a job with `!reference`:
#
# rules:
# - !reference [.rule_fragments, main_repository_only]
# - !reference [.rule_fragments, variable_filters]
# - if: $SOMETHING
# when: on_success
# - !reference [.rule_fragments, then_manual]
#
# Fragments have no `rules:` key of their own, so using one in `extends:` splices
# its keys into the job and GitLab rejects it - never use them there.
#
# 2. Rule sets named `.job_rules_*` - a *complete* rules list, used with
# `extends:`. There is deliberately only one of them, see below.
#
# Conventions:
#
# * A `.job_rules_*` set must end in an unconditional entry whose `when` is not
# `never`, so that extending it always yields a job that runs somewhere.
# Anything that only ever excludes (like `main_repository_only`), or that ends
# in `when: never`, has to be a fragment. This is the mistake to worry about:
# unlike a fragment in `extends:`, which GitLab rejects, a set that can only
# exclude is accepted without error or warning and the job is simply never
# created in any pipeline.
# * A job's own `rules:` replaces every inherited list rather than merging with
# it. Never add a rule set to a job or template that already inherits one -
# the reader cannot see which of the two wins. Write the deviating list at the
# job as `rules:` instead.
# * `then_*` fragments contain the closing, unconditional entry of a list and
# therefore appear last and exactly once. Every other fragment contributes
# only conditional entries and can be placed anywhere before that.
# * Order within a list: guards (`main_repository_only`) first, then the pipeline
# filters (`variable_filters`), then the job's own conditions, then the closing
# entry. A guard placed further down is dead code.
# * Spell out `when:` in every entry, including `when: on_success`. An entry
# without it reads as if it would not start the job.
# * `$JOB_FILTER` is matched against `$CI_JOB_NAME`, which for `parallel` and
# `matrix` jobs contains the shard suffix (e.g. `rspec 1/8`), so those jobs can
# only be selected with their full name.
#
# Every job needs rules of its own to honour $JOB_FILTER: `default:` cannot carry
# `rules:`, and a job without any `rules:` runs unconditionally. The few jobs that
# want exactly that - because filtered pipelines still need their cache or their
# artifacts - say so in a comment.
.rule_fragments:
# Never run in forks, e.g. because project specific secrets or runners are needed.
main_repository_only:
- if: $CI_PROJECT_PATH != "zammad/zammad"
when: never
# Honour the JOB_FILTER and BROWSER_TEST_FILTER push variables, which reduce a
# pipeline to a single job (see workflow.yml). A matching JOB_FILTER *forces*
# the job to run, whatever its own conditions say.
variable_filters:
- if: $BROWSER_TEST_FILTER
when: never
@@ -8,22 +68,51 @@
- if: $JOB_FILTER && $JOB_FILTER != $CI_JOB_NAME
when: never
.job_rules_default:
rules:
- !reference [.job_rules, variable_filters]
- when: on_success
# Like variable_filters, but without forcing: JOB_FILTER can only *select* the
# job, its own conditions still have to match as well. For jobs that must not
# become startable outside of the pipelines they were written for.
variable_filters_without_force:
- if: $BROWSER_TEST_FILTER
when: never
- if: $JOB_FILTER && $JOB_FILTER != $CI_JOB_NAME
when: never
.job_rules_manual_in_mr:
rules:
- !reference [.job_rules, variable_filters]
# Closing entry: offer a manual start and never block the pipeline. `allow_failure`
# has to be spelled out for manual jobs - in `rules:` it defaults to `false`,
# which would turn the manual job into a blocking one.
then_manual:
- when: manual
allow_failure: true
# Closing entry for jobs whose signal is wanted regularly, but which are too slow
# or too dependent on third parties to gate every commit: they run in the
# scheduled pipelines and can be started manually everywhere else.
then_manual_unless_scheduled:
- if: $CI_PIPELINE_SOURCE == "schedule"
when: on_success
- when: manual
allow_failure: true
# Closing entry for secondary test lanes that cover the same specs as a primary
# lane (e.g. Firefox next to Chrome): in merge requests they are advisory and
# only started on demand, on branches they run automatically.
then_manual_in_mr:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
allow_failure: true
- when: on_success
.job_rules_integration:
# Closing entry for the capybara:filtered:* jobs, which exist only to loop a
# single spec selected with BROWSER_TEST_FILTER. JOB_FILTER is deliberately not
# honoured here, BROWSER_TEST_FILTER is the selector for these jobs.
then_browser_test_filter_only:
- if: $BROWSER_TEST_FILTER
when: always
- when: never
# The one and only rule set: for jobs that just have to honour the pipeline filters.
# Everything else writes its own `rules:`, see the header.
.job_rules_default:
rules:
- !reference [.job_rules, variable_filters]
- if: $CI_COMMIT_REF_NAME =~ /^private/
allow_failure: true
- !reference [.rule_fragments, variable_filters]
- when: on_success

View File

@@ -32,7 +32,12 @@
# gem and the package.json pin; the driver asserts this at startup
# (spec/support/capybara/playwright_driver.rb), so a gem bump fails fast
# here instead of at the server's version handshake.
PLAYWRIGHT_VERSION: '1.62.1'
PLAYWRIGHT_VERSION: '1.63.0'
.variables_playwright_firefox:
extends: .variables_playwright
variables:
SELENIUM_BROWSER: 'playwright_firefox'
.variables_proxy:
variables:
@@ -84,14 +89,13 @@
alias: selenium-firefox
playwright:
name: $CI_REGISTRY/docker/zammad-playwright:stable
name: $CI_REGISTRY/docker/zammad-playwright:latest
alias: playwright
variables:
# Lets the server accept the Chromium args the driver passes via the
# endpoint's launch-options (see playwright_driver.rb); it strips them
# otherwise. Opt-in because it also unlocks executablePath - fine here,
# as the server is only reachable from this job. See
# docker/zammad-playwright!2.
# as the server is only reachable from this job.
PLAYWRIGHT_UNSAFE: '1'
redis:

View File

@@ -0,0 +1,16 @@
# For the jobs writing JUnit reports (see CI_JUNIT_REPORT_DIR): upload the artifacts of
# passed jobs, too, so that the test reports of pipelines and merge requests contain
# all tests and not only the failed ones. The default after_script removes the logs and
# screenshots of passed jobs, so that only the report of these is uploaded.
.artifacts_with_test_report:
artifacts:
expire_in: 1 week
when: always
paths:
- tmp/screenshot*
- tmp/screenshots/*
# Recorded by spec/support/capybara/playwright_traces.rb in the Playwright lanes.
- tmp/playwright-traces/*
- log/*.log
reports:
junit: tmp/junit/*.xml

View File

@@ -29,6 +29,16 @@ variables:
# Record the runtime of each test file, used to keep .gitlab/ci/timings/ up to date.
CI_TEST_TIMINGS_PATH: 'tmp/test_timings/timings.yml'
# Write JUnit reports for the test reports of GitLab, see spec/support/junit_report.rb and
# .artifacts_with_test_report.
CI_JUNIT_REPORT_DIR: 'tmp/junit'
# Branches that are only ever built through their merge request: feature branches of
# developers (private/*) and branches created by automation (cherry-picks, reverts,
# dependency updates). Used in rules with `=~`, which requires the pattern to be
# enclosed in forward slashes.
CI_ZAMMAD_MR_ONLY_BRANCH_PATTERN: '/^(private|cherry-pick-|revert-|renovate|dependabot)/'
# Compile assets only once.
CI_SKIP_ASSETS_PRECOMPILE: 'true'
# Avoid unnecessary DB resets.

View File

@@ -9,6 +9,6 @@ workflow:
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"
when: never
# Don't create branch pipelines for pushes to private branches (as there will also be a MR for them).
- if: $CI_COMMIT_BRANCH =~ /^(private|cherry-pick-|revert-|renovate|dependabot)/ && $CI_PIPELINE_SOURCE == "push"
- if: $CI_COMMIT_BRANCH =~ $CI_ZAMMAD_MR_ONLY_BRANCH_PATTERN && $CI_PIPELINE_SOURCE == "push"
when: never
- when: always

View File

@@ -3,10 +3,12 @@
image:
name: alpine/curl:latest
entrypoint: ['']
cache: []
rules:
- !reference [.rule_fragments, main_repository_only]
# Don't apply job filter here.
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /weblate-merge$/ && $CI_MERGE_REQUEST_SQUASH_ON_MERGE == "true"'
before_script: []
when: on_success
- when: never
script:
- echo "⚠️ Disabling squash option for this MR..."
- |
@@ -20,6 +22,7 @@
'push translations update commit':
stage: automate
rules:
- !reference [.rule_fragments, main_repository_only]
# Don't apply job filter here.
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_MESSAGE =~ /Translations update from translations[.]zammad[.]org/
when: always
@@ -76,11 +79,12 @@
# GITLAB_API_TOKEN - read_api token for the MR query (defaults to RENOVATE_TOKEN)
'dependency digest':
stage: automate
cache: []
rules:
- if: $BROWSER_TEST_FILTER
when: never
- !reference [.rule_fragments, main_repository_only]
- !reference [.rule_fragments, variable_filters_without_force]
# Runs only when explicitly selected via JOB_FILTER, see the comment above.
- if: $JOB_FILTER == $CI_JOB_NAME && ($CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web") && $CI_COMMIT_REF_NAME == "develop"
when: on_success
- when: never
variables:
GIT_DEPTH: '500'
@@ -101,15 +105,12 @@
image:
name: ghcr.io/updatecli/updatecli:latest
entrypoint: ['']
cache: []
rules:
- if: $BROWSER_TEST_FILTER
when: never
- if: $JOB_FILTER && $JOB_FILTER != $CI_JOB_NAME
when: never
- !reference [.rule_fragments, main_repository_only]
- !reference [.rule_fragments, variable_filters_without_force]
- if: ($CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web") && $CI_COMMIT_REF_NAME == "develop"
when: on_success
- when: never
before_script: []
script:
- updatecli pipeline apply --config .dev/updatecli.yaml --values .dev/updatecli-values-enable-gitlab.yaml
after_script: []

View File

@@ -1,8 +1,16 @@
.template_browser-core_capybara:
stage: browser
extends:
- .template_ruby
- .job_rules_default
- .tags_docker_low_concurrency
- .artifacts_with_test_report
needs:
- !reference [.needs, ruby]
- !reference [.needs, database]
- !reference [.needs, assets]
- !reference [.needs, lint]
timeout: 40 minutes
script:
- !reference [.scripts, zammad_ci_test_prepare]
- bundle exec rspec --require ./spec/rspec_extensions.rb --fail-fast -t ~integration -t ~searchindex spec/system
@@ -19,8 +27,11 @@
.template_browser-core_capybara_firefox:
extends:
- .template_browser-core_capybara
- .job_rules_manual_in_mr
- .variables_selenium_firefox
# Chrome covers the same specs, so the Firefox lane is advisory in merge requests.
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
services:
- !reference [.services, postgresql]
- !reference [.services, selenium-firefox]
@@ -34,13 +45,12 @@
- !reference [.services, postgresql]
- !reference [.services, playwright]
- !reference [.services, redis]
# A job-level artifacts block replaces the global default one entirely -
# repeat its paths and add the traces recorded by playwright_traces.rb.
artifacts:
expire_in: 1 week
when: on_failure
paths:
- tmp/screenshot*
- tmp/screenshots/*
- tmp/playwright-traces/*
- log/*.log
.template_browser-core_capybara_playwright_firefox:
extends:
- .template_browser-core_capybara
- .variables_playwright_firefox
services:
- !reference [.services, postgresql]
- !reference [.services, playwright]
- !reference [.services, redis]

View File

@@ -8,11 +8,25 @@ capybara:firefox:
- .template_browser-core_capybara_firefox
parallel: 16
# Shadow lane for the Playwright driver evaluation: runs automatically in every
# pipeline next to capybara:chrome, so each commit yields a comparable pair,
# and never gates - allow_failure keeps a red lane from blocking the pipeline.
# Shadow lane for the Playwright driver evaluation: runs automatically on develop
# and in scheduled pipelines next to capybara:chrome, is only started on demand
# in merge requests, and never gates - allow_failure keeps a red lane from
# blocking the pipeline.
capybara:playwright:
extends:
- .template_browser-core_capybara_playwright
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
allow_failure: true
parallel: 16
# Evaluation lane for Playwright's Firefox, not decided yet whether it stays -
# only started on demand and never gates.
capybara:firefox:playwright:
extends:
- .template_browser-core_capybara_playwright_firefox
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual]
parallel: 16

View File

@@ -1,6 +1,7 @@
.template_browser-core_capybara_es:
extends:
- .template_browser-core_capybara
timeout: 30 minutes
script:
- !reference [.scripts, zammad_ci_test_prepare]
- bundle exec rspec --require ./spec/rspec_extensions.rb --fail-fast -t ~integration -t searchindex spec/system
@@ -19,6 +20,10 @@
extends:
- .template_browser-core_capybara_es
- .variables_selenium_firefox
# Like the non-ES Firefox lane: advisory in merge requests.
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
services:
- !reference [.services, postgresql]
- !reference [.services, elasticsearch]
@@ -34,16 +39,6 @@
- !reference [.services, elasticsearch]
- !reference [.services, playwright]
- !reference [.services, redis]
# A job-level artifacts block replaces the global default one entirely -
# repeat its paths and add the traces recorded by playwright_traces.rb.
artifacts:
expire_in: 1 week
when: on_failure
paths:
- tmp/screenshot*
- tmp/screenshots/*
- tmp/playwright-traces/*
- log/*.log
capybara:es:chrome:
extends:
@@ -52,7 +47,6 @@ capybara:es:chrome:
capybara:es:firefox:
extends:
- .template_browser-core_capybara_firefox_es
- .job_rules_manual_in_mr
# Shadow lane for the Playwright driver evaluation, twin of capybara:es:chrome:
# the searchindex-tagged system specs are excluded from the main capybara
@@ -60,4 +54,7 @@ capybara:es:firefox:
capybara:es:playwright:
extends:
- .template_browser-core_capybara_playwright_es
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
allow_failure: true

View File

@@ -1,8 +1,11 @@
.template_capybara_filtered:
# This template has to be listed *after* the browser template in `extends:`: its
# `rules:` and `script:` deliberately replace the inherited ones (lists are
# replaced, not merged).
rules:
- if: $BROWSER_TEST_FILTER
when: always
- when: never
- !reference [.rule_fragments, then_browser_test_filter_only]
# Looping a single spec takes much longer than a regular slice of the suite.
timeout: 2 hours
variables:
BROWSER_TEST_FILTER_LOOP_COUNT: 50
script:

View File

@@ -1,9 +1,14 @@
capybara:integration:other:
extends:
- .template_browser-core_capybara_chrome
- .job_rules_manual_in_mr
- .variables_idoit
- .variables_proxy
timeout: 20 minutes
# These tests need third party service containers and are slow, so they are
# advisory in merge requests.
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
services:
- !reference [.services, postgresql]
- !reference [.services, selenium-chrome]
@@ -18,8 +23,13 @@ capybara:integration:other:
capybara:integration:saml:
extends:
- .template_browser-core_capybara_chrome
- .job_rules_manual_in_mr
- .variables_auth
timeout: 20 minutes
# These tests need third party service containers and are slow, so they are
# advisory in merge requests.
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
services:
- !reference [.services, postgresql]
- !reference [.services, selenium-chrome]
@@ -32,7 +42,12 @@ capybara:integration:saml:
.template_capybara_integration_otrs:
extends:
- .template_browser-core_capybara_chrome
- .job_rules_manual_in_mr
timeout: 20 minutes
# These tests need third party service containers and are slow, so they are
# advisory in merge requests.
rules:
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_in_mr]
variables:
IMPORT_OTRS_ENDPOINT: "http://zammad-ci-otrsimport-app/otrs/public.pl?Action=ZammadMigrator"
services:

View File

@@ -5,7 +5,10 @@
'write database dumps':
stage: lint
extends:
- .template_ruby
- .job_rules_default
timeout: 6 minutes
needs: !reference [.needs, ruby]
services:
- !reference [.services, postgresql]
variables:

View File

@@ -3,16 +3,18 @@
stage: lint
extends:
- .job_rules_default
# Start right away: the caches written in the pre stage only change with the lock
# files, so they are usually filled already.
needs: []
.template_lint_rails:
extends:
- .template_lint
- .template_ruby
services:
- !reference [.services, postgresql]
before_script:
- !reference [.scripts, source_rvm]
- !reference [.scripts, bundle_install]
- !reference [.scripts, configure_environment]
- !reference [.template_ruby, before_script]
- !reference [.scripts, zammad_db_init]
variables:
ZAMMAD_SAFE_MODE: 1
@@ -21,16 +23,16 @@
'lint: shellcheck':
extends:
- .template_lint
timeout: 2 minutes
image: koalaman/shellcheck-alpine:stable
cache: []
before_script: []
script:
- shellcheck -S warning $(find . -name "*.sh" -o -name "functions" | egrep -v "/vendor|node_modules/")
- find . \( -name vendor -o -name node_modules \) -prune -o \( -name '*.sh' -o -name functions \) -print0 | xargs -0 shellcheck -S warning
after_script: []
'lint: i18n & rails':
extends:
- .template_lint_rails
timeout: 8 minutes
cache: !reference [.cache, read_only_full]
script:
- !reference [.scripts, pnpm_install]
@@ -47,6 +49,8 @@
'lint: ruby & security':
extends:
- .template_lint
- .template_ruby
timeout: 10 minutes
before_script:
- !reference [.scripts, source_rvm]
- !reference [.scripts, bundle_install]
@@ -63,8 +67,10 @@
- echo "bundler-audit security check…"
- gem exec bundle-audit update
- gem exec bundle-audit
- echo "Native gem platforms check…"
- .gitlab/check_native_gem_platforms.rb
- echo "Brakeman security check…"
- bundle exec brakeman -o /dev/stdout -o tmp/brakeman-report.html
- bin/brakeman -o /dev/stdout -o tmp/brakeman-report.html
- echo "Checking if yard can generate documentation…"
- bundle exec yard --no-output --no-progress
- echo "Verify that vendored gems are not world writable"
@@ -76,6 +82,7 @@
'lint: markdown, coffee & css':
extends:
- .template_lint
timeout: 5 minutes
cache: !reference [.cache, read_only_nodejs]
before_script:
- !reference [.scripts, pnpm_install]
@@ -90,6 +97,7 @@
'lint: js':
extends:
- .template_lint
timeout: 10 minutes
cache:
- !reference [.cache, read_only_nodejs]
- !reference [.cache, read_write_eslint]
@@ -116,22 +124,21 @@
'lint: orphaned ruby gems':
extends:
- .template_lint_rails
timeout: 2 minutes
allow_failure: true
rules:
- !reference [.job_rules, variable_filters]
- when: manual
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual]
script:
- bundle exec rake zammad:ci:bundle:orphaned 6
'update CI variables':
extends:
- .template_lint_rails
timeout: 4 minutes
rules:
- !reference [.job_rules, variable_filters]
- if: $CI_PIPELINE_SOURCE != "schedule"
when: manual
allow_failure: true
- when: on_success
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_unless_scheduled]
script:
- bundle exec rake zammad:ci:update_ci_variables
@@ -140,13 +147,21 @@
image:
name: 'zricethezav/gitleaks:latest'
entrypoint: ['']
cache: []
before_script: []
timeout: 2 minutes
variables:
# Go uses all CPUs by default.
GOMAXPROCS: 1
script:
# Since we clone with GIT_DEPTH=1, the commit has the entire codebase as a diff.
# Otherwise, we'd need to use --no-git to scan the entire codebase, but that is slower
# as it also traverses directories not scanned by git.
- gitleaks detect --report-path secret-detection-report.json --verbose
# Deep enough to contain the pipeline's base commit, so only the new commits are scanned.
# Setting GIT_DEPTH for this job has no effect, the checkout keeps the depth of 1.
- git fetch --quiet --deepen=99 origin "$CI_COMMIT_SHA"
# Scan only the commits the pipeline adds. Without a base commit in the checkout
# (scheduled pipelines, new branches, force pushes), scan only the current commit.
- BASE="${CI_MERGE_REQUEST_DIFF_BASE_SHA:-$CI_COMMIT_BEFORE_SHA}"
- git merge-base --is-ancestor "$BASE" HEAD 2>/dev/null || BASE='HEAD~1'
- set -o pipefail; gitleaks git --log-opts="${BASE}..HEAD" --report-path secret-detection-report.json --verbose --no-color 2>&1 | tee gitleaks.log
# gitleaks only logs errors of the git commands it runs and still exits successfully.
- if grep -qE '(^| )ERR ' gitleaks.log; then echo 'gitleaks reported errors, failing.'; exit 1; fi
after_script: []
# # GitLab can show this in a security widget, but that seems to be useless at this point (offers empty file for download).
# artifacts:

View File

@@ -1,4 +1,6 @@
# Write CI cache once for faster runs
# No `rules:` on purpose: filtered pipelines ($JOB_FILTER, $BROWSER_TEST_FILTER)
# need the cache, too.
'write cache: ruby':
stage: pre
cache: !reference [.cache, read_write_ruby]
@@ -8,20 +10,20 @@
- !reference [.scripts, bundle_install]
after_script: []
# No `rules:` on purpose, see above.
'write cache: nodejs':
stage: pre
cache: !reference [.cache, read_write_nodejs]
before_script: []
script:
- !reference [.scripts, pnpm_install_all]
after_script: []
'push to github':
stage: pre
cache: []
rules:
- !reference [.rule_fragments, main_repository_only]
# Don't apply job filter here.
- if: $CI_COMMIT_REF_NAME =~ /^(private|cherry-pick-|revert-|renovate|dependabot)/
- if: $CI_COMMIT_REF_NAME =~ $CI_ZAMMAD_MR_ONLY_BRANCH_PATTERN
when: never
- when: always
before_script:
@@ -39,8 +41,8 @@
stage: pre
tags:
- push-to-ftp
cache: []
rules:
- !reference [.rule_fragments, main_repository_only]
# Don't apply job filter here.
- if: $CI_COMMIT_TAG
when: always

View File

@@ -56,7 +56,7 @@
# Build/update docker image and publish it to the GitLab container registry.
- |
echo "Build docker image…"
docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
echo "$CI_JOB_TOKEN" | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY
echo -e "\\e[0Ksection_start:`date +%s`:docker_build[collapsed=true]\\r\\e[0Kdocker build"
if [ ! "${SKIP_DOCKER_BUILD}" ]
then
@@ -231,7 +231,6 @@
tags:
- review-apps
stage: pre
cache: []
variables:
SERVER_NAME: review-apps.dc.zammad.com
APP_SLUG: $CI_COMMIT_REF_SLUG
@@ -242,7 +241,10 @@
environment:
name: ${APP_NAME}
url: https://${APP_FQDN}#getting_started/auto_wizard
# Deploying, stopping and restarting the same review app must not overlap.
resource_group: ${APP_NAME}
rules:
- !reference [.rule_fragments, main_repository_only]
- if: $CI_MERGE_REQUEST_ID
when: manual
allow_failure: true

View File

@@ -1,8 +1,13 @@
.template_api_client:
stage: test
extends:
- .template_ruby
- .job_rules_default
- .tags_docker_low_concurrency
needs:
- !reference [.needs, ruby]
- !reference [.needs, lint]
timeout: 15 minutes
services:
- !reference [.services, postgresql]
- !reference [.services, redis]
@@ -15,10 +20,15 @@ api_client:php:
extends:
- .template_api_client
variables:
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.cache/composer"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL: "http://localhost:3000"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME: "admin@example.com"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD: "test"
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_DEBUG: 0
cache:
key: "composer-api-client-php"
paths:
- .cache/composer/
script:
- RAILS_ENV=test bundle exec rake db:create
- echo -e "\\e[0Ksection_start:`date +%s`:zammad_ci_test_start[collapsed=true]\\r\\e[0Kbundle exec rake zammad:ci:test:start"
@@ -30,11 +40,15 @@ api_client:php:
- ln -s /usr/local/bin/composer.phar /usr/local/bin/composer
- cd zammad-api-client-php
- composer install
- vendor/bin/phpunit
- vendor/bin/phpunit --no-coverage
api_client:ruby:
extends:
- .template_api_client
cache:
key: "bundle-api-client-ruby"
paths:
- .cache/bundle/
script:
- RAILS_ENV=test bundle exec rake db:create
- cp contrib/auto_wizard_test.json auto_wizard.json
@@ -43,6 +57,7 @@ api_client:ruby:
- echo -e "\\e[0Ksection_end:`date +%s`:zammad_ci_test_start\\r\\e[0K"
- git clone https://github.com/zammad/zammad-api-client-ruby.git
- cd zammad-api-client-ruby
- export BUNDLE_PATH="$CI_PROJECT_DIR/.cache/bundle"
- bundle update --bundler
- bundle install -j $(nproc)
- bundle exec rspec

View File

@@ -1,5 +1,16 @@
assets:build:
stage: test
needs:
- !reference [.needs, ruby]
- !reference [.needs, nodejs]
- !reference [.needs, lint]
timeout: 15 minutes
# No `rules:` on purpose: the browser jobs of a filtered pipeline ($JOB_FILTER,
# $BROWSER_TEST_FILTER) still need the assets built here, as the specs
# themselves skip the compilation ($CI_SKIP_ASSETS_PRECOMPILE).
extends:
- .template_ruby
- .no_artifact_dependencies
services:
- !reference [.services, postgresql]
artifacts:
@@ -28,7 +39,14 @@ assets:build:
assets:build:pwa:
stage: test
extends:
- .template_ruby
- .job_rules_default
- .no_artifact_dependencies
needs:
- !reference [.needs, ruby]
- !reference [.needs, nodejs]
- !reference [.needs, lint]
timeout: 15 minutes
services:
- !reference [.services, postgresql]
cache: !reference [.cache, read_only_full]

View File

@@ -6,11 +6,12 @@
# The job only needs the repository checkout. Without this, it implicitly
# depends on the artifacts of all earlier stages, so retrying it after those
# artifacts expired fails with `missing_dependency_failure`.
needs: []
needs:
- !reference [.needs, lint]
timeout: 15 minutes
variables:
DOCKER_HOST: unix:///var/run/docker.sock
DEVCONTAINERS_CLI_VERSION: '0.87.0'
cache: []
DEVCONTAINERS_CLI_VERSION: '0.89.0'
before_script:
- apk add --no-cache docker-cli docker-cli-buildx docker-compose
after_script: []
@@ -23,8 +24,5 @@
--config .devcontainer/$VARIANT/devcontainer.json
--frozen-lockfile
rules:
- !reference [.job_rules, variable_filters]
- if: $CI_PIPELINE_SOURCE == "schedule"
when: on_success
- when: manual
allow_failure: true
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual_unless_scheduled]

View File

@@ -1,48 +1,73 @@
docker:
.template_docker:
image: docker
stage: test
extends:
- .no_artifact_dependencies
needs:
- !reference [.needs, lint]
timeout: 30 minutes
interruptible: false
rules:
- !reference [.job_rules, variable_filters]
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_REF_NAME =~ /^(private|cherry-pick-|revert-|renovate|dependabot)/
- !reference [.rule_fragments, main_repository_only]
- !reference [.rule_fragments, variable_filters]
- if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_REF_NAME =~ $CI_ZAMMAD_MR_ONLY_BRANCH_PATTERN
when: manual
allow_failure: true
- when: on_success
services: []
cache: []
variables:
ZAMMAD_IMAGE_TAG: "ci-${CI_JOB_ID}-${CI_COMMIT_SHORT_SHA}"
ZAMMAD_IMAGE_REPO: "$CI_REGISTRY/zammad/zammad"
ZAMMAD_IMAGE_REPO: "zammad"
ZAMMAD_IMAGE: "${ZAMMAD_IMAGE_REPO}:${ZAMMAD_IMAGE_TAG}"
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build --build-arg COMMIT_SHA=${CI_COMMIT_SHORT_SHA} --pull -t $ZAMMAD_IMAGE .
- docker push $ZAMMAD_IMAGE
script: |
git clone --depth=1 https://github.com/zammad/zammad-docker-compose.git
cd zammad-docker-compose
- echo "$CI_JOB_TOKEN" | docker login -u gitlab-ci-token --password-stdin $CI_REGISTRY
- git clone --depth=1 https://github.com/zammad/zammad-docker-compose.git
# Make the stack use the image that was just built.
echo "IMAGE_REPO=${ZAMMAD_IMAGE_REPO}" > .env
echo "VERSION=${ZAMMAD_IMAGE_TAG}" >> .env
cat .env
- echo "IMAGE_REPO=${ZAMMAD_IMAGE_REPO}" > zammad-docker-compose/.env
- echo "VERSION=${ZAMMAD_IMAGE_TAG}" >> zammad-docker-compose/.env
- cat zammad-docker-compose/.env
# Use dynamic stack name and no exposed port to avoid conflicts.
cat - > docker-compose.override.yml <<COMPOSE_OVERRIDE_FILE
---
name: zammad-docker-ci-${ZAMMAD_IMAGE_TAG}
services:
zammad-nginx:
ports: !reset []
COMPOSE_OVERRIDE_FILE
.github/tests/setup/default.sh
DISABLE_EXTERNAL_TESTS=1 .github/tests/default.sh
docker compose ps
# Ensure we are running with the correct image
docker compose ps | grep ${ZAMMAD_IMAGE}
- |
cat - > zammad-docker-compose/docker-compose.override.yml <<COMPOSE_OVERRIDE_FILE
---
name: zammad-docker-ci-${ZAMMAD_IMAGE_TAG}
services:
zammad-nginx:
ports: !reset []
COMPOSE_OVERRIDE_FILE
after_script:
- cd zammad-docker-compose
- docker compose down --timeout 0 --volumes
- docker image rm ${ZAMMAD_IMAGE}
docker base tests:
extends: .template_docker
script:
- docker build --build-arg COMMIT_SHA=${CI_COMMIT_SHORT_SHA} --pull -t $ZAMMAD_IMAGE .
- cd zammad-docker-compose
- .github/tests/setup/default.sh
- DISABLE_EXTERNAL_TESTS=1 .github/tests/default.sh
- docker compose ps
# Ensure we are running with the correct image
- docker compose ps | grep ${ZAMMAD_IMAGE}
docker addon management:
extends: .template_docker
script:
- mkdir -p packages/install packages/uninstall
# part 1: test package installation
- cp .gitlab/docker/*.zpm packages/install/
- docker build --build-arg COMMIT_SHA=${CI_COMMIT_SHORT_SHA} --pull -t $ZAMMAD_IMAGE .
- cd zammad-docker-compose
- echo "ELASTICSEARCH_ENABLED=false" >> .env
- docker compose run --rm zammad-init
- |
docker compose run --rm zammad-railsserver bundle exec rails r "Setting.where(name: ['zammad_test_a_addon_up', 'zammad_test_b_addon_up']).count == 2 || raise('Setting not found')"
- cd ..
# part 2: test package uninstallation
- mv packages/install/*.zpm packages/uninstall
- docker build --build-arg COMMIT_SHA=${CI_COMMIT_SHORT_SHA} --pull -t $ZAMMAD_IMAGE .
- cd zammad-docker-compose
- docker compose run --rm zammad-init
- |
docker compose run --rm zammad-railsserver bundle exec rails r "Setting.where(name: ['zammad_test_a_addon_down', 'zammad_test_b_addon_down']).count == 2 || raise('Setting not found')"

View File

@@ -2,13 +2,20 @@
stage: test
extends:
- .job_rules_default
- .no_artifact_dependencies
- .artifacts_with_test_report
needs:
- !reference [.needs, nodejs]
- !reference [.needs, lint]
timeout: 15 minutes
cache: !reference [.cache, read_only_nodejs]
before_script:
- !reference [.scripts, pnpm_install]
- !reference [.scripts, pnpm_install_eslint_plugin_zammad]
script:
# Only run one worker to avoid load peaks affecting other jobs.
- pnpm test --max-workers=1 --shard $CI_NODE_INDEX/$CI_NODE_TOTAL
# Vitest resolves a relative output file against app/frontend, its root.
- pnpm test --max-workers=1 --shard $CI_NODE_INDEX/$CI_NODE_TOTAL --reporter=default --reporter=junit --outputFile.junit=$CI_PROJECT_DIR/$CI_JUNIT_REPORT_DIR/vitest.xml
# Stop here, except on the first shard.
- "[[ $CI_NODE_INDEX = 1 ]] || exit 0"
- echo "Also test eslint-plugin-zammad…"
@@ -25,6 +32,11 @@ frontend:cypress:
extends:
- .job_rules_default
- .tags_docker_low_concurrency
- .no_artifact_dependencies
needs:
- !reference [.needs, nodejs]
- !reference [.needs, lint]
timeout: 15 minutes
cache: !reference [.cache, read_only_nodejs]
before_script:
- !reference [.scripts, pnpm_install_cypress]

View File

@@ -3,10 +3,24 @@ include:
.template_integration:
extends:
- .job_rules_integration
- .template_ruby
- .artifacts_with_test_report
# These tests partly talk to live third party services. On private branches their
# signal is advisory only, so a failure does not block the pipeline.
rules:
- !reference [.rule_fragments, variable_filters]
- if: $CI_COMMIT_REF_NAME =~ /^private/
when: on_success
allow_failure: true
- when: on_success
services:
- !reference [.services, postgresql]
- !reference [.services, imap]
- !reference [.services, redis]
- !reference [.services, memcached]
stage: test
needs:
- !reference [.needs, ruby]
- !reference [.needs, database]
- !reference [.needs, lint]
timeout: 15 minutes

View File

@@ -1,10 +1,18 @@
.template_integration_es:
extends:
- .template_ruby
- .artifacts_with_test_report
services:
- !reference [.services, postgresql]
- !reference [.services, redis]
- !reference [.services, memcached]
- !reference [.services, elasticsearch]
stage: test
needs:
- !reference [.needs, ruby]
- !reference [.needs, database]
- !reference [.needs, lint]
timeout: 30 minutes
script:
- !reference [.scripts, zammad_db_init]
- bundle exec rspec --tag searchindex --tag ~type:system
@@ -19,8 +27,8 @@ rspec:es:
matrix:
- ELASTICSEARCH_TAG: [stable, latest]
rules:
- !reference [.job_rules, variable_filters]
- !reference [.rule_fragments, variable_filters]
# The lowest supported version is covered in every pipeline.
- if: $ELASTICSEARCH_TAG == "stable"
- if: $CI_PIPELINE_SOURCE == "schedule"
- when: manual
allow_failure: true
when: on_success
- !reference [.rule_fragments, then_manual_unless_scheduled]

View File

@@ -20,9 +20,8 @@
extends:
- .template_integration_otrs
rules:
- !reference [.job_rules, variable_filters]
- when: manual
allow_failure: true
- !reference [.rule_fragments, variable_filters]
- !reference [.rule_fragments, then_manual]
rspec:integration:otrs:6:
extends:

View File

@@ -5,26 +5,31 @@ rspec:migration:
stage: test
extends:
- .job_rules_default
- .artifacts_with_test_report
needs:
- !reference [.needs, ruby]
- !reference [.needs, lint]
timeout: 20 minutes
cache: !reference [.cache, read_only_ruby]
services:
- !reference [.services, postgresql]
- !reference [.services, redis]
# The database must be initialized by the code of the stable branch, so the database dump
# of this pipeline must not be used here.
dependencies: []
before_script: []
script:
# First, checkout stable and set it up.
- git fetch --unshallow
- git checkout stable-6.0
- git fetch --depth=1 origin stable-6.0
- git checkout FETCH_HEAD
- !reference [.scripts, source_rvm]
- rvm use 3.1.3
- rvm use $(cat .ruby-version)
- bundle config set without mysql
- !reference [.scripts, bundle_install]
- !reference [.scripts, configure_environment]
- !reference [.scripts, zammad_db_init]
# Then, switch to the current commit, migrate to it and run a few selected tests.
- git checkout $CI_COMMIT_SHA
- rvm use 3.4.9
- rvm use $(cat .ruby-version)
- !reference [.scripts, bundle_install]
# Force redis usage, even if it was disabled by the initial configure_environment script of stable.
- export REDIS_URL=redis://redis

View File

@@ -1,16 +1,24 @@
.packager.io:
image: docker
stage: test
extends:
- .no_artifact_dependencies
needs:
- !reference [.needs, lint]
timeout: 30 minutes
interruptible: false
allow_failure: true
retry: 2
rules:
- !reference [.rule_fragments, main_repository_only]
# Don't apply job filter here.
# These jobs can only run for a) published paths and b) when triggered after the packager.io build is complete.
# The pattern is deliberately not anchored at the end: packager.io publishes a
# repository per branch, so stable-* maintenance branches are valid targets, too.
- if: ($CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web") && $CI_COMMIT_REF_NAME =~ /^(stable|develop)/
when: on_success
- when: never
services: []
cache: []
before_script: []
script:
- .gitlab/packager.io/run_scenarios.sh
after_script:
@@ -20,4 +28,4 @@ packager.io:
extends: [.packager.io]
parallel:
matrix:
- DISTRO: [debian-11, debian-12, debian-13, ubuntu-22.04, ubuntu-24.04, ubuntu-26.04, rhel-9, rhel-10, sles-15, sles-16.0]
- DISTRO: [debian-12, debian-13, ubuntu-22.04, ubuntu-24.04, ubuntu-26.04, rhel-9, rhel-10, sles-15, sles-16.0]

View File

@@ -1,29 +1,35 @@
performance:
stage: test
needs:
- !reference [.needs, lint]
timeout: 45 minutes
rules:
- !reference [.job_rules, variable_filters]
- !reference [.rule_fragments, variable_filters]
- if: $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"
when: on_success
allow_failure: true
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- .gitlab/ci/test/performance.yml
- .gitlab/performance/**/*
when: on_success
allow_failure: true
# No manual start: the job runs for a long time and needs an exclusive runner.
- when: never
# Make sure this gets executed exclusively on a runner.
tags: ['no_concurrency']
services:
- !reference [.services, postgresql]
- !reference [.services, redis]
cache: []
before_script:
# Copy performance checking code to tmp/ so that it survives branch switching
- cp -rv .gitlab/performance tmp/
# First, checkout stable and set it up.
- git fetch --unshallow >/dev/null 2>&1
- git checkout stable-6.0
- git fetch --depth=1 origin stable-6.0
- git checkout FETCH_HEAD
- !reference [.scripts, source_rvm]
- rvm use 3.1.3
- rvm use $(cat .ruby-version)
- bundle config set without mysql
- !reference [.scripts, bundle_install]
- !reference [.scripts, configure_environment]
@@ -35,7 +41,7 @@ performance:
- bundle exec rails r tmp/performance/run_tests.rb || true
# Then, switch to the current commit, migrate to it and run a few selected tests.
- git checkout $CI_COMMIT_SHA
- rvm use 3.4.9
- rvm use $(cat .ruby-version)
- !reference [.scripts, bundle_install]
# Force redis usage, even if it was disabled by the initial configure_environment script of stable.
- export REDIS_URL=redis://redis

View File

@@ -4,7 +4,14 @@ include:
.template_rspec:
stage: test
extends:
- .template_ruby
- .job_rules_default
- .artifacts_with_test_report
needs:
- !reference [.needs, ruby]
- !reference [.needs, database]
- !reference [.needs, lint]
timeout: 40 minutes
script:
- !reference [.scripts, zammad_db_init]
- bundle exec rspec --require ./spec/rspec_extensions.rb --exclude-pattern "spec/system/**/*_spec.rb" -t ~searchindex -t ~integration
@@ -19,13 +26,28 @@ include:
rspec:integration:
stage: test
extends:
- .template_ruby
- .variables_proxy
- .variables_mattermost
- .variables_s3
- .artifacts_with_test_report
needs:
- !reference [.needs, ruby]
- !reference [.needs, database]
- !reference [.needs, lint]
timeout: 30 minutes
allow_failure: true
# Squid permits CONNECT to 443, 8443 and 3001 only (configs/squid-ci.conf in
# docker/zammad-proxies), so the SSL server of spec/lib/user_agent_spec.rb
# has to stay on 3001 for the examples that go through the proxy service.
# Safe here, and only here: this is the one job that runs that spec, and it
# excludes spec/system, so Capybara never serves on that port alongside it.
variables:
ZAMMAD_TEST_WEB_SSL_PORT: '3001'
rules:
- !reference [.job_rules, variable_filters]
- !reference [.rule_fragments, variable_filters]
- if: $CI_PIPELINE_SOURCE == "schedule"
when: on_success
allow_failure: true
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
@@ -67,9 +89,9 @@ rspec:integration:
- app/models/{channel,external_credential,store,webhook}.rb
- app/models/{channel,external_credential,store,webhook}/**/*
- app/services/service/{ai,ticket/external_references}/**/*
when: on_success
allow_failure: true
- when: manual
allow_failure: true
- !reference [.rule_fragments, then_manual]
services:
- !reference [.services, postgresql]
- !reference [.services, imap]

View File

@@ -2,14 +2,21 @@
addon:subscription:
stage: test
extends:
- .job_rules_default
- .template_ruby
- .artifacts_with_test_report
needs:
- !reference [.needs, ruby]
- !reference [.needs, database]
- !reference [.needs, lint]
timeout: 15 minutes
rules:
- !reference [.job_rules, variable_filters]
- !reference [.rule_fragments, main_repository_only]
- !reference [.rule_fragments, variable_filters]
- if: $CI_COMMIT_REF_NAME == 'develop'
when: on_success
# Complain if breaking changes were merged and subscription is not ported.
allow_failure: false
- if: $CI_COMMIT_REF_NAME =~ /^(private|develop)/
- if: $CI_COMMIT_REF_NAME =~ /^private/
when: on_success
# It could happen that in a branch we need to make breaking changes, so
# only hint at the situation but don't block the pipeline.

View File

@@ -1,5 +1,12 @@
.template_supabase:
stage: test
extends:
- .template_ruby
- .no_artifact_dependencies
needs:
- !reference [.needs, ruby]
- !reference [.needs, lint]
timeout: 10 minutes
tags:
- push-to-ftp # use only allow-listed ci runners
services:
@@ -11,9 +18,10 @@ supabase:translation_stats:
extends:
- .template_supabase
rules:
- if: $CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME =~ /^(develop|stable)$/
when: always
- when: never
- !reference [.rule_fragments, main_repository_only]
- if: $CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME =~ /^(develop|stable)$/
when: always
- when: never
script:
- bundle exec rails runner .gitlab/supabase/translation_stats.rb
@@ -21,6 +29,7 @@ supabase:github_stats:
extends:
- .template_supabase
rules:
- !reference [.rule_fragments, main_repository_only]
- if: $CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME == "develop"
when: always
- when: never

View File

@@ -0,0 +1,39 @@
---
name: ZammadTestA
version: 1.0.0
vendor: Zammad GmbH
license: GNU AFFERO GENERAL PUBLIC LICENSE
url: http://zammad.com/
change_log: []
dependencies:
ZammadTestB: ">= 1.0.0"
description:
- language: en
text: Test package for CI
- language: de
text: Testpaket für die CI
buildhost: zammad42
builddate: 2026-08-19 15:29:46 UTC
files:
- location: db/addon/zammad_test_a/20251112113130_zammad_test_a_settings.rb
permission: 644
content: |
class ZammadTestASettings < ActiveRecord::Migration[8.0]
def self.up
Setting.create_if_not_exists(
title: 'Zammad Test A Addon Up',
name: 'zammad_test_a_addon_up',
area: 'Core',
description: 'Verify that the addon was installed.',
)
end
def self.down
Setting.create_if_not_exists(
title: 'Zammad Test A Addon Down',
name: 'zammad_test_a_addon_down',
area: 'Core',
description: 'Verify that the addon was removed.',
)
end
end

View File

@@ -0,0 +1,31 @@
{
"name": "ZammadTestA",
"version": "1.0.0",
"vendor": "Zammad GmbH",
"license": "GNU AFFERO GENERAL PUBLIC LICENSE",
"url": "http://zammad.com/",
"change_log": [],
"dependencies": {
"ZammadTestB": ">= 1.0.0"
},
"description": [
{
"language": "en",
"text": "Test package for CI"
},
{
"language": "de",
"text": "Testpaket für die CI"
}
],
"buildhost": "zammad42",
"builddate": "2026-08-19 15:29:46 UTC",
"files": [
{
"location": "db/addon/zammad_test_a/20251112113130_zammad_test_a_settings.rb",
"permission": 644,
"content": "Y2xhc3MgWmFtbWFkVGVzdEFTZXR0aW5ncyA8IEFjdGl2ZVJlY29yZDo6TWlncmF0aW9uWzguMF0KICBkZWYgc2VsZi51cAogICAgU2V0dGluZy5jcmVhdGVfaWZfbm90X2V4aXN0cygKICAgICAgdGl0bGU6ICAgICAgICdaYW1tYWQgVGVzdCBBIEFkZG9uIFVwJywKICAgICAgbmFtZTogICAgICAgICd6YW1tYWRfdGVzdF9hX2FkZG9uX3VwJywKICAgICAgYXJlYTogICAgICAgICdDb3JlJywKICAgICAgZGVzY3JpcHRpb246ICdWZXJpZnkgdGhhdCB0aGUgYWRkb24gd2FzIGluc3RhbGxlZC4nLAogICAgKQogIGVuZAoKICBkZWYgc2VsZi5kb3duCiAgICBTZXR0aW5nLmNyZWF0ZV9pZl9ub3RfZXhpc3RzKAogICAgICB0aXRsZTogICAgICAgJ1phbW1hZCBUZXN0IEEgQWRkb24gRG93bicsCiAgICAgIG5hbWU6ICAgICAgICAnemFtbWFkX3Rlc3RfYV9hZGRvbl9kb3duJywKICAgICAgYXJlYTogICAgICAgICdDb3JlJywKICAgICAgZGVzY3JpcHRpb246ICdWZXJpZnkgdGhhdCB0aGUgYWRkb24gd2FzIHJlbW92ZWQuJywKICAgICkKICBlbmQKZW5kCg==",
"encode": "base64"
}
]
}

View File

@@ -0,0 +1,37 @@
---
name: ZammadTestB
version: 1.0.0
vendor: Zammad GmbH
license: GNU AFFERO GENERAL PUBLIC LICENSE
url: http://zammad.com/
change_log: []
description:
- language: en
text: Test package for CI
- language: de
text: Testpaket für die CI
buildhost: zammad42
builddate: 2026-08-19 15:29:46 UTC
files:
- location: db/addon/zammad_test_b/20251112113130_zammad_test_b_settings.rb
permission: 644
content: |
class ZammadTestBSettings < ActiveRecord::Migration[8.0]
def self.up
Setting.create_if_not_exists(
title: 'Zammad Test B Addon Up',
name: 'zammad_test_b_addon_up',
area: 'Core',
description: 'Verify that the addon was installed.',
)
end
def self.down
Setting.create_if_not_exists(
title: 'Zammad Test B Addon Down',
name: 'zammad_test_b_addon_down',
area: 'Core',
description: 'Verify that the addon was removed.',
)
end
end

View File

@@ -0,0 +1,28 @@
{
"name": "ZammadTestB",
"version": "1.0.0",
"vendor": "Zammad GmbH",
"license": "GNU AFFERO GENERAL PUBLIC LICENSE",
"url": "http://zammad.com/",
"change_log": [],
"description": [
{
"language": "en",
"text": "Test package for CI"
},
{
"language": "de",
"text": "Testpaket für die CI"
}
],
"buildhost": "zammad42",
"builddate": "2026-08-19 15:29:46 UTC",
"files": [
{
"location": "db/addon/zammad_test_b/20251112113130_zammad_test_b_settings.rb",
"permission": 644,
"content": "Y2xhc3MgWmFtbWFkVGVzdEJTZXR0aW5ncyA8IEFjdGl2ZVJlY29yZDo6TWlncmF0aW9uWzguMF0KICBkZWYgc2VsZi51cAogICAgU2V0dGluZy5jcmVhdGVfaWZfbm90X2V4aXN0cygKICAgICAgdGl0bGU6ICAgICAgICdaYW1tYWQgVGVzdCBCIEFkZG9uIFVwJywKICAgICAgbmFtZTogICAgICAgICd6YW1tYWRfdGVzdF9iX2FkZG9uX3VwJywKICAgICAgYXJlYTogICAgICAgICdDb3JlJywKICAgICAgZGVzY3JpcHRpb246ICdWZXJpZnkgdGhhdCB0aGUgYWRkb24gd2FzIGluc3RhbGxlZC4nLAogICAgKQogIGVuZAoKICBkZWYgc2VsZi5kb3duCiAgICBTZXR0aW5nLmNyZWF0ZV9pZl9ub3RfZXhpc3RzKAogICAgICB0aXRsZTogICAgICAgJ1phbW1hZCBUZXN0IEIgQWRkb24gRG93bicsCiAgICAgIG5hbWU6ICAgICAgICAnemFtbWFkX3Rlc3RfYl9hZGRvbl9kb3duJywKICAgICAgYXJlYTogICAgICAgICdDb3JlJywKICAgICAgZGVzY3JpcHRpb246ICdWZXJpZnkgdGhhdCB0aGUgYWRkb24gd2FzIHJlbW92ZWQuJywKICAgICkKICBlbmQKZW5kCg==",
"encode": "base64"
}
]
}

41
.gitlab/docker/yaml_to_zpm.rb Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env ruby
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
# Converts all package definitions in YAML form found next to this script to the
# JSON based .zpm format, written to a file of the same base name. The content of
# all entries in "files" is transcoded to base64.
#
# ruby yaml_to_zpm.rb
#
# ZammadTestA-1.0.0.yaml -> ZammadTestA-1.0.0.zpm
require 'base64'
require 'json'
require 'yaml'
def convert(source, target)
package = YAML.safe_load_file(source, aliases: true)
raise "Expected a YAML mapping as package definition in #{source}." if !package.is_a?(Hash)
Array(package['files']).each do |file|
next if !file.is_a?(Hash) || !file.key?('content')
file['encode'] = 'base64'
file['content'] = Base64.strict_encode64(file['content'].to_s)
end
File.write(target, "#{JSON.pretty_generate(package)}\n")
end
sources = Dir.glob('*.{yaml,yml}', base: __dir__).sort
raise 'No YAML package definitions found.' if sources.empty?
sources.each do |source|
target = "#{File.basename(source, '.*')}.zpm"
convert(File.join(__dir__, source), File.join(__dir__, target))
puts "#{source} -> #{target}"
end

View File

@@ -1,19 +0,0 @@
#!/bin/bash
date -u --rfc-3339=seconds
echo
top -b -n1 -w240
echo
ps aux --width 240
echo
free -m
echo
mount
echo
df -h
echo
echo "Last 100 lines from kernel log"
dmesg | tail -n 100
echo
echo "Last 1000 lines from system log (journalctl)"
journalctl -n 1000

View File

@@ -6,6 +6,9 @@ dnf update -y
dnf install -y systemd epel-release
# A background metadata refresh races with the scenario's dnf calls on the lock file.
systemctl mask dnf-makecache.timer dnf-makecache.service
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
echo "[elasticsearch-8.x]
name=Elasticsearch repository for 8.x packages

View File

@@ -2,6 +2,21 @@
set -eu
# systemd is not reachable yet right after container start, and reports "degraded" inside
# containers once boot has finished.
booted=false
for _ in $(seq 60); do
case "$(systemctl is-system-running 2>/dev/null || true)" in
running|degraded) booted=true; break ;;
esac
sleep 1
done
if [ "$booted" != true ]; then
echo "systemd did not finish booting within 60 seconds." >&2
exit 1
fi
dnf install -y zammad
curl --retry 30 --retry-delay 1 --retry-connrefused http://localhost:3000 | grep "Zammad Helpdesk"

View File

@@ -1,5 +1,16 @@
{
"enabledManagers": ["bundler", "npm", "github-actions", "devcontainer", "custom.regex"],
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"ignorePaths": ["**/node_modules/**"],
"enabledManagers": [
"bundler",
"npm",
"github-actions",
"devcontainer",
"dockerfile",
"docker-compose",
"gitlabci",
"custom.regex"
],
"customManagers": [
{
"customType": "regex",
@@ -10,6 +21,15 @@
"depNameTemplate": "@devcontainers/cli",
"datasourceTemplate": "npm"
},
{
"customType": "regex",
"managerFilePatterns": [
"/^\\.gitlab/ci/.*\\.yml$/"
],
"matchStrings": ["PLAYWRIGHT_VERSION: '(?<currentValue>[^']+)'"],
"depNameTemplate": "playwright",
"datasourceTemplate": "npm"
},
{
"customType": "regex",
"managerFilePatterns": [
@@ -27,6 +47,11 @@
"commitMessageExtra": "(Ruby)"
}
},
"gitlabci": {
"managerFilePatterns": [
"/^\\.gitlab/ci/.*\\.yml$/"
]
},
"devcontainer": {
"managerFilePatterns": [
"/^\\.devcontainer/[^/]+/devcontainer\\.json$/"
@@ -41,6 +66,11 @@
}
},
"minimumReleaseAge": "3 days",
"osvVulnerabilityAlerts": true,
"vulnerabilityAlerts": {
"minimumReleaseAge": null,
"addLabels": ["security"]
},
"assignees": ["fl"],
"packageRules": [
{
@@ -65,8 +95,35 @@
},
{
"matchCategories": ["js"],
"matchFileNames": [".cypress/package.json"],
"labels": ["cypress"]
"matchFileNames": [".dev/cypress/package.json"],
"addLabels": ["cypress"]
},
{
"matchManagers": ["dockerfile", "docker-compose"],
"matchFileNames": [
".dev/**",
".gitlab/packager.io/**"
],
"enabled": false
},
{
"matchPackageNames": [
"playwright",
"playwright-ruby-client",
"capybara-playwright-driver"
],
"groupName": "Playwright packages",
"groupSlug": "playwright"
},
{
"matchDepNames": ["ruby", "docker.io/library/ruby"],
"groupName": "Ruby version",
"groupSlug": "ruby-version"
},
{
"matchDepNames": ["node", "docker.io/library/node"],
"groupName": "Node version",
"groupSlug": "node-version"
},
{
"matchCategories": ["js"],
@@ -74,6 +131,12 @@
"groupSlug": "eslint",
"matchPackageNames": ["/eslint/"]
},
{
"matchCategories": ["js"],
"groupName": "Oxc packages",
"groupSlug": "oxc",
"matchPackageNames": ["oxlint", "oxfmt", "eslint-plugin-oxlint"]
},
{
"matchCategories": ["js"],
"groupName": "Stylelint packages",
@@ -118,7 +181,8 @@
"/^@vue/",
"/^vue/",
"!@vue/eslint-config-typescript",
"!@vuepic/vue-datepicker"
"!@vuepic/vue-datepicker",
"!/^@vueuse/"
],
"groupName": "Vue packages",
"groupSlug": "vue"

View File

@@ -2,24 +2,7 @@
"semi": false,
"singleQuote": true,
"printWidth": 100,
"sortTailwindcss": {
"stylesheet": "./app/frontend/apps/desktop/styles/main.css"
},
"sortPackageJson": false,
"overrides": [
{
"files": ["./app/frontend/apps/desktop/**"],
"options": {
"sortTailwindcss": { "stylesheet": "./app/frontend/apps/desktop/styles/main.css" }
}
},
{
"files": ["./app/frontend/apps/mobile/**"],
"options": {
"sortTailwindcss": { "stylesheet": "./app/frontend/apps/mobile/styles/main.css" }
}
}
],
"ignorePatterns": [
"app/frontend/**/graphql/**/*.ts",
"!app/frontend/tests/graphql/**/*.ts",

View File

@@ -35,20 +35,6 @@ targets:
# EPEL packages can only be installed after enabling the EPEL repo (see before.sh).
- epel-release
- shared-mime-info
debian-11:
dependencies:
- curl
- elasticsearch|elasticsearch-oss
- nginx|apache2
- postgresql
- libimlib2
- libjemalloc2
- shared-mime-info
- redis-server
build_dependencies:
- libimlib2
- libimlib2-dev
- shared-mime-info
debian-12:
dependencies:
- curl

View File

@@ -2,6 +2,32 @@
## Upcoming releases
### `String#utf8_encode` and `Object#to_utf8` will be removed
**Who is affected?** Developers of third-party packages, patches or add-ons that still call
`String#utf8_encode` or `Object#to_utf8`.
Both core extensions, deprecated since Zammad 7.3, will be removed in Zammad 8.0. Code still calling
them will then raise `NoMethodError`.
⚠️ Switch such code to `TextEncoding.utf8_encode` before updating to Zammad 8.0.
**Related issue:** [#6371](https://github.com/zammad/zammad/issues/6371)
### Debian 11 will no longer be supported
**Who is affected?** Admins of instances installed from the Zammad package repository on Debian 11
("bullseye").
Debian 11 reached its LTS end of life on 2026-08-31 and no longer receives security updates. A future
release will therefore no longer be built for it, and the package repository will provide no Debian 11
packages for that release and later ones. Zammad 7.1 keeps receiving Debian 11 packages until it goes
out of maintenance.
⚠️ Upgrade the operating system to a supported Debian release before updating.
**Related issue:** [#6360](https://github.com/zammad/zammad/issues/6360)
### Inline attachments will be listed separately in ticket article API responses
**Who is affected?** Integrations that read inline images from the `attachments` list of ticket article
@@ -15,32 +41,6 @@ will be returned in a dedicated `inline_attachments` key instead.
**Related issue:** [#6254](https://github.com/zammad/zammad/issues/6254)
### Deleting organizations via API will require the `admin.organization` permission
**Who is affected?** Integrations that call `DELETE /api/v1/organizations/:id` with a token that only
has the `ticket.agent` permission.
The endpoint has historically been permitted for users with only the `ticket.agent` permission. That is
deprecated and will be restricted to `admin.organization` in a future release; agent tokens will then
receive `403 Forbidden`.
⚠️ Switch such integrations to an account with the `admin.organization` permission.
**Related issue:** [#6315](https://github.com/zammad/zammad/issues/6315)
### Import mode will also enable maintenance mode
**Who is affected?** Admins of instances that are left in import mode while users are expected to work
in them.
Import mode was never intended for productive environments. In a future release, an instance running in
import mode will automatically run in maintenance mode as well, which prevents non-admin users from
logging in.
⚠️ Disable import mode before letting users work in the instance.
**Related issue:** [#6237](https://github.com/zammad/zammad/issues/6237)
### `Exceptions::UnprocessableEntity` will be removed
**Who is affected?** Developers of custom packages, patches or add-ons that still reference
@@ -71,8 +71,191 @@ instead.
**Related issue:** [#5580](https://github.com/zammad/zammad/issues/5580)
## 7.3
### `String#utf8_encode` and `Object#to_utf8` were deprecated in favour of `TextEncoding.utf8_encode`
**Who is affected?** Developers of third-party packages, patches or add-ons that call
`String#utf8_encode`, `String#utf8_encode!` or `Object#to_utf8`.
Encoding resolution moved out of `lib/core_ext/string.rb` into a dedicated class, `TextEncoding`,
which takes the string as an argument: `string.utf8_encode(from: 'iso-8859-1')` became
`TextEncoding.utf8_encode(string, from: 'iso-8859-1')`, and `object.to_utf8` became
`TextEncoding.utf8_encode(object.to_s)`. `String` no longer requires the `rchardet` and `mail` gems.
`String#utf8_encode` and `Object#to_utf8` still work and now emit a deprecation warning.
`String#utf8_encode!` was removed without a deprecation period, since `TextEncoding.utf8_encode`
never modifies its argument and always returns a new string.
⚠️ Replace calls to `String#utf8_encode` and `Object#to_utf8` with `TextEncoding.utf8_encode`, and
replace `String#utf8_encode!` with an assignment from it.
**Related issue:** [#6371](https://github.com/zammad/zammad/issues/6371)
### Import mode also enables maintenance mode
**Who is affected?** Admins of instances that are left in import mode while users are expected to work
in them.
Import mode was never intended for productive environments. An instance running in import mode now
automatically runs in maintenance mode as well: while the `import_mode` setting is enabled, every
authenticated request of a user without the `admin.maintenance` permission is refused. REST and API
requests get `403 Forbidden`, and GraphQL treats the session as logged out. This covers new logins,
sessions that were already open, and API access via token or HTTP basic authentication alike. An
impersonated session is only exempt when the administrator who switched into it holds
`admin.maintenance`; this also applies to the classic maintenance mode.
⚠️ Disable import mode before letting users work in the instance.
**Related issue:** [#6237](https://github.com/zammad/zammad/issues/6237)
### The knowledge base search index was switched from HTML-escaped text to plain text
**Who is affected?** Admins of instances with Elasticsearch enabled that use the knowledge base, and
integrations that call `POST /api/v1/knowledge_bases/search`.
Knowledge base titles and answer bodies were written into the search index HTML-escaped, so a
category named `Warranty & Returns` was indexed as `Warranty &amp; Returns`. The escape sequences
were searchable in their own right: `amp`, `lt`, `gt` and `nbsp` matched content that contains no
such word, and a search matching one of them, the wildcard `*` among them, highlighted it and left
the sequence visible in the result list ([#6369](https://github.com/zammad/zammad/issues/6369)).
Both texts are now indexed as plain text. Markup in a title is still dropped on its way into the
index, and the title columns themselves keep what was written to them.
The details returned by `POST /api/v1/knowledge_bases/search` changed with it: `title`, `body` and
`subtitle` are now always HTML, with every character escaped and, when `highlight_enabled` is on,
`<em>` marking the matched runs. Previously only a highlighted fragment was guaranteed to look like
this. A detail without a match in its field came back the way its fallback produced it: HTML-escaped
for the answer `body`, the category and knowledge base `title` and the `subtitle`, and as written
for the answer `title`, with highlighting on or off.
The `result` list no longer carries the `highlight` key of each hit. The raw fragments it held mark
their matches with characters that only the `details` rendering above knows how to interpret.
⚠️ Integrations that read the answer `title` as text have to decode it as HTML, like every other detail.
⚠️ Rebuild the search index after the update: `zammad run rake zammad:searchindex:rebuild`
**Related issue:** [#6399](https://github.com/zammad/zammad/issues/6399)
### Deleting organizations via API requires the `admin.organization` permission
**Who is affected?** Integrations that call `DELETE /api/v1/organizations/:id` with a token that only
has the `ticket.agent` permission.
The endpoint has historically been permitted for users with only the `ticket.agent` permission, which
contradicted the API documentation. That behaviour was deprecated in Zammad 7.2: the endpoint now
requires `admin.organization`, and agent tokens receive `403 Forbidden`.
⚠️ Switch such integrations to an account with the `admin.organization` permission.
**Related issue:** [#6315](https://github.com/zammad/zammad/issues/6315)
## 7.2
### Verifying a signup email address no longer signs the user in
**Who is affected?** Integrations that redeem a signup verification token through
`POST /api/v1/users/email_verify` or the `userSignupVerify` GraphQL mutation and rely on the session
that the redemption used to establish, and admins who send a verification or password reset link to a
deactivated account.
Redeeming the token established a fully authenticated session for the account, without the account's
password and without the second factor configured for it. Both endpoints now only mark the email
address as verified and establish no session. The REST endpoint still answers
`200 {"message":"ok","user_email":"…"}`, but the response no longer carries a session cookie. The
mutation's payload field `session` was replaced by a boolean `success`, so a client selecting
`session { id }` receives a GraphQL error.
A non-persistent token — `Signup`, `PasswordReset`, `AdminAuth`, `KnowledgeBasePreview` — is now
rejected when it belongs to an inactive user. Previously only persistent tokens were checked this
way. The mail carrying such a link is still sent, but the link itself no longer works while the
account is deactivated.
⚠️ Sign in through `POST /api/v1/signin` or the `login` mutation after redeeming a verification
token, and pass the configured second factor there. Change GraphQL clients to select `success`
instead of `session`. Activate a deactivated account before sending it a verification or password
reset link.
**Related advisory:** [GHSA-f3qr-94c2-7mx2](https://github.com/zammad/zammad/security/advisories/GHSA-f3qr-94c2-7mx2)
### The getting started endpoints require `admin.wizard` once the system is set up
**Who is affected?** Integrations that read `GET /api/v1/getting_started` or
`GET /api/v1/getting_started/auto_wizard` while authenticated as a user without the `admin.wizard`
permission, and any integration that reads them while a migration is running.
On a system that is set up, both endpoints returned the list of active groups and of active email
addresses, the branding config and the available email channel drivers to every authenticated user,
customers included. That data is only of use to a user who may run the setup wizard, and it is
restricted on its own endpoints: `GET /api/v1/email_addresses` answers a customer with `403`, and
`GET /api/v1/groups/:id` field-scopes its response. Both endpoints now answer a user without
`admin.wizard` with `403 Forbidden` instead.
Before the setup, and while a migration is running, the endpoints stay reachable without
authentication: a migration is started anonymously from the installer, and its progress screens have
no user to authenticate as. While a migration is running the response is now reduced to the setup and
migration state — `setup_done`, `import_mode`, `import_backend` and `system_online_service` — which
is all those screens read; the group and email address list, the branding config and the email
channel drivers are no longer part of it, for any caller. A migration only clears its import mode
when it succeeds, so a failed one would otherwise keep that data readable without authentication
indefinitely. Outside a migration the response is unchanged for users with `admin.wizard`.
⚠️ Switch integrations that read the group or email address list from these endpoints to an account
with the `admin.wizard` permission — an API token needs that permission in its scope as well — or to
`GET /api/v1/groups` respectively `GET /api/v1/email_addresses`.
**Related advisory:** [GHSA-x3fm-p95w-xj7v](https://github.com/zammad/zammad/security/advisories/GHSA-x3fm-p95w-xj7v)
### The legacy WebSocket login is authenticated from the session cookie and `signshow` no longer returns `session_id`
**Who is affected?** Developers of custom clients that connect to the legacy WebSocket server and
authenticate with a `session_id` in the `login` event, integrations that read `session_id` from the
`GET /api/v1/signshow` response, and admins whose `http_type` and `fqdn` settings do not match the URL
users open Zammad with.
The `GET /api/v1/signshow` response contained the ID of the current session for logged-in users, and the
legacy frontend sent that ID in the `login` event of the WebSocket connection to authenticate it. Exposing
the session ID to page scripts was unnecessary and made it easier to steal a session. The `session_id`
key was removed from the response, and the WebSocket server now ignores any `session_id` in the `login`
payload. Instead it resolves the user from the session cookie sent with the WebSocket handshake, in the
same way ActionCable already does for the new frontend, and only if the handshake `Origin` header matches
`<http_type>://<fqdn>` or a localhost origin. A `login` event without a matching cookie and origin creates
an unauthenticated WebSocket session that receives no user-specific data.
⚠️ Send the session cookie with the WebSocket handshake instead of a `session_id` in the `login` payload,
and make sure the `http_type` and `fqdn` settings match the origin users open Zammad with.
**Related advisory:** [GHSA-23hj-h8w6-rgm3](https://github.com/zammad/zammad/security/advisories/GHSA-23hj-h8w6-rgm3)
### Zammad's reverse proxy configuration is authoritative for the request scheme
**Who is affected?** Admins running the Docker images or a newly installed nginx behind a proxy
that reports the request scheme via the `Forwarded` or `X-Forwarded-Ssl` header, and admins of a
newly installed Apache behind any TLS-terminating proxy.
Rack evaluates `X-Forwarded-Ssl` and `Forwarded` with a higher priority than `X-Forwarded-Proto`, so
an outer proxy could silently override the scheme that Zammad's own web server configuration had set.
The nginx and Apache configurations shipped with Zammad therefore now clear `Forwarded`,
`X-Forwarded-Scheme` and `X-Forwarded-Ssl`, which leaves `X-Forwarded-Proto` as the only place where
the scheme is decided.
The Apache configurations did not set `X-Forwarded-Proto` at all before, so an instance behind an
SSL-terminating Apache was never seen as HTTPS. They now set it explicitly, to `http` in the non-SSL
and to `https` in the SSL virtual host, which also means an outer proxy's value is no longer passed
through.
The Docker images rebuild their nginx configuration from the shipped one on every start, so the change
applies to them automatically. Package installations keep the web server configuration they already
have; only newly installed ones receive the updated files.
⚠️ If Zammad was reachable over HTTPS only because the proxy in front of it sent `Forwarded` or
`X-Forwarded-Ssl` — or, on Apache, `X-Forwarded-Proto` — configure the scheme in Zammad's own web
server instead: set `NGINX_SERVER_SCHEME=https` for the Docker images, or the `X-Forwarded-Proto` line
in the nginx respectively Apache configuration file.
**Related issue:** [#6305](https://github.com/zammad/zammad/issues/6305)
### Links between an object and itself are rejected, and failed link creations answer `422`
**Who is affected?** Integrations that create links via `POST /api/v1/links/add`.
@@ -202,6 +385,29 @@ reverse proxy are blocked again by it.
## 7.1
### `X-Forwarded-Proto` takes precedence over `X-Forwarded-Scheme`
**Who is affected?** Admins running Zammad behind a reverse proxy that terminates TLS and reports the
request scheme via the `X-Forwarded-Scheme` header only, such as Nginx Proxy Manager.
Zammad was updated from Rack 2 to Rack 3, which changed which header decides whether a request is
seen as HTTPS: Rack 2 evaluated `X-Forwarded-Scheme` first, Rack 3 evaluates `X-Forwarded-Proto`
first. Zammad's own nginx configuration sets `X-Forwarded-Proto` from the scheme of the connection it
receives, in the Docker images via the `NGINX_SERVER_SCHEME` variable, which defaults to `$scheme`.
Behind a proxy that terminates TLS and forwards plain HTTP, that value is `http`, so such requests
are now seen as plain HTTP even though the proxy reported `X-Forwarded-Scheme: https`.
With the `http_type` setting set to `https`, the session cookie is marked `Secure`, and a `Secure`
cookie is not written on a request that is seen as plain HTTP. No session is created at all, and
logging in fails with "CSRF token verification failed!". Sessions that already hold a cookie keep
working, so the problem only surfaces after a logout.
⚠️ Configure the scheme in Zammad's own web server rather than in the proxy in front of it, whose
`X-Forwarded-Proto` header is overwritten: set `NGINX_SERVER_SCHEME=https` for the Docker images, or
the `X-Forwarded-Proto` line in the nginx configuration file.
**Related issue:** [#6305](https://github.com/zammad/zammad/issues/6305)
### Elasticsearch 7 was deprecated
**Who is affected?** Admins of systems that run Elasticsearch 7.

View File

@@ -1,8 +1,8 @@
# Change Log for Zammad 7.2.x
# Change Log for Zammad 7.3.x
- [Release notes](https://zammad.com/en/product/releases/zammad-7-2-x)
- [Breaking changes](BREAKING_CHANGES.md#72)
- [Implemented enhancements](https://github.com/zammad/zammad/issues?q=is%3Aclosed+milestone%3A7.2+(-type%3ABug+AND+-label%3Abug))
- [Closed bugs](https://github.com/zammad/zammad/issues?q=is%3Aclosed+milestone%3A7.2+(type%3ABug+OR+label%3Abug))
- [Full commit log](https://github.com/zammad/zammad/compare/7.0.1...7.2.x)
- [File tree](https://github.com/zammad/zammad/tree/7.2.x)
- [Release notes](https://zammad.com/en/product/releases/zammad-7-3-x)
- [Breaking changes](BREAKING_CHANGES.md#73)
- [Implemented enhancements](https://github.com/zammad/zammad/issues?q=is%3Aclosed+milestone%3A7.3+(-type%3ABug+AND+-label%3Abug))
- [Closed bugs](https://github.com/zammad/zammad/issues?q=is%3Aclosed+milestone%3A7.3+(type%3ABug+OR+label%3Abug))
- [Full commit log](https://github.com/zammad/zammad/compare/7.2.0...7.3.x)
- [File tree](https://github.com/zammad/zammad/tree/7.3.x)

View File

@@ -18,7 +18,7 @@ WORKDIR /opt/zammad
RUN apt-get update -qq && \
apt-get install -y postgresql-common && \
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
apt-get install -y --no-install-recommends libjemalloc2 curl libimlib2 libpq5 nginx gnupg postgresql-client && \
apt-get install -y --no-install-recommends libjemalloc2 curl libimlib2 nginx gnupg postgresql-client && \
apt-get remove -y --purge exim4-base exim4-config bsd-mailx && \
apt-get autoremove -y --purge && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
@@ -40,12 +40,15 @@ RUN npm -g install corepack && corepack enable pnpm && \
FROM base AS build
ARG COMMIT_SHA
# Optional additional build information, e.g. the customer image name when building
# images with addon packages. Shown in the version string of the instance.
ARG BUILD_LABEL
SHELL ["/bin/bash", "-o", "errexit", "-o", "pipefail", "-c"]
# Install packages needed to build gems and node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git libimlib2-dev libpq-dev libyaml-dev && \
apt-get install --no-install-recommends -y build-essential git libimlib2-dev libssl-dev libyaml-dev && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install application gems
@@ -71,11 +74,38 @@ RUN if [ -z "${COMMIT_SHA}" ]; then \
echo "Error: the required build argument \$COMMIT_SHA is missing."; \
exit 1; \
fi; \
if ! [[ "${COMMIT_SHA}" =~ ^[0-9a-fA-F]{8,40}$ ]]; then \
echo "Error: the build argument \$COMMIT_SHA must match [0-9a-fA-F]{8,40}, got '${COMMIT_SHA}'."; \
exit 1; \
fi; \
if [ -n "${BUILD_LABEL}" ] && ! [[ "${BUILD_LABEL}" =~ ^[0-9a-zA-Z_.-]{1,35}$ ]]; then \
echo "Error: the build argument \$BUILD_LABEL must match [0-9a-zA-Z_.-]{1,35}, got '${BUILD_LABEL}'."; \
exit 1; \
fi; \
COMMIT_SHA_SHORT=$(echo "${COMMIT_SHA}" | cut -c 1-8); \
echo "$(tr -d '\n' < VERSION)-${COMMIT_SHA_SHORT}.docker" > VERSION; \
echo "$(tr -d '\n' < VERSION)-${COMMIT_SHA_SHORT}${BUILD_LABEL:+.${BUILD_LABEL}}.docker" > VERSION; \
echo 'Updated build information in VERSION:'; \
cat VERSION
# Special handling for zpm addons, if present:
# - Unpack addon packages provided in packages/install/*.zpm (registered in the database on container start)
# and packages/uninstall/*.zpm (removed from the database on container start).
# - Install additional gems provided by addon packages (Gemfile.local.*).
# Deployment mode is disabled to allow updating the Gemfile.lock with the additional gems.
# - Regenerate the GraphQL frontend API in case addon packages extend the GraphQL schema.
# A rake task is used instead of 'rails generate' because rake skips eager loading,
# which would require a database connection.
RUN if compgen -G "packages/*/*.zpm" > /dev/null; then \
ruby script/build/unpack_addon_packages.rb; \
if compgen -G "Gemfile.local.*" > /dev/null; then \
BUNDLE_DEPLOYMENT=false bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache; \
fi; \
mkdir -p tmp && touch db/schema.rb; \
RAILS_LOG_TO_STDOUT= ZAMMAD_SAFE_MODE=1 DATABASE_URL=postgresql://zammad:/zammad ZAMMAD_GRAPHQL_INTROSPECTION=true bundle exec rake zammad:graphql:introspection > app/graphql/graphql_introspection.json; \
pnpm exec graphql-codegen -c .graphql_code_generator.js; \
fi
# Don't require Redis or Postgres (use fake DATABASE_URL to make Rails validation happy).
RUN touch db/schema.rb && \
ZAMMAD_SAFE_MODE=1 DATABASE_URL=postgresql://zammad:/zammad bundle exec rake assets:precompile

12
Gemfile
View File

@@ -4,7 +4,7 @@ source 'https://rubygems.org'
# core - base
ruby '3.4.9'
gem 'rails', '~> 8.0.0'
gem 'rails', '~> 8.1.0'
gem 'rake'
# core - rails additions
@@ -21,6 +21,7 @@ gem 'puma', group: :puma
gem 'pg', '~> 1.5', '>= 1.5.4', group: :postgres
# core - asynchrous task execution
gem 'benchmark' # undeclared delayed_job dependency
gem 'daemons'
gem 'delayed_job', git: 'https://github.com/zammad-deps/delayed_job', branch: 'threadsafe'
gem 'delayed_job_active_record', git: 'https://github.com/zammad-deps/delayed_job_active_record', branch: 'update-limit-143'
@@ -216,6 +217,7 @@ gem 'elasticsearch', '8.11.2', require: false
group :development, :test do
# test frameworks
gem 'rspec_junit_formatter'
gem 'rspec-rails'
gem 'rspec-retry'
gem 'shoulda-matchers'
@@ -227,8 +229,11 @@ group :development, :test do
gem 'capybara'
gem 'selenium-webdriver'
# Playwright pilot (throwaway evaluation)
# Playwright pilot (throwaway evaluation). playwright-ruby-client is pulled in
# by the driver; it is declared here so Renovate sees it and can keep it in
# lockstep with the playwright npm package.
gem 'capybara-playwright-driver'
gem 'playwright-ruby-client'
# code QA
gem 'brakeman', require: false
@@ -268,9 +273,6 @@ group :development, :test do
# Self-signed localhost certificates for puma / capybara.
gem 'localhost'
# Keycloak admin tool for setting up SAML auth tests
gem 'ruby-keycloak-admin'
# Debugging and profiling
gem 'pry-doc', require: false # This gem is very large, so don't include it in production.

View File

@@ -32,29 +32,31 @@ GEM
simple_po_parser (~> 1.1.6)
aasm (6.0.0)
concurrent-ruby (~> 1.0)
actioncable (8.0.5.1)
actionpack (= 8.0.5.1)
activesupport (= 8.0.5.1)
action_text-trix (2.1.19)
railties
actioncable (8.1.4)
actionpack (= 8.1.4)
activesupport (= 8.1.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (8.0.5.1)
actionpack (= 8.0.5.1)
activejob (= 8.0.5.1)
activerecord (= 8.0.5.1)
activestorage (= 8.0.5.1)
activesupport (= 8.0.5.1)
actionmailbox (8.1.4)
actionpack (= 8.1.4)
activejob (= 8.1.4)
activerecord (= 8.1.4)
activestorage (= 8.1.4)
activesupport (= 8.1.4)
mail (>= 2.8.0)
actionmailer (8.0.5.1)
actionpack (= 8.0.5.1)
actionview (= 8.0.5.1)
activejob (= 8.0.5.1)
activesupport (= 8.0.5.1)
actionmailer (8.1.4)
actionpack (= 8.1.4)
actionview (= 8.1.4)
activejob (= 8.1.4)
activesupport (= 8.1.4)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
actionpack (8.0.5.1)
actionview (= 8.0.5.1)
activesupport (= 8.0.5.1)
actionpack (8.1.4)
actionview (= 8.1.4)
activesupport (= 8.1.4)
nokogiri (>= 1.8.5)
rack (>= 2.2.4)
rack-session (>= 1.0.1)
@@ -62,27 +64,28 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (8.0.5.1)
actionpack (= 8.0.5.1)
activerecord (= 8.0.5.1)
activestorage (= 8.0.5.1)
activesupport (= 8.0.5.1)
actiontext (8.1.4)
action_text-trix (~> 2.1.15)
actionpack (= 8.1.4)
activerecord (= 8.1.4)
activestorage (= 8.1.4)
activesupport (= 8.1.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (8.0.5.1)
activesupport (= 8.0.5.1)
actionview (8.1.4)
activesupport (= 8.1.4)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (8.0.5.1)
activesupport (= 8.0.5.1)
activejob (8.1.4)
activesupport (= 8.1.4)
globalid (>= 0.3.6)
activemodel (8.0.5.1)
activesupport (= 8.0.5.1)
activerecord (8.0.5.1)
activemodel (= 8.0.5.1)
activesupport (= 8.0.5.1)
activemodel (8.1.4)
activesupport (= 8.1.4)
activerecord (8.1.4)
activemodel (= 8.1.4)
activesupport (= 8.1.4)
timeout (>= 0.4.0)
activerecord-import (2.3.0)
activerecord (>= 4.2)
@@ -92,20 +95,20 @@ GEM
cgi (>= 0.3.6)
rack (>= 2.0.8, < 4)
railties (>= 7.1)
activestorage (8.0.5.1)
actionpack (= 8.0.5.1)
activejob (= 8.0.5.1)
activerecord (= 8.0.5.1)
activesupport (= 8.0.5.1)
activestorage (8.1.4)
actionpack (= 8.1.4)
activejob (= 8.1.4)
activerecord (= 8.1.4)
activesupport (= 8.1.4)
marcel (~> 1.0)
activesupport (8.0.5.1)
activesupport (8.1.4)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
json
logger (>= 1.4.2)
minitest (>= 5.1)
securerandom (>= 0.3)
@@ -130,8 +133,8 @@ GEM
autoprefixer-rails (10.4.21.0)
execjs (~> 2)
aws-eventstream (1.4.0)
aws-partitions (1.1282.0)
aws-sdk-core (3.254.1)
aws-partitions (1.1292.0)
aws-sdk-core (3.257.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
@@ -139,11 +142,12 @@ GEM
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.130.0)
aws-sdk-core (~> 3, >= 3.254.0)
rexml (~> 3.4, >= 3.4.2)
aws-sdk-kms (1.132.0)
aws-sdk-core (~> 3, >= 3.256.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.229.0)
aws-sdk-core (~> 3, >= 3.254.1)
aws-sdk-s3 (1.233.1)
aws-sdk-core (~> 3, >= 3.256.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
@@ -153,16 +157,16 @@ GEM
samovar (~> 2.1)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.1.2)
bigdecimal (4.1.3)
bindata (2.5.1)
binding_of_caller (2.0.0)
debug_inspector (>= 1.2.0)
biz (1.8.2)
clavius (~> 1.0)
tzinfo
bootsnap (1.25.0)
bootsnap (1.26.0)
msgpack (~> 1.5)
brakeman (8.0.6)
brakeman (8.1.0)
racc
browser (6.2.0)
builder (3.3.0)
@@ -177,7 +181,7 @@ GEM
rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
capybara-playwright-driver (0.5.10)
capybara-playwright-driver (0.5.12)
addressable
capybara
playwright-ruby-client (>= 1.16.0)
@@ -202,7 +206,7 @@ GEM
coffee-script-source (1.12.2)
concurrent-ruby (1.3.8)
connection_pool (3.0.2)
console (1.37.0)
console (1.38.0)
fiber-annotation
fiber-local (~> 1.1)
json
@@ -215,7 +219,7 @@ GEM
crass (1.0.7)
csv (3.3.6)
daemons (1.4.1)
dalli (5.1.0)
dalli (5.2.2)
logger
dartsass-rails (0.5.1)
railties (>= 6.0.0)
@@ -229,7 +233,7 @@ GEM
activesupport (>= 7.0)
diff-lcs (1.6.2)
diffy (3.4.4)
doorkeeper (5.9.6)
doorkeeper (5.9.9)
railties (>= 5)
drb (2.2.3)
dry-cli (1.4.1)
@@ -286,7 +290,7 @@ GEM
railties (>= 6.1.0)
faker (3.8.0)
i18n (>= 1.8.11, < 2)
faraday (2.14.3)
faraday (2.14.4)
faraday-net_http (>= 2.0, < 3.5)
json
logger
@@ -300,6 +304,10 @@ GEM
faraday-net_http (3.4.4)
net-http (~> 0.5)
ffi (1.17.4)
ffi (1.17.4-aarch64-linux-gnu)
ffi (1.17.4-arm64-darwin)
ffi (1.17.4-x86_64-darwin)
ffi (1.17.4-x86_64-linux-gnu)
ffi-compiler (1.4.2)
ffi (>= 1.15.5)
rake
@@ -311,10 +319,22 @@ GEM
ostruct
globalid (1.4.0)
activesupport (>= 6.1)
google-protobuf (4.36.0)
google-protobuf (4.36.2)
bigdecimal
rake (~> 13.3)
graphql (2.6.10)
google-protobuf (4.36.2-aarch64-linux-gnu)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.2-arm64-darwin)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.2-x86_64-darwin)
bigdecimal
rake (~> 13.3)
google-protobuf (4.36.2-x86_64-linux-gnu)
bigdecimal
rake (~> 13.3)
graphql (2.6.11)
base64
fiber-storage
logger
@@ -330,15 +350,11 @@ GEM
redis-client (= 0.30.1)
htmlentities (4.4.2)
http_parser.rb (0.8.1)
httparty (0.24.2)
csv
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
httpclient (2.9.0)
mutex_m
i18n (1.15.2)
concurrent-ruby (~> 1.0)
icalendar (2.12.4)
icalendar (2.12.5)
base64
ice_cube (~> 0.16)
logger
@@ -352,14 +368,14 @@ GEM
inflection (1.0.0)
iniparse (1.5.0)
interception (0.5)
io-console (0.9.2)
io-console (0.9.4)
irb (1.18.0)
pp (>= 0.6.0)
prism (>= 1.3.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jmespath (1.6.2)
json (2.21.2)
json (3.0.2)
json-jwt (1.17.2)
activesupport (>= 4.2)
aes_key_wrap
@@ -367,7 +383,7 @@ GEM
bindata
faraday (~> 2.0)
faraday-follow_redirects
jwt (3.2.0)
jwt (3.3.0)
base64
koala (3.7.0)
addressable
@@ -380,7 +396,7 @@ GEM
rexml
language_server-protocol (3.17.0.6)
lint_roller (1.1.0)
listen (3.10.0)
listen (3.10.1)
logger
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
@@ -410,14 +426,14 @@ GEM
mime-types (3.7.0)
logger
mime-types-data (~> 3.2025, >= 3.2025.0507)
mime-types-data (3.2026.0701)
mime-types-data (3.2026.0929)
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (6.0.6)
drb (~> 2.0)
prism (~> 1.5)
msgpack (1.8.4)
multi_json (1.21.1)
msgpack (1.8.5)
multi_json (1.21.2)
multi_xml (0.9.1)
bigdecimal (>= 3.1, < 5)
multipart-post (2.4.1)
@@ -425,7 +441,7 @@ GEM
nestful (1.1.4)
net-http (0.9.1)
uri (>= 0.11.1)
net-imap (0.6.6)
net-imap (0.6.7)
date
net-protocol
net-ldap (0.20.0)
@@ -433,15 +449,23 @@ GEM
ostruct
net-pop (0.1.2)
net-protocol
net-protocol (0.3.0)
net-protocol (0.4.0)
timeout
net-smtp (0.5.1)
net-smtp (0.5.2)
net-protocol
nio4r (2.7.5)
nkf (0.3.0)
nokogiri (1.19.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-arm64-darwin)
racc (~> 1.4)
nokogiri (1.19.4-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.19.4-x86_64-linux-gnu)
racc (~> 1.4)
nori (2.9.1)
bigdecimal
stringio
@@ -480,8 +504,8 @@ GEM
omniauth-gitlab (4.1.0)
omniauth (~> 2.0)
omniauth-oauth2 (~> 1.8.0)
omniauth-google-oauth2 (1.2.2)
jwt (>= 2.9.2)
omniauth-google-oauth2 (1.2.3)
jwt (>= 2.9.2, < 4)
oauth2 (~> 2.0)
omniauth (~> 2.0)
omniauth-oauth2 (~> 1.8)
@@ -529,16 +553,20 @@ GEM
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
ostruct (0.6.3)
overcommit (0.72.0)
overcommit (0.73.0)
childprocess (>= 0.6.3, < 6)
iniparse (~> 1.4)
rexml (>= 3.4.2)
parallel (2.1.0)
parallel (2.3.0)
parser (3.3.12.0)
ast (~> 2.4.1)
racc
pg (1.6.3)
playwright-ruby-client (1.62.0)
pg (1.7.0)
pg (1.7.0-aarch64-linux)
pg (1.7.0-arm64-darwin)
pg (1.7.0-x86_64-darwin)
pg (1.7.0-x86_64-linux)
playwright-ruby-client (1.63.0)
base64
concurrent-ruby (>= 1.1.6)
mime-types (>= 3.0)
@@ -592,7 +620,7 @@ GEM
base64 (>= 0.1.0)
logger (>= 1.6.0)
rack (>= 3.0.0, < 4)
rack-proxy (1.0.1)
rack-proxy (2.0.1)
rack (>= 2.0, < 4)
rack-session (2.1.2)
base64 (>= 0.1.0)
@@ -601,20 +629,20 @@ GEM
rack (>= 1.3)
rackup (2.3.1)
rack (>= 3)
rails (8.0.5.1)
actioncable (= 8.0.5.1)
actionmailbox (= 8.0.5.1)
actionmailer (= 8.0.5.1)
actionpack (= 8.0.5.1)
actiontext (= 8.0.5.1)
actionview (= 8.0.5.1)
activejob (= 8.0.5.1)
activemodel (= 8.0.5.1)
activerecord (= 8.0.5.1)
activestorage (= 8.0.5.1)
activesupport (= 8.0.5.1)
rails (8.1.4)
actioncable (= 8.1.4)
actionmailbox (= 8.1.4)
actionmailer (= 8.1.4)
actionpack (= 8.1.4)
actiontext (= 8.1.4)
actionview (= 8.1.4)
activejob (= 8.1.4)
activemodel (= 8.1.4)
activerecord (= 8.1.4)
activestorage (= 8.1.4)
activesupport (= 8.1.4)
bundler (>= 1.15.0)
railties (= 8.0.5.1)
railties (= 8.1.4)
rails-dom-testing (2.3.0)
activesupport (>= 5.0.0)
minitest
@@ -622,9 +650,9 @@ GEM
rails-html-sanitizer (1.7.1)
loofah (~> 2.25, >= 2.25.2)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
railties (8.0.5.1)
actionpack (= 8.0.5.1)
activesupport (= 8.0.5.1)
railties (8.1.4)
actionpack (= 8.1.4)
activesupport (= 8.1.4)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
@@ -641,7 +669,7 @@ GEM
prism (>= 1.6.0)
tsort
rchardet (1.10.2)
rdoc (8.0.0)
rdoc (8.1.0)
erb
prism (>= 1.6.0)
rbs (>= 4.0.0)
@@ -650,7 +678,7 @@ GEM
redis-client (>= 0.22.0)
redis-client (0.30.1)
connection_pool
regexp_parser (2.12.0)
regexp_parser (2.13.1)
reline (0.7.0)
io-console (~> 0.5)
rexml (3.4.4)
@@ -674,8 +702,10 @@ GEM
rspec-retry (0.6.2)
rspec-core (> 3.3)
rspec-support (3.13.7)
rspec_junit_formatter (0.6.0)
rspec-core (>= 2, < 4, != 2.12.0)
rszr (1.5.0)
rubocop (1.90.0)
rubocop (1.91.0)
json (>= 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@@ -710,7 +740,7 @@ GEM
lint_roller (~> 1.1)
rubocop (>= 1.89.0, < 2.0)
rubocop-ast (>= 1.47.1, < 2.0)
rubocop-rails (2.37.0)
rubocop-rails (2.38.0)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
@@ -727,9 +757,6 @@ GEM
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec (~> 3.5)
ruby-keycloak-admin (26.5.2)
hashie (>= 5.0.0)
httparty (>= 0.21, < 0.25)
ruby-lsp (0.26.11)
language_server-protocol (~> 3.17.0)
prism (>= 1.2, < 2.0)
@@ -742,27 +769,34 @@ GEM
ruby-saml (1.18.1)
nokogiri (>= 1.13.10)
rexml
rubyntlm (0.6.5)
rubyntlm (0.6.8)
base64
rubyzip (3.5.0)
rubyzip (3.7.0)
safety_net_attestation (0.5.0)
jwt (>= 2.0, < 4.0)
samovar (2.5.1)
console (~> 1.0)
sass-embedded (1.103.1)
sass-embedded (1.105.1)
google-protobuf (~> 4.31)
rake (~> 13.3)
sass-embedded (1.105.1-aarch64-linux-gnu)
google-protobuf (~> 4.31)
sass-embedded (1.105.1-arm64-darwin)
google-protobuf (~> 4.31)
sass-embedded (1.105.1-x86_64-darwin)
google-protobuf (~> 4.31)
sass-embedded (1.105.1-x86_64-linux-gnu)
google-protobuf (~> 4.31)
rake (>= 13)
securerandom (0.4.1)
selenium-webdriver (4.48.0)
selenium-webdriver (4.50.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0)
shoulda-matchers (8.0.1)
activesupport (>= 7.2)
simple_po_parser (1.1.6)
simpleidn (0.2.3)
simpleidn (0.3.0)
slack-ruby-client (3.2.0)
faraday (>= 2.0.1)
faraday-mashify
@@ -807,7 +841,7 @@ GEM
openssl (> 2.0)
openssl-signature_algorithm (~> 1.0)
tsort (0.2.0)
twilio-ruby (7.11.1)
twilio-ruby (7.11.3)
faraday (>= 2.0, < 3.0)
jwt (>= 1.5, < 4.0)
nokogiri (>= 1.6, < 2.0)
@@ -818,11 +852,11 @@ GEM
tzinfo
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2026.3)
tzinfo-data (1.2026.5)
tzinfo (>= 1.0.0)
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.2.0)
unicode-display_width (3.3.0)
unicode-emoji (~> 4.3)
unicode-emoji (4.3.0)
uri (1.1.1)
useragent (0.16.11)
validate_url (1.0.15)
@@ -840,7 +874,7 @@ GEM
vite_rails (3.11.1)
railties (>= 5.1, < 9)
vite_ruby (~> 3.0, >= 3.2.2)
vite_ruby (3.10.5)
vite_ruby (3.11.1)
dry-cli (>= 0.7, < 2)
logger (~> 1.6)
mutex_m
@@ -867,11 +901,11 @@ GEM
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
whatsapp_sdk (1.1.0)
whatsapp_sdk (1.2.0)
faraday (~> 2.0, > 2.0.1)
faraday-multipart (~> 1)
zeitwerk (~> 2)
write_xlsx (1.15.0)
write_xlsx (1.15.1)
nkf
rubyzip (>= 2.4.0, < 4.0)
xpath (3.2.0)
@@ -888,7 +922,11 @@ GEM
multipart-post (~> 2.0)
PLATFORMS
aarch64-linux-gnu
arm64-darwin
ruby
x86_64-darwin
x86_64-linux-gnu
DEPENDENCIES
PoParser
@@ -900,6 +938,7 @@ DEPENDENCIES
autodiscover!
autoprefixer-rails
aws-sdk-s3
benchmark
biz
bootsnap
brakeman
@@ -965,6 +1004,7 @@ DEPENDENCIES
overcommit
parallel
pg (~> 1.5, >= 1.5.4)
playwright-ruby-client
pry-doc
pry-rails
pry-remote
@@ -975,13 +1015,14 @@ DEPENDENCIES
pundit
pundit-matchers
rack-attack
rails (~> 8.0.0)
rails (~> 8.1.0)
rake
rchardet (>= 1.8.0)
redis (< 6)
rotp
rspec-rails
rspec-retry
rspec_junit_formatter
rszr
rubocop
rubocop-capybara
@@ -994,7 +1035,6 @@ DEPENDENCIES
rubocop-rake
rubocop-rspec
rubocop-rspec_rails
ruby-keycloak-admin
ruby-lsp
ruby-lsp-rails
ruby-lsp-rspec

View File

@@ -133,6 +133,12 @@ Copyright: Faruk Ates (https://twitter.com/KuraFire)
Richard Herrera (https://twitter.com/doctyper)
License: MIT license & BSD license
-----------------------------------------------------------------------------
playwright_visible_text.js
Source: https://github.com/SeleniumHQ/selenium
Copyright: 2011-2026 Software Freedom Conservancy
2004-2011 Selenium committers
License: Apache License, Version 2.0
-----------------------------------------------------------------------------
qrcodegen.js
Source: https://www.nayuki.io/page/qr-code-generator-library
Copyright: 2022, Project Nayuki

View File

@@ -1 +1 @@
7.2.x
7.3.x

View File

@@ -39,10 +39,10 @@ class App.ControllerAIFeatureBase extends App.ControllerSubContent
# Injects a provider modal into .page-header-meta for the given feature identifier;
# selecting the default entry deletes the feature's routing row. Shown only to provider
# administrators — the backing APIs require admin.ai_provider.
# administrators — the backing APIs require admin.ai_provider. The AI provider switch does not
# matter here: routing is configuration and may be prepared while the switch is off.
renderProviderModal: (identifier) =>
return unless identifier
return unless App.Config.get('ai_provider')
return unless @permissionCheck('admin.ai_provider')
@el.find('.js-featureProviderButton').remove()

View File

@@ -41,27 +41,15 @@ class App.ControllerAIFeatureProviderModal extends App.ControllerModal
@controller?.releaseController()
@controller = new App.ControllerForm(
model: { configure_attributes: @buildProviderAttributes() }
params: @row
params: { provider_connection_id: App.AIFeatureProvider.valueFor(@row) }
autofocus: true
)
@controller.form
buildProviderAttributes: =>
defaultChatProvider = _.find(@providers, (provider) -> provider.default_chat)
options = _.map(@providers, (c) ->
value: c.id
name: c.name
)
options.unshift(
value: ''
name: App.i18n.translateInline('Default (%s)', defaultChatProvider?.name or App.i18n.translateInline('none'))
)
[
{ name: 'provider_connection_id', display: __('Provider'), tag: 'select', null: false, options: options }
{ name: 'provider_connection_id', display: __('Provider'), tag: 'select', null: false, options: App.AIFeatureProvider.options(@providers) }
]
onSubmit: (e) =>
@@ -71,49 +59,20 @@ class App.ControllerAIFeatureProviderModal extends App.ControllerModal
@formDisable(e)
params = @formParam(e.target)
connectionId = parseInt(params.provider_connection_id, 10) or null
@formEnable(e)
done = =>
@notify(
type: 'success'
msg: App.i18n.translateInline('Provider updated successfully.')
)
@close()
fail = (data) =>
details = data?.responseJSON or {}
@notify(
type: 'error'
msg: details.error_human or details.error or __('The provider could not be updated.')
)
if not connectionId
return @close() if not @row?.id
# Back to the default connection = remove the routing row.
@ajax(
id: 'remove_feature_provider'
type: 'DELETE'
url: App.Config.get('api_path') + '/ai/feature_providers/' + @row.id
success: done
error: fail
)
else if @row?.id
@ajax(
id: 'update_feature_provider'
type: 'PUT'
url: App.Config.get('api_path') + '/ai/feature_providers/' + @row.id
data: JSON.stringify(provider_connection_id: connectionId)
success: done
error: fail
)
else
@ajax(
id: 'create_feature_provider'
type: 'POST'
url: App.Config.get('api_path') + '/ai/feature_providers'
data: JSON.stringify(identifier: @featureIdentifier, provider_connection_id: connectionId)
success: done
error: fail
)
App.AIFeatureProvider.save(
identifier: @featureIdentifier
row: @row
value: params.provider_connection_id
success: =>
@notify(
type: 'success'
msg: App.i18n.translateInline('Provider updated successfully.')
)
@close()
error: (msg) =>
@formEnable(e)
@notify(
type: 'error'
msg: msg
)
)

View File

@@ -57,7 +57,9 @@ class App.AIFeedbackWidget extends App.Controller
submitPositiveReaction: (e) ->
@preventDefault(e)
@recordUsage(rating: true, null, =>
@recordUsage(rating: true, null, (data) =>
return false if @feedbackAlreadyProvided(data)
@hideAcknowledgment()
@showQuestionAndButtons()
)
@@ -68,9 +70,14 @@ class App.AIFeedbackWidget extends App.Controller
submitNegativeReaction: (e) ->
@preventDefault(e)
@recordUsage(rating: false, null, =>
@recordUsage(rating: false, null, (data) =>
@hideComment()
@showToolbar()
if @feedbackAlreadyProvided(data)
@showAcknowledgment()
return false
@showQuestionAndButtons()
)
@@ -94,7 +101,9 @@ class App.AIFeedbackWidget extends App.Controller
@preventDefault(e)
if commentText = @comment.find('textarea').val()
@recordUsage(comment: commentText, null, =>
@recordUsage(comment: commentText, null, (data) =>
return false if @feedbackAlreadyProvided(data)
@hideAcknowledgment()
@hideToolbar()
@showComment()
@@ -104,6 +113,10 @@ class App.AIFeedbackWidget extends App.Controller
@showToolbar()
@showAcknowledgment()
# Feedback may have been given meanwhile in another view of the same result.
feedbackAlreadyProvided: (data) ->
!!data.responseJSON?.feedback_already_provided
showQuestionAndButtons: ->
@question.removeClass('hide')
@buttons.removeClass('hide')

View File

@@ -27,38 +27,10 @@ class FeedbackAndLogs extends App.ControllerAIFeatureBase
# Feedback & Logs works independently of any configured provider connection.
showAlert: -> false
sendDownloadRequest: (type) ->
isFeedback = type is 'with_usages'
downloadFeedback: ->
App.AIAnalyticsDownload.request(type: 'with_usages', button: @$('.js-downloadFeedback'))
button = @$(if isFeedback then '.js-downloadFeedback' else '.js-downloadErrorLogs')
disableButton = (disabled) -> button.prop('disabled', disabled)
disableButton(true)
fallbackFilename = if isFeedback then 'ai_analytics_with_usages.xlsx' else 'ai_analytics_errors.xlsx'
App.Ajax.request(
id: 'ai-analytics-download'
type: 'GET'
url: "#{@apiPath}/ai/analytics/download/#{type}"
processData: true
dataType: 'binary'
contentType: 'application/octet-stream'
xhrFields:
responseType: 'blob'
success: (data, status, xhr) ->
App.Utils.downloadFileFromBlob(data, xhr, { fallbackFilename: fallbackFilename })
disableButton(false)
error: (xhr, status, error) =>
@log 'error', error || status
@notify(
type: 'error'
msg: __('The download could not be started. Please try again later.')
)
disableButton(false)
)
downloadFeedback: -> @sendDownloadRequest('with_usages')
downloadErrorLogs: -> @sendDownloadRequest('errors')
downloadErrorLogs: ->
App.AIAnalyticsDownload.request(type: 'errors', button: @$('.js-downloadErrorLogs'))
App.Config.set('FeedbackAndLogs', { prio: 1060, name: __('Feedback & Logs'), parent: '#ai', target: '#ai/feedback_logs', controller: FeedbackAndLogs, permission: ['admin.ai_feedback_logs'] }, 'NavBarAdmin')

View File

@@ -513,18 +513,27 @@ ProviderConnectionFormMixin =
# Type and name head the credential step, and only it: the provider is picked before anything
# is fetched with it, and the model step must not offer to change it behind the fetched list.
providerSelectionAttributes: (providerKey, excludeProviders) ->
sortedOptions = {}
Object
.entries(App.Config.get('AIProviders'))
.filter(([key, _]) -> key not in excludeProviders)
.sort(([_, a], [__, b]) -> a.prio - b.prio)
.forEach(([key, { label }]) -> sortedOptions[key] = label)
attributes = []
# Provider comes first: selecting it pre-fills the name below.
[
{ name: 'provider', display: __('Type'), tag: 'select', options: sortedOptions, null: false, nulloption: true, value: providerKey, customsort: 'on' }
if providerKey isnt 'zammad_ai' or not App.Config.get('system_online_service')
sortedOptions = {}
Object
.entries(App.Config.get('AIProviders'))
.filter(([key, _]) -> key not in excludeProviders)
.sort(([_, a], [__, b]) -> a.prio - b.prio)
.forEach(([key, { label }]) -> sortedOptions[key] = label)
# Provider comes first: selecting it pre-fills the name below.
attributes.push(
{ name: 'provider', display: __('Type'), tag: 'select', options: sortedOptions, null: false, nulloption: true, value: providerKey, customsort: 'on' }
)
attributes.push(
{ name: 'name', display: __('Name'), tag: 'input', type: 'text', null: false, limit: 100, autocomplete: 'one-time-code' }
]
)
attributes
# The provider fields are built dynamically ('config.token', ...), so they are not part of
# App.AIProviderConnection.configure_attributes — App.Model#validate resolves the attributes
@@ -646,8 +655,8 @@ class ProviderConnectionCredentials extends App.ControllerModal
@buttonSubmit = if lastStep then __('Submit') else __('Next')
@buttonClass = if lastStep then 'btn--success' else 'btn--primary'
# Only when creating: an existing Zammad AI connection stays editable on SaaS.
excludeProviders = if !@id and App.Config.get('system_online_service') then ['zammad_ai'] else []
# Provider dropdown: hide Zammad AI from the list in SaaS.
excludeProviders = if App.Config.get('system_online_service') then ['zammad_ai'] else []
# Merge unsaved edits (captured on a provider change, and on the way back from the model step)
# over the persisted values, so switching the provider keeps them for all compatible fields.
@@ -680,6 +689,13 @@ class ProviderConnectionCredentials extends App.ControllerModal
@update()
@$('[name=name]').focus()
if @providerKey is 'zammad_ai' and App.Config.get('system_online_service')
$('<div />')
.addClass('alert alert--warning')
.attr('role', 'alert')
.text(App.i18n.translatePlain('The Zammad AI provider is managed by the platform and cannot be fully edited here.'))
.prependTo(@controller.form)
@controller.form
onSubmit: (e) =>

View File

@@ -67,34 +67,18 @@ class TextTool extends App.ControllerAIFeatureBase
@controllerBind('config_update', @configHasChanged)
downloadFeedbackReport: (id) =>
downloadFeedbackReport: (id) ->
text_tool = App.AITextTool.find(id)
url = "#{@apiPath}/ai/analytics/download/with_usages?filters[triggered_by_type]=AI::TextTool&filters[triggered_by_id]=#{id}"
filters =
triggered_by_type: 'AI::TextTool'
triggered_by_id: id
# Only limit the report to feedback since the last reset if one happened;
# otherwise the report should collect everything.
if text_tool.analytics_stats_reset_at
url += "&filters[created_after]=#{text_tool.analytics_stats_reset_at}"
filters.created_after = text_tool.analytics_stats_reset_at if text_tool.analytics_stats_reset_at
@ajax(
id: 'download_feedback_report'
type: 'GET'
url: url
processData: true
dataType: 'binary'
contentType: 'application/octet-stream'
xhrFields:
responseType: 'blob'
success: (data, status, xhr) ->
App.Utils.downloadFileFromBlob(data, xhr, { fallbackFilename: 'ai_analytics_with_usages.xlsx' })
error: (xhr, status, error) =>
@log 'error', error || status
@notify(
type: 'error'
msg: __('The download could not be started. Please try again later.')
)
)
App.AIAnalyticsDownload.request(type: 'with_usages', filters: filters)
resetFeedbackTimestamp: (id) =>
@ajax(

View File

@@ -40,6 +40,12 @@ class App.Controller extends Spine.Controller
oldLocation = history[history.length-2]
if oldLocation
window.history.replaceState(null, null, oldLocation)
else
# Direct entry, e. g. a bookmark: there is no previous route to fall back to, so
# replace the current entry instead of adding one. The hash still changes, so the
# target route is executed by the resulting hashchange event.
window.location.replace(location.replace(/^#*/, '#'))
return
super location
preventDefault: (e) ->

View File

@@ -18,6 +18,7 @@ class App.ControllerModal extends App.Controller
buttonSubmit: true
includeForm: true
headPrefix: ''
headMarkup: true
headIcon: null
headIconClass: null
shown: true
@@ -77,6 +78,7 @@ class App.ControllerModal extends App.Controller
modal = $(App.view('modal')(
head: @head
headPrefix: @headPrefix
headMarkup: @headMarkup
headIcon: @headIcon
headIconClass: @headIconClass
message: @message

View File

@@ -35,6 +35,7 @@ class App.ControllerTabs extends App.Controller
@html App.view('generic/tabs')(
header: @header
description: @description
subHeader: @subHeader
tabs: displayTabs
addTab: @addTab

View File

@@ -0,0 +1,368 @@
# Copyright (C) 2012-2026 Zammad Foundation, https://zammad-foundation.org/
class ContentTranslation extends App.ControllerTabs
@requiredPermission: 'admin.integration'
header: __('Translation services')
description: __('This service allows agents to translate articles to their configured UI language or other languages of their choice.')
constructor: ->
super
@title __('Translation services'), true
@tabs = [
{ name: __('Provider Settings'), target: 'provider-settings', controller: ProviderSettings }
{ name: __('Ticket Articles'), target: 'ticket-articles', controller: TicketArticles }
{ name: __('Logs'), target: 'logs', controller: Logs }
{ name: __('Feedback'), target: 'feedback', controller: Feedback }
]
# The tabs read settings while rendering, and App.ControllerTabs#render never releases the
# tab controllers, so the page is rendered once after the settings are loaded.
App.Setting.fetchFull((=> @render()), force: false)
class ProviderSettings extends App.Controller
events:
'submit form': 'submit'
'change [name=provider]': 'changeProvider'
constructor: ->
super
@config = App.Setting.get('content_translation_service_config') || {}
# The connection may be chosen while the AI provider is switched off: routing is configuration,
# the switch only decides whether it is used.
if @permissionCheck('admin.ai_provider')
@fetchAIFeatureProviders()
else
@render()
# The connections to choose from.
fetchAIFeatureProviders: =>
App.AIProviderConnection.fetchFull(
=>
@ajax(
id: 'content_translation_ai_feature_providers'
type: 'GET'
url: "#{App.Config.get('api_path')}/ai/feature_providers"
success: (rows) =>
@aiFeatureProviders = rows
@render()
error: =>
@notify(
type: 'error'
msg: __('The providers could not be loaded.')
)
@render()
)
force: false
)
render: =>
provider = @availableProvider()
attributes = @configurationAttributes(provider)
@html App.view('integration/content_translation_provider_settings')(attributes: attributes)
# The AI service stays selectable while the AI provider is switched off, so that its
# configuration survives the switch. The alert says why nothing translates in the meantime.
if @service('ai').available() and provider is 'ai' and not App.Config.get('ai_provider')
@el.prepend(App.view('ai/missing_provider_alert')(visible: true))
defaults = { ai_provider_connection_id: App.AIFeatureProvider.valueFor(@aiFeatureProvider(provider)) }
params = _.extend(defaults, @config, provider: provider)
@forms = [
new App.ControllerForm(
el: @$('.js-serviceForm')
model: { configure_attributes: [@serviceAttribute()] }
params: params
autofocus: false
)
]
for attribute in attributes
@forms.push(new App.ControllerForm(
el: @$("[data-name='#{attribute.name}']")
model: { configure_attributes: [_.extend({}, attribute, class: 'form-control--small', label_class: 'sr-only')] }
params: params
autofocus: false
))
# Only the service and the AI connection survive the switch: credentials belong to the service
# they were entered for, so a field both services ask for, like the API key, would stay filled.
changeProvider: =>
params = _.extend({}, @config, @formParam(@$('form')))
@config = _.pick(params, 'provider', 'ai_provider_connection_id')
@render()
service: (key) ->
App.Config.get('ContentTranslationServices')[key]
# Only a service that is still offered is kept as the selection: a service that is gone would
# otherwise end up in the select as its raw value.
availableProvider: ->
provider = @config.provider
return if !provider
return if !@service(provider)?.available()
provider
serviceOptions: ->
services = _.filter(_.values(App.Config.get('ContentTranslationServices')), (service) -> service.available())
_.map(_.sortBy(services, (service) -> service.prio), (service) -> { name: service.label, value: service.key })
# customsort keeps the services in the prio order the options were built in.
serviceAttribute: ->
{ name: 'provider', display: __('Translation service provider'), tag: 'select', options: @serviceOptions(), null: true, nulloption: true, translate: true, customsort: 'on' }
configurationAttributes: (provider) ->
(@service(provider)?.credential_attributes or []).concat(@aiProviderAttributes(provider))
# The AI feature the given service runs as, declared on the service entry.
aiFeatureIdentifier: (provider) ->
@service(provider)?.ai_feature_identifier
# The routing row serving that feature, while the admin has pointed it at a connection.
aiFeatureProvider: (provider) ->
identifier = @aiFeatureIdentifier(provider)
return if !identifier
_.find(@aiFeatureProviders, (row) -> row.identifier is identifier)
aiProviderAttributes: (provider) ->
return [] if !@aiFeatureProviders
return [] if !@aiFeatureIdentifier(provider)
[
{ name: 'ai_provider_connection_id', display: __('Provider'), tag: 'select', options: @aiProviderOptions() }
]
aiProviderOptions: ->
App.AIFeatureProvider.options(App.AIProviderConnection.all())
submit: (e) =>
e.preventDefault()
params = @formParam(e.target)
errors = {}
for form in @forms
_.extend(errors, form.validate(params))
if !_.isEmpty(errors)
@formValidate(form: e.target, errors: errors)
return
# The routing row is written first: the settings announce the success of the whole form, so
# they must not run while the row this form also changed is still the old one.
@submitAIFeatureProvider(params.provider, params.ai_provider_connection_id, => @submitConfig(params))
# The flag follows the config only once the config is saved, so a refused config leaves the flag
# alone and the admin sees one error instead of an error next to a success.
submitConfig: (params) ->
# Without a service there is nothing to keep, not even the credentials of the previous one.
config = if params.provider then _.omit(params, 'ai_provider_connection_id') else {}
hasProvider = !!params.provider
form = @$('form')
App.ControllerForm.disable(form)
App.Setting.set('content_translation_service_config', config,
notify: false
failLocal: -> App.ControllerForm.enable(form)
doneLocal: =>
App.ControllerForm.enable(form)
if App.Config.get('content_translation_service') is hasProvider
@notify(type: 'success', msg: __('Update successful.'), timeout: 2000)
return
App.Setting.set('content_translation_service', hasProvider, notify: true)
)
# Calls back only once the row is written, so a failure stops the submit instead of ending in a
# success and an error notification next to each other.
submitAIFeatureProvider: (provider, value, done) =>
return done() if !value
identifier = @aiFeatureIdentifier(provider)
App.AIFeatureProvider.save(
identifier: identifier
row: @aiFeatureProvider(provider)
value: value
success: (updated) =>
@replaceAIFeatureProvider(identifier, updated)
done()
error: (msg) =>
@notify(type: 'error', msg: msg)
)
# Keeps the rows in step with what was just written, so a second submit updates the row it
# created instead of trying to create it again.
replaceAIFeatureProvider: (identifier, row) =>
@aiFeatureProviders = _.reject(@aiFeatureProviders, (existing) -> existing.identifier is identifier)
@aiFeatureProviders.push(row) if row
class TicketArticles extends App.Controller
events:
'change .js-ticketArticleSetting input': 'toggleTicketArticle'
'change .js-ticketArticleAutoSetting input': 'toggleTicketArticleAuto'
'submit form': 'submit'
elements:
'.js-ticketArticleSetting input': 'ticketArticleSetting'
'.js-ticketArticleAutoSetting input': 'ticketArticleAutoSetting'
constructor: ->
super
@render()
# Follow the switches in place instead of rendering again, so a pushed setting change (also the
# one a switch on this page caused) does not discard an unsaved role selection.
@controllerBind('config_update', (data) =>
switch data.name
when 'content_translation_ticket_article'
@ticketArticleSetting.prop('checked', App.Config.get(data.name))
when 'content_translation_ticket_article_auto'
@ticketArticleAutoSetting.prop('checked', App.Config.get(data.name))
)
# The roles and the language detection are no frontend settings, so they are followed via the
# collection. Only a role change from elsewhere is drawn; the own submit already shows what it saved.
@subscribeId = App.Setting.subscribe(@settingsChanged, initFetch: true, clear: false)
settingsChanged: =>
@renderAlert()
return if _.isEqual(App.Setting.get('content_translation_ticket_article_auto_role_ids'), @renderedRoleIds)
@render()
render: =>
@html App.view('integration/content_translation_ticket_articles')(
ticketArticle: App.Config.get('content_translation_ticket_article')
ticketArticleAuto: App.Config.get('content_translation_ticket_article_auto')
alert: App.i18n.translateContent('The article language detection is disabled. Enable it to avoid translating articles that are already in the target language. %l', '#settings/ticket')
)
@renderAlert()
@renderedRoleIds = App.Setting.get('content_translation_ticket_article_auto_role_ids')
new App.ControllerForm(
el: @$('.js-form')
model:
configure_attributes: [
{ name: 'content_translation_ticket_article_auto_role_ids', display: __('Available for the following roles'), tag: 'column_select', null: true, relation: 'Role', translate: true }
]
params:
content_translation_ticket_article_auto_role_ids: @renderedRoleIds
autofocus: false
)
toggleTicketArticle: =>
App.Setting.set('content_translation_ticket_article', @ticketArticleSetting.prop('checked'), failLocal: @render, notify: true)
toggleTicketArticleAuto: =>
App.Setting.set('content_translation_ticket_article_auto', @ticketArticleAutoSetting.prop('checked'), failLocal: @render, notify: true)
submit: (e) =>
e.preventDefault()
roleIds = @formParam(e.target).content_translation_ticket_article_auto_role_ids || []
# Known as shown before the request, so neither the local change nor the push redraws the form.
@renderedRoleIds = roleIds
App.Setting.set('content_translation_ticket_article_auto_role_ids', roleIds, failLocal: @render, notify: true)
renderAlert: =>
@$('.js-languageDetectionAlert').toggleClass('hide', !!App.Setting.get('language_detection_article'))
release: =>
App.Setting.unsubscribe(@subscribeId)
class Logs extends App.Controller
constructor: ->
super
@render()
@subscribeId = App.Setting.subscribe(@renderAiState, initFetch: true, clear: false)
render: =>
@html App.view('integration/content_translation_logs')(hint: @aiHint())
# The heading belongs to the tab, so it stays while the log itself makes way for the AI hint.
@httpLog = new App.HttpLog(
el: @$('.js-log')
facility: 'content_translation'
header: false
)
# Linked only for whoever may open that screen: the link would otherwise just bounce off
# permissionCheckRedirect.
aiHint: ->
if @permissionCheck('admin.ai_feedback_logs')
return App.i18n.translateContent('You can find relevant AI provider logs under AI > Feedback & Logs. %l', '#ai/feedback_logs')
App.i18n.translateContent('You can find relevant AI provider logs under AI > Feedback & Logs.')
renderAiState: =>
isAI = App.Setting.get('content_translation_service_config')?.provider is 'ai'
@$('.js-aiHint').toggleClass('hide', !isAI)
@$('.js-log').toggleClass('hide', isAI)
release: =>
App.Setting.unsubscribe(@subscribeId)
@httpLog?.releaseController()
@httpLog = null
class Feedback extends App.Controller
# The page asks for admin.integration only, but the download answers to admin.ai_feedback_logs, so
# App.ControllerTabs leaves out the tab for whoever lacks it instead of offering a failing button.
@requiredPermission: 'admin.ai_feedback_logs'
events:
'click .js-downloadFeedback': 'downloadFeedback'
constructor: ->
super
@render()
render: =>
@html App.view('integration/content_translation_feedback')()
# Hardcoded, as Service::ContentTranslation::StoredTranslation::IDENTIFIER does not reach the
# frontend. It covers the AI service and the others alike.
downloadFeedback: =>
App.AIAnalyticsDownload.request(
type: 'with_usages'
filters: { identifier: 'translate' }
button: @$('.js-downloadFeedback')
)
class State
@current: ->
not _.isEmpty(App.Setting.get('content_translation_service_config'))
App.Config.set(
'IntegrationContentTranslation'
{
name: __('Translation services')
target: '#system/integration/content_translation'
description: __('Translate ticket articles into another language.')
controller: ContentTranslation
state: State
permission: ['admin.integration']
}
'NavBarIntegrations'
)

View File

@@ -62,13 +62,10 @@ class Form extends App.Controller
)
# placeholder
configure_attributes = [
{ name: 'user_ids', display: '', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'displayName' },
]
new App.ControllerForm(
el: @$('.js-userSelectorBlank')
model:
configure_attributes: configure_attributes,
configure_attributes: @userSelectorAttributes()
params:
user_ids: []
autofocus: false
@@ -99,18 +96,35 @@ class Form extends App.Controller
)
for row in @config.notify_map
configure_attributes = [
{ name: 'user_ids', display: '', tag: 'column_select', multiple: true, null: true, relation: 'User', sortBy: 'displayName' },
]
new App.ControllerForm(
el: @$("[name=queue][value='#{row.queue}']").closest('tr').find('.js-userSelector')
model:
configure_attributes: configure_attributes,
configure_attributes: @userSelectorAttributes()
params:
user_ids: row.user_ids
autofocus: false
)
# Only a user with the cti.agent permission ever sees a caller log, so the
# filter offers no one else. The form runs it twice, over the records and
# over the built options, so an entry carries the user as id or as value.
userSelectorAttributes: ->
[
{
name: 'user_ids'
display: ''
tag: 'column_select'
multiple: true
null: true
relation: 'User'
sortBy: 'displayName'
filter: (entries) ->
entries.filter (entry) ->
user = App.User.find(entry.id || entry.value)
user && user.permission('cti.agent')
}
]
updateCurrentConfig: =>
config = @config
cleanupInput = @cleanupInput

View File

@@ -24,6 +24,16 @@ class Maintenance extends App.Controller
@maintenanceForceRefresh(data)
)
# Import mode is switched by the import controllers and background services,
# never via the websocket maintenance event, so react to the setting itself.
@controllerBind('config_update_local', (data) =>
return if data.name isnt 'import_mode'
return if @permissionCheck('admin.maintenance')
# A switched session is judged by the switching user, which only the backend knows.
return if @Config.get('switch_back_to_possible')
@maintenanceMode(on: data.value)
)
showMessage: (message = {}) =>
if message.reload
@disconnectClient()

View File

@@ -27,6 +27,10 @@ class App.UiElement.postmaster_set extends App.UiElement.ApplicationAction
if !row.readonly
config = _.clone(row)
# values may contain placeholders like #{regexp.NAME}, which fail the native field validation
if config.type is 'email' || config.type is 'tel' || config.type is 'url'
config.type = 'text'
switch config.tag
when 'date'
config.operator = ['static', 'relative']

View File

@@ -81,7 +81,7 @@ class App.UiElement.richtext.additions.RichTextToolPopupVideo extends App.UiElem
if !parsed.host
return "( widget: video, provider: #{parsed.provider}, id: #{parsed.id} )"
hostAllowed = _.some(App.Config.get('kb_self_hosted_video_servers'), (server) -> server.host == parsed.host)
hostAllowed = App.KnowledgeBaseVideo.hostAllowed(parsed.host)
if !hostAllowed
@applyError(__('Video server not allowed. Please add to the list of allowed video servers.'))

View File

@@ -19,7 +19,7 @@ class App.UiElement.sla_times
# apply hour picker
item.find('.js-timeConvertFrom').timepicker(
maxHours: 999
maxHours: 99999
)
# disable/enable rows

View File

@@ -866,7 +866,7 @@ class Router extends App.ControllerPermanent
split = "/query/#{params.query}"
id = Math.floor( Math.random() * 99999 )
@navigate "#ticket/create/id/#{id}#{split}"
@navigate "#ticket/create/id/#{id}#{split}", { hideCurrentLocationFromHistory: true }
return
# check authentication

View File

@@ -72,6 +72,7 @@ class SidebarCustomer extends App.Controller
object: __('User')
objects: __('Users')
container: @elSidebar.closest('.content')
handlers: [App.FormHandlerUserPasswordChangeHint.run]
)
App.Config.set('200-Customer', SidebarCustomer, 'TicketCreateSidebar')

View File

@@ -120,6 +120,11 @@ class App.CustomerChat extends App.Controller
chat_sessions: list
)
# Bootstrap's popover sanitizer has no <time> in its allowlist and would drop
# the relative time rendered by the humanTime helper together with its text.
chatSessionListWhiteList: ->
$.extend({}, $.fn.popover.Constructor.DEFAULTS.whiteList, time: ['datetime', 'title'])
show: (params) =>
@title(__('Customer Chat'), true)
@@ -253,6 +258,7 @@ class App.CustomerChat extends App.Controller
@popovers.push @el.find(".js-waitingCustomers[data-chat-id=#{chat.id}] .js-info").popover(
trigger: 'hover'
html: true
whiteList: @chatSessionListWhiteList()
animation: false
delay: 0
placement: 'bottom'
@@ -267,6 +273,7 @@ class App.CustomerChat extends App.Controller
@popovers.push @el.find('.js-waitingCustomers .js-totalInfo').popover(
trigger: 'hover'
html: true
whiteList: @chatSessionListWhiteList()
animation: false
delay: 0
placement: 'bottom'
@@ -289,6 +296,7 @@ class App.CustomerChat extends App.Controller
@popovers.push @el.find('.js-chattingCustomers .js-info').popover(
trigger: 'hover'
html: true
whiteList: @chatSessionListWhiteList()
animation: false
delay: 0
placement: 'bottom'

View File

@@ -35,7 +35,7 @@ class Success extends App.ControllerAppContent
detail: __('Woo hoo! Your email address has been verified!')
)
delay = =>
App.Auth.loginCheck(=> @navigate '#')
@navigate '#login'
@delay(delay, 2000)
class Fail extends App.ControllerAppContent

View File

@@ -6,6 +6,11 @@ class GettingStartedAgent extends App.ControllerWizardFullScreen
super
@authenticateCheckRedirect()
# redirect if we are not admin
if !@permissionCheck('admin.wizard')
@navigate '#'
return
# set title
@title __('Invite Agents')
@fetch()

View File

@@ -2,6 +2,12 @@ class Import extends App.ControllerWizardFullScreen
constructor: ->
super
@title __('Import')
# redirect to login if admin user already exists
if @Config.get('system_init_done')
@navigate '#login'
return
@fetch()
fetch: ->
@@ -14,11 +20,6 @@ class Import extends App.ControllerWizardFullScreen
processData: true,
success: (data, status, xhr) =>
# redirect to login if admin user already exists
if @Config.get('system_init_done')
@navigate '#login'
return
if data.import_mode == true
@navigate '#import/' + data.import_backend, { emptyEl: true }
return

View File

@@ -15,6 +15,10 @@ class App.KnowledgeBaseAgentController extends App.Controller
App.Event.bind 'kb_visibility_may_have_changed', @visibilityMayHaveChanged
# The role collection push is the only signal a role's knowledge base permissions or its active
# flag changed, both of which App.KnowledgeBaseAccess resolves the access from.
@listenTo App.Role, 'refresh', @accessMayHaveChanged
if @permissionCheck('knowledge_base.*') and App.Config.get('kb_active')
@updateNavMenu()
else if App.Config.get('kb_active_publicly')
@@ -191,6 +195,9 @@ class App.KnowledgeBaseAgentController extends App.Controller
renderControllers: (params) ->
object = @constructor.pickObjectUsing(params, @)
# What the screen below is being built against — see #accessMayHaveChanged.
@renderedAccessSignature = @roleAccessSignature()
if !object || (!@isEditor() && !object.visibleInternally(@kb_locale()))
@renderNotFound()
return
@@ -417,6 +424,48 @@ class App.KnowledgeBaseAgentController extends App.Controller
if didRemove
@notifyVisibilityChangeLoaded()
# A role losing a knowledge base permission, or being deactivated, changes what the user may do
# with categories and answers that did not change themselves: nothing sends `kb_data_changed`
# for any of them, and the id sets #visibilityMayHaveChanged diffs stay equal whenever the
# access only drops from editor to reader. Building the screen again is what applies the new
# access — the content controller reads it once, when it is built, and hands it down to the
# items it renders.
accessMayHaveChanged: =>
return if !@renderedAccessSignature?
return if @renderedAccessSignature is @roleAccessSignature()
@updateNavMenu()
# Refetched either way, because which categories and answers the user may see grows with a
# raised access and shrinks with a lowered one, and no assets accompany a role change.
#
# Built again only while on display: #show leaves an edit route the access no longer allows,
# which must not happen behind the back of a user working elsewhere. Dropping the url of the
# content controller is what defers the rebuild to their return.
@contentController?.url = null
@pendingParams = @lastParams if @active()
@fetch(false, @active())
# The knowledge base access every role of the current user grants, as App.KnowledgeBaseAccess
# reads it: the granular permissions arrive as ordinary asset changes, the role's own
# permissions and its active flag do not.
roleAccessSignature: ->
kb_permission_ids = ['knowledge_base.reader', 'knowledge_base.editor']
.map (name) -> App.Permission.findByAttribute('name', name)?.id
App.User
.current()
.role_ids
.map (role_id) ->
role = App.Role.find(role_id)
return "#{role_id}:inactive" if !role?.active
"#{role_id}:#{_.intersection(role.permission_ids, kb_permission_ids).sort().join(',')}"
.sort()
.join('|')
@pickObjectUsing: (params, parentController) ->
kb = parentController.getKnowledgeBase()
return if !kb

View File

@@ -102,7 +102,7 @@ class App.KnowledgeBaseReaderController extends App.Controller
renderBody: (translation) ->
body = translation.content().body
body = @prepareLinks(body)
body = @prepareVideos(body)
body = App.KnowledgeBaseReaderController.prepareVideos(body)
@answerBody.html(body)
@bindInlineImagePreview()
@@ -128,32 +128,56 @@ class App.KnowledgeBaseReaderController extends App.Controller
$('<container>').append(input).html()
prepareVideos: (input) ->
# An admin-approved server may carry an explicit port (see
# Setting::Validation::KbSelfHostedVideoServers), which must stay a literal ':'
# delimiter - escaping it would point the iframe at a host that does not exist, and
# no longer match the origin allowed via the CSP frame-src. The host name itself is
# still escaped, so a value that somehow entered the setting without passing its
# validation cannot break out of the surrounding attribute.
@escapeVideoHost: (host) ->
index = host.lastIndexOf(':')
name = host.slice(0, index)
port = host.slice(index + 1)
return encodeURIComponent(host) if index < 1 || !/^\d+$/.test(port)
"#{encodeURIComponent(name)}:#{port}"
@prepareVideos: (input) ->
input.replace /\(([\s]*)widget:([\s]*)video[\W]([\s\S])+?\)/g, (match) ->
settings = match
.slice(1, -1)
.split(',')
.map (pair) -> pair.split(':').map (elem) -> elem.trim()
.map (pair) ->
[key, rest...] = pair.split(':')
[key.trim(), rest.join(':').trim()]
.reduce (memo, elem) ->
memo[elem[0]] = elem[1]
return memo
, {}
id = encodeURIComponent(settings.id ? '')
host = App.KnowledgeBaseReaderController.escapeVideoHost(settings.host ? '')
hostAllowed = App.KnowledgeBaseVideo.hostAllowed(settings.host)
# coffeelint: disable=indentation
url = switch settings.provider
when 'youtube'
"https://www.youtube.com/embed/#{settings.id}"
"https://www.youtube.com/embed/#{id}"
when 'vimeo'
"https://player.vimeo.com/video/#{settings.id}"
"https://player.vimeo.com/video/#{id}"
when 'peertube'
"https://#{settings.host}/videos/embed/#{settings.id}"
"https://#{host}/videos/embed/#{id}" if hostAllowed
when 'mediacms'
"https://#{settings.host}/embed?m=#{settings.id}"
"https://#{host}/embed?m=#{id}" if hostAllowed
# coffeelint: enable=indentation
return match unless url
return '' if !url
"<div class='videoWrapper'><iframe allowfullscreen id='#{settings.provider}#{settings.id}' type='text/html' src='#{url}' frameborder='0'></iframe></div>"
idAttribute = App.Utils.htmlEscape("#{settings.provider}#{settings.id ? ''}")
"<div class='videoWrapper'><iframe allowfullscreen id='#{idAttribute}' type='text/html' src='#{App.Utils.htmlEscape(url)}' frameborder='0'></iframe></div>"
renderAttachments: (attachments) ->
@answerAttachments.html App.view('generic/attachments')(

View File

@@ -66,7 +66,8 @@ class Login extends App.ControllerFullPage
data.name != 'product_name' &&
data.name != 'product_logo' &&
data.name != 'fqdn' &&
data.name != 'user_show_password_login'
data.name != 'user_show_password_login' &&
data.name != 'import_mode'
@render()
'rerender'
)

View File

@@ -34,6 +34,7 @@ class Package extends App.ControllerSubContent
)
render: ->
actions_available = App.Config.get('package_actions_enabled')
for item in @packages
item.action = []
@@ -45,6 +46,7 @@ class Package extends App.ControllerSubContent
else if item.state == 'deactivate'
item.action = ['uninstall', 'activate']
item.action = ['uninstall']
continue if !actions_available
if @api_package_metas[item.name] && item.vendor is @api_package_metas[item.name].vendor
if @isNewerVersion(item.version, @api_package_metas[item.name].version)
item.action.unshift('update_api')
@@ -63,6 +65,7 @@ class Package extends App.ControllerSubContent
token_present: @token_present
api_package_metas: @api_package_metas
api_packages_installable: api_packages_installable
actions_available: actions_available
)
uninstall: (e) ->
@@ -207,4 +210,4 @@ class PackageSettingsModal extends App.ControllerModal
super
@parent.load()
App.Config.set('Packages', { prio: 3700, name: __('Packages'), parent: '#system', target: '#system/package', controller: Package, permission: (controller) -> App.Config.get('admin_packages') && controller.permissionCheck('admin.package') }, 'NavBarAdmin')
App.Config.set('Packages', { prio: 3700, name: __('Packages'), parent: '#system', target: '#system/package', controller: Package, permission: (controller) -> controller.permissionCheck('admin.package') }, 'NavBarAdmin')

Some files were not shown because too many files have changed in this diff Show More