Browse Source

SONAR-8458 add SonarQube buildNumber to plugin API

tags/6.3-RC1
Simon Brandhof 7 years ago
parent
commit
e1eaa719e9

+ 8
- 5
server/sonar-server/src/main/java/org/sonar/server/plugins/UpdateCenterMatrixFactory.java View File

@@ -20,7 +20,7 @@
package org.sonar.server.plugins;

import com.google.common.base.Optional;
import org.sonar.api.platform.Server;
import org.sonar.api.SonarRuntime;
import org.sonar.updatecenter.common.UpdateCenter;
import org.sonar.updatecenter.common.Version;

@@ -30,20 +30,23 @@ import org.sonar.updatecenter.common.Version;
public class UpdateCenterMatrixFactory {

private final UpdateCenterClient centerClient;
private final Version sonarVersion;
private final SonarRuntime sonarRuntime;
private final InstalledPluginReferentialFactory installedPluginReferentialFactory;

public UpdateCenterMatrixFactory(UpdateCenterClient centerClient, Server server,
public UpdateCenterMatrixFactory(UpdateCenterClient centerClient, SonarRuntime runtime,
InstalledPluginReferentialFactory installedPluginReferentialFactory) {
this.centerClient = centerClient;
this.sonarRuntime = runtime;
this.installedPluginReferentialFactory = installedPluginReferentialFactory;
this.sonarVersion = Version.create(server.getVersion());
}

public Optional<UpdateCenter> getUpdateCenter(boolean refreshUpdateCenter) {
Optional<UpdateCenter> updateCenter = centerClient.getUpdateCenter(refreshUpdateCenter);
if (updateCenter.isPresent()) {
return Optional.of(updateCenter.get().setInstalledSonarVersion(sonarVersion).registerInstalledPlugins(
org.sonar.api.utils.Version fullVersion = sonarRuntime.getApiVersion();
org.sonar.api.utils.Version semanticVersion = org.sonar.api.utils.Version.create(fullVersion.major(), fullVersion.minor(), fullVersion.patch());

return Optional.of(updateCenter.get().setInstalledSonarVersion(Version.create(semanticVersion.toString())).registerInstalledPlugins(
installedPluginReferentialFactory.getInstalledPluginReferential())
.setDate(centerClient.getLastRefreshDate()));
}

+ 10
- 15
server/sonar-server/src/test/filteredresources/org/sonar/server/plugins/ws/PluginsWsMediumTest/update-center.properties View File

@@ -1,20 +1,15 @@
# Current development version (single value)
devVersion=${project.version}
devVersion=6.3-SNAPSHOT

# List of all versions available in production update center. No need to sort nor to include "devVersion".
publicVersions=3.7.1

# Versions not published to production, usually when technical release is done but doc and annoucement are still pending. No need to include "devVersion"
#privateVersions=4.2
publicVersions=5.6

# Long Term Support release. Must be declared in "publicVersions".
ltsVersion=3.7.1
ltsVersion=5.6

# Describe each version listed in "publicVersions" and "privateVersions" : release date, URL to release notes, plain-text description, URL to ZIP distribution
3.7.1.date=2012-05-20
3.7.1.changelogUrl=http://jira.sonarsource.com/secure/ReleaseNote.jspa?projectId=11694&version=232323
3.7.1.description=Fix regressions
3.7.1.downloadUrl=http://dist.sonar.codehaus.org/sonar-3.7.1.zip
5.6.description=Long-Term Support version - Many bug fixes and small improvements
5.6.downloadUrl=https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-5.6.zip
5.6.changelogUrl=https://jira.sonarsource.com/jira/secure/ReleaseNote.jspa?projectId=10930&version=12869
5.6.date=2016-06-01

# list of plugins. It is used to load other files from the same directory. No need to sort.
plugins=foo,decoy
@@ -32,13 +27,13 @@ decoy.defaults.mavenArtifactId=sonar-decoy-plugin
# Metadata of each release
# The range of supported SQ versions accepts the alias LATEST and *
decoy.1.0.date=2012-03-18
decoy.1.0.sqVersions=${project.version}
decoy.1.0.sqVersions=6.2,6.3
decoy.1.0.description=Surprise
decoy.1.0.downloadUrl=[[decoy.10.jar]]
decoy.1.0.changelogUrl=http://jira.sonarsource.com/foo

decoy.1.1.date=2012-03-18
decoy.1.1.sqVersions=${project.version}
decoy.1.1.sqVersions=6.2,6.3
decoy.1.1.description=Surprise
decoy.1.1.downloadUrl=[[decoy.11.jar]]
decoy.1.1.changelogUrl=http://jira.sonarsource.com/foo
@@ -56,7 +51,7 @@ foo.defaults.mavenArtifactId=sonar-foo-plugin
# Metadata of each release
# The range of supported SQ versions accepts the alias LATEST and *
foo.1.0.date=2012-03-18
foo.1.0.sqVersions=${project.version}
foo.1.0.sqVersions=6.2,6.3
foo.1.0.description=Surprise
foo.1.0.downloadUrl=[[foo.10.jar]]
foo.1.0.changelogUrl=http://jira.sonarsource.com/foo

+ 4
- 4
server/sonar-server/src/test/java/org/sonar/server/plugins/UpdateCenterMatrixFactoryTest.java View File

@@ -21,7 +21,7 @@ package org.sonar.server.plugins;

import com.google.common.base.Optional;
import org.junit.Test;
import org.sonar.api.platform.Server;
import org.sonar.api.SonarRuntime;
import org.sonar.updatecenter.common.UpdateCenter;

import static org.assertj.guava.api.Assertions.assertThat;
@@ -31,14 +31,14 @@ import static org.mockito.Mockito.when;

public class UpdateCenterMatrixFactoryTest {

UpdateCenterMatrixFactory underTest;
private UpdateCenterMatrixFactory underTest;

@Test
public void return_absent_update_center() {
UpdateCenterClient updateCenterClient = mock(UpdateCenterClient.class);
when(updateCenterClient.getUpdateCenter(anyBoolean())).thenReturn(Optional.<UpdateCenter>absent());
when(updateCenterClient.getUpdateCenter(anyBoolean())).thenReturn(Optional.absent());

underTest = new UpdateCenterMatrixFactory(updateCenterClient, mock(Server.class), mock(InstalledPluginReferentialFactory.class));
underTest = new UpdateCenterMatrixFactory(updateCenterClient, mock(SonarRuntime.class), mock(InstalledPluginReferentialFactory.class));

Optional<UpdateCenter> updateCenter = underTest.getUpdateCenter(false);


+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginsWsMediumTest.java View File

@@ -67,7 +67,7 @@ public class PluginsWsMediumTest {
" {" +
" \"key\": \"foo\"," +
" \"release\": {" +
" \"version\": \"1.0\"," +
" \"version\": \"1.0\"" +
" }," +
" \"update\": {" +
" \"status\": \"COMPATIBLE\"," +

+ 1
- 1
sonar-plugin-api/pom.xml View File

@@ -217,7 +217,7 @@

<resources>
<resource>
<!-- Used to set SonarQube version in sq-version.txt file -->
<!-- Used to resolve variables in files sq-version.txt and sonar-api-version.txt -->
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>

+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/SonarProduct.java View File

@@ -26,6 +26,6 @@ package org.sonar.api;
public enum SonarProduct {

SONARQUBE,
SONARLINT;
SONARLINT

}

+ 6
- 3
sonar-plugin-api/src/main/java/org/sonar/api/SonarRuntime.java View File

@@ -144,9 +144,12 @@ import org.sonarsource.api.sonarlint.SonarLintSide;
public interface SonarRuntime {

/**
* Version of API (sonar-plugin-api artifact) at runtime.
* It can be helpful to call some API classes/methods without checking their availability at
* runtime by using reflection.
* Version of API (sonar-plugin-api artifact) at runtime.
* It can be helpful to call some API classes/methods without checking their availability at
* runtime by using reflection.
* <br/>
* Since 6.3, the returned version includes the build number in the fourth field, for
* example {@code "6.3.0.12345"}.
*/
Version getApiVersion();


+ 1
- 1
sonar-plugin-api/src/main/java/org/sonar/api/internal/ApiVersion.java View File

@@ -33,7 +33,7 @@ import org.sonar.api.utils.Version;
*/
public class ApiVersion {

private static final String FILE_PATH = "/sq-version.txt";
private static final String FILE_PATH = "/sonar-api-version.txt";

private ApiVersion() {
// only static methods

+ 2
- 3
sonar-plugin-api/src/main/java/org/sonar/api/internal/SonarRuntimeImpl.java View File

@@ -40,17 +40,16 @@ public class SonarRuntimeImpl implements SonarRuntime {
private final SonarQubeSide sonarQubeSide;

private SonarRuntimeImpl(Version version, SonarProduct product, @Nullable SonarQubeSide sonarQubeSide) {
requireNonNull(version);
requireNonNull(product);
checkArgument((product == SonarProduct.SONARQUBE) == (sonarQubeSide != null), "sonarQubeSide should be provided only for SonarQube product");
this.version = version;
this.version = requireNonNull(version);
this.product = product;
this.sonarQubeSide = sonarQubeSide;
}

@Override
public Version getApiVersion() {
return this.version;
return version;
}

@Override

+ 72
- 24
sonar-plugin-api/src/main/java/org/sonar/api/utils/Version.java View File

@@ -24,15 +24,16 @@ import java.util.List;
import javax.annotation.concurrent.Immutable;

import static java.lang.Integer.parseInt;
import static java.lang.Long.parseLong;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.substringAfter;
import static org.apache.commons.lang.StringUtils.substringBefore;
import static org.apache.commons.lang.StringUtils.trimToEmpty;

/**
* Version composed of 3 integer-sequences (major, minor and patch fields) and optionally a qualifier.
* Version composed of maximum four fields (major, minor, patch and build ID numbers) and optionally a qualifier.
* <p>
* Examples: 1.0, 1.0.0, 1.2.3, 1.2-beta1, 1.2.1-beta-1
* Examples: 1.0, 1.0.0, 1.2.3, 1.2-beta1, 1.2.1-beta-1, 1.2.3.4567
* <p>
* <h3>IMPORTANT NOTE</h3>
* Qualifier is ignored when comparing objects (methods {@link #equals(Object)}, {@link #hashCode()}
@@ -48,6 +49,9 @@ import static org.apache.commons.lang.StringUtils.trimToEmpty;
@Immutable
public class Version implements Comparable<Version> {

private static final long DEFAULT_BUILD_NUMBER = 0L;
private static final int DEFAULT_PATCH = 0;
private static final String DEFAULT_QUALIFIER = "";
private static final String QUALIFIER_SEPARATOR = "-";
private static final char SEQUENCE_SEPARATOR = '.';
private static final Splitter SEQUENCE_SPLITTER = Splitter.on(SEQUENCE_SEPARATOR);
@@ -55,14 +59,15 @@ public class Version implements Comparable<Version> {
private final int major;
private final int minor;
private final int patch;
private final long buildNumber;
private final String qualifier;

private Version(int major, int minor, int patch, String qualifier) {
requireNonNull(qualifier, "Version qualifier must not be null");
private Version(int major, int minor, int patch, long buildNumber, String qualifier) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.qualifier = qualifier;
this.buildNumber = buildNumber;
this.qualifier = requireNonNull(qualifier, "Version qualifier must not be null");
}

public int major() {
@@ -77,6 +82,15 @@ public class Version implements Comparable<Version> {
return patch;
}

/**
* Build number if the fourth field, for example {@code 12345} for "6.3.0.12345".
* If absent, then value is zero.
* @since 6.3
*/
public long buildNumber() {
return buildNumber;
}

/**
* @return non-null suffix. Empty if absent, else the suffix without the first character "-"
*/
@@ -93,11 +107,13 @@ public class Version implements Comparable<Version> {
* <li>1-beta-1</li>
* <li>1.2-beta-1</li>
* <li>1.2.3-beta-1</li>
* <li>1.2.3.4567</li>
* <li>1.2.3.4567-beta-1</li>
* </ul>
* Note that the optional qualifier is the part after the first "-".
*
* @throws IllegalArgumentException if parameter is badly formatted, for example
* if it defines 4 integer-sequences.
* if it defines 5 integer-sequences.
*/
public static Version parse(String text) {
String s = trimToEmpty(text);
@@ -105,43 +121,58 @@ public class Version implements Comparable<Version> {
if (!qualifier.isEmpty()) {
s = substringBefore(s, QUALIFIER_SEPARATOR);
}
List<String> split = SEQUENCE_SPLITTER.splitToList(s);
List<String> fields = SEQUENCE_SPLITTER.splitToList(s);
int major = 0;
int minor = 0;
int patch = 0;
int size = split.size();
int patch = DEFAULT_PATCH;
long buildNumber = DEFAULT_BUILD_NUMBER;
int size = fields.size();
if (size > 0) {
major = parseSequence(split.get(0));
major = parseFieldAsInt(fields.get(0));
if (size > 1) {
minor = parseSequence(split.get(1));
minor = parseFieldAsInt(fields.get(1));
if (size > 2) {
patch = parseSequence(split.get(2));
patch = parseFieldAsInt(fields.get(2));
if (size > 3) {
throw new IllegalArgumentException("Only 3 sequences are accepted");
buildNumber = parseFieldAsLong(fields.get(3));
if (size > 4) {
throw new IllegalArgumentException("Maximum 4 fields are accepted: " + text);
}
}
}
}
}
return new Version(major, minor, patch, qualifier);
return new Version(major, minor, patch, buildNumber, qualifier);
}

public static Version create(int major, int minor) {
return new Version(major, minor, 0, "");
return new Version(major, minor, DEFAULT_PATCH, DEFAULT_BUILD_NUMBER, DEFAULT_QUALIFIER);
}

public static Version create(int major, int minor, int patch) {
return new Version(major, minor, patch, "");
return new Version(major, minor, patch, DEFAULT_BUILD_NUMBER, DEFAULT_QUALIFIER);
}

/**
* @deprecated in 6.3 to avoid ambiguity with build number (see {@link #buildNumber()}
*/
@Deprecated
public static Version create(int major, int minor, int patch, String qualifier) {
return new Version(major, minor, patch, qualifier);
return new Version(major, minor, patch, DEFAULT_BUILD_NUMBER, qualifier);
}

private static int parseSequence(String sequence) {
if (sequence.isEmpty()) {
private static int parseFieldAsInt(String field) {
if (field.isEmpty()) {
return 0;
}
return parseInt(sequence);
return parseInt(field);
}

private static long parseFieldAsLong(String field) {
if (field.isEmpty()) {
return 0L;
}
return parseLong(field);
}

public boolean isGreaterThanOrEqual(Version than) {
@@ -153,11 +184,20 @@ public class Version implements Comparable<Version> {
if (this == o) {
return true;
}
if (!(o instanceof Version)) {
if (o == null || getClass() != o.getClass()) {
return false;
}
Version other = (Version) o;
return major == other.major && minor == other.minor && patch == other.patch;
Version version = (Version) o;
if (major != version.major) {
return false;
}
if (minor != version.minor) {
return false;
}
if (patch != version.patch) {
return false;
}
return buildNumber == version.buildNumber;
}

@Override
@@ -165,6 +205,7 @@ public class Version implements Comparable<Version> {
int result = major;
result = 31 * result + minor;
result = 31 * result + patch;
result = 31 * result + (int) (buildNumber ^ (buildNumber >>> 32));
return result;
}

@@ -175,6 +216,10 @@ public class Version implements Comparable<Version> {
c = minor - other.minor;
if (c == 0) {
c = patch - other.patch;
if (c == 0) {
long diff = buildNumber - other.buildNumber;
c = diff > 0 ? 1 : (diff < 0 ? -1 : 0);
}
}
}
return c;
@@ -184,8 +229,11 @@ public class Version implements Comparable<Version> {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(major).append(SEQUENCE_SEPARATOR).append(minor);
if (patch > 0) {
if (patch > 0 || buildNumber > 0) {
sb.append(SEQUENCE_SEPARATOR).append(patch);
if (buildNumber > 0) {
sb.append(SEQUENCE_SEPARATOR).append(buildNumber);
}
}
if (!qualifier.isEmpty()) {
sb.append(QUALIFIER_SEPARATOR).append(qualifier);

+ 1
- 0
sonar-plugin-api/src/main/resources/sonar-api-version.txt View File

@@ -0,0 +1 @@
${project.version}

+ 1
- 1
sonar-plugin-api/src/test/java/org/sonar/api/internal/ApiVersionTest.java View File

@@ -46,7 +46,7 @@ public class ApiVersionTest {
@Test
public void throw_ISE_if_fail_to_load_version() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Can not load /sq-version.txt from classpath");
expectedException.expectMessage("Can not load /sonar-api-version.txt from classpath");

System2 system = spy(System2.class);
when(system.getResource(anyString())).thenReturn(new File("target/unknown").toURI().toURL());

+ 36
- 15
sonar-plugin-api/src/test/java/org/sonar/api/utils/VersionTest.java View File

@@ -33,20 +33,23 @@ public class VersionTest {

@Test
public void test_parse() {
assertVersion(parse(""), 0, 0, 0, "");
assertVersion(parse("1"), 1, 0, 0, "");
assertVersion(parse("1.2"), 1, 2, 0, "");
assertVersion(parse("1.2.3"), 1, 2, 3, "");
assertVersion(parse("1.2-beta-1"), 1, 2, 0, "beta-1");
assertVersion(parse("1.2.3-beta1"), 1, 2, 3, "beta1");
assertVersion(parse("1.2.3-beta-1"), 1, 2, 3, "beta-1");
assertVersion(parse(""), 0, 0, 0, 0, "");
assertVersion(parse("1"), 1, 0, 0, 0, "");
assertVersion(parse("1.2"), 1, 2, 0, 0,"");
assertVersion(parse("1.2.3"), 1, 2, 3, 0,"");
assertVersion(parse("1.2-beta-1"), 1, 2, 0, 0,"beta-1");
assertVersion(parse("1.2.3-beta1"), 1, 2, 3, 0,"beta1");
assertVersion(parse("1.2.3-beta-1"), 1, 2, 3, 0,"beta-1");
assertVersion(parse("1.2.3.4567"), 1, 2, 3, 4567,"");
assertVersion(parse("1.2.3.4567-alpha"), 1, 2, 3, 4567,"alpha");
}

@Test
public void parse_throws_IAE_if_more_than_3_sequences() {
public void parse_throws_IAE_if_more_than_4_fields() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Only 3 sequences are accepted");
parse("1.2.3.4");
expectedException.expectMessage("Maximum 4 fields are accepted: 1.2.3.456.7");

parse("1.2.3.456.7");
}

@Test
@@ -83,6 +86,15 @@ public class VersionTest {
assertThat(parse("2.0").compareTo(parse("1.2"))).isGreaterThan(0);
}

@Test
public void compareTo_handles_build_number() {
assertThat(parse("1.2").compareTo(parse("1.2.0.0"))).isEqualTo(0);
assertThat(parse("1.2.3.1234").compareTo(parse("1.2.3.4567"))).isLessThan(0);
assertThat(parse("1.2.3.1234").compareTo(parse("1.2.3"))).isGreaterThan(0);
assertThat(parse("1.2.3.1234").compareTo(parse("1.2.4"))).isLessThan(0);
assertThat(parse("1.2.3.9999").compareTo(parse("1.2.4.1111"))).isLessThan(0);
}

@Test
public void qualifier_is_ignored_from_comparison() {
assertThat(parse("1.2.3")).isEqualTo(parse("1.2.3-build1"));
@@ -97,23 +109,32 @@ public class VersionTest {
assertThat(parse("1.2.3").toString()).isEqualTo("1.2.3");
assertThat(parse("1.2-b1").toString()).isEqualTo("1.2-b1");
assertThat(parse("1.2.3-b1").toString()).isEqualTo("1.2.3-b1");
assertThat(parse("1.2.3.4567").toString()).isEqualTo("1.2.3.4567");
assertThat(parse("1.2.3.4567-beta1").toString()).isEqualTo("1.2.3.4567-beta1");

// do not display zero numbers when possible
assertThat(parse("1.2.0.0").toString()).isEqualTo("1.2");
assertThat(parse("1.2.0.1").toString()).isEqualTo("1.2.0.1");
assertThat(parse("1.2.1.0").toString()).isEqualTo("1.2.1");
assertThat(parse("1.2.1.0-beta").toString()).isEqualTo("1.2.1-beta");
}

@Test
public void test_create() {
assertVersion(Version.create(1, 2), 1, 2, 0, "");
assertVersion(Version.create(1, 2, 3), 1, 2, 3, "");
assertVersion(Version.create(1, 2, 0, ""), 1, 2, 0, "");
assertVersion(Version.create(1, 2, 3, "build1"), 1, 2, 3, "build1");
assertVersion(Version.create(1, 2), 1, 2, 0, 0, "");
assertVersion(Version.create(1, 2, 3), 1, 2, 3, 0, "");
assertVersion(Version.create(1, 2, 0, ""), 1, 2, 0, 0, "");
assertVersion(Version.create(1, 2, 3, "build1"), 1, 2, 3, 0, "build1");
assertThat(Version.create(1, 2, 3, "build1").toString()).isEqualTo("1.2.3-build1");

}

private static void assertVersion(Version version,
int expectedMajor, int expectedMinor, int expectedPatch, String expectedQualifier) {
int expectedMajor, int expectedMinor, int expectedPatch, long expectedBuildNumber, String expectedQualifier) {
assertThat(version.major()).isEqualTo(expectedMajor);
assertThat(version.minor()).isEqualTo(expectedMinor);
assertThat(version.patch()).isEqualTo(expectedPatch);
assertThat(version.buildNumber()).isEqualTo(expectedBuildNumber);
assertThat(version.qualifier()).isEqualTo(expectedQualifier);
}
}

+ 1
- 26
sonar-scanner-engine/src/main/java/org/sonar/scanner/util/BatchUtils.java View File

@@ -20,24 +20,14 @@
package org.sonar.scanner.util;

import com.google.common.base.Strings;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BatchUtils {
private static final Logger LOG = LoggerFactory.getLogger(BatchUtils.class);

private BatchUtils() {
// prevent instantiation
}

/**
@@ -72,19 +62,4 @@ public class BatchUtils {

return o.getClass().getName();
}

@CheckForNull
public static String getServerVersion() {
InputStream is = BatchUtils.class.getResourceAsStream("/sq-version.txt");
if (is == null) {
LOG.warn("Failed to get SQ version");
return null;
}
try (BufferedReader br = IOUtils.toBufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
return br.readLine();
} catch (IOException e) {
LOG.warn("Failed to get SQ version", e);
return null;
}
}
}

Loading…
Cancel
Save