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");
}