github.com/jaylevin/jenkins-library@v1.230.4/pkg/maven/settings.go (about) 1 package maven 2 3 import ( 4 "encoding/xml" 5 "fmt" 6 "net/http" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "github.com/SAP/jenkins-library/pkg/log" 12 ) 13 14 var getenv = os.Getenv 15 16 // SettingsDownloadUtils defines an interface for downloading and storing maven settings files. 17 type SettingsDownloadUtils interface { 18 DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error 19 FileExists(filename string) (bool, error) 20 Copy(src, dest string) (int64, error) 21 MkdirAll(path string, perm os.FileMode) error 22 FileWrite(path string, content []byte, perm os.FileMode) error 23 FileRead(path string) ([]byte, error) 24 } 25 26 // DownloadAndGetMavenParameters downloads the global or project settings file if the strings contain URLs. 27 // It then constructs the arguments that need to be passed to maven in order to point to use these settings files. 28 func DownloadAndGetMavenParameters(globalSettingsFile string, projectSettingsFile string, utils SettingsDownloadUtils) ([]string, error) { 29 mavenArgs := []string{} 30 if len(globalSettingsFile) > 0 { 31 globalSettingsFileName, err := downloadSettingsIfURL(globalSettingsFile, ".pipeline/mavenGlobalSettings.xml", utils, false) 32 if err != nil { 33 return nil, err 34 } 35 mavenArgs = append(mavenArgs, "--global-settings", globalSettingsFileName) 36 } else { 37 38 log.Entry().Debugf("Global settings file not provided via configuration.") 39 } 40 41 if len(projectSettingsFile) > 0 { 42 projectSettingsFileName, err := downloadSettingsIfURL(projectSettingsFile, ".pipeline/mavenProjectSettings.xml", utils, false) 43 if err != nil { 44 return nil, err 45 } 46 mavenArgs = append(mavenArgs, "--settings", projectSettingsFileName) 47 } else { 48 49 log.Entry().Debugf("Project settings file not provided via configuration.") 50 } 51 return mavenArgs, nil 52 } 53 54 // DownloadAndCopySettingsFiles downloads the global or project settings file if the strings contain URLs. 55 // It copies the given files to either the locations specified in the environment variables M2_HOME and HOME 56 // or the default locations where maven expects them. 57 func DownloadAndCopySettingsFiles(globalSettingsFile string, projectSettingsFile string, utils SettingsDownloadUtils) error { 58 if len(projectSettingsFile) > 0 { 59 destination, err := getProjectSettingsFileDest() 60 if err != nil { 61 return err 62 } 63 64 if err := downloadAndCopySettingsFile(projectSettingsFile, destination, utils); err != nil { 65 return err 66 } 67 } else { 68 69 log.Entry().Debugf("Project settings file not provided via configuration.") 70 } 71 72 if len(globalSettingsFile) > 0 { 73 destination, err := getGlobalSettingsFileDest() 74 if err != nil { 75 return err 76 } 77 if err := downloadAndCopySettingsFile(globalSettingsFile, destination, utils); err != nil { 78 return err 79 } 80 } else { 81 82 log.Entry().Debugf("Global settings file not provided via configuration.") 83 } 84 85 return nil 86 } 87 88 func UpdateActiveProfileInSettingsXML(newActiveProfiles []string, utils SettingsDownloadUtils) error { 89 settingsFile, err := getGlobalSettingsFileDest() 90 if err != nil { 91 return err 92 } 93 94 settingsXMLContent, err := utils.FileRead(settingsFile) 95 if err != nil { 96 return fmt.Errorf("error reading global settings xml file at %v , continuing without active profile update", settingsFile) 97 } 98 99 var projectSettings Settings 100 err = xml.Unmarshal([]byte(settingsXMLContent), &projectSettings) 101 102 if err != nil { 103 return fmt.Errorf("failed to unmarshal settings xml file '%v': %w", settingsFile, err) 104 } 105 106 if len(projectSettings.ActiveProfiles.ActiveProfile) == 0 { 107 log.Entry().Warnf("no active profile found to replace in settings xml %v , continuing without file edit", settingsFile) 108 } else { 109 projectSettings.Xsi = "http://www.w3.org/2001/XMLSchema-instance" 110 projectSettings.SchemaLocation = "http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd" 111 112 projectSettings.ActiveProfiles.ActiveProfile = []string{} 113 projectSettings.ActiveProfiles.ActiveProfile = append(projectSettings.ActiveProfiles.ActiveProfile, newActiveProfiles...) 114 115 settingsXml, err := xml.MarshalIndent(projectSettings, "", " ") 116 if err != nil { 117 return fmt.Errorf("failed to marshal maven project settings xml: %w", err) 118 } 119 120 settingsXmlString := string(settingsXml) 121 Replacer := strings.NewReplacer("
", "", "	", "") 122 settingsXmlString = Replacer.Replace(settingsXmlString) 123 xmlstring := []byte(xml.Header + settingsXmlString) 124 125 err = utils.FileWrite(settingsFile, xmlstring, 0777) 126 127 if err != nil { 128 return fmt.Errorf("failed to write maven Settings during <activeProfile> update xml: %w", err) 129 } 130 log.Entry().Infof("Successfully updated <acitveProfile> details in maven settings file : '%s'", settingsFile) 131 132 } 133 return nil 134 } 135 136 func CreateNewProjectSettingsXML(altDeploymentRepositoryID string, altDeploymentRepositoryUser string, altDeploymentRepositoryPassword string, utils SettingsDownloadUtils) (string, error) { 137 settingsXML := Settings{ 138 XMLName: xml.Name{Local: "settings"}, 139 Xsi: "http://www.w3.org/2001/XMLSchema-instance", 140 SchemaLocation: "http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd", 141 Servers: ServersType{ 142 ServerType: []Server{ 143 { 144 ID: altDeploymentRepositoryID, 145 Username: altDeploymentRepositoryUser, 146 Password: altDeploymentRepositoryPassword, 147 }, 148 }, 149 }, 150 } 151 152 xmlstring, err := xml.MarshalIndent(settingsXML, "", " ") 153 if err != nil { 154 return "", fmt.Errorf("failed to marshal Settings.xml: %w", err) 155 } 156 157 xmlstring = []byte(xml.Header + string(xmlstring)) 158 159 err = utils.FileWrite(".pipeline/mavenProjectSettings.xml", xmlstring, 0777) 160 if err != nil { 161 return "", fmt.Errorf("failed to write maven Project Settings xml: %w", err) 162 } 163 164 log.Entry().Infof("Successfully created maven project settings with <server> details at .pipeline/mavenProjectSettings.xml") 165 166 return ".pipeline/mavenProjectSettings.xml", nil 167 168 } 169 170 func UpdateProjectSettingsXML(projectSettingsFile string, altDeploymentRepositoryID string, altDeploymentRepositoryUser string, altDeploymentRepositoryPassword string, utils SettingsDownloadUtils) (string, error) { 171 projectSettingsFileDestination := ".pipeline/mavenProjectSettings" 172 if exists, _ := utils.FileExists(projectSettingsFile); exists { 173 projectSettingsFileDestination = projectSettingsFile 174 addServerTagtoProjectSettingsXML(projectSettingsFile, altDeploymentRepositoryID, altDeploymentRepositoryUser, altDeploymentRepositoryPassword, utils) 175 } else { 176 addServerTagtoProjectSettingsXML(".pipeline/mavenProjectSettings", altDeploymentRepositoryID, altDeploymentRepositoryUser, altDeploymentRepositoryPassword, utils) 177 } 178 return projectSettingsFileDestination, nil 179 180 } 181 182 func addServerTagtoProjectSettingsXML(projectSettingsFile string, altDeploymentRepositoryID string, altDeploymentRepositoryUser string, altDeploymentRepositoryPassword string, utils SettingsDownloadUtils) error { 183 var projectSettings Settings 184 settingsXMLContent, err := utils.FileRead(projectSettingsFile) 185 if err != nil { 186 return fmt.Errorf("failed to read file '%v': %w", projectSettingsFile, err) 187 } 188 189 err = xml.Unmarshal([]byte(settingsXMLContent), &projectSettings) 190 if err != nil { 191 return fmt.Errorf("failed to unmarshal settings xml file '%v': %w", projectSettingsFile, err) 192 } 193 194 if len(projectSettings.Servers.ServerType) == 0 { 195 projectSettings.Xsi = "http://www.w3.org/2001/XMLSchema-instance" 196 projectSettings.SchemaLocation = "http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd" 197 projectSettings.Servers.ServerType = []Server{ 198 { 199 ID: altDeploymentRepositoryID, 200 Username: altDeploymentRepositoryUser, 201 Password: altDeploymentRepositoryPassword, 202 }, 203 } 204 } else if len(projectSettings.Servers.ServerType) > 0 { // if <server> tag is present then add the staging server tag 205 stagingServer := Server{ 206 ID: altDeploymentRepositoryID, 207 Username: altDeploymentRepositoryUser, 208 Password: altDeploymentRepositoryPassword, 209 } 210 projectSettings.Servers.ServerType = append(projectSettings.Servers.ServerType, stagingServer) 211 } 212 213 settingsXml, err := xml.MarshalIndent(projectSettings, "", " ") 214 if err != nil { 215 fmt.Errorf("failed to marshal maven project settings xml: %w", err) 216 } 217 settingsXmlString := string(settingsXml) 218 Replacer := strings.NewReplacer("
", "", "	", "") 219 settingsXmlString = Replacer.Replace(settingsXmlString) 220 221 xmlstring := []byte(xml.Header + settingsXmlString) 222 223 err = utils.FileWrite(projectSettingsFile, xmlstring, 0777) 224 if err != nil { 225 fmt.Errorf("failed to write maven Settings xml: %w", err) 226 } 227 log.Entry().Infof("Successfully updated <server> details in maven project settings file : '%s'", projectSettingsFile) 228 229 return nil 230 } 231 232 func downloadAndCopySettingsFile(src string, dest string, utils SettingsDownloadUtils) error { 233 if len(src) == 0 { 234 return fmt.Errorf("Settings file source location not provided") 235 } 236 237 if len(dest) == 0 { 238 return fmt.Errorf("Settings file destination location not provided") 239 } 240 241 log.Entry().Debugf("Copying file \"%s\" to \"%s\"", src, dest) 242 243 if strings.HasPrefix(src, "http:") || strings.HasPrefix(src, "https:") { 244 err := downloadSettingsFromURL(src, dest, utils, true) 245 if err != nil { 246 return err 247 } 248 } else { 249 250 // for sake os symmetry it would be better to use a file protocol prefix here (file:) 251 252 parent := filepath.Dir(dest) 253 254 parentFolderExists, err := utils.FileExists(parent) 255 256 if err != nil { 257 return err 258 } 259 260 if !parentFolderExists { 261 if err = utils.MkdirAll(parent, 0775); err != nil { 262 return err 263 } 264 } 265 266 if _, err := utils.Copy(src, dest); err != nil { 267 return err 268 } 269 } 270 271 return nil 272 } 273 274 func downloadSettingsIfURL(settingsFileOption, settingsFile string, utils SettingsDownloadUtils, overwrite bool) (string, error) { 275 result := settingsFileOption 276 if strings.HasPrefix(settingsFileOption, "http:") || strings.HasPrefix(settingsFileOption, "https:") { 277 err := downloadSettingsFromURL(settingsFileOption, settingsFile, utils, overwrite) 278 if err != nil { 279 return "", err 280 } 281 result = settingsFile 282 } 283 return result, nil 284 } 285 286 func downloadSettingsFromURL(url, filename string, utils SettingsDownloadUtils, overwrite bool) error { 287 exists, _ := utils.FileExists(filename) 288 if exists && !overwrite { 289 log.Entry().Infof("Not downloading maven settings file, because it already exists at '%s'", filename) 290 return nil 291 } 292 err := utils.DownloadFile(url, filename, nil, nil) 293 if err != nil { 294 return fmt.Errorf("failed to download maven settings from URL '%s' to file '%s': %w", 295 url, filename, err) 296 } 297 return nil 298 } 299 300 func getGlobalSettingsFileDest() (string, error) { 301 302 m2Home, err := getEnvironmentVariable("M2_HOME") 303 if err != nil { 304 return "", err 305 } 306 return filepath.Join(m2Home, "conf", "settings.xml"), nil 307 } 308 309 func getProjectSettingsFileDest() (string, error) { 310 home, err := getEnvironmentVariable("HOME") 311 if err != nil { 312 return "", err 313 } 314 return filepath.Join(home, ".m2", "settings.xml"), nil 315 } 316 317 func getEnvironmentVariable(name string) (string, error) { 318 319 envVar := getenv(name) 320 321 if len(envVar) == 0 { 322 return "", fmt.Errorf("Environment variable \"%s\" not set or empty", name) 323 } 324 325 return envVar, nil 326 }