Skip to content

Commit

Permalink
Add support for LATEST_RELEASE (#182)
Browse files Browse the repository at this point in the history
  • Loading branch information
costin-zaharia-sonarsource authored Dec 8, 2023
1 parent 617459b commit 52f2245
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@
package com.sonar.orchestrator.build;

import com.sonar.orchestrator.config.Configuration;
import com.sonar.orchestrator.locator.GitHub;
import com.sonar.orchestrator.locator.GitHubImpl;
import com.sonar.orchestrator.locator.Location;
import com.sonar.orchestrator.version.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.Map;
import javax.annotation.CheckForNull;
Expand All @@ -42,7 +47,10 @@ public class ScannerForMSBuild extends Build<ScannerForMSBuild> {
static final int DOT_NET_CORE_INTRODUCTION_MAJOR_VERSION = 4;
static final int DOT_NET_CORE_INTRODUCTION_MINOR_VERSION = 1;
public static final String DOT_NET_CORE_INTRODUCTION_VERSION = DOT_NET_CORE_INTRODUCTION_MAJOR_VERSION + "." + DOT_NET_CORE_INTRODUCTION_MINOR_VERSION;
public static final String LATEST_RELEASE = "LATEST_RELEASE";
private static final Logger LOG = LoggerFactory.getLogger(ScannerForMSBuild.class);

private final GitHub gitHub;
private Version scannerVersion = null;
private File projectDir;
private File dotNetCoreExecutable = null;
Expand All @@ -54,6 +62,11 @@ public class ScannerForMSBuild extends Build<ScannerForMSBuild> {
private Location location;

ScannerForMSBuild() {
this(new GitHubImpl());
}

ScannerForMSBuild(GitHub gitHub) {
this.gitHub = gitHub;
}

@CheckForNull
Expand Down Expand Up @@ -121,6 +134,10 @@ public boolean isDebugLogs() {

public ScannerForMSBuild setScannerVersion(String s) {
checkArgument(!isEmpty(s), "version must be set");
if (s.equals(LATEST_RELEASE)) {
s = gitHub.getLatestScannerReleaseVersion();
}
LOG.info("Setting the scanner version to {}", s);
this.scannerVersion = Version.create(s);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,3 @@
package com.sonar.orchestrator.build.dotnet.scanner;

import javax.annotation.ParametersAreNonnullByDefault;

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Orchestrator
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package com.sonar.orchestrator.locator;

public interface GitHub {
String getLatestScannerReleaseVersion();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Orchestrator
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package com.sonar.orchestrator.locator;

import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonValue;
import com.sonar.orchestrator.http.HttpCall;
import com.sonar.orchestrator.http.HttpClientFactory;
import okhttp3.HttpUrl;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

public class GitHubImpl implements GitHub {
private static final Logger LOG = LoggerFactory.getLogger(GitHubImpl.class);
private static final Object cacheLock = new Object();
private static volatile String cachedVersion;
private final String baseUrl;

public GitHubImpl() {
this("https://api.github.com");
}

GitHubImpl(String baseUrl) {
this.baseUrl = baseUrl;
}

@Override
public synchronized String getLatestScannerReleaseVersion() {
// GitHub API has a rate limit of 60 requests per hour for unauthenticated users.
// To avoid reaching that limit when running integration tests, the returned value needs to be cached.
synchronized (cacheLock) {
if (cachedVersion != null) {
return cachedVersion;
}
}
LOG.info("Retrieving the latest scanner release.");
HttpUrl url = HttpUrl
.parse(baseUrl)
.newBuilder()
.addPathSegments("repos/SonarSource/sonar-scanner-msbuild/releases/latest")
.build();

HttpCall call = HttpClientFactory
.create()
.newCall(url)
// GitHub recommendation: https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#get-the-latest-release
.setHeader("Accept","application/vnd.github+json")
.setHeader("X-GitHub-Api-Version", "2022-11-28");

try {
File jsonFile = new File("scanner-latest-release.json");

LOG.info("Downloading {}", url);
call.downloadToFile(jsonFile);
LOG.info("SonarScanner for .Net latest release details downloaded to {}", jsonFile);

String jsonContent = FileUtils.readFileToString(jsonFile, StandardCharsets.UTF_8);
JsonValue response = Json.parse(jsonContent);
Files.delete(jsonFile.toPath());
cachedVersion = response.asObject().getString("tag_name", null);
return cachedVersion;
} catch (Exception exception) {
throw new IllegalStateException("Fail to download the latest release details for SonarScanner for .Net", exception);
}
}

public static synchronized void resetCache() {
synchronized (cacheLock) {
cachedVersion = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public boolean isSnapshot() {
return asString.endsWith("-SNAPSHOT");
}

static class VersionParsingException extends RuntimeException {
public static class VersionParsingException extends RuntimeException {
private static final String PARSE_ERR_MSG = "Version string cannot be parsed.";

VersionParsingException() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,19 @@
package com.sonar.orchestrator.build;

import com.sonar.orchestrator.locator.FileLocation;
import com.sonar.orchestrator.locator.GitHub;
import com.sonar.orchestrator.version.Version;
import java.io.File;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ScannerForMSBuildTest {

Expand Down Expand Up @@ -100,4 +105,27 @@ public void test_dotnet_core_checks_version_if_set_before() {

build.check();
}

@Test
public void test_setScannerVersion_with_specific_version() {
String version = "6.0.0.0";
ScannerForMSBuild build = ScannerForMSBuild.create().setScannerVersion(version);
assertThat(build.scannerVersion()).isEqualTo(Version.create(version));
}

@Test
public void test_setScannerVersion_with_latest_version() {
String version = "6.0.0.0";
GitHub gitHub = mock(GitHub.class);
when(gitHub.getLatestScannerReleaseVersion()).thenReturn(version);
ScannerForMSBuild sut = new ScannerForMSBuild(gitHub);
ScannerForMSBuild build = sut.setScannerVersion(ScannerForMSBuild.LATEST_RELEASE);
assertThat(build.scannerVersion()).isEqualTo(Version.create(version));
}

@Test
public void test_setScannerVersion_with_wrong_version() {
ScannerForMSBuild build = ScannerForMSBuild.create();
assertThatThrownBy(() -> build.setScannerVersion("wrong version")).isInstanceOf(Version.VersionParsingException.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Orchestrator
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package com.sonar.orchestrator.locator;

import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class GitHubImplTest {
@Rule
public MockWebServer server = new MockWebServer();

@Test
public void getLatestScannerReleaseVersion_returns_valid_version() {
prepareResponse("1.2.3.4");

GitHubImpl sut = new GitHubImpl(server.url("/").toString());
String latestVersion = sut.getLatestScannerReleaseVersion();
assertThat(latestVersion).isEqualTo("1.2.3.4");
}

@Test
public void getLatestScannerReleaseVersion_two_calls_make_a_single_request() {
prepareResponse("version");

GitHubImpl sut = new GitHubImpl(server.url("/").toString());
String first = sut.getLatestScannerReleaseVersion();
assertThat(first).isEqualTo("version");
String second = sut.getLatestScannerReleaseVersion();
assertThat(second).isEqualTo("version");
assertThat(server.getRequestCount()).isEqualTo(1);
}

@After
public void resetCache() {
GitHubImpl.resetCache();
}

private void prepareResponse(String version) {
server.enqueue(new MockResponse().setBody("{ \"tag_name\": \"" + version + "\" }"));
}
}

0 comments on commit 52f2245

Please sign in to comment.