diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 8eaa73a20..0cb9147bc 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -40,7 +40,7 @@ Jenkins workflow regression tests typically output a .txt file into [tests/jenki For example, [TestHello.groovy](tests/jenkins/TestHello.groovy) executes [Hello_Jenkinsfile](tests/jenkins/jobs/Hello_Jenkinsfile) and outputs [Hello_Jenkinsfile.txt](tests/jenkins/jobs/Hello_Jenkinsfile.txt). If the job execution changes, the regression test will fail. -- To update the recorded .txt file run `./gradlew test -info -Ppipeline.stack.write=true` or update its value in [gradle.properties](gradle.properties). +- To update the recorded .txt file run `./gradlew test -info -Dpipeline.stack.write=true` or update its value in [gradle.properties](gradle.properties). - To run a specific test case, run `./gradlew test -info --tests=TestCaseClassName` diff --git a/README.md b/README.md index 78d4abe67..19cfe2168 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,10 @@ lib = library(identifier: 'jenkins@', retriever: modernSCM([ | [publishToNuget.groovy](./vars/publishToNuget.groovy) | A library to build, sign and publish dotnet artifacts to [Nuget Gallery](https://www.nuget.org/). Please check if the [default docker](https://github.com/opensearch-project/opensearch-build/blob/main/docker/ci/dockerfiles/current/release.centos.clients.x64.arm64.dockerfile) file contains the required dotnet sdk. You can use [PublishToNugetLibTester](./tests/jenkins/lib-testers/PublishToNugetLibTester.groovy) to add tests in your repository. See how to use the lib in your [jenkinsFile](./tests/jenkins/jobs/PublishToNuget_Jenkinsfile). | [publishToArtifactsProdBucket.groovy](./vars/publishToArtifactsProdBucket.groovy) | This library signs and uploads the artifacts to production S3 bucket which points to artifacts.opensearch.org. Please make sure the role that you use to upload exists and has the right permission. For artifacts of different types like macos, linux and windows, call this lib for each artifact with different signing parameters. You can use [PublishToArtifactsProdBucketLibTester](./tests/jenkins/lib-testers/PublishToArtifactsProdBucketLibTester.groovy) to add tests in your repository. See how to use the lib in your [jenkinsFile](./tests/jenkins/jobs/PublishToArtifactsProdBucket_Jenkinsfile). +| [buildMessage.groovy](./vars/buildMessage.groovy) | This library that can parse the jenkins build log based on the user defined input query string. +| [closeBuildSuccessGithubIssue.groovy](./vars/closeBuildSuccessGithubIssue.groovy) | This library that identifies the successfully built components and closes the created [AUTOCUT] issues. +| [createGithubIssue.groovy](./vars/createGithubIssue.groovy) | This library that identifies the failed components and creates the [AUTOCUT] issues. + ## Contributing See [developer guide](DEVELOPER_GUIDE.md) and [how to contribute to this project](CONTRIBUTING.md). diff --git a/build.gradle b/build.gradle index 3a9d1f6dd..6f02cd940 100644 --- a/build.gradle +++ b/build.gradle @@ -120,7 +120,7 @@ jacocoTestReport { } } -String version = '5.3.0' +String version = '5.4.0' task updateVersion { doLast { diff --git a/tests/jenkins/TestBuildFailureMessage.groovy b/tests/jenkins/TestBuildMessage.groovy similarity index 73% rename from tests/jenkins/TestBuildFailureMessage.groovy rename to tests/jenkins/TestBuildMessage.groovy index 9edee45c7..d5e4fd200 100644 --- a/tests/jenkins/TestBuildFailureMessage.groovy +++ b/tests/jenkins/TestBuildMessage.groovy @@ -11,12 +11,12 @@ package jenkins.tests import jenkins.tests.BuildPipelineTest import org.junit.* -class TestBuildFailureMessage extends BuildPipelineTest { +class TestBuildMessage extends BuildPipelineTest { @Before void setUp() { - this.registerLibTester(new BuildFailureMessageLibTester()) + this.registerLibTester(new BuildMessageLibTester('pass')) super.setUp() def currentBuild = binding.getVariable('currentBuild') binding.setVariable("currentBuild", currentBuild) @@ -24,6 +24,6 @@ class TestBuildFailureMessage extends BuildPipelineTest { @Test void testBuildFailureMsg() { - super.testPipeline("tests/jenkins/jobs/BuildFailureMessage_Jenkinsfile") + super.testPipeline("tests/jenkins/jobs/BuildMessage_Jenkinsfile") } } diff --git a/tests/jenkins/TestCloseBuildSuccessGithubIssue.groovy b/tests/jenkins/TestCloseBuildSuccessGithubIssue.groovy new file mode 100644 index 000000000..db79af40e --- /dev/null +++ b/tests/jenkins/TestCloseBuildSuccessGithubIssue.groovy @@ -0,0 +1,46 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package jenkins.tests + +import jenkins.tests.BuildPipelineTest +import org.junit.Before +import org.junit.Test +import static com.lesfurets.jenkins.unit.MethodCall.callArgsToString +import static org.hamcrest.CoreMatchers.hasItem +import static org.hamcrest.MatcherAssert.assertThat + +class TestCloseBuildSuccessGithubIssue extends BuildPipelineTest { + + @Override + @Before + void setUp() { + this.registerLibTester(new CloseBuildSuccessGithubIssueLibTester(['Build successful OpenSearch'])) + super.setUp() + } + + + @Test + public void testExistingGithubIssue() { + super.testPipeline('tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile') + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue list --repo https://github.com/opensearch-project/OpenSearch.git -S \"[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0 in:title\" --label autocut,v2.0.0 --json number --jq '.[0].number', returnStdout=true}")) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue close bbb\nccc -R opensearch-project/OpenSearch --comment \"Closing the issue as the distribution build for OpenSearch has passed for version: **2.0.0**.\n Please see build log at www.example.com/jobs/test/123/consoleFull\", returnStdout=true}")) + } + + def getCommands(method, text) { + def shCommands = helper.callStack.findAll { call -> + call.methodName == method + }.collect { call -> + callArgsToString(call) + }.findAll { command -> + command.contains(text) + } + return shCommands + } + +} diff --git a/tests/jenkins/TestCloseGithubIssue.groovy b/tests/jenkins/TestCloseGithubIssue.groovy new file mode 100644 index 000000000..db4955347 --- /dev/null +++ b/tests/jenkins/TestCloseGithubIssue.groovy @@ -0,0 +1,51 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package jenkins.tests + +import jenkins.tests.BuildPipelineTest +import org.junit.Before +import org.junit.Test +import static com.lesfurets.jenkins.unit.MethodCall.callArgsToString +import static org.hamcrest.CoreMatchers.hasItem +import static org.hamcrest.MatcherAssert.assertThat + +class TestCloseGithubIssue extends BuildPipelineTest { + + @Override + @Before + void setUp() { + super.setUp() + } + + + @Test + void testCloseExistingGithubIssue() { + this.registerLibTester(new CloseGithubIssueLibTester( + 'https://github.com/opensearch-project/opensearch-build', + 'Test GH issue title', + 'Test GH issue close comment', + 'label101', + )) + super.testPipeline("tests/jenkins/jobs/CloseGithubIssue_JenkinsFile") + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S \"Test GH issue title in:title\" --label label101 --json number --jq '.[0].number', returnStdout=true}")) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue close bbb\nccc -R opensearch-project/opensearch-build --comment \"Test GH issue close comment\", returnStdout=true}")) + } + + def getCommands(method, text) { + def shCommands = helper.callStack.findAll { call -> + call.methodName == method + }.collect { call -> + callArgsToString(call) + }.findAll { command -> + command.contains(text) + } + return shCommands + } + +} diff --git a/tests/jenkins/TestCreateBuildFailureGithubIssue.groovy b/tests/jenkins/TestCreateBuildFailureGithubIssue.groovy index fe1dfc0aa..d8023b3c8 100644 --- a/tests/jenkins/TestCreateBuildFailureGithubIssue.groovy +++ b/tests/jenkins/TestCreateBuildFailureGithubIssue.groovy @@ -34,7 +34,10 @@ class TestCreateBuildFailureGithubIssue extends BuildPipelineTest { @Test public void testExistingGithubIssue() { super.testPipeline('tests/jenkins/jobs/CreateBuildFailureGithubIssue_Jenkinsfile', 'tests/jenkins/jobs/CreateBuildFailureGithubExistingIssueCheck_Jenkinsfile') - assertThat(getCommands('println', ''), hasItem('Issue already exists in the repository, skipping.')) + assertThat(getCommands('println', ''), hasItem('Issue already exists, adding a comment.')) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue list --repo https://github.com/opensearch-project/OpenSearch.git -S \"[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0 in:title\" --label autocut,v2.0.0, returnStdout=true}")) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue list --repo https://github.com/opensearch-project/OpenSearch.git -S \"[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0 in:title\" --label autocut,v2.0.0 --json number --jq '.[0].number', returnStdout=true}")) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue comment bbb\nccc --repo https://github.com/opensearch-project/OpenSearch.git --body \"***Received Error***: **Error building OpenSearch, retry with: ./build.sh manifests/2.2.0/opensearch-2.2.0.yml --component OpenSearch --snapshot**.\n The distribution build for OpenSearch has failed for version: 2.0.0.\n Please see build log at www.example.com/jobs/test/123/consoleFull\", returnStdout=true}")) } def getCommands(method, text) { diff --git a/tests/jenkins/TestCreateGithubIssue.groovy b/tests/jenkins/TestCreateGithubIssue.groovy index c271bc53a..eab71196e 100644 --- a/tests/jenkins/TestCreateGithubIssue.groovy +++ b/tests/jenkins/TestCreateGithubIssue.groovy @@ -41,11 +41,13 @@ class TestCreateGithubIssue extends BuildPipelineTest { this.registerLibTester(new CreateGithubIssueLibTester( 'https://github.com/opensearch-project/opensearch-build', 'Test GH issue title', - 'Test GH issue body' + 'Test GH issue body', )) super.testPipeline('tests/jenkins/jobs/CreateGithubIssueExisting_JenkinsFile') - assertThat(getCommands('println', ''), hasItem('Issue already exists in the repository, skipping.')) - assertThat(getCommands('sh', 'script'), hasItem('{script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S "Test GH issue title in:title" --label autocut, returnStdout=true}')) + assertThat(getCommands('println', ''), hasItem('Issue already exists, adding a comment.')) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S \"Test GH issue title in:title\" --label autocut, returnStdout=true}")) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S \"Test GH issue title in:title\" --label autocut --json number --jq '.[0].number', returnStdout=true}")) + assertThat(getCommands('sh', 'script'), hasItem("{script=gh issue comment bbb\nccc --repo https://github.com/opensearch-project/opensearch-build --body \"Test GH issue body\", returnStdout=true}")) } def getCommands(method, text) { diff --git a/tests/jenkins/jobs/BuildFailureMessage_Jenkinsfile b/tests/jenkins/jobs/BuildMessage_Jenkinsfile similarity index 92% rename from tests/jenkins/jobs/BuildFailureMessage_Jenkinsfile rename to tests/jenkins/jobs/BuildMessage_Jenkinsfile index fb5b2b8df..cd927551e 100644 --- a/tests/jenkins/jobs/BuildFailureMessage_Jenkinsfile +++ b/tests/jenkins/jobs/BuildMessage_Jenkinsfile @@ -14,7 +14,7 @@ pipeline { steps { script { try { - buildFailureMessage() + buildMessage(search: 'pass') currentBuild.result = 'SUCCESS' } catch (Exception err) { //https://github.com/jenkinsci/JenkinsPipelineUnit/issues/509 diff --git a/tests/jenkins/jobs/BuildMessage_Jenkinsfile.txt b/tests/jenkins/jobs/BuildMessage_Jenkinsfile.txt new file mode 100644 index 000000000..4a2ca6331 --- /dev/null +++ b/tests/jenkins/jobs/BuildMessage_Jenkinsfile.txt @@ -0,0 +1,6 @@ + BuildMessage_Jenkinsfile.run() + BuildMessage_Jenkinsfile.pipeline(groovy.lang.Closure) + BuildMessage_Jenkinsfile.echo(Executing on agent [label:none]) + BuildMessage_Jenkinsfile.stage(notify, groovy.lang.Closure) + BuildMessage_Jenkinsfile.script(groovy.lang.Closure) + BuildMessage_Jenkinsfile.buildMessage({search=pass}) diff --git a/tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile b/tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile new file mode 100644 index 000000000..2121c74ff --- /dev/null +++ b/tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +pipeline { + agent none + stages { + stage('notify') { + steps { + script { + closeBuildSuccessGithubIssue( + message: ["Build successful OpenSearch"], + inputManifestPath: 'tests/data/opensearch-2.0.0.yml' + ) + } + } + } + } +} diff --git a/tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile.txt b/tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile.txt new file mode 100644 index 000000000..256ba579a --- /dev/null +++ b/tests/jenkins/jobs/CloseBuildSuccessGithubIssue_JenkinsFile.txt @@ -0,0 +1,18 @@ + CloseBuildSuccessGithubIssue_JenkinsFile.run() + CloseBuildSuccessGithubIssue_JenkinsFile.pipeline(groovy.lang.Closure) + CloseBuildSuccessGithubIssue_JenkinsFile.echo(Executing on agent [label:none]) + CloseBuildSuccessGithubIssue_JenkinsFile.stage(notify, groovy.lang.Closure) + CloseBuildSuccessGithubIssue_JenkinsFile.script(groovy.lang.Closure) + CloseBuildSuccessGithubIssue_JenkinsFile.closeBuildSuccessGithubIssue({message=[Build successful OpenSearch], inputManifestPath=tests/data/opensearch-2.0.0.yml}) + closeBuildSuccessGithubIssue.legacySCM(groovy.lang.Closure) + closeBuildSuccessGithubIssue.library({identifier=jenkins@main, retriever=null}) + closeBuildSuccessGithubIssue.readYaml({file=tests/data/opensearch-2.0.0.yml}) + closeBuildSuccessGithubIssue.closeGithubIssue({repoUrl=https://github.com/opensearch-project/OpenSearch.git, issueTitle=[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0, closeComment=Closing the issue as the distribution build for OpenSearch has passed for version: **2.0.0**. + Please see build log at www.example.com/jobs/test/123/consoleFull, label=autocut,v2.0.0}) + closeGithubIssue.usernamePassword({credentialsId=jenkins-github-bot-token, passwordVariable=GITHUB_TOKEN, usernameVariable=GITHUB_USER}) + closeGithubIssue.withCredentials([[GITHUB_USER, GITHUB_TOKEN]], groovy.lang.Closure) + closeGithubIssue.sh({script=gh issue list --repo https://github.com/opensearch-project/OpenSearch.git -S "[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0 in:title" --label autocut,v2.0.0 --json number --jq '.[0].number', returnStdout=true}) + closeGithubIssue.sh({script=gh issue close bbb +ccc -R opensearch-project/OpenSearch --comment "Closing the issue as the distribution build for OpenSearch has passed for version: **2.0.0**. + Please see build log at www.example.com/jobs/test/123/consoleFull", returnStdout=true}) + closeBuildSuccessGithubIssue.sleep({time=3, unit=SECONDS}) diff --git a/tests/jenkins/jobs/CloseGithubIssue_JenkinsFile b/tests/jenkins/jobs/CloseGithubIssue_JenkinsFile new file mode 100644 index 000000000..e8e313011 --- /dev/null +++ b/tests/jenkins/jobs/CloseGithubIssue_JenkinsFile @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +pipeline { + agent none + stages { + stage('closeGithubIssue') { + steps { + script { + closeGithubIssue( + repoUrl: 'https://github.com/opensearch-project/opensearch-build', + issueTitle: 'Test GH issue title', + closeComment: 'Test GH issue close comment', + label: 'label101' + ) + } + } + } + } +} diff --git a/tests/jenkins/jobs/CloseGithubIssue_JenkinsFile.txt b/tests/jenkins/jobs/CloseGithubIssue_JenkinsFile.txt new file mode 100644 index 000000000..acc8ef946 --- /dev/null +++ b/tests/jenkins/jobs/CloseGithubIssue_JenkinsFile.txt @@ -0,0 +1,11 @@ + CloseGithubIssue_JenkinsFile.run() + CloseGithubIssue_JenkinsFile.pipeline(groovy.lang.Closure) + CloseGithubIssue_JenkinsFile.echo(Executing on agent [label:none]) + CloseGithubIssue_JenkinsFile.stage(closeGithubIssue, groovy.lang.Closure) + CloseGithubIssue_JenkinsFile.script(groovy.lang.Closure) + CloseGithubIssue_JenkinsFile.closeGithubIssue({repoUrl=https://github.com/opensearch-project/opensearch-build, issueTitle=Test GH issue title, closeComment=Test GH issue close comment, label=label101}) + closeGithubIssue.usernamePassword({credentialsId=jenkins-github-bot-token, passwordVariable=GITHUB_TOKEN, usernameVariable=GITHUB_USER}) + closeGithubIssue.withCredentials([[GITHUB_USER, GITHUB_TOKEN]], groovy.lang.Closure) + closeGithubIssue.sh({script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S "Test GH issue title in:title" --label label101 --json number --jq '.[0].number', returnStdout=true}) + closeGithubIssue.sh({script=gh issue close bbb +ccc -R opensearch-project/opensearch-build --comment "Test GH issue close comment", returnStdout=true}) diff --git a/tests/jenkins/jobs/CreateBuildFailureGithubExistingIssueCheck_Jenkinsfile.txt b/tests/jenkins/jobs/CreateBuildFailureGithubExistingIssueCheck_Jenkinsfile.txt index e19c52fdd..c17ca14d4 100644 --- a/tests/jenkins/jobs/CreateBuildFailureGithubExistingIssueCheck_Jenkinsfile.txt +++ b/tests/jenkins/jobs/CreateBuildFailureGithubExistingIssueCheck_Jenkinsfile.txt @@ -13,5 +13,10 @@ createGithubIssue.usernamePassword({credentialsId=jenkins-github-bot-token, passwordVariable=GITHUB_TOKEN, usernameVariable=GITHUB_USER}) createGithubIssue.withCredentials([[GITHUB_USER, GITHUB_TOKEN]], groovy.lang.Closure) createGithubIssue.sh({script=gh issue list --repo https://github.com/opensearch-project/OpenSearch.git -S "[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0 in:title" --label autocut,v2.0.0, returnStdout=true}) - createGithubIssue.println(Issue already exists in the repository, skipping.) + createGithubIssue.println(Issue already exists, adding a comment.) + createGithubIssue.sh({script=gh issue list --repo https://github.com/opensearch-project/OpenSearch.git -S "[AUTOCUT] Distribution Build Failed for OpenSearch-2.0.0 in:title" --label autocut,v2.0.0 --json number --jq '.[0].number', returnStdout=true}) + createGithubIssue.sh({script=gh issue comment bbb +ccc --repo https://github.com/opensearch-project/OpenSearch.git --body "***Received Error***: **Error building OpenSearch, retry with: ./build.sh manifests/2.2.0/opensearch-2.2.0.yml --component OpenSearch --snapshot**. + The distribution build for OpenSearch has failed for version: 2.0.0. + Please see build log at www.example.com/jobs/test/123/consoleFull", returnStdout=true}) createBuildFailureGithubIssue.sleep({time=3, unit=SECONDS}) diff --git a/tests/jenkins/jobs/CreateGithubIssueExisting_JenkinsFile.txt b/tests/jenkins/jobs/CreateGithubIssueExisting_JenkinsFile.txt index 2589b67a6..4ca263dbe 100644 --- a/tests/jenkins/jobs/CreateGithubIssueExisting_JenkinsFile.txt +++ b/tests/jenkins/jobs/CreateGithubIssueExisting_JenkinsFile.txt @@ -7,4 +7,7 @@ createGithubIssue.usernamePassword({credentialsId=jenkins-github-bot-token, passwordVariable=GITHUB_TOKEN, usernameVariable=GITHUB_USER}) createGithubIssue.withCredentials([[GITHUB_USER, GITHUB_TOKEN]], groovy.lang.Closure) createGithubIssue.sh({script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S "Test GH issue title in:title" --label autocut, returnStdout=true}) - createGithubIssue.println(Issue already exists in the repository, skipping.) + createGithubIssue.println(Issue already exists, adding a comment.) + createGithubIssue.sh({script=gh issue list --repo https://github.com/opensearch-project/opensearch-build -S "Test GH issue title in:title" --label autocut --json number --jq '.[0].number', returnStdout=true}) + createGithubIssue.sh({script=gh issue comment bbb +ccc --repo https://github.com/opensearch-project/opensearch-build --body "Test GH issue body", returnStdout=true}) diff --git a/tests/jenkins/lib-testers/BuildFailureMessageLibTester.groovy b/tests/jenkins/lib-testers/BuildFailureMessageLibTester.groovy deleted file mode 100644 index 02a14117f..000000000 --- a/tests/jenkins/lib-testers/BuildFailureMessageLibTester.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ - -class BuildFailureMessageLibTester extends LibFunctionTester { - - - public BuildFailureMessageLibTester() {} - - - void parameterInvariantsAssertions(call) { - //N/A - } - - boolean expectedParametersMatcher(call) { - return true - } - - - String libFunctionName() { - return 'buildFailureMessage' - } - - void configure(helper, binding){ - } -} diff --git a/tests/jenkins/lib-testers/BuildMessageLibTester.groovy b/tests/jenkins/lib-testers/BuildMessageLibTester.groovy new file mode 100644 index 000000000..633590beb --- /dev/null +++ b/tests/jenkins/lib-testers/BuildMessageLibTester.groovy @@ -0,0 +1,36 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +import static org.hamcrest.CoreMatchers.notNullValue +import static org.hamcrest.CoreMatchers.nullValue +import static org.hamcrest.MatcherAssert.assertThat + +class BuildMessageLibTester extends LibFunctionTester { + + private String search + + public BuildMessageLibTester(search) { + this.search = search + } + + + void parameterInvariantsAssertions(call) { + assertThat(call.args.search.first(), notNullValue()) + } + + boolean expectedParametersMatcher(call) { + return call.args.search.first().toString().equals(this.search) + } + + String libFunctionName() { + return 'buildMessage' + } + + void configure(helper, binding){ + } +} diff --git a/tests/jenkins/lib-testers/CloseBuildSuccessGithubIssueLibTester.groovy b/tests/jenkins/lib-testers/CloseBuildSuccessGithubIssueLibTester.groovy new file mode 100644 index 000000000..ce674905c --- /dev/null +++ b/tests/jenkins/lib-testers/CloseBuildSuccessGithubIssueLibTester.groovy @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +import static org.hamcrest.CoreMatchers.notNullValue +import static org.hamcrest.CoreMatchers.nullValue +import static org.hamcrest.MatcherAssert.assertThat + +class CloseBuildSuccessGithubIssueLibTester extends LibFunctionTester{ + private List message + + public CloseBuildSuccessGithubIssueLibTester(message){ + this.message = message + } + + @Override + String libFunctionName() { + return 'closeBuildSuccessGithubIssue' + } + + @Override + void parameterInvariantsAssertions(Object call) { + assertThat(call.args.message.first(), notNullValue()) + } + + @Override + boolean expectedParametersMatcher(Object call) { + return call.args.message.first().equals(this.message) + } + + @Override + void configure(Object helper, Object binding) { + helper.registerAllowedMethod('withCredentials', [Map]) + helper.registerAllowedMethod('sleep', [Map]) + binding.setVariable('BUILD_URL', 'www.example.com/jobs/test/123/') + } +} diff --git a/tests/jenkins/lib-testers/CloseGithubIssueLibTester.groovy b/tests/jenkins/lib-testers/CloseGithubIssueLibTester.groovy new file mode 100644 index 000000000..9ef114e2c --- /dev/null +++ b/tests/jenkins/lib-testers/CloseGithubIssueLibTester.groovy @@ -0,0 +1,53 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +import static org.hamcrest.CoreMatchers.notNullValue +import static org.hamcrest.CoreMatchers.nullValue +import static org.hamcrest.MatcherAssert.assertThat + +class CloseGithubIssueLibTester extends LibFunctionTester { + + private String repoUrl + private String issueTitle + private String closeComment + private String label + + public CloseGithubIssueLibTester(repoUrl, issueTitle, closeComment, label){ + this.repoUrl = repoUrl + this.issueTitle = issueTitle + this.closeComment = closeComment + this.label = label + } + + + @Override + String libFunctionName() { + return 'closeGithubIssue' + } + + @Override + void parameterInvariantsAssertions(Object call) { + assertThat(call.args.repoUrl.first(), notNullValue()) + assertThat(call.args.issueTitle.first(), notNullValue()) + assertThat(call.args.closeComment.first(), notNullValue()) + assertThat(call.args.label.first(), notNullValue()) + } + + @Override + boolean expectedParametersMatcher(Object call) { + return call.args.label.first().equals(this.label) + && call.args.repoUrl.first().equals(this.repoUrl) + && call.args.issueTitle.first().equals(this.issueTitle) + && call.args.closeComment.first().equals(this.closeComment) + } + + @Override + void configure(Object helper, Object binding) { + helper.registerAllowedMethod('withCredentials', [Map]) + } +} diff --git a/vars/buildFailureMessage.groovy b/vars/buildMessage.groovy similarity index 62% rename from vars/buildFailureMessage.groovy rename to vars/buildMessage.groovy index 2887f8697..0bf25ce91 100644 --- a/vars/buildFailureMessage.groovy +++ b/vars/buildMessage.groovy @@ -6,11 +6,15 @@ * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ + /** Library to find a pattern in jenkins build log. + @param Map args = [:] args A map of the following parameters + @param args.search - Use 'pass' to get the components passed and 'fail' for components failed. + */ import com.cloudbees.groovy.cps.NonCPS import org.apache.commons.io.IOUtils @NonCPS -def call(){ - String ERROR_STRING = "Error building" +def call(Map args = [:]){ + String QUERY_STRING = args.search List message = [] Reader performance_log = currentBuild.getRawBuild().getLogReader() String logContent = IOUtils.toString(performance_log) @@ -18,8 +22,8 @@ def call(){ performance_log = null logContent.eachLine() { line -> line=line.replace("\"", "") - //Gets the exact match for Error building - def java.util.regex.Matcher match = (line =~ /$ERROR_STRING.*/) + //Gets the exact match of the log starting with args.search + def java.util.regex.Matcher match = (line =~ /$QUERY_STRING.*/) if (match.find()) { line=match[0] message.add(line) @@ -27,7 +31,7 @@ def call(){ } //if no match returns as Build failed if(message.isEmpty()){ - message=["Build failed"] + message=["The search QUERY_STRING not identified in build log"] } return message } diff --git a/vars/closeBuildSuccessGithubIssue.groovy b/vars/closeBuildSuccessGithubIssue.groovy new file mode 100644 index 000000000..228f04811 --- /dev/null +++ b/vars/closeBuildSuccessGithubIssue.groovy @@ -0,0 +1,42 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + /** Library to close GitHub issue across opensearch-project repositories. + @param Map args = [:] args A map of the following parameters + @param args.message - message retrieved from buildMessage() method. + @param args.inputManifestPath - Path to input manifest. + */ +void call(Map args = [:]) { + lib = library(identifier: 'jenkins@main', retriever: legacySCM(scm)) + def passMessages = args.message + List passedComponents = [] + for (message in passMessages.unique()) { + java.util.regex.Matcher match = (message =~ /(?<=\bBuild successful\s).*/) + String matched = match[0] + println(matched.split(" ")[0].trim()) + passedComponents.add(matched.split(" ")[0].trim()) + } + + def yamlFile = readYaml(file: args.inputManifestPath) + def currentVersion = yamlFile.build.version + + for (component in yamlFile.components) { + if (passedComponents.contains(component.name)) { + println("Component ${component.name} passed, closing github issue") + ghIssueBody = """Closing the issue as the distribution build for ${component.name} has passed for version: **${currentVersion}**. + Please see build log at ${BUILD_URL}consoleFull""".stripIndent() + closeGithubIssue( + repoUrl: component.repository, + issueTitle: "[AUTOCUT] Distribution Build Failed for ${component.name}-${currentVersion}", + closeComment: ghIssueBody, + label: "autocut,v${currentVersion}" + ) + sleep(time:3, unit:'SECONDS') + } + } +} diff --git a/vars/closeGithubIssue.groovy b/vars/closeGithubIssue.groovy new file mode 100644 index 000000000..f93966721 --- /dev/null +++ b/vars/closeGithubIssue.groovy @@ -0,0 +1,38 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** Library to close GitHub issue across opensearch-project repositories. + @param Map args = [:] args A map of the following parameters + @param args.repoUrl - GitHub repository URL to create issue + @param args.issueTitle - GitHub issue title + @param args.closeComment - GitHub issue leave a closing comment + @param args.label - GitHub issue label to be attached along with 'untriaged'. Defaults to autocut. + */ +void call(Map args = [:]) { + label = args.label ?: 'autocut' + try { + withCredentials([usernamePassword(credentialsId: 'jenkins-github-bot-token', passwordVariable: 'GITHUB_TOKEN', usernameVariable: 'GITHUB_USER')]) { + def issuesNumber = sh( + script: "gh issue list --repo ${args.repoUrl} -S \"${args.issueTitle} in:title\" --label ${label} --json number --jq '.[0].number'", + returnStdout: true + ).trim() + if (!issuesNumber.isEmpty()) { + def repoPath = args.repoUrl.replaceFirst("https://github.com/", "").replaceAll("\\.git\$", "") + sh( + script: "gh issue close ${issuesNumber} -R ${repoPath} --comment \"${args.closeComment}\"", + returnStdout: true + ) + } else { + println("No open distribution failure AUTOCUT issues found that needs to be closed for the repo ${args.repoUrl}") + } + } + } catch (Exception ex) { + error("Unable to close GitHub issue for ${args.repoUrl}", ex.getMessage()) + } +} \ No newline at end of file diff --git a/vars/createGithubIssue.groovy b/vars/createGithubIssue.groovy index 2134c357d..ccece247b 100644 --- a/vars/createGithubIssue.groovy +++ b/vars/createGithubIssue.groovy @@ -22,9 +22,16 @@ void call(Map args = [:]) { script: "gh issue list --repo ${args.repoUrl} -S \"${args.issueTitle} in:title\" --label ${label}", returnStdout: true ) - if (issues) { - println('Issue already exists in the repository, skipping.') + println('Issue already exists, adding a comment.') + def issuesNumber = sh( + script: "gh issue list --repo ${args.repoUrl} -S \"${args.issueTitle} in:title\" --label ${label} --json number --jq '.[0].number'", + returnStdout: true + ).trim() + sh( + script: "gh issue comment ${issuesNumber} --repo ${args.repoUrl} --body \"${args.issueBody}\"", + returnStdout: true + ) } else { sh(