Files
supabase/apps/studio/hooks/misc/useGitHubConfigDrift.ts
Ivan Vasilov 8422045b86 chore: Use @supabase/config for the code configuration page (#50398)
How to test:
1. Connect a project to GH repo
2. Deploy the `config.toml` once
3. Change some setting in Auth
4. You should see a change in
`/dashboard/project/_/settings/code-configuration`

<img width="1271" height="1186" alt="Screenshot 2026-09-16 at 16 26 39"
src="https://github.com/user-attachments/assets/dfc135a4-e495-489e-88fd-b760383793b4"
/>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Configuration drift comparisons now use a consistent project
configuration schema.
- Drift details display complete current-environment and `config.toml`
values, grouped by section.
- Matching and unmanaged settings are organized into dedicated sections.
  - Configuration fields link directly to relevant Studio settings.
  - Added a warning that GitHub deployments overwrite local changes.

- **Bug Fixes**
- Configuration updates now refresh project configuration data
automatically.
  - Improved labels and formatting for boolean and redirect URL values.
- Drift errors identify invalid configuration paths and provide
corrective guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-21 16:38:37 +02:00

137 lines
5.0 KiB
TypeScript

import { useQuery } from '@tanstack/react-query'
import { useParams } from 'common'
import { useCallback, useMemo } from 'react'
import {
formatGitHubConfigDecodeMessage,
fromDashboardProjectConfig,
getConfigDriftSummary,
type GitHubConfigDriftSummary,
} from '@/components/interfaces/ConfigDrift/github-config-drift'
import type { Branch } from '@/data/branches/branches-query'
import { useBranchesQuery } from '@/data/branches/branches-query'
import { useGitHubConfigQuery } from '@/data/config/github-config-query'
import { projectConfigV2QueryOptions } from '@/data/config/project-config-query'
import { useProjectGitHubConnectionQuery } from '@/data/integrations/github-connections-query'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { IS_PLATFORM } from '@/lib/constants'
const EMPTY_SUMMARY: GitHubConfigDriftSummary = {
driftedFields: [],
matchedFields: [],
unmanagedFields: [],
}
type ConfigDriftMemoResult =
| { status: 'success'; summary: GitHubConfigDriftSummary }
| { status: 'error'; source: 'project-config' | 'config-toml'; error: Error }
export function getGitBranchName(branch?: Branch): string | undefined {
return branch?.git_branch?.trim() || (branch?.is_default ? undefined : branch?.name?.trim())
}
export function useSelectedGitHubConfigDrift() {
const { ref: projectRef } = useParams()
const projectQuery = useSelectedProjectQuery()
const project = projectQuery.data
const parentProjectRef = project?.parentRef ?? projectRef
const shouldLoad = IS_PLATFORM && Boolean(projectRef) && Boolean(project)
const branchesQuery = useBranchesQuery({ projectRef: parentProjectRef }, { enabled: shouldLoad })
const connectionQuery = useProjectGitHubConnectionQuery({ ref: parentProjectRef })
const connection = connectionQuery.data
const hasConnection = connection !== undefined
const branches = branchesQuery.data ?? []
const selectedBranch = branches.find((branch) => branch.project_ref === projectRef)
const gitBranch = getGitBranchName(selectedBranch)
const queriesEnabled =
shouldLoad && branchesQuery.isSuccess && connectionQuery.isSuccess && hasConnection
const projectConfigQuery = useQuery({
...projectConfigV2QueryOptions({ projectRef }),
enabled: queriesEnabled,
staleTime: 30_000,
})
const githubConfigQuery = useGitHubConfigQuery(
{ connectionId: connection?.id, branch: gitBranch },
{ enabled: queriesEnabled }
)
const { refetch: branchesRefetch } = branchesQuery
const { refetch: connectionRefetch } = connectionQuery
const { refetch: projectConfigRefetch } = projectConfigQuery
const { refetch: githubConfigRefetch } = githubConfigQuery
const refetch = useCallback(
() =>
Promise.all([
branchesRefetch(),
connectionRefetch(),
projectConfigRefetch(),
githubConfigRefetch(),
]),
[branchesRefetch, connectionRefetch, projectConfigRefetch, githubConfigRefetch]
)
const driftResult = useMemo((): ConfigDriftMemoResult => {
let dashboardConfig
try {
dashboardConfig = fromDashboardProjectConfig(projectConfigQuery.data?.attributes)
} catch (error) {
console.error('Failed to read project configuration:', error)
return {
status: 'error',
source: 'project-config',
error: new Error('Could not read the project configuration returned by the API.'),
}
}
const result = getConfigDriftSummary({
dashboardConfig,
githubConfig: githubConfigQuery.data?.config,
})
if (result.status === 'invalid-config') {
return {
status: 'error',
source: 'config-toml',
error: new Error(formatGitHubConfigDecodeMessage(result.issues)),
}
}
return { status: 'success', summary: result.summary }
}, [projectConfigQuery.data?.attributes, githubConfigQuery.data?.config])
const summary = driftResult.status === 'success' ? driftResult.summary : EMPTY_SUMMARY
const conversionError = driftResult.status === 'error' ? driftResult.error : undefined
const activeQueries = [
projectQuery,
...(shouldLoad ? [branchesQuery, connectionQuery] : []),
...(shouldLoad && hasConnection ? [projectConfigQuery, githubConfigQuery] : []),
]
const isReady =
shouldLoad && hasConnection && projectConfigQuery.isSuccess && githubConfigQuery.isSuccess
const issueCount = summary.driftedFields.length
const queryError = activeQueries.find((query) => query.error)?.error
let errorSource: 'query' | 'project-config' | 'config-toml' | undefined = undefined
if (queryError) {
errorSource = 'query'
} else if (driftResult.status === 'error') {
errorSource = driftResult.source
}
return {
requestedGitBranch: gitBranch,
isReady,
isPending: activeQueries.some((query) => query.isPending),
isFetching: activeQueries.some((query) => query.isFetching),
isError: activeQueries.some((query) => query.isError) || conversionError !== undefined,
error: queryError ?? conversionError,
errorSource,
hasConfigurationIssues: isReady && issueCount > 0,
summary,
refetch,
}
}