Skip to content

Commit

Permalink
Add standard release pipeline library with generic trigger (#22)
Browse files Browse the repository at this point in the history
Signed-off-by: Sayali Gaikawad <[email protected]>
  • Loading branch information
gaiksaya committed Nov 4, 2022
1 parent c53f06c commit 1f7055b
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 3 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ lib = library(identifier: 'jenkins@<tag>', retriever: modernSCM([
| Name | Description |
|--------------------------------------------------------|:-----------------------------------------------------------------------------------------|
| [standardReleasePipeline.groovy](./vars/standardReleasePipeline.groovy) | The library sets up the necessary jenkins properties for you such as agent label, docker image to use as well as workflow time out. Check how to use the [default](./tests/jenkins/jobs/StandardReleasePipeline_JenkinsFile) in your workflow and how to [overide](./tests/jenkins/jobs/StandardReleasePipelineWithArgs_JenkinsFile) agent & docker image if you need.|
| [standardReleasePipelineWithGenericTrigger.groovy](./vars/standardReleasePipelineWithGenericTrigger.groovy) | A standard release pipeline for OpenSearch projects including generic triggers. A tag or a draft release can be used as a trigger using this library. The defaults are all set to trigger via a draft release. If the release is successful, the release can be published by using right params.. Check how to use the [default](./tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggers_Jenkinsfile) in your workflow and how to [overide](./tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile) values|
| [publishToNpm.groovy](./vars/publishToNpm.groovy) | A library to publish artifacts to NPM registry under @opensearch-project namespace. You can use [PublishToNpmLibTester](./tests/jenkins/lib-testers/PublishToNpmLibTester.groovy) to add tests in your repository. See how to use the lib in your [jenkinsFile](./tests/jenkins/jobs/PublishToNpm_Jenkinsfile)|

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion tests/jenkins/TestStandardReleasePipeline.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class TestStandardReleasePipeline extends BuildPipelineTest {
assertThat(echoCommand, hasItem('Executing on agent [docker:[image:test:image, reuseNode:false, stages:[:], args:, alwaysPull:true, containerPerStageRoot:false, label:AL2-X64]]'))
}

@Test
@Test
void 'check default values'(){
runScript("tests/jenkins/jobs/StandardReleasePipeline_JenkinsFile")
def echoCommand = getEchoCommands().findAll{
Expand Down
101 changes: 101 additions & 0 deletions tests/jenkins/TestStandardReleasePipelineWithGenericTriggers.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* 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 jenkins.tests.BuildPipelineTest
import org.junit.Before
import org.junit.Test
import static org.hamcrest.MatcherAssert.assertThat
import static org.hamcrest.CoreMatchers.equalTo
import static com.lesfurets.jenkins.unit.MethodCall.callArgsToString
import static org.hamcrest.CoreMatchers.hasItem



class TestStandardReleasePipelineWithGenericTriggers extends BuildPipelineTest {

@Before
void setUp() {
helper.registerAllowedMethod("GenericTrigger", [Map.class], null)
binding.setVariable('tag', '1.0.0')
binding.setVariable('release_url', 'https://api.github.com/repos/Codertocat/Hello-World/releases/17372790')
super.setUp()
}


@Test
void testStandardReleasePipelineWithGenericTriggers() {
super.testPipeline('tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggers_Jenkinsfile')
}

@Test
void testStandardReleasePipelineWithTagTriggers() {
super.testPipeline('tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile')
}

@Test
void 'validate override values'() {
runScript("tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggers_Jenkinsfile")
def echoCommand = getCommands('echo').findAll{
command -> command.contains('agent')
}

assertThat(echoCommand.size(), equalTo(1))
assertThat(echoCommand, hasItem('Executing on agent [docker:[image:centos:7, reuseNode:false, stages:[:], args:, alwaysPull:true, containerPerStageRoot:false, label:AL2-X64]]'))
}

@Test
void 'validate default triggers'(){
runScript("tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggers_Jenkinsfile")
def cmd = getCommands('GenericTrigger').findAll{
c -> c.contains('generic')
}
assertThat(cmd.size(), equalTo(1))
assertThat(cmd, hasItem('{genericVariables=[{key=ref, value=$.release.tag_name}, {key=isDraft, value=$.release.draft}, {key=release_url, value=$.release.url}], tokenCredentialId=opensearch-ci-webhook-trigger-token, causeString=A tag was cut on opensearch-ci repo, printContributedVariables=false, printPostContent=false, regexpFilterText=$isDraft, regexpFilterExpression=true}'))
}

@Test
void 'validate release is published'(){
runScript("tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggers_Jenkinsfile")
def cmd = getCommands('sh').findAll{
c -> c.contains('curl')
}
assertThat(cmd.size(), equalTo(1))
assertThat(cmd, hasItem("curl -X PATCH -H 'Accept: application/vnd.github+json' -H 'Authorization: Bearer GIT_TOKEN' https://api.github.com/repos/Codertocat/Hello-World/releases/17372790 -d '{\"tag_name\":\"1.0.0\",\"draft\":false,\"prerelease\":false}'"))

}

@Test
void 'use tag as trigger'(){
runScript("tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile")
def cmd = getCommands('GenericTrigger').findAll{
c -> c.contains('generic')
}
assertThat(cmd.size(), equalTo(1))
assertThat(cmd, hasItem('{genericVariables=[{key=ref, value=.ref}, {key=isDraft, value=$.release.draft}, {key=release_url, value=$.release.url}], tokenCredentialId=opensearch-ci-webhook-trigger-token, causeString=A tag was cut on opensearch-ci repo, printContributedVariables=false, printPostContent=false, regexpFilterText=$ref, regexpFilterExpression=^refs/tags/.*}'))
}

@Test
void 'validate release is not published'(){
runScript("tests/jenkins/jobs/StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile")
def cmd = getCommands('sh').findAll{
c -> c.contains('curl')
}
assertThat(cmd.size(), equalTo(0))
}

def getCommands(String method) {
def echoCommands = helper.callStack.findAll { call ->
call.methodName == method
}.collect { call ->
callArgsToString(call)
}
return echoCommands
}
}
2 changes: 1 addition & 1 deletion tests/jenkins/jobs/PublishToNpm_Jenkinsfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
PublishToNpm_Jenkinsfile.script(groovy.lang.Closure)
PublishToNpm_Jenkinsfile.publishToNpm({repository=https://github.com/opensearch-project/opensearch-ci, tag=1.0.0})
publishToNpm.checkout({$class=GitSCM, branches=[{name=1.0.0}], userRemoteConfigs=[{url=https://github.com/opensearch-project/opensearch-ci}]})
publishToNpm.string({credentialsId=publish-to-npm-token, variable=NPM_TOKEN})
publishToNpm.string({credentialsId=jenkins-opensearch-publish-to-npm-token, variable=NPM_TOKEN})
publishToNpm.withCredentials([NPM_TOKEN], groovy.lang.Closure)
publishToNpm.sh(npm set registry "https://registry.npmjs.org"; npm set //registry.npmjs.org/:_authToken NPM_TOKEN; npm publish --dry-run && npm publish --access public)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
standardReleasePipelineWithGenericTrigger(jsonValue: '.ref',
tokenIdCredential: 'opensearch-ci-webhook-trigger-token',
causeString: 'A tag was cut on opensearch-ci repo',
regexpFilterText: '$ref',
regexpFilterExpression: '^refs/tags/.*')
{
fakePublishToNpm(
tag: "${tag}"
)
}

def fakePublishToNpm(Map args) {
echo "fakePublishToNpm ${args}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile.run()
StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile.standardReleasePipelineWithGenericTrigger({jsonValue=.ref, tokenIdCredential=opensearch-ci-webhook-trigger-token, causeString=A tag was cut on opensearch-ci repo, regexpFilterText=$ref, regexpFilterExpression=^refs/tags/.*}, groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.pipeline(groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.timeout({time=1, unit=HOURS})
standardReleasePipelineWithGenericTrigger.echo(Executing on agent [docker:[image:opensearchstaging/ci-runner:release-centos7-clients-v1, reuseNode:false, stages:[:], args:, alwaysPull:true, containerPerStageRoot:false, label:Jenkins-Agent-AL2-X64-C54xlarge-Docker-Host]])
standardReleasePipelineWithGenericTrigger.GenericTrigger({genericVariables=[{key=ref, value=.ref}, {key=isDraft, value=$.release.draft}, {key=release_url, value=$.release.url}], tokenCredentialId=opensearch-ci-webhook-trigger-token, causeString=A tag was cut on opensearch-ci repo, printContributedVariables=false, printPostContent=false, regexpFilterText=$ref, regexpFilterExpression=^refs/tags/.*})
standardReleasePipelineWithGenericTrigger.stage(Release, groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.script(groovy.lang.Closure)
StandardReleasePipelineWithGenericTriggersTag_Jenkinsfile.echo(fakePublishToNpm [tag:1.0.0])
standardReleasePipelineWithGenericTrigger.script(groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.postCleanup()
postCleanup.cleanWs({disableDeferredWipeout=true, deleteDirs=true})
standardReleasePipelineWithGenericTrigger.script(groovy.lang.Closure)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
standardReleasePipelineWithGenericTrigger(overrideAgent: 'AL2-X64',
overrideDockerImage: 'centos:7',
tokenIdCredential: 'opensearch-ci-webhook-trigger-token',
causeString: 'A tag was cut on opensearch-ci repo',
publishRelease: true)
{
fakePublishToMaven(
mavenArtifactsPath: "/maven",
autoPublish: true
)
}

def fakePublishToMaven(Map args) {
echo "fakePublishToMaven ${args}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
StandardReleasePipelineWithGenericTriggers_Jenkinsfile.run()
StandardReleasePipelineWithGenericTriggers_Jenkinsfile.standardReleasePipelineWithGenericTrigger({overrideAgent=AL2-X64, overrideDockerImage=centos:7, tokenIdCredential=opensearch-ci-webhook-trigger-token, causeString=A tag was cut on opensearch-ci repo, publishRelease=true}, groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.pipeline(groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.timeout({time=1, unit=HOURS})
standardReleasePipelineWithGenericTrigger.echo(Executing on agent [docker:[image:centos:7, reuseNode:false, stages:[:], args:, alwaysPull:true, containerPerStageRoot:false, label:AL2-X64]])
standardReleasePipelineWithGenericTrigger.GenericTrigger({genericVariables=[{key=ref, value=$.release.tag_name}, {key=isDraft, value=$.release.draft}, {key=release_url, value=$.release.url}], tokenCredentialId=opensearch-ci-webhook-trigger-token, causeString=A tag was cut on opensearch-ci repo, printContributedVariables=false, printPostContent=false, regexpFilterText=$isDraft, regexpFilterExpression=true})
standardReleasePipelineWithGenericTrigger.stage(Release, groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.script(groovy.lang.Closure)
StandardReleasePipelineWithGenericTriggers_Jenkinsfile.echo(fakePublishToMaven [mavenArtifactsPath:/maven, autoPublish:true])
standardReleasePipelineWithGenericTrigger.script(groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.postCleanup()
postCleanup.cleanWs({disableDeferredWipeout=true, deleteDirs=true})
standardReleasePipelineWithGenericTrigger.script(groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.string({credentialsId=jenkins-github-bot-token, variable=GIT_TOKEN})
standardReleasePipelineWithGenericTrigger.withCredentials([GIT_TOKEN], groovy.lang.Closure)
standardReleasePipelineWithGenericTrigger.sh(curl -X PATCH -H 'Accept: application/vnd.github+json' -H 'Authorization: Bearer GIT_TOKEN' https://api.github.com/repos/Codertocat/Hello-World/releases/17372790 -d '{"tag_name":"1.0.0","draft":false,"prerelease":false}')
2 changes: 1 addition & 1 deletion vars/publishToNpm.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void call(Map args = [:]) {

checkout([$class: 'GitSCM', branches: [[name: "${args.tag}" ]], userRemoteConfigs: [[url: "${args.repository}" ]]])

withCredentials([string(credentialsId: 'publish-to-npm-token', variable: 'NPM_TOKEN')]){
withCredentials([string(credentialsId: 'jenkins-opensearch-publish-to-npm-token', variable: 'NPM_TOKEN')]){
sh """npm set registry "https://registry.npmjs.org"; npm set //registry.npmjs.org/:_authToken ${NPM_TOKEN}; npm publish --dry-run && npm publish --access public"""
}
}
80 changes: 80 additions & 0 deletions vars/standardReleasePipelineWithGenericTrigger.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* 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.
*/

/** A standard release pipeline for OpenSearch projects including generic triggers. A tag or a draft release can be used as a trigger using this library. The defaults are all set to trigger via a draft release. If the release is successful, the release can be published by using right params.
@param Map args = [:] args A map of the following parameters
@param body <Required> - A closure containing release steps to be executed in release stage.
@param args.tokenIdCredential <Required> - Credential id containing token for trigger authentication
@param args.overrideAgent <Optional> - Jenkins agent label to override the default ('Jenkins-Agent-AL2-X64-C54xlarge-Docker-Host')
@param args.overrideDockerImage <Optional> - Docker image to override the default ('opensearchstaging/ci-runner:release-centos7-clients-v1')
@param args.jsonValue <Optional> - Json value retrieved from payload of the webhook. Defaults to '$.release.tag_name'
@param args.causeString <Optional> - String mentioning why the workflow was triggered. Defaults to 'A tag was cut on GitHub repository causing this workflow to run'
@param args.regexpFilterText <Optional> - Variable to apply regular expression on. Defaults to '$isDraft'
@param.regexpFilterExpression <Optional> - Regular expression to test on the evaluated text specified. Defaults to ''
@param.publishRelease <Optional> - If set to true the release that triggered the job will be published on GitHub.
*/

void call(Map args = [:], Closure body) {
pipeline {
agent
{
docker {
label args.overrideAgent ?: 'Jenkins-Agent-AL2-X64-C54xlarge-Docker-Host'
image args.overrideDockerImage ?: 'opensearchstaging/ci-runner:release-centos7-clients-v1'
alwaysPull true
}
}
options {
timeout(time: 1, unit: 'HOURS')
}
triggers {
GenericTrigger(
genericVariables: [
[key: 'ref', value: (args.jsonValue ?: '$.release.tag_name')],
[key: 'isDraft', value: '$.release.draft'],
[key: 'release_url', value: '$.release.url']
],
tokenCredentialId: args.tokenIdCredential,
causeString: args.causeString ?: 'A tag was cut on GitHub repository causing this workflow to run',
printContributedVariables: false,
printPostContent: false,
regexpFilterText: (args.regexpFilterText ?: '$isDraft'),
regexpFilterExpression: (args.regexpFilterExpression ?: 'true')
)
}
environment {
tag = "$ref"
}
stages{
stage("Release") {
steps {
script {
body()
}
}
}
}
post {
success {
script {
if (args.publishRelease && release_url!= null){
withCredentials([string(credentialsId: 'jenkins-github-bot-token', variable: 'GIT_TOKEN')]){
sh "curl -X PATCH -H 'Accept: application/vnd.github+json' -H 'Authorization: Bearer ${GIT_TOKEN}' ${release_url} -d '{\"tag_name\":\"${tag}\",\"draft\":false,\"prerelease\":false}'"
}
}
}
}
always {
script {
postCleanup()
}
}
}
}
}

0 comments on commit 1f7055b

Please sign in to comment.