Add SDKMAN to pipeline

Closes gh-11987
pull/24789/head
Madhura Bhave 4 years ago
parent 9763ba56ba
commit 7b38b0ed11

@ -0,0 +1,66 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.concourse.releasescripts.command;
import java.util.List;
import io.spring.concourse.releasescripts.ReleaseType;
import io.spring.concourse.releasescripts.sdkman.SdkmanService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
/**
* Command used to publish to SDKMAN.
*
* @author Madhura Bhave
*/
@Component
public class PublishToSdkmanCommand implements Command {
private static final Logger logger = LoggerFactory.getLogger(PublishToSdkmanCommand.class);
private final SdkmanService service;
public PublishToSdkmanCommand(SdkmanService service) {
this.service = service;
}
@Override
public void run(ApplicationArguments args) throws Exception {
logger.debug("Running 'push to SDKMAN' command");
List<String> nonOptionArgs = args.getNonOptionArgs();
Assert.state(!nonOptionArgs.isEmpty(), "No command argument specified");
Assert.state(nonOptionArgs.size() >= 3, "Release type or version not specified");
String releaseType = nonOptionArgs.get(1);
ReleaseType type = ReleaseType.from(releaseType);
if (!ReleaseType.RELEASE.equals(type)) {
return;
}
String version = nonOptionArgs.get(2);
boolean makeDefault = false;
if (nonOptionArgs.size() == 4) {
String releaseBranch = nonOptionArgs.get(3);
makeDefault = ("master".equals(releaseBranch));
}
this.service.publish(version, makeDefault);
}
}

@ -0,0 +1,49 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.concourse.releasescripts.sdkman;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for SDKMAN.
*
* @author Madhura Bhave
*/
@ConfigurationProperties(prefix = "sdkman")
public class SdkmanProperties {
private String consumerKey;
private String consumerToken;
public String getConsumerKey() {
return this.consumerKey;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public String getConsumerToken() {
return this.consumerToken;
}
public void setConsumerToken(String consumerToken) {
this.consumerToken = consumerToken;
}
}

@ -0,0 +1,141 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.concourse.releasescripts.sdkman;
import java.net.URI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
/**
* Central class for interacting with SDKMAN's API.
*
* @author Madhura Bhave
*/
@Component
public class SdkmanService {
private static final Logger logger = LoggerFactory.getLogger(SdkmanService.class);
private static final String SDKMAN_URL = "https://vendors.sdkman.io/";
private static final String DOWNLOAD_URL = "https://repo.spring.io/simple/libs-release-local/org/springframework/boot/spring-boot-cli/"
+ "%s/spring-boot-cli-%s-bin.zip";
private static final String SPRING_BOOT = "springboot";
private final RestTemplate restTemplate;
public SdkmanService(RestTemplateBuilder builder, SdkmanProperties properties) {
String consumerKey = properties.getConsumerKey();
String consumerToken = properties.getConsumerToken();
if (StringUtils.hasLength(consumerKey)) {
builder = builder.basicAuthentication(consumerKey, consumerToken);
}
this.restTemplate = builder.build();
}
public void publish(String version, boolean makeDefault) {
release(version);
if (makeDefault) {
makeDefault(version);
}
broadcast(version);
}
private void broadcast(String version) {
BroadcastRequest broadcastRequest = new BroadcastRequest(version);
RequestEntity<BroadcastRequest> broadcastEntity = RequestEntity.post(URI.create(SDKMAN_URL + "announce/struct"))
.contentType(MediaType.APPLICATION_JSON).body(broadcastRequest);
this.restTemplate.exchange(broadcastEntity, String.class);
logger.debug("Broadcast complete");
}
private void makeDefault(String version) {
logger.debug("Making this version the default");
Request request = new Request(version);
RequestEntity<Request> requestEntity = RequestEntity.post(URI.create(SDKMAN_URL + "default"))
.contentType(MediaType.APPLICATION_JSON).body(request);
this.restTemplate.exchange(requestEntity, String.class);
logger.debug("Make default complete");
}
private void release(String version) {
ReleaseRequest releaseRequest = new ReleaseRequest(version, String.format(DOWNLOAD_URL, version, version));
RequestEntity<ReleaseRequest> releaseEntity = RequestEntity.post(URI.create(SDKMAN_URL + "release"))
.contentType(MediaType.APPLICATION_JSON).body(releaseRequest);
this.restTemplate.exchange(releaseEntity, String.class);
logger.debug("Release complete");
}
static class Request {
private final String candidate = SPRING_BOOT;
private final String version;
Request(String version) {
this.version = version;
}
public String getCandidate() {
return this.candidate;
}
public String getVersion() {
return this.version;
}
}
static class ReleaseRequest extends Request {
private final String url;
ReleaseRequest(String version, String url) {
super(version);
this.url = url;
}
public String getUrl() {
return this.url;
}
}
static class BroadcastRequest extends Request {
private final String hashtag = SPRING_BOOT;
BroadcastRequest(String version) {
super(version);
}
public String getHashtag() {
return this.hashtag;
}
}
}

@ -0,0 +1,104 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.concourse.releasescripts.command;
import io.spring.concourse.releasescripts.sdkman.SdkmanService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.DefaultApplicationArguments;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link PublishToSdkmanCommand}.
*
* @author Madhura Bhave
*/
class PublishToSdkmanCommandTests {
@Mock
private SdkmanService service;
private PublishToSdkmanCommand command;
@BeforeEach
void setup() {
MockitoAnnotations.initMocks(this);
this.command = new PublishToSdkmanCommand(this.service);
}
@Test
void runWhenReleaseTypeNotSpecifiedShouldThrowException() throws Exception {
Assertions.assertThatIllegalStateException()
.isThrownBy(() -> this.command.run(new DefaultApplicationArguments("publishGradlePlugin")));
}
@Test
void runWhenVersionNotSpecifiedShouldThrowException() throws Exception {
Assertions.assertThatIllegalStateException()
.isThrownBy(() -> this.command.run(new DefaultApplicationArguments("publishGradlePlugin", "RELEASE")));
}
@Test
void runWhenReleaseTypeMilestoneShouldDoNothing() throws Exception {
this.command.run(new DefaultApplicationArguments("publishGradlePlugin", "M", "1.2.3"));
verifyNoInteractions(this.service);
}
@Test
void runWhenReleaseTypeRCShouldDoNothing() throws Exception {
this.command.run(new DefaultApplicationArguments("publishGradlePlugin", "RC", "1.2.3"));
verifyNoInteractions(this.service);
}
@Test
void runWhenBranchNotSpecifiedShouldCallServiceWithMakeDefaultFalse() throws Exception {
DefaultApplicationArguments args = new DefaultApplicationArguments("promote", "RELEASE", "1.2.3");
testRun(args, false);
}
@Test
void runWhenBranchNotMasterShouldCallServiceWithMakeDefaultFalse() throws Exception {
DefaultApplicationArguments args = new DefaultApplicationArguments("promote", "RELEASE", "1.2.3", "other");
testRun(args, false);
}
@Test
void runWhenReleaseTypeReleaseShouldCallService() throws Exception {
DefaultApplicationArguments args = new DefaultApplicationArguments("promote", "RELEASE", "1.2.3", "master");
testRun(args, true);
}
private void testRun(DefaultApplicationArguments args, boolean makeDefault) throws Exception {
ArgumentCaptor<String> versionCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Boolean> makeDefaultCaptor = ArgumentCaptor.forClass(Boolean.class);
this.command.run(args);
verify(this.service).publish(versionCaptor.capture(), makeDefaultCaptor.capture());
String version = versionCaptor.getValue();
Boolean makeDefaultValue = makeDefaultCaptor.getValue();
assertThat(version).isEqualTo("1.2.3");
assertThat(makeDefaultValue).isEqualTo(makeDefault);
}
}

@ -0,0 +1,90 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.concourse.releasescripts.sdkman;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.util.Base64Utils;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
/**
* Tests for {@link SdkmanService}.
*
* @author Madhura Bhave
*/
@EnableConfigurationProperties(SdkmanProperties.class)
@RestClientTest(SdkmanService.class)
class SdkmanServiceTests {
@Autowired
private SdkmanService service;
@Autowired
private SdkmanProperties properties;
@Autowired
private MockRestServiceServer server;
@AfterEach
void tearDown() {
this.server.reset();
}
@Test
void publishWhenMakeDefaultTrue() throws Exception {
setupExpectation("https://vendors.sdkman.io/release",
"{\"candidate\": \"springboot\", \"version\": \"1.2.3\", \"url\": \"https://repo.spring.io/simple/libs-release-local/org/springframework/boot/spring-boot-cli/1.2.3/spring-boot-cli-1.2.3-bin.zip\"}");
setupExpectation("https://vendors.sdkman.io/default",
"{\"candidate\": \"springboot\", \"version\": \"1.2.3\"}");
setupExpectation("https://vendors.sdkman.io/announce/struct",
"{\"candidate\": \"springboot\", \"version\": \"1.2.3\", \"hashtag\": \"springboot\"}");
this.service.publish("1.2.3", true);
this.server.verify();
}
@Test
void publishWhenMakeDefaultFalse() throws Exception {
setupExpectation("https://vendors.sdkman.io/release",
"{\"candidate\": \"springboot\", \"version\": \"1.2.3\", \"url\": \"https://repo.spring.io/simple/libs-release-local/org/springframework/boot/spring-boot-cli/1.2.3/spring-boot-cli-1.2.3-bin.zip\"}");
setupExpectation("https://vendors.sdkman.io/announce/struct",
"{\"candidate\": \"springboot\", \"version\": \"1.2.3\", \"hashtag\": \"springboot\"}");
this.service.publish("1.2.3", false);
this.server.verify();
}
private void setupExpectation(String url, String body) {
this.server.expect(requestTo(url)).andExpect(method(HttpMethod.POST)).andExpect(content().json(body))
.andExpect(header("Authorization",
"Basic " + Base64Utils.encodeToString(String
.format("%s:%s", this.properties.getConsumerKey(), this.properties.getConsumerToken())
.getBytes())))
.andExpect(header("Content-Type", MediaType.APPLICATION_JSON.toString())).andRespond(withSuccess());
}
}

@ -9,3 +9,6 @@ bintray:
sonatype:
user-token: sonatype-user
password-token: sonatype-password
sdkman:
consumer-key: sdkman-consumer-key
consumer-token: sdkman-consumer-token

@ -4,7 +4,7 @@ anchors:
username: ((github-username))
password: ((github-password))
branch: ((branch))
docker-resource-source: &docker-resource-source
docker-resource-source: &docker-resource-source
username: ((docker-hub-username))
password: ((docker-hub-password))
tag: ((milestone))
@ -42,6 +42,9 @@ anchors:
ARTIFACTORY_SERVER: ((artifactory-server))
ARTIFACTORY_USERNAME: ((artifactory-username))
ARTIFACTORY_PASSWORD: ((artifactory-password))
sdkman-task-params: &sdkman-task-params
SDKMAN_CONSUMER_KEY: ((sdkman-consumer-key))
SDKMAN_CONSUMER_TOKEN: ((sdkman-consumer-token))
artifactory-repo-put-params: &artifactory-repo-put-params
repo: libs-snapshot-local
folder: distribution-repository
@ -623,8 +626,10 @@ jobs:
file: git-repo/ci/tasks/promote.yml
params:
RELEASE_TYPE: RELEASE
BRANCH: ((BRANCH))
<<: *artifactory-task-params
<<: *bintray-task-params
<<: *sdkman-task-params
- name: sync-to-maven-central
serial: true
plan:

@ -11,5 +11,7 @@ java -jar /spring-boot-release-scripts.jar distribute $RELEASE_TYPE $BUILD_INFO_
java -jar /spring-boot-release-scripts.jar publishGradlePlugin $RELEASE_TYPE $BUILD_INFO_LOCATION || { exit 1; }
java -jar /spring-boot-release-scripts.jar publishToSdkman $RELEASE_TYPE $version $BRANCH || {exit 1;}
echo "Promotion complete"
echo $version > version/version

@ -14,5 +14,8 @@ params:
BINTRAY_REPO:
BINTRAY_USERNAME:
BINTRAY_API_KEY:
SDKMAN_CONSUMER_KEY:
SDKMAN_CONSUMER_TOKEN:
BRANCH:
run:
path: git-repo/ci/scripts/promote.sh

Loading…
Cancel
Save