diff --git a/src/components/CIPipelineN/AdvancedConfigOptions.tsx b/src/components/CIPipelineN/AdvancedConfigOptions.tsx
index ee2160bd67..50e11d2c05 100644
--- a/src/components/CIPipelineN/AdvancedConfigOptions.tsx
+++ b/src/components/CIPipelineN/AdvancedConfigOptions.tsx
@@ -15,18 +15,32 @@
*/
import { useContext, useEffect, useState } from 'react'
-import { CIBuildConfigType, CIBuildType, KeyValueTableProps, noop, OptionType } from '@devtron-labs/devtron-fe-common-lib'
+
+import {
+ BuildSecretType,
+ CIBuildConfigType,
+ CIBuildType,
+ KeyValueTableProps,
+ noop,
+ OptionType,
+} from '@devtron-labs/devtron-fe-common-lib'
+
+import { importComponentFromFELibrary } from '@Components/common'
+
import CIConfig from '../ciConfig/CIConfig'
-import DockerArgs from './DockerArgs'
-import CustomImageTags from './CustomImageTags'
+import { getTargetPlatformMap } from '../ciConfig/CIConfig.utils'
import TargetPlatformSelector from '../ciConfig/TargetPlatformSelector'
import { AdvancedConfigOptionsProps, CIConfigParentState } from '../ciConfig/types'
import { DockerConfigOverrideKeys } from '../ciPipeline/types'
-import { getTargetPlatformMap } from '../ciConfig/CIConfig.utils'
import { pipelineContext } from '../workflowEditor/workflowEditor'
-import '../ciConfig/CIConfig.scss'
+import CustomImageTags from './CustomImageTags'
+import DockerArgs from './DockerArgs'
import { ComponentStates } from './types'
+import '../ciConfig/CIConfig.scss'
+
+const BuildSecrets = importComponentFromFELibrary('BuildSecrets', null, 'function')
+
export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateView }: AdvancedConfigOptionsProps) {
const { formData, setFormData, loadingState, setLoadingState, formDataErrorObj, setFormDataErrorObj } =
useContext(pipelineContext)
@@ -53,6 +67,25 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
const hasParentLoaded = parentState?.loadingState === ComponentStates.loaded
const showNonBuildpackOptions = hasParentLoaded && (isGlobalAndNotBuildpack || !isCurrentCITypeBuildpack)
+ // Shows the current override draft if present; else the pipeline's saved override (fetched via
+ // app/ci-pipeline/:appId/:ciPipelineId) if it already has one; else the global config as the starting
+ // point to override - same precedence populateCurrentPlatformsData uses for targetPlatform below
+ const currentSecrets: BuildSecretType[] =
+ (allowOverride
+ ? formData.dockerConfigOverride?.ciBuildConfig?.dockerBuildConfig?.secrets ??
+ (parentState.selectedCIPipeline?.isDockerConfigOverridden
+ ? parentState.selectedCIPipeline?.dockerConfigOverride?.ciBuildConfig?.dockerBuildConfig?.secrets
+ : parentState.ciConfig?.ciBuildConfig?.dockerBuildConfig?.secrets)
+ : parentState.ciConfig?.ciBuildConfig?.dockerBuildConfig?.secrets) ?? []
+
+ const currentSSH: BuildSecretType[] =
+ (allowOverride
+ ? formData.dockerConfigOverride?.ciBuildConfig?.dockerBuildConfig?.ssh ??
+ (parentState.selectedCIPipeline?.isDockerConfigOverridden
+ ? parentState.selectedCIPipeline?.dockerConfigOverride?.ciBuildConfig?.dockerBuildConfig?.ssh
+ : parentState.ciConfig?.ciBuildConfig?.dockerBuildConfig?.ssh)
+ : parentState.ciConfig?.ciBuildConfig?.dockerBuildConfig?.ssh) ?? []
+
useEffect(() => {
if (parentState.ciConfig) {
populateCurrentPlatformsData()
@@ -99,7 +132,7 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
const updateDockerConfigOverride = (
key: string,
- value: CIBuildConfigType | OptionType[] | boolean | string,
+ value: CIBuildConfigType | OptionType[] | boolean | string | BuildSecretType[],
): void => {
setFormData((prevFormData) => {
const _form = structuredClone(prevFormData)
@@ -143,6 +176,22 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
_form.dockerConfigOverride.ciBuildConfig.dockerBuildConfig.buildContext = value as string
} else if (key === DockerConfigOverrideKeys.projectPath) {
_form.dockerConfigOverride.ciBuildConfig.buildPackConfig.projectPath = value as string
+ } else if (key === DockerConfigOverrideKeys.secrets) {
+ _form.dockerConfigOverride.ciBuildConfig = {
+ ..._form.dockerConfigOverride.ciBuildConfig,
+ dockerBuildConfig: {
+ ..._form.dockerConfigOverride.ciBuildConfig.dockerBuildConfig,
+ secrets: value as BuildSecretType[],
+ },
+ }
+ } else if (key === DockerConfigOverrideKeys.ssh) {
+ _form.dockerConfigOverride.ciBuildConfig = {
+ ..._form.dockerConfigOverride.ciBuildConfig,
+ dockerBuildConfig: {
+ ..._form.dockerConfigOverride.ciBuildConfig.dockerBuildConfig,
+ ssh: value as BuildSecretType[],
+ },
+ }
} else {
_form.dockerConfigOverride.ciBuildConfig = value as CIBuildConfigType
}
@@ -156,6 +205,28 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
})
}
+ const handleBuildSecretsUpdate = (secrets: BuildSecretType[]): void => {
+ updateDockerConfigOverride(DockerConfigOverrideKeys.secrets, secrets)
+ }
+
+ const handleSSHUpdate = (ssh: BuildSecretType[]): void => {
+ updateDockerConfigOverride(DockerConfigOverrideKeys.ssh, ssh)
+ }
+
+ const handleBuildSecretsError = (hasError: boolean): void => {
+ setFormDataErrorObj((prev) => ({
+ ...prev,
+ secretsError: { isValid: !hasError, message: 'Invalid build secrets' },
+ }))
+ }
+
+ const handleSSHError = (hasError: boolean): void => {
+ setFormDataErrorObj((prev) => ({
+ ...prev,
+ sshError: { isValid: !hasError, message: 'Invalid SSH keys' },
+ }))
+ }
+
const toggleAdvancedOptions = (): void => {
setCollapsedSection(!collapsedSection)
}
@@ -232,6 +303,18 @@ export default function AdvancedConfigOptions({ ciPipeline, appId, isTemplateVie
handleDockerArgsError={handleDockerArgsError}
/>
)}
+
+ {showNonBuildpackOptions && BuildSecrets && (
+
+ )}
)
diff --git a/src/components/CIPipelineN/CIPipeline.tsx b/src/components/CIPipelineN/CIPipeline.tsx
index 97e3a72a38..65c2b66095 100644
--- a/src/components/CIPipelineN/CIPipeline.tsx
+++ b/src/components/CIPipelineN/CIPipeline.tsx
@@ -176,6 +176,14 @@ export default function CIPipeline({
message: '',
isValid: true,
},
+ secretsError: {
+ message: '',
+ isValid: true,
+ },
+ sshError: {
+ message: '',
+ isValid: true,
+ },
})
const [ciPipeline, setCIPipeline] = useState({
@@ -419,7 +427,11 @@ export default function CIPipeline({
}
_formDataErrorObj[BuildStageVariable.Build].isValid =
- _formDataErrorObj.name.isValid && valid && _formDataErrorObj.dockerArgsError.isValid
+ _formDataErrorObj.name.isValid &&
+ valid &&
+ _formDataErrorObj.dockerArgsError.isValid &&
+ _formDataErrorObj.secretsError.isValid &&
+ _formDataErrorObj.sshError.isValid
} else {
const stepsLength = _formData[stageName].steps.length
let isStageValid = true
@@ -639,7 +651,13 @@ export default function CIPipeline({
) {
setApiInProgress(false)
const branchNameNotPresent = formData.materials.some((_mat) => !_mat.value)
- if (formData.name === '' || branchNameNotPresent || !formDataErrorObj.dockerArgsError.isValid) {
+ if (
+ formData.name === '' ||
+ branchNameNotPresent ||
+ !formDataErrorObj.dockerArgsError.isValid ||
+ !formDataErrorObj.secretsError.isValid ||
+ !formDataErrorObj.sshError.isValid
+ ) {
ToastManager.showToast({
variant: ToastVariantType.error,
description: 'Please ensure all fields are valid',
diff --git a/src/components/ciConfig/CIAdvancedConfig.tsx b/src/components/ciConfig/CIAdvancedConfig.tsx
index 1e7648fe1f..dcd79468e3 100644
--- a/src/components/ciConfig/CIAdvancedConfig.tsx
+++ b/src/components/ciConfig/CIAdvancedConfig.tsx
@@ -15,12 +15,17 @@
*/
import { useState } from 'react'
-import PluginIcon from '../../assets/icons/ic-plugin.svg?react'
+
+import { BuildSecretType, KeyValueTableProps } from '@devtron-labs/devtron-fe-common-lib'
+
import Dropdown from '../../assets/icons/ic-chevron-down.svg?react'
-import { CIAdvancedConfigProps } from './types'
-import TargetPlatformSelector from './TargetPlatformSelector'
+import PluginIcon from '../../assets/icons/ic-plugin.svg?react'
import DockerArgs from '../CIPipelineN/DockerArgs'
-import { KeyValueTableProps } from '@devtron-labs/devtron-fe-common-lib'
+import { importComponentFromFELibrary } from '../common'
+import TargetPlatformSelector from './TargetPlatformSelector'
+import { CIAdvancedConfigProps } from './types'
+
+const BuildSecrets = importComponentFromFELibrary('BuildSecrets', null, 'function')
export default function CIAdvancedConfig({
configOverrideView,
@@ -28,6 +33,13 @@ export default function CIAdvancedConfig({
args,
setArgs,
setArgsError,
+ appId,
+ secrets,
+ setSecrets,
+ ssh,
+ setSsh,
+ setSecretsError,
+ setSshError,
isBuildpackType,
selectedTargetPlatforms,
setSelectedTargetPlatforms,
@@ -50,21 +62,27 @@ export default function CIAdvancedConfig({
setArgsError((prev) => ({ ...prev, [isBuildpackType ? 'buildEnvArgs' : 'args']: errorState }))
}
- const renderTargetPlatform = () => {
- return (
-
-
-
- )
+ const handleBuildSecretsChange = (updatedSecrets: BuildSecretType[]): void => {
+ setSecrets(updatedSecrets)
}
+ const handleSSHChange = (updatedSSH: BuildSecretType[]): void => {
+ setSsh(updatedSSH)
+ }
+
+ const renderTargetPlatform = () => (
+
+
+
+ )
+
if (configOverrideView && !isBuildpackType) {
return null
}
@@ -110,6 +128,17 @@ export default function CIAdvancedConfig({
handleDockerArgsError={handleDockerArgsError}
readOnly={updateNotAllowed}
/>
+ {BuildSecrets && (
+
+ )}
>
)}
>
diff --git a/src/components/ciConfig/CIConfigForm.tsx b/src/components/ciConfig/CIConfigForm.tsx
index 129a7b2a70..c609760c43 100644
--- a/src/components/ciConfig/CIConfigForm.tsx
+++ b/src/components/ciConfig/CIConfigForm.tsx
@@ -16,6 +16,7 @@
import { useEffect, useState } from 'react'
import {
+ BuildSecretType,
CIBuildConfigType,
CIBuildType,
showError,
@@ -134,6 +135,10 @@ export default function CIConfigForm({
args: false,
buildEnvArgs: false,
})
+ const [secrets, setSecrets] = useState([])
+ const [ssh, setSsh] = useState([])
+ const [secretsError, setSecretsError] = useState(false)
+ const [sshError, setSshError] = useState(false)
const [loadingDataState, setLoadingDataState] = useState({
loading: false,
failed: false,
@@ -182,6 +187,10 @@ export default function CIConfigForm({
// Buildpack - build env arguments
setBuildEnvArgs(processBuildArgs(currentCIBuildConfig.buildPackConfig?.args))
+
+ // Build secrets and SSH keys
+ setSecrets(ciConfig?.ciBuildConfig?.dockerBuildConfig?.secrets ?? [])
+ setSsh(ciConfig?.ciBuildConfig?.dockerBuildConfig?.ssh ?? [])
}
const updateParentCIBuildTypeState = () => {
@@ -203,6 +212,14 @@ export default function CIConfigForm({
return
}
+ if (!isBuildpackType && (secretsError || sshError)) {
+ ToastManager.showToast({
+ variant: ToastVariantType.error,
+ description: 'Please ensure build secrets and SSH keys are valid',
+ })
+ return
+ }
+
let targetPlatforms = ''
const targetPlatformsSet = new Set()
for (let index = 0; index < selectedTargetPlatforms.length; index++) {
@@ -247,6 +264,8 @@ export default function CIConfigForm({
dockerfileRepository: repository.value,
targetPlatform: targetPlatforms,
buildContext: buildContext.value,
+ secrets: secrets.filter((secret) => secret.type && secret.secretName && secret.id && secret.key),
+ ssh: ssh.filter((sshKey) => sshKey.type && sshKey.secretName && sshKey.id && sshKey.key),
}
}
@@ -405,6 +424,13 @@ export default function CIConfigForm({
buildEnvArgs={buildEnvArgs}
setBuildEnvArgs={setBuildEnvArgs}
setArgsError={setArgsError}
+ appId={appId}
+ secrets={secrets}
+ setSecrets={setSecrets}
+ ssh={ssh}
+ setSsh={setSsh}
+ setSecretsError={setSecretsError}
+ setSshError={setSshError}
handleOnChangeConfig={handleOnChangeConfig}
selectedTargetPlatforms={selectedTargetPlatforms}
setSelectedTargetPlatforms={setSelectedTargetPlatforms}
diff --git a/src/components/ciConfig/CIDockerFileConfig.tsx b/src/components/ciConfig/CIDockerFileConfig.tsx
index faa203f1f9..8a0d25f97f 100644
--- a/src/components/ciConfig/CIDockerFileConfig.tsx
+++ b/src/components/ciConfig/CIDockerFileConfig.tsx
@@ -56,6 +56,13 @@ export default function CIDockerFileConfig({
buildEnvArgs,
setBuildEnvArgs,
setArgsError,
+ appId,
+ secrets,
+ setSecrets,
+ ssh,
+ setSsh,
+ setSecretsError,
+ setSshError,
handleOnChangeConfig,
selectedTargetPlatforms,
setSelectedTargetPlatforms,
@@ -436,6 +443,13 @@ export default function CIDockerFileConfig({
args={isBuildpackType ? buildEnvArgs : args}
setArgs={isBuildpackType ? setBuildEnvArgs : setArgs}
setArgsError={setArgsError}
+ appId={appId}
+ secrets={secrets}
+ setSecrets={setSecrets}
+ ssh={ssh}
+ setSsh={setSsh}
+ setSecretsError={setSecretsError}
+ setSshError={setSshError}
isBuildpackType={isBuildpackType}
selectedTargetPlatforms={selectedTargetPlatforms}
setSelectedTargetPlatforms={setSelectedTargetPlatforms}
diff --git a/src/components/ciConfig/types.tsx b/src/components/ciConfig/types.tsx
index 177f56af60..d44baebc69 100644
--- a/src/components/ciConfig/types.tsx
+++ b/src/components/ciConfig/types.tsx
@@ -18,6 +18,7 @@ import React from 'react'
import {
AppConfigProps,
+ BuildSecretType,
CIBuildConfigType,
CIBuildType,
CiPipeline,
@@ -285,6 +286,13 @@ export interface CIDockerFileConfigProps {
buildEnvArgs: CIBuildArgType[]
setBuildEnvArgs: React.Dispatch>
setArgsError: React.Dispatch>
+ appId: string
+ secrets: BuildSecretType[]
+ setSecrets: React.Dispatch>
+ ssh: BuildSecretType[]
+ setSsh: React.Dispatch>
+ setSecretsError: React.Dispatch>
+ setSshError: React.Dispatch>
handleOnChangeConfig: (e) => void
selectedTargetPlatforms: any
setSelectedTargetPlatforms: any
@@ -336,7 +344,20 @@ export interface CIBuildpackBuildOptionsProps {
readOnly?: boolean
}
-export interface CIAdvancedConfigProps extends Pick {
+export interface CIAdvancedConfigProps
+ extends Pick<
+ CIDockerFileConfigProps,
+ | 'args'
+ | 'setArgs'
+ | 'setArgsError'
+ | 'appId'
+ | 'secrets'
+ | 'setSecrets'
+ | 'ssh'
+ | 'setSsh'
+ | 'setSecretsError'
+ | 'setSshError'
+ > {
configOverrideView: boolean
allowOverride: boolean
isBuildpackType: boolean
diff --git a/src/components/ciPipeline/types.ts b/src/components/ciPipeline/types.ts
index 57ae140d23..d8ee7df78b 100644
--- a/src/components/ciPipeline/types.ts
+++ b/src/components/ciPipeline/types.ts
@@ -269,6 +269,8 @@ export const DockerConfigOverrideKeys = {
dockerfileRelativePath: 'dockerfileRelativePath',
targetPlatform: 'targetPlatform',
buildContext: 'buildContext',
+ secrets: 'secrets',
+ ssh: 'ssh',
}
export interface CIPipelineType extends Required> {
diff --git a/src/components/workflowEditor/types.ts b/src/components/workflowEditor/types.ts
index ca532f4a98..348edffb87 100644
--- a/src/components/workflowEditor/types.ts
+++ b/src/components/workflowEditor/types.ts
@@ -222,6 +222,8 @@ export interface PipelineFormDataErrorType {
containerRegistryError?: { isValid: boolean; message?: string }
repositoryError?: { isValid: boolean; message?: string }
dockerArgsError?: { isValid: boolean; message?: string }
+ secretsError?: { isValid: boolean; message?: string }
+ sshError?: { isValid: boolean; message?: string }
preBuildStage: {
steps: TaskErrorObj[]
isValid: boolean