Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev -> main #1031

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/docker-build-beta-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,6 @@ jobs:
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.CI_BOT_TOKEN }}
repository: kubeshop/helm-charts
event-type: trigger-workflow-dashboard-pre-release
client-payload: '{"image_tag_dashboard": "${{ steps.tag.outputs.tag }}"}'
repository: kubeshop/testkube-dashboard-chart
event-type: trigger-workflow-dashboard-chart
client-payload: '{"app_version": "${{ steps.tag.outputs.tag }}"}'
30 changes: 8 additions & 22 deletions .github/workflows/docker-build-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,26 +99,12 @@ jobs:
id: tag
uses: dawidd6/action-get-tag@v1
with:
strip_v: false

- name: Editing helm-release repo with version based on a Tag pushed.
run: |
# Setting up Git:
git clone https://kubeshop-bot:[email protected]/kubeshop/helm-charts
cd ./helm-charts
git config user.name "kubeshop-bot"
git config user.email "[email protected]"

export GH_TOKEN
export RELEASE_VERSION
export SERVICE

git fetch origin main
git checkout main
cd ./scripts
./chart_releaser.sh --helm-chart-folder testkube-$SERVICE
strip_v: true

env:
SERVICE: dashboard
GH_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
RELEASE_VERSION: ${{ steps.tag.outputs.tag }}
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.CI_BOT_TOKEN }}
repository: kubeshop/testkube-dashboard-chart
event-type: trigger-workflow-dashboard-chart
client-payload: '{"app_version": "${{ steps.tag.outputs.tag }}"}'
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ COPY --from=build /app/packages/web/build /app/build

COPY ./packages/web/scripts/env.sh /app/init/
COPY ./packages/web/scripts/inject-base-href.sh /app/init/
COPY ./packages/web/scripts/security.sh /app/init/

RUN chmod +x /app/init/env.sh /app/init/inject-base-href.sh && \
chmod a+w /etc/nginx/nginx.conf /app/build/index.html && \
Expand All @@ -49,6 +50,7 @@ CMD [ \
cp -R /app/nginx/. /etc/nginx && \
sh /app/init/env.sh env-config.js && \
sh /app/init/inject-base-href.sh && \
sh /app/init/security.sh && \
export DISABLE_IPV6=\"$([[ \"$ENABLE_IPV6\" = \"true\" ]] && echo \"false\" || echo \"true\")\" && \
envsubst '$DISABLE_IPV6' < /etc/nginx/nginx.conf.tmpl | sed -e '1h;2,$H;$!d;g' -e 's/# cut true.*# end//g' > /etc/nginx/nginx.conf && \
nginx -g \"daemon off;\"" ]
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
<p align="center">
:warning: <strong>The Open Source version of the Testkube Dashboard is being deprecated.
<br />
For further details and next steps, please refer to <a href="https://testkube.io/blog/testkube-dashboard-announcement">our official announcement</a>.</strong>
</p>

---

<p align="center">
<img src="docs/testkube-logo.svg" alt="Testkube Logo" width="80"/>
</p>
Expand Down
4 changes: 4 additions & 0 deletions nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,9 @@ http {
gzip_http_version 1.1;
gzip_min_length 0;
gzip_types text/plain application/javascript text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/vnd.ms-fontobject application/x-font-ttf font/opentype;

#SecurityHeaders


}
}
21 changes: 21 additions & 0 deletions packages/web/scripts/security.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh

API_HOST=$(echo $REACT_APP_API_SERVER_ENDPOINT | sed -e 's|http://||g' -e 's|https://||g')

tempFile=$(mktemp /etc/nginx/tempfile.XXXXXXXX)

cat > "${tempFile}" <<EOF

add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' http://${API_HOST} https://${API_HOST} ws://${API_HOST} wss://${API_HOST} blob:;";
EOF

if [ "${ENABLE_SECURITY_HEADERS}" = "true" ]; then
sed -i "/#SecurityHeaders/r ${tempFile}" /etc/nginx/nginx.conf.tmpl
fi

rm "${tempFile}"

cat /etc/nginx/nginx.conf.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,21 @@ export const StyledMultiLabel = styled.div`
padding: 3px 5px;
`;

