Open gloriaJun opened 2 years ago
#!/usr/bin/env groovy
pipeline {
agent any
tools {
nodejs "nodejs12.13.0"
jdk "jdk11"
}
environment {
HOME = "${env.WORKSPACE}"
YARN_CACHE_FOLDER = "${env.HOME}/.yarn-cache"
/**
defined internal env
*/
npmCommand = "yarn"
}
options {
buildDiscarder(logRotator(
artifactDaysToKeepStr: '1',
artifactNumToKeepStr: '10',
daysToKeepStr: '3',
numToKeepStr: "10")
)
timestamps()
timeout(time: 30, unit: 'MINUTES')
}
stages {
stage('Checkout') {
steps {
echo "Branch: ${env.BRANCH_NAME}, PrBranch: ${env.CHANGE_BRANCH}"
sh "which node; npm --version; node --version; yarn -version"
sh 'which java; java -version; echo $JAVA_HOME'
sh "printenv"
}
}
stage('Install packages') {
steps {
sh "${npmCommand} install"
}
}
stage('Lint') {
steps {
sh "${npmCommand} run lint -- -f json -o eslint.json"
}
}
stage('parallel test') {
parallel {
stage('parallel-1') {
steps {
script {
sh "ls -al ${env.JAVA_HOME}/bin"
sh "which java; java -version; echo ${env.JAVA_HOME}
}
}
}
stage('parallel-2') {
steps {
script {
sh "ls -al ${env.JAVA_HOME}/bin"
sh "which java; java -version; echo ${env.JAVA_HOME}
}
}
}
}
}
}
}
node {
/**
set tools
**/
env.NODEJS_HOME = tool name: 'nodejs12.13.0'
env.JAVA_HOME=tool name: 'jdk11'
env.PATH="${env.JAVA_HOME}/bin:${env.NODEJS_HOME}/bin:${env.PATH}"
/**
set cache node_modules
**/
env.YARN_CACHE_FOLDER = "${env.WORKSPACE}/.yarn-cache"
/**
defined internal env vars
**/
npmCommand = "yarn"
properties([
// 오래된 빌드 삭제
buildDiscarder(logRotator(
artifactDaysToKeepStr: '1',
artifactNumToKeepStr: '10',
daysToKeepStr: '3',
numToKeepStr: '10'
)),
])
timestamps {
timeout(time: 30, unit: 'MINUTES') {
stage ('Env') {
echo "Branch: ${env.BRANCH_NAME}, PrBranch: ${env.CHANGE_BRANCH}"
sh "which node; npm --version; node --version; yarn -version"
sh "which java; java -version; echo $JAVA_HOME"
sh "printenv"
}
stage ('Checkout') {
checkout scm
}
stage ('Install Dependencies') {
sh "${npmCommand} install"
}
stage ('Lint') {
sh "${npmCommand} run lint -- -f json -o eslint.json"
}
stage ('parallel test') {
def stages = [:]
stages['parallel-1'] = {
sh "ls -al ${env.JAVA_HOME}/bin"
sh "which java; java -version; echo ${env.JAVA_HOME}"
}
stages['parallel-2'] = {
sh "ls -al ${env.JAVA_HOME}/bin"
sh "which java; java -version; echo ${env.JAVA_HOME}"
}
parallel(stages)
}
}
}
}
#!/usr/bin/env groovy
void setBuildStatus(String context, String message, String state, String url) {
if (state == 'PENDING') {
backref = "${env.RUN_DISPLAY_URL}"
} else {
backref = url
}
// step([
// $class: "GitHubCommitStatusSetter",
// reposSource: [$class: "ManuallyEnteredRepositorySource", url: "${env.GIT_URL}"],
// contextSource: [$class: "ManuallyEnteredCommitContextSource", context: "ci/jenkins/${context}"],
// errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]],
// statusBackrefSource: [$class: "ManuallyEnteredBackrefSource", backref: backref],
// statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ]
// ]);
// To set publishChecks
title = 'Build Check'
if (state == 'PENDING') {
publishChecks title: title,
name: context,
status: 'IN_PROGRESS',
detailsURL: url
} else if (state != 'SUCCESS') {
publishChecks title: title,
name: context,
status: 'COMPLETED',
conclusion: 'FAILURE',
detailsURL: url
} else {
publishChecks title: title,
name: context,
detailsURL: url
}
}
void notifySlack(String message, String color) {
slackSend (channel: '#*******', color: color, message: message + ": Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
}
pipeline {
agent {
dockerfile { filename './build/Dockerfile' }
}
environment {
// npm_config_cache = "npm-cache"
HOME = "${env.WORKSPACE}"
// NPM_CONFIG_PREFIX = "${env.HOME}/.npm"
YARN_CACHE_FOLDER = "${env.HOME}/.yarn-cache"
/**
defined internal env
*/
npmCommand = "yarn"
isDevelop = "${env.BRANCH_NAME ==~ /(develop)/}"
isMainBranch = "${env.BRANCH_NAME ==~ /(master|develop|release.*)/}"
isPrBranch= "${env.BRANCH_NAME ==~ /^PR-\d+$/}"
isSonarCoverage= "${isMainBranch || env.isPrBranch && env.CHANGE_TARGET ==~ /(develop)/}"
coverageReportDir = "tests/coverages/reports"
junitReportFilename = 'test-junit-report.xml'
sonarReportFilename = 'test-sonar-report.xml'
buildUrl ="${env.BUILD_URL}"
storybookUrl = "*******"
// sonarQubeUrl = "http://*******/dashboard?id=*******&${isPrBranch == 'true' ? 'pullRequest=' + env.CHANGE_ID : 'branch=' + env.BRANCH_NAME.replaceAll('/', '%2F')}"
repository= "*******"
githubEndpoint= "*******"
}
options {
buildDiscarder(logRotator(artifactDaysToKeepStr: '1', artifactNumToKeepStr: '10', daysToKeepStr: '3',numToKeepStr: "10"))
timestamps()
timeout(time: 30, unit: 'MINUTES')
}
stages {
stage('Checkout') {
steps {
echo "Branch: ${env.BRANCH_NAME}, PrBranch: ${env.CHANGE_BRANCH}"
sh "which node; npm --version; node --version; yarn -version"
sh 'which java; java -version'
sh "printenv"
}
}
stage('Install dependencies') {
steps {
echo 'Installing dependencies...'
// sh "npm config set registry *******"
// sh "npm install"
// sh "npm ci --prefer-offline --no-audit"
sh "yarn config set registry *******"
sh "yarn install"
}
}
stage('Lint') {
environment {
context="lint"
}
steps {
script {
try {
sh "${npmCommand} run lint -- -f json -o eslint.json"
sh "${npmCommand} run lint -- -f checkstyle -o checkstyle-eslint.xml"
} catch (e) {
sh "${npmCommand} run lint"
}
}
}
post {
always {
recordIssues enabledForFailure: true, aggregatingResults: true, tool: checkStyle(pattern: 'checkstyle-eslint.xml')
}
}
}
stage('Build') {
parallel {
stage('App') {
environment {
context="build"
}
steps {
setBuildStatus("${context}", "${context} Progressing...", "PENDING", "${buildUrl}");
sh "${npmCommand} run build"
}
post {
always {
script {
if (currentBuild.currentResult != 'FAILURE') {
setBuildStatus("${context}", "${env.STAGE_NAME} Success", "SUCCESS", "${buildUrl}");
} else {
setBuildStatus("${context}", "${env.STAGE_NAME} Failed", "FAILURE", "${buildUrl}");
}
}
}
}
}
stage('Storybook') {
when {
expression { isMainBranch == 'true' }
}
environment {
context="storybook/build"
}
steps {
setBuildStatus("${context}", "${context} Progressing...", "PENDING", "${buildUrl}");
script {
withCredentials([string(credentialsId: 'ZEPLIN_TOKEN', variable: 'TOKEN')]) {
sh "STORYBOOK_ZEPLIN_TOKEN=${TOKEN} ${npmCommand} run storybook:build"
}
}
}
post {
always {
script {
if (currentBuild.currentResult != 'FAILURE') {
setBuildStatus("${context}", "${env.STAGE_NAME} Success", "SUCCESS", "${buildUrl}");
} else {
setBuildStatus("${context}", "${env.STAGE_NAME} Failed", "FAILURE", "${buildUrl}");
}
}
}
}
}
}
}
// stage('Test') {
// parallel {
stage('Unit Test') {
environment {
context="unit"
}
steps {
sh "${npmCommand} run test:unit:coverage --detectOpenHandles"
}
post {
always {
script {
junit "${coverageReportDir}/${context}/${junitReportFilename}"
}
}
}
}
stage('Storyshot') {
environment {
context="storybook/snapshot-test"
}
steps {
// setBuildStatus(context, "${context} Progressing...", "PENDING", "${buildUrl}");
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
script {
try {
// sh "${npmCommand} run test:storybook:coverage"
sh "${npmCommand} run test:storybook"
} catch (e) {
// setBuildStatus("${context}", "${context} Failed", "FAILURE", "${buildUrl}");
error e.message
}
}
}
}
post {
always {
script {
junit "${coverageReportDir}/storybook/${junitReportFilename}"
}
}
// success {
// setBuildStatus("${context}", "${context} Pass", "SUCCESS", "${buildUrl}");
// }
}
}
// }
// }
stage('Report') {
parallel {
stage('Coverage Report to CI') {
steps {
sh "${npmCommand} run combine-coverage"
script {
step([$class: 'CoberturaPublisher', coberturaReportFile: "${coverageReportDir}/combined/cobertura-coverage.xml"])
}
}
}
stage('SonarQube Analysis') {
when {
expression { isSonarCoverage == 'true' }
}
environment {
scannerHome = tool 'SonarQubeScanner'
}
steps {
script{
withCredentials([string(credentialsId: '*******', variable: 'TOKEN')]) {
withSonarQubeEnv('SonarQubeServer') {
def args = ''
if (isPrBranch == 'true') {
args = args + " \
-Dsonar.pullrequest.key=${env.CHANGE_ID} \
-Dsonar.pullrequest.branch=${env.CHANGE_BRANCH} \
-Dsonar.pullrequest.base=${env.CHANGE_TARGET} \
-Dsonar.pullrequest.provider=github \
-Dsonar.pullrequest.github.repository=${repository} \
-Dsonar.pullrequest.github.endpoint=${githubEndpoint} \
"
} else {
args = args + " \
-Dsonar.branch.name=${env.BRANCH_NAME} \
"
}
// -Dsonar.testExecutionReportPaths='${coverageReportDir}/unit/test-sonar-report.xml,${coverageReportDir}/storybook/test-sonar-report.xml' \
sh "BROWSERSLIST_IGNORE_OLD_DATA=true ${scannerHome}/bin/sonar-scanner \
-Dsonar.projectKey=******* \
-Dsonar.projectName=******* \
-Dsonar.sources=./src \
-Dsonar.exclusions='./build,./cypress,./tests,./src/assets,./src/xlt,./src/lib,./src/config,./src/languages,**/*.stories.js,**/*.stories.tsx,**/__snapshots__,**/*.snap.js' \
-Dsonar.tests=./src \
-Dsonar.test.inclusions='**/*.test.tsx,**/*.test.ts,**/*.test.js,**/*.spec.ts,**/*.spec.tsx' \
-Dsonar.testExecutionReportPaths='${coverageReportDir}/unit/${sonarReportFilename},${coverageReportDir}/storybook/${sonarReportFilename}' \
-Dsonar.javascript.lcov.reportPaths=${coverageReportDir}/combined/lcov.info \
-Dsonar.eslint.reportPaths=eslint.json \
-Dsonar.typescript.tsconfigPath=tsconfig.json \
${args} \
"
}
}
}
}
}
}
}
stage('Deploy') {
parallel {
stage('Storybook image snapshot Test') {
when {
expression { isMainBranch == 'true' }
}
environment {
context="storybook/snapshot-image-test"
}
steps {
setBuildStatus(context, "${context} Progressing...", "PENDING", "${buildUrl}");
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
script {
try {
sh "${npmCommand} run test:image-snapshot"
} catch (e) {
setBuildStatus("${context}", "${context} Failed", "FAILURE", "${buildUrl}");
error e.message
}
}
}
}
post {
success {
setBuildStatus("${context}", "${context} Pass", "SUCCESS", "${buildUrl}");
}
}
}
stage('Deploy Storybook') {
when {
expression { isMainBranch == 'true' }
}
environment {
context="storybook/deploy"
host = "*******"
outputDir = ".out"
branchName = "${env.BRANCH_NAME.split("/")[0]}"
deployTempDir = "/tmp/jenkins_tmp/*******/${env.GIT_BRANCH}"
deployTargetDir = "~/deploy/storybook-*******/${env.branchName}"
}
steps {
setBuildStatus(context, "${context} Progressing...", "PENDING", "${buildUrl}");
script {
def remote = [:]
remote.name = "*******"
remote.host = "*******"
remote.allowAnyHosts = true
withCredentials([sshUserPrivateKey(credentialsId: 'jenkins-private-key', keyFileVariable: 'identity')]) {
remote.user = '*******'
remote.identityFile = identity
remote.logLevel = 'INFO'
sshCommand remote: remote, command: "mkdir -p ${deployTempDir}"
sshPut remote: remote, from: "./${outputDir}", into: "${deployTempDir}"
sshCommand remote: remote, command: "rsync -avzh ${deployTempDir}/${outputDir}/* ${deployTargetDir}/ --delete"
sshRemove remote: remote, path: "${deployTempDir}", failOnError: false
}
}
}
post {
always {
script {
if (currentBuild.currentResult != 'FAILURE') {
setBuildStatus("${context}", "${env.STAGE_NAME} Success", "SUCCESS", "${storybookUrl}/${branchName}");
} else {
setBuildStatus("${context}", "${env.STAGE_NAME} Failed", "FAILURE", "${storybookUrl}/${branchName}");
}
}
}
}
}
}
}
stage('SonarQube Quality Gate') {
when {
expression { isSonarCoverage == 'true' }
}
// environment {
// context="SonarQube"
// }
steps {
// setBuildStatus(context, "${context} Progressing...", "PENDING", "${buildUrl}");
script{
timeout(time: 10, unit: 'MINUTES') {
waitForQualityGate abortPipeline: false
// def qg = waitForQualityGate()
// if (qg.status != 'OK') {
// echo "Pipeline aborted due to quality gate failure: ${qg.status}"
// // setBuildStatus("${context}", "${context} Failed", "UNSTABLE", "${sonarQubeUrl}");
// }
}
}
}
// post {
// always {
// script {
// if (currentBuild.currentResult != 'FAILURE') {
// setBuildStatus("${context}", "${env.STAGE_NAME} Success", "SUCCESS", "${sonarQubeUrl}");
// } else {
// setBuildStatus("${context}", "${env.STAGE_NAME} Failed", "FAILURE", "${sonarQubeUrl}");
// }
// }
// }
// }
}
// stage('PR Comment') {
// when {
// expression { isPrBranch == 'true' }
// }
// steps {
// step([
// $class: 'ViolationsToGitHubRecorder',
// config: [
// gitHubUrl: 'https://*******.git',
// repositoryOwner: '*******',
// repositoryName: '*******',
// pullRequestId: "${env.CHANGE_ID}",
// credentialsId: 'githubApp',
// createCommentWithAllSingleFileComments: true,
// createSingleFileComments: true,
// commentOnlyChangedContent: true,
// commentOnlyChangedFiles: true,
// minSeverity: 'INFO',
// maxNumberOfViolations: 99999,
// keepOldComments: false,
// commentTemplate: """
// *Reporter**: {{violation.reporter}}{{#violation.rule}}
// **Rule**: {{violation.rule}}{{/violation.rule}}
// **Severity**: {{violation.severity}}
// **File**: {{violation.file}} L{{violation.startLine}}{{#violation.source}}
// **Source**: {{violation.source}}{{/violation.source}}
// {{violation.message}}
// """,
// violationConfigs: [
// [ pattern: '.*/checkstyle-*\\.xml$', parser: 'CHECKSTYLE', reporter: 'Checkstyle' ],
// // [ pattern: '.*/findbugsXml\\.xml$', parser: 'FINDBUGS', reporter: 'Findbugs' ],
// // [ pattern: '.*/pmd\\.xml$', parser: 'PMD', reporter: 'PMD' ],
// ]
// ]
// ])
// }
// }
}
post {
cleanup {
cleanWs(
deleteDirs: true,
patterns: [
[pattern: 'dist', type: 'INCLUDE'],
[pattern: '.out', type: 'INCLUDE'],
]
)
}
success {
script {
def previousResult = currentBuild.previousBuild?.result
if (!previousResult || (previousResult && previousResult != currentBuild.result)) {
notifySlack ('SUCCESS', '#00FF00')
}
}
}
unstable {
notifySlack ('UNSTABLE', '#FFFF00')
}
failure {
notifySlack ('FAILED', '#FF0000')
}
}
}
def setBuildStatus(String context, String message, String state, String url) {
if (state == 'PENDING') {
backref = "${env.RUN_DISPLAY_URL}"
} else {
backref = url
}
step([
$class: "GitHubCommitStatusSetter",
reposSource: [$class: "ManuallyEnteredRepositorySource", url: "${env.GIT_URL}"],
contextSource: [$class: "ManuallyEnteredCommitContextSource", context: "ci/jenkins/${context}"],
errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]],
statusBackrefSource: [$class: "ManuallyEnteredBackrefSource", backref: backref],
statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ]
]);
// To set publishChecks
// title = 'Build Check'
// if (state == 'PENDING') {
// publishChecks title: title,
// name: context,
// status: 'IN_PROGRESS',
// detailsURL: url
// } else if (state != 'SUCCESS') {
// publishChecks title: title,
// name: context,
// status: 'COMPLETED',
// conclusion: 'FAILURE',
// detailsURL: url
// } else {
// publishChecks title: title,
// name: context,
// detailsURL: url
// }
}
def setBuildProcessingStatus(String context, String url) {
setBuildStatus(context, "${context} Progressing...", "PENDING", url)
}
def setBuildResultStatus(String context, Boolean isSuccess, String url) {
if (isSuccess) {
setBuildStatus(context, "${context} Success", "SUCCESS", url)
} else {
setBuildStatus(context, "${context} Failed", "FAILURE", url)
}
}
TL;DR;
Declarative Pipeline
Scripted Pipleline![image](https://user-images.githubusercontent.com/25721616/166435587-4ddb154b-22aa-4cfb-9b3d-d9d4967c1709.png)
Reference