diff --git a/.github/send-notification/action.yml b/.github/send-notification/action.yml new file mode 100644 index 0000000..cf02fc5 --- /dev/null +++ b/.github/send-notification/action.yml @@ -0,0 +1,21 @@ +name: Send notification +description: Sends a Google Chat message as a notification of the job's outcome +inputs: + branch: + description: 'The branch on which the failure occurred' + required: true + failure-url: + description: 'The URL to use to link to the failure' + required: true + task: + description: 'The task that has failed' + required: true + webhook-url: + description: 'Google Chat Webhook URL' + required: true +runs: + using: composite + steps: + - shell: bash + run: | + curl -s -o /dev/null -X POST '${{ inputs.webhook-url }}' -H 'Content-Type: application/json' -d '{ text: " ${{ inputs.task }} <${{ inputs.failure-url }}|failed> on ${{ inputs.branch }} "}' || true diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/build.gradle b/gradle/plugins/lifecycle-smoke-test-ci-plugin/build.gradle new file mode 100644 index 0000000..9c52aad --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/build.gradle @@ -0,0 +1,25 @@ +plugins { + id "checkstyle" + id "io.spring.javaformat" version "0.0.41" + id "java-gradle-plugin" +} + +repositories { + mavenCentral() +} + +gradlePlugin { + plugins { + lifecycleSmokeTestCiPlugin { + id = "org.springframework.lifecycle.smoke-test-ci" + implementationClass = "org.springframework.lifecycle.gradle.LifecycleSmokeTestCiPlugin" + } + } +} + +dependencies { + checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:0.0.41") +} + +sourceCompatibility = "17" +targetCompatibility = "17" diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/config/checkstyle/checkstyle.xml b/gradle/plugins/lifecycle-smoke-test-ci-plugin/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000..f8fd854 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/config/checkstyle/checkstyle.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/settings.gradle b/gradle/plugins/lifecycle-smoke-test-ci-plugin/settings.gradle new file mode 100644 index 0000000..293155a --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/settings.gradle @@ -0,0 +1 @@ +rootProject.name="lifecycle-smoke-test-ci-plugin" diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/GenerateGitHubActionsWorkflows.java b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/GenerateGitHubActionsWorkflows.java new file mode 100644 index 0000000..e10a7c5 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/GenerateGitHubActionsWorkflows.java @@ -0,0 +1,143 @@ +/* + * Copyright 2022-2024 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.lifecycle.gradle; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; + +/** + * Task to generate the GitHub Actions workflows for the smoke tests. + * + * @author Andy Wilkinson + */ +public abstract class GenerateGitHubActionsWorkflows extends DefaultTask { + + private static final String GITHUB_REPOSITORY = "spring-projects/spring-lifecycle-smoke-tests"; + + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); + + @Input + public abstract ListProperty getSmokeTests(); + + @Input + public abstract Property getGitBranch(); + + @Input + public abstract Property getCronSchedule(); + + @Input + public abstract Property getSpringBootGeneration(); + + public GenerateGitHubActionsWorkflows() { + getGitBranch().convention(getSpringBootGeneration()); + getOutputDirectory().convention(getSpringBootGeneration() + .flatMap((generation) -> getProject().getLayout().getBuildDirectory().dir("workflows/" + generation))); + } + + @TaskAction + void generateWorkflows() { + getProject().delete(getOutputDirectory()); + getSmokeTests().get().forEach(this::generateWorkflow); + } + + void generateWorkflow(SmokeTest smokeTest) { + File workflowFile = getOutputDirectory() + .file(getSpringBootGeneration().get() + "-" + smokeTest.group() + "-" + smokeTest.name() + ".yml") + .get() + .getAsFile(); + workflowFile.getParentFile().mkdirs(); + String workflowName = getSpringBootGeneration().get() + " | " + name(smokeTest.group()) + " Smoke Tests | " + + name(smokeTest.name()); + try (PrintWriter writer = new PrintWriter(new FileWriter(workflowFile))) { + writer.println("name: " + workflowName); + writer.println("on:"); + writer.println(" schedule:"); + writer.println(" - cron : '" + getCronSchedule().get() + "'"); + writer.println(" workflow_dispatch:"); + writer.println("jobs:"); + if (smokeTest.tests()) { + writeJob(writer, smokeTest, "test"); + } + if (smokeTest.appTests()) { + writeJob(writer, smokeTest, "appTest"); + writeJob(writer, smokeTest, "checkpointRestoreAppTest"); + } + } + catch (IOException ex) { + throw new GradleException("Failed to write workflow file '" + workflowFile + "'", ex); + } + } + + private void writeJob(PrintWriter writer, SmokeTest smokeTest, String taskName) { + writer.println(" " + jobId(smokeTest.name(), taskName) + ":"); + writer.println(" name: " + name(smokeTest.name() + " " + taskName)); + writer.println(" uses: ./.github/workflows/smoke-test.yml"); + writer.println(" secrets: inherit"); + writer.println(" with:"); + writer.println(" checkout_repository: " + GITHUB_REPOSITORY); + writer.println(" checkout_ref: " + getGitBranch().get()); + writer.println(" project: " + smokeTest.group() + ":" + smokeTest.name()); + writer.println(" task: " + taskName); + } + + private String jobId(String smokeTestName, String taskName) { + StringBuilder output = new StringBuilder(); + output.append(smokeTestName.replace("-", "_")); + output.append("_"); + for (char c : taskName.toCharArray()) { + if (Character.isUpperCase(c)) { + output.append("_"); + output.append(Character.toLowerCase(c)); + } + else { + output.append(c); + } + } + return output.toString(); + } + + private String name(String input) { + StringBuilder output = new StringBuilder(input.length()); + char previous = ' '; + for (char c : input.replace("-", " ").toCharArray()) { + if (previous == ' ') { + output.append(Character.toUpperCase(c)); + } + else { + if (Character.isUpperCase(c)) { + output.append(' '); + } + output.append(c); + } + previous = c; + } + return output.toString(); + } + +} diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/LifecycleSmokeTestCiPlugin.java b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/LifecycleSmokeTestCiPlugin.java new file mode 100644 index 0000000..537b050 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/LifecycleSmokeTestCiPlugin.java @@ -0,0 +1,110 @@ +/* + * Copyright 2022-2024 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.lifecycle.gradle; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Stream; + +import org.gradle.api.GradleException; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.Exec; +import org.gradle.api.tasks.Sync; +import org.gradle.api.tasks.TaskProvider; + +/** + * {@link Plugin} for lifecycle smoke test CI. + * + * @author Andy Wilkinson + */ +public class LifecycleSmokeTestCiPlugin implements Plugin { + + @Override + public void apply(Project project) { + NamedDomainObjectContainer smokeTests = project.getObjects() + .domainObjectContainer(SmokeTests.class); + project.getExtensions().add("smokeTests", smokeTests); + TaskProvider syncWorkflows = project.getTasks().register("syncGitHubActionsWorkflows", Sync.class); + syncWorkflows.configure((sync) -> { + sync.into(".github/workflows"); + syncFromClasspath("smoke-test.yml", sync); + syncFromClasspath("validate-gradle-wrapper.yml", sync); + }); + smokeTests.configureEach((tests) -> { + TaskProvider describeSmokeTestsForBranch = project.getTasks() + .register("describeSmokeTestsFor" + tests.getName(), Exec.class); + describeSmokeTestsForBranch.configure((task) -> { + task.setWorkingDir(new File(tests.getLocation())); + task.commandLine("./gradlew", "describeSmokeTests", "--no-scan"); + }); + TaskProvider generateWorkflowsForBranch = project.getTasks() + .register("generateGitHubActionsWorkflowsFor" + tests.getName(), GenerateGitHubActionsWorkflows.class); + generateWorkflowsForBranch.configure((task) -> { + task.dependsOn(describeSmokeTestsForBranch); + task.getSpringBootGeneration().set(tests.getName()); + if (tests.getBranch() != null) { + task.getGitBranch().set(tests.getBranch()); + } + task.getSmokeTests().set(project.provider(() -> loadSmokeTests(tests.getLocation()))); + task.getCronSchedule().set(tests.getCronSchedule()); + }); + syncWorkflows.configure((sync) -> sync.from(generateWorkflowsForBranch)); + }); + TaskProvider updateStatusPage = project.getTasks() + .register("updateStatusPage", UpdateStatusPage.class); + updateStatusPage.configure((task) -> { + task.dependsOn(syncWorkflows); + Map> allSmokeTests = new LinkedHashMap<>(); + smokeTests.forEach((tests) -> { + List testsForGeneration = loadSmokeTests(tests.getLocation()); + allSmokeTests.put(tests.getName(), testsForGeneration); + }); + task.getSmokeTests().set(allSmokeTests); + task.getOutputFile().set(project.getLayout().getProjectDirectory().file("STATUS.adoc")); + }); + project.getTasks().register("updateInfrastructure", (task) -> task.dependsOn(syncWorkflows, updateStatusPage)); + } + + private List loadSmokeTests(String location) { + File[] smokeTests = new File(location + "/build/smoke-tests").listFiles(); + return Stream.of(smokeTests).map(this::load).map(SmokeTest::new).toList(); + } + + private Properties load(File file) { + Properties properties = new Properties(); + try (FileInputStream input = new FileInputStream(file)) { + properties.load(input); + return properties; + } + catch (IOException ex) { + throw new GradleException("Failed to load smoke test properties from '" + file + "'", ex); + } + } + + private void syncFromClasspath(String name, Sync sync) { + sync.from(sync.getProject().getResources().getText().fromUri(getClass().getClassLoader().getResource(name)), + (spec) -> spec.rename((temp) -> name)); + } + +} diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/SmokeTest.java b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/SmokeTest.java new file mode 100644 index 0000000..c87e583 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/SmokeTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2022-2024 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.lifecycle.gradle; + +import java.io.Serializable; +import java.util.Properties; + +/** + * A smoke test. + * + * @author Andy Wilkinson + * @param name name of the smoke test + * @param group group of the smoke test + * @param path path of the smoke test project + * @param tests whether the smoke test contains any unit tests + * @param appTests whether the smoke test contains any app tests + */ +public record SmokeTest(String name, String group, String path, boolean tests, + boolean appTests) implements Serializable { + + SmokeTest(Properties properties) { + this(properties.getProperty("name"), properties.getProperty("group"), properties.getProperty("path"), + Boolean.valueOf(properties.getProperty("tests")), Boolean.valueOf(properties.getProperty("appTests"))); + } + +} diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/SmokeTests.java b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/SmokeTests.java new file mode 100644 index 0000000..8421749 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/SmokeTests.java @@ -0,0 +1,66 @@ +/* + * Copyright 2022-2024 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.lifecycle.gradle; + +/** + * A branch's smoke tests. + * + * @author Andy Wilkinson + */ +public class SmokeTests { + + private final String name; + + private String branch; + + private String location; + + private String cronSchedule; + + public SmokeTests(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public String getBranch() { + return this.branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } + + public String getLocation() { + return this.location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getCronSchedule() { + return this.cronSchedule; + } + + public void setCronSchedule(String cronSchedule) { + this.cronSchedule = cronSchedule; + } + +} diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/UpdateStatusPage.java b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/UpdateStatusPage.java new file mode 100644 index 0000000..556150c --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/UpdateStatusPage.java @@ -0,0 +1,154 @@ +/* + * Copyright 2022-2024 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.lifecycle.gradle; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Predicate; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +/** + * Task to update the {@code STATUS.adoc} with the smoke tests. + * + * @author Stephane Nicoll + */ +public abstract class UpdateStatusPage extends DefaultTask { + + @OutputFile + public abstract RegularFileProperty getOutputFile(); + + @Input + public abstract MapProperty> getSmokeTests(); + + @TaskAction + void updateStatusPage() throws IOException { + List lines = new ArrayList<>(); + lines.add("= Smoke Tests Status"); + lines.add(":toc:"); + lines.add(""); + lines.add("Check each test for potential configuration guidance."); + lines.add(""); + getSmokeTests().get().forEach((name, tests) -> handleLocation(lines, name, tests)); + Files.write(getOutputFile().get().getAsFile().toPath(), lines); + } + + private void handleLocation(List content, String generation, List smokeTests) { + content.add(":toc-title: %s Projects".formatted(generation)); + content.add("== %s Projects".formatted(generation)); + content.add(""); + Map> groupedSmokeTests = new TreeMap<>(); + for (SmokeTest smokeTest : smokeTests) { + groupedSmokeTests + .computeIfAbsent(smokeTest.group(), (group) -> new TreeSet<>(Comparator.comparing(SmokeTest::name))) + .add(smokeTest); + } + groupedSmokeTests.forEach((group, tests) -> { + content.add("=== " + capitalize(group)); + content.add(""); + content.add("[%header,cols=\"2\"]"); + content.add("|==="); + content.add("h|Smoke Test"); + content.add("h|Status"); + content.add(""); + for (SmokeTest test : tests) { + String name = test.name(); + String workflowUrl = workflowUrl(generation, name); + content.add("|" + testUrl(group, name) + "[" + name + "]"); + content.add("| image:%s/badge.svg[\"Status\", link=\"%s\"]".formatted(workflowUrl, workflowUrl)); + content.add(""); + } + content.add("|==="); + content.add(""); + }); + } + + private String workflowUrl(String generation, String name) { + return "https://github.com/spring-projects/spring-lifecycle-smoke-tests/actions/workflows/%s-%s.yml" + .formatted(generation, name); + } + + private String capitalize(String input) { + StringBuffer buffer = new StringBuffer(input.length()); + for (char c : input.toCharArray()) { + buffer.append(buffer.isEmpty() ? Character.toUpperCase(c) : c); + } + return buffer.toString(); + } + + private String testUrl(String group, String name) { + return "https://github.com/spring-projects/spring-lifecycle-smoke-tests/tree/main/" + group + "/" + name; + } + + private enum TestType { + + APP_TEST(SmokeTest::appTests, "-app-test", "appTest"), + CR_APP_TEST(SmokeTest::appTests, "-cr-app-test", "checkpointRestoreAppTest"), + TEST(SmokeTest::tests, "-test", "test"); + + private final Predicate predicate; + + private final String urlSuffix; + + private final String taskName; + + TestType(Predicate predicate, String suffix, String taskName) { + this.predicate = predicate; + this.urlSuffix = suffix; + this.taskName = taskName; + } + + String badge(SmokeTest smokeTest) { + if (!this.predicate.test(smokeTest)) { + return ""; + } + return "image:" + badgeUrl(smokeTest.name(), this.urlSuffix) + "[link=" + + jobUrl(smokeTest.name(), this.urlSuffix) + "]"; + } + + String taskName() { + return this.taskName; + } + + private String badgeUrl(String name, String suffix) { + return workflowUrl(name) + "/badge.svg?branch=main"; + } + + private String jobUrl(String name, String suffix) { + return workflowUrl(name); + } + + private String workflowUrl(String name) { + return "https://github.com/spring-projects/spring-lifecycle-smoke-tests/workflows/3.3.x-%s.yml" + .formatted(name); + } + + } + +} diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/package-info.java b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/package-info.java new file mode 100644 index 0000000..69d8052 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/java/org/springframework/lifecycle/gradle/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2022-2024 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. + */ + +/** + * Gradle plugin for CI infrastructure of lifecycle smoke tests. + */ +package org.springframework.lifecycle.gradle; diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/resources/smoke-test.yml b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/resources/smoke-test.yml new file mode 100644 index 0000000..60da7e4 --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/resources/smoke-test.yml @@ -0,0 +1,63 @@ +name: Smoke Test +on: + workflow_call: + inputs: + project: + required: true + type: string + task: + required: true + type: string + checkout_repository: + required: true + type: string + checkout_ref: + required: true + type: string + expected_to_fail: + required: false + type: boolean + default: false +jobs: + smoke_test: + name: ${{ inputs.task }} + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v4 + with: + repository: ${{ inputs.checkout_repository }} + ref: ${{ inputs.checkout_ref }} + - name: Set up Java + uses: actions/setup-java@v3 + with: + java-version: '17' + java-package: 'jdk+crac' + distribution: 'zulu' + - name: Set up Gradle + uses: gradle/gradle-build-action@982da8e78c05368c70dac0351bb82647a9e9a5d2 + - name: Configure Gradle user.name + run: | + mkdir -p ~/.gradle + echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties + - name: Build + id: build + env: + GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }} + run: ./gradlew ${{ inputs.project }}:${{ inputs.task }} + continue-on-error: ${{ inputs.expected_to_fail }} + - name: Check out send notification action + uses: actions/checkout@v4 + if: ${{ failure() }} + with: + path: ci + ref: ci + sparse-checkout: .github/actions/send-notification + - name: Send notification + uses: ./ci/.github/actions/send-notification + if: ${{ failure() }} + with: + webhook-url: ${{ secrets.GOOGLE_CHAT_WEBHOOK_URL }} + task: ${{ inputs.project }}:${{ inputs.task }} + branch: ${{ inputs.checkout_ref }} + failure-url: ${{ steps.build.outputs.build-scan-url || format('{0}/{1}/actions/runs/{2}', github.server_url, github.repository, github.run_id) }} diff --git a/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/resources/validate-gradle-wrapper.yml b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/resources/validate-gradle-wrapper.yml new file mode 100644 index 0000000..95db47d --- /dev/null +++ b/gradle/plugins/lifecycle-smoke-test-ci-plugin/src/main/resources/validate-gradle-wrapper.yml @@ -0,0 +1,11 @@ +name: "Validate Gradle Wrapper" +on: [push, pull_request] +permissions: + contents: read +jobs: + validation: + name: "Validate Gradle Wrapper" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: gradle/actions/wrapper-validation@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 #v3.3.0 diff --git a/settings.gradle b/settings.gradle index e69de29..8b38059 100644 --- a/settings.gradle +++ b/settings.gradle @@ -0,0 +1,3 @@ +pluginManagement { + includeBuild "gradle/plugins/lifecycle-smoke-test-ci-plugin" +}