mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
feat: add build option for a ClickHouse bundled build (#1717)
References HDX-3265 Closes HDX-3389 Adds a build that we can use in ClickHouse. This build enables bundling HyperDX with ClickHouse https://github.com/ClickHouse/ClickHouse/pull/96597
This commit is contained in:
parent
ebbfa2410e
commit
ce09b59b1d
15 changed files with 264 additions and 20 deletions
5
.changeset/large-wolves-destroy.md
Normal file
5
.changeset/large-wolves-destroy.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": patch
|
||||
---
|
||||
|
||||
feat: add static build generation
|
||||
43
.github/workflows/main.yml
vendored
43
.github/workflows/main.yml
vendored
|
|
@ -306,3 +306,46 @@ jobs:
|
|||
echo "::error::One or more E2E test shards failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
clickhouse-static-build:
|
||||
name: ClickHouse Bundle Build
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Build App
|
||||
run: yarn build:clickhouse
|
||||
|
||||
- name: Verify output directory exists and has size
|
||||
run: |
|
||||
if [ ! -d "packages/app/out" ]; then
|
||||
echo "::error::Output directory 'packages/app/out' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Output directory exists"
|
||||
# Calculate size in bytes and convert to MB
|
||||
size_kb=$(du -sk packages/app/out | cut -f1)
|
||||
size_mb=$((size_kb / 1024))
|
||||
echo "Output directory size: ${size_mb} MB (${size_kb} KB)"
|
||||
if [ "$size_mb" -lt 10 ]; then
|
||||
echo "::error::Output directory is only ${size_mb} MB, expected at least 10 MB"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Output directory size check passed (${size_mb} MB)"
|
||||
|
|
|
|||
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
|
|
@ -216,3 +216,30 @@ jobs:
|
|||
tag: TAG
|
||||
}
|
||||
});
|
||||
|
||||
notify_clickhouse_clickstack:
|
||||
needs: [check_changesets, release, check_release_app_pushed]
|
||||
if: needs.check_release_app_pushed.outputs.app_was_pushed == 'true'
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Load Environment Variables from .env
|
||||
uses: xom9ikk/dotenv@v2
|
||||
- name: Notify ClickHouse/clickhouse-clickstack Downstream
|
||||
uses: actions/github-script@v7
|
||||
continue-on-error: true
|
||||
env:
|
||||
TAG: ${{ env.IMAGE_VERSION }}${{ env.IMAGE_VERSION_SUB_TAG }}
|
||||
with:
|
||||
github-token: ${{ secrets.CH_BOT_PAT }}
|
||||
script: |
|
||||
const { TAG } = process.env;
|
||||
const result = await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'ClickHouse',
|
||||
repo: 'clickhouse-clickstack',
|
||||
workflow_id: 'sync-hyperdx.yml',
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
tag: TAG,
|
||||
}
|
||||
});
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -34,6 +34,7 @@ packages/app/.pnp.js
|
|||
packages/app/.vercel
|
||||
packages/app/coverage
|
||||
packages/app/out
|
||||
packages/app/tmp
|
||||
packages/app/next-env.d.ts
|
||||
|
||||
# optional npm cache directory
|
||||
|
|
@ -79,4 +80,4 @@ docker-compose.prod.yml
|
|||
.nx/
|
||||
|
||||
# webstorm
|
||||
.idea
|
||||
.idea
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@
|
|||
"app:dev:local": "concurrently -k -n 'APP,COMMON-UTILS' -c 'blue.bold,magenta' 'nx run @hyperdx/app:dev:local' 'nx run @hyperdx/common-utils:dev'",
|
||||
"app:lint": "nx run @hyperdx/app:ci:lint",
|
||||
"app:storybook": "nx run @hyperdx/app:storybook",
|
||||
"build:clickhouse": "nx run @hyperdx/common-utils:build && nx run @hyperdx/app:build:clickhouse",
|
||||
"run:clickhouse": "nx run @hyperdx/app:run:clickhouse",
|
||||
"dev": "yarn build:common-utils && dotenvx run --convention=nextjs -- docker compose -f docker-compose.dev.yml up -d && yarn app:dev && docker compose -f docker-compose.dev.yml down",
|
||||
"dev:local": "IS_LOCAL_APP_MODE='DANGEROUSLY_is_local_app_mode💀' yarn dev",
|
||||
"dev:down": "docker compose -f docker-compose.dev.yml down",
|
||||
|
|
|
|||
|
|
@ -81,6 +81,14 @@ const nextConfig = {
|
|||
}
|
||||
: {}),
|
||||
}),
|
||||
...(process.env.NEXT_PUBLIC_CLICKHOUSE_BUILD
|
||||
? {
|
||||
assetPrefix: '/clickstack',
|
||||
basePath: '/clickstack',
|
||||
images: { unoptimized: true },
|
||||
output: 'export',
|
||||
}
|
||||
: {}),
|
||||
logging: {
|
||||
incomingRequests: {
|
||||
// We also log this in the API server, so we don't want to log it twice.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
"dev": "npx dotenv -e .env.development -- next dev --webpack",
|
||||
"dev:local": "NEXT_PUBLIC_IS_LOCAL_MODE=true npx dotenv -e .env.development -- next dev --webpack",
|
||||
"build": "next build --webpack",
|
||||
"build:clickhouse": "NEXT_PUBLIC_THEME=clickstack NEXT_PUBLIC_IS_LOCAL_MODE=true NEXT_PUBLIC_CLICKHOUSE_BUILD=true next build --webpack && node scripts/prepare-clickhouse-build-export.js",
|
||||
"run:clickhouse": "test -d out && npx rimraf tmp && mkdir tmp && cp -r out tmp/clickstack && echo 'visit http://localhost:3000/clickstack to start' && npx serve tmp -l 3000 || echo 'run build:clickhouse first'",
|
||||
"start": "next start",
|
||||
"lint": "npx eslint --quiet . --ext .ts,.tsx",
|
||||
"lint:fix": "npx eslint . --ext .ts,.tsx --fix",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Head, Html, Main, NextScript } from 'next/document';
|
||||
|
||||
import { IS_CLICKHOUSE_BUILD } from '@/config';
|
||||
import { ibmPlexMono, inter, roboto, robotoMono } from '@/fonts';
|
||||
|
||||
// Get theme class for SSR - must match ThemeProvider's resolution
|
||||
|
|
@ -28,8 +29,12 @@ export default function Document() {
|
|||
<Head>
|
||||
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
|
||||
<script src="/__ENV.js" />
|
||||
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
|
||||
<script src="/pyodide/pyodide.js"></script>
|
||||
{!IS_CLICKHOUSE_BUILD && (
|
||||
<>
|
||||
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
|
||||
<script src="/pyodide/pyodide.js"></script>
|
||||
</>
|
||||
)}
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
|
|
|
|||
41
packages/app/scripts/move-to-clickhouse.js
Normal file
41
packages/app/scripts/move-to-clickhouse.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Move out-gzipped to replace ClickHouse clickstack directory
|
||||
function moveToClickHouse() {
|
||||
const outGzippedDir = path.join(__dirname, '../out-gzipped');
|
||||
const clickStackDir = path.join(
|
||||
__dirname,
|
||||
'../../../../ClickHouse/programs/server/clickstack',
|
||||
);
|
||||
|
||||
if (!fs.existsSync(outGzippedDir)) {
|
||||
console.error('Error: out-gzipped directory does not exist');
|
||||
console.error('Run build:clickhouse first to generate the build');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const clickStackParentDir = path.dirname(clickStackDir);
|
||||
if (!fs.existsSync(clickStackParentDir)) {
|
||||
console.error(
|
||||
`Error: ClickHouse server directory does not exist at ${clickStackParentDir}`,
|
||||
);
|
||||
console.error(
|
||||
'Make sure the ClickHouse repository is cloned at ../ClickHouse',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Remove existing clickstack directory if it exists
|
||||
if (fs.existsSync(clickStackDir)) {
|
||||
console.log('Removing existing clickstack directory...');
|
||||
fs.rmSync(clickStackDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// Copy out-gzipped to clickstack
|
||||
console.log(`Moving out-gzipped to ${clickStackDir}...`);
|
||||
fs.cpSync(outGzippedDir, clickStackDir, { recursive: true });
|
||||
console.log('✓ Successfully moved build to ClickHouse clickstack directory');
|
||||
}
|
||||
|
||||
moveToClickHouse();
|
||||
58
packages/app/scripts/prepare-clickhouse-build-export.js
Normal file
58
packages/app/scripts/prepare-clickhouse-build-export.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const OUT_DIR = path.join(__dirname, '../out');
|
||||
const PYODIDE_PATH = path.join(OUT_DIR, 'pyodide');
|
||||
const ALLOWED_EXTENSIONS = [
|
||||
'.html',
|
||||
'.js',
|
||||
'.css',
|
||||
'.map',
|
||||
'.woff2',
|
||||
'.png',
|
||||
'.svg',
|
||||
'.ico',
|
||||
];
|
||||
|
||||
// removes pyodide from a next static build. We want a small bundle size, so that feature would just be ignored
|
||||
if (fs.existsSync(PYODIDE_PATH)) {
|
||||
fs.rmSync(PYODIDE_PATH, { recursive: true, force: true });
|
||||
console.log('Removed pyodide from static build');
|
||||
}
|
||||
|
||||
// Remove all files that are not in ALLOWED_EXTENSIONS
|
||||
function removeNonEssentialFiles(dir) {
|
||||
let removedCount = 0;
|
||||
|
||||
function walkDir(currentPath) {
|
||||
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentPath, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
walkDir(fullPath);
|
||||
} else if (entry.isFile()) {
|
||||
const ext = path.extname(entry.name).toLowerCase();
|
||||
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
||||
fs.unlinkSync(fullPath);
|
||||
console.log(
|
||||
`Removed non-essential file: ${path.relative(OUT_DIR, fullPath)}`,
|
||||
);
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walkDir(dir);
|
||||
console.log(`Removed ${removedCount} non-essential file(s)`);
|
||||
}
|
||||
|
||||
// Execute cleanup and optimization
|
||||
if (fs.existsSync(OUT_DIR)) {
|
||||
removeNonEssentialFiles(OUT_DIR);
|
||||
} else {
|
||||
console.error('No out directory found. Build preparation failed.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ import {
|
|||
IconSitemap,
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
import { IS_CLICKHOUSE_BUILD } from '@/config';
|
||||
import {
|
||||
useAllFields,
|
||||
useColumns,
|
||||
|
|
@ -1184,9 +1185,11 @@ const DBSearchPageFiltersComponent = ({
|
|||
<Text size="xs">Event Deltas</Text>
|
||||
</Tabs.Tab>
|
||||
)}
|
||||
<Tabs.Tab value="pattern" size="xs" h="24px">
|
||||
<Text size="xs">Event Patterns</Text>
|
||||
</Tabs.Tab>
|
||||
{!IS_CLICKHOUSE_BUILD && (
|
||||
<Tabs.Tab value="pattern" size="xs" h="24px">
|
||||
<Text size="xs">Event Patterns</Text>
|
||||
</Tabs.Tab>
|
||||
)}
|
||||
</Tabs.List>
|
||||
</Tabs>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { notifications } from '@mantine/notifications';
|
|||
import { IconArrowLeft } from '@tabler/icons-react';
|
||||
|
||||
import { ConnectionForm } from '@/components/ConnectionForm';
|
||||
import { IS_LOCAL_MODE } from '@/config';
|
||||
import { IS_CLICKHOUSE_BUILD, IS_LOCAL_MODE } from '@/config';
|
||||
import { useConnections, useCreateConnection } from '@/connection';
|
||||
import { useMetadataWithSettings } from '@/hooks/useMetadata';
|
||||
import {
|
||||
|
|
@ -517,6 +517,7 @@ function OnboardingModalComponent({
|
|||
]);
|
||||
|
||||
const handleDemoServerClick = useCallback(async () => {
|
||||
if (IS_CLICKHOUSE_BUILD) return;
|
||||
try {
|
||||
if (sources) {
|
||||
for (const source of sources) {
|
||||
|
|
@ -724,15 +725,19 @@ function OnboardingModalComponent({
|
|||
You can always add and edit connections later.
|
||||
</Text>
|
||||
)}
|
||||
<Divider label="OR" my="md" />
|
||||
<Button
|
||||
data-testid="demo-server-button"
|
||||
variant="secondary"
|
||||
w="100%"
|
||||
onClick={handleDemoServerClick}
|
||||
>
|
||||
Connect to Demo Server
|
||||
</Button>
|
||||
{!IS_CLICKHOUSE_BUILD && (
|
||||
<>
|
||||
<Divider label="OR" my="md" />
|
||||
<Button
|
||||
data-testid="demo-server-button"
|
||||
variant="secondary"
|
||||
w="100%"
|
||||
onClick={handleDemoServerClick}
|
||||
>
|
||||
Connect to Demo Server
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{step === 'auto-detect' && (
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ export const IS_OSS = process.env.NEXT_PUBLIC_IS_OSS ?? 'true' === 'true';
|
|||
export const IS_LOCAL_MODE = //true;
|
||||
// @ts-ignore
|
||||
(process.env.NEXT_PUBLIC_IS_LOCAL_MODE ?? 'false') === 'true';
|
||||
export const IS_CLICKHOUSE_BUILD =
|
||||
process.env.NEXT_PUBLIC_CLICKHOUSE_BUILD === 'true';
|
||||
|
||||
// Features in development
|
||||
export const IS_K8S_DASHBOARD_ENABLED = true;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { Box, Center, Text } from '@mantine/core';
|
||||
|
||||
import AppNav from '@/components/AppNav';
|
||||
import { IS_CLICKHOUSE_BUILD } from '@/config';
|
||||
|
||||
import { HDXSpotlightProvider } from './Spotlights';
|
||||
|
||||
|
|
@ -16,10 +19,39 @@ import { HDXSpotlightProvider } from './Spotlights';
|
|||
export const withAppNav = (page: React.ReactNode) => {
|
||||
return (
|
||||
<HDXSpotlightProvider>
|
||||
<div className="d-flex">
|
||||
<AppNav fixed />
|
||||
<div className="w-100 min-w-0" style={{ minWidth: 0 }}>
|
||||
{page}
|
||||
<div
|
||||
className={
|
||||
IS_CLICKHOUSE_BUILD ? 'app-layout-with-banner' : 'app-layout'
|
||||
}
|
||||
>
|
||||
{IS_CLICKHOUSE_BUILD && (
|
||||
<Box bg="var(--color-text-primary)">
|
||||
<Center>
|
||||
<Text py="xs" size="sm" c="var(--color-text-inverted)">
|
||||
This is not recommended for production use and is lacking core
|
||||
ClickStack features such as alerts and saved searches. For a
|
||||
proper experience, visit the{' '}
|
||||
<strong>
|
||||
<Link
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/getting-started"
|
||||
target="_blank"
|
||||
rel="noopener norefeer"
|
||||
>
|
||||
ClickStack Docs
|
||||
</Link>
|
||||
</strong>
|
||||
</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
)}
|
||||
<div className="d-flex" style={{ height: '100%', overflow: 'hidden' }}>
|
||||
<AppNav />
|
||||
<div
|
||||
className="w-100 min-w-0"
|
||||
style={{ minWidth: 0, overflow: 'auto' }}
|
||||
>
|
||||
{page}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</HDXSpotlightProvider>
|
||||
|
|
|
|||
|
|
@ -12,3 +12,13 @@ a {
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.app-layout {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.app-layout-with-banner {
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue