Skip to content

Commit 8b60620

Browse files
authored
Merge pull request #1324 from devtron-labs/feat-ci-job-plugin
feat: Added support for job type CI
2 parents b0d9054 + eca521f commit 8b60620

33 files changed

+292
-97
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ POSTHOG_ENABLED=
1313
POSTHOG_TOKEN=
1414
RECOMMEND_SECURITY_SCANNING=false
1515
FORCE_SECURITY_SCANNING=false
16+
ENABLE_CI_JOB=false
1617
HIDE_DISCORD=false
1718
DEVTRON_APP_DETAILS_POLLING_INTERVAL=30000
1819
HELM_APP_DETAILS_POLLING_INTERVAL=30000

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "0.0.28",
7+
"@devtron-labs/devtron-fe-common-lib": "0.0.29",
88
"@sentry/browser": "^7.3.1",
99
"@sentry/integrations": "^7.3.1",
1010
"@sentry/tracing": "^7.3.1",

src/components/ApplicationGroup/Details/EnvCIDetails/EnvCIDetails.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { getTriggerHistory } from '../../../app/service'
1717
import { asyncWrap, mapByKey, useAsync, useInterval } from '../../../common'
1818
import { getCIConfigList } from '../../AppGroup.service'
1919
import { AppGroupDetailDefaultType } from '../../AppGroup.types'
20+
import { CIPipelineBuildType } from '../../../ciPipeline/types'
2021

2122
export default function EnvCIDetails({ filteredAppIds }: AppGroupDetailDefaultType) {
2223
const { envId, pipelineId, buildId } = useParams<{
@@ -181,7 +182,7 @@ export default function EnvCIDetails({ filteredAppIds }: AppGroupDetailDefaultTy
181182
replace(generatePath(path, { buildId: triggerHistory.entries().next().value[0], envId, pipelineId }))
182183
}
183184
const pipelineOptions: CICDSidebarFilterOptionType[] = (pipelineList || []).map((item) => {
184-
return { value: `${item.id}`, label: item.appName, pipelineId: item.id }
185+
return { value: `${item.id}`, label: item.appName, pipelineId: item.id, pipelineType: item.pipelineType }
185186
})
186187
const pipelinesMap = mapByKey(pipelineList, 'id')
187188
const pipeline = pipelinesMap.get(+pipelineId)
@@ -205,6 +206,7 @@ export default function EnvCIDetails({ filteredAppIds }: AppGroupDetailDefaultTy
205206
tagsEditable={tagsEditable}
206207
hideImageTaggingHardDelete={hideImageTaggingHardDelete}
207208
fetchIdData={fetchBuildIdData}
209+
isJobCI={pipeline.pipelineType === CIPipelineBuildType.CI_JOB}
208210
/>
209211
</Route>
210212
)

src/components/ApplicationGroup/Details/TriggerView/EnvTriggerView.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ import { getModuleInfo } from '../../../v2/devtronStackManager/DevtronStackManag
9191
import GitCommitInfoGeneric from '../../../common/GitCommitInfoGeneric'
9292
import { getDefaultConfig } from '../../../notifications/notifications.service'
9393
import BulkSourceChange from './BulkSourceChange'
94+
import { CIPipelineBuildType } from '../../../ciPipeline/types'
9495

9596
const ApprovalMaterialModal = importComponentFromFELibrary('ApprovalMaterialModal')
9697
const getDeployManifestDownload = importComponentFromFELibrary('getDeployManifestDownload', null, 'function')
@@ -991,10 +992,12 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
991992
setCDLoading(false)
992993
return
993994
}
995+
994996
const payload = {
995997
pipelineId: +selectedCINode.id,
996998
ciPipelineMaterials: ciPipelineMaterials,
997999
invalidateCache: invalidateCache,
1000+
pipelineType: node.isJobCI ? CIPipelineBuildType.CI_JOB : CIPipelineBuildType.CI_BUILD
9981001
}
9991002

10001003
triggerCINode(payload)
@@ -1596,10 +1599,12 @@ export default function EnvTriggerView({ filteredAppIds, isVirtualEnv }: AppGrou
15961599
ciPipelineMaterials.push(historyItem)
15971600
})
15981601
}
1602+
15991603
const payload = {
16001604
pipelineId: +node.id,
16011605
ciPipelineMaterials: ciPipelineMaterials,
16021606
invalidateCache: appIgnoreCache[+node.id],
1607+
pipelineType: node.isJobCI ? CIPipelineBuildType.CI_JOB : CIPipelineBuildType.CI_BUILD
16031608
}
16041609
_CITriggerPromiseList.push(triggerCINode(payload))
16051610
})

