Compare commits

...

1 Commits

Author SHA1 Message Date
Ansuman Satapathy c7470a71a6
CDE fix issues (#3639)
- Fixed issue enabling support for all IDEs.
- Corrected access checks for Gitspace creation.
- Disabled usage limiter for local Gitspaces.

---------

Co-authored-by: Shashank Bandaru <bandaru.shashank@harness.io>
Co-authored-by: Dhruv Dhruv <dhruv@harness.io>
Co-authored-by: Deepak Bhatt <deepak.bhatt@harness.io>
2025-08-14 13:02:16 +05:30
27 changed files with 368 additions and 151 deletions

View File

@ -79,7 +79,7 @@ func (c *Controller) Action(
// All the actions should be idempotent.
switch in.Action {
case enum.GitspaceActionTypeStart:
err = c.gitspaceLimiter.Usage(ctx, space.ID)
err = c.gitspaceLimiter.Usage(ctx, space.ID, gitspaceConfig.InfraProviderResource.InfraProviderType)
if err != nil {
return nil, err
}

View File

@ -89,11 +89,6 @@ func (c *Controller) Create(
return nil, err
}
err = c.gitspaceLimiter.Usage(ctx, space.ID)
if err != nil {
return nil, err
}
// check if it's an internal repo
if in.CodeRepoType == enum.CodeRepoTypeGitness && *in.CodeRepoRef != "" {
repo, err := c.repoFinder.FindByRef(ctx, *in.CodeRepoRef)
@ -111,7 +106,7 @@ func (c *Controller) Create(
}
identifier, err := buildIdentifier(in.Identifier)
if err != nil {
return nil, fmt.Errorf("could not generate identrifier for gitspace config : %q %w", in.Identifier, err)
return nil, fmt.Errorf("could not generate identifier for gitspace config : %q %w", in.Identifier, err)
}
now := time.Now().UnixMilli()
var gitspaceConfig *types.GitspaceConfig
@ -150,6 +145,11 @@ func (c *Controller) Create(
return nil, err
}
err = c.gitspaceLimiter.Usage(ctx, space.ID, infraProviderResource.InfraProviderType)
if err != nil {
return nil, err
}
err = c.tx.WithTx(ctx, func(ctx context.Context) error {
codeRepo := types.CodeRepo{
URL: in.CodeRepoURL,

View File

@ -16,12 +16,14 @@ package limiter
import (
"context"
"github.com/harness/gitness/types/enum"
)
// Gitspace is an interface for managing gitspace limitations.
type Gitspace interface {
// Usage checks if the total usage for the root space and all sub-spaces is under a limit.
Usage(ctx context.Context, spaceID int64) error
Usage(ctx context.Context, spaceID int64, infraProviderType enum.InfraProviderType) error
}
var _ Gitspace = (*UnlimitedUsage)(nil)
@ -34,6 +36,6 @@ func NewUnlimitedUsage() Gitspace {
return UnlimitedUsage{}
}
func (UnlimitedUsage) Usage(_ context.Context, _ int64) error {
func (UnlimitedUsage) Usage(_ context.Context, _ int64, _ enum.InfraProviderType) error {
return nil
}

View File

@ -24,7 +24,6 @@ import (
"io"
"os/exec"
"path/filepath"
goruntime "runtime"
"strconv"
"strings"
@ -312,9 +311,7 @@ func prepareHostConfig(
}
extraHosts := getExtraHosts(runArgsMap)
if goruntime.GOOS == "linux" {
extraHosts = append(extraHosts, "host.docker.internal:host-gateway")
}
extraHosts = append(extraHosts, "host.docker.internal:host-gateway")
restartPolicy, err := getRestartPolicy(runArgsMap)
if err != nil {

View File

@ -119,7 +119,7 @@ func detectDefaultGitBranch(ctx context.Context, gitRepoDir string) (string, err
)
output := &bytes.Buffer{}
if err := cmd.Run(ctx, command.WithStdout(output)); err != nil {
return "", fmt.Errorf("failed to ls remote repo")
return "", fmt.Errorf("failed to ls remote repo: %w", err)
}
var lsRemoteHeadRegexp = regexp.MustCompile(`ref: refs/heads/([^\s]+)\s+HEAD`)
match := lsRemoteHeadRegexp.FindStringSubmatch(strings.TrimSpace(output.String()))

View File

@ -60,6 +60,11 @@ var membershipRoleContributorPermissions = slices.Clip(slices.Insert(membershipR
PermissionArtifactsUpload,
PermissionArtifactsDelete,
PermissionGitspaceCreate,
PermissionGitspaceEdit,
PermissionGitspaceDelete,
PermissionGitspaceUse,
))
var membershipRoleSpaceOwnerPermissions = slices.Clip(slices.Insert(membershipRoleReaderPermissions, 0,
@ -92,6 +97,7 @@ var membershipRoleSpaceOwnerPermissions = slices.Clip(slices.Insert(membershipRo
PermissionTemplateDelete,
PermissionTemplateEdit,
PermissionGitspaceCreate,
PermissionGitspaceEdit,
PermissionGitspaceDelete,
PermissionGitspaceUse,

View File

@ -33,11 +33,18 @@ interface CDEIDESelectProps {
isEditMode?: boolean
}
export const CDEIDESelect = ({ onChange, selectedIde, filteredIdeOptions = [], isEditMode }: CDEIDESelectProps) => {
export const CDEIDESelect = ({
onChange,
selectedIde,
filteredIdeOptions = [],
isEditMode = false
}: CDEIDESelectProps) => {
const { getString } = useStrings()
const selectedIDEOption = useMemo(() => {
if (!selectedIde) return undefined
if (!selectedIde) {
return undefined
}
const foundOption = filteredIdeOptions.find(item => item.value === selectedIde)
return foundOption || (filteredIdeOptions.length > 0 ? filteredIdeOptions[0] : undefined)

View File

@ -6,8 +6,64 @@
--button-height: 0 !important;
}
.detailsContainer {
display: grid !important;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)) !important;
gap: 20px;
> div {
margin-bottom: 10px;
}
}
.horizontalContainer {
:global(.Button--button--maDuR[class*='bp3-button']:not([class*='bp3-intent-']) > span svg path) {
fill: var(--white) !important;
}
}
.marginLeftContainer {
margin-left: var(--layout-spacing) !important;
}
.clickableText {
cursor: pointer !important;
color: var(--primary-7) !important;
font-size: var(--font-size-normal) !important;
text-align: left !important;
margin-left: var(--spacing-small) !important;
}
.iconMarginRight {
margin-right: 1px !important;
}
.infoIcon {
height: 14px !important;
width: 14px !important;
fill: white !important;
color: #0278d5 !important;
}
.standardIcon {
height: 20px !important;
width: 20px !important;
margin-right: 1px !important;
}
.statusText {
color: var(--black) !important;
font-size: var(--font-size-normal) !important;
font-weight: 600 !important;
text-align: left !important;
}
.providerText {
color: var(--grey-500) !important;
font-size: var(--font-size-normal) !important;
text-align: left !important;
}
.greyText {
color: var(--grey-500) !important;
}

View File

@ -16,6 +16,15 @@
/* eslint-disable */
// This is an auto-generated file
export declare const clickableText: string
export declare const detailsContainer: string
export declare const greyText: string
export declare const horizontalContainer: string
export declare const iconMarginRight: string
export declare const infoButton: string
export declare const infoIcon: string
export declare const marginLeftContainer: string
export declare const providerText: string
export declare const rowHeaders: string
export declare const standardIcon: string
export declare const statusText: string

View File

@ -16,30 +16,59 @@
import { Color } from '@harnessio/design-system'
import { Button, ButtonVariation, Container, Layout, Text } from '@harnessio/uicore'
import React from 'react'
import React, { useEffect, useState } from 'react'
import ReactTimeago from 'react-timeago'
import { PopoverPosition } from '@blueprintjs/core'
import { Circle, InfoEmpty } from 'iconoir-react'
import { Circle, InfoEmpty, Cloud } from 'iconoir-react'
import type { IconName } from '@harnessio/icons'
import Secret from 'cde-gitness/assests/secret.svg?url'
import { useStrings } from 'framework/strings'
import { GitspaceStatus } from 'cde-gitness/constants'
import { getGitspaceChanges, getIconByRepoType } from 'cde-gitness/utils/SelectRepository.utils'
import type { TypesGitspaceConfig } from 'services/cde'
import type { TypesGitspaceConfig, TypesInfraProviderConfig } from 'services/cde'
import { useInfraListingApi } from 'cde-gitness/hooks/useGetInfraListProvider'
import { getStatusColor, getStatusText } from '../GitspaceListing/ListGitspaces'
import getProviderIcon from '../../utils/InfraProvider.utils'
import ResourceDetails from '../ResourceDetails/ResourceDetails'
import { getRepoNameFromURL } from '../../utils/SelectRepository.utils'
import css from './DetailsCard.module.scss'
export const DetailsCard = ({
data
data,
standalone
}: {
data: TypesGitspaceConfig | TypesGitspaceConfig | null
data: TypesGitspaceConfig | null
standalone?: boolean
loading?: boolean
}) => {
const { getString } = useStrings()
const { branch, state, name, branch_url, code_repo_url, code_repo_type, instance, resource } = data || {}
const repoName = getRepoNameFromURL(code_repo_url) || ''
const [infraProvidersList, setInfraProvidersList] = useState<TypesInfraProviderConfig[]>([])
const {
data: infraProvidersData,
refetch: refetchInfraProviders,
loading: infraProvidersLoading
} = useInfraListingApi({
queryParams: {
acl_filter: 'true'
}
})
useEffect(() => {
// Only fetch infra providers if not in standalone mode
if (!standalone) {
refetchInfraProviders()
}
}, [refetchInfraProviders, standalone])
useEffect(() => {
if (infraProvidersData) {
setInfraProvidersList(infraProvidersData)
}
}, [infraProvidersData])
const { has_git_changes } = instance || {}
const gitChanges = getGitspaceChanges(has_git_changes, getString, '--')
const color = getStatusColor(state)
@ -52,24 +81,19 @@ export const DetailsCard = ({
: { icon: undefined }
return (
<>
<Layout.Horizontal
width={'90%'}
flex={{ justifyContent: 'space-between' }}
padding={{ bottom: 'xlarge', top: 'xlarge' }}>
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Layout.Horizontal width={'90%'} className={css.detailsContainer} padding={{ bottom: 'xlarge', top: 'xlarge' }}>
<Layout.Vertical
spacing="small"
flex={{ justifyContent: 'center', alignItems: 'flex-start' }}
className={css.marginLeftContainer}>
<Text className={css.rowHeaders}>{getString('cde.status')}</Text>
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
{state !== GitspaceStatus.STARTING && <Circle height={10} width={10} color={color} fill={color} />}
<Text
{...customProps}
color={Color.BLACK}
title={name}
font={{ align: 'left', size: 'normal', weight: 'semi-bold' }}>
<Text {...customProps} className={css.statusText} title={name}>
{getStatusText(getString, state)}
</Text>
</Layout.Horizontal>
</Layout.Vertical>
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Text className={css.rowHeaders}>{getString('cde.repository.repo')}</Text>
<Layout.Horizontal
@ -80,35 +104,75 @@ export const DetailsCard = ({
e.stopPropagation()
}}>
{getIconByRepoType({ repoType: code_repo_type, height: 20 })}
<Text
title={'RepoName'}
color={Color.PRIMARY_7}
margin={{ left: 'small' }}
style={{ cursor: 'pointer' }}
font={{ align: 'left', size: 'normal' }}
onClick={() => window.open(code_repo_url, '_blank')}>
<Text title={'RepoName'} className={css.clickableText} onClick={() => window.open(code_repo_url, '_blank')}>
{repoName}
</Text>
</Layout.Horizontal>
</Layout.Vertical>
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Text className={css.rowHeaders}>{getString('branch')}</Text>
<Text
iconProps={{ size: 10 }}
color={Color.PRIMARY_7}
icon="git-branch"
style={{ cursor: 'pointer' }}
className={css.clickableText}
onClick={() => window.open(branch_url, '_blank')}>
{branch}
</Text>
</Layout.Vertical>
{!standalone && (
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Text className={css.rowHeaders}>{getString('cde.infraProvider')}</Text>
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
{(() => {
const providerType = resource?.infra_provider_type || ''
const providerConfigId = resource?.config_identifier || ''
const providerIcon = getProviderIcon(providerType)
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Text className={css.rowHeaders}>{getString('cde.regionMachineType')}</Text>
<ResourceDetails resource={resource} />
</Layout.Vertical>
const provider = infraProvidersList.find(item => item.identifier === providerConfigId)
const displayName = provider?.name || providerConfigId
return (
<>
{providerIcon ? (
<img src={providerIcon} className={css.standardIcon} alt={'provider icon'} />
) : (
<Cloud className={css.standardIcon} />
)}
{infraProvidersLoading ? (
<Text
icon="loading"
iconProps={{ size: 16 }}
lineClamp={1}
font={{ align: 'left', size: 'normal' }}></Text>
) : (
<Text lineClamp={1} className={css.providerText} title={displayName}>
{displayName}
</Text>
)}
</>
)
})()}
</Layout.Horizontal>
</Layout.Vertical>
)}
{!standalone && (
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Text className={css.rowHeaders}>{getString('cde.regionMachineType')}</Text>
<ResourceDetails resource={resource} />
</Layout.Vertical>
)}
{/* Conditional SSH Key field - only shown when ssh_token_identifier exists */}
{data?.ssh_token_identifier && (
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Text className={css.rowHeaders}>{'SSH Key'}</Text>
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
<img src={Secret} className={css.standardIcon} alt={'secret'} />
<Text lineClamp={1} className={css.providerText} title={data.ssh_token_identifier}>
{data.ssh_token_identifier}
</Text>
</Layout.Horizontal>
</Layout.Vertical>
)}
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Layout.Horizontal
flex={{ alignItems: 'center', justifyContent: 'start' }}
@ -127,16 +191,15 @@ export const DetailsCard = ({
</Container>
}
tooltipProps={{ isDark: true, position: PopoverPosition.AUTO }}>
<InfoEmpty height={14} color="#0278D5" fill="white" />
<InfoEmpty className={css.infoIcon} />
</Button>
</Layout.Horizontal>
{instance?.active_time_started ? (
<ReactTimeago date={instance?.active_time_started || 0} />
) : (
<Text color={Color.GREY_500}>{getString('cde.na')}</Text>
<Text className={css.greyText}>{getString('cde.na')}</Text>
)}
</Layout.Vertical>
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Layout.Horizontal
flex={{ alignItems: 'center', justifyContent: 'start' }}
@ -155,16 +218,15 @@ export const DetailsCard = ({
</Container>
}
tooltipProps={{ isDark: true, position: PopoverPosition.AUTO }}>
<InfoEmpty height={14} color="#0278D5" fill="white" />
<InfoEmpty className={css.infoIcon} />
</Button>
</Layout.Horizontal>
{instance?.last_used ? (
<ReactTimeago date={instance?.last_used || 0} />
) : (
<Text color={Color.GREY_500}>{getString('cde.na')}</Text>
<Text className={css.greyText}>{getString('cde.na')}</Text>
)}
</Layout.Vertical>
<Layout.Vertical spacing="small" flex={{ justifyContent: 'center', alignItems: 'flex-start' }}>
<Layout.Horizontal
flex={{ alignItems: 'center', justifyContent: 'start' }}
@ -183,10 +245,10 @@ export const DetailsCard = ({
</Container>
}
tooltipProps={{ isDark: true, position: PopoverPosition.AUTO }}>
<InfoEmpty height={14} color="#0278D5" fill="white" />
<InfoEmpty className={css.infoIcon} />
</Button>
</Layout.Horizontal>
<Text color={Color.GREY_500}>{gitChanges}</Text>
<Text className={css.greyText}>{gitChanges}</Text>
</Layout.Vertical>
</Layout.Horizontal>
</>

View File

@ -210,7 +210,7 @@ export const GitnessRepoImportForm = ({ isCDE }: { isCDE?: boolean }) => {
code_repo_url: repo.git_url,
branch: repo.default_branch,
identifier: repoParams?.[repoParams.length - 1],
name: repo.path,
name: '',
code_repo_ref: repo.path,
...codeRepoType
}

View File

@ -24,7 +24,7 @@
div[class*='TableV2--cells'],
div[class*='TableV2--header'] {
display: grid !important;
grid-template-columns: 1fr 1fr 1fr 0.7fr 50px;
grid-template-columns: 1fr 1fr 1fr 0.7fr 50px !important;
}
}
@ -38,7 +38,7 @@
div[class*='TableV2--cells'],
div[class*='TableV2--header'] {
display: grid !important;
grid-template-columns: 1.2fr 1fr 1.5fr 0.75fr 0.75fr 1fr 50px;
grid-template-columns: 1.4fr 1.4fr 1.5fr 0.6fr 0.7fr 0.8fr 50px !important;
}
}
@ -107,3 +107,15 @@
fill: #004ba4;
}
}
.standardProviderIcon {
height: 20px !important;
width: 20px !important;
margin-right: 1px !important;
}
.providerNameText {
color: var(--black) !important;
font-size: var(--font-size-normal) !important;
text-align: left !important;
}

View File

@ -25,6 +25,8 @@ export declare const gitspaceIdContainer: string
export declare const gitspaceUrl: string
export declare const listContainer: string
export declare const popover: string
export declare const providerNameText: string
export declare const repositoryCell: string
export declare const standardProviderIcon: string
export declare const stopModal: string
export declare const table: string

View File

@ -30,7 +30,7 @@ import { Color } from '@harnessio/design-system'
import type { Renderer, CellProps } from 'react-table'
//import { Icon } from '@harnessio/icons'
import ReactTimeago from 'react-timeago'
import { Circle, Cpu, Clock, Play, Db, ModernTv } from 'iconoir-react'
import { Circle, Cpu, Clock, Play, Db, ModernTv, Cloud } from 'iconoir-react'
import { Intent, Menu, MenuItem, PopoverInteractionKind, Position } from '@blueprintjs/core'
import { useHistory } from 'react-router-dom'
import type { IconName } from '@harnessio/icons'
@ -45,6 +45,7 @@ import type {
EnumIDEType,
TypesGitspaceConfig,
TypesInfraProviderResource,
TypesInfraProviderConfig,
TypesGitspaceSettingsResponse
} from 'services/cde'
import gitspaceIcon from 'cde-gitness/assests/gitspace.svg?url'
@ -56,7 +57,9 @@ import { useGitspaceActions } from 'cde-gitness/hooks/useGitspaceActions'
import { useDeleteGitspaces } from 'cde-gitness/hooks/useDeleteGitspaces'
import { useOpenVSCodeBrowserURL } from 'cde-gitness/hooks/useOpenVSCodeBrowserURL'
import { getGitspaceChanges, getIconByRepoType } from 'cde-gitness/utils/SelectRepository.utils'
import { useInfraListingApi } from 'cde-gitness/hooks/useGetInfraListProvider'
import { usePaginationProps } from 'cde-gitness/hooks/usePaginationProps'
import getProviderIcon from '../../utils/InfraProvider.utils'
import ResourceDetails from '../ResourceDetails/ResourceDetails'
import CopyButton from '../CopyButton/CopyButton'
import { EditGitspace } from '../EditGitspace/EditGitspace'
@ -164,16 +167,36 @@ export const RenderGitspaceName: Renderer<
)
}
export const RenderRegionAndMachine: Renderer<
CellProps<TypesGitspaceConfig & { resource?: TypesInfraProviderResource }>
> = ({ row }) => {
const details = row.original
const { resource } = details
return (
<Layout.Vertical spacing={'medium'}>
<ResourceDetails resource={resource} isListingPage={true} />
</Layout.Vertical>
)
const getRenderInfraProvider = (infraList: TypesInfraProviderConfig[], infraListLoading: boolean) => {
return ({ row }: CellProps<TypesGitspaceConfig & { resource?: TypesInfraProviderResource }>) => {
const details = row.original
const { resource } = details
const providerType = details.resource?.infra_provider_type || ''
const providerConfigId = details.resource?.config_identifier || ''
const providerIcon = getProviderIcon(providerType)
const provider = infraList.find(item => item.identifier === providerConfigId)
const displayName = provider?.name || ''
return (
<Layout.Vertical spacing={'medium'}>
<Layout.Horizontal spacing={'small'} flex={{ alignItems: 'center', justifyContent: 'start' }}>
{providerIcon ? (
<img src={providerIcon} className={css.standardProviderIcon} />
) : (
<Cloud className={css.standardProviderIcon} />
)}
{infraListLoading ? (
<Text icon="loading" iconProps={{ size: 16 }} lineClamp={1} className={css.providerNameText}></Text>
) : (
<Text lineClamp={1} color={Color.BLACK} title={displayName} className={css.providerNameText}>
{displayName}
</Text>
)}
</Layout.Horizontal>
<ResourceDetails resource={resource} />
</Layout.Vertical>
)
}
}
export const OwnerAndCreatedAt: Renderer<CellProps<TypesGitspaceConfig>> = ({ row }) => {
@ -775,6 +798,30 @@ export const ListGitspaces = ({
const { routes, standalone } = useAppContext()
const [currentRow, setCurrentRow] = useState<TypesGitspaceConfig>()
const [infraProvidersList, setInfraProvidersList] = useState<TypesInfraProviderConfig[]>([])
// Fetch the infrastructure providers list
const {
data: infraProvidersData,
refetch: refetchInfraProviders,
loading: infraProvidersLoading
} = useInfraListingApi({
queryParams: {
acl_filter: 'true'
}
})
useEffect(() => {
if (!standalone) {
refetchInfraProviders()
}
}, [refetchInfraProviders, standalone])
useEffect(() => {
if (infraProvidersData) {
setInfraProvidersList(infraProvidersData)
}
}, [infraProvidersData])
const [handleStartGitspace, hideStartModal] = useModalHook(() => {
return (
@ -855,13 +902,13 @@ export const ListGitspaces = ({
}
]
const regionColumn = standalone
const infraProviderColumn = standalone
? []
: [
{
id: 'gitsapceId',
Header: getString('cde.regionAndMachineSize'),
Cell: RenderRegionAndMachine
id: 'infraProvider',
Header: getString('cde.listing.infrastructureDetails'),
Cell: getRenderInfraProvider(infraProvidersList, infraProvidersLoading) // Pass the list here
}
]
@ -924,7 +971,7 @@ export const ListGitspaces = ({
Header: getString('cde.gitspaces'),
Cell: RenderGitspaceName
},
...regionColumn,
...infraProviderColumn,
{
id: 'repository',
Header: getString('cde.repositoryAndBranch'),

View File

@ -3,3 +3,23 @@
align-items: center;
gap: 4px;
}
.container {
width: 100%;
overflow: hidden;
}
.iconContainer {
flex-shrink: 0;
}
.textContainer {
overflow: hidden;
}
.truncatedText {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 88%;
}

View File

@ -16,4 +16,8 @@
/* eslint-disable */
// This is an auto-generated file
export declare const container: string
export declare const iconContainer: string
export declare const iconTextStyle: string
export declare const textContainer: string
export declare const truncatedText: string

View File

@ -6,42 +6,21 @@ import { useStrings } from 'framework/strings'
import type { TypesInfraProviderResource } from 'services/cde'
import css from './ResourceDetails.module.scss'
const ResourceDetails = ({
resource,
isListingPage = false
}: {
resource?: TypesInfraProviderResource
isListingPage?: boolean
}) => {
const ResourceDetails = ({ resource }: { resource?: TypesInfraProviderResource }) => {
const { getString } = useStrings()
const { region, name } = resource || {}
return isListingPage ? (
<Layout.Vertical spacing="small">
<span className={css.iconTextStyle}>
<img height={12} width={12} src={RegionIcon} />
<Text font={{ size: 'small' }} lineClamp={1}>
{region?.toUpperCase() || getString('cde.na')}
</Text>
</span>
<span className={css.iconTextStyle}>
<Cpu height={12} width={12} />
<Text font={{ size: 'small' }} lineClamp={1}>
{name || getString('cde.na')}
</Text>
</span>
</Layout.Vertical>
) : (
<Layout.Horizontal spacing="small">
<Layout.Horizontal spacing={'xsmall'} flex={{ alignItems: 'center' }}>
<img height={12} width={12} src={RegionIcon} />{' '}
<Text font={{ size: 'small' }} lineClamp={1}>
return (
<Layout.Horizontal spacing="small" className={css.container}>
<Layout.Horizontal spacing={'xsmall'} flex={{ alignItems: 'center' }} className={css.textContainer}>
<img height={12} width={12} src={RegionIcon} className={css.iconContainer} />
<Text font={{ size: 'small' }} lineClamp={1} title={region?.toUpperCase() || getString('cde.na')}>
{region?.toUpperCase() || getString('cde.na')}
</Text>
</Layout.Horizontal>
<Container width={'5px'} />
<Layout.Horizontal spacing={'xsmall'} flex={{ alignItems: 'center' }}>
<Cpu height={12} width={12} />{' '}
<Text font={{ size: 'small' }} lineClamp={1}>
<Container width={'3px'} />
<Layout.Horizontal spacing={'xsmall'} flex={{ alignItems: 'center' }} className={css.textContainer}>
<Cpu height={12} width={12} className={css.iconContainer} />
<Text font={{ size: 'small' }} title={name || getString('cde.na')} lineClamp={1} className={css.truncatedText}>
{name || getString('cde.na')}
</Text>
</Layout.Horizontal>

View File

@ -6,27 +6,13 @@ import { useFormikContext } from 'formik'
import { Color } from '@harnessio/design-system'
import { useStrings } from 'framework/strings'
import type { OpenapiCreateGitspaceRequest, TypesInfraProviderConfig } from 'services/cde'
import { HARNESS_GCP, HYBRID_VM_GCP, HYBRID_VM_AWS } from 'cde-gitness/constants'
import { HARNESS_GCP } from 'cde-gitness/constants'
import infrasvg from 'icons/Infrastructure.svg?url'
import googleCloudIcon from 'icons/google-cloud.svg?url'
import awsIcon from 'cde-gitness/assests/aws.svg?url'
import HarnessIcon from 'icons/Harness.svg?url'
import type { dropdownProps } from 'cde-gitness/constants'
import getProviderIcon from '../../utils/InfraProvider.utils'
import { CDECustomDropdown } from '../CDECustomDropdown/CDECustomDropdown'
import css from './SelectInfraProviderType.module.scss'
// Function to get provider icon based on provider type
const getProviderIcon = (providerType: string) => {
if (providerType === HYBRID_VM_GCP) {
return googleCloudIcon
} else if (providerType === HARNESS_GCP) {
return HarnessIcon
} else if (providerType === HYBRID_VM_AWS) {
return awsIcon
}
return null
}
interface SelectInfraProviderTypeProps {
infraProviders: dropdownProps[]
allProviders?: TypesInfraProviderConfig[]

View File

@ -23,7 +23,7 @@ import { Color } from '@harnessio/design-system'
import { useHistory } from 'react-router-dom'
import { Icon } from '@harnessio/icons'
import { useStrings } from 'framework/strings'
import { getRepoIdFromURL, getRepoNameFromURL, isValidUrl } from 'cde-gitness/utils/SelectRepository.utils'
import { getRepoIdFromURL, isValidUrl } from 'cde-gitness/utils/SelectRepository.utils'
import { BranchInput } from 'cde-gitness/components/BranchInput/BranchInput'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import NewRepoModalButton from 'components/NewRepoModalButton/NewRepoModalButton'
@ -69,7 +69,7 @@ export const ThirdPartyRepoImportForm = () => {
code_repo_url: response.url,
branch: response.branch,
identifier: getRepoIdFromURL(response.url),
name: getRepoNameFromURL(response.url),
name: '',
code_repo_type: EnumGitspaceCodeRepoType.UNKNOWN
}
})

View File

@ -34,6 +34,7 @@ import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import { getErrorMessage } from 'utils/Utils'
import { useFindGitspaceSettings } from 'services/cde'
import { GitspaceSelect } from 'cde-gitness/components/GitspaceSelect/GitspaceSelect'
import harnessCode from 'cde-gitness/assests/harnessCode.svg?url'
import codeSandboxIcon from 'cde-gitness/assests/codeSandboxLogo.svg?url'
@ -45,7 +46,7 @@ import bitbucket from 'cde-gitness/assests/bitbucket.svg?url'
import { CDEAnyGitImport } from 'cde-gitness/components/CDEAnyGitImport/CDEAnyGitImport'
import { CDEIDESelect } from 'cde-gitness/components/CDEIDESelect/CDEIDESelect'
import { SelectInfraProvider } from 'cde-gitness/components/SelectInfraProvider/SelectInfraProvider'
import { OpenapiCreateGitspaceRequest, useCreateGitspace, TypesGitspaceSettingsResponse } from 'services/cde'
import { OpenapiCreateGitspaceRequest, useCreateGitspace } from 'services/cde'
import { useGetCDEAPIParams } from 'cde-gitness/hooks/useGetCDEAPIParams'
import { EnumGitspaceCodeRepoType, getIDETypeOptions, getIDEOption } from 'cde-gitness/constants'
import { CDESSHSelect } from 'cde-gitness/components/CDESSHSelect/CDESSHSelect'
@ -71,10 +72,6 @@ export interface RepoQueryParams {
codeRepoType?: EnumGitspaceCodeRepoType
}
export interface CDECreateGitspaceProps {
gitspaceSettings: TypesGitspaceSettingsResponse | null
}
export const scmOptions: SCMType[] = [
{ name: 'Harness Code', value: EnumGitspaceCodeRepoType.HARNESS_CODE, icon: harnessCode },
{ name: 'GitHub Cloud', value: EnumGitspaceCodeRepoType.GITHUB, icon: github },
@ -99,7 +96,7 @@ export const scmOptionsCDE: SCMType[] = [
...onPremSCMOptions
]
export const CDECreateGitspace = ({ gitspaceSettings }: CDECreateGitspaceProps) => {
export const CDECreateGitspace = () => {
const { getString } = useStrings()
const { routes, currentUserProfileURL, hooks, currentUser } = useAppContext()
const { useGetUserSourceCodeManagers } = hooks
@ -119,6 +116,16 @@ export const CDECreateGitspace = ({ gitspaceSettings }: CDECreateGitspaceProps)
const [repoURLviaQueryParam, setrepoURLviaQueryParam] = useState<RepoQueryParams>({ ...repoQueryParams })
const { data: gitspaceSettings, error: settingsError } = useFindGitspaceSettings({
accountIdentifier
})
useEffect(() => {
if (settingsError) {
showError(getErrorMessage(settingsError))
}
}, [settingsError, getString, showError])
useEffect(() => {
if (gitspaceSettings?.settings?.gitspace_config) {
const { scm } = gitspaceSettings.settings.gitspace_config

View File

@ -27,12 +27,13 @@ import {
useToaster
} from '@harnessio/uicore'
import { useHistory } from 'react-router-dom'
import { FontVariation } from '@harnessio/design-system'
import { Color, FontVariation } from '@harnessio/design-system'
import { useCreateGitspace, type OpenapiCreateGitspaceRequest } from 'cde-gitness/services'
import RepositoryTypeButton, { RepositoryType } from 'cde-gitness/components/RepositoryTypeButton/RepositoryTypeButton'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { useStrings } from 'framework/strings'
import { useAppContext } from 'AppContext'
import { getIDETypeOptions } from 'cde-gitness/constants'
import codeSandboxIcon from 'cde-gitness/assests/codeSandboxLogo.svg?url'
import { getErrorMessage } from 'utils/Utils'
import { GitnessRepoImportForm } from 'cde-gitness/components/GitnessRepoImportForm/GitnessRepoImportForm'
@ -51,6 +52,9 @@ export const GitnessCreateGitspace = () => {
const [activeButton, setActiveButton] = useState(RepositoryType.GITNESS)
const { showSuccess, showError } = useToaster()
const { mutate } = useCreateGitspace({})
const allIdeOptions = useMemo(() => {
return getIDETypeOptions(getString)
}, [getString])
const suggestedName = useMemo(() => generateGitspaceName(), [])
return (
@ -76,7 +80,10 @@ export const GitnessCreateGitspace = () => {
showError(getErrorMessage(error))
}
}}
initialValues={gitnessFormInitialValues}
initialValues={{
...gitnessFormInitialValues,
ide: allIdeOptions.length > 0 ? allIdeOptions[0].value : getString('cde.create.ideEmpty')
}}
validationSchema={validateGitnessForm(getString)}
formName="importRepoForm"
enableReinitialize>
@ -114,14 +121,16 @@ export const GitnessCreateGitspace = () => {
<Layout.Horizontal className={css.leftSection}>
<img src={codeSandboxIcon} alt="gitspace" className={css.icon} />
<Layout.Vertical className={css.textSection} spacing={'small'}>
<Text>{getString('cde.create.gitspaceNameLabel')}</Text>
<Text color={Color.GREY_500} font={{ weight: 'bold' }}>
{getString('cde.create.gitspaceNameLabel')}
</Text>
<Layout.Vertical spacing={'xsmall'}>
<Text font={'small'}>{getString('cde.create.gitspaceNameHelpertext1')}</Text>
<Layout.Horizontal>
<Layout.Horizontal spacing={'xsmall'}>
<Text font={'small'}>{getString('cde.create.gitspaceNameHelpertext2')}</Text>
<Text
className={css.suggestedName}
font={{ variation: FontVariation.SMALL }}
font={'small'}
onClick={e => {
e.stopPropagation()
formik.setFieldValue('name', suggestedName)
@ -141,7 +150,11 @@ export const GitnessCreateGitspace = () => {
/>
</Container>
</Layout.Horizontal>
<CDEIDESelect onChange={formik.setFieldValue} selectedIde={formik.values.ide} />
<CDEIDESelect
onChange={formik.setFieldValue}
selectedIde={formik.values.ide}
filteredIdeOptions={allIdeOptions}
/>
<Button width={'100%'} variation={ButtonVariation.PRIMARY} height={50} type="submit">
{getString('cde.createGitspace')}
</Button>

View File

@ -20,8 +20,6 @@ import { Color } from '@harnessio/design-system'
import { useStrings } from 'framework/strings'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { useAppContext } from 'AppContext'
import { useFindGitspaceSettings } from 'services/cde'
import { useGetCDEAPIParams } from 'cde-gitness/hooks/useGetCDEAPIParams'
import { GitnessCreateGitspace } from './GitnessCreateGitspace'
import { CDECreateGitspace } from './CDECreateGitspace'
import css from './GitspaceCreate.module.scss'
@ -30,15 +28,6 @@ const GitspaceCreate = () => {
const { getString } = useStrings()
const space = useGetSpaceParam()
const { standalone, routes } = useAppContext()
const { accountIdentifier = '' } = useGetCDEAPIParams()
const {
data: gitspaceSettings,
loading: settingsLoading,
error: settingsError
} = useFindGitspaceSettings({
accountIdentifier
})
return (
<>
@ -53,7 +42,7 @@ const GitspaceCreate = () => {
/>
}
/>
<Page.Body className={css.main} loading={settingsLoading} error={settingsError}>
<Page.Body className={css.main}>
<Container className={css.titleContainer}>
<Layout.Vertical spacing="small" margin={{ bottom: 'medium' }}>
<Heading font={{ weight: 'bold' }} color={Color.BLACK} level={2}>
@ -64,7 +53,7 @@ const GitspaceCreate = () => {
</Container>
<Card className={css.cardMain}>
<Container className={css.subContainers}>
{standalone ? <GitnessCreateGitspace /> : <CDECreateGitspace gitspaceSettings={gitspaceSettings} />}
{standalone ? <GitnessCreateGitspace /> : <CDECreateGitspace />}
</Container>
</Card>
</Page.Body>

View File

@ -276,7 +276,7 @@ const GitspaceDetails = () => {
error: settingsError
} = useFindGitspaceSettings({
accountIdentifier: accountIdentifier || '',
lazy: !accountIdentifier
lazy: !accountIdentifier || standalone
})
const { refetchToken, setSelectedRowUrl } = useOpenVSCodeBrowserURL()
@ -575,7 +575,7 @@ const GitspaceDetails = () => {
)}
<Card className={css.cardContainer}>
<Text font={{ variation: FontVariation.CARD_TITLE }}>{getString('cde.gitspaceDetail')}</Text>
<DetailsCard data={data} loading={mutateLoading} />
<DetailsCard data={data} standalone={standalone} loading={mutateLoading} />
</Card>
<Card className={css.cardContainer}>
<EventTimelineAccordion data={eventData as TypesGitspaceEventResponse[]} />
@ -660,7 +660,7 @@ const GitspaceDetails = () => {
}}>
<EditGitspace
isOpen={isEditModalOpen}
gitspaceSettings={gitspaceSettings}
gitspaceSettings={gitspaceSettings || null}
onClose={() => setIsEditModalOpen(false)}
gitspaceId={gitspaceId}
onGitspaceUpdated={() => {

View File

@ -312,6 +312,7 @@ listing:
uninitialized: Uninitialized
cleaning: Cleaning
ownerAndCreated: Owner & Created On
infrastructureDetails: Infrastructure Details
create:
repositoryDetails: Repository Details
gitnessRepositories: Harness Repositories

View File

@ -0,0 +1,17 @@
import googleCloudIcon from 'icons/google-cloud.svg?url'
import awsIcon from 'cde-gitness/assests/aws.svg?url'
import HarnessIcon from 'icons/Harness.svg?url'
import { HARNESS_GCP, HYBRID_VM_GCP, HYBRID_VM_AWS } from 'cde-gitness/constants'
const getProviderIcon = (providerType: string) => {
if (providerType === HYBRID_VM_GCP) {
return googleCloudIcon
} else if (providerType === HARNESS_GCP) {
return HarnessIcon
} else if (providerType === HYBRID_VM_AWS) {
return awsIcon
}
return null
}
export default getProviderIcon

View File

@ -65,7 +65,7 @@ export const getIconByRepoType = ({
}): React.ReactNode => {
const scmOption = [...scmOptions, ...onPremSCMOptions].find(option => option.value === repoType)
return (
<img height={height} width={height} src={defaultTo(scmOption?.icon, genericGit)} style={{ marginRight: '10px' }} />
<img height={height} width={height} src={defaultTo(scmOption?.icon, genericGit)} style={{ marginRight: '4px' }} />
)
}

View File

@ -1602,6 +1602,7 @@ export interface StringsMap {
'cde.lastUsedTooltip': string
'cde.listing.cleaning': string
'cde.listing.error': string
'cde.listing.infrastructureDetails': string
'cde.listing.offline': string
'cde.listing.online': string
'cde.listing.ownerAndCreated': string