From 98dd1e2e8eb86d78dc279d46abb3760f32697cfa Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Thu, 20 Mar 2025 14:51:20 -0400 Subject: [PATCH 1/6] Try and get self-hosted dashbaord built as daily precompile --- .github/workflows/precompile.yml | 54 ++++- .../dashboard-self-hosted/next.config.js | 22 +- .../dashboard-self-hosted/package.json | 1 + .../components/DeploymentCredentialsForm.tsx | 66 ++++++ .../src/components/DeploymentList.tsx | 73 +++++++ .../src/lib/checkDeploymentInfo.ts | 37 ++++ .../dashboard-self-hosted/src/pages/_app.tsx | 202 ++++++++++++------ 7 files changed, 376 insertions(+), 79 deletions(-) create mode 100644 npm-packages/dashboard-self-hosted/src/components/DeploymentCredentialsForm.tsx create mode 100644 npm-packages/dashboard-self-hosted/src/components/DeploymentList.tsx create mode 100644 npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 4a507b597..eb9dd7328 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -6,7 +6,57 @@ on: workflow_dispatch: jobs: - release: + release_dashboard: + name: Build Convex Dashboard + runs-on: [self-hosted, aws, x64, xlarge] + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + + - name: NPM install globals + run: npm ci --prefix scripts + + - name: Rush install + run: | + just rush install + + - name: Build dashboard dependencies + run: | + just rush build -T dashboard-self-hosted + + - name: Build dashboard + run: | + cd npm-packages/dashboard-self-hosted && npm run build:export + + - name: Zip output + run: | + zip -r dashboard.zip npm-packages/dashboard-self-hosted/out + + - name: Precompute release name + id: release_name + shell: bash + run: | + echo "RELEASE_NAME=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Create Upload Precompiled Artifacts + id: create_release + uses: softprops/action-gh-release@v2 + with: + files: | + dashboard.zip + tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} + name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} + draft: true + prerelease: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + release_backend: strategy: fail-fast: false matrix: @@ -97,7 +147,7 @@ jobs: LICENSE.md tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} - draft: false + draft: true prerelease: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/npm-packages/dashboard-self-hosted/next.config.js b/npm-packages/dashboard-self-hosted/next.config.js index 46c953b28..d7f6767ee 100644 --- a/npm-packages/dashboard-self-hosted/next.config.js +++ b/npm-packages/dashboard-self-hosted/next.config.js @@ -31,11 +31,13 @@ const securityHeaders = [ }, ]; -/** @type {import('next').NextConfig} */ -const nextConfig = { - swcMinify: true, - transpilePackages: [], - reactStrictMode: true, +const optionsForExport = { + output: "export", + images: { + unoptimized: true, + }, +}; +const optionsForBuild = { output: "standalone", async headers() { return [ @@ -46,6 +48,14 @@ const nextConfig = { }, ]; }, +}; + +/** @type {import('next').NextConfig} */ +const nextConfig = { + swcMinify: true, + transpilePackages: [], + reactStrictMode: true, + ...(process.env.BUILD_TYPE === "export" ? optionsForExport : optionsForBuild), experimental: { webpackBuildWorker: true, }, @@ -103,4 +113,4 @@ const nextConfig = { }, }; -module.exports = nextConfig; +module.exports = nextConfig; \ No newline at end of file diff --git a/npm-packages/dashboard-self-hosted/package.json b/npm-packages/dashboard-self-hosted/package.json index acb23f95b..ae1077a83 100644 --- a/npm-packages/dashboard-self-hosted/package.json +++ b/npm-packages/dashboard-self-hosted/package.json @@ -6,6 +6,7 @@ "dev": "npm run build:generated && next dev --port 6790", "build": "npm run build:generated && next build", "build:generated": "python3 ../dashboard-common/scripts/build-convexServerTypes.py", + "build:export": "BUILD_TYPE=export NEXT_PUBLIC_DEFAULT_LIST_DEPLOYMENTS_API_PORT=6791 npm run build", "start": "next start -p 6791", "lint": "next lint --max-warnings 0 --dir src/ && tsc", "lint:fix": "next lint --fix --max-warnings 0 --dir src/" diff --git a/npm-packages/dashboard-self-hosted/src/components/DeploymentCredentialsForm.tsx b/npm-packages/dashboard-self-hosted/src/components/DeploymentCredentialsForm.tsx new file mode 100644 index 000000000..98e4ac351 --- /dev/null +++ b/npm-packages/dashboard-self-hosted/src/components/DeploymentCredentialsForm.tsx @@ -0,0 +1,66 @@ +import { EnterIcon, EyeNoneIcon, EyeOpenIcon } from "@radix-ui/react-icons"; +import { Button } from "dashboard-common/elements/Button"; +import { TextInput } from "dashboard-common/elements/TextInput"; +import { useState } from "react"; + +export function DeploymentCredentialsForm({ + onSubmit, + initialAdminKey, + initialDeploymentUrl, +}: { + onSubmit: (adminKey: string, deploymentUrl: string) => Promise; + initialAdminKey: string | null; + initialDeploymentUrl: string | null; +}) { + const [draftAdminKey, setDraftAdminKey] = useState( + initialAdminKey ?? "", + ); + const [draftDeploymentUrl, setDraftDeploymentUrl] = useState( + initialDeploymentUrl ?? "", + ); + const [showKey, setShowKey] = useState(false); + return ( +
{ + e.preventDefault(); + void onSubmit(draftAdminKey, draftDeploymentUrl); + }} + > + { + setDraftDeploymentUrl(e.target.value); + }} + /> + { + setShowKey(!showKey); + }} + description="The admin key is required every time you open the dashboard." + onChange={(e) => { + setDraftAdminKey(e.target.value); + }} + /> + + + ); +} \ No newline at end of file diff --git a/npm-packages/dashboard-self-hosted/src/components/DeploymentList.tsx b/npm-packages/dashboard-self-hosted/src/components/DeploymentList.tsx new file mode 100644 index 000000000..d4af1b8ff --- /dev/null +++ b/npm-packages/dashboard-self-hosted/src/components/DeploymentList.tsx @@ -0,0 +1,73 @@ +import { Button } from "dashboard-common/elements/Button"; +import { useEffect, useState } from "react"; +import { useLocalStorage } from "react-use"; + +export type Deployment = { + name: string; + adminKey: string; + url: string; +}; + +export function DeploymentList({ + listDeploymentsApiUrl, + onError, + onSelect, +}: { + listDeploymentsApiUrl: string; + onError: (error: string) => void; + onSelect: (adminKey: string, deploymentUrl: string) => Promise; +}) { + const [lastStoredDeployment, setLastStoredDeployment] = useLocalStorage( + "lastDeployment", + "", + ); + const [deployments, setDeployments] = useState([]); + useEffect(() => { + const f = async () => { + let resp: Response; + try { + resp = await fetch(listDeploymentsApiUrl); + } catch (e) { + onError(`Failed to fetch deployments: ${e}`); + return; + } + if (!resp.ok) { + const text = await resp.text(); + onError(`Failed to fetch deployments: ${resp.statusText} ${text}`); + return; + } + let data: { deployments: Deployment[] }; + try { + data = await resp.json(); + } catch (e) { + onError(`Failed to parse deployments: ${e}`); + return; + } + setDeployments(data.deployments); + const lastDeployment = data.deployments.find( + (d: Deployment) => d.name === lastStoredDeployment, + ); + if (lastDeployment) { + void onSelect(lastDeployment.adminKey, lastDeployment.url); + } + }; + void f(); + }, [listDeploymentsApiUrl, onError, onSelect, lastStoredDeployment]); + return ( +
+

Select a deployment:

+ {deployments.map((d) => ( + + ))} +
+ ); +} \ No newline at end of file diff --git a/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts b/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts new file mode 100644 index 000000000..cdee05660 --- /dev/null +++ b/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts @@ -0,0 +1,37 @@ +async function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + const MAX_RETRIES = 3; + const MAX_RETRIES_DELAY_MS = 500; + + export async function checkDeploymentInfo( + adminKey: string, + deploymentUrl: string, + ): Promise { + let retries = 0; + while (retries < MAX_RETRIES) { + try { + const resp = await fetch(new URL("/api/check_admin_key", deploymentUrl), { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Convex ${adminKey}`, + "Convex-Client": "dashboard-0.0.0", + }, + }); + if (resp.ok) { + return true; + } + if (resp.status === 404) { + return null; + } + } catch (e) { + // Do nothing + } + await sleep(MAX_RETRIES_DELAY_MS); + retries++; + } + retur \ No newline at end of file diff --git a/npm-packages/dashboard-self-hosted/src/pages/_app.tsx b/npm-packages/dashboard-self-hosted/src/pages/_app.tsx index 6b74dd80d..21994b3f4 100644 --- a/npm-packages/dashboard-self-hosted/src/pages/_app.tsx +++ b/npm-packages/dashboard-self-hosted/src/pages/_app.tsx @@ -5,23 +5,15 @@ import Head from "next/head"; import { useQuery } from "convex/react"; import udfs from "dashboard-common/udfs"; import { useSessionStorage } from "react-use"; -import { - EnterIcon, - ExitIcon, - EyeNoneIcon, - EyeOpenIcon, - GearIcon, -} from "@radix-ui/react-icons"; +import { ExitIcon, GearIcon } from "@radix-ui/react-icons"; import { ConvexLogo } from "dashboard-common/elements/ConvexLogo"; import { ToastContainer } from "dashboard-common/elements/ToastContainer"; import { ThemeConsumer } from "dashboard-common/elements/ThemeConsumer"; import { Favicon } from "dashboard-common/elements/Favicon"; import { ToggleTheme } from "dashboard-common/elements/ToggleTheme"; import { Menu, MenuItem } from "dashboard-common/elements/Menu"; -import { TextInput } from "dashboard-common/elements/TextInput"; -import { Button } from "dashboard-common/elements/Button"; import { ThemeProvider } from "next-themes"; -import React, { useEffect, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { ErrorBoundary } from "components/ErrorBoundary"; import { DeploymentDashboardLayout } from "dashboard-common/layouts/DeploymentDashboardLayout"; import { @@ -31,11 +23,20 @@ import { DeploymentInfoContext, } from "dashboard-common/lib/deploymentContext"; import { Tooltip } from "dashboard-common/elements/Tooltip"; +import { DeploymentCredentialsForm } from "components/DeploymentCredentialsForm"; +import { DeploymentList } from "components/DeploymentList"; +import { checkDeploymentInfo } from "lib/checkDeploymentInfo"; function App({ Component, - pageProps: { deploymentUrl, ...pageProps }, -}: AppProps & { pageProps: { deploymentUrl: string } }) { + pageProps: { deploymentUrl, adminKey, listDeploymentsApiUrl, ...pageProps }, +}: AppProps & { + pageProps: { + deploymentUrl: string | null; + adminKey: string | null; + listDeploymentsApiUrl: string | null; + }; +}) { return ( <> @@ -47,7 +48,11 @@ function App({
- + @@ -62,35 +67,61 @@ function App({ ); } +const LIST_DEPLOYMENTS_API_PORT_QUERY_PARAM = "a"; + +function normalizeUrl(url: string) { + try { + const parsedUrl = new URL(url); + // remove trailing slash + return parsedUrl.href.replace(/\/$/, ""); + } catch (e) { + return null; + } +} + App.getInitialProps = async ({ ctx }: { ctx: { req?: any } }) => { // On server-side, get from process.env if (ctx.req) { - // Tolerate a trailing slash on the url e.g. https://example.com/ should be valid and stripped to https://example.com - const deploymentUrl = process.env.NEXT_PUBLIC_DEPLOYMENT_URL?.replace( - /\/$/, - "", - ); - if (!deploymentUrl) { - throw new Error( - "NEXT_PUBLIC_DEPLOYMENT_URL environment variable is not set", - ); + // This is a relative URL, so add localhost as the origin so it can be parsed + const url = new URL(ctx.req.url, "http://127.0.0.1"); + + let deploymentUrl: string | null = null; + if (process.env.NEXT_PUBLIC_DEPLOYMENT_URL) { + deploymentUrl = normalizeUrl(process.env.NEXT_PUBLIC_DEPLOYMENT_URL); } + + const listDeploymentsApiPort = + url.searchParams.get(LIST_DEPLOYMENTS_API_PORT_QUERY_PARAM) ?? + process.env.NEXT_PUBLIC_DEFAULT_LIST_DEPLOYMENTS_API_PORT; + let listDeploymentsApiUrl: string | null = null; + if (listDeploymentsApiPort) { + const port = parseInt(listDeploymentsApiPort); + if (!Number.isNaN(port)) { + listDeploymentsApiUrl = normalizeUrl(`http://127.0.0.1:${port}`); + } + } + return { pageProps: { deploymentUrl, + adminKey: null, + listDeploymentsApiUrl, }, }; } // On client-side navigation, get from window.__NEXT_DATA__ - const deploymentUrl = window.__NEXT_DATA__?.props?.pageProps?.deploymentUrl; - if (!deploymentUrl) { - throw new Error("deploymentUrl not found in __NEXT_DATA__"); - } - + const clientSideDeploymentUrl = + window.__NEXT_DATA__?.props?.pageProps?.deploymentUrl ?? null; + const clientSideAdminKey = + window.__NEXT_DATA__?.props?.pageProps?.adminKey ?? null; + const clientSideListDeploymentsApiUrl = + window.__NEXT_DATA__?.props?.pageProps?.listDeploymentsApiUrl ?? null; return { pageProps: { - deploymentUrl, + deploymentUrl: clientSideDeploymentUrl ?? null, + adminKey: clientSideAdminKey ?? null, + listDeploymentsApiUrl: clientSideListDeploymentsApiUrl ?? null, }, }; }; @@ -161,78 +192,107 @@ const deploymentInfo: Omit = { projectsURI: "", deploymentsURI: "", isSelfHosted: true, - enableIndexFilters: false, }; function DeploymentInfoProvider({ children, deploymentUrl, + adminKey, + listDeploymentsApiUrl, }: { children: React.ReactNode; - deploymentUrl: string; + deploymentUrl: string | null; + adminKey: string | null; + listDeploymentsApiUrl: string | null; }) { - const [adminKey, setAdminKey] = useSessionStorage("adminKey", ""); - const [draftAdminKey, setDraftAdminKey] = useState(""); - - const [showKey, setShowKey] = useState(false); + const [shouldListDeployments, setShouldListDeployments] = useState( + listDeploymentsApiUrl !== null, + ); + const [isValidDeploymentInfo, setIsValidDeploymentInfo] = useState< + boolean | null + >(null); + const [storedAdminKey, setStoredAdminKey] = useSessionStorage("adminKey", ""); + const [storedDeploymentUrl, setStoredDeploymentUrl] = useSessionStorage( + "deploymentUrl", + "", + ); + const onSubmit = useCallback( + async (submittedAdminKey: string, submittedDeploymentUrl: string) => { + const isValid = await checkDeploymentInfo( + submittedAdminKey, + submittedDeploymentUrl, + ); + if (isValid === false) { + setIsValidDeploymentInfo(false); + return; + } + // For deployments that don't have the `/check_admin_key` endpoint, + // we set isValidDeploymentInfo to true so we can move on. The dashboard + // will just hit a less graceful error later if the credentials are invalid. + setIsValidDeploymentInfo(true); + setStoredAdminKey(submittedAdminKey); + setStoredDeploymentUrl(submittedDeploymentUrl); + }, + [setStoredAdminKey, setStoredDeploymentUrl], + ); const finalValue: DeploymentInfo = useMemo( () => ({ ...deploymentInfo, ok: true, - adminKey, - deploymentUrl, + adminKey: storedAdminKey, + deploymentUrl: storedDeploymentUrl, }) as DeploymentInfo, - [adminKey, deploymentUrl], + [storedAdminKey, storedDeploymentUrl], ); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); + useEffect(() => { + if (typeof window !== "undefined") { + const url = new URL(window.location.href); + url.searchParams.delete(LIST_DEPLOYMENTS_API_PORT_QUERY_PARAM); + window.history.replaceState({}, "", url.toString()); + } + }, []); if (!mounted) return null; - if (!adminKey) { + if (!isValidDeploymentInfo) { return (
-
{ - setDraftAdminKey(""); - setAdminKey(draftAdminKey); - }} - > - { - setShowKey(!showKey); - }} - description="The admin key is required every time you open the dashboard." - onChange={(e) => { - setDraftAdminKey(e.target.value); + {shouldListDeployments && listDeploymentsApiUrl !== null ? ( + { + setShouldListDeployments(false); }} + onSelect={onSubmit} /> - - + ) : ( + + )} + {isValidDeploymentInfo === false && ( +
+ The deployment URL or admin key is invalid. Please check that you + have entered the correct values. +
+ )}
); } return ( <> -
setAdminKey("")} /> +
{ + setStoredAdminKey(""); + setStoredDeploymentUrl(""); + }} + /> {children} @@ -264,4 +324,4 @@ function Header({ onLogout }: { onLogout: () => void }) {
); -} +} \ No newline at end of file From 5a9b1be8cc1f1abeaec500a52bc2b47cc6b97a23 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Thu, 20 Mar 2025 14:56:44 -0400 Subject: [PATCH 2/6] install just --- .github/workflows/precompile.yml | 195 ++++++++++++++++--------------- 1 file changed, 100 insertions(+), 95 deletions(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index eb9dd7328..8ebde9e42 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -18,6 +18,11 @@ jobs: with: node-version-file: ".nvmrc" + - name: Install just + uses: extractions/setup-just@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: NPM install globals run: npm ci --prefix scripts @@ -56,98 +61,98 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release_backend: - strategy: - fail-fast: false - matrix: - include: - - target: x86_64-unknown-linux-gnu - os: "['self-hosted', 'aws', 'x64', 'xlarge']" - - target: x86_64-apple-darwin - # large are on intel - os: "'macos-latest-large'" - - target: aarch64-apple-darwin - # xlarge are on arm - os: "'macos-latest-xlarge'" - - target: x86_64-pc-windows-msvc - os: "'windows-latest'" - - target: aarch64-unknown-linux-gnu - os: "['self-hosted', 'aws', 'arm64', 'xlarge']" - name: Build Convex Backend - runs-on: ${{ fromJSON(matrix.os) }} - steps: - - name: Check out code - uses: actions/checkout@v4 - - - name: Setup Rust - uses: ./.github/actions/setup-rust - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - r2-access-key: ${{ secrets.R2_ACCESS_KEY_ID }} - r2-secret-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} - - - name: Cache pnpm artifacts - uses: runs-on/cache@v4 - env: - AWS_REGION: ${{ vars.AWS_REGION }} - RUNS_ON_S3_BUCKET_CACHE: ${{ vars.RUNS_ON_S3_BUCKET_CACHE }} - with: - path: | - npm-packages/common/temp/build-cache - npm-packages/common/temp/pnpm-store - key: pnpm-cache-${{ - hashFiles('npm-packages/common/config/rush/pnpm-lock.yaml') }}-2 - restore-keys: pnpm-cache- - - - name: Node setup - uses: actions/setup-node@v4 - with: - node-version-file: ".nvmrc" - - - name: NPM install globals - run: npm ci --prefix scripts - - - name: Install JS - run: | - just rush install - - - name: Precompute release name - id: release_name - shell: bash - run: | - echo "RELEASE_NAME=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - ZIP_NAME=convex-local-backend-${{ matrix.target }}.zip - BINARY_NAME=convex-local-backend${{ runner.os == 'Windows' && '.exe' || '' }} - echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT - echo "BINARY_NAME=$BINARY_NAME" >> $GITHUB_OUTPUT - - - name: Build backend - shell: bash - run: | - set -ex - unset ROCKSDB_LIB_DIR - unset SODIUM_USE_PKG_CONFIG - unset SNAPPY_LIB_DIR - cargo build --release -p local_backend --bin convex-local-backend - mv target/release/${{ steps.release_name.outputs.BINARY_NAME }} . - - - name: Zip backend into arch - uses: thedoctor0/zip-release@0.7.5 - with: - type: zip - filename: ${{ steps.release_name.outputs.ZIP_NAME }} - path: ${{ steps.release_name.outputs.BINARY_NAME }} - - - name: Create Upload Precompiled Artifacts - id: create_release - uses: softprops/action-gh-release@v2 - with: - files: | - ${{ steps.release_name.outputs.ZIP_NAME }} - LICENSE.md - tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} - name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} - draft: true - prerelease: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # release_backend: + # strategy: + # fail-fast: false + # matrix: + # include: + # - target: x86_64-unknown-linux-gnu + # os: "['self-hosted', 'aws', 'x64', 'xlarge']" + # - target: x86_64-apple-darwin + # # large are on intel + # os: "'macos-latest-large'" + # - target: aarch64-apple-darwin + # # xlarge are on arm + # os: "'macos-latest-xlarge'" + # - target: x86_64-pc-windows-msvc + # os: "'windows-latest'" + # - target: aarch64-unknown-linux-gnu + # os: "['self-hosted', 'aws', 'arm64', 'xlarge']" + # name: Build Convex Backend + # runs-on: ${{ fromJSON(matrix.os) }} + # steps: + # - name: Check out code + # uses: actions/checkout@v4 + + # - name: Setup Rust + # uses: ./.github/actions/setup-rust + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # r2-access-key: ${{ secrets.R2_ACCESS_KEY_ID }} + # r2-secret-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} + + # - name: Cache pnpm artifacts + # uses: runs-on/cache@v4 + # env: + # AWS_REGION: ${{ vars.AWS_REGION }} + # RUNS_ON_S3_BUCKET_CACHE: ${{ vars.RUNS_ON_S3_BUCKET_CACHE }} + # with: + # path: | + # npm-packages/common/temp/build-cache + # npm-packages/common/temp/pnpm-store + # key: pnpm-cache-${{ + # hashFiles('npm-packages/common/config/rush/pnpm-lock.yaml') }}-2 + # restore-keys: pnpm-cache- + + # - name: Node setup + # uses: actions/setup-node@v4 + # with: + # node-version-file: ".nvmrc" + + # - name: NPM install globals + # run: npm ci --prefix scripts + + # - name: Install JS + # run: | + # just rush install + + # - name: Precompute release name + # id: release_name + # shell: bash + # run: | + # echo "RELEASE_NAME=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + # ZIP_NAME=convex-local-backend-${{ matrix.target }}.zip + # BINARY_NAME=convex-local-backend${{ runner.os == 'Windows' && '.exe' || '' }} + # echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT + # echo "BINARY_NAME=$BINARY_NAME" >> $GITHUB_OUTPUT + + # - name: Build backend + # shell: bash + # run: | + # set -ex + # unset ROCKSDB_LIB_DIR + # unset SODIUM_USE_PKG_CONFIG + # unset SNAPPY_LIB_DIR + # cargo build --release -p local_backend --bin convex-local-backend + # mv target/release/${{ steps.release_name.outputs.BINARY_NAME }} . + + # - name: Zip backend into arch + # uses: thedoctor0/zip-release@0.7.5 + # with: + # type: zip + # filename: ${{ steps.release_name.outputs.ZIP_NAME }} + # path: ${{ steps.release_name.outputs.BINARY_NAME }} + + # - name: Create Upload Precompiled Artifacts + # id: create_release + # uses: softprops/action-gh-release@v2 + # with: + # files: | + # ${{ steps.release_name.outputs.ZIP_NAME }} + # LICENSE.md + # tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} + # name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} + # draft: true + # prerelease: true + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a1224855b6cf37341248cbfcab3aa91a57a4b26d Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Thu, 20 Mar 2025 15:10:59 -0400 Subject: [PATCH 3/6] fix typo --- .github/workflows/precompile.yml | 2 +- .../src/lib/checkDeploymentInfo.ts | 69 ++++++++++--------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 8ebde9e42..11f74bfcd 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -61,7 +61,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # release_backend: + # release_backend: xcxc add back later # strategy: # fail-fast: false # matrix: diff --git a/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts b/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts index cdee05660..3c84b175e 100644 --- a/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts +++ b/npm-packages/dashboard-self-hosted/src/lib/checkDeploymentInfo.ts @@ -1,37 +1,38 @@ async function sleep(ms: number) { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); - } - - const MAX_RETRIES = 3; - const MAX_RETRIES_DELAY_MS = 500; - - export async function checkDeploymentInfo( - adminKey: string, - deploymentUrl: string, - ): Promise { - let retries = 0; - while (retries < MAX_RETRIES) { - try { - const resp = await fetch(new URL("/api/check_admin_key", deploymentUrl), { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Convex ${adminKey}`, - "Convex-Client": "dashboard-0.0.0", - }, - }); - if (resp.ok) { - return true; - } - if (resp.status === 404) { - return null; - } - } catch (e) { - // Do nothing + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +const MAX_RETRIES = 3; +const MAX_RETRIES_DELAY_MS = 500; + +export async function checkDeploymentInfo( + adminKey: string, + deploymentUrl: string +): Promise { + let retries = 0; + while (retries < MAX_RETRIES) { + try { + const resp = await fetch(new URL("/api/check_admin_key", deploymentUrl), { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Convex ${adminKey}`, + "Convex-Client": "dashboard-0.0.0", + }, + }); + if (resp.ok) { + return true; + } + if (resp.status === 404) { + return null; } - await sleep(MAX_RETRIES_DELAY_MS); - retries++; + } catch (e) { + // Do nothing } - retur \ No newline at end of file + await sleep(MAX_RETRIES_DELAY_MS); + retries++; + } + return false; +} From 33bfdc46752d226106416111aa452595892e62cf Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Thu, 20 Mar 2025 15:42:13 -0400 Subject: [PATCH 4/6] fix build --- .../dashboard-self-hosted/src/pages/_app.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/npm-packages/dashboard-self-hosted/src/pages/_app.tsx b/npm-packages/dashboard-self-hosted/src/pages/_app.tsx index 21994b3f4..0b2629375 100644 --- a/npm-packages/dashboard-self-hosted/src/pages/_app.tsx +++ b/npm-packages/dashboard-self-hosted/src/pages/_app.tsx @@ -136,10 +136,10 @@ const deploymentInfo: Omit = { reportHttpError: ( method: string, url: string, - error: { code: string; message: string }, + error: { code: string; message: string } ) => { console.error( - `failed to request ${method} ${url}: ${error.code} - ${error.message} `, + `failed to request ${method} ${url}: ${error.code} - ${error.message} ` ); }, useCurrentTeam: () => ({ @@ -192,6 +192,7 @@ const deploymentInfo: Omit = { projectsURI: "", deploymentsURI: "", isSelfHosted: true, + enableIndexFilters: false, }; function DeploymentInfoProvider({ @@ -206,7 +207,7 @@ function DeploymentInfoProvider({ listDeploymentsApiUrl: string | null; }) { const [shouldListDeployments, setShouldListDeployments] = useState( - listDeploymentsApiUrl !== null, + listDeploymentsApiUrl !== null ); const [isValidDeploymentInfo, setIsValidDeploymentInfo] = useState< boolean | null @@ -214,13 +215,13 @@ function DeploymentInfoProvider({ const [storedAdminKey, setStoredAdminKey] = useSessionStorage("adminKey", ""); const [storedDeploymentUrl, setStoredDeploymentUrl] = useSessionStorage( "deploymentUrl", - "", + "" ); const onSubmit = useCallback( async (submittedAdminKey: string, submittedDeploymentUrl: string) => { const isValid = await checkDeploymentInfo( submittedAdminKey, - submittedDeploymentUrl, + submittedDeploymentUrl ); if (isValid === false) { setIsValidDeploymentInfo(false); @@ -233,7 +234,7 @@ function DeploymentInfoProvider({ setStoredAdminKey(submittedAdminKey); setStoredDeploymentUrl(submittedDeploymentUrl); }, - [setStoredAdminKey, setStoredDeploymentUrl], + [setStoredAdminKey, setStoredDeploymentUrl] ); const finalValue: DeploymentInfo = useMemo( @@ -244,7 +245,7 @@ function DeploymentInfoProvider({ adminKey: storedAdminKey, deploymentUrl: storedDeploymentUrl, }) as DeploymentInfo, - [storedAdminKey, storedDeploymentUrl], + [storedAdminKey, storedDeploymentUrl] ); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); @@ -324,4 +325,4 @@ function Header({ onLogout }: { onLogout: () => void }) {
); -} \ No newline at end of file +} From dd87f482141295e91b09deee48c05dac0009494a Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Thu, 20 Mar 2025 15:48:22 -0400 Subject: [PATCH 5/6] update precompile job --- .github/workflows/precompile.yml | 192 +++++++++++++++---------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 11f74bfcd..00de77236 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -40,7 +40,7 @@ jobs: - name: Zip output run: | - zip -r dashboard.zip npm-packages/dashboard-self-hosted/out + cd npm-packages/dashboard-self-hosted/out && zip -r ../../../dashboard.zip . - name: Precompute release name id: release_name @@ -61,98 +61,98 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # release_backend: xcxc add back later - # strategy: - # fail-fast: false - # matrix: - # include: - # - target: x86_64-unknown-linux-gnu - # os: "['self-hosted', 'aws', 'x64', 'xlarge']" - # - target: x86_64-apple-darwin - # # large are on intel - # os: "'macos-latest-large'" - # - target: aarch64-apple-darwin - # # xlarge are on arm - # os: "'macos-latest-xlarge'" - # - target: x86_64-pc-windows-msvc - # os: "'windows-latest'" - # - target: aarch64-unknown-linux-gnu - # os: "['self-hosted', 'aws', 'arm64', 'xlarge']" - # name: Build Convex Backend - # runs-on: ${{ fromJSON(matrix.os) }} - # steps: - # - name: Check out code - # uses: actions/checkout@v4 - - # - name: Setup Rust - # uses: ./.github/actions/setup-rust - # with: - # github-token: ${{ secrets.GITHUB_TOKEN }} - # r2-access-key: ${{ secrets.R2_ACCESS_KEY_ID }} - # r2-secret-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} - - # - name: Cache pnpm artifacts - # uses: runs-on/cache@v4 - # env: - # AWS_REGION: ${{ vars.AWS_REGION }} - # RUNS_ON_S3_BUCKET_CACHE: ${{ vars.RUNS_ON_S3_BUCKET_CACHE }} - # with: - # path: | - # npm-packages/common/temp/build-cache - # npm-packages/common/temp/pnpm-store - # key: pnpm-cache-${{ - # hashFiles('npm-packages/common/config/rush/pnpm-lock.yaml') }}-2 - # restore-keys: pnpm-cache- - - # - name: Node setup - # uses: actions/setup-node@v4 - # with: - # node-version-file: ".nvmrc" - - # - name: NPM install globals - # run: npm ci --prefix scripts - - # - name: Install JS - # run: | - # just rush install - - # - name: Precompute release name - # id: release_name - # shell: bash - # run: | - # echo "RELEASE_NAME=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - # ZIP_NAME=convex-local-backend-${{ matrix.target }}.zip - # BINARY_NAME=convex-local-backend${{ runner.os == 'Windows' && '.exe' || '' }} - # echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT - # echo "BINARY_NAME=$BINARY_NAME" >> $GITHUB_OUTPUT - - # - name: Build backend - # shell: bash - # run: | - # set -ex - # unset ROCKSDB_LIB_DIR - # unset SODIUM_USE_PKG_CONFIG - # unset SNAPPY_LIB_DIR - # cargo build --release -p local_backend --bin convex-local-backend - # mv target/release/${{ steps.release_name.outputs.BINARY_NAME }} . - - # - name: Zip backend into arch - # uses: thedoctor0/zip-release@0.7.5 - # with: - # type: zip - # filename: ${{ steps.release_name.outputs.ZIP_NAME }} - # path: ${{ steps.release_name.outputs.BINARY_NAME }} - - # - name: Create Upload Precompiled Artifacts - # id: create_release - # uses: softprops/action-gh-release@v2 - # with: - # files: | - # ${{ steps.release_name.outputs.ZIP_NAME }} - # LICENSE.md - # tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} - # name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} - # draft: true - # prerelease: true - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + release_backend: + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: "['self-hosted', 'aws', 'x64', 'xlarge']" + - target: x86_64-apple-darwin + # large are on intel + os: "'macos-latest-large'" + - target: aarch64-apple-darwin + # xlarge are on arm + os: "'macos-latest-xlarge'" + - target: x86_64-pc-windows-msvc + os: "'windows-latest'" + - target: aarch64-unknown-linux-gnu + os: "['self-hosted', 'aws', 'arm64', 'xlarge']" + name: Build Convex Backend + runs-on: ${{ fromJSON(matrix.os) }} + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Setup Rust + uses: ./.github/actions/setup-rust + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + r2-access-key: ${{ secrets.R2_ACCESS_KEY_ID }} + r2-secret-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} + + - name: Cache pnpm artifacts + uses: runs-on/cache@v4 + env: + AWS_REGION: ${{ vars.AWS_REGION }} + RUNS_ON_S3_BUCKET_CACHE: ${{ vars.RUNS_ON_S3_BUCKET_CACHE }} + with: + path: | + npm-packages/common/temp/build-cache + npm-packages/common/temp/pnpm-store + key: pnpm-cache-${{ + hashFiles('npm-packages/common/config/rush/pnpm-lock.yaml') }}-2 + restore-keys: pnpm-cache- + + - name: Node setup + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + + - name: NPM install globals + run: npm ci --prefix scripts + + - name: Install JS + run: | + just rush install + + - name: Precompute release name + id: release_name + shell: bash + run: | + echo "RELEASE_NAME=$(date +'%Y-%m-%d')-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + ZIP_NAME=convex-local-backend-${{ matrix.target }}.zip + BINARY_NAME=convex-local-backend${{ runner.os == 'Windows' && '.exe' || '' }} + echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_OUTPUT + echo "BINARY_NAME=$BINARY_NAME" >> $GITHUB_OUTPUT + + - name: Build backend + shell: bash + run: | + set -ex + unset ROCKSDB_LIB_DIR + unset SODIUM_USE_PKG_CONFIG + unset SNAPPY_LIB_DIR + cargo build --release -p local_backend --bin convex-local-backend + mv target/release/${{ steps.release_name.outputs.BINARY_NAME }} . + + - name: Zip backend into arch + uses: thedoctor0/zip-release@0.7.5 + with: + type: zip + filename: ${{ steps.release_name.outputs.ZIP_NAME }} + path: ${{ steps.release_name.outputs.BINARY_NAME }} + + - name: Create Upload Precompiled Artifacts + id: create_release + uses: softprops/action-gh-release@v2 + with: + files: | + ${{ steps.release_name.outputs.ZIP_NAME }} + LICENSE.md + tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} + name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} + draft: true + prerelease: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 90ee643e6cb2dd3ef8d958863a7d9cb6abb1d2c2 Mon Sep 17 00:00:00 2001 From: Sarah Shader Date: Thu, 20 Mar 2025 16:40:27 -0400 Subject: [PATCH 6/6] try without draft --- .github/workflows/precompile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 00de77236..6364a7276 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -56,7 +56,7 @@ jobs: dashboard.zip tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} - draft: true + draft: false prerelease: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -152,7 +152,7 @@ jobs: LICENSE.md tag_name: precompiled-${{ steps.release_name.outputs.RELEASE_NAME }} name: Precompiled ${{ steps.release_name.outputs.RELEASE_NAME }} - draft: true + draft: false prerelease: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}