From bf7412dddf78ae75094e8cd6bdec479b59ce41d0 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 21 Aug 2026 13:02:29 -0300 Subject: [PATCH 1/4] Merge 5.0.0 into 5.0.1 (#1518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Report skipped bumps in the repository bumper workflow (#1492) Improve repository bumper error handling * Recalculate flyout position when the sidecar is opened (#1503) * Adapt flyout position in relation to the sidecar * Add changelog * Change resizable button emphasis style from :focus to :active (#1508) * fix(sidecar): change resizable button emphasis style from :focus to :active Update the .sidecar-resizableButton CSS to emphasize the "grab" icon on :active instead of :focus, matching the intended interaction feedback when dragging the resizer. Closes https://github.com/wazuh/wazuh-dashboard-plugins/issues/8989 Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * fix(sidecar): remove leftover native focus outline on resizable button The resizable button programmatically calls .focus() on every click, so after removing the custom :focus emphasis in favor of :active, the browser's default focus outline was left showing as an unstyled colored border until focus moved elsewhere. Suppress it for mouse/touch interaction while keeping it for keyboard users via :focus-visible. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * fix(sidecar): opt out resizable button from the global focus-ring animation The resizable button programmatically calls .focus() on every click, which triggered OSD's global brute-force focus-ring animation (:focus:not([class^="eui"]):not(.osd-resetFocusState) in _base.scss) since the button isn't an EUI component. That showed as a lingering colored border after dragging, independent from the button's own :active styling. Opt out via the "osd-resetFocusState" class, which is the mechanism _base.scss already documents for components with hand-crafted focus states, instead of fighting the rule's !important/specificity locally. Supersedes the previous outline:none attempt, which didn't target the actual animation. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * feat(sidecar): allow configuring the resizable button className Add an optional className prop to ResizableButton, and forward it from a new classNameButton option on OverlaySidecarOpenOptions/Sidecar, so callers of overlays.sidecar.open() can style/opt-out the resizer button (e.g. pass "osd-resetFocusState" to drop the global focus-ring animation) without hardcoding it in the component. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez --------- Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude * Change top bar health-check icon (#1505) * feat: update health check indicator * Change top bar health-check icon to a pulse shown only when attention is needed Co-Authored-By: Claude Opus 4.8 Signed-off-by: Rodrigo Lopez * fix(core): use kebab-case for sidecar docked-mode CSS variable Rename --osdSidecarSize to --osd-sidecar-size and switch attribute selectors to double quotes to satisfy the repo's stylelint rules (custom-property-pattern, string-quotes), registering sidecar.scss in the global selectors allowlist for its .euiFlyout references. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * chore: use constant * Rework health-check popover * Rework health-check popover * List failing health checks in the nav-button popover Show the enabled non-green checks as an EuiHealth list in the health-check popover, labelled by the check name and with the error surfaced in a tooltip. Use the shared mapTaskStatusToHealthColor helper and TASK.RUN_RESULT constants instead of magic strings. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Rodrigo Lopez * Fix popover closes when clicking inside * Close popover after navigating to the health check --------- Signed-off-by: Rodrigo Lopez Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude Opus 4.8 Co-authored-by: Antonio David Gutiérrez Co-authored-by: Federico Rodriguez * Bump 5.0.0 branch (#1513) feat: bump 5.0.0 * Fix RPM changelog entry order causing build failure (#1516) Fix RPM changelog entry order for 5.0.0 The 5.0.0 changelog entry was placed out of descending chronological order, causing rpmbuild to fail while parsing %changelog. Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 * Merge 4.14.9 into 5.0.0 (#1517) Remove unnecessary debhelper install-time dependency (#1484) fix: remove unnecessary debhelper install-time dependency debhelper is only required to build the .deb package (declared under Build-Depends in dev-tools/build-packages/deb/debian/control), not to install a pre-built one. Remove it from the apt-get install steps used to test package install/upgrade. Signed-off-by: Antonio David Gutiérrez Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Claude --------- Signed-off-by: Antonio David Gutiérrez Signed-off-by: Rodrigo Lopez Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Federico Rodriguez Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: Rodrigo López <37187963+rodrigofez@users.noreply.github.com> Co-authored-by: Antonio David Gutiérrez Co-authored-by: Wazuh CI <22834044+wazuhci@users.noreply.github.com> --- .github/workflows/5_bumper_repository.yml | 18 +- CHANGELOG.md | 1 + dev-tools/build-packages/deb/debian/changelog | 2 +- .../build-packages/rpm/wazuh-dashboard.spec | 4 +- .../config/global_selectors.json | 9 +- .../sidecar/components/resizable_button.scss | 10 +- .../sidecar/components/resizable_button.tsx | 15 +- .../overlays/sidecar/components/sidecar.scss | 16 ++ .../overlays/sidecar/components/sidecar.tsx | 30 ++- .../overlays/sidecar/sidecar_service.tsx | 1 + .../health_check_nav_button.test.tsx | 204 ++++++++++++++++++ .../button_header/health_check_nav_button.tsx | 120 +++++++---- 12 files changed, 369 insertions(+), 61 deletions(-) create mode 100644 src/plugins/healthcheck/public/components/button_header/health_check_nav_button.test.tsx diff --git a/.github/workflows/5_bumper_repository.yml b/.github/workflows/5_bumper_repository.yml index 2929153f4735..fc44fc50befc 100644 --- a/.github/workflows/5_bumper_repository.yml +++ b/.github/workflows/5_bumper_repository.yml @@ -187,11 +187,14 @@ jobs: PR_NUMBER=$(gh pr list --head "$BUMP_BRANCH" --base "${{ github.ref_name }}" --state merged --json number --jq '.[0].number') if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then - echo "Error: The original PR for the bump was not found" + echo "The original PR for the bump was not found" echo "Searching merged PR from: $BUMP_BRANCH to ${{ github.ref_name }}" - exit 1 + echo "pr_found=false" >> $GITHUB_OUTPUT + echo "has_changes=false" >> $GITHUB_OUTPUT + exit 0 fi + echo "pr_found=true" >> $GITHUB_OUTPUT echo "Original PR found: #$PR_NUMBER" MERGE_COMMIT=$(gh pr view $PR_NUMBER --json mergeCommit --jq '.mergeCommit.oid') @@ -244,6 +247,17 @@ jobs: run: | gh pr merge "${{ steps.create_pr.outputs.pull_request_url }}" --squash --admin + - name: 'Result: original bump pull request not found' + if: inputs.revert == true && steps.revert_step.outputs.pr_found == 'false' + run: echo "There is no original bump pull request to revert, nothing to do." + + - name: 'Result: no changes to commit' + if: >- + (inputs.revert != true && steps.bump_changes.outputs.has_changes == 'false') || + (inputs.revert == true && steps.revert_step.outputs.pr_found == 'true' && + steps.revert_step.outputs.has_changes == 'false') + run: echo "The bump produced no changes, no pull request was created." + - name: Show logs if: inputs.revert != true run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 7178601b8283..72c6e6b5efda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ## Prior versions +- [v4.14.7](https://github.com/wazuh/wazuh-dashboard/blob/v4.14.7/CHANGELOG.md) - [v4.14.6](https://github.com/wazuh/wazuh-dashboard/blob/v4.14.6/CHANGELOG.md) - [v4.14.5](https://github.com/wazuh/wazuh-dashboard/blob/v4.14.5/CHANGELOG.md) - [v4.14.4](https://github.com/wazuh/wazuh-dashboard/blob/v4.14.4/CHANGELOG.md) diff --git a/dev-tools/build-packages/deb/debian/changelog b/dev-tools/build-packages/deb/debian/changelog index 778353e91bfd..7a2ff098bd2f 100644 --- a/dev-tools/build-packages/deb/debian/changelog +++ b/dev-tools/build-packages/deb/debian/changelog @@ -8,7 +8,7 @@ wazuh-dashboard (5.0.0-RELEASE) stable; urgency=low * More info: https://documentation.wazuh.com/current/release-notes/release-5-0-0.html - -- Wazuh, Inc Tue, 04 Aug 2026 12:00:00 +0000 + -- Wazuh, Inc Wed, 09 Sep 2026 12:00:00 +0000 wazuh-dashboard (4.14.9-RELEASE) stable; urgency=low diff --git a/dev-tools/build-packages/rpm/wazuh-dashboard.spec b/dev-tools/build-packages/rpm/wazuh-dashboard.spec index 6814ca25ecb2..8a3e0a6da72f 100644 --- a/dev-tools/build-packages/rpm/wazuh-dashboard.spec +++ b/dev-tools/build-packages/rpm/wazuh-dashboard.spec @@ -483,12 +483,12 @@ rm -fr %{buildroot} - More info: https://documentation.wazuh.com/current/release-notes/release-4-14-9.html * Wed Sep 09 2026 support - 5.0.1 - More info: https://documentation.wazuh.com/current/release-notes/release-5-0-1.html +* Wed Sep 09 2026 support - 5.0.0 +- More info: https://documentation.wazuh.com/current/release-notes/release-5-0-0.html * Thu Sep 03 2026 support - 4.10.5 - More info: https://documentation.wazuh.com/current/release-notes/release-4-10-5.html * Wed Sep 02 2026 support - 4.14.8 - More info: https://documentation.wazuh.com/current/release-notes/release-4-14-8.html -* Tue Aug 04 2026 support - 5.0.0 -- More info: https://documentation.wazuh.com/current/release-notes/release-5-0-0.html * Wed Jul 29 2026 support - 4.14.7 - More info: https://documentation.wazuh.com/current/release-notes/release-4-14-7.html * Wed Jul 01 2026 support - 4.14.6 diff --git a/packages/osd-stylelint-config/config/global_selectors.json b/packages/osd-stylelint-config/config/global_selectors.json index 9cc07f14725a..74680c0c67c3 100644 --- a/packages/osd-stylelint-config/config/global_selectors.json +++ b/packages/osd-stylelint-config/config/global_selectors.json @@ -25,11 +25,16 @@ "src/plugins/data/public/ui/query_string_input/_query_bar.scss", "src/plugins/data/public/ui/query_editor/_query_editor.scss", "src/plugins/data/public/ui/dataset_selector/_dataset_selector.scss", - "src/plugins/explore/public/components/data_table/table_cell/table_source_cell.scss" + "src/plugins/explore/public/components/data_table/table_cell/table_source_cell.scss", + "src/core/public/overlays/sidecar/components/sidecar.scss" ] }, "/\\.explore/": { "explanation": "\"explore\" prefix is preserved for Explore plugin", - "approved": ["src/plugins/explore/", "src/plugins/agent_traces/", "examples/expressions_example/public/index.scss"] + "approved": [ + "src/plugins/explore/", + "src/plugins/agent_traces/", + "examples/expressions_example/public/index.scss" + ] } } diff --git a/src/core/public/overlays/sidecar/components/resizable_button.scss b/src/core/public/overlays/sidecar/components/resizable_button.scss index 2473b49455f6..0485c8cdfbfb 100644 --- a/src/core/public/overlays/sidecar/components/resizable_button.scss +++ b/src/core/public/overlays/sidecar/components/resizable_button.scss @@ -74,8 +74,9 @@ } } - // Add a transparent background to the container and emphasis the "grab" icon with primary color on :focus - &:focus:not(:disabled) { + // Wazuh: changed from :focus to :active (upstream OSD used :focus here) + // Add a transparent background to the container and emphasis the "grab" icon with primary color on :active + &:active:not(:disabled) { background-color: transparentize($euiColorPrimary, 0.9); &::before, @@ -91,9 +92,10 @@ } } - // Morph the "grab" icon into a fluid 2px straight line on :hover and :focus + // Wazuh: changed from :focus to :active (upstream OSD used :focus here) + // Morph the "grab" icon into a fluid 2px straight line on :hover and :active &:hover:not(:disabled), - &:focus:not(:disabled) { + &:active:not(:disabled) { &.resizableButton--horizontal { &::before, &::after { diff --git a/src/core/public/overlays/sidecar/components/resizable_button.tsx b/src/core/public/overlays/sidecar/components/resizable_button.tsx index 9a2d492da701..3a54e3280177 100644 --- a/src/core/public/overlays/sidecar/components/resizable_button.tsx +++ b/src/core/public/overlays/sidecar/components/resizable_button.tsx @@ -14,17 +14,22 @@ interface Props { onResize: (size: number) => void; flyoutSize: number; dockedMode: ISidecarConfig['dockedMode'] | undefined; + className?: string; } const RESIZE_DEBOUNCE_DELAY = 50; -export const ResizableButton = ({ dockedMode, onResize, flyoutSize }: Props) => { +export const ResizableButton = ({ dockedMode, onResize, flyoutSize, className }: Props) => { const isHorizontal = dockedMode !== SIDECAR_DOCKED_MODE.TAKEOVER; - const classes = classNames('sidecar-resizableButton', { - 'resizableButton--vertical': !isHorizontal, - 'resizableButton--horizontal': isHorizontal, - }); + const classes = classNames( + 'sidecar-resizableButton', + { + 'resizableButton--vertical': !isHorizontal, + 'resizableButton--horizontal': isHorizontal, + }, + className + ); const initialMouseXorY = useRef(0); const initialFlyoutSize = useRef(flyoutSize); diff --git a/src/core/public/overlays/sidecar/components/sidecar.scss b/src/core/public/overlays/sidecar/components/sidecar.scss index 7d4a1c65e69d..2e1efb7f69d2 100644 --- a/src/core/public/overlays/sidecar/components/sidecar.scss +++ b/src/core/public/overlays/sidecar/components/sidecar.scss @@ -45,3 +45,19 @@ height: 100%; } } + +// WAZUH +// EuiFlyout is `position: fixed` and portaled to , so it sits outside +// the app-wrapper subtree that the sidecar pushes over via padding (see +// getOsdSidecarPaddingStyle). Left to itself it keeps `right: 0` / `left: 0` +// and ends up underneath the sidecar instead of beside it. `Sidecar` keeps +// these attributes/custom property in sync with its own config while docked +// left or right (not for `--dockedTakeover`, which is meant to cover +// everything), so displace any open flyout by that same amount here. +body[data-osd-sidecar-docked-mode="right"] .euiFlyout:not(.euiFlyout--left) { + right: var(--osd-sidecar-size, 0); +} + +body[data-osd-sidecar-docked-mode="left"] .euiFlyout--left { + left: var(--osd-sidecar-size, 0); +} diff --git a/src/core/public/overlays/sidecar/components/sidecar.tsx b/src/core/public/overlays/sidecar/components/sidecar.tsx index 7eec0f666412..e448243c81d0 100644 --- a/src/core/public/overlays/sidecar/components/sidecar.tsx +++ b/src/core/public/overlays/sidecar/components/sidecar.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { BehaviorSubject } from 'rxjs'; import classNames from 'classnames'; @@ -57,6 +57,33 @@ export const Sidecar = ({ sidecarConfig$, options, setSidecarConfig, i18n, mount [sidecarConfig] ); + // WAZUH + // Other overlays (e.g. EuiFlyout) are rendered outside this component's DOM + // subtree, so they can't be shifted via a padding/width style the way the + // app-wrapper is. Expose the docked side and size on `document.body` so + // global CSS can displace them instead of letting the sidecar cover them. + useEffect(() => { + const { body } = document; + const isSideDocked = + sidecarConfig && + !sidecarConfig.isHidden && + (sidecarConfig.dockedMode === SIDECAR_DOCKED_MODE.LEFT || + sidecarConfig.dockedMode === SIDECAR_DOCKED_MODE.RIGHT); + + if (isSideDocked) { + body.dataset.osdSidecarDockedMode = sidecarConfig!.dockedMode; + body.style.setProperty('--osd-sidecar-size', `${sidecarConfig!.paddingSize}px`); + } else { + delete body.dataset.osdSidecarDockedMode; + body.style.removeProperty('--osd-sidecar-size'); + } + + return () => { + delete body.dataset.osdSidecarDockedMode; + body.style.removeProperty('--osd-sidecar-size'); + }; + }, [sidecarConfig]); + return (
@@ -64,6 +91,7 @@ export const Sidecar = ({ sidecarConfig$, options, setSidecarConfig, i18n, mount onResize={handleResize} dockedMode={sidecarConfig?.dockedMode} flyoutSize={sidecarConfig?.paddingSize ?? 0} + className={options.classNameButton} />
diff --git a/src/core/public/overlays/sidecar/sidecar_service.tsx b/src/core/public/overlays/sidecar/sidecar_service.tsx index 13410bf2062a..79cba1d9bff7 100644 --- a/src/core/public/overlays/sidecar/sidecar_service.tsx +++ b/src/core/public/overlays/sidecar/sidecar_service.tsx @@ -97,6 +97,7 @@ export interface OverlaySidecarStart { */ export interface OverlaySidecarOpenOptions { className?: string; + classNameButton?: string; 'data-test-subj'?: string; config: ISidecarConfig; } diff --git a/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.test.tsx b/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.test.tsx new file mode 100644 index 000000000000..c4f6713f2ee6 --- /dev/null +++ b/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.test.tsx @@ -0,0 +1,204 @@ +/* + * Copyright Wazuh + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import { BehaviorSubject } from 'rxjs'; +import { HealthCheckStatus, TaskInfo } from 'src/core/common/healthcheck'; +import { HealthCheckNavButton, HealthCheckNavButtonProps } from './health_check_nav_button'; +import { setCore } from '../../dashboards_services'; +import { PLUGIN_NAME } from '../../../common'; + +jest.mock('@elastic/eui', () => { + const actual = jest.requireActual('@elastic/eui'); + return { + ...actual, + EuiIcon: ({ type, color }: { type: string; color?: string }) => ( + + ), + }; +}); + +const coreStart = { + uiSettings: { get: jest.fn().mockReturnValue(false) }, + application: { + getUrlForApp: jest.fn().mockReturnValue('/app/healthcheck'), + navigateToUrl: jest.fn(), + // Needed by RedirectAppLinks, which is only mounted once the popover opens. + currentAppId$: new BehaviorSubject('healthcheck'), + }, +} as any; + +const buildCheck = (name: string, result: TaskInfo['result']): TaskInfo => ({ + name, + status: 'finished', + result, + createdAt: null, + startedAt: null, + finishedAt: null, + duration: null, + data: null, + error: `${name} failed`, + enabled: true, + critical: false, +}); + +const buildStatus = ( + status: HealthCheckStatus['status'], + checks: TaskInfo[] = [] +): HealthCheckStatus => ({ + status, + checks, +}); + +const renderButton = (status: HealthCheckStatus['status'], checks: TaskInfo[] = []) => { + const props: HealthCheckNavButtonProps = { + coreStart, + status$: new BehaviorSubject(buildStatus(status, checks)), + fetch: jest.fn().mockResolvedValue(undefined), + getConfig: jest.fn().mockResolvedValue({ interval: 0 }), + }; + return render(); +}; + +describe('HealthCheckNavButton', () => { + beforeEach(() => { + setCore(coreStart); + jest.clearAllMocks(); + }); + + it('does not render when the overall status is green', () => { + const { queryByTestId } = renderButton('green'); + expect(queryByTestId('healthcheck-icon')).toBeNull(); + }); + + it('does not render when the overall status is gray', () => { + const { queryByTestId } = renderButton('gray'); + expect(queryByTestId('healthcheck-icon')).toBeNull(); + }); + + it('renders a pulse icon when the overall status is yellow', () => { + const { getByTestId } = renderButton('yellow'); + const icon = getByTestId('healthcheck-icon'); + expect(icon).toHaveAttribute('data-icon-type', 'pulse'); + expect(icon).toHaveAttribute('data-icon-color', 'warning'); + }); + + it('renders a pulse icon when the overall status is red', () => { + const { getByTestId } = renderButton('red'); + const icon = getByTestId('healthcheck-icon'); + expect(icon).toHaveAttribute('data-icon-type', 'pulse'); + expect(icon).toHaveAttribute('data-icon-color', 'danger'); + }); + + describe('popover', () => { + const checks = [buildCheck('server-api:run-as', 'yellow')]; + + // `aria-expanded` mirrors the open state synchronously. The panel itself stays + // mounted while OuiPopover plays its closing transition, so its presence in the + // DOM is not a reliable signal that the popover is still open. + it('opens the popover when the button is clicked', async () => { + const { getByTestId, queryByRole } = renderButton('yellow', checks); + const trigger = getByTestId('healthcheckNavButton'); + expect(trigger).toHaveAttribute('aria-expanded', 'false'); + expect(queryByRole('dialog')).toBeNull(); + + fireEvent.click(trigger); + + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + expect(await waitFor(() => queryByRole('dialog'))).toBeInTheDocument(); + }); + + it('closes the popover when the button is clicked again', async () => { + const { getByTestId, queryByRole } = renderButton('yellow', checks); + const trigger = getByTestId('healthcheckNavButton'); + + fireEvent.click(trigger); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(trigger); + + expect(trigger).toHaveAttribute('aria-expanded', 'false'); + await waitFor(() => expect(queryByRole('dialog')).toBeNull()); + }); + + // The panel is rendered in a portal, but React events bubble along the React tree, + // so a toggle handler on an ancestor of OuiPopover also receives clicks coming from + // the panel and closes it. The trigger must own the toggle instead. See #1504. + it('keeps the popover open when clicking inside the panel', () => { + const { getByRole, getByTestId, getByText } = renderButton('yellow', checks); + const trigger = getByTestId('healthcheckNavButton'); + + fireEvent.click(trigger); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByText('run-as')); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByRole('dialog')); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByText('For more details, go to the', { exact: false })); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + }); + + it('closes the popover when the Health Check link is clicked', async () => { + const { getByRole, getByTestId, queryByRole } = renderButton('yellow', checks); + const trigger = getByTestId('healthcheckNavButton'); + + fireEvent.click(trigger); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByRole('link', { name: PLUGIN_NAME })); + + expect(trigger).toHaveAttribute('aria-expanded', 'false'); + await waitFor(() => expect(queryByRole('dialog')).toBeNull()); + }); + + // A modified click opens the link in a new tab, so the current page — and the + // popover on it — should stay as it is. + it('keeps the popover open when the link is opened in a new tab', () => { + const { getByRole, getByTestId } = renderButton('yellow', checks); + const trigger = getByTestId('healthcheckNavButton'); + + fireEvent.click(trigger); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByRole('link', { name: PLUGIN_NAME }), { ctrlKey: true }); + + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + }); + }); + + describe('popover when placed in the left nav', () => { + const checks = [buildCheck('server-api:run-as', 'yellow')]; + + beforeEach(() => { + coreStart.uiSettings.get.mockReturnValue(true); + }); + + afterEach(() => { + coreStart.uiSettings.get.mockReturnValue(false); + }); + + it('toggles the popover from the button and ignores clicks inside the panel', () => { + const { getByRole, getByTestId, getByText } = renderButton('yellow', checks); + const trigger = getByTestId('healthcheckNavButton'); + expect(trigger).toHaveAttribute('aria-expanded', 'false'); + + fireEvent.click(trigger); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByText('run-as')); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(getByRole('dialog')); + expect(trigger).toHaveAttribute('aria-expanded', 'true'); + + fireEvent.click(trigger); + expect(trigger).toHaveAttribute('aria-expanded', 'false'); + }); + }); +}); diff --git a/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.tsx b/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.tsx index 50598d3e68d6..b93bd885f43f 100644 --- a/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.tsx +++ b/src/plugins/healthcheck/public/components/button_header/health_check_nav_button.tsx @@ -12,8 +12,11 @@ import { EuiIcon, EuiPopover, EuiToolTip, - EuiText, EuiLink, + EuiHealth, + OuiDescriptionList, + OuiDescriptionListTitle, + EuiHorizontalRule, } from '@elastic/eui'; import useObservable from 'react-use/lib/useObservable'; import { BehaviorSubject, interval, Subscription } from 'rxjs'; @@ -24,7 +27,7 @@ import { mapTaskStatusToHealthColor } from '../services/health'; import { RedirectAppLinks } from '../../../../opensearch_dashboards_react/public'; import { getCore } from '../../dashboards_services'; import { PLUGIN_ID, PLUGIN_NAME } from '../../../common'; -import { BadgeResults } from '../utils/badge_results'; +import { TASK } from '../../constants'; export interface HealthCheckNavButtonProps { coreStart: CoreStart; @@ -58,6 +61,9 @@ export const HealthCheckNavButton = ({ const isPlacedInLeftNav = coreStart.uiSettings.get('home:useNewHomePage'); + const shouldRenderIndicator = + status === TASK.RUN_RESULT.YELLOW.value || status === TASK.RUN_RESULT.RED.value; + const overallStatusIndicator = ( @@ -88,47 +94,89 @@ export const HealthCheckNavButton = ({ {overallStatusIndicator} ); + const switchPopover = () => setPopoverOpen((prevState) => !prevState); + const innerElement = isPlacedInLeftNav ? ( {button} ) : ( - button + + {button} + ); const contextMenuPanel = ( - -

- - check.enabled)} /> -

- - - + {checks + .filter( + (check) => + check.enabled && + (check.result === TASK.RUN_RESULT.RED.value || + check.result === TASK.RUN_RESULT.YELLOW.value) + ) + .map((check) => { + const [category, name] = check.name.split(':'); + return ( + + + + + {category}: {name} + + + + + ); + })} + + + + { + if (event.defaultPrevented) { + setPopoverOpen(false); + } + }} + > {PLUGIN_NAME} - ), - }} - /> - -
+
+ ), + }} + /> + ); @@ -149,25 +197,9 @@ export const HealthCheckNavButton = ({ ); - const switchPopover = () => setPopoverOpen((prevState) => !prevState); + if (!shouldRenderIndicator) { + return null; + } - return ( - -
{ - if (e.key === 'Enter' || e.key === ' ') switchPopover(); - }} - > - {isPlacedInLeftNav ? ( - popover - ) : ( - {popover} - )} -
-
- ); + return {popover}; }; From 201cf8e00a6fc61acd549752a2965e0af1514d12 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:02:05 -0300 Subject: [PATCH 2/4] Merge 5.0.0 into 5.0.1 (#1541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Report skipped bumps in the repository bumper workflow (#1492) Improve repository bumper error handling * Recalculate flyout position when the sidecar is opened (#1503) * Adapt flyout position in relation to the sidecar * Add changelog * Change resizable button emphasis style from :focus to :active (#1508) * fix(sidecar): change resizable button emphasis style from :focus to :active Update the .sidecar-resizableButton CSS to emphasize the "grab" icon on :active instead of :focus, matching the intended interaction feedback when dragging the resizer. Closes https://github.com/wazuh/wazuh-dashboard-plugins/issues/8989 Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * fix(sidecar): remove leftover native focus outline on resizable button The resizable button programmatically calls .focus() on every click, so after removing the custom :focus emphasis in favor of :active, the browser's default focus outline was left showing as an unstyled colored border until focus moved elsewhere. Suppress it for mouse/touch interaction while keeping it for keyboard users via :focus-visible. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * fix(sidecar): opt out resizable button from the global focus-ring animation The resizable button programmatically calls .focus() on every click, which triggered OSD's global brute-force focus-ring animation (:focus:not([class^="eui"]):not(.osd-resetFocusState) in _base.scss) since the button isn't an EUI component. That showed as a lingering colored border after dragging, independent from the button's own :active styling. Opt out via the "osd-resetFocusState" class, which is the mechanism _base.scss already documents for components with hand-crafted focus states, instead of fighting the rule's !important/specificity locally. Supersedes the previous outline:none attempt, which didn't target the actual animation. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * feat(sidecar): allow configuring the resizable button className Add an optional className prop to ResizableButton, and forward it from a new classNameButton option on OverlaySidecarOpenOptions/Sidecar, so callers of overlays.sidecar.open() can style/opt-out the resizer button (e.g. pass "osd-resetFocusState" to drop the global focus-ring animation) without hardcoding it in the component. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez --------- Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude * Change top bar health-check icon (#1505) * feat: update health check indicator * Change top bar health-check icon to a pulse shown only when attention is needed Co-Authored-By: Claude Opus 4.8 Signed-off-by: Rodrigo Lopez * fix(core): use kebab-case for sidecar docked-mode CSS variable Rename --osdSidecarSize to --osd-sidecar-size and switch attribute selectors to double quotes to satisfy the repo's stylelint rules (custom-property-pattern, string-quotes), registering sidecar.scss in the global selectors allowlist for its .euiFlyout references. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * chore: use constant * Rework health-check popover * Rework health-check popover * List failing health checks in the nav-button popover Show the enabled non-green checks as an EuiHealth list in the health-check popover, labelled by the check name and with the error surfaced in a tooltip. Use the shared mapTaskStatusToHealthColor helper and TASK.RUN_RESULT constants instead of magic strings. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Rodrigo Lopez * Fix popover closes when clicking inside * Close popover after navigating to the health check --------- Signed-off-by: Rodrigo Lopez Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude Opus 4.8 Co-authored-by: Antonio David Gutiérrez Co-authored-by: Federico Rodriguez * Bump 5.0.0 branch (#1513) feat: bump 5.0.0 * Fix RPM changelog entry order causing build failure (#1516) Fix RPM changelog entry order for 5.0.0 The 5.0.0 changelog entry was placed out of descending chronological order, causing rpmbuild to fail while parsing %changelog. Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 * Merge 4.14.9 into 5.0.0 (#1517) Remove unnecessary debhelper install-time dependency (#1484) fix: remove unnecessary debhelper install-time dependency debhelper is only required to build the .deb package (declared under Build-Depends in dev-tools/build-packages/deb/debian/control), not to install a pre-built one. Remove it from the apt-get install steps used to test package install/upgrade. Signed-off-by: Antonio David Gutiérrez Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Claude * Merge 4.14.9 into 5.0.0 (#1537) Merge 4.14.9 into 5.0.0: no changes (verified 2026-08-28) * fix: remove inert opensearch_security.cookie.ttl setting (#1534) The dashboard config declares opensearch_security.cookie.ttl, but the security plugin never reads it: only opensearch_security.session.ttl governs session/cookie expiry. Shipping the setting misleads operators into believing it bounds the cookie lifetime. Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude --------- Signed-off-by: Antonio David Gutiérrez Signed-off-by: Rodrigo Lopez Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Federico Rodriguez Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: Rodrigo López <37187963+rodrigofez@users.noreply.github.com> Co-authored-by: Antonio David Gutiérrez Co-authored-by: Wazuh CI <22834044+wazuhci@users.noreply.github.com> --- config/opensearch_dashboards.prod.yml | 11 +++++------ .../config/opensearch_dashboards.dev.security.yml | 15 +++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/config/opensearch_dashboards.prod.yml b/config/opensearch_dashboards.prod.yml index 2250915e3e64..83c4a920ccc6 100644 --- a/config/opensearch_dashboards.prod.yml +++ b/config/opensearch_dashboards.prod.yml @@ -4,16 +4,15 @@ opensearch.hosts: https://localhost:9200 opensearch.ssl.verificationMode: certificate #opensearch.username: #opensearch.password: -opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"] +opensearch.requestHeadersAllowlist: ['securitytenant', 'Authorization'] opensearch_security.multitenancy.enabled: false -opensearch_security.readonly_mode.roles: ["kibana_read_only"] +opensearch_security.readonly_mode.roles: ['kibana_read_only'] server.ssl.enabled: true -server.ssl.key: "/etc/wazuh-dashboard/certs/dashboard-key.pem" -server.ssl.certificate: "/etc/wazuh-dashboard/certs/dashboard.pem" -opensearch.ssl.certificateAuthorities: ["/etc/wazuh-dashboard/certs/root-ca.pem"] +server.ssl.key: '/etc/wazuh-dashboard/certs/dashboard-key.pem' +server.ssl.certificate: '/etc/wazuh-dashboard/certs/dashboard.pem' +opensearch.ssl.certificateAuthorities: ['/etc/wazuh-dashboard/certs/root-ca.pem'] uiSettings.overrides.defaultRoute: /app/wz-home # Session expiration settings -opensearch_security.cookie.ttl: 900000 opensearch_security.session.ttl: 900000 opensearch_security.session.keepalive: true diff --git a/docker/config/opensearch_dashboards.dev.security.yml b/docker/config/opensearch_dashboards.dev.security.yml index 51e8cb9fad0e..ad5628e294f2 100644 --- a/docker/config/opensearch_dashboards.dev.security.yml +++ b/docker/config/opensearch_dashboards.dev.security.yml @@ -1,15 +1,14 @@ -server.host: "0.0.0.0" +server.host: '0.0.0.0' -opensearch.hosts: ["https://indexer:9200"] +opensearch.hosts: ['https://indexer:9200'] opensearch.ssl.verificationMode: none -opensearch.username: "kibanaserver" -opensearch.password: "kibanaserver" -opensearch.requestHeadersWhitelist: [ authorization,securitytenant ] +opensearch.username: 'kibanaserver' +opensearch.password: 'kibanaserver' +opensearch.requestHeadersWhitelist: [authorization, securitytenant] opensearch_security.multitenancy.enabled: true -opensearch_security.multitenancy.tenants.preferred: ["Private", "Global"] -opensearch_security.readonly_mode.roles: ["kibana_read_only"] +opensearch_security.multitenancy.tenants.preferred: ['Private', 'Global'] +opensearch_security.readonly_mode.roles: ['kibana_read_only'] # Session expiration settings -opensearch_security.cookie.ttl: 900000 opensearch_security.session.ttl: 900000 opensearch_security.session.keepalive: true From cd04694be8996bf88fa58477adc625921c532201 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:52:13 -0300 Subject: [PATCH 3/4] Merge 5.0.0 into 5.0.1 (#1564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Report skipped bumps in the repository bumper workflow (#1492) Improve repository bumper error handling * Recalculate flyout position when the sidecar is opened (#1503) * Adapt flyout position in relation to the sidecar * Add changelog * Change resizable button emphasis style from :focus to :active (#1508) * fix(sidecar): change resizable button emphasis style from :focus to :active Update the .sidecar-resizableButton CSS to emphasize the "grab" icon on :active instead of :focus, matching the intended interaction feedback when dragging the resizer. Closes https://github.com/wazuh/wazuh-dashboard-plugins/issues/8989 Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * fix(sidecar): remove leftover native focus outline on resizable button The resizable button programmatically calls .focus() on every click, so after removing the custom :focus emphasis in favor of :active, the browser's default focus outline was left showing as an unstyled colored border until focus moved elsewhere. Suppress it for mouse/touch interaction while keeping it for keyboard users via :focus-visible. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * fix(sidecar): opt out resizable button from the global focus-ring animation The resizable button programmatically calls .focus() on every click, which triggered OSD's global brute-force focus-ring animation (:focus:not([class^="eui"]):not(.osd-resetFocusState) in _base.scss) since the button isn't an EUI component. That showed as a lingering colored border after dragging, independent from the button's own :active styling. Opt out via the "osd-resetFocusState" class, which is the mechanism _base.scss already documents for components with hand-crafted focus states, instead of fighting the rule's !important/specificity locally. Supersedes the previous outline:none attempt, which didn't target the actual animation. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * feat(sidecar): allow configuring the resizable button className Add an optional className prop to ResizableButton, and forward it from a new classNameButton option on OverlaySidecarOpenOptions/Sidecar, so callers of overlays.sidecar.open() can style/opt-out the resizer button (e.g. pass "osd-resetFocusState" to drop the global focus-ring animation) without hardcoding it in the component. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez --------- Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude * Change top bar health-check icon (#1505) * feat: update health check indicator * Change top bar health-check icon to a pulse shown only when attention is needed Co-Authored-By: Claude Opus 4.8 Signed-off-by: Rodrigo Lopez * fix(core): use kebab-case for sidecar docked-mode CSS variable Rename --osdSidecarSize to --osd-sidecar-size and switch attribute selectors to double quotes to satisfy the repo's stylelint rules (custom-property-pattern, string-quotes), registering sidecar.scss in the global selectors allowlist for its .euiFlyout references. Co-Authored-By: Claude Signed-off-by: Antonio David Gutiérrez * chore: use constant * Rework health-check popover * Rework health-check popover * List failing health checks in the nav-button popover Show the enabled non-green checks as an EuiHealth list in the health-check popover, labelled by the check name and with the error surfaced in a tooltip. Use the shared mapTaskStatusToHealthColor helper and TASK.RUN_RESULT constants instead of magic strings. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Rodrigo Lopez * Fix popover closes when clicking inside * Close popover after navigating to the health check --------- Signed-off-by: Rodrigo Lopez Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude Opus 4.8 Co-authored-by: Antonio David Gutiérrez Co-authored-by: Federico Rodriguez * Bump 5.0.0 branch (#1513) feat: bump 5.0.0 * Fix RPM changelog entry order causing build failure (#1516) Fix RPM changelog entry order for 5.0.0 The 5.0.0 changelog entry was placed out of descending chronological order, causing rpmbuild to fail while parsing %changelog. Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 * Merge 4.14.9 into 5.0.0 (#1517) Remove unnecessary debhelper install-time dependency (#1484) fix: remove unnecessary debhelper install-time dependency debhelper is only required to build the .deb package (declared under Build-Depends in dev-tools/build-packages/deb/debian/control), not to install a pre-built one. Remove it from the apt-get install steps used to test package install/upgrade. Signed-off-by: Antonio David Gutiérrez Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Claude * Merge 4.14.9 into 5.0.0 (#1537) Merge 4.14.9 into 5.0.0: no changes (verified 2026-08-28) * fix: remove inert opensearch_security.cookie.ttl setting (#1534) The dashboard config declares opensearch_security.cookie.ttl, but the security plugin never reads it: only opensearch_security.session.ttl governs session/cookie expiry. Shipping the setting misleads operators into believing it bounds the cookie lifetime. Signed-off-by: Antonio David Gutiérrez Co-authored-by: Claude * Bump 5.0.0 branch (#1545) feat: bump 5.0.0 * Revert bump 5.0.0 branch (#1546) feat: revert 5.0.0 references * Fix changelog not in descending chronological order (#1548) * fix: keep RPM changelog entries in descending date order on re-bump (#1549) Re-bumping an existing version updated its date in place instead of repositioning it, so a newer date could end up below older entries and rpmbuild rejected the spec (%changelog not in descending chronological order). The entry is now removed and reinserted at the position matching its date. Also fixes date/sed portability on macOS (BSD date/sed vs GNU), and makes an invalid date abort the script instead of writing the error text into the changelog. Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 * Bump 5.0.0 branch (#1551) feat: bump 5.0.0 * Review the 5.0.0 changelog (#1556) * docs(changelog): review the 5.0.0 entries Removed the open redirect fix entry (#1285) and the pre install script check entry (#1328, #1365). Reordered the sections to Added, Changed, Removed, Fixed, and qualified the two wazuh-dashboard-plugins references so they no longer read as wazuh-dashboard issues. Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) * docs(changelog): reference issues instead of pull requests Each entry now links the issue the pull request closed, so the reference carries the description and the discussion instead of just the diff. Where an entry listed several references that resolve the same issue, it collapses to that one; where they resolve different issues, the entry keeps the main one and the rest are attached to it as sub-issues on GitHub, following the shape of wazuh-dashboard-plugins#8789. Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) * docs(changelog): merge the health check service and app entries Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> * docs(changelog): point the health check entry at its parent issue Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> --------- Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) * Merge 4.14.9 into 5.0.0 (#1563) Merge 4.14.8 into 4.14.9 (#1562) Merge pull request #1507 from wazuh/change/1506-add-dev-image-construction-in-4x Add dev image build tooling for 4.14.x # Conflicts: # dev-tools/build-dev-image/README.md # dev-tools/build-dev-image/build-multiarch.sh # dev-tools/build-dev-image/install-plugins.sh # dev-tools/build-dev-image/plugins # dev-tools/build-dev-image/warmup-opensearch_dashboards.yml # dev-tools/build-dev-image/warmup-optimizer.sh # dev-tools/build-dev-image/wzd.dockerfile --------- Signed-off-by: Antonio David Gutiérrez Signed-off-by: Rodrigo Lopez Signed-off-by: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Co-authored-by: Federico Rodriguez Co-authored-by: Antonio <34042064+Desvelao@users.noreply.github.com> Co-authored-by: Claude Co-authored-by: Rodrigo López <37187963+rodrigofez@users.noreply.github.com> Co-authored-by: Antonio David Gutiérrez Co-authored-by: Wazuh CI <22834044+wazuhci@users.noreply.github.com> --- .../4_builderpackage_dev_docker_image.yml | 148 ++++++++++++++++++ dev-tools/build-packages/deb/debian/changelog | 2 +- .../build-packages/rpm/wazuh-dashboard.spec | 2 + tools/repository_bumper.sh | 85 +++++++--- 4 files changed, 215 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/4_builderpackage_dev_docker_image.yml diff --git a/.github/workflows/4_builderpackage_dev_docker_image.yml b/.github/workflows/4_builderpackage_dev_docker_image.yml new file mode 100644 index 000000000000..5e884db54c6e --- /dev/null +++ b/.github/workflows/4_builderpackage_dev_docker_image.yml @@ -0,0 +1,148 @@ +# This workflow automates the build and push of Wazuh dashboard development +# Docker images for different architectures (amd64 and arm64). +# +# This workflow: +# - Builds multi-architecture Docker images for Wazuh dashboard development. +# - Pushes individual architecture images to Quay.io registry. +# - Creates and pushes a multi-arch manifest combining both architectures. +# - Automatically extracts the Node.js version from .nvmrc and the OpenSearch +# Dashboards version from package.json. +# - Uses the branch the workflow runs on for wazuh-dashboard, and a single +# optional input for the branch/tag of the plugin repositories +# (wazuh-security-dashboards-plugin and wazuh-dashboard-plugins), which +# defaults to that same branch when not provided. + +name: (4.14.x) Build and Push Dev Image + +on: + workflow_dispatch: + inputs: + plugins_branch: + description: 'Branch/tag/commit of wazuh-security-dashboards-plugin and wazuh-dashboard-plugins (defaults to the branch this workflow runs on)' + required: false + default: '' + type: string + tag: + description: 'Tag for the Docker image' + required: false + default: '' + type: string + +jobs: + extract-versions: + runs-on: codebuild-github-actions-codebuild-runner-dashboard-amd-${{ github.run_id }}-${{ github.run_attempt }} + outputs: + node_version: ${{ steps.versions.outputs.node_version }} + opensearch_version: ${{ steps.versions.outputs.opensearch_version }} + tag: ${{ steps.versions.outputs.tag }} + wazuh_branch: ${{ steps.versions.outputs.wazuh_branch }} + plugins_branch: ${{ steps.versions.outputs.plugins_branch }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Extract versions from repository + id: versions + run: | + OPENSEARCH_VERSION=$(node -p "require('./package.json').version") + NODE_VERSION=$(cat .nvmrc) + + WAZUH_BRANCH="${{ github.ref_name }}" + PLUGINS_BRANCH="${{ inputs.plugins_branch || github.ref_name }}" + + TAG="${{ inputs.tag }}" + if [ -z "$TAG" ]; then + TAG="$OPENSEARCH_VERSION" + fi + + echo "node_version=${NODE_VERSION}" >> $GITHUB_OUTPUT + echo "opensearch_version=${OPENSEARCH_VERSION}" >> $GITHUB_OUTPUT + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "wazuh_branch=${WAZUH_BRANCH}" >> $GITHUB_OUTPUT + echo "plugins_branch=${PLUGINS_BRANCH}" >> $GITHUB_OUTPUT + + echo "📦 OpenSearch Dashboards Version: ${OPENSEARCH_VERSION}" + echo "🏷️ Tag: ${TAG}" + echo "📟 Node Version: ${NODE_VERSION}" + echo "🌿 Wazuh Dashboard Branch: ${WAZUH_BRANCH}" + echo "🌿 Plugins Branch: ${PLUGINS_BRANCH}" + + build: + runs-on: ${{ matrix.runner }} + needs: [extract-versions] + strategy: + matrix: + include: + - platform: linux/amd64 + runner: ${{ format('codebuild-github-actions-codebuild-runner-dashboard-amd-{0}-{1}', github.run_id, github.run_attempt) }} + arch: amd64 + - platform: linux/arm64 + runner: ${{ format('codebuild-github-actions-codebuild-runner-dashboard-arm-{0}-{1}', github.run_id, github.run_attempt) }} + arch: arm64 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Workaround: AWS CodeBuild runners do not start the Docker daemon automatically + - name: Start Docker daemon + run: | + sudo nohup dockerd --storage-driver vfs > /var/log/dockerd.log 2>&1 & + timeout 60 bash -c 'until docker info &>/dev/null 2>&1; do sleep 1; done' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + network=host + + - name: Login to Quay.io + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_TOKEN }} + + - name: Build ${{ matrix.arch }} image + run: | + chmod +x ./dev-tools/build-dev-image/build-multiarch.sh + cd ./dev-tools/build-dev-image + + ./build-multiarch.sh \ + --node-version "${{ needs.extract-versions.outputs.node_version }}" \ + --wazuh-branch "${{ needs.extract-versions.outputs.wazuh_branch }}" \ + --security-branch "${{ needs.extract-versions.outputs.plugins_branch }}" \ + --plugins-branch "${{ needs.extract-versions.outputs.plugins_branch }}" \ + --tag "${{ needs.extract-versions.outputs.tag }}-${{ matrix.arch }}" \ + --platform "${{ matrix.platform }}" \ + --push + + create-manifest: + runs-on: codebuild-github-actions-codebuild-runner-dashboard-amd-${{ github.run_id }}-${{ github.run_attempt }} + needs: [extract-versions, build] + steps: + # Workaround: AWS CodeBuild runners do not start the Docker daemon automatically + - name: Start Docker daemon + run: | + sudo nohup dockerd --storage-driver vfs > /var/log/dockerd.log 2>&1 & + timeout 60 bash -c 'until docker info &>/dev/null 2>&1; do sleep 1; done' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Quay.io + uses: docker/login-action@v3 + with: + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_TOKEN }} + + - name: Create and push multi-arch manifest + run: | + TAG="${{ needs.extract-versions.outputs.tag }}" + + docker buildx imagetools create -t quay.io/wazuh/osd-dev:${TAG} \ + quay.io/wazuh/osd-dev:${TAG}-amd64 \ + quay.io/wazuh/osd-dev:${TAG}-arm64 + + echo "✅ Multi-arch manifest created and pushed!" + echo "📦 Image available at: quay.io/wazuh/osd-dev:${TAG}" diff --git a/dev-tools/build-packages/deb/debian/changelog b/dev-tools/build-packages/deb/debian/changelog index 7a2ff098bd2f..9beb7edf0974 100644 --- a/dev-tools/build-packages/deb/debian/changelog +++ b/dev-tools/build-packages/deb/debian/changelog @@ -8,7 +8,7 @@ wazuh-dashboard (5.0.0-RELEASE) stable; urgency=low * More info: https://documentation.wazuh.com/current/release-notes/release-5-0-0.html - -- Wazuh, Inc Wed, 09 Sep 2026 12:00:00 +0000 + -- Wazuh, Inc Wed, 23 Sep 2026 12:00:00 +0000 wazuh-dashboard (4.14.9-RELEASE) stable; urgency=low diff --git a/dev-tools/build-packages/rpm/wazuh-dashboard.spec b/dev-tools/build-packages/rpm/wazuh-dashboard.spec index 8a3e0a6da72f..7e209b2eb7df 100644 --- a/dev-tools/build-packages/rpm/wazuh-dashboard.spec +++ b/dev-tools/build-packages/rpm/wazuh-dashboard.spec @@ -479,6 +479,8 @@ rm -fr %{buildroot} %attr(644, root, root) "/usr/lib/systemd/system/wazuh-dashboard.service" %changelog +* Wed Sep 23 2026 support - 5.0.0 +- More info: https://documentation.wazuh.com/current/release-notes/release-5-0-0.html * Wed Sep 16 2026 support - 4.14.9 - More info: https://documentation.wazuh.com/current/release-notes/release-4-14-9.html * Wed Sep 09 2026 support - 5.0.1 diff --git a/tools/repository_bumper.sh b/tools/repository_bumper.sh index c1682acac92c..c89728451d2e 100755 --- a/tools/repository_bumper.sh +++ b/tools/repository_bumper.sh @@ -532,21 +532,37 @@ update_build_docker_dev_multiarch_readme() { # Function to convert date from yyyy-mm-dd to RPM format (e.g., "Thu Sep 04 2025") convert_date_to_rpm_format() { local input_date="$1" - # Use date command to convert and format with English locale - LC_ALL=C date -d "$input_date" "+%a %b %d %Y" 2>/dev/null || { - log "ERROR: Invalid date format: $input_date" - exit 1 - } + # Use date command to convert and format with English locale (GNU date on Linux, BSD date on macOS) + if [[ "$OSTYPE" == "darwin"* ]]; then + LC_ALL=C date -j -f "%Y-%m-%d" "$input_date" "+%a %b %d %Y" 2>/dev/null && return 0 + else + LC_ALL=C date -d "$input_date" "+%a %b %d %Y" 2>/dev/null && return 0 + fi + log "ERROR: Invalid date format: $input_date" + return 1 +} + +# Function to convert an RPM changelog date (e.g. "Thu Sep 10 2026") to epoch seconds, for ordering comparisons +rpm_date_to_epoch() { + local rpm_date="$1" + if [[ "$OSTYPE" == "darwin"* ]]; then + LC_ALL=C date -j -f "%a %b %d %Y" "$rpm_date" "+%s" 2>/dev/null + else + LC_ALL=C date -d "$rpm_date" "+%s" 2>/dev/null + fi } # Function to convert date from yyyy-mm-dd to Debian RFC 2822 format (e.g., "Thu, 04 Sep 2025 12:00:00 +0000") convert_date_to_deb_format() { local input_date="$1" - # Use date command to convert and format with English locale - LC_ALL=C date -d "$input_date" "+%a, %d %b %Y 12:00:00 +0000" 2>/dev/null || { - log "ERROR: Invalid date format: $input_date" - exit 1 - } + # Use date command to convert and format with English locale (GNU date on Linux, BSD date on macOS) + if [[ "$OSTYPE" == "darwin"* ]]; then + LC_ALL=C date -j -f "%Y-%m-%d" "$input_date" "+%a, %d %b %Y 12:00:00 +0000" 2>/dev/null && return 0 + else + LC_ALL=C date -d "$input_date" "+%a, %d %b %Y 12:00:00 +0000" 2>/dev/null && return 0 + fi + log "ERROR: Invalid date format: $input_date" + return 1 } # Function to update RPM changelog @@ -564,24 +580,50 @@ update_rpm_changelog() { log "Updating RPM changelog..." - local rpm_date=$(convert_date_to_rpm_format "$DATE") + local rpm_date + rpm_date=$(convert_date_to_rpm_format "$DATE") || exit 1 local changelog_entry="* $rpm_date support - $VERSION" local more_info_entry="- More info: https://documentation.wazuh.com/current/release-notes/release-$(echo $VERSION | tr '.' '-').html" + local new_epoch + new_epoch=$(rpm_date_to_epoch "$rpm_date") || exit 1 + # Check if entry already exists - if grep -q "* .* support - $VERSION" "$RPM_CHANGELOG"; then - log "RPM changelog entry for version $VERSION already exists. Updating date..." - # Update existing entry date - sed -i "s/^\* .* support - $VERSION$/$changelog_entry/" "$RPM_CHANGELOG" - log "Successfully updated RPM changelog date for version $VERSION" + if grep -q "^\* .* support - $VERSION\$" "$RPM_CHANGELOG"; then + log "RPM changelog entry for version $VERSION already exists. Repositioning it with the updated date..." + # Remove the existing entry (its changelog line and following "More info" line); + # it gets reinserted below at the position matching its (possibly new) date, so + # entries stay in descending order regardless of where the old entry used to sit. + sed_inplace "/^\* .* support - $VERSION\$/,+1d" "$RPM_CHANGELOG" else log "Adding new RPM changelog entry for version $VERSION..." - # Find the %changelog line and add new entry after it - sed -i "/^%changelog$/a\\ + fi + + # Entries must stay in descending date order (rpmbuild enforces it), so insert the + # entry right before the first existing entry whose date is not newer than it. + local insert_before_line="" + local line_num=0 + while IFS= read -r line; do + line_num=$((line_num + 1)) + if [[ "$line" =~ ^\*\ (.*)\ support ]]; then + local existing_epoch + existing_epoch=$(rpm_date_to_epoch "${BASH_REMATCH[1]}") || continue + if [ "$existing_epoch" -le "$new_epoch" ]; then + insert_before_line=$line_num + break + fi + fi + done < "$RPM_CHANGELOG" + + if [ -n "$insert_before_line" ]; then + sed_inplace "${insert_before_line}i\\ $changelog_entry\\ $more_info_entry" "$RPM_CHANGELOG" - log "Successfully added new RPM changelog entry for version $VERSION" + else + # New entry is older than every existing one (or there are no entries yet): goes last + printf '%s\n%s\n' "$changelog_entry" "$more_info_entry" >> "$RPM_CHANGELOG" fi + log "Successfully updated RPM changelog entry for version $VERSION" } # Function to update Debian changelog @@ -599,7 +641,8 @@ update_deb_changelog() { log "Updating Debian changelog..." - local deb_date=$(convert_date_to_deb_format "$DATE") + local deb_date + deb_date=$(convert_date_to_deb_format "$DATE") || exit 1 local package_version="$VERSION-RELEASE" local changelog_header="wazuh-dashboard ($package_version) stable; urgency=low" local more_info_entry=" * More info: https://documentation.wazuh.com/current/release-notes/release-$(echo $VERSION | tr '.' '-').html" @@ -613,7 +656,7 @@ update_deb_changelog() { log "Debian changelog entry for version $VERSION already exists. Updating date..." # Update existing entry date # Find the line with the version and then find the next maintainer line to update - sed -i "/wazuh-dashboard ($escaped_package_version)/,/^ *-- Wazuh, Inc/ s|^ *-- Wazuh, Inc .*|$maintainer_line|" "$DEB_CHANGELOG" + sed_inplace "/wazuh-dashboard ($escaped_package_version)/,/^ *-- Wazuh, Inc/ s|^ *-- Wazuh, Inc .*|$maintainer_line|" "$DEB_CHANGELOG" log "Successfully updated Debian changelog date for version $VERSION" else log "Adding new Debian changelog entry for version $VERSION..." From 9c39ff15b15665962822257ca62a6f0876837ac7 Mon Sep 17 00:00:00 2001 From: Ian Yenien Serrano <63758389+yenienserrano@users.noreply.github.com> Date: Fri, 4 Sep 2026 15:07:05 -0300 Subject: [PATCH 4/4] Update header help menu snapshot to the 5.1 documentation URL WAZUH_DOCUMENTATION_URL is derived from the product version, so on main it now resolves to https://documentation.wazuh.com/5.1/ while the committed snapshot still held 5.0. Co-Authored-By: Claude Opus 5 (1M context) --- .../__snapshots__/header_help_menu.test.tsx.snap | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap index 2d2f157805e0..a1776fbd8b3f 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header_help_menu.test.tsx.snap @@ -1812,7 +1812,7 @@ exports[`Header help menu hides survey link 1`] = ` > @@ -2029,14 +2029,14 @@ exports[`Header help menu hides survey link 1`] = ` > @@ -4043,7 +4043,7 @@ exports[`Header help menu renders survey link 1`] = ` > @@ -4286,14 +4286,14 @@ exports[`Header help menu renders survey link 1`] = ` >