Skip to content

Instantly share code, notes, and snippets.

@rcbop
Created December 21, 2017 14:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rcbop/80369f293ee24c0db61039a67c6c5ee3 to your computer and use it in GitHub Desktop.
Save rcbop/80369f293ee24c0db61039a67c6c5ee3 to your computer and use it in GitHub Desktop.
Jenkinsfile for REST API + Oracle instant client download + Docker image build
// ##########################################################################
// # REST API jenkinsfile + oracle client
// ##########################################################################
// # Mantainer rcbpeixoto@gmail.com
// ##########################################################################
env.DOCKER_TAG="${params.BRANCH}"
env.BRANCH="${params.BRANCH}"
env.DOCKER_IMAGE="${env.JOB_BASE_NAME}"
env.DOCKER_NAME="${env.DOCKER_IMAGE}:${env.DOCKER_TAG}"
env.AWS_ENV="${env.DOCKER_IMAGE}-${env.DOCKER_TAG}"
env.ENV_TYPE="production"
env.CI_IMAGE_NAME="ci-img"
env.CI_TEST_NAME="${env.BUILD_TAG}"
env.SONAR_PROJECT_URL="http://<sonar-xxxxxxxxxxx>"
env.DEBUG="${params.DEBUG}"
env.ISSUE_PATTERN="<jira-issue-pattern-xxx>"
env.AWS_KEY="xxxxxxxxx"
env.AWS_SECRET="xxxxxxxxxx"
env.AWS_PROFILE="aws-deploy"
env.AWS_BUCKET_REGION="us-east-1"
env.AWS_S3_PREFIX="third-party"
env.AWS_S3_BUCKET="deploy-packages"
env.CHANGELOG="<div> - No changes </div>"
env.gitUrl = "ssh://git@xxxxxxxxxxxxxxxxxx/${env.DOCKER_IMAGE}.git"
env.EMAIL_LIST = 'xxxx,xxxxx,xxxxx,xxxxx,xxxxx'
timestampedNode('master') {
stage('Checkout') {
// checkout given branch
gitCheckoutBranch()
lastChanges()
}
stage('Download deps'){
downloadOracleInstantClient()
}
stage('Build base img'){
buildBaseImage()
}
stage('Build test img') {
// build ci container
removePreviousTestContainer()
buildTestsContainer()
}
stage('Unit Tests') {
// run mocha + sonar + istanbul
runTests("unit")
// coverage report
copyTestResults("/app/coverage")
publishHtmlReport(
"coverage/lcov-report",
"$JOB_BASE_NAME-coverage",
"index.html")
// mocha report
copyTestResults("/app/mocha-report")
publishHtmlReport(
"mocha-report",
"$JOB_BASE_NAME-mocha",
"unit-tests.html")
// xunit
copyTestResults("/app/test-results.xml")
junit 'test-results.xml'
}
stage('Build deploy img') {
// builds production container
buildProdContainer()
}
stage('Ship to ECR') {
ecrLogin()
// creates ecr docker image repository
createEcrRepositoryIfNotExists()
// creates eb application
createElasticBeanstalkApplicationIfNotExist()
// tags docker image and sends to aws ecr
dockerTagAndPushToECR()
}
stage('Deploy on EB') {
// removes anything but Dockerrun.aws.json from workspace
cleanWorkdir()
// initialize CLI application
initializeElasticBeanstalkCLI()
// new git branches creates new eb environments
def enviromentStatus = checkIfEnvironmentCreated()
if (enviromentStatus.contains('NOT-CREATED')) {
createEbEnvironment()
} else {
deployOnExistingEnvironment()
}
// displays eb application information
sh "eb status --verbose"
sh "eb setenv JENKINS_BUILD_NUMBER=${env.BUILD_NUMBER}"
sh "eb setenv JENKINS_PACKAGE_ID=AWS"
}
stage('API Tests') {
removePreviousTestContainer()
runTests("integration")
copyTestResults("/app/reports")
junit 'reports/**'
}
stage('Archive') {
sh "rm -rf *.tar.gz || echo 'Previous package not found'"
sh "tar zcf api-package.tar.gz *"
archiveArtifacts(
allowEmptyArchive: false,
artifacts: 'api-package.tar.gz',
onlyIfSuccessful: true)
}
stage('Close Issue') {
//checks commit logs for ISSUE_PATTERN env
def pattern = java.util.regex.Pattern.compile(env.ISSUE_PATTERN)
def issueKeyList = filterIssuesWithPattern(pattern)
closeGivenIssues(issueKeyList)
}
}
def timestampedNode(String label = "master", Closure body) {
node(label) {
def message = ""
try {
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
wrap([$class: 'TimestamperBuildWrapper']) {
body.call();
}
}
} catch (err) {
wrap([$class: 'BuildUser']) {
echo "CURRENT ERROR: ${err}"
currentBuild.result = "FAILED"
message = """
<hr>
<div style='color: red;'><b>BUILD FAILURE CAUSE ::</b> ${err.getMessage()}</div>
"""
notifyFailed();
}
} finally {
wrap([$class: 'BuildUser']) {
processGitHistoryAndCreateChangelog()
currentBuild.result = currentBuild.result ?: 'SUCCESS'
if (currentBuild.result != "ABORTED") {
emailNotification("${env.CHANGELOG}", "${message}", "${env.EMAIL_LIST}")
}
if (currentBuild.result == "SUCCESS"){
notifySuccess()
}
}
}
}
}
def processGitHistoryAndCreateChangelog(){
def passedBuilds = []
lastSuccessfulBuild(passedBuilds, currentBuild);
env.CHANGELOG = getChangeLog(passedBuilds)
debug("${env.CHANGELOG}")
}
def notifySuccess() {
slackSend(color: '#00FF00', message: "SUCCESSFUL: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
def notifyFailed() {
slackSend(color: '#FF0000', message: "FAILED: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
def lastSuccessfulBuild(passedBuilds, build) {
debug('lastSuccessfulBuild')
if ((build != null) && (build.result != 'SUCCESS')) {
passedBuilds.add(build)
lastSuccessfulBuild(passedBuilds, build.getPreviousBuild())
}
}
@NonCPS
def getChangeLog(passedBuilds) {
def log = "<ul>"
def changes = []
for (int x = 0; x < passedBuilds.size(); x++) {
def currentBuild = passedBuilds[x];
def changeLogSets = currentBuild.rawBuild.changeSets
debug("${changeLogSets}")
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
debug("${entries}")
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
debug("${entry}")
changes.push("<li> - ${entry.msg} [${entry.author}]</li>")
}
}
}
if (changes.size() > 0){
log = "${log}${changes.join('')}</ul>"
} else {
log = "<div><b>- No changes </b></div>"
}
return log;
}
def emailNotification(changelog, message, emailList){
debug("emailNotification :: ${changelog} :: ${message} :: ${emailList}")
def bodyContent = """
<div> <h2> ${env.JOB_NAME} Continuous Integration Report </h2> </div>
<hr>
<div style='color: blue;'><b>JENKINS JOB :: </b> ${env.JENKINS_URL}job/${env.JOB_NAME}/${env.BUILD_NUMBER}</div>
<hr>
<div style='color: blue;'><b>SONAR REPORT :: </b>${env.SONAR_PROJECT_URL}</div>
<hr>
<div style='color: blue;'><b>COVERAGE REPORT :: </b> ${JENKINS_URL}job/${env.JOB_NAME}/${env.JOB_NAME}-coverage/</div>
<hr>
<div style='color: blue;'><b>UNIT TESTS REPORT :: </b> ${JENKINS_URL}job/${env.JOB_NAME}/${env.JOB_NAME}-mocha/</div>
<hr>
<div style='color: blue;'><b>BUILD USER :: </b> ${env.BUILD_USER}</div>
<hr>
<div style='color: blue;'><b>BRANCH :: </b> ${env.BRANCH}</div>
${message}
<hr>
<div style='color: green;'><b> CHANGELOG </b></div>
<div> ${changelog} </div>
"""
emailext(body: "${bodyContent}",
mimeType: 'text/html',
replyTo: 'jenkins@mycompany.com',
subject: "[JENKINS] ${env.JOB_NAME} - BUILD #${env.BUILD_NUMBER} - ${currentBuild.result}!",
attachLog: true,
attachmentsPattern: "mocha-report/unit-tests.html",
recipientProviders: [
[$class: 'CulpritsRecipientProvider'],
[$class: 'DevelopersRecipientProvider'],
[$class: 'RequesterRecipientProvider']
],
to: emailList)
}
def gitCheckoutBranch(){
checkout([$class: 'GitSCM',
branches: [[name: "*/${env.BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'LocalBranch', localBranch: "**"]],
submoduleCfg: [],
userRemoteConfigs:
[[credentialsId: '${env.CREDENTIAL_ID}', url: env.gitUrl ]]]
)
}
def ecrLogin(){
sh "\$(aws ecr get-login --region ${env.AWS_DOCKER_REGION} --no-include-email --profile default)"
}
def createEcrRepositoryIfNotExists(){
def repoList = sh script: "aws ecr describe-repositories --profile default", returnStdout: true
if (!repoList.contains(env.DOCKER_IMAGE)) {
sh "aws ecr create-repository --repository-name ${env.DOCKER_IMAGE} --profile default"
}
}
def createElasticBeanstalkApplicationIfNotExist(){
def appList = sh script: "aws elasticbeanstalk describe-applications --profile default", returnStdout: true
if (!appList.contains(env.DOCKER_IMAGE)) {
sh "aws elasticbeanstalk create-application --application-name ${env.DOCKER_IMAGE} --profile default"
}
}
def dockerTagAndPushToECR(){
sh "docker tag ${env.DOCKER_IMAGE}:${env.DOCKER_TAG} ${env.DOCKER_AWS}/${env.DOCKER_NAME}"
sh "docker push ${env.DOCKER_AWS}/${env.DOCKER_NAME}"
}
def cleanWorkdir(){
sh 'DOCKER_RUN=$(cat Dockerrun.aws.json) && rm -rf * .git .gitignore .elasticbeanstalk && echo $DOCKER_RUN >> Dockerrun.aws.json'
}
def initializeElasticBeanstalkCLI(){
sh "eb init ${env.DOCKER_IMAGE} -r ${env.AWS_DOCKER_REGION} --profile default"
}
def createEbEnvironment(){
sh "eb create ${env.AWS_ENV} -r ${env.AWS_DOCKER_REGION} --single --profile default"
}
def checkIfEnvironmentCreated(){
def value = sh script: 'eb list --profile default | grep $AWS_ENV || echo "NOT-CREATED"', returnStdout: true
return value
}
def deployOnExistingEnvironment(){
sh "eb use ${env.AWS_ENV} -r ${env.AWS_DOCKER_REGION} --profile default"
sh "eb deploy ${env.AWS_ENV} --staged -r ${env.AWS_DOCKER_REGION} --profile default"
}
def runTests(type){
sh "docker run -t --name ${CI_TEST_NAME} ${CI_IMAGE_NAME}:latest $type"
}
def removePreviousTestContainer(){
sh "docker rm ${CI_TEST_NAME} || echo Container not created"
}
def copyTestResults(path){
sh "docker cp ${CI_TEST_NAME}:$path . || echo Test result not found"
}
def configureAWSCLI(){
sh """
aws configure set aws_access_key_id $AWS_KEY --profile $AWS_PROFILE
aws configure set aws_secret_access_key $AWS_SECRET --profile $AWS_PROFILE
aws configure set output 'json' --profile $AWS_PROFILE --profile $AWS_PROFILE
aws configure set region $AWS_BUCKET_REGION --profile $AWS_PROFILE
"""
}
def downloadOracleInstantClient(){
configureAWSCLI()
def folder = 'oracle-drivers'
def instantBasic = 'instantclient-basic-linux.x64-12.2.0.1.0.zip'
def instantSDK = 'instantclient-sdk-linux.x64-12.2.0.1.0.zip'
sh "mkdir -p ${folder}"
if (!fileExists("${folder}/${instantBasic}")){
downloadOracleInstantClientFromS3(instantBasic, "${folder}/${instantBasic}")
}
if (!fileExists("${folder}/${instantSDK}")){
downloadOracleInstantClientFromS3(instantSDK, "${folder}/${instantSDK}")
}
}
def downloadOracleInstantClientFromS3(file, destination){
sh """
aws s3 cp s3://$AWS_S3_BUCKET/$AWS_S3_PREFIX/${file} ${destination} --profile $AWS_PROFILE
"""
}
def buildBaseImage(){
sh "bash ./scripts/docker/build-base-img.sh"
}
def buildTestsContainer(){
sh """docker build -t ${CI_IMAGE_NAME}:latest \
--build-arg SONAR_URL=${SONAR_URL} \
--build-arg NODE_ENV=development \
-f ./dockers/ci/Dockerfile .
"""
}
def buildProdContainer(){
sh """
DOCKER_IMAGE_NAME=${env.DOCKER_IMAGE} DOCKER_IMAGE_TAG=${env.DOCKER_TAG} ./scripts/docker/build-dkr-image.sh
"""
}
def publishHtmlReport(reportDir, reportName, reportFiles){
publishHTML (target: [
allowMissing: true,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: "$reportDir",
reportFiles: "$reportFiles",
reportName: "$reportName"
])
}
def filterIssuesWithPattern(pattern){
def issueKeyList = []
for (def list : currentBuild.rawBuild.changeSets) {
for (def change : list.getLogs()) {
def comment = change.getComment()
def matcher = pattern.matcher(comment)
while(matcher.find()) {
issueKeyList.push(matcher.group().trim())
}
}
}
return issueKeyList
}
def closeGivenIssues(issueKeyList){
for (def issueKey : issueKeyList) {
def issueList = jiraSearch "issuekey = " + issueKey
if (issueList.size() == 0) {
error "Issue " + issueKey + " not found."
}
step(
$class: 'JiraIssueUpdateBuilder',
jqlSearch: "issuekey = "+issueKey,
workflowActionName: 'Done',
comment: "Issue closed automatically by ${env.JOB_NAME} - Build #${env.BUILD_NUMBER}"
)
}
}
def debug(msg){
if (env.DEBUG.toBoolean()){
echo "DEBUG :: ${msg} ::"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment