Skip to content

Commit 883a25a

Browse files
authored
Maven: Add RemoveArtifactoryRepository and ValidateArtifactoryRepository functions (#1478)
- Add RemoveArtifactoryRepository() to remove all Artifactory configuration from settings.xml - Add ValidateArtifactoryRepository() to check if configuration exists and optionally validate values - Extract shared helper functions: buildRepositoryURL, findElementByID, removeElementByID, removeEmptyContainer - Refactor findOrCreateElementByID to use new findElementByID helper - Add 10 comprehensive tests (6 removal + 5 validation tests) - Test coverage: 94.7%
1 parent 8a8c042 commit 883a25a

File tree

2 files changed

+720
-4
lines changed

2 files changed

+720
-4
lines changed

artifactory/utils/maven/settingsxml.go

Lines changed: 204 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ func (sxm *SettingsXmlManager) loadSettings() {
9797
sxm.doc.Indent(2)
9898
}
9999

100+
// buildRepositoryURL constructs the full repository URL from base URL and repository name.
101+
func buildRepositoryURL(artifactoryUrl, repoName string) string {
102+
return strings.TrimRight(artifactoryUrl, "/") + "/" + repoName
103+
}
104+
100105
// ConfigureArtifactoryRepository configures Maven to use Artifactory for both downloading and deployment.
101106
// It updates or creates the following in settings.xml:
102107
// - Mirror configuration for downloading artifacts from Artifactory
@@ -120,7 +125,7 @@ func (sxm *SettingsXmlManager) ConfigureArtifactoryRepository(artifactoryUrl, re
120125
}
121126

122127
// Build repository URL
123-
repoUrl := strings.TrimRight(artifactoryUrl, "/") + "/" + repoName
128+
repoUrl := buildRepositoryURL(artifactoryUrl, repoName)
124129

125130
// Ensure we have a root <settings> element
126131
root := sxm.doc.SelectElement(xmlElementSettings)
@@ -189,14 +194,40 @@ func getOrCreateElement(parent *etree.Element, name string) *etree.Element {
189194
return element
190195
}
191196

192-
// findOrCreateElementByID finds an element with a specific ID or creates a new one.
193-
func findOrCreateElementByID(parent *etree.Element, elementName, id string) *etree.Element {
197+
// findElementByID finds an element with a specific ID within a parent container.
198+
// Returns nil if not found.
199+
func findElementByID(parent *etree.Element, elementName, id string) *etree.Element {
194200
for _, elem := range parent.SelectElements(elementName) {
195201
if idElem := elem.SelectElement(xmlElementID); idElem != nil && idElem.Text() == id {
196202
return elem
197203
}
198204
}
199-
return parent.CreateElement(elementName)
205+
return nil
206+
}
207+
208+
// findOrCreateElementByID finds an element with a specific ID or creates a new one.
209+
func findOrCreateElementByID(parent *etree.Element, elementName, id string) *etree.Element {
210+
elem := findElementByID(parent, elementName, id)
211+
if elem == nil {
212+
elem = parent.CreateElement(elementName)
213+
}
214+
return elem
215+
}
216+
217+
// removeElementByID removes an element with a specific ID from its parent container.
218+
func removeElementByID(parent *etree.Element, elementName, id string) {
219+
elem := findElementByID(parent, elementName, id)
220+
if elem != nil {
221+
parent.RemoveChild(elem)
222+
}
223+
}
224+
225+
// removeEmptyContainer removes a container element from its parent if it has no children.
226+
func removeEmptyContainer(root *etree.Element, containerName string) {
227+
container := root.SelectElement(containerName)
228+
if container != nil && len(container.ChildElements()) == 0 {
229+
root.RemoveChild(container)
230+
}
200231
}
201232

202233
// setOrCreateChildElement sets or creates a child element with the given name and text.
@@ -205,6 +236,175 @@ func setOrCreateChildElement(parent *etree.Element, name, text string) {
205236
child.SetText(text)
206237
}
207238

239+
// ValidateArtifactoryRepository checks if Artifactory repository configuration exists in settings.xml
240+
// and optionally validates the configuration values match the expected parameters.
241+
// Returns true if all components (mirror, server, profile) are configured correctly, false otherwise.
242+
//
243+
// Parameters:
244+
// - artifactoryUrl: Base URL to validate against (optional, can be empty to skip validation)
245+
// - repoName: Repository name to validate against (optional, can be empty to skip validation)
246+
// - username: Username to validate against (optional, can be empty to skip validation)
247+
// - password: Password to validate against (optional, can be empty to skip validation)
248+
func (sxm *SettingsXmlManager) ValidateArtifactoryRepository(artifactoryUrl, repoName, username, password string) (bool, error) {
249+
root := sxm.doc.SelectElement(xmlElementSettings)
250+
if root == nil {
251+
return false, fmt.Errorf("invalid settings.xml: missing <%s> root element", xmlElementSettings)
252+
}
253+
254+
// Build expected repository URL if parameters provided
255+
var expectedRepoUrl string
256+
if artifactoryUrl != "" && repoName != "" {
257+
expectedRepoUrl = buildRepositoryURL(artifactoryUrl, repoName)
258+
}
259+
260+
// Check mirror configuration
261+
mirrors := root.SelectElement(xmlElementMirrors)
262+
mirrorConfigured := false
263+
if mirrors != nil {
264+
mirror := findElementByID(mirrors, xmlElementMirror, ArtifactoryMirrorID)
265+
if mirror != nil {
266+
mirrorConfigured = true
267+
// Validate URL if provided
268+
if expectedRepoUrl != "" {
269+
urlElem := mirror.SelectElement(xmlElementURL)
270+
if urlElem == nil || urlElem.Text() != expectedRepoUrl {
271+
return false, nil
272+
}
273+
}
274+
}
275+
}
276+
277+
// Check server configuration
278+
servers := root.SelectElement(xmlElementServers)
279+
serverConfigured := false
280+
if servers != nil {
281+
server := findElementByID(servers, xmlElementServer, ArtifactoryMirrorID)
282+
if server != nil {
283+
serverConfigured = true
284+
// Validate credentials if provided
285+
if username != "" {
286+
usernameElem := server.SelectElement(xmlElementUsername)
287+
if usernameElem == nil || usernameElem.Text() != username {
288+
return false, nil
289+
}
290+
}
291+
if password != "" {
292+
passwordElem := server.SelectElement(xmlElementPassword)
293+
if passwordElem == nil || passwordElem.Text() != password {
294+
return false, nil
295+
}
296+
}
297+
}
298+
}
299+
300+
// Check deployment profile
301+
profiles := root.SelectElement(xmlElementProfiles)
302+
profileConfigured := false
303+
if profiles != nil {
304+
profile := findElementByID(profiles, xmlElementProfile, ArtifactoryDeployProfileID)
305+
if profile != nil {
306+
profileConfigured = true
307+
// Validate altDeploymentRepository if URL provided
308+
if expectedRepoUrl != "" {
309+
properties := profile.SelectElement(xmlElementProperties)
310+
if properties != nil {
311+
altDeployElem := properties.SelectElement(AltDeploymentRepositoryProperty)
312+
expectedAltDeploy := fmt.Sprintf("%s::default::%s", ArtifactoryMirrorID, expectedRepoUrl)
313+
if altDeployElem == nil || altDeployElem.Text() != expectedAltDeploy {
314+
return false, nil
315+
}
316+
}
317+
}
318+
}
319+
}
320+
321+
return mirrorConfigured && serverConfigured && profileConfigured, nil
322+
}
323+
324+
// RemoveArtifactoryRepository removes all Artifactory configuration from settings.xml.
325+
// This includes:
326+
// - Mirror configuration with ArtifactoryMirrorID
327+
// - Server credentials with ArtifactoryMirrorID
328+
// - Deployment profile with ArtifactoryDeployProfileID
329+
//
330+
// Parameters:
331+
// - artifactoryUrl: Base URL of the Artifactory instance (used for verification, optional)
332+
// - repoName: Name of the Artifactory repository (used for verification, optional)
333+
//
334+
// Returns an error if the settings.xml cannot be updated.
335+
func (sxm *SettingsXmlManager) RemoveArtifactoryRepository(artifactoryUrl, repoName string) error {
336+
root := sxm.doc.SelectElement(xmlElementSettings)
337+
if root == nil {
338+
return fmt.Errorf("invalid settings.xml: missing <%s> root element", xmlElementSettings)
339+
}
340+
341+
// Build repository URL for verification if provided
342+
var repoUrl string
343+
if artifactoryUrl != "" && repoName != "" {
344+
repoUrl = buildRepositoryURL(artifactoryUrl, repoName)
345+
}
346+
347+
// Remove mirror
348+
if err := sxm.removeMirror(root, repoUrl); err != nil {
349+
return err
350+
}
351+
352+
// Remove server
353+
sxm.removeServer(root)
354+
355+
// Remove deployment profile
356+
sxm.removeDeploymentProfile(root)
357+
358+
// Write settings to file
359+
return sxm.writeSettingsToFile()
360+
}
361+
362+
// removeMirror removes the Artifactory mirror entry.
363+
func (sxm *SettingsXmlManager) removeMirror(root *etree.Element, expectedUrl string) error {
364+
mirrors := root.SelectElement(xmlElementMirrors)
365+
if mirrors == nil {
366+
return nil
367+
}
368+
369+
// Verify URL if provided before removing
370+
if expectedUrl != "" {
371+
mirror := findElementByID(mirrors, xmlElementMirror, ArtifactoryMirrorID)
372+
if mirror != nil {
373+
urlElem := mirror.SelectElement(xmlElementURL)
374+
if urlElem != nil && urlElem.Text() != expectedUrl {
375+
return fmt.Errorf("mirror URL mismatch: expected %s, found %s", expectedUrl, urlElem.Text())
376+
}
377+
}
378+
}
379+
380+
removeElementByID(mirrors, xmlElementMirror, ArtifactoryMirrorID)
381+
removeEmptyContainer(root, xmlElementMirrors)
382+
383+
return nil
384+
}
385+
386+
// removeServer removes the Artifactory server entry.
387+
func (sxm *SettingsXmlManager) removeServer(root *etree.Element) {
388+
servers := root.SelectElement(xmlElementServers)
389+
if servers == nil {
390+
return
391+
}
392+
393+
removeElementByID(servers, xmlElementServer, ArtifactoryMirrorID)
394+
removeEmptyContainer(root, xmlElementServers)
395+
}
396+
397+
// removeDeploymentProfile removes the Artifactory deployment profile.
398+
func (sxm *SettingsXmlManager) removeDeploymentProfile(root *etree.Element) {
399+
profiles := root.SelectElement(xmlElementProfiles)
400+
if profiles == nil {
401+
return
402+
}
403+
404+
removeElementByID(profiles, xmlElementProfile, ArtifactoryDeployProfileID)
405+
removeEmptyContainer(root, xmlElementProfiles)
406+
}
407+
208408
// writeSettingsToFile writes the document to the settings.xml file.
209409
func (sxm *SettingsXmlManager) writeSettingsToFile() error {
210410
// Ensure directory exists

0 commit comments

Comments
 (0)