@@ -760,12 +760,16 @@ func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string,
760760 Errors : make (map [string ]error ),
761761 Removed : []string {},
762762 }
763- removeList := [] string {}
763+
764764 reverseMap , appMap , err := buildAppMap (systemdquadlet .GetInstallUnitDirPath (rootless .IsRootless ()))
765765 if err != nil {
766766 return nil , fmt .Errorf ("unable to build app map: %w" , err )
767767 }
768- expandQuadletList := []string {}
768+
769+ // Use sets for deduplication
770+ removeSet := make (map [string ]struct {})
771+ expandQuadletSet := make (map [string ]struct {})
772+
769773 // Process all `.app` files in arguments, if `.app` file
770774 // is found then expand it to its respective quadlet files
771775 // and remove it from the processing list.
@@ -777,32 +781,36 @@ func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string,
777781 if ok {
778782 for _ , file := range files {
779783 if ! systemdquadlet .IsExtSupported (file ) {
780- removeList = append ( removeList , file )
784+ removeSet [ file ] = struct {}{}
781785 } else {
782- expandQuadletList = append ( expandQuadletList , file )
786+ expandQuadletSet [ file ] = struct {}{}
783787 }
784788 }
785789 }
786790 // also add .app file itself to the remove list so it can
787791 // be cleaned after removal of all components in the list
788- if ! slices .Contains (removeList , quadlet ) {
789- removeList = append (removeList , quadlet )
790- }
792+ removeSet [quadlet ] = struct {}{}
791793 } else {
792- expandQuadletList = append ( expandQuadletList , quadlet )
794+ expandQuadletSet [ quadlet ] = struct {}{}
793795 }
794796 }
795- quadlets = expandQuadletList
797+
798+ // Convert expandQuadletSet to slice
799+ quadlets = make ([]string , 0 , len (expandQuadletSet ))
800+ for quadlet := range expandQuadletSet {
801+ quadlets = append (quadlets , quadlet )
802+ }
803+
804+ if len (quadlets ) == 0 && ! options .All {
805+ return nil , errors .New ("must provide at least 1 quadlet to remove" )
806+ }
807+
796808 allQuadletPaths := make ([]string , 0 , len (quadlets ))
797809 allServiceNames := make ([]string , 0 , len (quadlets ))
798810 runningQuadlets := make ([]string , 0 , len (quadlets ))
799811 serviceNameToQuadletName := make (map [string ]string )
800812 needReload := options .ReloadSystemd
801813
802- if len (quadlets ) == 0 && ! options .All {
803- return nil , errors .New ("must provide at least 1 quadlet to remove" )
804- }
805-
806814 // Is systemd available to the current user?
807815 // We cannot proceed if not.
808816 conn , err := systemd .ConnectToDBUS ()
@@ -813,7 +821,12 @@ func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string,
813821
814822 if options .All {
815823 allQuadlets := getAllQuadletPaths ()
816- quadlets = allQuadlets
824+ for _ , quadlet := range allQuadlets {
825+ if _ , exists := expandQuadletSet [quadlet ]; ! exists {
826+ quadlets = append (quadlets , quadlet )
827+ expandQuadletSet [quadlet ] = struct {}{}
828+ }
829+ }
817830 }
818831
819832 // We are using index wise iteration here instead of `range`
@@ -838,30 +851,37 @@ func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string,
838851 }
839852 continue
840853 }
841- value , ok := reverseMap [quadlet ]
854+ // Use base filename for reverseMap lookup since map keys are filenames, not full paths
855+ quadletBaseName := filepath .Base (quadlet )
856+ value , ok := reverseMap [quadletBaseName ]
842857 if ok {
843858 // If this is part of app and we are cleaning entire .app
844859 // make sure to add .app file itself to the removal list
845860 // if it does not already exists.
846- if ! slices .Contains (removeList , value ) {
847- removeList = append (removeList , value )
848- }
861+ removeSet [value ] = struct {}{}
849862 appFilePath := filepath .Join (systemdquadlet .GetInstallUnitDirPath (rootless .IsRootless ()), value )
850863 filesToRemove , err := getAssetListFromFile (appFilePath )
851864 if err != nil {
852865 return nil , fmt .Errorf ("unable to get list of files to remove: %w" , err )
853866 }
854867 for _ , entry := range filesToRemove {
855868 if ! systemdquadlet .IsExtSupported (entry ) {
856- removeList = append (removeList , entry )
857- if ! slices .Contains (removeList , value ) {
858- // In the last also clean .<quadlet>.app file
859- removeList = append (removeList , value )
860- }
869+ removeSet [entry ] = struct {}{}
870+ removeSet [value ] = struct {}{}
861871 continue
862872 }
863- if ! slices .Contains (quadlets , entry ) {
864- quadlets = append (quadlets , entry )
873+ // When options.All is true, use full paths; when false, use filenames.
874+ // this ensures consistency since getAllQuadletPaths() returns full paths
875+ // but getQuadletPathByName() expects filenames
876+ var entryToAdd string
877+ if options .All {
878+ entryToAdd = filepath .Join (systemdquadlet .GetInstallUnitDirPath (rootless .IsRootless ()), entry )
879+ } else {
880+ entryToAdd = entry
881+ }
882+ if _ , exists := expandQuadletSet [entryToAdd ]; ! exists {
883+ quadlets = append (quadlets , entryToAdd )
884+ expandQuadletSet [entryToAdd ] = struct {}{}
865885 }
866886 }
867887 }
@@ -937,13 +957,24 @@ func (ic *ContainerEngine) QuadletRemove(ctx context.Context, quadlets []string,
937957 continue
938958 }
939959 }
940- for _ , entry := range removeList {
941- os .Remove (filepath .Join (systemdquadlet .GetInstallUnitDirPath (rootless .IsRootless ()), entry ))
942- }
943960 report .Removed = append (report .Removed , quadletName )
944961 }
945962 }
946963
964+ // Remove .app and .asset files after the main quadlet removal loop
965+ // This ensures they are cleaned up properly since they are not included in allQuadletPaths
966+ installDir := systemdquadlet .GetInstallUnitDirPath (rootless .IsRootless ())
967+ for entry := range removeSet {
968+ entryPath := filepath .Join (installDir , entry )
969+ if err := os .Remove (entryPath ); err != nil {
970+ if ! errors .Is (err , fs .ErrNotExist ) {
971+ logrus .Warnf ("Failed to remove metadata file %s: %v" , entry , err )
972+ }
973+ } else {
974+ logrus .Debugf ("Removed metadata file %s" , entry )
975+ }
976+ }
977+
947978 // Reload systemd, if necessary/requested.
948979 if needReload {
949980 if err := conn .ReloadContext (ctx ); err != nil {
0 commit comments