diff --git a/libs/cypress/fixtures/repositories/initialRepositories.ts b/libs/cypress/fixtures/repositories/initialRepositories.ts index f9d578ae5..df4ae625a 100644 --- a/libs/cypress/fixtures/repositories/initialRepositories.ts +++ b/libs/cypress/fixtures/repositories/initialRepositories.ts @@ -12,7 +12,7 @@ const repoList: Repository[] = [ }, spec: { url: 'https://github.com/flightctl/flightctl-demos', - type: RepoSpecType.GIT, + type: RepoSpecType.RepoSpecTypeGit, }, status: { conditions: [ diff --git a/libs/types/index.ts b/libs/types/index.ts index 8ab150f66..07884afc2 100644 --- a/libs/types/index.ts +++ b/libs/types/index.ts @@ -111,9 +111,9 @@ export { FleetRolloutStartedDetails } from './models/FleetRolloutStartedDetails' export type { FleetRolloutStatus } from './models/FleetRolloutStatus'; export type { FleetSpec } from './models/FleetSpec'; export type { FleetStatus } from './models/FleetStatus'; -export type { GenericRepoSpec } from './models/GenericRepoSpec'; export type { GitConfigProviderSpec } from './models/GitConfigProviderSpec'; export type { GitHubIntrospectionSpec } from './models/GitHubIntrospectionSpec'; +export type { GitRepoSpec } from './models/GitRepoSpec'; export type { HookAction } from './models/HookAction'; export type { HookActionRun } from './models/HookActionRun'; export type { HookCondition } from './models/HookCondition'; @@ -180,7 +180,6 @@ export type { RolloutDeviceSelection } from './models/RolloutDeviceSelection'; export type { RolloutPolicy } from './models/RolloutPolicy'; export { RolloutStrategy } from './models/RolloutStrategy'; export type { SshConfig } from './models/SshConfig'; -export type { SshRepoSpec } from './models/SshRepoSpec'; export type { Status } from './models/Status'; export { SystemdActiveStateType } from './models/SystemdActiveStateType'; export { SystemdEnableStateType } from './models/SystemdEnableStateType'; diff --git a/libs/types/models/GenericRepoSpec.ts b/libs/types/models/GenericRepoSpec.ts deleted file mode 100644 index 1a5a1736b..000000000 --- a/libs/types/models/GenericRepoSpec.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { RepoSpecType } from './RepoSpecType'; -export type GenericRepoSpec = { - /** - * The (possibly remote) repository URL. - */ - url: string; - type: RepoSpecType; -}; - diff --git a/libs/types/models/GitRepoSpec.ts b/libs/types/models/GitRepoSpec.ts new file mode 100644 index 000000000..bed87dfa7 --- /dev/null +++ b/libs/types/models/GitRepoSpec.ts @@ -0,0 +1,22 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { HttpConfig } from './HttpConfig'; +import type { SshConfig } from './SshConfig'; +/** + * Git repository specification. Supports no auth (public repos), HTTP/HTTPS auth, or SSH auth. + */ +export type GitRepoSpec = { + /** + * The Git repository URL to clone from. + */ + url: string; + /** + * The repository type discriminator. + */ + type: 'git'; + httpConfig?: HttpConfig; + sshConfig?: SshConfig; +}; + diff --git a/libs/types/models/HttpRepoSpec.ts b/libs/types/models/HttpRepoSpec.ts index f07a16059..e960e6d26 100644 --- a/libs/types/models/HttpRepoSpec.ts +++ b/libs/types/models/HttpRepoSpec.ts @@ -3,14 +3,19 @@ /* tslint:disable */ /* eslint-disable */ import type { HttpConfig } from './HttpConfig'; -import type { RepoSpecType } from './RepoSpecType'; +/** + * HTTP endpoint specification for fetching configuration. + */ export type HttpRepoSpec = { /** - * The HTTP URL to call or clone from. + * The HTTP URL to call. */ url: string; - type: RepoSpecType; - httpConfig: HttpConfig; + /** + * The repository type discriminator. + */ + type: 'http'; + httpConfig?: HttpConfig; /** * URL suffix used only for validating access to the repository. Users might use the URL field as a root URL to be used by config sources adding suffixes. This will help with the validation of the http endpoint. */ diff --git a/libs/types/models/OciRepoSpec.ts b/libs/types/models/OciRepoSpec.ts index 7913cea3e..62e1f7072 100644 --- a/libs/types/models/OciRepoSpec.ts +++ b/libs/types/models/OciRepoSpec.ts @@ -3,7 +3,6 @@ /* tslint:disable */ /* eslint-disable */ import type { OciAuth } from './OciAuth'; -import type { RepoSpecType } from './RepoSpecType'; /** * OCI container registry specification. */ @@ -16,7 +15,10 @@ export type OciRepoSpec = { * URL scheme for connecting to the registry. */ scheme?: OciRepoSpec.scheme; - type: RepoSpecType; + /** + * The repository type discriminator. + */ + type: 'oci'; /** * Access mode for the registry: "Read" for read-only (pull), "ReadWrite" for read-write (pull and push). */ diff --git a/libs/types/models/RepoSpecType.ts b/libs/types/models/RepoSpecType.ts index fa2e96a94..19064f83f 100644 --- a/libs/types/models/RepoSpecType.ts +++ b/libs/types/models/RepoSpecType.ts @@ -6,7 +6,7 @@ * RepoSpecType is the type of the repository. */ export enum RepoSpecType { - GIT = 'git', - HTTP = 'http', - OCI = 'oci', + RepoSpecTypeGit = 'git', + RepoSpecTypeHttp = 'http', + RepoSpecTypeOci = 'oci', } diff --git a/libs/types/models/RepositorySpec.ts b/libs/types/models/RepositorySpec.ts index c7e262898..7aa67223b 100644 --- a/libs/types/models/RepositorySpec.ts +++ b/libs/types/models/RepositorySpec.ts @@ -2,12 +2,11 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { GenericRepoSpec } from './GenericRepoSpec'; +import type { GitRepoSpec } from './GitRepoSpec'; import type { HttpRepoSpec } from './HttpRepoSpec'; import type { OciRepoSpec } from './OciRepoSpec'; -import type { SshRepoSpec } from './SshRepoSpec'; /** * RepositorySpec describes a configuration repository. */ -export type RepositorySpec = (GenericRepoSpec | HttpRepoSpec | SshRepoSpec | OciRepoSpec); +export type RepositorySpec = (GitRepoSpec | HttpRepoSpec | OciRepoSpec); diff --git a/libs/types/models/SshRepoSpec.ts b/libs/types/models/SshRepoSpec.ts deleted file mode 100644 index f6fa1091b..000000000 --- a/libs/types/models/SshRepoSpec.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { RepoSpecType } from './RepoSpecType'; -import type { SshConfig } from './SshConfig'; -export type SshRepoSpec = { - /** - * The SSH Git repository URL to clone from. - */ - url: string; - type: RepoSpecType; - sshConfig: SshConfig; -}; - diff --git a/libs/ui-components/src/components/Device/EditDeviceWizard/steps/ConfigWithRepositoryTemplateForm.tsx b/libs/ui-components/src/components/Device/EditDeviceWizard/steps/ConfigWithRepositoryTemplateForm.tsx index 72a1bb350..d056b7831 100644 --- a/libs/ui-components/src/components/Device/EditDeviceWizard/steps/ConfigWithRepositoryTemplateForm.tsx +++ b/libs/ui-components/src/components/Device/EditDeviceWizard/steps/ConfigWithRepositoryTemplateForm.tsx @@ -3,7 +3,7 @@ import { useFormikContext } from 'formik'; import { FormGroup } from '@patternfly/react-core'; import { Trans } from 'react-i18next'; -import { GenericRepoSpec, HttpRepoSpec, RepoSpecType, Repository } from '@flightctl/types'; +import { GitRepoSpec, HttpRepoSpec, RepoSpecType, Repository } from '@flightctl/types'; import { DeviceSpecConfigFormValues, GitConfigTemplate, HttpConfigTemplate } from '../../../../types/deviceSpec'; import { useTranslation } from '../../../../hooks/useTranslation'; import TextField from '../../../form/TextField'; @@ -127,7 +127,7 @@ const ConfigWithRepositoryTemplateForm = ({ const ct = values.configTemplates[index] as HttpConfigTemplate | GitConfigTemplate; const selectedRepo = repositories.find((repo) => repo.metadata.name === ct.repository); - const repoSpec = selectedRepo?.spec as GenericRepoSpec | HttpRepoSpec | undefined; + const repoSpec = selectedRepo?.spec as GitRepoSpec | HttpRepoSpec | undefined; return ( <> @@ -140,10 +140,10 @@ const ConfigWithRepositoryTemplateForm = ({ repoRefetch={repoRefetch} isRequired /> - {repoType === RepoSpecType.GIT && ( + {repoType === RepoSpecType.RepoSpecTypeGit && ( )} - {repoType === RepoSpecType.HTTP && ( + {repoType === RepoSpecType.RepoSpecTypeHttp && ( } {(type === ConfigType.GIT || type === ConfigType.HTTP) && ( { endpoint: 'repositories', }); - const gitRepositories = (repoList?.items || []).filter((repo) => repo.spec.type === RepoSpecType.GIT); + const gitRepositories = (repoList?.items || []).filter((repo) => repo.spec.type === RepoSpecType.RepoSpecTypeGit); let body: React.ReactNode; @@ -93,7 +93,7 @@ const ImportFleetWizard = () => { } else { const repoInitValues = getInitValues({ options: { - allowedRepoTypes: [RepoSpecType.GIT], + allowedRepoTypes: [RepoSpecType.RepoSpecTypeGit], showRepoTypes: false, }, }); diff --git a/libs/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.tsx b/libs/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.tsx index 223602b16..bb641f8e2 100644 --- a/libs/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.tsx +++ b/libs/ui-components/src/components/Fleet/ImportFleetWizard/steps/RepositoryStep.tsx @@ -3,7 +3,7 @@ import { FormGroup, FormSection, Grid, Radio } from '@patternfly/react-core'; import { FormikErrors, useFormikContext } from 'formik'; import { Table, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; -import { GenericRepoSpec, Repository } from '@flightctl/types'; +import { GitRepoSpec, Repository } from '@flightctl/types'; import { ImportFleetFormValues } from '../types'; import { RepositoryForm } from '../../../Repository/CreateRepository/CreateRepositoryForm'; @@ -36,7 +36,7 @@ const ExistingRepoForm = ({ repositories }: { repositories: Repository[] }) => { const { values } = useFormikContext(); const currentRepo = repositories.find((r) => r.metadata.name === values.existingRepo); - const repoSpec = currentRepo?.spec as GenericRepoSpec | undefined; // Only git repositories can be used for importing fleets; + const repoSpec = currentRepo?.spec as GitRepoSpec | undefined; // Only git repositories can be used for importing fleets; return ( <> diff --git a/libs/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.tsx b/libs/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.tsx index 8e5f03f13..5d2223321 100644 --- a/libs/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.tsx +++ b/libs/ui-components/src/components/ImageBuilds/CreateImageBuildWizard/steps/OutputImageStep.tsx @@ -72,7 +72,7 @@ const OutputImageStep = () => { { name="source.repository" label={t('Source repository')} repositories={ociRegistries} - repoType={RepoSpecType.OCI} + repoType={RepoSpecType.RepoSpecTypeOci} canCreateRepo={canCreateRepo} repoRefetch={refetch} isRequired diff --git a/libs/ui-components/src/components/ImageBuilds/OciRegistriesContext.tsx b/libs/ui-components/src/components/ImageBuilds/OciRegistriesContext.tsx index 4402f36cc..cb7e9e03d 100644 --- a/libs/ui-components/src/components/ImageBuilds/OciRegistriesContext.tsx +++ b/libs/ui-components/src/components/ImageBuilds/OciRegistriesContext.tsx @@ -15,7 +15,7 @@ const OciRegistriesContext = React.createContext { const [repoList, isLoading, error, refetch] = useFetchPeriodically({ - endpoint: `repositories?fieldSelector=spec.type=${RepoSpecType.OCI}`, + endpoint: `repositories?fieldSelector=spec.type=${RepoSpecType.RepoSpecTypeOci}`, }); const ociRegistries = React.useMemo(() => repoList?.items || [], [repoList]); diff --git a/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx b/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx index b01218de0..eb70a60d8 100644 --- a/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx +++ b/libs/ui-components/src/components/Repository/CreateRepository/CreateRepositoryForm.tsx @@ -54,8 +54,8 @@ import './CreateRepositoryForm.css'; const AdvancedSection = () => { const { t } = useTranslation(); const { values } = useFormikContext(); - const showConfigTypeRadios = values.repoType === RepoSpecType.GIT; - const isOciRepo = values.repoType === RepoSpecType.OCI; + const showConfigTypeRadios = values.repoType === RepoSpecType.RepoSpecTypeGit; + const isOciRepo = values.repoType === RepoSpecType.RepoSpecTypeOci; if (isOciRepo) { return ( @@ -104,7 +104,7 @@ const AdvancedSection = () => { )} {values.configType === 'http' && ( - {values.repoType === RepoSpecType.HTTP && ( + {values.repoType === RepoSpecType.RepoSpecTypeHttp && ( { isDisabled={values.httpConfig?.skipServerVerification} /> - {values.repoType === RepoSpecType.HTTP && ( + {values.repoType === RepoSpecType.RepoSpecTypeHttp && ( @@ -188,21 +188,21 @@ const RepositoryType = ({ isEdit }: { isEdit?: boolean }) => { const isRepoTypeChangeDisabled = values.allowedRepoTypes?.length === 1; const doChangeRepoType = (toType: RepoSpecType) => { - if (toType === RepoSpecType.GIT) { - void setFieldValue('repoType', RepoSpecType.GIT); + if (toType === RepoSpecType.RepoSpecTypeGit) { + void setFieldValue('repoType', RepoSpecType.RepoSpecTypeGit); void setFieldValue('httpConfig.token', undefined); void setFieldValue('canUseResourceSyncs', true); } - if (toType === RepoSpecType.HTTP) { - void setFieldValue('repoType', RepoSpecType.HTTP); + if (toType === RepoSpecType.RepoSpecTypeHttp) { + void setFieldValue('repoType', RepoSpecType.RepoSpecTypeHttp); void setFieldValue('configType', 'http'); void setFieldValue('useResourceSyncs', false); void setFieldValue('canUseResourceSyncs', false); } - if (toType === RepoSpecType.OCI) { - void setFieldValue('repoType', RepoSpecType.OCI); + if (toType === RepoSpecType.RepoSpecTypeOci) { + void setFieldValue('repoType', RepoSpecType.RepoSpecTypeOci); void setFieldValue('useResourceSyncs', false); void setFieldValue('canUseResourceSyncs', false); if (!values.ociConfig) { @@ -233,7 +233,7 @@ const RepositoryType = ({ isEdit }: { isEdit?: boolean }) => { id="git-repo-radio" name="repoType" label={t('Use Git repository')} - checkedValue={RepoSpecType.GIT} + checkedValue={RepoSpecType.RepoSpecTypeGit} onChangeCustom={onRepoTypeChange} noDefaultOnChange isDisabled={isRepoTypeChangeDisabled} @@ -244,7 +244,7 @@ const RepositoryType = ({ isEdit }: { isEdit?: boolean }) => { id="http-repo-radio" name="repoType" label={t('Use HTTP service')} - checkedValue={RepoSpecType.HTTP} + checkedValue={RepoSpecType.RepoSpecTypeHttp} onChangeCustom={onRepoTypeChange} noDefaultOnChange isDisabled={isRepoTypeChangeDisabled} @@ -255,7 +255,7 @@ const RepositoryType = ({ isEdit }: { isEdit?: boolean }) => { id="oci-repo-radio" name="repoType" label={t('Use OCI registry')} - checkedValue={RepoSpecType.OCI} + checkedValue={RepoSpecType.RepoSpecTypeOci} onChangeCustom={onRepoTypeChange} noDefaultOnChange isDisabled={isRepoTypeChangeDisabled} @@ -299,7 +299,7 @@ const RepositoryType = ({ isEdit }: { isEdit?: boolean }) => { export const RepositoryForm = ({ isEdit }: { isEdit?: boolean }) => { const { t } = useTranslation(); const { values } = useFormikContext(); - const isOciRepo = values.repoType === RepoSpecType.OCI; + const isOciRepo = values.repoType === RepoSpecType.RepoSpecTypeOci; return ( <> @@ -393,7 +393,7 @@ const CreateRepositoryFormContent = ({ isEdit, isReadOnly, onClose, children }: const { checkPermissions } = usePermissionsContext(); const [canCreateRS] = checkPermissions([{ kind: RESOURCE.RESOURCE_SYNC, verb: VERB.CREATE }]); - const showResourceSyncs = values.canUseResourceSyncs && values.repoType === RepoSpecType.GIT; + const showResourceSyncs = values.canUseResourceSyncs && values.repoType === RepoSpecType.RepoSpecTypeGit; return (
diff --git a/libs/ui-components/src/components/Repository/CreateRepository/utils.ts b/libs/ui-components/src/components/Repository/CreateRepository/utils.ts index 8f2e175a4..44a21811d 100644 --- a/libs/ui-components/src/components/Repository/CreateRepository/utils.ts +++ b/libs/ui-components/src/components/Repository/CreateRepository/utils.ts @@ -2,6 +2,7 @@ import * as Yup from 'yup'; import { TFunction } from 'i18next'; import { DockerAuth, + GitRepoSpec, HttpConfig, HttpRepoSpec, OciAuthType, @@ -12,7 +13,6 @@ import { RepositorySpec, ResourceSync, SshConfig, - SshRepoSpec, } from '@flightctl/types'; import { RepositoryFormValues, ResourceSyncFormValue } from './types'; @@ -30,22 +30,427 @@ const pathRegex = /\/.+/; const jwtTokenRegexp = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/; export const isHttpRepoSpec = (repoSpec: RepositorySpec): repoSpec is HttpRepoSpec => - !!(repoSpec['httpConfig'] || (repoSpec as HttpRepoSpec).validationSuffix); -export const isSshRepoSpec = (repoSpec: RepositorySpec): repoSpec is SshRepoSpec => !!repoSpec['sshConfig']; -export const isOciRepoSpec = (repoSpec: RepositorySpec): repoSpec is OciRepoSpec => repoSpec.type === RepoSpecType.OCI; + repoSpec.type === RepoSpecType.RepoSpecTypeHttp; +export const isGitRepoSpec = (repoSpec: RepositorySpec): repoSpec is GitRepoSpec => + repoSpec.type === RepoSpecType.RepoSpecTypeGit; +export const isOciRepoSpec = (repoSpec: RepositorySpec): repoSpec is OciRepoSpec => + repoSpec.type === RepoSpecType.RepoSpecTypeOci; + +export const hasCredentialsSettings = (repoSpec: RepositorySpec): boolean => { + // The credentials settings can be in different fields depending on the repository type + if ('sshConfig' in repoSpec) { + return Boolean(repoSpec.sshConfig?.sshPrivateKey); + } else if ('httpConfig' in repoSpec) { + return Boolean( + repoSpec.httpConfig?.password || repoSpec.httpConfig?.['tls.crt'] || repoSpec.httpConfig?.['tls.key'], + ); + } else if ('ociAuth' in repoSpec) { + return Boolean(repoSpec.ociAuth); + } + return false; +}; + +const addHttpConfigPatches = ( + patches: PatchRequest, + values: RepositoryFormValues, + httpConfig: HttpConfig | undefined, +) => { + // If httpConfig doesn't exist, create it as a whole object + if (!httpConfig) { + const value: HttpConfig = { + skipServerVerification: values.httpConfig?.skipServerVerification, + }; + + if (values.httpConfig?.caCrt && !value.skipServerVerification) { + value['ca.crt'] = btoa(values.httpConfig.caCrt); + } + + if (values.httpConfig?.basicAuth?.use) { + value.password = values.httpConfig.basicAuth.password; + value.username = values.httpConfig.basicAuth.username; + } + if (values.httpConfig?.mTlsAuth?.use) { + if (values.httpConfig.mTlsAuth.tlsCrt) { + value['tls.crt'] = btoa(values.httpConfig.mTlsAuth.tlsCrt); + } + if (values.httpConfig.mTlsAuth.tlsKey) { + value['tls.key'] = btoa(values.httpConfig.mTlsAuth.tlsKey); + } + } + if (values.httpConfig?.token) { + value.token = values.httpConfig.token; + } + patches.push({ + op: 'add', + path: '/spec/httpConfig', + value, + }); + return; + } + + // httpConfig exists, patch individual properties + appendJSONPatch({ + patches, + newValue: values.httpConfig?.skipServerVerification, + originalValue: httpConfig?.skipServerVerification, + path: '/spec/httpConfig/skipServerVerification', + }); + if (values.httpConfig?.skipServerVerification) { + if (httpConfig?.['ca.crt']) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig/ca.crt', + }); + } + } else { + const caCrt = values.httpConfig?.caCrt; + appendJSONPatch({ + patches, + newValue: caCrt ? btoa(caCrt) : caCrt, + originalValue: httpConfig?.['ca.crt'], + path: '/spec/httpConfig/ca.crt', + }); + } + + if (!values.httpConfig?.basicAuth?.use) { + if (httpConfig?.password) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig/password', + }); + } + if (httpConfig?.username) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig/username', + }); + } + } else { + appendJSONPatch({ + patches, + newValue: values.httpConfig?.basicAuth.password, + originalValue: httpConfig?.password, + path: '/spec/httpConfig/password', + }); + appendJSONPatch({ + patches, + newValue: values.httpConfig?.basicAuth.username, + originalValue: httpConfig?.username, + path: '/spec/httpConfig/username', + }); + } + + if (!values.httpConfig?.mTlsAuth?.use) { + if (httpConfig?.['tls.crt']) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig/tls.crt', + }); + } + if (httpConfig?.['tls.key']) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig/tls.key', + }); + } + } else { + appendJSONPatch({ + patches, + newValue: values.httpConfig?.mTlsAuth.tlsCrt, + originalValue: httpConfig?.['tls.crt'], + path: '/spec/httpConfig/tls.crt', + encodeB64: true, + }); + appendJSONPatch({ + patches, + newValue: values.httpConfig?.mTlsAuth.tlsKey, + originalValue: httpConfig?.['tls.key'], + path: '/spec/httpConfig/tls.key', + encodeB64: true, + }); + } + + if (!values.httpConfig?.token) { + if (httpConfig?.token) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig/token', + }); + } + } else { + appendJSONPatch({ + patches, + newValue: values.httpConfig?.token, + originalValue: httpConfig?.token, + path: '/spec/httpConfig/token', + }); + } +}; + +const getHttpRepositoryPatches = (values: RepositoryFormValues, repoSpec: HttpRepoSpec): PatchRequest => { + const patches: PatchRequest = []; + + appendJSONPatch({ + patches, + newValue: values.url, + originalValue: repoSpec.url, + path: '/spec/url', + }); + + if (!values.useAdvancedConfig) { + if (repoSpec.validationSuffix) { + patches.push({ + op: 'remove', + path: '/spec/validationSuffix', + }); + } + patches.push({ + op: 'remove', + path: '/spec/httpConfig', + }); + return patches; + } + + // The rest of the fields are part of advanced settings + appendJSONPatch({ + patches, + newValue: values.validationSuffix, + originalValue: repoSpec.validationSuffix, + path: '/spec/validationSuffix', + }); + + addHttpConfigPatches(patches, values, repoSpec.httpConfig); + + return patches; +}; + +const getOciRepositoryPatches = (values: RepositoryFormValues, repoSpec: OciRepoSpec): PatchRequest => { + const formOciConfig = values.ociConfig; + if (!formOciConfig) { + return []; + } + + const patches: PatchRequest = []; + appendJSONPatch({ + patches, + newValue: formOciConfig.registry, + originalValue: repoSpec.registry, + path: '/spec/registry', + }); + appendJSONPatch({ + patches, + newValue: formOciConfig.scheme || OciRepoSpec.scheme.HTTPS, + originalValue: repoSpec.scheme, + path: '/spec/scheme', + }); + + appendJSONPatch({ + patches, + newValue: formOciConfig.accessMode || OciRepoSpec.accessMode.READ, + originalValue: repoSpec.accessMode, + path: '/spec/accessMode', + }); + + if (!values.useAdvancedConfig) { + if (repoSpec.ociAuth) { + patches.push({ op: 'remove', path: '/spec/ociAuth' }); + } + if (repoSpec['ca.crt']) { + patches.push({ op: 'remove', path: '/spec/ca.crt' }); + } + if (repoSpec.skipServerVerification !== undefined) { + patches.push({ op: 'remove', path: '/spec/skipServerVerification' }); + } + return patches; + } + + appendJSONPatch({ + patches, + newValue: formOciConfig.skipServerVerification, + originalValue: repoSpec.skipServerVerification, + path: '/spec/skipServerVerification', + }); + + if (formOciConfig.skipServerVerification && repoSpec['ca.crt']) { + patches.push({ op: 'remove', path: '/spec/ca.crt' }); + } else { + const caCrt = formOciConfig.caCrt; + appendJSONPatch({ + patches, + newValue: caCrt ? btoa(caCrt) : caCrt, + originalValue: repoSpec['ca.crt'], + path: '/spec/ca.crt', + }); + } + + const ociAuth = formOciConfig.ociAuth; + if (ociAuth?.use && ociAuth.username && ociAuth.password) { + if (!repoSpec.ociAuth) { + const ociAuthValue: DockerAuth = { + authType: OciAuthType.DOCKER, + username: ociAuth.username, + password: ociAuth.password, + }; + patches.push({ + op: 'add', + path: '/spec/ociAuth', + value: ociAuthValue, + }); + } else { + if (ociAuth.username !== repoSpec.ociAuth.username) { + appendJSONPatch({ + patches, + newValue: ociAuth.username, + originalValue: repoSpec.ociAuth.username, + path: '/spec/ociAuth/username', + }); + } + + if (ociAuth.password !== repoSpec.ociAuth.password) { + appendJSONPatch({ + patches, + newValue: ociAuth.password, + originalValue: repoSpec.ociAuth.password, + path: '/spec/ociAuth/password', + }); + } + } + } else if (repoSpec.ociAuth) { + patches.push({ op: 'remove', path: '/spec/ociAuth' }); + } + return patches; +}; + +const getGitRepositoryPatches = (values: RepositoryFormValues, gitRepoSpec: GitRepoSpec): PatchRequest => { + const patches: PatchRequest = []; + + appendJSONPatch({ + patches, + newValue: values.url, + originalValue: gitRepoSpec.url, + path: '/spec/url', + }); + + if (!values.useAdvancedConfig) { + if (gitRepoSpec.httpConfig) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig', + }); + } + if (gitRepoSpec.sshConfig) { + patches.push({ + op: 'remove', + path: '/spec/sshConfig', + }); + } + return patches; + } + + // Determine which config is being used based on form values (configType) + const usingHttpConfig = values.configType === 'http'; + const usingSshConfig = values.configType === 'ssh'; + + if (usingHttpConfig) { + // Switching from SSH to HTTP or editing HTTP config + if (gitRepoSpec.sshConfig) { + patches.push({ + op: 'remove', + path: '/spec/sshConfig', + }); + } + + addHttpConfigPatches(patches, values, gitRepoSpec.httpConfig); + } else if (usingSshConfig) { + // Switching from HTTP to SSH or editing SSH config + if (gitRepoSpec.httpConfig) { + patches.push({ + op: 'remove', + path: '/spec/httpConfig', + }); + } + + const sshConfig = gitRepoSpec.sshConfig; + // If sshConfig doesn't exist, create it as a whole object + if (!sshConfig) { + const value: SshConfig = { + privateKeyPassphrase: values.sshConfig?.privateKeyPassphrase, + skipServerVerification: values.sshConfig?.skipServerVerification, + }; + if (values.sshConfig?.sshPrivateKey) { + value.sshPrivateKey = btoa(values.sshConfig.sshPrivateKey); + } + + patches.push({ + op: 'add', + path: '/spec/sshConfig', + value, + }); + } else { + // sshConfig exists, patch individual properties + appendJSONPatch({ + patches, + newValue: values.sshConfig?.privateKeyPassphrase, + originalValue: sshConfig?.privateKeyPassphrase, + path: '/spec/sshConfig/privateKeyPassphrase', + }); + + appendJSONPatch({ + patches, + newValue: values.sshConfig?.skipServerVerification, + originalValue: sshConfig?.skipServerVerification, + path: '/spec/sshConfig/skipServerVerification', + }); + + appendJSONPatch({ + patches, + newValue: values.sshConfig?.sshPrivateKey, + originalValue: sshConfig?.sshPrivateKey, + path: '/spec/sshConfig/sshPrivateKey', + encodeB64: true, + }); + } + } + return patches; +}; + +/** + * Converts HttpConfig from the repository spec to form values format + */ +export const httpConfigToFormValues = (httpConfig?: HttpConfig): RepositoryFormValues['httpConfig'] => { + if (!httpConfig) { + return undefined; + } + + return { + caCrt: httpConfig['ca.crt'] ? atob(httpConfig['ca.crt']) : undefined, + basicAuth: { + username: httpConfig.username, + password: httpConfig.password, + use: !!httpConfig.username || !!httpConfig.password, + }, + mTlsAuth: { + tlsCrt: httpConfig['tls.crt'], + tlsKey: httpConfig['tls.key'], + use: !!httpConfig['tls.crt'] || !!httpConfig['tls.key'], + }, + skipServerVerification: httpConfig.skipServerVerification, + token: httpConfig.token, + }; +}; export const getRepoUrlOrRegistry = (repoSpec: RepositorySpec): string => { - if (isOciRepoSpec(repoSpec)) { + if (repoSpec.type === RepoSpecType.RepoSpecTypeOci) { return repoSpec.registry || ''; } return repoSpec.url || ''; }; -export const getRepoTypeLabel = (t: TFunction, repoType: RepoSpecType): string => { +export const getRepoTypeLabel = (t: TFunction, repoType: RepositorySpec['type']): string => { switch (repoType) { - case RepoSpecType.HTTP: + case RepoSpecType.RepoSpecTypeHttp: return t('HTTP service'); - case RepoSpecType.OCI: + case RepoSpecType.RepoSpecTypeOci: return t('OCI registry'); default: return t('Git repository'); @@ -68,8 +473,9 @@ export const getInitValues = ({ const configAllowsResourceSyncs = options?.canUseResourceSyncs ?? true; if (!repository) { - const selectedRepoType = options?.allowedRepoTypes?.length === 1 ? options.allowedRepoTypes[0] : RepoSpecType.GIT; - const canUseRSs = selectedRepoType === RepoSpecType.GIT && configAllowsResourceSyncs; + const selectedRepoType = + options?.allowedRepoTypes?.length === 1 ? options.allowedRepoTypes[0] : RepoSpecType.RepoSpecTypeGit; + const canUseRSs = selectedRepoType === RepoSpecType.RepoSpecTypeGit && configAllowsResourceSyncs; const initValues: RepositoryFormValues = { exists: false, @@ -91,7 +497,7 @@ export const getInitValues = ({ ], }; - if (selectedRepoType === RepoSpecType.OCI) { + if (selectedRepoType === RepoSpecType.RepoSpecTypeOci) { initValues.ociConfig = { registry: '', scheme: OciRepoSpec.scheme.HTTPS, @@ -102,13 +508,13 @@ export const getInitValues = ({ return initValues; } - const canUseRSs = repository.spec.type === RepoSpecType.GIT && configAllowsResourceSyncs; + const canUseRSs = repository.spec.type === RepoSpecType.RepoSpecTypeGit && configAllowsResourceSyncs; const formValues: RepositoryFormValues = { exists: true, name: repository.metadata.name || '', - url: isOciRepoSpec(repository.spec) ? '' : repository.spec.url || '', - repoType: repository.spec.type, + url: repository.spec.type === RepoSpecType.RepoSpecTypeOci ? '' : repository.spec.url || '', + repoType: repository.spec.type as RepoSpecType, validationSuffix: 'validationSuffix' in repository.spec ? repository.spec.validationSuffix : '', allowedRepoTypes: options?.allowedRepoTypes, showRepoTypes: options?.showRepoTypes ?? true, @@ -128,7 +534,7 @@ export const getInitValues = ({ : [{ name: '', path: '', targetRevision: '' }], }; - if (isOciRepoSpec(repository.spec)) { + if (repository.spec.type === RepoSpecType.RepoSpecTypeOci) { formValues.useAdvancedConfig = !!( repository.spec.ociAuth || repository.spec['ca.crt'] || @@ -148,32 +554,30 @@ export const getInitValues = ({ caCrt: repository.spec['ca.crt'] ? atob(repository.spec['ca.crt']) : undefined, skipServerVerification: repository.spec.skipServerVerification, }; - } else if (isHttpRepoSpec(repository.spec)) { - formValues.useAdvancedConfig = true; + } else if (repository.spec.type === RepoSpecType.RepoSpecTypeHttp) { formValues.configType = 'http'; - formValues.httpConfig = { - caCrt: repository.spec.httpConfig['ca.crt'] ? atob(repository.spec.httpConfig['ca.crt']) : undefined, - basicAuth: { - username: repository.spec.httpConfig.username, - password: repository.spec.httpConfig.password, - use: !!repository.spec.httpConfig.username || !!repository.spec.httpConfig.password, - }, - mTlsAuth: { - tlsCrt: repository.spec.httpConfig['tls.crt'], - tlsKey: repository.spec.httpConfig['tls.key'], - use: !!repository.spec.httpConfig['tls.crt'] || !!repository.spec.httpConfig['tls.key'], - }, - skipServerVerification: repository.spec.httpConfig.skipServerVerification, - token: repository.spec.httpConfig.token, - }; - } else if (isSshRepoSpec(repository.spec)) { - formValues.useAdvancedConfig = true; - formValues.configType = 'ssh'; - formValues.sshConfig = { - privateKeyPassphrase: repository.spec.sshConfig.privateKeyPassphrase, - skipServerVerification: repository.spec.sshConfig.skipServerVerification, - sshPrivateKey: repository.spec.sshConfig.sshPrivateKey, - }; + formValues.validationSuffix = repository.spec.validationSuffix; + + const valuesHttpConfig = httpConfigToFormValues(repository.spec.httpConfig); + if (valuesHttpConfig) { + formValues.httpConfig = valuesHttpConfig; + } + formValues.useAdvancedConfig = !!formValues.httpConfig || !!formValues.validationSuffix; + } else if (repository.spec.type === RepoSpecType.RepoSpecTypeGit) { + const gitRepoSpec = repository.spec; + if (gitRepoSpec.httpConfig) { + formValues.configType = 'http'; + formValues.useAdvancedConfig = true; + formValues.httpConfig = httpConfigToFormValues(gitRepoSpec.httpConfig); + } else if (gitRepoSpec.sshConfig) { + formValues.configType = 'ssh'; + formValues.useAdvancedConfig = true; + formValues.sshConfig = { + privateKeyPassphrase: gitRepoSpec.sshConfig.privateKeyPassphrase, + skipServerVerification: gitRepoSpec.sshConfig.skipServerVerification, + sshPrivateKey: gitRepoSpec.sshConfig.sshPrivateKey, + }; + } } return formValues; @@ -192,321 +596,20 @@ export const getRepositoryPatches = (values: RepositoryFormValues, repository: R ]; } - const patches: PatchRequest = []; - - // Handle OCI repository patches first - if (values.repoType === RepoSpecType.OCI) { - const ociRepoSpec = repository.spec as OciRepoSpec; - if (values.ociConfig) { - appendJSONPatch({ - patches, - newValue: values.ociConfig.registry, - originalValue: ociRepoSpec.registry, - path: '/spec/registry', - }); - - if (!values.useAdvancedConfig) { - if (ociRepoSpec.ociAuth) { - patches.push({ op: 'remove', path: '/spec/ociAuth' }); - } - if (ociRepoSpec['ca.crt']) { - patches.push({ op: 'remove', path: '/spec/ca.crt' }); - } - if (ociRepoSpec.skipServerVerification !== undefined) { - patches.push({ op: 'remove', path: '/spec/skipServerVerification' }); - } - if (ociRepoSpec.scheme) { - patches.push({ op: 'remove', path: '/spec/scheme' }); - } - if (ociRepoSpec.accessMode) { - patches.push({ op: 'remove', path: '/spec/accessMode' }); - } - return patches; - } - - appendJSONPatch({ - patches, - newValue: values.ociConfig.scheme || OciRepoSpec.scheme.HTTPS, - originalValue: ociRepoSpec.scheme, - path: '/spec/scheme', - }); - - appendJSONPatch({ - patches, - newValue: values.ociConfig.accessMode || OciRepoSpec.accessMode.READ, - originalValue: ociRepoSpec.accessMode, - path: '/spec/accessMode', - }); - - appendJSONPatch({ - patches, - newValue: values.ociConfig.skipServerVerification, - originalValue: ociRepoSpec.skipServerVerification, - path: '/spec/skipServerVerification', - }); - - if (values.ociConfig.skipServerVerification && ociRepoSpec['ca.crt']) { - patches.push({ op: 'remove', path: '/spec/ca.crt' }); - } else { - const caCrt = values.ociConfig.caCrt; - appendJSONPatch({ - patches, - newValue: caCrt ? btoa(caCrt) : caCrt, - originalValue: ociRepoSpec['ca.crt'], - path: '/spec/ca.crt', - }); - } - - const ociAuth = values.ociConfig.ociAuth; - if (ociAuth?.use && ociAuth.username && ociAuth.password) { - const ociAuthValue: DockerAuth = { - authType: OciAuthType.DOCKER, - username: ociAuth.username, - password: ociAuth.password, - }; - appendJSONPatch({ - patches, - newValue: ociAuthValue, - originalValue: ociRepoSpec.ociAuth, - path: '/spec/ociAuth', - }); - } else if (ociRepoSpec.ociAuth) { - patches.push({ op: 'remove', path: '/spec/ociAuth' }); - } - } - return patches; + // Otherwise, we have the same type of repository, and we must patch only the fields that changed + if (values.repoType === RepoSpecType.RepoSpecTypeOci) { + return getOciRepositoryPatches(values, repository.spec as OciRepoSpec); } - // Handle Git/Http repository patches - if ('url' in repository.spec) { - appendJSONPatch({ - patches, - newValue: values.url, - originalValue: repository.spec.url, - path: '/spec/url', - }); + if (values.repoType === RepoSpecType.RepoSpecTypeHttp) { + return getHttpRepositoryPatches(values, repository.spec as HttpRepoSpec); } - if (!values.useAdvancedConfig) { - if (isHttpRepoSpec(repository.spec)) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig', - }); - patches.push({ - op: 'remove', - path: '/spec/validationSuffix', - }); - } - if (isSshRepoSpec(repository.spec)) { - patches.push({ - op: 'remove', - path: '/spec/sshConfig', - }); - } - return patches; - } - - if (values.configType === 'http') { - if (isSshRepoSpec(repository.spec)) { - patches.push({ - op: 'remove', - path: '/spec/sshConfig', - }); - } - if (!isHttpRepoSpec(repository.spec)) { - const value: HttpConfig = { - skipServerVerification: values.httpConfig?.skipServerVerification, - }; - - if (values.httpConfig?.caCrt && !value.skipServerVerification) { - value['ca.crt'] = btoa(values.httpConfig.caCrt); - } - - if (values.httpConfig?.basicAuth?.use) { - value.password = values.httpConfig.basicAuth.password; - value.username = values.httpConfig.basicAuth.username; - } - if (values.httpConfig?.mTlsAuth?.use) { - if (values.httpConfig.mTlsAuth.tlsCrt) { - value['tls.crt'] = btoa(values.httpConfig.mTlsAuth.tlsCrt); - } - if (values.httpConfig.mTlsAuth.tlsKey) { - value['tls.key'] = btoa(values.httpConfig.mTlsAuth.tlsKey); - } - } - if (values.httpConfig?.token) { - value.token = values.httpConfig.token; - } - patches.push({ - op: 'add', - path: '/spec/httpConfig', - value, - }); - appendJSONPatch({ - patches, - newValue: values.validationSuffix, - originalValue: undefined, - path: '/spec/validationSuffix', - }); - } else { - appendJSONPatch({ - patches, - newValue: values.repoType === RepoSpecType.HTTP ? values.validationSuffix : undefined, - originalValue: repository.spec.validationSuffix, - path: '/spec/validationSuffix', - }); - - appendJSONPatch({ - patches, - newValue: values.httpConfig?.skipServerVerification, - originalValue: repository.spec.httpConfig.skipServerVerification, - path: '/spec/httpConfig/skipServerVerification', - }); - if (values.httpConfig?.skipServerVerification) { - if (repository.spec.httpConfig['ca.crt']) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig/ca.crt', - }); - } - } else { - const caCrt = values.httpConfig?.caCrt; - appendJSONPatch({ - patches, - newValue: caCrt ? btoa(caCrt) : caCrt, - originalValue: repository.spec.httpConfig['ca.crt'], - path: '/spec/httpConfig/ca.crt', - }); - } - - if (!values.httpConfig?.basicAuth?.use) { - if (repository.spec.httpConfig.password) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig/password', - }); - } - if (repository.spec.httpConfig.username) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig/username', - }); - } - } else { - appendJSONPatch({ - patches, - newValue: values.httpConfig?.basicAuth.password, - originalValue: repository.spec.httpConfig.password, - path: '/spec/httpConfig/password', - }); - appendJSONPatch({ - patches, - newValue: values.httpConfig?.basicAuth.username, - originalValue: repository.spec.httpConfig.username, - path: '/spec/httpConfig/username', - }); - } - - if (!values.httpConfig?.mTlsAuth?.use) { - if (repository.spec.httpConfig['tls.crt']) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig/tls.crt', - }); - } - if (repository.spec.httpConfig['tls.key']) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig/tls.key', - }); - } - } else { - appendJSONPatch({ - patches, - newValue: values.httpConfig?.mTlsAuth.tlsCrt, - originalValue: repository.spec.httpConfig['tls.crt'], - path: '/spec/httpConfig/tls.crt', - encodeB64: true, - }); - appendJSONPatch({ - patches, - newValue: values.httpConfig?.mTlsAuth.tlsKey, - originalValue: repository.spec.httpConfig['tls.key'], - path: '/spec/httpConfig/tls.key', - encodeB64: true, - }); - } - - if (!values.httpConfig?.token) { - if (repository.spec.httpConfig.token) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig/token', - }); - } - } else { - appendJSONPatch({ - patches, - newValue: values.httpConfig?.token, - originalValue: repository.spec.httpConfig.token, - path: '/spec/httpConfig/token', - }); - } - } - } else if (values.configType === 'ssh') { - if (isHttpRepoSpec(repository.spec)) { - patches.push({ - op: 'remove', - path: '/spec/httpConfig', - }); - if (repository.spec.validationSuffix) { - patches.push({ - op: 'remove', - path: '/spec/validationSuffix', - }); - } - } - if (!isSshRepoSpec(repository.spec)) { - const value: SshConfig = { - privateKeyPassphrase: values.sshConfig?.privateKeyPassphrase, - skipServerVerification: values.sshConfig?.skipServerVerification, - }; - if (values.sshConfig?.sshPrivateKey) { - value.sshPrivateKey = btoa(values.sshConfig.sshPrivateKey); - } - - patches.push({ - op: 'add', - path: '/spec/sshConfig', - value, - }); - } else { - appendJSONPatch({ - patches, - newValue: values.sshConfig?.privateKeyPassphrase, - originalValue: repository.spec.sshConfig.privateKeyPassphrase, - path: '/spec/sshConfig/privateKeyPassphrase', - }); - - appendJSONPatch({ - patches, - newValue: values.sshConfig?.skipServerVerification, - originalValue: repository.spec.sshConfig.skipServerVerification, - path: '/spec/sshConfig/skipServerVerification', - }); - - appendJSONPatch({ - patches, - newValue: values.sshConfig?.sshPrivateKey, - originalValue: repository.spec.sshConfig.sshPrivateKey, - path: '/spec/sshConfig/sshPrivateKey', - encodeB64: true, - }); - } + if (values.repoType === RepoSpecType.RepoSpecTypeGit) { + return getGitRepositoryPatches(values, repository.spec as GitRepoSpec); } - return patches; + return []; }; export const getResourceSyncEditPatch = (rs: ResourceSyncFormValue) => { @@ -604,7 +707,7 @@ export const repositorySchema = resourceSyncs: values.useResourceSyncs ? repoSyncSchema(t, values.resourceSyncs) : Yup.array(), }; - if (values.repoType === RepoSpecType.OCI) { + if (values.repoType === RepoSpecType.RepoSpecTypeOci) { return Yup.object({ ...baseSchema, url: Yup.string(), @@ -631,7 +734,7 @@ export const repositorySchema = return Yup.object({ ...baseSchema, url: Yup.string().when('repoType', { - is: (repoType: RepoSpecType) => repoType === RepoSpecType.GIT, + is: (repoType: RepoSpecType) => repoType === RepoSpecType.RepoSpecTypeGit, then: () => Yup.string() .matches( @@ -651,10 +754,10 @@ export const repositorySchema = }; export const getRepository = (values: Omit): Repository => { - if (values.repoType === RepoSpecType.OCI && values.ociConfig) { + if (values.repoType === RepoSpecType.RepoSpecTypeOci && values.ociConfig) { const ociRepoSpec: OciRepoSpec = { registry: values.ociConfig.registry, - type: RepoSpecType.OCI, + type: RepoSpecType.RepoSpecTypeOci, scheme: values.ociConfig.scheme || OciRepoSpec.scheme.HTTPS, accessMode: values.ociConfig.accessMode || OciRepoSpec.accessMode.READ, }; @@ -685,57 +788,91 @@ export const getRepository = (values: Omit { - {canListRS && repoDetails.spec.type === RepoSpecType.GIT && ( + {canListRS && repoDetails.spec.type === RepoSpecType.RepoSpecTypeGit && ( diff --git a/libs/ui-components/src/components/Repository/RepositoryDetails/RepositoryGeneralDetailsCard.tsx b/libs/ui-components/src/components/Repository/RepositoryDetails/RepositoryGeneralDetailsCard.tsx index 15ad5f4b8..7c38f2c44 100644 --- a/libs/ui-components/src/components/Repository/RepositoryDetails/RepositoryGeneralDetailsCard.tsx +++ b/libs/ui-components/src/components/Repository/RepositoryDetails/RepositoryGeneralDetailsCard.tsx @@ -20,28 +20,16 @@ import RepositoryStatus from '../../Status/RepositoryStatus'; import { getRepoTypeLabel, getRepoUrlOrRegistry, + hasCredentialsSettings, isHttpRepoSpec, isOciRepoSpec, - isSshRepoSpec, } from '../CreateRepository/utils'; import { GitRepositoryLink, HttpRepositoryUrl } from './RepositorySource'; const RepoPrivacy = ({ repo }: { repo: Repository }) => { const { t } = useTranslation(); - let isPrivate = false; - if (isHttpRepoSpec(repo.spec)) { - if (repo.spec.httpConfig.password || repo.spec.httpConfig['tls.crt'] || repo.spec.httpConfig['tls.key']) { - isPrivate = true; - } - } else if (isSshRepoSpec(repo.spec)) { - if (repo.spec.sshConfig.sshPrivateKey) { - isPrivate = true; - } - } else if (isOciRepoSpec(repo.spec)) { - if (repo.spec.ociAuth) { - isPrivate = true; - } - } + + const isPrivate = hasCredentialsSettings(repo.spec); return isPrivate ? ( <>