move pipelines inside repos

This commit is contained in:
Dan Wilson 2023-08-31 16:13:55 +01:00
parent d15aa03dfd
commit 9f20c3a1b9
12 changed files with 2655 additions and 973 deletions

View File

@ -45,7 +45,7 @@ export interface CODERoutes {
toCODESpaceAccessControl: (args: Required<Pick<CODEProps, 'space'>>) => string
toCODESpaceSettings: (args: Required<Pick<CODEProps, 'space'>>) => string
toCODEPipelines: (args: Required<Pick<CODEProps, 'space'>>) => string
toCODEPipelines: (args: Required<Pick<CODEProps, 'repoPath'>>) => string
toCODEPipelinesNew: (args: Required<Pick<CODEProps, 'space'>>) => string
toCODESecrets: (args: Required<Pick<CODEProps, 'space'>>) => string
@ -74,8 +74,8 @@ export interface CODERoutes {
toCODEWebhookDetails: (args: Required<Pick<CODEProps, 'repoPath' | 'webhookId'>>) => string
toCODESettings: (args: Required<Pick<CODEProps, 'repoPath'>>) => string
toCODEExecutions: (args: Required<Pick<CODEProps, 'space' | 'pipeline'>>) => string
toCODEExecution: (args: Required<Pick<CODEProps, 'space' | 'pipeline' | 'execution'>>) => string
toCODEExecutions: (args: Required<Pick<CODEProps, 'repoPath' | 'pipeline'>>) => string
toCODEExecution: (args: Required<Pick<CODEProps, 'repoPath' | 'pipeline' | 'execution'>>) => string
toCODESecret: (args: Required<Pick<CODEProps, 'space' | 'secret'>>) => string
}
@ -96,7 +96,7 @@ export const routes: CODERoutes = {
toCODESpaceAccessControl: ({ space }) => `/access-control/${space}`,
toCODESpaceSettings: ({ space }) => `/settings/${space}`,
toCODEPipelines: ({ space }) => `/pipelines/${space}`,
toCODEPipelines: ({ repoPath }) => `/${repoPath}/pipelines`,
toCODEPipelinesNew: ({ space }) => `/pipelines/${space}/new`,
toCODESecrets: ({ space }) => `/secrets/${space}`,
@ -130,8 +130,7 @@ export const routes: CODERoutes = {
toCODEWebhookNew: ({ repoPath }) => `/${repoPath}/webhooks/new`,
toCODEWebhookDetails: ({ repoPath, webhookId }) => `/${repoPath}/webhook/${webhookId}`,
toCODEExecutions: ({ space, pipeline }) => `/pipelines/${space}/pipeline/${pipeline}`,
toCODEExecution: ({ space, pipeline, execution }) =>
`/pipelines/${space}/pipeline/${pipeline}/execution/${execution}`,
toCODEExecutions: ({ repoPath, pipeline }) => `/${repoPath}/pipelines/${pipeline}`,
toCODEExecution: ({ repoPath, pipeline, execution }) => `/${repoPath}/pipelines/${pipeline}/execution/${execution}`,
toCODESecret: ({ space, secret }) => `/secrets/${space}/secret/${secret}`
}

View File

@ -167,7 +167,7 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
{OPEN_SOURCE_PIPELINES && (
<Route
path={routes.toCODEExecution({
space: pathProps.space,
repoPath,
pipeline: pathProps.pipeline,
execution: pathProps.execution
})}
@ -179,7 +179,7 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
)}
{OPEN_SOURCE_PIPELINES && (
<Route path={routes.toCODEExecutions({ space: pathProps.space, pipeline: pathProps.pipeline })} exact>
<Route path={routes.toCODEExecutions({ repoPath, pipeline: pathProps.pipeline })} exact>
<LayoutWithSideNav title={getString('pageTitle.executions')}>
<ExecutionList />
</LayoutWithSideNav>
@ -195,7 +195,7 @@ export const RouteDestinations: React.FC = React.memo(function RouteDestinations
)}
{OPEN_SOURCE_PIPELINES && (
<Route path={routes.toCODEPipelines({ space: pathProps.space })} exact>
<Route path={routes.toCODEPipelines({ repoPath })} exact>
<LayoutWithSideNav title={getString('pageTitle.pipelines')}>
<PipelineList />
</LayoutWithSideNav>

View File

@ -2,19 +2,20 @@ import React, { FC } from 'react'
import { useParams } from 'react-router-dom'
import { Container, Layout, Text } from '@harnessio/uicore'
import { Color, FontVariation } from '@harnessio/design-system'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import type { CODEProps } from 'RouteDefinitions'
import type { TypesStage } from 'services/code'
import ConsoleStep from 'components/ConsoleStep/ConsoleStep'
import { timeDistance } from 'utils/Utils'
// import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
import css from './Console.module.scss'
interface ConsoleProps {
stage: TypesStage | undefined
repoPath: string
}
const Console: FC<ConsoleProps> = ({ stage }) => {
const space = useGetSpaceParam()
const Console: FC<ConsoleProps> = ({ stage, repoPath }) => {
// const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
const { pipeline, execution: executionNum } = useParams<CODEProps>()
return (
@ -39,7 +40,7 @@ const Console: FC<ConsoleProps> = ({ stage }) => {
step={step}
executionNumber={Number(executionNum)}
pipelineName={pipeline}
spaceName={space}
repoPath={repoPath}
stageNumber={stage.number}
/>
))}

View File

@ -12,18 +12,18 @@ import css from './ConsoleStep.module.scss'
interface ConsoleStepProps {
step: TypesStep | undefined
stageNumber: number | undefined
spaceName: string
repoPath: string
pipelineName: string | undefined
executionNumber: number
}
const ConsoleStep: FC<ConsoleStepProps> = ({ step, stageNumber, spaceName, pipelineName, executionNumber }) => {
const ConsoleStep: FC<ConsoleStepProps> = ({ step, stageNumber, repoPath, pipelineName, executionNumber }) => {
const { getString } = useStrings()
const [isOpened, setIsOpened] = React.useState(false)
const { data, error, loading, refetch } = useGet<string>({
path: `/api/v1/pipelines/${spaceName}/${pipelineName}/+/executions/${executionNumber}/logs/${String(
path: `/api/v1/repos/${repoPath}/+/pipelines/${pipelineName}/executions/${executionNumber}/logs/${String(
stageNumber
)}/${String(step?.number)}`,
lazy: true

View File

@ -14,12 +14,15 @@
margin: 0.5rem 0 0.5rem 1rem !important;
cursor: pointer;
&:not(:last-child) {
border-bottom: 1px solid var(--grey-100);
}
.layout {
display: flex;
align-items: center;
min-height: var(--stage-title-height);
padding: 0 var(--spacing-medium) 0 var(--spacing-medium);
border-radius: 10px 0 0 10px !important;
&.selected {
background-color: var(--primary-1);

View File

@ -18,7 +18,7 @@ interface ExecutionStageProps {
setSelectedStage: (selectedStage: number | null) => void
}
const ExecutionStage: FC<ExecutionStageProps> = ({ stage, isSelected = false, setSelectedStage = () => {} }) => {
const ExecutionStage: FC<ExecutionStageProps> = ({ stage, isSelected = false, setSelectedStage }) => {
return (
<Container
className={css.menuItem}

View File

@ -1,21 +1,19 @@
import React, { useMemo, useState } from 'react'
import { Container, Layout } from '@harnessio/uicore'
import { Render } from 'react-jsx-match'
import { useHistory, useRouteMatch, useParams } from 'react-router-dom'
import { useHistory, useRouteMatch } from 'react-router-dom'
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
import { useStrings } from 'framework/strings'
import type { TypesSpace } from 'services/code'
import { SpaceSelector } from 'components/SpaceSelector/SpaceSelector'
import { useAppContext } from 'AppContext'
import { useFeatureFlag } from 'hooks/useFeatureFlag'
import type { CODEProps } from 'RouteDefinitions'
import { NavMenuItem } from './NavMenuItem'
import css from './DefaultMenu.module.scss'
export const DefaultMenu: React.FC = () => {
const history = useHistory()
const { routes } = useAppContext()
const params = useParams<CODEProps>()
const [selectedSpace, setSelectedSpace] = useState<TypesSpace | undefined>()
const { repoMetadata, gitRef, commitRef } = useGetRepositoryMetadata()
const { getString } = useStrings()
@ -26,7 +24,6 @@ export const DefaultMenu: React.FC = () => {
[routeMatch]
)
const isCommitSelected = useMemo(() => routeMatch.path === '/:space*/:repoName/commit/:commitRef*', [routeMatch])
const isPipelineSelected = routeMatch.path.startsWith('/pipelines/:space*/pipeline/:pipeline')
const { OPEN_SOURCE_PIPELINES, OPEN_SOURCE_SECRETS } = useFeatureFlag()
return (
@ -121,6 +118,17 @@ export const DefaultMenu: React.FC = () => {
})}
/>
{OPEN_SOURCE_PIPELINES && (
<NavMenuItem
data-code-repo-section="pipelines"
isSubLink
label={getString('pageTitle.pipelines')}
to={routes.toCODEPipelines({
repoPath
})}
/>
)}
<NavMenuItem
data-code-repo-section="settings"
isSubLink
@ -133,48 +141,6 @@ export const DefaultMenu: React.FC = () => {
</Container>
</Render>
{OPEN_SOURCE_PIPELINES && (
<Render when={selectedSpace}>
{/* icon is placeholder */}
<NavMenuItem
icon="pipeline"
label={getString('pageTitle.pipelines')}
to={routes.toCODEPipelines({ space: selectedSpace?.path as string })}
rightIcon={isPipelineSelected ? 'main-chevron-down' : 'main-chevron-right'}
textProps={{
rightIconProps: {
size: 10,
style: {
flexGrow: 1,
justifyContent: 'end',
display: 'flex'
}
}
}}
isDeselected={isPipelineSelected}
isHighlighted={isPipelineSelected}
/>
</Render>
)}
{OPEN_SOURCE_PIPELINES && (
<Render when={isPipelineSelected}>
<Container className={css.repoLinks}>
<Layout.Vertical spacing="small">
<NavMenuItem
isSubLink
isSelected={isPipelineSelected}
label={getString('pageTitle.executions')}
to={routes.toCODEExecutions({
space: selectedSpace?.path as string,
pipeline: params?.pipeline || ''
})}
/>
</Layout.Vertical>
</Container>
</Render>
)}
{OPEN_SOURCE_SECRETS && (
<Render when={selectedSpace}>
{/* icon is placeholder */}

View File

@ -1,60 +1,80 @@
import { Container, PageHeader, PageBody } from '@harnessio/uicore'
import { Container, PageBody } from '@harnessio/uicore'
import React, { useState } from 'react'
import cx from 'classnames'
import { useParams } from 'react-router-dom'
import { useGet } from 'restful-react'
import SplitPane from 'react-split-pane'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import type { CODEProps } from 'RouteDefinitions'
import { routes, type CODEProps } from 'RouteDefinitions'
import type { TypesExecution } from 'services/code'
import ExecutionStageList from 'components/ExecutionStageList/ExecutionStageList'
import Console from 'components/Console/Console'
import { getErrorMessage, voidFn } from 'utils/Utils'
import { useStrings } from 'framework/strings'
import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
import { RepositoryPageHeader } from 'components/RepositoryPageHeader/RepositoryPageHeader'
import noExecutionImage from '../RepositoriesListing/no-repo.svg'
import css from './Execution.module.scss'
const Execution = () => {
const space = useGetSpaceParam()
const { pipeline, execution: executionNum } = useParams<CODEProps>()
const { getString } = useStrings()
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
const {
data: execution,
error,
loading,
refetch
error: executionError,
loading: executionLoading
} = useGet<TypesExecution>({
path: `/api/v1/pipelines/${space}/${pipeline}/+/executions/${executionNum}`
path: `/api/v1/repos/${repoMetadata?.path}/+/pipelines/${pipeline}/executions/${executionNum}`,
lazy: !repoMetadata
})
const [selectedStage, setSelectedStage] = useState<number | null>(1)
return (
<Container className={css.main}>
<PageHeader title={execution?.title}>
<div>hello</div>
</PageHeader>
<RepositoryPageHeader
repoMetadata={repoMetadata}
title={execution?.title as string}
dataTooltipId="repositoryExecution"
extraBreadcrumbLinks={
repoMetadata && [
{
label: getString('pageTitle.pipelines'),
url: routes.toCODEPipelines({ repoPath: repoMetadata.path as string })
},
{
label: getString('pageTitle.executions'),
url: routes.toCODEExecutions({ repoPath: repoMetadata.path as string, pipeline: pipeline as string })
}
]
}
/>
<PageBody
className={cx({ [css.withError]: !!error })}
error={error ? getErrorMessage(error) : null}
error={error ? getErrorMessage(error || executionError) : null}
retryOnError={voidFn(refetch)}
noData={{
when: () => !execution && !loading,
when: () => !execution && !loading && !executionLoading,
image: noExecutionImage,
message: getString('executions.noData')
// button: NewExecutionButton
}}>
<LoadingSpinner visible={loading} />
<SplitPane split="vertical" size={300} minSize={200} maxSize={400}>
<ExecutionStageList
stages={execution?.stages || []}
setSelectedStage={setSelectedStage}
selectedStage={selectedStage}
/>
{selectedStage && <Console stage={execution?.stages?.[selectedStage - 1]} />}
</SplitPane>
<LoadingSpinner visible={loading || executionLoading} withBorder={!!execution && executionLoading} />
{execution && (
<SplitPane split="vertical" size={300} minSize={200} maxSize={400}>
<ExecutionStageList
stages={execution?.stages || []}
setSelectedStage={setSelectedStage}
selectedStage={selectedStage}
/>
{selectedStage && (
<Console stage={execution?.stages?.[selectedStage - 1]} repoPath={repoMetadata?.path as string} />
)}
</SplitPane>
)}
</PageBody>
</Container>
)

View File

@ -7,7 +7,6 @@ import {
FlexExpander,
Layout,
PageBody,
PageHeader,
TableV2 as Table,
Text,
Utils
@ -26,16 +25,16 @@ import { NoResultCard } from 'components/NoResultCard/NoResultCard'
import { LIST_FETCHING_LIMIT, PageBrowserProps, getErrorMessage, timeDistance, voidFn } from 'utils/Utils'
import type { CODEProps } from 'RouteDefinitions'
import type { TypesExecution } from 'services/code'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import { useQueryParams } from 'hooks/useQueryParams'
import { usePageIndex } from 'hooks/usePageIndex'
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
import { RepositoryPageHeader } from 'components/RepositoryPageHeader/RepositoryPageHeader'
import noExecutionImage from '../RepositoriesListing/no-repo.svg'
import css from './ExecutionList.module.scss'
const ExecutionList = () => {
const { routes } = useAppContext()
const space = useGetSpaceParam()
const { pipeline } = useParams<CODEProps>()
const history = useHistory()
const { getString } = useStrings()
@ -43,15 +42,17 @@ const ExecutionList = () => {
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
const [page, setPage] = usePageIndex(pageInit)
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
const {
data: executions,
error,
loading,
refetch,
error: executionsError,
loading: executionsLoading,
response
} = useGet<TypesExecution[]>({
path: `/api/v1/pipelines/${space}/${pipeline}/+/executions`,
queryParams: { page, limit: LIST_FETCHING_LIMIT }
path: `/api/v1/repos/${repoMetadata?.path}/+/pipelines/${pipeline}/executions`,
queryParams: { page, limit: LIST_FETCHING_LIMIT },
lazy: !repoMetadata
})
const NewExecutionButton = (
@ -123,10 +124,23 @@ const ExecutionList = () => {
return (
<Container className={css.main}>
<PageHeader title={getString('pageTitle.executions')} />
{/* <PageHeader title={getString('pageTitle.executions')} /> */}
<RepositoryPageHeader
repoMetadata={repoMetadata}
title={getString('pageTitle.executions')}
dataTooltipId="repositoryExecutions"
extraBreadcrumbLinks={
repoMetadata && [
{
label: getString('pageTitle.pipelines'),
url: routes.toCODEPipelines({ repoPath: repoMetadata.path as string })
}
]
}
/>
<PageBody
className={cx({ [css.withError]: !!error })}
error={error ? getErrorMessage(error) : null}
error={error ? getErrorMessage(error || executionsError) : null}
retryOnError={voidFn(refetch)}
noData={{
when: () => executions?.length === 0,
@ -134,7 +148,7 @@ const ExecutionList = () => {
message: getString('executions.noData'),
button: NewExecutionButton
}}>
<LoadingSpinner visible={loading} />
<LoadingSpinner visible={loading || executionsLoading} withBorder={!!executions && executionsLoading} />
<Container padding="xlarge">
<Layout.Horizontal spacing="large" className={css.layout}>
@ -151,7 +165,7 @@ const ExecutionList = () => {
onRowClick={executionInfo =>
history.push(
routes.toCODEExecution({
space,
repoPath: repoMetadata?.path as string,
pipeline: pipeline as string,
execution: String(executionInfo.number)
})

View File

@ -6,7 +6,6 @@ import {
FlexExpander,
Layout,
PageBody,
PageHeader,
TableV2 as Table,
Text
} from '@harnessio/uicore'
@ -21,18 +20,18 @@ import { LoadingSpinner } from 'components/LoadingSpinner/LoadingSpinner'
import { SearchInputWithSpinner } from 'components/SearchInputWithSpinner/SearchInputWithSpinner'
import { NoResultCard } from 'components/NoResultCard/NoResultCard'
import { LIST_FETCHING_LIMIT, PageBrowserProps, formatDate, getErrorMessage, voidFn } from 'utils/Utils'
import { useGetSpaceParam } from 'hooks/useGetSpaceParam'
import type { TypesPipeline } from 'services/code'
import { useQueryParams } from 'hooks/useQueryParams'
import { usePageIndex } from 'hooks/usePageIndex'
import { ResourceListingPagination } from 'components/ResourceListingPagination/ResourceListingPagination'
import { useAppContext } from 'AppContext'
import { useGetRepositoryMetadata } from 'hooks/useGetRepositoryMetadata'
import { RepositoryPageHeader } from 'components/RepositoryPageHeader/RepositoryPageHeader'
import noPipelineImage from '../RepositoriesListing/no-repo.svg'
import css from './PipelineList.module.scss'
const PipelineList = () => {
const { routes } = useAppContext()
const space = useGetSpaceParam()
const history = useHistory()
const { getString } = useStrings()
const [searchTerm, setSearchTerm] = useState<string | undefined>()
@ -40,15 +39,17 @@ const PipelineList = () => {
const pageInit = pageBrowser.page ? parseInt(pageBrowser.page) : 1
const [page, setPage] = usePageIndex(pageInit)
const { repoMetadata, error, loading, refetch } = useGetRepositoryMetadata()
const {
data: pipelines,
error,
loading,
refetch,
error: pipelinesError,
loading: pipelinesLoading,
response
} = useGet<TypesPipeline[]>({
path: `/api/v1/spaces/${space}/+/pipelines`,
queryParams: { page, limit: LIST_FETCHING_LIMIT, query: searchTerm }
path: `/api/v1/repos/${repoMetadata?.path}/+/pipelines`,
queryParams: { page, limit: LIST_FETCHING_LIMIT, query: searchTerm },
lazy: !repoMetadata
})
const NewPipelineButton = (
@ -102,10 +103,14 @@ const PipelineList = () => {
return (
<Container className={css.main}>
<PageHeader title={getString('pageTitle.pipelines')} />
<RepositoryPageHeader
repoMetadata={repoMetadata}
title={getString('pageTitle.pipelines')}
dataTooltipId="repositoryPipelines"
/>
<PageBody
className={cx({ [css.withError]: !!error })}
error={error ? getErrorMessage(error) : null}
error={error ? getErrorMessage(error || pipelinesError) : null}
retryOnError={voidFn(refetch)}
noData={{
when: () => pipelines?.length === 0 && searchTerm === undefined,
@ -113,7 +118,10 @@ const PipelineList = () => {
message: getString('pipelines.noData'),
button: NewPipelineButton
}}>
<LoadingSpinner visible={loading && !searchTerm} />
<LoadingSpinner
visible={(loading || pipelinesLoading) && !searchTerm}
withBorder={!!pipelines && pipelinesLoading}
/>
<Container padding="xlarge">
<Layout.Horizontal spacing="large" className={css.layout}>
@ -129,7 +137,12 @@ const PipelineList = () => {
columns={columns}
data={pipelines || []}
onRowClick={pipelineInfo =>
history.push(routes.toCODEExecutions({ space, pipeline: pipelineInfo.uid as string }))
history.push(
routes.toCODEExecutions({
repoPath: repoMetadata?.path as string,
pipeline: pipelineInfo.uid as string
})
)
}
getRowClassName={row => cx(css.row, !row.original.description && css.noDesc)}
/>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff