From c152a8c09c6dc62a7cc102721c1a848a493fb68d Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 22 Mar 2012 16:49:01 +0100 Subject: SONAR-3224 API: support Ruby on Rails applications --- .../sonar/core/plugins/PluginFileExtractor.java | 139 --------------------- .../org/sonar/core/plugins/PluginInstaller.java | 139 +++++++++++++++++++++ .../core/plugins/PluginFileExtractorTest.java | 105 ---------------- .../sonar/core/plugins/PluginInstallerTest.java | 105 ++++++++++++++++ .../checkstyle-extension.xml | 1 - .../checkstyle-extension.xml | 1 + 6 files changed, 245 insertions(+), 245 deletions(-) delete mode 100644 sonar-core/src/main/java/org/sonar/core/plugins/PluginFileExtractor.java create mode 100644 sonar-core/src/main/java/org/sonar/core/plugins/PluginInstaller.java delete mode 100644 sonar-core/src/test/java/org/sonar/core/plugins/PluginFileExtractorTest.java create mode 100644 sonar-core/src/test/java/org/sonar/core/plugins/PluginInstallerTest.java delete mode 100644 sonar-core/src/test/resources/org/sonar/core/plugins/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/plugins/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml (limited to 'sonar-core') diff --git a/sonar-core/src/main/java/org/sonar/core/plugins/PluginFileExtractor.java b/sonar-core/src/main/java/org/sonar/core/plugins/PluginFileExtractor.java deleted file mode 100644 index 59676d078be..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/plugins/PluginFileExtractor.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.core.plugins; - -import org.apache.commons.io.FileUtils; -import org.sonar.api.Plugin; -import org.sonar.api.utils.SonarException; -import org.sonar.api.utils.ZipUtils; -import org.sonar.updatecenter.common.PluginKeyUtils; -import org.sonar.updatecenter.common.PluginManifest; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; -import java.util.zip.ZipEntry; - -public class PluginFileExtractor { - - public DefaultPluginMetadata installInSameLocation(File pluginFile, boolean isCore, List deprecatedExtensions) { - return install(pluginFile, isCore, deprecatedExtensions, null); - } - - public DefaultPluginMetadata install(File pluginFile, boolean isCore, List deprecatedExtensions, File toDir) { - DefaultPluginMetadata metadata = extractMetadata(pluginFile, isCore); - metadata.setDeprecatedExtensions(deprecatedExtensions); - return install(metadata, toDir); - } - - public DefaultPluginMetadata install(DefaultPluginMetadata metadata, File toDir) { - try { - File pluginFile = metadata.getFile(); - File pluginBasedir; - if (toDir != null) { - pluginBasedir = toDir; - FileUtils.forceMkdir(pluginBasedir); - File targetFile = new File(pluginBasedir, pluginFile.getName()); - FileUtils.copyFile(pluginFile, targetFile); - metadata.addDeployedFile(targetFile); - } else { - pluginBasedir = pluginFile.getParentFile(); - metadata.addDeployedFile(pluginFile); - } - - if (metadata.getPathsToInternalDeps().length > 0) { - // needs to unzip the jar - ZipUtils.unzip(pluginFile, pluginBasedir, new ZipUtils.ZipEntryFilter() { - public boolean accept(ZipEntry entry) { - return entry.getName().startsWith("META-INF/lib"); - } - }); - for (String depPath : metadata.getPathsToInternalDeps()) { - File dependency = new File(pluginBasedir, depPath); - if (!dependency.isFile() || !dependency.exists()) { - throw new IllegalArgumentException("Dependency " + depPath + " can not be found in " + pluginFile.getName()); - } - metadata.addDeployedFile(dependency); - } - } - - for (File extension : metadata.getDeprecatedExtensions()) { - File toFile = new File(pluginBasedir, extension.getName()); - if (!toFile.equals(extension)) { - FileUtils.copyFile(extension, toFile); - } - metadata.addDeployedFile(toFile); - } - - return metadata; - - } catch (IOException e) { - throw new SonarException("Fail to install plugin: " + metadata, e); - } - } - - public DefaultPluginMetadata extractMetadata(File file, boolean isCore) { - try { - PluginManifest manifest = new PluginManifest(file); - DefaultPluginMetadata metadata = DefaultPluginMetadata.create(file); - metadata.setKey(manifest.getKey()); - metadata.setName(manifest.getName()); - metadata.setDescription(manifest.getDescription()); - metadata.setLicense(manifest.getLicense()); - metadata.setOrganization(manifest.getOrganization()); - metadata.setOrganizationUrl(manifest.getOrganizationUrl()); - metadata.setMainClass(manifest.getMainClass()); - metadata.setVersion(manifest.getVersion()); - metadata.setHomepage(manifest.getHomepage()); - metadata.setPathsToInternalDeps(manifest.getDependencies()); - metadata.setUseChildFirstClassLoader(manifest.isUseChildFirstClassLoader()); - metadata.setBasePlugin(manifest.getBasePlugin()); - metadata.setCore(isCore); - if (metadata.isOldManifest()) { - completeDeprecatedMetadata(metadata); - } - return metadata; - - } catch (IOException e) { - throw new IllegalStateException("Fail to extract plugin metadata from file: " + file, e); - } - } - - private void completeDeprecatedMetadata(DefaultPluginMetadata metadata) throws IOException { - String mainClass = metadata.getMainClass(); - File pluginFile = metadata.getFile(); - try { - // copy file in a temp directory because Windows+Oracle JVM Classloader lock the JAR file - File tempFile = File.createTempFile(pluginFile.getName(), null); - FileUtils.copyFile(pluginFile, tempFile); - - URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[]{tempFile.toURI().toURL()}, getClass().getClassLoader()); - Plugin pluginInstance = (Plugin) pluginClassLoader.loadClass(mainClass).newInstance(); - metadata.setKey(PluginKeyUtils.sanitize(pluginInstance.getKey())); - metadata.setDescription(pluginInstance.getDescription()); - metadata.setName(pluginInstance.getName()); - - } catch (Exception e) { - throw new RuntimeException("The metadata main class can not be created. Plugin file=" + pluginFile.getName() + ", class=" + mainClass, e); - } - } -} diff --git a/sonar-core/src/main/java/org/sonar/core/plugins/PluginInstaller.java b/sonar-core/src/main/java/org/sonar/core/plugins/PluginInstaller.java new file mode 100644 index 00000000000..3be1816658c --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/plugins/PluginInstaller.java @@ -0,0 +1,139 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.plugins; + +import org.apache.commons.io.FileUtils; +import org.sonar.api.Plugin; +import org.sonar.api.utils.SonarException; +import org.sonar.api.utils.ZipUtils; +import org.sonar.updatecenter.common.PluginKeyUtils; +import org.sonar.updatecenter.common.PluginManifest; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; +import java.util.zip.ZipEntry; + +public class PluginInstaller { + + public DefaultPluginMetadata installInSameLocation(File pluginFile, boolean isCore, List deprecatedExtensions) { + return install(pluginFile, isCore, deprecatedExtensions, null); + } + + public DefaultPluginMetadata install(File pluginFile, boolean isCore, List deprecatedExtensions, File toDir) { + DefaultPluginMetadata metadata = extractMetadata(pluginFile, isCore); + metadata.setDeprecatedExtensions(deprecatedExtensions); + return install(metadata, toDir); + } + + public DefaultPluginMetadata install(DefaultPluginMetadata metadata, File toDir) { + try { + File pluginFile = metadata.getFile(); + File pluginBasedir; + if (toDir != null) { + pluginBasedir = toDir; + FileUtils.forceMkdir(pluginBasedir); + File targetFile = new File(pluginBasedir, pluginFile.getName()); + FileUtils.copyFile(pluginFile, targetFile); + metadata.addDeployedFile(targetFile); + } else { + pluginBasedir = pluginFile.getParentFile(); + metadata.addDeployedFile(pluginFile); + } + + if (metadata.getPathsToInternalDeps().length > 0) { + // needs to unzip the jar + ZipUtils.unzip(pluginFile, pluginBasedir, new ZipUtils.ZipEntryFilter() { + public boolean accept(ZipEntry entry) { + return entry.getName().startsWith("META-INF/lib"); + } + }); + for (String depPath : metadata.getPathsToInternalDeps()) { + File dependency = new File(pluginBasedir, depPath); + if (!dependency.isFile() || !dependency.exists()) { + throw new IllegalArgumentException("Dependency " + depPath + " can not be found in " + pluginFile.getName()); + } + metadata.addDeployedFile(dependency); + } + } + + for (File extension : metadata.getDeprecatedExtensions()) { + File toFile = new File(pluginBasedir, extension.getName()); + if (!toFile.equals(extension)) { + FileUtils.copyFile(extension, toFile); + } + metadata.addDeployedFile(toFile); + } + + return metadata; + + } catch (IOException e) { + throw new SonarException("Fail to install plugin: " + metadata, e); + } + } + + public DefaultPluginMetadata extractMetadata(File file, boolean isCore) { + try { + PluginManifest manifest = new PluginManifest(file); + DefaultPluginMetadata metadata = DefaultPluginMetadata.create(file); + metadata.setKey(manifest.getKey()); + metadata.setName(manifest.getName()); + metadata.setDescription(manifest.getDescription()); + metadata.setLicense(manifest.getLicense()); + metadata.setOrganization(manifest.getOrganization()); + metadata.setOrganizationUrl(manifest.getOrganizationUrl()); + metadata.setMainClass(manifest.getMainClass()); + metadata.setVersion(manifest.getVersion()); + metadata.setHomepage(manifest.getHomepage()); + metadata.setPathsToInternalDeps(manifest.getDependencies()); + metadata.setUseChildFirstClassLoader(manifest.isUseChildFirstClassLoader()); + metadata.setBasePlugin(manifest.getBasePlugin()); + metadata.setCore(isCore); + if (metadata.isOldManifest()) { + completeDeprecatedMetadata(metadata); + } + return metadata; + + } catch (IOException e) { + throw new IllegalStateException("Fail to extract plugin metadata from file: " + file, e); + } + } + + private void completeDeprecatedMetadata(DefaultPluginMetadata metadata) throws IOException { + String mainClass = metadata.getMainClass(); + File pluginFile = metadata.getFile(); + try { + // copy file in a temp directory because Windows+Oracle JVM Classloader lock the JAR file + File tempFile = File.createTempFile(pluginFile.getName(), null); + FileUtils.copyFile(pluginFile, tempFile); + + URLClassLoader pluginClassLoader = URLClassLoader.newInstance(new URL[]{tempFile.toURI().toURL()}, getClass().getClassLoader()); + Plugin pluginInstance = (Plugin) pluginClassLoader.loadClass(mainClass).newInstance(); + metadata.setKey(PluginKeyUtils.sanitize(pluginInstance.getKey())); + metadata.setDescription(pluginInstance.getDescription()); + metadata.setName(pluginInstance.getName()); + + } catch (Exception e) { + throw new RuntimeException("The metadata main class can not be created. Plugin file=" + pluginFile.getName() + ", class=" + mainClass, e); + } + } +} diff --git a/sonar-core/src/test/java/org/sonar/core/plugins/PluginFileExtractorTest.java b/sonar-core/src/test/java/org/sonar/core/plugins/PluginFileExtractorTest.java deleted file mode 100644 index fbd644ef68b..00000000000 --- a/sonar-core/src/test/java/org/sonar/core/plugins/PluginFileExtractorTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 SonarSource - * mailto:contact AT sonarsource DOT com - * - * Sonar is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * Sonar is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Sonar; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonar.core.plugins; - -import org.apache.commons.io.FileUtils; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.junit.Assert.assertThat; - -public class PluginFileExtractorTest { - - private PluginFileExtractor extractor= new PluginFileExtractor(); - - @Test - public void shouldExtractMetadata() { - DefaultPluginMetadata metadata = extractor.extractMetadata(getFile("sonar-checkstyle-plugin-2.8.jar"), true); - assertThat(metadata.getKey(), is("checkstyle")); - assertThat(metadata.getBasePlugin(), nullValue()); - assertThat(metadata.getName(), is("Checkstyle")); - assertThat(metadata.isCore(), is(true)); - assertThat(metadata.getFile().getName(), is("sonar-checkstyle-plugin-2.8.jar")); - } - - @Test - public void shouldExtractDeprecatedMetadata() { - DefaultPluginMetadata metadata = extractor.extractMetadata(getFile("sonar-emma-plugin-0.3.jar"), false); - assertThat(metadata.getKey(), is("emma")); - assertThat(metadata.getBasePlugin(), nullValue()); - assertThat(metadata.getName(), is("Emma")); - } - - @Test - public void shouldExtractExtensionMetadata() { - DefaultPluginMetadata metadata = extractor.extractMetadata(getFile("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"), true); - assertThat(metadata.getKey(), is("checkstyleextensions")); - assertThat(metadata.getBasePlugin(), is("checkstyle")); - } - - @Test - public void shouldCopyAndExtractDependencies() throws IOException { - File toDir = new File("target/test-tmp/PluginFileExtractorTest/shouldCopyAndExtractDependencies"); - FileUtils.forceMkdir(toDir); - FileUtils.cleanDirectory(toDir); - - DefaultPluginMetadata metadata = extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, null, toDir); - - assertThat(metadata.getKey(), is("checkstyle")); - assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true)); - assertThat(new File(toDir, "META-INF/lib/checkstyle-5.1.jar").exists(), is(true)); - } - - @Test - public void shouldExtractOnlyDependencies() throws IOException { - File toDir = new File("target/test-tmp/PluginFileExtractorTest/shouldExtractOnlyDependencies"); - FileUtils.forceMkdir(toDir); - FileUtils.cleanDirectory(toDir); - - extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, null, toDir); - - assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true)); - assertThat(new File(toDir, "META-INF/MANIFEST.MF").exists(), is(false)); - assertThat(new File(toDir, "org/sonar/plugins/checkstyle/CheckstyleVersion.class").exists(), is(false)); - } - - @Test - public void shouldCopyRuleExtensionsOnServerSide() throws IOException { - File toDir = new File("target/test-tmp/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide"); - FileUtils.forceMkdir(toDir); - FileUtils.cleanDirectory(toDir); - - DefaultPluginMetadata metadata = DefaultPluginMetadata.create(getFile("sonar-checkstyle-plugin-2.8.jar")) - .setKey("checkstyle") - .addDeprecatedExtension(getFile("PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml")); - extractor.install(metadata, toDir); - - assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true)); - assertThat(new File(toDir, "checkstyle-extension.xml").exists(), is(true)); - } - - private File getFile(String filename) { - return FileUtils.toFile(getClass().getResource("/org/sonar/core/plugins/" + filename)); - } -} diff --git a/sonar-core/src/test/java/org/sonar/core/plugins/PluginInstallerTest.java b/sonar-core/src/test/java/org/sonar/core/plugins/PluginInstallerTest.java new file mode 100644 index 00000000000..9afc9c9de17 --- /dev/null +++ b/sonar-core/src/test/java/org/sonar/core/plugins/PluginInstallerTest.java @@ -0,0 +1,105 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2008-2012 SonarSource + * mailto:contact AT sonarsource DOT com + * + * Sonar is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * Sonar is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.core.plugins; + +import org.apache.commons.io.FileUtils; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +public class PluginInstallerTest { + + private PluginInstaller extractor= new PluginInstaller(); + + @Test + public void shouldExtractMetadata() { + DefaultPluginMetadata metadata = extractor.extractMetadata(getFile("sonar-checkstyle-plugin-2.8.jar"), true); + assertThat(metadata.getKey(), is("checkstyle")); + assertThat(metadata.getBasePlugin(), nullValue()); + assertThat(metadata.getName(), is("Checkstyle")); + assertThat(metadata.isCore(), is(true)); + assertThat(metadata.getFile().getName(), is("sonar-checkstyle-plugin-2.8.jar")); + } + + @Test + public void shouldExtractDeprecatedMetadata() { + DefaultPluginMetadata metadata = extractor.extractMetadata(getFile("sonar-emma-plugin-0.3.jar"), false); + assertThat(metadata.getKey(), is("emma")); + assertThat(metadata.getBasePlugin(), nullValue()); + assertThat(metadata.getName(), is("Emma")); + } + + @Test + public void shouldExtractExtensionMetadata() { + DefaultPluginMetadata metadata = extractor.extractMetadata(getFile("sonar-checkstyle-extensions-plugin-0.1-SNAPSHOT.jar"), true); + assertThat(metadata.getKey(), is("checkstyleextensions")); + assertThat(metadata.getBasePlugin(), is("checkstyle")); + } + + @Test + public void shouldCopyAndExtractDependencies() throws IOException { + File toDir = new File("target/test-tmp/PluginInstallerTest/shouldCopyAndExtractDependencies"); + FileUtils.forceMkdir(toDir); + FileUtils.cleanDirectory(toDir); + + DefaultPluginMetadata metadata = extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, null, toDir); + + assertThat(metadata.getKey(), is("checkstyle")); + assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true)); + assertThat(new File(toDir, "META-INF/lib/checkstyle-5.1.jar").exists(), is(true)); + } + + @Test + public void shouldExtractOnlyDependencies() throws IOException { + File toDir = new File("target/test-tmp/PluginInstallerTest/shouldExtractOnlyDependencies"); + FileUtils.forceMkdir(toDir); + FileUtils.cleanDirectory(toDir); + + extractor.install(getFile("sonar-checkstyle-plugin-2.8.jar"), true, null, toDir); + + assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true)); + assertThat(new File(toDir, "META-INF/MANIFEST.MF").exists(), is(false)); + assertThat(new File(toDir, "org/sonar/plugins/checkstyle/CheckstyleVersion.class").exists(), is(false)); + } + + @Test + public void shouldCopyRuleExtensionsOnServerSide() throws IOException { + File toDir = new File("target/test-tmp/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide"); + FileUtils.forceMkdir(toDir); + FileUtils.cleanDirectory(toDir); + + DefaultPluginMetadata metadata = DefaultPluginMetadata.create(getFile("sonar-checkstyle-plugin-2.8.jar")) + .setKey("checkstyle") + .addDeprecatedExtension(getFile("PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml")); + extractor.install(metadata, toDir); + + assertThat(new File(toDir, "sonar-checkstyle-plugin-2.8.jar").exists(), is(true)); + assertThat(new File(toDir, "checkstyle-extension.xml").exists(), is(true)); + } + + private File getFile(String filename) { + return FileUtils.toFile(getClass().getResource("/org/sonar/core/plugins/" + filename)); + } +} diff --git a/sonar-core/src/test/resources/org/sonar/core/plugins/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml b/sonar-core/src/test/resources/org/sonar/core/plugins/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml deleted file mode 100644 index 75a263db3c3..00000000000 --- a/sonar-core/src/test/resources/org/sonar/core/plugins/PluginFileExtractorTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/sonar-core/src/test/resources/org/sonar/core/plugins/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml b/sonar-core/src/test/resources/org/sonar/core/plugins/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml new file mode 100644 index 00000000000..75a263db3c3 --- /dev/null +++ b/sonar-core/src/test/resources/org/sonar/core/plugins/PluginInstallerTest/shouldCopyRuleExtensionsOnServerSide/checkstyle-extension.xml @@ -0,0 +1 @@ + \ No newline at end of file -- cgit v1.2.3