You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +02:00 
			
		
		
		
	helmExecute: add remoteHelmChartPath CPE value (#3965)
* Add remoteHelmChartPath CPE value * Fix tests * Add empty line at the end of yaml file * Fix yaml file
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							74cc828221
						
					
				
				
					commit
					b31549cf7f
				
			| @@ -11,7 +11,7 @@ import ( | ||||
| 	"github.com/SAP/jenkins-library/pkg/versioning" | ||||
| ) | ||||
|  | ||||
| func helmExecute(config helmExecuteOptions, telemetryData *telemetry.CustomData) { | ||||
| func helmExecute(config helmExecuteOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *helmExecuteCommonPipelineEnvironment) { | ||||
| 	helmConfig := kubernetes.HelmExecuteOptions{ | ||||
| 		AdditionalParameters:      config.AdditionalParameters, | ||||
| 		ChartPath:                 config.ChartPath, | ||||
| @@ -64,12 +64,12 @@ func helmExecute(config helmExecuteOptions, telemetryData *telemetry.CustomData) | ||||
| 	helmExecutor := kubernetes.NewHelmExecutor(helmConfig, utils, GeneralConfig.Verbose, log.Writer()) | ||||
|  | ||||
| 	// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end | ||||
| 	if err := runHelmExecute(config, helmExecutor); err != nil { | ||||
| 	if err := runHelmExecute(config, helmExecutor, commonPipelineEnvironment); err != nil { | ||||
| 		log.Entry().WithError(err).Fatalf("step execution failed: %v", err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runHelmExecute(config helmExecuteOptions, helmExecutor kubernetes.HelmExecutor) error { | ||||
| func runHelmExecute(config helmExecuteOptions, helmExecutor kubernetes.HelmExecutor, commonPipelineEnvironment *helmExecuteCommonPipelineEnvironment) error { | ||||
| 	switch config.HelmCommand { | ||||
| 	case "upgrade": | ||||
| 		if err := helmExecutor.RunHelmUpgrade(); err != nil { | ||||
| @@ -96,11 +96,13 @@ func runHelmExecute(config helmExecuteOptions, helmExecutor kubernetes.HelmExecu | ||||
| 			return fmt.Errorf("failed to execute helm dependency: %v", err) | ||||
| 		} | ||||
| 	case "publish": | ||||
| 		if err := helmExecutor.RunHelmPublish(); err != nil { | ||||
| 		targetURL, err := helmExecutor.RunHelmPublish() | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm publish: %v", err) | ||||
| 		} | ||||
| 		commonPipelineEnvironment.custom.remoteHelmChartPath = targetURL | ||||
| 	default: | ||||
| 		if err := runHelmExecuteDefault(config, helmExecutor); err != nil { | ||||
| 		if err := runHelmExecuteDefault(config, helmExecutor, commonPipelineEnvironment); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| @@ -108,7 +110,7 @@ func runHelmExecute(config helmExecuteOptions, helmExecutor kubernetes.HelmExecu | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func runHelmExecuteDefault(config helmExecuteOptions, helmExecutor kubernetes.HelmExecutor) error { | ||||
| func runHelmExecuteDefault(config helmExecuteOptions, helmExecutor kubernetes.HelmExecutor, commonPipelineEnvironment *helmExecuteCommonPipelineEnvironment) error { | ||||
| 	if err := helmExecutor.RunHelmLint(); err != nil { | ||||
| 		return fmt.Errorf("failed to execute helm lint: %v", err) | ||||
| 	} | ||||
| @@ -120,9 +122,11 @@ func runHelmExecuteDefault(config helmExecuteOptions, helmExecutor kubernetes.He | ||||
| 	} | ||||
|  | ||||
| 	if config.Publish { | ||||
| 		if err := helmExecutor.RunHelmPublish(); err != nil { | ||||
| 		targetURL, err := helmExecutor.RunHelmPublish() | ||||
| 		if err != nil { | ||||
| 			return fmt.Errorf("failed to execute helm publish: %v", err) | ||||
| 		} | ||||
| 		commonPipelineEnvironment.custom.remoteHelmChartPath = targetURL | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
| @@ -5,10 +5,12 @@ package cmd | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/config" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperenv" | ||||
| 	"github.com/SAP/jenkins-library/pkg/splunk" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/SAP/jenkins-library/pkg/validation" | ||||
| @@ -41,6 +43,34 @@ type helmExecuteOptions struct { | ||||
| 	Version                   string   `json:"version,omitempty"` | ||||
| } | ||||
|  | ||||
| type helmExecuteCommonPipelineEnvironment struct { | ||||
| 	custom struct { | ||||
| 		remoteHelmChartPath string | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *helmExecuteCommonPipelineEnvironment) persist(path, resourceName string) { | ||||
| 	content := []struct { | ||||
| 		category string | ||||
| 		name     string | ||||
| 		value    interface{} | ||||
| 	}{ | ||||
| 		{category: "custom", name: "remoteHelmChartPath", value: p.custom.remoteHelmChartPath}, | ||||
| 	} | ||||
|  | ||||
| 	errCount := 0 | ||||
| 	for _, param := range content { | ||||
| 		err := piperenv.SetResourceParameter(path, resourceName, filepath.Join(param.category, param.name), param.value) | ||||
| 		if err != nil { | ||||
| 			log.Entry().WithError(err).Error("Error persisting piper environment.") | ||||
| 			errCount++ | ||||
| 		} | ||||
| 	} | ||||
| 	if errCount > 0 { | ||||
| 		log.Entry().Error("failed to persist Piper environment") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // HelmExecuteCommand Executes helm3 functionality as the package manager for Kubernetes. | ||||
| func HelmExecuteCommand() *cobra.Command { | ||||
| 	const STEP_NAME = "helmExecute" | ||||
| @@ -48,6 +78,7 @@ func HelmExecuteCommand() *cobra.Command { | ||||
| 	metadata := helmExecuteMetadata() | ||||
| 	var stepConfig helmExecuteOptions | ||||
| 	var startTime time.Time | ||||
| 	var commonPipelineEnvironment helmExecuteCommonPipelineEnvironment | ||||
| 	var logCollector *log.CollectorHook | ||||
| 	var splunkClient *splunk.Splunk | ||||
| 	telemetryClient := &telemetry.Telemetry{} | ||||
| @@ -128,6 +159,7 @@ Note: piper supports only helm3 version, since helm2 is deprecated.`, | ||||
| 			stepTelemetryData := telemetry.CustomData{} | ||||
| 			stepTelemetryData.ErrorCode = "1" | ||||
| 			handler := func() { | ||||
| 				commonPipelineEnvironment.persist(GeneralConfig.EnvRootPath, "commonPipelineEnvironment") | ||||
| 				config.RemoveVaultSecretFiles() | ||||
| 				stepTelemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds()) | ||||
| 				stepTelemetryData.ErrorCategory = log.GetErrorCategory().String() | ||||
| @@ -148,7 +180,7 @@ Note: piper supports only helm3 version, since helm2 is deprecated.`, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Index, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.SendLogs) | ||||
| 			} | ||||
| 			helmExecute(stepConfig, &stepTelemetryData) | ||||
| 			helmExecute(stepConfig, &stepTelemetryData, &commonPipelineEnvironment) | ||||
| 			stepTelemetryData.ErrorCode = "0" | ||||
| 			log.Entry().Info("SUCCESS") | ||||
| 		}, | ||||
| @@ -498,6 +530,17 @@ func helmExecuteMetadata() config.StepData { | ||||
| 			Containers: []config.Container{ | ||||
| 				{Image: "dtzar/helm-kubectl:3.8.0", WorkingDir: "/config", Options: []config.Option{{Name: "-u", Value: "0"}}}, | ||||
| 			}, | ||||
| 			Outputs: config.StepOutputs{ | ||||
| 				Resources: []config.StepResources{ | ||||
| 					{ | ||||
| 						Name: "commonPipelineEnvironment", | ||||
| 						Type: "piperEnvironment", | ||||
| 						Parameters: []map[string]interface{}{ | ||||
| 							{"name": "custom/remoteHelmChartPath"}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return theMetaData | ||||
|   | ||||
| @@ -34,6 +34,7 @@ func newHelmMockUtilsBundle() helmMockUtilsBundle { | ||||
| func TestRunHelmUpgrade(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| @@ -59,7 +60,7 @@ func TestRunHelmUpgrade(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmUpgrade").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -71,6 +72,7 @@ func TestRunHelmUpgrade(t *testing.T) { | ||||
| func TestRunHelmLint(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		expectedConfig []string | ||||
| @@ -97,7 +99,7 @@ func TestRunHelmLint(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmLint").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -109,6 +111,7 @@ func TestRunHelmLint(t *testing.T) { | ||||
| func TestRunHelmInstall(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		expectedConfig []string | ||||
| @@ -135,7 +138,7 @@ func TestRunHelmInstall(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmInstall").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -147,6 +150,7 @@ func TestRunHelmInstall(t *testing.T) { | ||||
| func TestRunHelmTest(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| @@ -172,7 +176,7 @@ func TestRunHelmTest(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmTest").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -184,6 +188,7 @@ func TestRunHelmTest(t *testing.T) { | ||||
| func TestRunHelmUninstall(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| @@ -209,7 +214,7 @@ func TestRunHelmUninstall(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmUninstall").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -221,6 +226,7 @@ func TestRunHelmUninstall(t *testing.T) { | ||||
| func TestRunHelmDependency(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodError    error | ||||
| @@ -246,7 +252,7 @@ func TestRunHelmDependency(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmDependency").Return(testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -258,8 +264,10 @@ func TestRunHelmDependency(t *testing.T) { | ||||
| func TestRunHelmPush(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config         helmExecuteOptions | ||||
| 		methodString   string | ||||
| 		methodError    error | ||||
| 		expectedErrStr string | ||||
| 	}{ | ||||
| @@ -267,7 +275,8 @@ func TestRunHelmPush(t *testing.T) { | ||||
| 			config: helmExecuteOptions{ | ||||
| 				HelmCommand: "publish", | ||||
| 			}, | ||||
| 			methodError: nil, | ||||
| 			methodString: "https://my.target.repository", | ||||
| 			methodError:  nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			config: helmExecuteOptions{ | ||||
| @@ -281,9 +290,9 @@ func TestRunHelmPush(t *testing.T) { | ||||
| 	for i, testCase := range testTable { | ||||
| 		t.Run(fmt.Sprint("case ", i), func(t *testing.T) { | ||||
| 			helmExecute := &mocks.HelmExecutor{} | ||||
| 			helmExecute.On("RunHelmPublish").Return(testCase.methodError) | ||||
| 			helmExecute.On("RunHelmPublish").Return(testCase.methodString, testCase.methodError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
| @@ -295,6 +304,7 @@ func TestRunHelmPush(t *testing.T) { | ||||
| func TestRunHelmDefaultCommand(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	cpe := helmExecuteCommonPipelineEnvironment{} | ||||
| 	testTable := []struct { | ||||
| 		config             helmExecuteOptions | ||||
| 		methodLintError    error | ||||
| @@ -340,7 +350,7 @@ func TestRunHelmDefaultCommand(t *testing.T) { | ||||
| 			helmExecute.On("RunHelmDependency").Return(testCase.methodPackageError) | ||||
| 			helmExecute.On("RunHelmPublish").Return(testCase.methodPublishError) | ||||
|  | ||||
| 			err := runHelmExecute(testCase.config, helmExecute) | ||||
| 			err := runHelmExecute(testCase.config, helmExecute, &cpe) | ||||
| 			if err != nil { | ||||
| 				assert.Equal(t, testCase.expectedErrStr, err.Error()) | ||||
| 			} | ||||
|   | ||||
| @@ -17,7 +17,7 @@ type HelmExecutor interface { | ||||
| 	RunHelmInstall() error | ||||
| 	RunHelmUninstall() error | ||||
| 	RunHelmTest() error | ||||
| 	RunHelmPublish() error | ||||
| 	RunHelmPublish() (string, error) | ||||
| 	RunHelmDependency() error | ||||
| } | ||||
|  | ||||
| @@ -380,19 +380,19 @@ func (h *HelmExecute) RunHelmDependency() error { | ||||
| } | ||||
|  | ||||
| //RunHelmPublish is used to upload a chart to a registry | ||||
| func (h *HelmExecute) RunHelmPublish() error { | ||||
| func (h *HelmExecute) RunHelmPublish() (string, error) { | ||||
| 	err := h.runHelmInit() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 		return "", fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	err = h.runHelmPackage() | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 		return "", fmt.Errorf("failed to execute deployments: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if len(h.config.TargetRepositoryURL) == 0 { | ||||
| 		return fmt.Errorf("there's no target repository for helm chart publishing configured") | ||||
| 		return "", fmt.Errorf("there's no target repository for helm chart publishing configured") | ||||
| 	} | ||||
|  | ||||
| 	repoClientOptions := piperhttp.ClientOptions{ | ||||
| @@ -419,14 +419,14 @@ func (h *HelmExecute) RunHelmPublish() error { | ||||
|  | ||||
| 	response, err := h.utils.UploadRequest(http.MethodPut, targetURL, binary, "", nil, nil, "binary") | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("couldn't upload artifact: %w", err) | ||||
| 		return "", fmt.Errorf("couldn't upload artifact: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	if !(response.StatusCode == 200 || response.StatusCode == 201) { | ||||
| 		return fmt.Errorf("couldn't upload artifact, received status code %d", response.StatusCode) | ||||
| 		return "", fmt.Errorf("couldn't upload artifact, received status code %d", response.StatusCode) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| 	return targetURL, nil | ||||
| } | ||||
|  | ||||
| func (h *HelmExecute) runHelmCommand(helmParams []string) error { | ||||
|   | ||||
| @@ -506,9 +506,10 @@ func TestRunHelmPublish(t *testing.T) { | ||||
| 			stdout:  log.Writer(), | ||||
| 		} | ||||
|  | ||||
| 		err := helmExecute.RunHelmPublish() | ||||
| 		targetURL, err := helmExecute.RunHelmPublish() | ||||
| 		if assert.NoError(t, err) { | ||||
| 			assert.Equal(t, 1, len(utils.FileUploads)) | ||||
| 			assert.Equal(t, "https://my.target.repository.local/test_helm_chart/test_helm_chart-1.2.3.tgz", targetURL) | ||||
| 			assert.Equal(t, "https://my.target.repository.local/test_helm_chart/test_helm_chart-1.2.3.tgz", utils.FileUploads["test_helm_chart-1.2.3.tgz"]) | ||||
| 		} | ||||
| 	}) | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| // Code generated by mockery v2.10.0. DO NOT EDIT. | ||||
| // Code generated by mockery v2.14.0. DO NOT EDIT. | ||||
|  | ||||
| package mocks | ||||
|  | ||||
| @@ -52,17 +52,24 @@ func (_m *HelmExecutor) RunHelmLint() error { | ||||
| } | ||||
|  | ||||
| // RunHelmPublish provides a mock function with given fields: | ||||
| func (_m *HelmExecutor) RunHelmPublish() error { | ||||
| func (_m *HelmExecutor) RunHelmPublish() (string, error) { | ||||
| 	ret := _m.Called() | ||||
|  | ||||
| 	var r0 error | ||||
| 	if rf, ok := ret.Get(0).(func() error); ok { | ||||
| 	var r0 string | ||||
| 	if rf, ok := ret.Get(0).(func() string); ok { | ||||
| 		r0 = rf() | ||||
| 	} else { | ||||
| 		r0 = ret.Error(0) | ||||
| 		r0 = ret.Get(0).(string) | ||||
| 	} | ||||
|  | ||||
| 	return r0 | ||||
| 	var r1 error | ||||
| 	if rf, ok := ret.Get(1).(func() error); ok { | ||||
| 		r1 = rf() | ||||
| 	} else { | ||||
| 		r1 = ret.Error(1) | ||||
| 	} | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
|  | ||||
| // RunHelmTest provides a mock function with given fields: | ||||
| @@ -106,3 +113,18 @@ func (_m *HelmExecutor) RunHelmUpgrade() error { | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
|  | ||||
| type mockConstructorTestingTNewHelmExecutor interface { | ||||
| 	mock.TestingT | ||||
| 	Cleanup(func()) | ||||
| } | ||||
|  | ||||
| // NewHelmExecutor creates a new instance of HelmExecutor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | ||||
| func NewHelmExecutor(t mockConstructorTestingTNewHelmExecutor) *HelmExecutor { | ||||
| 	mock := &HelmExecutor{} | ||||
| 	mock.Mock.Test(t) | ||||
|  | ||||
| 	t.Cleanup(func() { mock.AssertExpectations(t) }) | ||||
|  | ||||
| 	return mock | ||||
| } | ||||
|   | ||||
| @@ -297,3 +297,9 @@ spec: | ||||
|       options: | ||||
|         - name: -u | ||||
|           value: "0" | ||||
|   outputs: | ||||
|     resources: | ||||
|       - name: commonPipelineEnvironment | ||||
|         type: piperEnvironment | ||||
|         params: | ||||
|           - name: custom/remoteHelmChartPath | ||||
|   | ||||
		Reference in New Issue
	
	Block a user