Browse Source

Add dispatchGitHubWorkflow gradle task

Steve Riesenberg 3 years ago
parent
commit
2c93a92afa

+ 7 - 0
build.gradle

@@ -69,6 +69,13 @@ tasks.named("createGitHubRelease") {
 	}
 }
 
+tasks.named("dispatchGitHubWorkflow") {
+	repository {
+		owner = "spring-projects"
+		name = "spring-security"
+	}
+}
+
 tasks.named("updateDependencies") {
 	// we aren't Gradle 7 compatible yet
 	checkForGradleUpdate = false

+ 79 - 0
buildSrc/src/main/java/org/springframework/gradle/github/release/DispatchGitHubWorkflowTask.java

@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2022 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 org.springframework.gradle.github.release;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.tasks.Input;
+import org.gradle.api.tasks.TaskAction;
+
+import org.springframework.gradle.github.RepositoryRef;
+
+/**
+ * @author Steve Riesenberg
+ */
+public class DispatchGitHubWorkflowTask extends DefaultTask {
+	@Input
+	private RepositoryRef repository = new RepositoryRef();
+
+	@Input
+	private String gitHubAccessToken;
+
+	@Input
+	private String branch;
+
+	@Input
+	private String workflowId;
+
+	@TaskAction
+	public void dispatchGitHubWorkflow() {
+		GitHubActionsApi gitHubActionsApi = new GitHubActionsApi(this.gitHubAccessToken);
+		WorkflowDispatch workflowDispatch = new WorkflowDispatch(this.branch, null);
+		gitHubActionsApi.dispatchWorkflow(this.repository, this.workflowId, workflowDispatch);
+	}
+
+	public RepositoryRef getRepository() {
+		return repository;
+	}
+
+	public void setRepository(RepositoryRef repository) {
+		this.repository = repository;
+	}
+
+	public String getGitHubAccessToken() {
+		return gitHubAccessToken;
+	}
+
+	public void setGitHubAccessToken(String gitHubAccessToken) {
+		this.gitHubAccessToken = gitHubAccessToken;
+	}
+
+	public String getBranch() {
+		return branch;
+	}
+
+	public void setBranch(String branch) {
+		this.branch = branch;
+	}
+
+	public String getWorkflowId() {
+		return workflowId;
+	}
+
+	public void setWorkflowId(String workflowId) {
+		this.workflowId = workflowId;
+	}
+}

+ 98 - 0
buildSrc/src/main/java/org/springframework/gradle/github/release/GitHubActionsApi.java

@@ -0,0 +1,98 @@
+/*
+ * Copyright 2002-2022 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 org.springframework.gradle.github.release;
+
+import java.io.IOException;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+import org.springframework.gradle.github.RepositoryRef;
+
+/**
+ * Manage GitHub Actions.
+ *
+ * @author Steve Riesenberg
+ */
+public class GitHubActionsApi {
+	private String baseUrl = "https://api.github.com";
+
+	private final OkHttpClient client;
+
+	private final Gson gson = new GsonBuilder().create();
+
+	public GitHubActionsApi() {
+		this.client = new OkHttpClient.Builder().build();
+	}
+
+	public GitHubActionsApi(String gitHubToken) {
+		this.client = new OkHttpClient.Builder()
+				.addInterceptor(new AuthorizationInterceptor(gitHubToken))
+				.build();
+	}
+
+	public void setBaseUrl(String baseUrl) {
+		this.baseUrl = baseUrl;
+	}
+
+	/**
+	 * Create a workflow dispatch event.
+	 *
+	 * @param repository The repository owner/name
+	 * @param workflowId The ID of the workflow or the name of the workflow file name
+	 * @param workflowDispatch The workflow dispatch containing a ref (branch) and optional inputs
+	 */
+	public void dispatchWorkflow(RepositoryRef repository, String workflowId, WorkflowDispatch workflowDispatch) {
+		String url = this.baseUrl + "/repos/" + repository.getOwner() + "/" + repository.getName() + "/actions/workflows/" + workflowId + "/dispatches";
+		String json = this.gson.toJson(workflowDispatch);
+		RequestBody body = RequestBody.create(MediaType.parse("application/json"), json);
+		Request request = new Request.Builder().url(url).post(body).build();
+		try {
+			Response response = this.client.newCall(request).execute();
+			if (!response.isSuccessful()) {
+				throw new RuntimeException(String.format("Could not create workflow dispatch %s for repository %s/%s. Got response %s",
+						workflowId, repository.getOwner(), repository.getName(), response));
+			}
+		} catch (IOException ex) {
+			throw new RuntimeException(String.format("Could not create workflow dispatch %s for repository %s/%s",
+					workflowId, repository.getOwner(), repository.getName()), ex);
+		}
+	}
+
+	private static class AuthorizationInterceptor implements Interceptor {
+		private final String token;
+
+		public AuthorizationInterceptor(String token) {
+			this.token = token;
+		}
+
+		@Override
+		public Response intercept(Chain chain) throws IOException {
+			Request request = chain.request().newBuilder()
+					.addHeader("Authorization", "Bearer " + this.token)
+					.build();
+
+			return chain.proceed(request);
+		}
+	}
+}

+ 21 - 16
buildSrc/src/main/java/org/springframework/gradle/github/release/GitHubReleasePlugin.java

@@ -17,7 +17,6 @@
 package org.springframework.gradle.github.release;
 
 import groovy.lang.MissingPropertyException;
-import org.gradle.api.Action;
 import org.gradle.api.Plugin;
 import org.gradle.api.Project;
 
@@ -27,23 +26,29 @@ import org.gradle.api.Project;
 public class GitHubReleasePlugin implements Plugin<Project> {
 	@Override
 	public void apply(Project project) {
-		project.getTasks().register("createGitHubRelease", CreateGitHubReleaseTask.class, new Action<CreateGitHubReleaseTask>() {
-			@Override
-			public void execute(CreateGitHubReleaseTask createGitHubRelease) {
-				createGitHubRelease.setGroup("Release");
-				createGitHubRelease.setDescription("Create a github release");
-				createGitHubRelease.dependsOn("generateChangelog");
+		project.getTasks().register("createGitHubRelease", CreateGitHubReleaseTask.class, (createGitHubRelease) -> {
+			createGitHubRelease.setGroup("Release");
+			createGitHubRelease.setDescription("Create a github release");
+			createGitHubRelease.dependsOn("generateChangelog");
 
-				createGitHubRelease.setCreateRelease("true".equals(project.findProperty("createRelease")));
-				createGitHubRelease.setVersion((String) project.findProperty("nextVersion"));
-				if (project.hasProperty("branch")) {
-					createGitHubRelease.setBranch((String) project.findProperty("branch"));
-				}
-				createGitHubRelease.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
-				if (createGitHubRelease.isCreateRelease() && createGitHubRelease.getGitHubAccessToken() == null) {
-					throw new MissingPropertyException("Please provide an access token with -PgitHubAccessToken=...");
-				}
+			createGitHubRelease.setCreateRelease("true".equals(project.findProperty("createRelease")));
+			createGitHubRelease.setVersion((String) project.findProperty("nextVersion"));
+			if (project.hasProperty("branch")) {
+				createGitHubRelease.setBranch((String) project.findProperty("branch"));
 			}
+			createGitHubRelease.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
+			if (createGitHubRelease.isCreateRelease() && createGitHubRelease.getGitHubAccessToken() == null) {
+				throw new MissingPropertyException("Please provide an access token with -PgitHubAccessToken=...");
+			}
+		});
+
+		project.getTasks().register("dispatchGitHubWorkflow", DispatchGitHubWorkflowTask.class, (dispatchGitHubWorkflow) -> {
+			dispatchGitHubWorkflow.setGroup("Release");
+			dispatchGitHubWorkflow.setDescription("Create a workflow_dispatch event on a given branch");
+
+			dispatchGitHubWorkflow.setBranch((String) project.findProperty("branch"));
+			dispatchGitHubWorkflow.setBranch((String) project.findProperty("workflowId"));
+			dispatchGitHubWorkflow.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
 		});
 	}
 }

+ 51 - 0
buildSrc/src/main/java/org/springframework/gradle/github/release/WorkflowDispatch.java

@@ -0,0 +1,51 @@
+/*
+ * Copyright 2002-2022 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 org.springframework.gradle.github.release;
+
+import java.util.Map;
+
+/**
+ * @author Steve Riesenberg
+ */
+public class WorkflowDispatch {
+	private String ref;
+	private Map<String, Object> inputs;
+
+	public WorkflowDispatch() {
+	}
+
+	public WorkflowDispatch(String ref, Map<String, Object> inputs) {
+		this.ref = ref;
+		this.inputs = inputs;
+	}
+
+	public String getRef() {
+		return ref;
+	}
+
+	public void setRef(String ref) {
+		this.ref = ref;
+	}
+
+	public Map<String, Object> getInputs() {
+		return inputs;
+	}
+
+	public void setInputs(Map<String, Object> inputs) {
+		this.inputs = inputs;
+	}
+}

+ 89 - 0
buildSrc/src/test/java/org/springframework/gradle/github/release/GitHubActionsApiTests.java

@@ -0,0 +1,89 @@
+/*
+ * Copyright 2002-2022 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 org.springframework.gradle.github.release;
+
+import java.nio.charset.Charset;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.gradle.github.RepositoryRef;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/**
+ * @author Steve Riesenberg
+ */
+public class GitHubActionsApiTests {
+	private GitHubActionsApi gitHubActionsApi;
+
+	private MockWebServer server;
+
+	private String baseUrl;
+
+	private RepositoryRef repository;
+
+	@BeforeEach
+	public void setup() throws Exception {
+		this.server = new MockWebServer();
+		this.server.start();
+		this.baseUrl = this.server.url("/api").toString();
+		this.gitHubActionsApi = new GitHubActionsApi("mock-oauth-token");
+		this.gitHubActionsApi.setBaseUrl(this.baseUrl);
+		this.repository = new RepositoryRef("spring-projects", "spring-security");
+	}
+
+	@AfterEach
+	public void cleanup() throws Exception {
+		this.server.shutdown();
+	}
+
+	@Test
+	public void dispatchWorkflowWhenValidParametersThenSuccess() throws Exception {
+		this.server.enqueue(new MockResponse().setResponseCode(204));
+
+		Map<String, Object> inputs = new LinkedHashMap<>();
+		inputs.put("input-1", "value");
+		inputs.put("input-2", false);
+		WorkflowDispatch workflowDispatch = new WorkflowDispatch("main", inputs);
+		this.gitHubActionsApi.dispatchWorkflow(this.repository, "test-workflow.yml", workflowDispatch);
+
+		RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
+		assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post");
+		assertThat(recordedRequest.getRequestUrl().toString())
+				.isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/actions/workflows/test-workflow.yml/dispatches");
+		assertThat(recordedRequest.getBody().readString(Charset.defaultCharset()))
+				.isEqualTo("{\"ref\":\"main\",\"inputs\":{\"input-1\":\"value\",\"input-2\":false}}");
+	}
+
+	@Test
+	public void dispatchWorkflowWhenErrorResponseThenException() throws Exception {
+		this.server.enqueue(new MockResponse().setResponseCode(400));
+
+		WorkflowDispatch workflowDispatch = new WorkflowDispatch("main", null);
+		assertThatExceptionOfType(RuntimeException.class)
+				.isThrownBy(() -> this.gitHubActionsApi.dispatchWorkflow(this.repository, "test-workflow.yml", workflowDispatch));
+	}
+}

+ 15 - 11
buildSrc/src/test/java/org/springframework/gradle/github/release/GitHubReleaseApiTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,14 +16,15 @@
 
 package org.springframework.gradle.github.release;
 
+import java.nio.charset.Charset;
 import java.util.concurrent.TimeUnit;
 
 import okhttp3.mockwebserver.MockResponse;
 import okhttp3.mockwebserver.MockWebServer;
 import okhttp3.mockwebserver.RecordedRequest;
-import org.junit.Test;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import org.springframework.gradle.github.RepositoryRef;
 
@@ -34,21 +35,22 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
  * @author Steve Riesenberg
  */
 public class GitHubReleaseApiTests {
-	private GitHubReleaseApi github;
-
-	private RepositoryRef repository = new RepositoryRef("spring-projects", "spring-security");
+	private GitHubReleaseApi gitHubReleaseApi;
 
 	private MockWebServer server;
 
 	private String baseUrl;
 
+	private RepositoryRef repository;
+
 	@BeforeEach
 	public void setup() throws Exception {
 		this.server = new MockWebServer();
 		this.server.start();
-		this.github = new GitHubReleaseApi("mock-oauth-token");
 		this.baseUrl = this.server.url("/api").toString();
-		this.github.setBaseUrl(this.baseUrl);
+		this.gitHubReleaseApi = new GitHubReleaseApi("mock-oauth-token");
+		this.gitHubReleaseApi.setBaseUrl(this.baseUrl);
+		this.repository = new RepositoryRef("spring-projects", "spring-security");
 	}
 
 	@AfterEach
@@ -134,18 +136,20 @@ public class GitHubReleaseApiTests {
 				"  ]\n" +
 				"}";
 		this.server.enqueue(new MockResponse().setBody(responseJson));
-		this.github.publishRelease(this.repository, Release.tag("1.0.0").build());
+		this.gitHubReleaseApi.publishRelease(this.repository, Release.tag("1.0.0").build());
 
 		RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
 		assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post");
-		assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/releases");
-		assertThat(recordedRequest.getBody().toString()).isEqualTo("{\"tag_name\":\"1.0.0\"}");
+		assertThat(recordedRequest.getRequestUrl().toString())
+				.isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/releases");
+		assertThat(recordedRequest.getBody().readString(Charset.defaultCharset()))
+				.isEqualTo("{\"tag_name\":\"1.0.0\",\"draft\":false,\"prerelease\":false,\"generate_release_notes\":false}");
 	}
 
 	@Test
 	public void publishReleaseWhenErrorResponseThenException() throws Exception {
 		this.server.enqueue(new MockResponse().setResponseCode(400));
 		assertThatExceptionOfType(RuntimeException.class)
-				.isThrownBy(() -> this.github.publishRelease(this.repository, Release.tag("1.0.0").build()));
+				.isThrownBy(() -> this.gitHubReleaseApi.publishRelease(this.repository, Release.tag("1.0.0").build()));
 	}
 }