diff --git a/src/components/InformationBanner.tsx b/src/components/InformationBanner.tsx
index ad01db56..f47086e8 100644
--- a/src/components/InformationBanner.tsx
+++ b/src/components/InformationBanner.tsx
@@ -24,7 +24,7 @@ export default function InformationBanner({ message, children, small, sx }: Prop
return (
- {message}
+ {message}
{children}
)
diff --git a/src/components/Setting.tsx b/src/components/Setting.tsx
index c5dc5c01..b2efc367 100644
--- a/src/components/Setting.tsx
+++ b/src/components/Setting.tsx
@@ -58,9 +58,7 @@ export const getSettingUiSchema = (settings: GetSettingsInfoApiResponse, setting
adminPassword: { 'ui:widget': 'hidden' },
useORCS: { 'ui:widget': 'hidden' },
aiEnabled: { 'ui:widget': 'hidden' },
- git: {
- password: { 'ui:widget': 'password' },
- },
+ git: { 'ui:widget': 'hidden' },
},
kms: {
sops: {
diff --git a/src/components/modals/ConfigureGitModal.tsx b/src/components/modals/ConfigureGitModal.tsx
index 0ce86d7d..785a68ed 100644
--- a/src/components/modals/ConfigureGitModal.tsx
+++ b/src/components/modals/ConfigureGitModal.tsx
@@ -1,7 +1,8 @@
/* eslint-disable no-nested-ternary */
+import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import { yupResolver } from '@hookform/resolvers/yup'
import { LoadingButton } from '@mui/lab'
-import { Box, Button, Modal, Typography, styled } from '@mui/material'
+import { Box, Button, IconButton, Modal, Tooltip, Typography, styled } from '@mui/material'
import { FetchBaseQueryError } from '@reduxjs/toolkit/query'
import InformationBanner from 'components/InformationBanner'
import { TextField } from 'components/forms/TextField'
@@ -9,7 +10,7 @@ import { useEffect, useMemo, useState } from 'react'
import { Controller, useForm } from 'react-hook-form'
import { useLocalStorage } from 'react-use'
import { useSession } from 'providers/Session'
-import { useMigrateGitMutation } from 'redux/otomiApi'
+import { useGetGitSettingsQuery, useMigrateGitMutation } from 'redux/otomiApi'
import { GitSettingsFormValues, gitSettingsSchema } from './gitSettingsValidator'
const MODAL_TITLE = 'Configure Git Repository'
@@ -88,6 +89,25 @@ const IntroParagraph = styled(BodyText)({
marginBottom: '24px',
})
+const DefaultGitUrlBlock = styled(Box)(({ theme }) => ({
+ marginTop: '24px',
+ padding: '14px 16px',
+ borderRadius: 8,
+ border: '1px solid rgba(145, 158, 171, 0.24)',
+ backgroundColor: theme.palette.cm.rowAlter,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ gap: '16px',
+}))
+
+const DefaultGitUrlText = styled(Typography)({
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ fontFamily: 'monospace',
+})
+
const SectionTitle = styled(Typography)({
marginBottom: '4px',
fontWeight: 550,
@@ -216,15 +236,45 @@ function getErrorMessage(error: unknown): string {
return 'Something went wrong while migrating Git settings.'
}
+const emptyGitFormValues: GitSettingsFormValues = {
+ repoUrl: '',
+ branch: '',
+ username: '',
+ password: '',
+ email: '',
+}
+
export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalProps) {
const {
user: { isPlatformAdmin },
settings: {
+ cluster: { domainSuffix },
otomi: { isPreInstalled },
},
} = useSession()
const [showGitWizard, setShowGitWizard] = useLocalStorage('showGitConfigureWizard', true)
+
+ const isControlled = typeof open === 'boolean'
+ const actualOpen = useMemo(() => (isControlled ? !!open : !!showGitWizard), [isControlled, open, showGitWizard])
+
+ const { data: gitSettings, isFetching: isFetchingGitSettings } = useGetGitSettingsQuery(undefined, {
+ skip: !isPlatformAdmin || !isPreInstalled || !actualOpen,
+ })
+
+ const defaultGitUrl = gitSettings?.repoUrl || ''
+ const isDefaultGitConfiguration = gitSettings?.repoUrl?.includes('git-server.git-server.svc.cluster.local') ?? false
+ const hasGitConfiguration = !!gitSettings?.repoUrl && !isDefaultGitConfiguration
+ const displayedRepoUrl = isDefaultGitConfiguration && domainSuffix ? `https://git.${domainSuffix}/otomi/values` : ''
+
+ const getGitFormValues = (): GitSettingsFormValues => ({
+ repoUrl: hasGitConfiguration ? gitSettings?.repoUrl || '' : '',
+ branch: hasGitConfiguration ? gitSettings?.branch || '' : '',
+ username: hasGitConfiguration ? gitSettings?.username || '' : '',
+ password: hasGitConfiguration ? gitSettings?.password || '' : '',
+ email: hasGitConfiguration ? gitSettings?.email || '' : '',
+ })
+
const [showFormStep, setShowFormStep] = useState(false)
const [isTransitioning, setIsTransitioning] = useState(false)
const [submitError, setSubmitError] = useState('')
@@ -235,29 +285,21 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
const {
control,
handleSubmit,
+ getValues,
formState: { errors },
reset,
} = useForm({
resolver: yupResolver(gitSettingsSchema),
- defaultValues: {
- repoUrl: '',
- branch: '',
- username: '',
- password: '',
- email: '',
- },
+ defaultValues: emptyGitFormValues,
mode: 'onBlur',
})
- const isControlled = typeof open === 'boolean'
- const actualOpen = useMemo(() => (isControlled ? !!open : !!showGitWizard), [isControlled, open, showGitWizard])
-
const resetModalState = () => {
- setShowFormStep(false)
+ setShowFormStep(hasGitConfiguration)
setSubmitError('')
setMigrationSucceeded(false)
setIsTransitioning(false)
- reset()
+ reset(getGitFormValues())
}
useEffect(() => {
@@ -269,9 +311,18 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
}, [isPreInstalled, isControlled, setShowGitWizard])
useEffect(() => {
- if (!actualOpen) resetModalState()
+ if (!actualOpen) {
+ resetModalState()
+ return
+ }
+
+ if (!gitSettings) return
+
+ reset(getGitFormValues())
+ setShowFormStep(hasGitConfiguration)
+
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [actualOpen])
+ }, [actualOpen, gitSettings, hasGitConfiguration])
const handleClose = () => {
resetModalState()
@@ -284,6 +335,19 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
setShowGitWizard(false)
}
+ const handleCopyDefaultGitUrl = async () => {
+ if (!displayedRepoUrl) return
+ await navigator.clipboard.writeText(displayedRepoUrl)
+ }
+
+ const handleCopyRepoUrl = async () => {
+ const repoUrl = getValues('repoUrl')
+
+ if (!repoUrl) return
+
+ await navigator.clipboard.writeText(repoUrl)
+ }
+
const goToFormStep = () => {
setIsTransitioning(true)
@@ -356,7 +420,12 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
>
- {!showFormStep ? (
+ {isFetchingGitSettings ? (
+
+ {MODAL_TITLE}
+ Loading Git settings...
+
+ ) : !showFormStep ? (
<>
{MODAL_TITLE}
@@ -370,6 +439,25 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
Configuring an external Git Repo is recommended for installing App Platform.
+
+ {!!defaultGitUrl && (
+
+
+ Current internal Git repository
+ {displayedRepoUrl}
+
+
+
+
+
+
+
+
+ )}
@@ -413,6 +501,10 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
{MODAL_TITLE}
+ {hasGitConfiguration && (
+
+ )}
+
{!!submitError && }
@@ -427,6 +519,24 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
fullWidth
error={!!errors.repoUrl}
helperText={errors.repoUrl?.message}
+ InputProps={
+ hasGitConfiguration
+ ? {
+ endAdornment: (
+
+
+
+
+
+ ),
+ }
+ : undefined
+ }
/>
)}
/>
@@ -513,7 +623,7 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
diff --git a/src/pages/SettingsOverview.tsx b/src/pages/SettingsOverview.tsx
index ef5d2ff8..70533db7 100644
--- a/src/pages/SettingsOverview.tsx
+++ b/src/pages/SettingsOverview.tsx
@@ -35,7 +35,7 @@ export default function SettingsOverview() {
{ title: 'Backup', path: '/settings/platformBackups', icon: getIcon('backup_icon.svg'), id: 'backup' },
{ title: 'Object Storage', path: '/settings/obj', icon: getIcon('cloud_upload.svg'), id: 'objectStorage' },
{
- title: 'Git',
+ title: 'GitOps',
icon: getIcon('git_icon.svg'),
id: 'git',
onClick: () => setOpenGitModal(true),
diff --git a/src/redux/otomiApi.ts b/src/redux/otomiApi.ts
index 4e567b4a..d525d1be 100644
--- a/src/redux/otomiApi.ts
+++ b/src/redux/otomiApi.ts
@@ -382,6 +382,9 @@ const injectedRtkApi = api.injectEndpoints({
body: queryArg.body,
}),
}),
+ getGitSettings: build.query({
+ query: () => ({ url: `/v2/git` }),
+ }),
migrateGit: build.mutation({
query: (queryArg) => ({ url: `/v2/git`, method: 'PUT', body: queryArg.body }),
}),
@@ -4251,6 +4254,9 @@ export type GetSettingsInfoApiResponse = /** status 200 The request is successfu
git?: {
repoUrl?: string
branch?: string
+ username?: string
+ password?: string
+ email?: string
}
}
ingressClassNames?: string[]
@@ -4854,6 +4860,14 @@ export type EditAppApiArg = {
}
}
}
+export type GetGitSettingsApiResponse = /** status 200 Current Git settings */ {
+ repoUrl: string
+ username?: string
+ password: string
+ email: string
+ branch: string
+}
+export type GetGitSettingsApiArg = void
export type MigrateGitApiResponse = /** status 200 Migration successful. API is now locked. */ undefined
export type MigrateGitApiArg = {
/** New git configuration to migrate to. */
@@ -4968,6 +4982,7 @@ export const {
useToggleAppsMutation,
useGetTeamAppQuery,
useEditAppMutation,
+ useGetGitSettingsQuery,
useMigrateGitMutation,
useGetApiStatusQuery,
} = injectedRtkApi