diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index 18cd48f7ead..a8cde728b22 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -111,6 +111,26 @@ jobs: export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" ./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace + check_samples: + name: Check Samples project + needs: [build_jdk_11] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: '11' + - name: Setup gradle user name + run: | + mkdir -p ~/.gradle + echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties + - name: Check samples project + run: | + export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER" + export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD" + export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY" + ./gradlew checkSamples --stacktrace deploy_artifacts: name: Deploy Artifacts needs: [build_jdk_11, snapshot_tests, sonar_analysis] diff --git a/build.gradle b/build.gradle index 36caef2fafa..95dd038fbb0 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ buildscript { apply plugin: 'io.spring.nohttp' apply plugin: 'locks' apply plugin: 'io.spring.convention.root' +apply plugin: 'io.spring.convention.include-check-remote' apply plugin: 'org.jetbrains.kotlin.jvm' apply plugin: 'org.springframework.security.update-dependencies' apply plugin: 'org.springframework.security.sagan' @@ -146,3 +147,11 @@ nohttp { source.exclude "buildSrc/build/**" } + +tasks.register('checkSamples') { + includeCheckRemote { + repository = 'spring-projects/spring-security-samples' + ref = samplesBranch + } + dependsOn checkRemote +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/IncludeRepoTask.groovy b/buildSrc/src/main/groovy/io/spring/gradle/IncludeRepoTask.groovy new file mode 100644 index 00000000000..b549ff73099 --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/IncludeRepoTask.groovy @@ -0,0 +1,104 @@ +/* + * Copyright 2002-2021 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.gradle + +import groovy.transform.CompileStatic +import groovy.transform.TypeChecked +import groovy.transform.TypeCheckingMode +import org.gradle.api.DefaultTask +import org.gradle.api.Task +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction + +/** + * Checkout a project template from a git repository. + * + * @author Marcus Da Coregio + */ +@CompileStatic +abstract class IncludeRepoTask extends DefaultTask { + + private static final String DEFAULT_URI_PREFIX = 'https://github.com/' + + /** + * Git repository to use. Will be prefixed with {@link #DEFAULT_URI_PREFIX} if it isn't already + * @return + */ + @Input + abstract Property getRepository(); + + /** + * Git reference to use. + */ + @Input + abstract Property getRef() + + /** + * Directory where the project template should be copied. + */ + @OutputDirectory + final File outputDirectory = project.file("$project.buildDir/$name") + + @TaskAction + void checkoutAndCopy() { + outputDirectory.deleteDir() + File checkoutDir = checkout(this, getRemoteUri(), ref.get()) + moveToOutputDir(checkoutDir, outputDirectory) + } + + private static File cleanTemporaryDir(Task task, File tmpDir) { + if (tmpDir.exists()) { + task.project.delete(tmpDir) + } + return tmpDir + } + + static File checkout(Task task, String remoteUri, String ref) { + checkout(task, remoteUri, ref, task.getTemporaryDir()) + } + + @TypeChecked(TypeCheckingMode.SKIP) + static File checkout(Task task, String remoteUri, String ref, File checkoutDir) { + cleanTemporaryDir(task, checkoutDir) + task.project.exec { + commandLine = ["git", "clone", "--no-checkout", remoteUri, checkoutDir.absolutePath] + errorOutput = System.err + } + task.project.exec { + commandLine = ["git", "checkout", ref] + workingDir = checkoutDir + errorOutput = System.err + } + return checkoutDir + } + + private static void moveToOutputDir(File tmpDir, File outputDirectory) { + File baseDir = tmpDir + baseDir.renameTo(outputDirectory) + } + + private String getRemoteUri() { + String remoteUri = this.repository.get() + if (remoteUri.startsWith(DEFAULT_URI_PREFIX)) { + return remoteUri + } + return DEFAULT_URI_PREFIX + remoteUri + } +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/IncludeCheckRemotePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/IncludeCheckRemotePlugin.groovy new file mode 100644 index 00000000000..3238bb3498a --- /dev/null +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/IncludeCheckRemotePlugin.groovy @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2021 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.gradle.convention + +import io.spring.gradle.IncludeRepoTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.provider.Property +import org.gradle.api.tasks.GradleBuild +import org.gradle.api.tasks.TaskProvider + +/** + * Adds a set of tasks that make easy to clone a remote repository and perform some task + * + * @author Marcus Da Coregio + */ +class IncludeCheckRemotePlugin implements Plugin { + @Override + void apply(Project project) { + IncludeCheckRemoteExtension extension = project.extensions.create('includeCheckRemote', IncludeCheckRemoteExtension) + TaskProvider includeRepoTask = project.tasks.register('includeRepo', IncludeRepoTask) { IncludeRepoTask it -> + it.repository = extension.repository.get() + it.ref = extension.ref.get() + } + project.tasks.register('checkRemote', GradleBuild) { + it.dependsOn 'includeRepo' + it.dir = includeRepoTask.get().outputDirectory + it.tasks = extension.getTasks() + } + } + + abstract static class IncludeCheckRemoteExtension { + /** + * Git repository to clone + */ + abstract Property getRepository(); + /** + * Git ref to checkout + */ + abstract Property getRef(); + /** + * Task to run in the repository + */ + List tasks = ['check'] + + void setTask(List tasks) { + this.tasks = tasks + } + } + +} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.include-check-remote.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.include-check-remote.properties new file mode 100644 index 00000000000..298e1bbad2d --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/io.spring.convention.include-check-remote.properties @@ -0,0 +1 @@ +implementation-class=io.spring.gradle.convention.IncludeCheckRemotePlugin diff --git a/buildSrc/src/test/java/io/spring/gradle/convention/IncludeCheckRemotePluginTest.java b/buildSrc/src/test/java/io/spring/gradle/convention/IncludeCheckRemotePluginTest.java new file mode 100644 index 00000000000..be15a477bdf --- /dev/null +++ b/buildSrc/src/test/java/io/spring/gradle/convention/IncludeCheckRemotePluginTest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2021 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.gradle.convention; + +import io.spring.gradle.IncludeRepoTask; +import org.apache.commons.io.FileUtils; +import org.gradle.api.Project; +import org.gradle.api.tasks.GradleBuild; +import org.gradle.testfixtures.ProjectBuilder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.assertThat; + +class IncludeCheckRemotePluginTest { + + Project rootProject; + + @AfterEach + public void cleanup() throws Exception { + if (rootProject != null) { + FileUtils.deleteDirectory(rootProject.getProjectDir()); + } + } + + @Test + void applyWhenExtensionPropertiesNoTasksThenCreateCheckRemoteTaskWithDefaultTask() { + this.rootProject = ProjectBuilder.builder().build(); + this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class); + this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class, + (includeCheckRemoteExtension) -> { + includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository"); + includeCheckRemoteExtension.setProperty("ref", "main"); + }); + + GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get(); + assertThat(checkRemote.getTasks()).containsExactly("check"); + } + + @Test + void applyWhenExtensionPropertiesTasksThenCreateCheckRemoteWithProvidedTasks() { + this.rootProject = ProjectBuilder.builder().build(); + this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class); + this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class, + (includeCheckRemoteExtension) -> { + includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository"); + includeCheckRemoteExtension.setProperty("ref", "main"); + includeCheckRemoteExtension.setProperty("tasks", Arrays.asList("clean", "build", "test")); + }); + + GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get(); + assertThat(checkRemote.getTasks()).containsExactly("clean", "build", "test"); + } + + @Test + void applyWhenExtensionPropertiesThenRegisterIncludeRepoTaskWithExtensionProperties() { + this.rootProject = ProjectBuilder.builder().build(); + this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class); + this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class, + (includeCheckRemoteExtension) -> { + includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository"); + includeCheckRemoteExtension.setProperty("ref", "main"); + }); + + IncludeRepoTask includeRepo = (IncludeRepoTask) this.rootProject.getTasks().named("includeRepo").get(); + assertThat(includeRepo).isNotNull(); + assertThat(includeRepo.getRepository().get()).isEqualTo("my-project/my-repository"); + assertThat(includeRepo.getRef().get()).isEqualTo("main"); + } + + @Test + void applyWhenRegisterTasksThenCheckRemoteDirSameAsIncludeRepoOutputDir() { + this.rootProject = ProjectBuilder.builder().build(); + this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class); + this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class, + (includeCheckRemoteExtension) -> { + includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository"); + includeCheckRemoteExtension.setProperty("ref", "main"); + }); + IncludeRepoTask includeRepo = (IncludeRepoTask) this.rootProject.getTasks().named("includeRepo").get(); + GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get(); + assertThat(checkRemote.getDir()).isEqualTo(includeRepo.getOutputDirectory()); + } +}