mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 01:45:10 +03:00
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 -->
138 lines
3.8 KiB
TypeScript
138 lines
3.8 KiB
TypeScript
import { startCase } from 'lodash'
|
|
|
|
import {
|
|
CONFIG_SECTIONS,
|
|
getFieldDefinition,
|
|
type ConfigSection,
|
|
} from '@/components/interfaces/ConfigDrift/ConfigurationDriftPage.constants'
|
|
import {
|
|
type GitHubConfigDriftField,
|
|
type MatchedConfigField,
|
|
type UnmanagedConfigField,
|
|
} from '@/components/interfaces/ConfigDrift/github-config-drift'
|
|
|
|
const CONFIG_SECTION_LABELS: Record<ConfigSection, string> = {
|
|
api: 'API',
|
|
auth: 'Auth',
|
|
db: 'Database',
|
|
storage: 'Storage',
|
|
compute: 'Compute',
|
|
experimental: 'Experimental',
|
|
}
|
|
|
|
interface ConfigurationIssueRowMeta {
|
|
settingLabel: string
|
|
settingHref: string
|
|
valueDiff: ConfigurationDriftValueDiff
|
|
}
|
|
|
|
export type ConfigurationDriftRow = Omit<GitHubConfigDriftField, 'settingHref'> &
|
|
ConfigurationIssueRowMeta & { status: 'drifted' }
|
|
|
|
type ConfigurationDriftValueDiff = {
|
|
kind: 'scalar'
|
|
dashboardValue: string
|
|
configValue: string
|
|
}
|
|
|
|
interface UnmanagedConfigRow {
|
|
configPath: string
|
|
label: string
|
|
value: string
|
|
}
|
|
|
|
export interface ConfigSectionGroup {
|
|
section: ConfigSection
|
|
sectionLabel: string
|
|
rows: UnmanagedConfigRow[]
|
|
}
|
|
|
|
export function createConfigurationDriftRows(
|
|
fields: GitHubConfigDriftField[],
|
|
projectRef: string
|
|
): ConfigurationDriftRow[] {
|
|
return fields.map((field) => ({
|
|
...field,
|
|
status: 'drifted' as const,
|
|
settingLabel: getConfigFieldLabel(field.configPath),
|
|
settingHref: field.settingHref(projectRef),
|
|
valueDiff: createConfigurationDriftValueDiff(field),
|
|
}))
|
|
}
|
|
|
|
export function groupUnmanagedConfigFields(
|
|
fields: readonly UnmanagedConfigField[]
|
|
): ConfigSectionGroup[] {
|
|
return groupConfigFieldsBySection(fields, (field) => field.dashboardValue)
|
|
}
|
|
|
|
export function groupMatchedConfigFields(
|
|
fields: readonly MatchedConfigField[]
|
|
): ConfigSectionGroup[] {
|
|
return groupConfigFieldsBySection(fields, (field) => field.value)
|
|
}
|
|
|
|
function groupConfigFieldsBySection<T extends { section: ConfigSection; configPath: string }>(
|
|
fields: readonly T[],
|
|
getValue: (field: T) => unknown
|
|
): ConfigSectionGroup[] {
|
|
const bySection = new Map<ConfigSection, UnmanagedConfigRow[]>()
|
|
|
|
for (const field of fields) {
|
|
const rows = bySection.get(field.section) ?? []
|
|
rows.push({
|
|
configPath: field.configPath,
|
|
label: getConfigFieldLabel(field.configPath),
|
|
value: formatConfigFieldValue(getValue(field)),
|
|
})
|
|
bySection.set(field.section, rows)
|
|
}
|
|
|
|
return CONFIG_SECTIONS.filter((section) => bySection.has(section)).map((section) => ({
|
|
section,
|
|
sectionLabel: CONFIG_SECTION_LABELS[section],
|
|
rows: bySection.get(section)!,
|
|
}))
|
|
}
|
|
|
|
function formatConfigFieldValue(value: unknown): string {
|
|
const normalizedValue = value
|
|
|
|
if (normalizedValue === undefined || normalizedValue === null || normalizedValue === '') {
|
|
return 'Not set'
|
|
}
|
|
if (Array.isArray(normalizedValue)) {
|
|
return normalizedValue.length === 0
|
|
? 'Not set'
|
|
: normalizedValue.map((item) => formatScalarValue(item)).join('\n')
|
|
}
|
|
|
|
return formatScalarValue(normalizedValue)
|
|
}
|
|
|
|
function createConfigurationDriftValueDiff(
|
|
field: GitHubConfigDriftField
|
|
): ConfigurationDriftValueDiff {
|
|
return {
|
|
kind: 'scalar',
|
|
dashboardValue: formatConfigFieldValue(field.dashboardValue),
|
|
configValue: formatConfigFieldValue(field.githubValue),
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prefers the registry's hand-written label, and otherwise title-cases the last path segment —
|
|
* never the whole path, which would leave the dots in.
|
|
*/
|
|
function getConfigFieldLabel(configPath: string): string {
|
|
const label = getFieldDefinition(configPath)?.label
|
|
return label ?? startCase(configPath.split('.').at(-1) ?? configPath)
|
|
}
|
|
|
|
function formatScalarValue(value: unknown): string {
|
|
if (typeof value === 'string') return value || 'Not set'
|
|
if (typeof value === 'boolean') return value ? 'true' : 'false'
|
|
if (typeof value === 'number') return String(value)
|
|
return JSON.stringify(value, null, 2) ?? 'Not set'
|
|
}
|