export const customStyles: (
validation?: boolean,
stylePlaceholderAsValue?: boolean
) => StylesConfig<Option, true, GroupBase<Option>> = (validation = true, stylePlaceholderAsValue = false) => ({
export const customStyles: (stylePlaceholderAsValue?: boolean) => StylesConfig<Option, true, GroupBase<Option>> = (
stylePlaceholderAsValue = false
) => ({
container: styles => ({...styles, width: '100%'}),
input: styles => ({...styles, color: Colors.slate200, fontWeight: 400}),
valueContainer: (styles, props) => ({
...styles,
backgroundColor: props.isDisabled ? 'transparent' : Colors.slate800,
gap: '4px',
}),
placeholder: styles => ({
...styles,
color: stylePlaceholderAsValue ? Colors.slate200 : Colors.slate500,
fontWeight: 400,
}),
control: (styles, props) => ({
...styles,
borderColor: validation ? 'transparent' : Colors.pink500,
backgroundColor: props.isDisabled ? '#1e293b80' : Colors.slate800,
minHeight: '44px',
}),
indicatorSeparator: styles => ({...styles, width: 0}),
dropdownIndicator: styles => ({
...styles,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import usePressEnter from '@hooks/usePressEnter';

import {Option} from '@models/form';

import Colors from '@src/styles/Colors';

import {customStyles, customTheme} from './CreatableMultiSelect.styled';
import {
DefaultDropdownIndicator,
Expand Down Expand Up @@ -91,7 +93,15 @@ const CreatableMultiSelect: React.FC<MultiSelectProps> = props => {
}}
formatCreateLabel={formatCreateLabel}
theme={customTheme}
styles={customStyles(validation, stylePlaceholderAsValue)}
styles={{
...customStyles(stylePlaceholderAsValue),
control: (styles, p) => ({
...styles,
borderColor: validation ? Colors.pink500 : 'transparent',
backgroundColor: p.isDisabled ? '#1e293b80' : Colors.slate800,
minHeight: '44px',
}),
}}
components={{
Option: CustomOptionComponent,
MultiValueLabel: CustomMultiValueLabelComponent,
Expand Down
64 changes: 7 additions & 57 deletions packages/web/src/components/molecules/LogOutput/LogOutput.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,14 @@
import React, {Fragment, createElement, memo, useEffect, useRef} from 'react';
import {createPortal} from 'react-dom';
import React, {memo} from 'react';

import {useSearch} from '@molecules/LogOutput/useSearch';
import {isFeatureEnabled} from '@src/utils/apiInfo';

import {useTestsSlot} from '@plugins/tests-and-test-suites/hooks';

import {useLogOutputField, useLogOutputPick, useLogOutputSync} from '@store/logOutput';

import FullscreenLogOutput from './FullscreenLogOutput';
import {LogOutputWrapper} from './LogOutput.styled';
import LogOutputPure, {LogOutputPureRef} from './LogOutputPure';
import {LogOutputProps, useLogOutput} from './useLogOutput';
import LogOutputV1 from './LogOutputV1';
import LogOutputV2 from './LogOutputV2';
import {LogOutputProps} from './useLogOutput';

const LogOutput: React.FC<LogOutputProps> = props => {
const {isRunning} = props;

const logRef = useRef<LogOutputPureRef>(null);
const options = useLogOutput(props);
const {isFullscreen} = useLogOutputPick('isFullscreen');
const fullscreenContainer = document.querySelector('#log-output-container')!;

// Search logic
const [, setSearching] = useLogOutputField('searching');
const [searchQuery] = useLogOutputField('searchQuery');

useEffect(() => {
if (!searchQuery) {
setSearching(false);
}
}, [searchQuery, setSearching]);

const search = useSearch({searchQuery, output: options.logs});
useLogOutputSync({
searching: search.loading,
searchResults: search.list,
searchLinesMap: search.map,
});

const [searchIndex, setSearchIndex] = useLogOutputField('searchIndex');
useEffect(() => {
if (search.list.length === 0) {
// Do nothing
} else if (searchIndex >= search.list.length) {
setSearchIndex(0);
} else {
const highlight = search.list[searchIndex];
logRef.current?.console?.scrollToLine(highlight.line);
}
}, [searchIndex, searchQuery, search.loading, logRef.current?.console]);

return (
<>
<LogOutputWrapper>
{/* eslint-disable-next-line react/no-array-index-key */}
{useTestsSlot('logOutputTop').map((element, i) => createElement(Fragment, {key: i}, element))}
<LogOutputPure ref={logRef} isRunning={isRunning} {...options} />
</LogOutputWrapper>
{isFullscreen ? createPortal(<FullscreenLogOutput {...options} />, fullscreenContainer) : null}
</>
);
const isV2 = isFeatureEnabled('logsV2');
return <>{isV2 ? <LogOutputV2 {...props} /> : <LogOutputV1 {...props} />}</>;
};

export default memo(LogOutput);
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {CopyButton, DownloadButton} from '@atoms';
import useLocation from '@hooks/useLocation';
import useSecureContext from '@hooks/useSecureContext';

import {isFeatureEnabled} from '@src/utils/apiInfo';

import FullscreenAction from './FullscreenAction';
import {StyledLogOutputActionsContainer} from './LogOutput.styled';
import SearchAction from './SearchAction';
Expand All @@ -21,15 +23,18 @@ const LogOutputActions: React.FC<LogOutputActionsProps> = props => {
const isSecureContext = useSecureContext();
const filename = useLocation().lastPathSegment;

const isV2 = isFeatureEnabled('logsV2');

return (
<StyledLogOutputActionsContainer>
<SearchAction />
{isV2 ? null : <SearchAction />}
{isSecureContext ? (
<CopyButton content={strippedLogOutput} />
) : (
<DownloadButton filename={filename} extension="log" content={strippedLogOutput} />
)}
<FullscreenAction key="fullscreen-log-action" />

{isV2 ? null : <FullscreenAction key="fullscreen-log-action" />}
</StyledLogOutputActionsContainer>
);
};
Expand Down
63 changes: 63 additions & 0 deletions packages/web/src/components/molecules/LogOutput/LogOutputV1.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {Fragment, createElement, memo, useEffect, useRef} from 'react';
import {createPortal} from 'react-dom';

import {useTestsSlot} from '@plugins/tests-and-test-suites/hooks';

import {useLogOutputField, useLogOutputPick, useLogOutputSync} from '@store/logOutput';

import FullscreenLogOutput from './FullscreenLogOutput';
import {LogOutputWrapper} from './LogOutput.styled';
import LogOutputPure, {LogOutputPureRef} from './LogOutputPure';
import {LogOutputProps, useLogOutput} from './useLogOutput';
import {useSearch} from './useSearch';

const LogOutputV1 = (props: LogOutputProps) => {
const {isRunning} = props;

const logRef = useRef<LogOutputPureRef>(null);
const options = useLogOutput(props);
const {isFullscreen} = useLogOutputPick('isFullscreen');
const fullscreenContainer = document.querySelector('#log-output-container')!;

// Search logic
const [, setSearching] = useLogOutputField('searching');
const [searchQuery] = useLogOutputField('searchQuery');

useEffect(() => {
if (!searchQuery) {
setSearching(false);
}
}, [searchQuery, setSearching]);

const search = useSearch({searchQuery, output: options.logs});
useLogOutputSync({
searching: search.loading,
searchResults: search.list,
searchLinesMap: search.map,
});

const [searchIndex, setSearchIndex] = useLogOutputField('searchIndex');
useEffect(() => {
if (search.list.length === 0) {
// Do nothing
} else if (searchIndex >= search.list.length) {
setSearchIndex(0);
} else {
const highlight = search.list[searchIndex];
logRef.current?.console?.scrollToLine(highlight.line);
}
}, [searchIndex, searchQuery, search.loading, logRef.current?.console]);

return (
<>
<LogOutputWrapper>
{/* eslint-disable-next-line react/no-array-index-key */}
{useTestsSlot('logOutputTop').map((element, i) => createElement(Fragment, {key: i}, element))}
<LogOutputPure ref={logRef} isRunning={isRunning} {...options} />
</LogOutputWrapper>
{isFullscreen ? createPortal(<FullscreenLogOutput {...options} />, fullscreenContainer) : null}
</>
);
};

export default memo(LogOutputV1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import styled from 'styled-components';

import Colors from '@src/styles/Colors';

export const SourceList = styled.ul<{$open?: boolean; $root?: boolean}>`
display: flex;
flex-direction: column;
list-style: none;
padding: 0;
margin: 0;
${({$open}) => ($open ? 'flex: 1;' : '')}
${({$root}) => ($root ? 'height: 100%;' : '')}
`;

export const SourceSection = styled.li<{$open?: boolean}>`
border: 1px solid ${Colors.slate700};
border-radius: 3px;
display: flex;
flex-direction: column;
margin-bottom: 8px;
${({$open}) =>
$open
? 'flex: 1;'
: `
${SourceContent} {
display: none;
}
`}
`;

export const SourceContent = styled.div<{$empty?: boolean}>`
position: relative;
display: flex;
align-items: stretch;
margin: 0;
background: ${Colors.slate900};
min-height: 300px;
flex: 1;

${({$empty}) => ($empty ? 'min-height: 80px;' : '')}
`;

export const SourceHeader = styled.header`
display: flex;
align-items: center;
background: ${Colors.slate900};
padding: 10px 16px;
gap: 16px;
user-select: none;
cursor: pointer;
width: 100%;
`;

export const Container = styled.div`
display: flex;
flex-direction: column;
`;
Loading
Loading