github.com/jenkins-x/jx/v2@v2.1.155/pkg/config/project_config.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/jenkins-x/jx/v2/pkg/jenkinsfile" 8 "github.com/jenkins-x/jx/v2/pkg/tekton/syntax" 9 "github.com/pkg/errors" 10 "sigs.k8s.io/yaml" 11 12 "io/ioutil" 13 "path/filepath" 14 "reflect" 15 16 "github.com/jenkins-x/jx/v2/pkg/util" 17 corev1 "k8s.io/api/core/v1" 18 ) 19 20 const ( 21 // ProjectConfigFileName is the name of the project configuration file 22 ProjectConfigFileName = "jenkins-x.yml" 23 ) 24 25 // +exported 26 27 // ProjectConfig defines Jenkins X Pipelines usually stored inside the `jenkins-x.yml` file in projects 28 type ProjectConfig struct { 29 // List of global environment variables to add to each branch build and each step 30 Env []corev1.EnvVar `json:"env,omitempty"` 31 32 PreviewEnvironments *PreviewEnvironmentConfig `json:"previewEnvironments,omitempty"` 33 IssueTracker *IssueTrackerConfig `json:"issueTracker,omitempty"` 34 Chat *ChatConfig `json:"chat,omitempty"` 35 Wiki *WikiConfig `json:"wiki,omitempty"` 36 Addons []*AddonConfig `json:"addons,omitempty"` 37 BuildPack string `json:"buildPack,omitempty"` 38 BuildPackGitURL string `json:"buildPackGitURL,omitempty"` 39 BuildPackGitURef string `json:"buildPackGitRef,omitempty"` 40 PipelineConfig *jenkinsfile.PipelineConfig `json:"pipelineConfig,omitempty"` 41 NoReleasePrepare bool `json:"noReleasePrepare,omitempty"` 42 DockerRegistryHost string `json:"dockerRegistryHost,omitempty"` 43 DockerRegistryOwner string `json:"dockerRegistryOwner,omitempty"` 44 } 45 46 type PreviewEnvironmentConfig struct { 47 Disabled bool `json:"disabled,omitempty"` 48 MaximumInstances int `json:"maximumInstances,omitempty"` 49 } 50 51 type IssueTrackerConfig struct { 52 Kind string `json:"kind,omitempty"` 53 URL string `json:"url,omitempty"` 54 Project string `json:"project,omitempty"` 55 } 56 57 type WikiConfig struct { 58 Kind string `json:"kind,omitempty"` 59 URL string `json:"url,omitempty"` 60 Space string `json:"space,omitempty"` 61 } 62 63 type ChatConfig struct { 64 Kind string `json:"kind,omitempty"` 65 URL string `json:"url,omitempty"` 66 DeveloperChannel string `json:"developerChannel,omitempty"` 67 UserChannel string `json:"userChannel,omitempty"` 68 } 69 70 type AddonConfig struct { 71 Name string `json:"name,omitempty"` 72 Version string `json:"version,omitempty"` 73 } 74 75 // LoadProjectConfig loads the project configuration if there is a project configuration file 76 func LoadProjectConfig(projectDir string) (*ProjectConfig, string, error) { 77 fileName := ProjectConfigFileName 78 if projectDir != "" { 79 fileName = filepath.Join(projectDir, fileName) 80 } 81 config, err := LoadProjectConfigFile(fileName) 82 return config, fileName, err 83 } 84 85 // LoadProjectConfigFile loads a specific project YAML configuration file 86 func LoadProjectConfigFile(fileName string) (*ProjectConfig, error) { 87 config := ProjectConfig{} 88 exists, err := util.FileExists(fileName) 89 if err != nil || !exists { 90 return &config, err 91 } 92 data, err := ioutil.ReadFile(fileName) 93 if err != nil { 94 return &config, fmt.Errorf("Failed to load file %s due to %s", fileName, err) 95 } 96 validationErrors, err := util.ValidateYaml(&config, data) 97 if err != nil { 98 return &config, fmt.Errorf("failed to validate YAML file %s due to %s", fileName, err) 99 } 100 if len(validationErrors) > 0 { 101 return &config, fmt.Errorf("Validation failures in YAML file %s:\n%s", fileName, strings.Join(validationErrors, "\n")) 102 } 103 err = yaml.Unmarshal(data, &config) 104 if err != nil { 105 return &config, fmt.Errorf("Failed to unmarshal YAML file %s due to %s", fileName, err) 106 } 107 return &config, nil 108 } 109 110 // IsEmpty returns true if this configuration is empty 111 func (c *ProjectConfig) IsEmpty() bool { 112 empty := &ProjectConfig{} 113 return reflect.DeepEqual(empty, c) 114 } 115 116 // SaveConfig saves the configuration file to the given project directory 117 func (c *ProjectConfig) SaveConfig(fileName string) error { 118 data, err := yaml.Marshal(c) 119 if err != nil { 120 return err 121 } 122 err = ioutil.WriteFile(fileName, data, util.DefaultWritePermissions) 123 if err != nil { 124 return errors.Wrapf(err, "failed to save file %s", fileName) 125 } 126 return nil 127 } 128 129 // GetOrCreatePipelineConfig lazily creates a PipelineConfig if required 130 func (c *ProjectConfig) GetOrCreatePipelineConfig() *jenkinsfile.PipelineConfig { 131 if c.PipelineConfig == nil { 132 c.PipelineConfig = &jenkinsfile.PipelineConfig{} 133 } 134 return c.PipelineConfig 135 } 136 137 // GetPipeline retrieves the parsed pipeline for the specified type 138 func (c *ProjectConfig) GetPipeline(kind string) (*syntax.ParsedPipeline, error) { 139 var parsed *syntax.ParsedPipeline 140 141 if c.PipelineConfig == nil { 142 return nil, nil 143 } 144 145 switch kind { 146 case jenkinsfile.PipelineKindRelease: 147 if c.PipelineConfig.Pipelines.Release == nil { 148 parsed = nil 149 } else { 150 parsed = c.PipelineConfig.Pipelines.Release.Pipeline 151 } 152 case jenkinsfile.PipelineKindPullRequest: 153 if c.PipelineConfig.Pipelines.PullRequest == nil { 154 parsed = nil 155 } else { 156 parsed = c.PipelineConfig.Pipelines.PullRequest.Pipeline 157 } 158 case jenkinsfile.PipelineKindFeature: 159 if c.PipelineConfig.Pipelines.Feature == nil { 160 parsed = nil 161 } else { 162 parsed = c.PipelineConfig.Pipelines.Feature.Pipeline 163 } 164 default: 165 return nil, errors.Errorf("unknown pipeline kind %s", kind) 166 } 167 168 if parsed == nil { 169 return nil, errors.Errorf("no pipeline defined for kind %s", kind) 170 } 171 return parsed, nil 172 }