Hi,

Resolved my own problem.


On 01/11/12 13:47, Darragh Bailey wrote:
> Hi,
>
> I'm trying to put together a script that can be run via the console or 
> possibly the groovy plugin, to remove all the additional saved configs 
> added due to how the envInject and jobConfigHistory plugins interact.
<snipped>
> For some reason though, when I ask the "Document" object to do the 
> comparison using the 'isEqualNode' method, there are a number of cases 
> where it says the xml does not match, but if I compare the xml by 
> formatting it and outputting to strings, it does match.
>
> I'm a little stumped as to what would be getting picked up as being 
> different. Is there something that the transform would be missing?

That was exactly the problem. The transform was leaving a blank line 
when removing the undesired element, which only became apparent when I 
decided to switch to using a simple 'for ( node in cfgDoc ) { 
println(node); }' to print the xml document.


Corrected xsl template is:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template 
match="org.jenkinsci.plugins.envinject.EnvInjectListener_-JobSetupEnvironmentWrapper"/>
 
<!-- this empty template will remove this element -->
</xsl:stylesheet>

Output ended up having all whitespace removed including indents despite 
the '<xsl:output indent="yes"/>' element, but that didn't bother me once 
it was correctly able to discard any extra configs saved due to the 
envInject plugin.

Script is at the end of the email for anyone else that might find it useful.

-- 
Regards,
Darragh Bailey



-----

import hudson.plugins.jobConfigHistory.*;

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;

import org.apache.commons.lang.StringUtils;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;

import org.w3c.dom.*;
import org.xml.sax.*;

// see 
http://stackoverflow.com/questions/141993/best-way-to-compare-2-xml-documents-in-java
public class BasicXsl {
     // applies the xsl string to inFilename and returns the result as a 
string
     public static String xsl(String inFilename, String xsl) {
         try {
             Source source = new StreamSource(new 
FileInputStream(inFilename));
             StringWriter writer = new StringWriter();

             // Create transformer factory
             TransformerFactory factory = TransformerFactory.newInstance();
             Templates template = factory.newTemplates(new 
StreamSource(new StringReader(xsl)));

             Transformer xformer = template.newTransformer();
             xformer.transform(source, new StreamResult(writer));

             return(writer.toString().trim());
         } catch (FileNotFoundException e) {
         } catch (TransformerConfigurationException e) {
             // An error occurred in the XSL file
         } catch (TransformerException e) {
             // An error occurred while applying the XSL file
             // Get location of error in input file
             SourceLocator locator = e.getLocator();
             int col = locator.getColumnNumber();
             int line = locator.getLineNumber();
             String publicId = locator.getPublicId();
             String systemId = locator.getSystemId();
         }
         return null;
     }
}

public class trimpath {
   public static String shortpath(String path, int elements) {
     String [] filePathArray = path.split(File.separator);
     return StringUtils.join(Arrays.copyOfRange(
           filePathArray, filePathArray.length - elements, 
filePathArray.length), File.separator)
   }
}

// see 
http://stackoverflow.com/questions/1137563/xsl-how-to-copy-a-tree-but-removing-some-nodes/1137628#1137628
// and 
http://stackoverflow.com/questions/10711023/getting-ride-of-empty-lines-after-deleting-nodes-using-xslt
//
// simple xsl template to discard any node matching
// 
org.jenkinsci.plugins.envinject.EnvInjectListener_-JobSetupEnvironmentWrapper
xsl = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
       " <xsl:stylesheet version=\"1.0\" 
xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\";>\n" +
       " <xsl:output indent=\"yes\"/>\n" +
       " <xsl:strip-space elements=\"*\"/>\n" +
       " <xsl:template match=\"node() | @*\">\n" +
       " <xsl:copy>\n" +
       " <xsl:copy-of select=\"@*\" />\n" +
       " <xsl:apply-templates/>\n" +
       " </xsl:copy>\n" +
       " </xsl:template>\n" +
       " <xsl:template 
match=\"org.jenkinsci.plugins.envinject.EnvInjectListener_-JobSetupEnvironmentWrapper\"/>
 
<!-- this empty template will this element -->\n" +
       "</xsl:stylesheet>";


// see 
http://stackoverflow.com/questions/141993/best-way-to-compare-2-xml-documents-in-java
//
// Set up docbuilder stuff for xml parsing/comparison
dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setCoalescing(true);
dbf.setIgnoringElementContentWhitespace(true);
dbf.setIgnoringComments(true);
db = dbf.newDocumentBuilder();

for(item in Hudson.instance.getItems(hudson.model.Project)) {

   AbstractProject project = item;
   println(item.getName());
   jch = new JobConfigHistoryProjectAction(project);
   allJch = jch.getConfigs()
   if(allJch.size() < 1) {
     continue;
   }

   // avoid deleting while the build is running, just in case
   if(item.isBuilding()) {
     continue;
   }

   // note: configs are ordered latest to earliest
   itr = allJch.listIterator(allJch.size());

   // always keep the very first one
   prevCfg = itr.previous();
   prevCfgFile = URLDecoder.decode(prevCfg.getFile(), "utf-8") + 
"/config.xml"
   prevdoc = db.parse(new InputSource(new 
StringReader(BasicXsl.xsl(prevCfgFile, xsl))));
   prevdoc.normalizeDocument();

   keeping=0;
   deleting=0;
   while(itr.hasPrevious()) {
     cfg = itr.previous();

     cfgDir = URLDecoder.decode(cfg.getFile(), "utf-8")
     cfgFile = cfgDir + "/config.xml"
     cfgdoc = db.parse(new InputSource(new 
StringReader(BasicXsl.xsl(cfgFile, xsl))));
     cfgdoc.normalizeDocument();

     // compare using document object
     if(!prevdoc.isEqualNode(cfgdoc)) {
       println("Ignoring as files differ: " + 
trimpath.shortpath(prevCfgFile, 4) + " " + trimpath.shortpath(cfgFile, 4) );
       prevCfg = cfg;
       prevCfgFile = cfgFile;
       prevdoc = cfgdoc;
       keeping += 1;
       continue;
     } else {
       println("Files are the same: " + trimpath.shortpath(prevCfgFile, 
4) + " " + trimpath.shortpath(cfgFile, 4) );
     }

     // ignore those created by other users besides SYSTEM since they 
were deliberately saved
     if( cfg.getUserID() != "SYSTEM" ) {
        keeping += 1;
        continue;
     }
     println("Following file can be deleted: " + 
trimpath.shortpath(cfgFile, 4));
     deleting += 1;
     // paranoia
     base = item.getRootDir().getCanonicalFile();
     child = (new File(cfgDir)).getCanonicalFile();

     File parentFile = child;
     while (parentFile != null) {
       if (base.equals(parentFile)) {
         println("Deleting: " + cfgDir);
         // when happy withe text output, uncomment the following lines
         // File.deleteDir(cfgDir);
         break;
       }
       parentFile = parentFile.getParentFile();
     }
   }
   println("Kept " + keeping + " files, deleted " + deleting + " files");
}

Reply via email to