github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/documentation/generator/parameters.go (about) 1 package generator 2 3 import ( 4 "fmt" 5 "path" 6 "sort" 7 "strings" 8 9 "github.com/SAP/jenkins-library/pkg/config" 10 ) 11 12 // Replaces the Parameters placeholder with the content from the yaml 13 func createParametersSection(stepData *config.StepData) string { 14 15 var parameters = "## Parameters\n\n" 16 17 // sort parameters alphabetically with mandatory parameters first 18 sortStepParameters(stepData, true) 19 parameters += "### Overview - Step\n\n" 20 parameters += createParameterOverview(stepData, false) 21 22 parameters += "### Overview - Execution Environment\n\n" 23 parameters += "!!! note \"Orchestrator-specific only\"\n\n These parameters are relevant for orchestrator usage and not considered when using the command line option.\n\n" 24 parameters += createParameterOverview(stepData, true) 25 26 // sort parameters alphabetically 27 sortStepParameters(stepData, false) 28 parameters += "### Details\n\n" 29 parameters += createParameterDetails(stepData) 30 31 return parameters 32 } 33 34 func parameterMandatoryInformation(param config.StepParameters, furtherInfo string) (mandatory bool, mandatoryString string, mandatoryInfo string) { 35 mandatory = param.Mandatory 36 mandatoryInfo = furtherInfo 37 38 mandatoryIf := param.MandatoryIf 39 if len(mandatoryIf) > 0 { 40 mandatory = true 41 if len(mandatoryInfo) > 0 { 42 mandatoryInfo += "<br />" 43 } 44 furtherInfoConditions := []string{"mandatory in case of:"} 45 for _, mandatoryCondition := range mandatoryIf { 46 furtherInfoConditions = append(furtherInfoConditions, fmt.Sprintf("- [`%v`](#%v)=`%v`", mandatoryCondition.Name, strings.ToLower(mandatoryCondition.Name), mandatoryCondition.Value)) 47 } 48 49 mandatoryInfo += strings.Join(furtherInfoConditions, "<br />") 50 } 51 52 mandatoryString = "**yes**" 53 if len(mandatoryInfo) > 0 { 54 mandatoryString = "**(yes)**" 55 } 56 return 57 } 58 59 func createParameterOverview(stepData *config.StepData, executionEnvironment bool) string { 60 var table = "| Name | Mandatory | Additional information |\n" 61 table += "| ---- | --------- | ---------------------- |\n" 62 63 for _, param := range stepData.Spec.Inputs.Parameters { 64 furtherInfo, err := parameterFurtherInfo(param.Name, stepData, executionEnvironment) 65 if err == nil { 66 67 var mandatory bool 68 var mandatoryString string 69 mandatory, mandatoryString, furtherInfo = parameterMandatoryInformation(param, furtherInfo) 70 table += fmt.Sprintf("| [%v](#%v) | %v | %v |\n", param.Name, strings.ToLower(param.Name), ifThenElse(mandatory, mandatoryString, "no"), furtherInfo) 71 } 72 } 73 74 table += "\n" 75 76 return table 77 } 78 79 func parameterFurtherInfo(paramName string, stepData *config.StepData, executionEnvironment bool) (string, error) { 80 81 // handle general parameters 82 // ToDo: add special handling once we have more than one general parameter to consider 83 if paramName == "verbose" { 84 return checkParameterInfo("activates debug output", true, executionEnvironment) 85 } 86 87 if paramName == "script" { 88 return checkParameterInfo("[![Jenkins only](https://img.shields.io/badge/-Jenkins%20only-yellowgreen)](#) reference to Jenkins main pipeline script", true, executionEnvironment) 89 } 90 91 // handle non-step parameters (e.g. Jenkins-specific parameters as well as execution environment parameters) 92 jenkinsParams := []string{"containerCommand", "containerName", "containerShell", "dockerVolumeBind", "dockerWorkspace", "sidecarReadyCommand", "sidecarWorkspace", "stashContent"} 93 if !contains(stepParameterNames, paramName) { 94 for _, secret := range stepData.Spec.Inputs.Secrets { 95 if paramName == secret.Name && secret.Type == "jenkins" { 96 return checkParameterInfo("[![Jenkins only](https://img.shields.io/badge/-Jenkins%20only-yellowgreen)](#) id of credentials ([using credentials](https://www.jenkins.io/doc/book/using/using-credentials/))", true, executionEnvironment) 97 } 98 } 99 if contains(jenkinsParams, paramName) { 100 return checkParameterInfo("[![Jenkins only](https://img.shields.io/badge/-Jenkins%20only-yellowgreen)](#)", false, executionEnvironment) 101 } 102 return checkParameterInfo("", false, executionEnvironment) 103 } 104 105 // handle step-parameters (incl. secrets) 106 for _, param := range stepData.Spec.Inputs.Parameters { 107 if paramName == param.Name { 108 furtherInfo := "" 109 if param.DeprecationMessage != "" { 110 furtherInfo += "![deprecated](https://img.shields.io/badge/-deprecated-red)" 111 } 112 if param.Secret { 113 secretInfo := "[![Secret](https://img.shields.io/badge/-Secret-yellowgreen)](#) pass via ENV or Jenkins credentials" 114 if param.GetReference("vaultSecret") != nil || param.GetReference("vaultSecretFile") != nil { 115 secretInfo = " [![Vault](https://img.shields.io/badge/-Vault-lightgrey)](#) [![Secret](https://img.shields.io/badge/-Secret-yellowgreen)](/) pass via ENV, Vault or Jenkins credentials" 116 117 } 118 for _, res := range param.ResourceRef { 119 if res.Type == "secret" { 120 secretInfo += fmt.Sprintf(" ([`%v`](#%v))", res.Name, strings.ToLower(res.Name)) 121 } 122 } 123 return checkParameterInfo(furtherInfo+secretInfo, true, executionEnvironment) 124 } 125 return checkParameterInfo(furtherInfo, true, executionEnvironment) 126 } 127 } 128 return checkParameterInfo("", true, executionEnvironment) 129 } 130 131 func checkParameterInfo(furtherInfo string, stepParam bool, executionEnvironment bool) (string, error) { 132 if stepParam && !executionEnvironment || !stepParam && executionEnvironment { 133 return furtherInfo, nil 134 } 135 136 if executionEnvironment { 137 return "", fmt.Errorf("step parameter not relevant as execution environment parameter") 138 } 139 return "", fmt.Errorf("execution environment parameter not relevant as step parameter") 140 } 141 142 func createParameterDetails(stepData *config.StepData) string { 143 144 details := "" 145 146 //jenkinsParameters := append(jenkinsParameters(stepData), "script") 147 148 for _, param := range stepData.Spec.Inputs.Parameters { 149 details += fmt.Sprintf("#### %v\n\n", param.Name) 150 151 if !contains(stepParameterNames, param.Name) { 152 details += "**Jenkins-specific:** Used for proper environment setup.\n\n" 153 } 154 155 if len(param.LongDescription) > 0 { 156 details += param.LongDescription + "\n\n" 157 } else { 158 details += param.Description + "\n\n" 159 } 160 161 details += "[back to overview](#parameters)\n\n" 162 163 details += "| Scope | Details |\n" 164 details += "| ---- | --------- |\n" 165 166 if param.DeprecationMessage != "" { 167 details += fmt.Sprintf("| Deprecated | %v |\n", param.DeprecationMessage) 168 } 169 details += fmt.Sprintf("| Aliases | %v |\n", aliasList(param.Aliases)) 170 details += fmt.Sprintf("| Type | `%v` |\n", param.Type) 171 mandatory, mandatoryString, furtherInfo := parameterMandatoryInformation(param, "") 172 if mandatory && len(furtherInfo) > 0 { 173 mandatoryString = furtherInfo 174 } 175 details += fmt.Sprintf("| Mandatory | %v |\n", ifThenElse(mandatory, mandatoryString, "no")) 176 details += fmt.Sprintf("| Default | %v |\n", formatDefault(param, stepParameterNames)) 177 if param.PossibleValues != nil { 178 details += fmt.Sprintf("| Possible values | %v |\n", possibleValueList(param.PossibleValues)) 179 } 180 details += fmt.Sprintf("| Secret | %v |\n", ifThenElse(param.Secret, "**yes**", "no")) 181 details += fmt.Sprintf("| Configuration scope | %v |\n", scopeDetails(param.Scope)) 182 details += fmt.Sprintf("| Resource references | %v |\n", resourceReferenceDetails(param.ResourceRef)) 183 184 details += "\n\n" 185 } 186 187 for _, secret := range stepData.Spec.Inputs.Secrets { 188 details += fmt.Sprintf("#### %v\n\n", secret.Name) 189 190 if !contains(stepParameterNames, secret.Name) { 191 details += "**Jenkins-specific:** Used for proper environment setup. See *[using credentials](https://www.jenkins.io/doc/book/using/using-credentials/)* for details.\n\n" 192 } 193 194 details += secret.Description + "\n\n" 195 196 details += "[back to overview](#parameters)\n\n" 197 198 details += "| Scope | Details |\n" 199 details += "| ---- | --------- |\n" 200 details += fmt.Sprintf("| Aliases | %v |\n", aliasList(secret.Aliases)) 201 details += fmt.Sprintf("| Type | `%v` |\n", "string") 202 details += fmt.Sprintf("| Configuration scope | %v |\n", scopeDetails([]string{"PARAMETERS", "GENERAL", "STEPS", "STAGES"})) 203 204 details += "\n\n" 205 } 206 207 return details 208 } 209 210 func formatDefault(param config.StepParameters, stepParameterNames []string) string { 211 if param.Default == nil { 212 // Return environment variable for all step parameters (not for Jenkins-specific parameters) in case no default is available 213 if contains(stepParameterNames, param.Name) { 214 return fmt.Sprintf("`$PIPER_%v` (if set)", param.Name) 215 } 216 return "" 217 } 218 //first consider conditional defaults 219 switch v := param.Default.(type) { 220 case []conditionDefault: 221 defaults := []string{} 222 for _, condDef := range v { 223 //ToDo: add type-specific handling of default 224 if len(condDef.key) > 0 && len(condDef.value) > 0 { 225 defaults = append(defaults, fmt.Sprintf("%v=`%v`: `%v`", condDef.key, condDef.value, condDef.def)) 226 } else { 227 // containers with no condition will only hold def 228 defaults = append(defaults, fmt.Sprintf("`%v`", condDef.def)) 229 } 230 } 231 return strings.Join(defaults, "<br />") 232 case []interface{}: 233 // handle for example stashes which possibly contain a mixture of fix and conditional values 234 defaults := []string{} 235 for _, def := range v { 236 if condDef, ok := def.(conditionDefault); ok { 237 defaults = append(defaults, fmt.Sprintf("%v=`%v`: `%v`", condDef.key, condDef.value, condDef.def)) 238 } else { 239 defaults = append(defaults, fmt.Sprintf("- `%v`", def)) 240 } 241 } 242 return strings.Join(defaults, "<br />") 243 case map[string]string: 244 defaults := []string{} 245 for key, def := range v { 246 defaults = append(defaults, fmt.Sprintf("`%v`: `%v`", key, def)) 247 } 248 return strings.Join(defaults, "<br />") 249 case string: 250 if len(v) == 0 { 251 return "`''`" 252 } 253 return fmt.Sprintf("`%v`", v) 254 default: 255 return fmt.Sprintf("`%v`", param.Default) 256 } 257 } 258 259 func aliasList(aliases []config.Alias) string { 260 switch len(aliases) { 261 case 0: 262 return "-" 263 case 1: 264 alias := fmt.Sprintf("`%v`", aliases[0].Name) 265 if aliases[0].Deprecated { 266 alias += " (**deprecated**)" 267 } 268 return alias 269 default: 270 aList := make([]string, len(aliases)) 271 for i, alias := range aliases { 272 aList[i] = fmt.Sprintf("- `%v`", alias.Name) 273 if alias.Deprecated { 274 aList[i] += " (**deprecated**)" 275 } 276 } 277 return strings.Join(aList, "<br />") 278 } 279 } 280 281 func possibleValueList(possibleValues []interface{}) string { 282 if len(possibleValues) == 0 { 283 return "" 284 } 285 286 pList := make([]string, len(possibleValues)) 287 for i, possibleValue := range possibleValues { 288 pList[i] = fmt.Sprintf("- `%v`", fmt.Sprint(possibleValue)) 289 } 290 return strings.Join(pList, "<br />") 291 } 292 293 func scopeDetails(scope []string) string { 294 scopeDetails := "<ul>" 295 scopeDetails += fmt.Sprintf("<li>%v parameter</li>", ifThenElse(contains(scope, "PARAMETERS"), "☒", "☐")) 296 scopeDetails += fmt.Sprintf("<li>%v general</li>", ifThenElse(contains(scope, "GENERAL"), "☒", "☐")) 297 scopeDetails += fmt.Sprintf("<li>%v steps</li>", ifThenElse(contains(scope, "STEPS"), "☒", "☐")) 298 scopeDetails += fmt.Sprintf("<li>%v stages</li>", ifThenElse(contains(scope, "STAGES"), "☒", "☐")) 299 scopeDetails += "</ul>" 300 return scopeDetails 301 } 302 303 func resourceReferenceDetails(resourceRef []config.ResourceReference) string { 304 305 if len(resourceRef) == 0 { 306 return "none" 307 } 308 309 resourceDetails := "" 310 for _, resource := range resourceRef { 311 if resource.Name == "commonPipelineEnvironment" { 312 resourceDetails += "_commonPipelineEnvironment_:<br />" 313 resourceDetails += fmt.Sprintf(" reference to: `%v`<br />", resource.Param) 314 continue 315 } 316 317 if resource.Type == "secret" { 318 resourceDetails += "Jenkins credential id:<br />" 319 for i, alias := range resource.Aliases { 320 if i == 0 { 321 resourceDetails += " aliases:<br />" 322 } 323 resourceDetails += fmt.Sprintf(" - `%v`%v<br />", alias.Name, ifThenElse(alias.Deprecated, " (**Deprecated**)", "")) 324 } 325 resourceDetails += fmt.Sprintf(" id: [`%v`](#%v)<br />", resource.Name, strings.ToLower(resource.Name)) 326 if resource.Param != "" { 327 resourceDetails += fmt.Sprintf(" reference to: `%v`<br />", resource.Param) 328 } 329 continue 330 } 331 332 resourceDetails = addVaultResourceDetails(resource, resourceDetails) 333 } 334 335 return resourceDetails 336 } 337 338 func addVaultResourceDetails(resource config.ResourceReference, resourceDetails string) string { 339 if resource.Type == "vaultSecret" || resource.Type == "vaultSecretFile" { 340 resourceDetails += "<br/>Vault paths: <br />" 341 resourceDetails += "<ul>" 342 for _, rootPath := range config.VaultRootPaths { 343 resourceDetails += fmt.Sprintf("<li>`%s`</li>", path.Join(rootPath, resource.Default)) 344 } 345 resourceDetails += "</ul>" 346 } 347 return resourceDetails 348 } 349 350 func sortStepParameters(stepData *config.StepData, considerMandatory bool) { 351 if stepData.Spec.Inputs.Parameters != nil { 352 parameters := stepData.Spec.Inputs.Parameters 353 354 if considerMandatory { 355 sort.SliceStable(parameters[:], func(i, j int) bool { 356 if (parameters[i].Mandatory || len(parameters[i].MandatoryIf) > 0) == (parameters[j].Mandatory || len(parameters[j].MandatoryIf) > 0) { 357 return strings.Compare(parameters[i].Name, parameters[j].Name) < 0 358 } else if parameters[i].Mandatory || len(parameters[i].MandatoryIf) > 0 { 359 return true 360 } 361 return false 362 }) 363 } else { 364 sort.SliceStable(parameters[:], func(i, j int) bool { 365 return strings.Compare(parameters[i].Name, parameters[j].Name) < 0 366 }) 367 } 368 } 369 }