So I got motivated to do enough cargo cult programming in ruby to try to
figure this out, and it looks like it's actually very simple, and is
nothing more than a classic "modify list during iteration" bug.

When it removes an item as a duplicate, it seems to cause the list
iteration to end up skipping an item.

I added debug code to show each step of the "remove duplicates" loop. 
Here's the start of that debug, using the ctools project data, up until
the error becomes visible.

D:    Examining item 7:1.7 status 2
D:    Examining other 7:1.7 status 2
D:    Examining other 6:1.12 status 2
D:    Examining other 7:1~~dev status 2
D:    Examining other 6:1~~dev status 2
D:    Examining other 7:1~~dev status 0
D:    Examining other 6:1~~dev status 0
D:    Examining item 6:1.12 status 2
D:    Examining other 7:1.7 status 2
D:    Examining other 6:1.12 status 2
D:    Examining other 7:1~~dev status 2
D:    Examining other 6:1~~dev status 2
D:    Examining other 7:1~~dev status 0
D:    Examining other 6:1~~dev status 0
D:    Examining item 7:1~~dev status 2
D:    Examining other 7:1.7 status 2
D:    Examining other 6:1.12 status 2
D:    Examining other 7:1~~dev status 2
D:    Examining other 6:1~~dev status 2
D:    Examining other 7:1~~dev status 0
D:    Removing duplicate version 1~~dev (status 2)
D:    Examining item 7:1~~dev status 0

Notice that after removing "7:1~~dev status 2" it should go on to
examine item = "6:1~~dev status 2", but instead it skips to the next
item "7:1~~dev status 0".  Removing the element from the array during
iteration has broken the iteration.

I've attached a patch that includes the above debug messages and my
ugly(?) fix for it, which works for me.  This is the first time I've
ever tried to write ruby code, so apologies if I'm missing some
ruby-isms.  Mainly I guessed the syntax by reading other parts of this
script :)

Cheers,
  -- Matt
--- /usr/bin/dh-make-drupal	2014-05-13 15:06:27.000000000 -0400
+++ /tmp/dh-make-drupal	2015-03-24 19:39:58.589820630 -0400
@@ -778,16 +778,23 @@
       # Remove wrongly detected duplicate versions on different
       # support levels (leave the lowest one, that's how Drupal's site
       # is structured. Ugly :-(
+      duplicates = Array.new
       list.each do |item|
+        Logger.instance.debug('Examining item %s:%s status %s' % [item.drupal_version, item.version, item.status])
         list.each do |other|
+          Logger.instance.debug('Examining other %s:%s status %s' % [other.drupal_version, other.version, other.status])
           next if item == other
           next if item.version != other.version
           if other.status < item.status
-            Logger.instance.debug('Removing duplicate version %s (status %s)' % [item.version, item.status])
-            list.delete(item) if other.status < item.status
+            Logger.instance.debug('Marking duplicate version %s (status %s)' % [item.version, item.status])
+            duplicates << item if other.status < item.status
           end
         end
       end
+      duplicates.each do |item|
+        Logger.instance.debug('Removing duplicate version %s (status %s)' % [item.version, item.status])
+        list.delete(item)
+      end
 
       list
     end

Reply via email to