src/components/CIPipelineN/CIPipeline.tsx

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
} from '../ciPipeline/ciPipeline.service'
4242
import { toast } from 'react-toastify'
4343
import { ValidationRules } from '../ciPipeline/validationRules'
44-
import { CIBuildType, CIPipelineDataType, CIPipelineType } from '../ciPipeline/types'
44+
import { CIBuildType, CIPipelineBuildType, CIPipelineDataType, CIPipelineType } from '../ciPipeline/types'
4545
import { ReactComponent as Close } from '../../assets/icons/ic-cross.svg'
4646
import Tippy from '@tippyjs/react'
4747
import { PreBuild } from './PreBuild'
@@ -70,6 +70,7 @@ export default function CIPipeline({
7070
close,
7171
deleteWorkflow,
7272
isJobView,
73+
isJobCI,
7374
}: CIPipelineType) {
7475
let { appId, workflowId, ciPipelineId } = useParams<{ appId: string; workflowId: string; ciPipelineId: string }>()
7576
if (ciPipelineId === '0') {
@@ -85,9 +86,10 @@ export default function CIPipeline({
8586
const { path } = useRouteMatch()
8687
const [pageState, setPageState] = useState(ViewType.LOADING)
8788
const text = ciPipelineId ? 'Update Pipeline' : 'Create Pipeline'
88-
const title = `${ciPipelineId ? 'Edit ' : 'Create '}${isJobView ? 'job' : 'build'} pipeline`
89+
const isJobCard = isJobCI || isJobView // constant for common elements of both Job and CI_JOB
90+
const title = `${ciPipelineId ? 'Edit ' : 'Create '}${isJobCard ? 'job' : 'build'} pipeline`
8991
const [isAdvanced, setIsAdvanced] = useState<boolean>(
90-
isJobView || (activeStageName !== BuildStageVariable.PreBuild && !!ciPipelineId),
92+
isJobCard || (activeStageName !== BuildStageVariable.PreBuild && !!ciPipelineId),
9193
)
9294
const [showFormError, setShowFormError] = useState<boolean>(false)
9395
const [loadingState, setLoadingState] = useState<LoadingState>({
@@ -154,6 +156,7 @@ export default function CIPipeline({
154156
linkedCount: 0,
155157
scanEnabled: false,
156158
environmentId: 0,
159+
pipelineType: "",
157160
})
158161
const validationRules = new ValidationRules()
159162
const [isDockerConfigOverridden, setDockerConfigOverridden] = useState(false)
@@ -184,7 +187,16 @@ export default function CIPipeline({
184187
) {
185188
localStorage.removeItem('takeMeThereClicked')
186189
}
187-
}, [location.pathname])
190+
// redirect to ci-job based on pipeline type
191+
if (
192+
location.pathname.includes(`/${URLS.APP_CI_CONFIG}/`) &&
193+
ciPipelineId &&
194+
ciPipeline.pipelineType === CIPipelineBuildType.CI_JOB
195+
) {
196+
const editCIPipelineURL: string = location.pathname.replace(`/${URLS.APP_CI_CONFIG}/`, `/${URLS.APP_JOB_CI_CONFIG}/`)
197+
window.location.href = editCIPipelineURL
198+
}
199+
}, [location.pathname, ciPipeline.pipelineType])
188200

189201
const getEnvironments = (envId) => {
190202
envId = envId || 0
@@ -284,7 +296,7 @@ export default function CIPipeline({
284296
showError(error)
285297
})
286298
} else {
287-
getInitData(appId, true, !isJobView)
299+
getInitData(appId, true, !isJobCard)
288300
.then((ciResponse) => {
289301
setFormData(ciResponse.result.form)
290302
setPageState(ViewType.FORM)
@@ -352,7 +364,7 @@ export default function CIPipeline({
352364
}
353365

354366
const getMandatoryPluginData = (_formData: PipelineFormType, pluginList: PluginDetailType[]): void => {
355-
if (!isJobView && processPluginData && prepareFormData && pluginList.length) {
367+
if (!isJobCard && processPluginData && prepareFormData && pluginList.length) {
356368
let branchName = ''
357369
if (_formData?.materials?.length) {
358370
for (const material of _formData.materials) {
@@ -524,7 +536,15 @@ export default function CIPipeline({
524536
} else {
525537
_ciPipeline.environmentId = undefined
526538
}
527-
539+
if (!isJobView) {
540+
let ciPipelineType: CIPipelineBuildType = CIPipelineBuildType.CI_BUILD
541+
if (ciPipeline.isExternal) {
542+
ciPipelineType = CIPipelineBuildType.CI_EXTERNAL
543+
} else if (isJobCI) {
544+
ciPipelineType = CIPipelineBuildType.CI_JOB
545+
}
546+
_ciPipeline.pipelineType = ciPipeline.id ? ciPipeline.pipelineType : ciPipelineType
547+
}
528548
saveCIPipeline(
529549
{
530550
...formData,
@@ -642,7 +662,7 @@ export default function CIPipeline({
642662
validateStage(activeStageName, formData)
643663
}}
644664
>
645-
{isJobView ? JobPipelineTabText[stageName] : BuildTabText[stageName]}
665+
{isJobCard ? JobPipelineTabText[stageName] : BuildTabText[stageName]}
646666
{(showAlert || showWarning) && (
647667
<WarningTriangle
648668
className={`icon-dim-16 mr-5 ml-5 mt-3 ${
@@ -713,7 +733,7 @@ export default function CIPipeline({
713733

714734
{isAdvanced && (
715735
<ul className="ml-20 tab-list w-90">
716-
{isJobView ? (
736+
{isJobCard ? (
717737
<>
718738
{getNavLink(`build`, BuildStageVariable.Build)}
719739
{getNavLink(`pre-build`, BuildStageVariable.PreBuild)}
@@ -734,6 +754,7 @@ export default function CIPipeline({
734754
<div className="sidebar-container">
735755
<Sidebar
736756
isJobView={isJobView}
757+
isJobCI={isJobCI}
737758
mandatoryPluginData={mandatoryPluginData}
738759
pluginList={[...presetPlugins, ...sharedPlugins]}
739760
setInputVariablesListFromPrevStep={setInputVariablesListFromPrevStep}
@@ -750,7 +771,7 @@ export default function CIPipeline({
750771
<PreBuild
751772
presetPlugins={presetPlugins}
752773
sharedPlugins={sharedPlugins}
753-
isJobView={isJobView}
774+
isJobView={isJobCard}
754775
mandatoryPluginsMap={mandatoryPluginsMap}
755776
/>
756777
</Route>
@@ -772,7 +793,7 @@ export default function CIPipeline({
772793
ciPipeline={ciPipeline}
773794
isSecurityModuleInstalled={isSecurityModuleInstalled}
774795
setDockerConfigOverridden={setDockerConfigOverridden}
775-
isJobView={isJobView}
796+
isJobView={isJobCard}
776797
getPluginData={getPluginData}
777798
/>
778799
</Route>

src/components/CIPipelineN/Sidebar.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const MandatoryPluginWarning = importComponentFromFELibrary('MandatoryPluginWarn
2727

2828
export function Sidebar({
2929
isJobView,
30+
isJobCI,
3031
mandatoryPluginData,
3132
pluginList,
3233
mandatoryPluginsMap,
@@ -62,9 +63,9 @@ export function Sidebar({
6263
_formData.triggerType = appCreationType
6364
setFormData(_formData)
6465
}
65-
66+
const isJobCard = isJobCI || isJobView // common constant for both job and CI_JOB
6667
useEffect(() => {
67-
if (isJobView) {
68+
if (isJobCard) {
6869
setHelpData({ helpText: 'Docs: Configure job', docLink: DOCUMENTATION.JOB_WORKFLOW_EDITOR })
6970
} else if (activeStageName === BuildStageVariable.Build) {
7071
setHelpData({ helpText: 'Docs: Configure build stage', docLink: DOCUMENTATION.BUILD_STAGE })
@@ -327,7 +328,7 @@ export function Sidebar({
327328
<div className="sidebar-action-container">
328329
{configurationType === ConfigurationType.GUI && (
329330
<>
330-
{!isCdPipeline && !isJobView && MandatoryPluginWarning && showMandatoryWarning() && (
331+
{!isCdPipeline && !isJobCard && MandatoryPluginWarning && showMandatoryWarning() && (
331332
<MandatoryPluginWarning
332333
stage={activeStageName}
333334
mandatoryPluginData={mandatoryPluginData}
@@ -345,7 +346,7 @@ export function Sidebar({
345346
withWarning={showMandatoryWarning()}
346347
mandatoryPluginsMap={mandatoryPluginsMap}
347348
setInputVariablesListFromPrevStep={setInputVariablesListFromPrevStep}
348-
isJobView={isJobView}
349+
isJobView={isJobCard}
349350
/>
350351
</div>
351352
{isCdPipeline && (!isVirtualEnvironment || formData.generatedHelmPushAction === GeneratedHelmPush.PUSH) && triggerPipelineMode()}
@@ -356,7 +357,7 @@ export function Sidebar({
356357
) : (
357358
<div className="sidebar-action-container pr-20">
358359
<div className="dc__uppercase fw-6 fs-12 cn-6 mb-12">
359-
Trigger {isJobView ? 'JOB' : 'BUILD'} PIPELINE
360+
Trigger {isJobCard ? 'JOB' : 'BUILD'} PIPELINE
360361
</div>
361362
<div>
362363
<RadioGroup

src/components/app/details/cIDetails/CIDetails.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import LogsRenderer from '../cicdHistory/LogsRenderer'
2222
import { EMPTY_STATE_STATUS } from '../../../../config/constantMessaging'
2323
import { ReactComponent as NoVulnerability } from '../../../../assets/img/ic-vulnerability-not-found.svg'
2424
import { ScannedByToolModal } from '../../../common/security/ScannedByToolModal'
25+
import { CIPipelineBuildType } from '../../../ciPipeline/types'
2526

2627
const terminalStatus = new Set(['succeeded', 'failed', 'error', 'cancelled', 'nottriggered', 'notbuilt'])
2728
let statusSet = new Set(['starting', 'running', 'pending'])
@@ -156,7 +157,7 @@ export default function CIDetails({ isJobView }: { isJobView?: boolean }) {
156157
replace(generatePath(path, { appId, pipelineId: pipelines[0].id }))
157158
}
158159
const pipelineOptions: CICDSidebarFilterOptionType[] = (pipelines || []).map((item) => {
159-
return { value: `${item.id}`, label: item.name, pipelineId: item.id }
160+
return { value: `${item.id}`, label: item.name, pipelineId: item.id, pipelineType: item.pipelineType}
160161
})
161162
const pipelinesMap = mapByKey(pipelines, 'id')
162163
const pipeline = pipelinesMap.get(+pipelineId)
@@ -221,6 +222,7 @@ export default function CIDetails({ isJobView }: { isJobView?: boolean }) {
221222
appReleaseTags={appReleaseTags}
222223
hideImageTaggingHardDelete={hideImageTaggingHardDelete}
223224
fetchIdData={fetchBuildIdData}
225+
isJobCI={pipeline.pipelineType === CIPipelineBuildType.CI_JOB}
224226
/>
225227
</Route>
226228
) : pipeline.parentCiPipeline || pipeline.pipelineType === 'LINKED' ? (
@@ -259,12 +261,14 @@ export const Details = ({
259261
isSecurityModuleInstalled,
260262
isBlobStorageConfigured,
261263
isJobView,
264+
isJobCI,
262265
appIdFromParent,
263266
tagsEditable,
264267
appReleaseTags,
265268
hideImageTaggingHardDelete,
266269
fetchIdData,
267270
}: BuildDetails) => {
271+
const isJobCard: boolean = isJobView || isJobCI
268272
const { pipelineId, appId, buildId } = useParams<{ appId: string; buildId: string; pipelineId: string }>()
269273
const triggerDetails = triggerHistory.get(+buildId)
270274
const [triggerDetailsLoading, triggerDetailsResult, triggerDetailsError, reloadTriggerDetails] = useAsync(
@@ -403,7 +407,7 @@ export const Details = ({
403407
Artifacts
404408
</NavLink>
405409
</li>
406-
{!isJobView && isSecurityModuleInstalled && (
410+
{!isJobCard && isSecurityModuleInstalled && (
407411
<li className="tab-list__tab">
408412
<NavLink
409413
replace
@@ -425,6 +429,7 @@ export const Details = ({
425429
triggerDetails={triggerDetails}
426430
isBlobStorageConfigured={isBlobStorageConfigured}
427431
isJobView={isJobView}
432+
isJobCI={isJobCI}
428433
appIdFromParent={appIdFromParent}
429434
appReleaseTags={appReleaseTags}
430435
tagsEditable={tagsEditable}
@@ -438,12 +443,14 @@ const HistoryLogs = ({
438443
triggerDetails,
439444
isBlobStorageConfigured,
440445
isJobView,
446+
isJobCI,
441447
appIdFromParent,
442448
appReleaseTags,
443449
tagsEditable,
444450
hideImageTaggingHardDelete,
445451
}: HistoryLogsType) => {
446452
let { path } = useRouteMatch()
453+
const isJobCard: boolean = isJobCI || isJobView
447454
const { pipelineId, buildId } = useParams<{ buildId: string; pipelineId: string }>()
448455
const [ref, scrollToTop, scrollToBottom] = useScrollable({
449456
autoBottomScroll: triggerDetails.status.toLowerCase() !== 'succeeded',
@@ -479,6 +486,7 @@ const HistoryLogs = ({
479486
getArtifactPromise={_getArtifactPromise}
480487
isArtifactUploaded={triggerDetails.isArtifactUploaded}
481488
isJobView={isJobView}
489+
isJobCI={isJobCI}
482490
imageComment={triggerDetails.imageComment}
483491
imageReleaseTags={triggerDetails.imageReleaseTags}
484492
ciPipelineId={triggerDetails.ciPipelineId}
@@ -489,7 +497,7 @@ const HistoryLogs = ({
489497
type={HistoryComponentType.CI}
490498
/>
491499
</Route>
492-
{!isJobView && (
500+
{!isJobCard && (
493501
<Route path={`${path}/security`}>
494502
<SecurityTab
495503
ciPipelineId={triggerDetails.ciPipelineId}

src/components/app/details/cIDetails/types.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface BuildDetails {
1414
isSecurityModuleInstalled: boolean
1515
isBlobStorageConfigured: boolean
1616
isJobView?: boolean
17+
isJobCI?: boolean
1718
appIdFromParent?: string
1819
appReleaseTags?: []
1920
tagsEditable: boolean
@@ -25,6 +26,7 @@ export interface HistoryLogsType {
2526
triggerDetails: History
2627
isBlobStorageConfigured?: boolean
2728
isJobView?: boolean
29+
isJobCI?: boolean
2830
appIdFromParent?: string
2931
appReleaseTags?: []
3032
tagsEditable: boolean

0 commit comments

Comments
 (0)