-
Notifications
You must be signed in to change notification settings - Fork 2
feat(setup): add local runner install step to setup wizard #452
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| import React from 'react'; | ||
| import { | ||
| Box, | ||
| Typography, | ||
| Button, | ||
| Alert, | ||
| CircularProgress, | ||
| } from '@mui/material'; | ||
| import { toast } from 'react-toastify'; | ||
| import { SettingsType } from '../../../types/backend'; | ||
| import { | ||
| useCheckRunnerVersions, | ||
| useInstallRunnerVersion, | ||
| } from '../../controllers'; | ||
|
|
||
| type Props = { | ||
| settings: SettingsType; | ||
| onInstallComplete: (runnerPath: string) => void; | ||
| }; | ||
|
|
||
| export const RunnerSetup: React.FC<Props> = ({ | ||
| settings, | ||
| onInstallComplete, | ||
| }) => { | ||
| const [latestVersion, setLatestVersion] = React.useState<string | null>(null); | ||
|
|
||
| const checkVersions = useCheckRunnerVersions({ | ||
| onSuccess: (data) => { | ||
| setLatestVersion(data.latestStable || null); | ||
| }, | ||
| onError: (error) => { | ||
| toast.error(`Failed to check runner versions: ${error.message}`); | ||
| }, | ||
| }); | ||
|
|
||
| const installRunner = useInstallRunnerVersion({ | ||
| onSuccess: (result) => { | ||
| if (result.success) { | ||
| toast.info(`Local runner ${result.version} installation completed`); | ||
| result.warnings?.forEach((warning) => toast.warning(warning)); | ||
| onInstallComplete(result.path); | ||
| } else { | ||
| toast.error(`Local runner installation failed: ${result.error}`); | ||
| } | ||
| }, | ||
| onError: (error) => { | ||
| toast.error(`Local runner installation failed: ${error.message}`); | ||
| }, | ||
| }); | ||
|
|
||
| React.useEffect(() => { | ||
| checkVersions.mutate(); | ||
| }, []); | ||
|
|
||
| const isInstalled = Boolean(settings.runnerPath); | ||
| let installButtonLabel = isInstalled | ||
| ? 'Reinstall Local Runner' | ||
| : 'Install Local Runner'; | ||
| if (installRunner.isLoading) installButtonLabel = 'Installing...'; | ||
|
|
||
| return ( | ||
| <Box> | ||
| <Typography variant="body1"> | ||
| The local runner executes rosetta pipelines on this machine, the same | ||
| way the cloud runner does on the server. | ||
| </Typography> | ||
|
|
||
| {isInstalled ? ( | ||
| <Alert severity="success" sx={{ mt: 2, mb: 2 }}> | ||
| Local runner (version {settings.runnerVersion || 'Unknown'}) is | ||
| already installed at: {settings.runnerPath} | ||
| </Alert> | ||
| ) : ( | ||
| <Alert severity="info" sx={{ mt: 2, mb: 2 }}> | ||
| The local runner is not installed yet. Click Install to set it up now. | ||
| </Alert> | ||
| )} | ||
|
|
||
| {latestVersion && !installRunner.isLoading && ( | ||
| <Typography variant="body2" sx={{ mb: 1 }}> | ||
| Latest version: {latestVersion} | ||
| </Typography> | ||
| )} | ||
|
|
||
| <Box sx={{ mt: 2, mb: 3 }}> | ||
| <Button | ||
| variant="contained" | ||
| onClick={() => latestVersion && installRunner.mutate(latestVersion)} | ||
| disabled={ | ||
| installRunner.isLoading || checkVersions.isLoading || !latestVersion | ||
| } | ||
| data-testid="setup-install-runner-btn" | ||
| startIcon={ | ||
| installRunner.isLoading || checkVersions.isLoading ? ( | ||
| <CircularProgress size={16} /> | ||
| ) : null | ||
| } | ||
| > | ||
| {checkVersions.isLoading | ||
| ? 'Checking versions...' | ||
| : installButtonLabel} | ||
| </Button> | ||
| </Box> | ||
| </Box> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,13 @@ import React from 'react'; | |
| import { Box, Button } from '@mui/material'; | ||
| import { toast } from 'react-toastify'; | ||
| import { useGetSettings, useUpdateSettings } from '../../controllers'; | ||
| import { Loader, FinishSetup, DbtSetup, PythonSetup } from '../../components'; | ||
| import { | ||
| Loader, | ||
| FinishSetup, | ||
| DbtSetup, | ||
| PythonSetup, | ||
| RunnerSetup, | ||
| } from '../../components'; | ||
| import { client } from '../../config/client'; | ||
|
|
||
| const ADAPTERS = [ | ||
|
|
@@ -18,6 +24,8 @@ const ADAPTERS = [ | |
| }, | ||
| ]; | ||
|
|
||
| const FINAL_STEP = 3; | ||
|
|
||
| const Setup: React.FC = () => { | ||
| const { data: settings, isLoading } = useGetSettings(); | ||
| const { mutateAsync: updateSettings } = useUpdateSettings(); | ||
|
|
@@ -44,7 +52,11 @@ const Setup: React.FC = () => { | |
| if (settings && !isInitialized) { | ||
| if (settings.pythonPath && settings.pythonPath !== '') { | ||
| if (settings.dbtPath && settings.dbtPath !== '') { | ||
| setCurrentStep(2); | ||
| if (settings.runnerPath && settings.runnerPath !== '') { | ||
| setCurrentStep(FINAL_STEP); | ||
| } else { | ||
| setCurrentStep(2); | ||
| } | ||
| } else { | ||
| setCurrentStep(1); | ||
| } | ||
|
|
@@ -97,6 +109,16 @@ const Setup: React.FC = () => { | |
| </div> | ||
| )} | ||
| {currentStep === 2 && ( | ||
| <div data-testid="setup-step-runner" style={{ width: '100%' }}> | ||
| <RunnerSetup | ||
| settings={settings} | ||
| onInstallComplete={() => { | ||
| setCurrentStep(currentStep + 1); | ||
| }} | ||
| /> | ||
| </div> | ||
| )} | ||
| {currentStep === FINAL_STEP && ( | ||
| <div data-testid="setup-step-complete" style={{ width: '100%' }}> | ||
| <FinishSetup settings={settings} /> | ||
|
Comment on lines
+119
to
123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1,70p' src/renderer/controllers/settings.controller.ts
sed -n '330,365p' src/renderer/controllers/settings.controller.ts
sed -n '100,135p' src/renderer/screens/setup/index.tsx
node -p "require('./package.json').dependencies['react-query'] || require('./package.json').devDependencies['react-query']"
rg -n "invalidateQueries|useGetSettings|GET_SETTINGS" src/renderer/controllers src/renderer/context package.json yarn.lock package-lock.json pnpm-lock.yaml 2>/dev/null | head -160Repository: rosettadb/dbt-studio Length of output: 21893 🏁 Script executed: set -e
printf '%s\n' '--- setup screen ---'
sed -n '1,155p' src/renderer/screens/setup/index.tsx
printf '%s\n' '--- runner setup ---'
sed -n '1,115p' src/renderer/components/runnerSetup/index.tsx
printf '%s\n' '--- settings provider and query client references ---'
sed -n '1,85p' src/renderer/context/AppProvider.tsx
rg -n --glob '*.tsx' --glob '*.ts' 'QueryClientProvider|new QueryClient|useGetSettings\(' src/renderer
printf '%s\n' '--- dependency declarations ---'
rg -n -C 2 '"react-query"|react-query@' package.json yarn.lock package-lock.json pnpm-lock.yaml 2>/dev/null | head -80Repository: rosettadb/dbt-studio Length of output: 13100 🏁 Script executed: set -e
printf '%s\n' '--- query client ---'
cat -n src/renderer/context/QueryClientContext.tsx
printf '%s\n' '--- exact react-query lock resolution ---'
rg -n -A 12 -B 3 'node_modules/react-query|"react-query":|react-query@' package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null | head -100
printf '%s\n' '--- settings query options and setup entrypoints ---'
rg -n -C 3 'useGetSettings\(|enabled:|refetchOn|staleTime|cacheTime' src/renderer/screens/setup src/renderer/controllers/settings.controller.ts src/renderer/contextRepository: rosettadb/dbt-studio Length of output: 4577 🌐 Web query:
💡 Result: <search_synthesis> <source_evidence> Citations:
🌐 Web query:
💡 Result: <search_synthesis> <source_evidence> Citations:
Do not advance when the settings refetch fails.
However, 🤖 Prompt for AI Agents |
||
| </div> | ||
|
|
@@ -113,21 +135,22 @@ const Setup: React.FC = () => { | |
| variant="contained" | ||
| disabled={ | ||
| (currentStep === 0 && !settings.pythonPath) || | ||
| (currentStep === 1 && !settings.dbtPath) | ||
| (currentStep === 1 && !settings.dbtPath) || | ||
| (currentStep === 2 && !settings.runnerPath) | ||
| } | ||
| style={{ marginLeft: 'auto' }} | ||
| data-testid={ | ||
| currentStep === 2 ? 'setup-finish-btn' : 'setup-next-btn' | ||
| currentStep === FINAL_STEP ? 'setup-finish-btn' : 'setup-next-btn' | ||
| } | ||
| onClick={() => { | ||
| if (currentStep === 2) { | ||
| if (currentStep === FINAL_STEP) { | ||
| handleSkip(); | ||
| return; | ||
| } | ||
| setCurrentStep(currentStep + 1); | ||
| }} | ||
| > | ||
| {currentStep === 2 ? 'Finish' : 'Next'} | ||
| {currentStep === FINAL_STEP ? 'Finish' : 'Next'} | ||
| </Button> | ||
| </div> | ||
| </Box> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: rosettadb/dbt-studio
Length of output: 14192
🏁 Script executed:
Repository: rosettadb/dbt-studio
Length of output: 50376
🌐 Web query:
official React Query v3 useMutation default retry behavior💡 Result:
<search_synthesis>
In React Query v3, the default retry behavior for useMutation is to not retry failed mutations [1][2]. Specifically, the retry option defaults to 0, which means failed mutations will not be retried by default [3][4]. If you wish to enable retries for a mutation, you can configure the retry option: - Setting retry to a number (e.g., 3) will cause the mutation to retry until the failure count reaches that number [3][4]. - Setting retry to true will cause the mutation to retry infinitely [3][4]. - Setting retry to false will disable retries (equivalent to the default 0) [3][4]. Additionally, if a mutation fails because the device is offline, React Query v3 supports retrying these mutations in order once the device reconnects [1][2].
</search_synthesis>
<source_evidence>
Citations:
Provide a retry path after a version-check failure.
useCheckRunnerVersionsuses React Query v3useMutation, whose default retry count is0. If the initialcheckVersions.mutate()fails,latestVersionstaysnull. The empty-dependency effect does not run again, and the install button remains disabled by!latestVersion. The separateRunnerSettingsrefresh action does not recover this mounted setup step.Add a retry action that calls
checkVersions.mutate()whencheckVersions.isError, or allow the disabled button to rerun the version check.🤖 Prompt for AI Agents