From e7c46c1985ebc7a42db5771cefb737947506dfc0 Mon Sep 17 00:00:00 2001 From: Jon Saad-Falcon <41205309+jonsaadfalcon@users.noreply.github.com> Date: Wed, 24 Jun 2026 13:15:06 -0700 Subject: [PATCH] fix(frontend): make Supabase anon key optional to unblock PyPI publishing (#589) PyPI publishing had been broken since v1.0.3.dev851: #587 made VITE_SUPABASE_ANON_KEY a hard build-time requirement, but no such secret exists, so the frontend build aborted every publish run before the PyPI upload. Decouple package buildability from the leaderboard credential: a missing anon key now disables the savings leaderboard at runtime instead of failing the build, and auto-enables when the secret is provided. Verified: npm run build with the key unset succeeds; tsc + vitest pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/frontend.yml | 9 ++++----- .../src/components/Desktop/SavingsDashboard.tsx | 7 ++++--- frontend/src/lib/supabase.ts | 11 +++++++---- frontend/vite.config.ts | 14 +++++--------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 87a788a8..a83be8cf 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -36,8 +36,7 @@ jobs: - run: npx tsc --noEmit - run: npm run build env: - # vite.config/supabase.ts require this at build time. The real value - # comes from the repo secret; CI falls back to a placeholder so the - # build check passes (the leaderboard is not exercised in CI builds). - # Release/docs builds MUST set the secret to the real anon key. - VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY || 'ci-placeholder-anon-key' }} + # Optional: when the secret is unset the build still succeeds and the + # leaderboard is disabled (see src/lib/supabase.ts). No placeholder, + # so a keyless CI build doesn't bake in a bogus anon key. + VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }} diff --git a/frontend/src/components/Desktop/SavingsDashboard.tsx b/frontend/src/components/Desktop/SavingsDashboard.tsx index 6c4f7d7e..9a003d83 100644 --- a/frontend/src/components/Desktop/SavingsDashboard.tsx +++ b/frontend/src/components/Desktop/SavingsDashboard.tsx @@ -1,7 +1,7 @@ import { useState, useEffect, useCallback } from 'react'; import type React from 'react'; import { invoke } from '@tauri-apps/api/core'; -import { SUPABASE_ANON_KEY, SUPABASE_URL } from '../../lib/supabase'; +import { LEADERBOARD_ENABLED, SUPABASE_ANON_KEY, SUPABASE_URL } from '../../lib/supabase'; // --------------------------------------------------------------------------- // Types @@ -316,9 +316,10 @@ export function SavingsDashboard({ apiUrl }: { apiUrl: string }) { return () => clearInterval(timer); }, [fetchData]); - // Share savings to Supabase when opted in and data changes + // Share savings to Supabase when opted in and data changes. Skipped entirely + // when no anon key was built in (leaderboard disabled). useEffect(() => { - if (!optInEnabled || !displayName || !data) return; + if (!LEADERBOARD_ENABLED || !optInEnabled || !displayName || !data) return; const dollarSavings = data.per_provider.reduce((s, p) => s + p.total_cost, 0); const energySaved = data.per_provider.reduce((s, p) => s + (p.energy_wh || 0), 0); const flopsSaved = data.per_provider.reduce((s, p) => s + (p.flops || 0), 0); diff --git a/frontend/src/lib/supabase.ts b/frontend/src/lib/supabase.ts index b87f6921..75a620cc 100644 --- a/frontend/src/lib/supabase.ts +++ b/frontend/src/lib/supabase.ts @@ -1,8 +1,11 @@ export const SUPABASE_URL = import.meta.env.VITE_SUPABASE_URL || 'https://mtbtgpwzrbostweaanpr.supabase.co'; -export const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY; +// The Supabase anon key is optional at build time. When it is unset the public +// savings leaderboard is disabled rather than failing the build — this keeps +// the `openjarvis` package and desktop app buildable without coupling +// publishability to a leaderboard credential. Set VITE_SUPABASE_ANON_KEY at +// build time (from a repo secret) to enable the leaderboard. +export const SUPABASE_ANON_KEY = import.meta.env.VITE_SUPABASE_ANON_KEY ?? ''; -if (!SUPABASE_ANON_KEY) { - throw new Error('VITE_SUPABASE_ANON_KEY is required'); -} +export const LEADERBOARD_ENABLED = SUPABASE_ANON_KEY.length > 0; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 36c1da32..c196d2bc 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,16 +1,13 @@ import path from 'path'; -import { defineConfig, loadEnv } from 'vite'; +import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; import { VitePWA } from 'vite-plugin-pwa'; -export default defineConfig(({ command, mode }) => { - const env = loadEnv(mode, __dirname, ''); - if (command === 'build' && !env.VITE_SUPABASE_ANON_KEY) { - throw new Error('VITE_SUPABASE_ANON_KEY is required'); - } - - return { +// VITE_SUPABASE_ANON_KEY is intentionally NOT required here: a missing key +// disables the savings leaderboard at runtime (see src/lib/supabase.ts) rather +// than failing the build, so the package/app stays publishable without it. +export default defineConfig({ resolve: { alias: { '@': path.resolve(__dirname, './src'), @@ -62,5 +59,4 @@ export default defineConfig(({ command, mode }) => { '/api': process.env.VITE_API_URL || 'http://localhost:8000', }, }, - }; });