https://jenkins.io/doc/pipeline/examples/
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building..'
}
}
stage('Test') {
steps {
echo 'Testing..'
}
}
stage('Deploy') {
steps {
echo 'Deploying....'
}
}
}
}
// Declarative //
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
script {
// 一個優雅的退出pipeline的方法,這裡可執行任意邏輯
if( $VALUE1 == $VALUE2 ) {
currentBuild.result = 'SUCCESS'
return
}
}
}
}
}
}
junit4
stage("測試") {
steps {
echo "單元測試中..."
// 請在這裡放置您項目代碼的單元測試調用過程,例如:
sh 'mvn test' // mvn 示例
// sh './gradlew test'
echo "單元測試完成."
junit 'target/surefire-reports/*.xml' // 收集單元測試報告的調用過程
}
}
junit5 測試報告路徑與 junit4 的位置不同
stage("測試") {
steps {
echo "單元測試中..."
sh './gradlew test'
echo "單元測試完成."
junit 'build/test-results/test/*.xml'
}
}
env.PROJECT_DIR='src/netkiller'
node {
withEnv(["GOPATH=$WORKSPACE"]) {
stage('Init gopath') {
sh 'mkdir -p $GOPATH/{bin,pkg,src}'
}
stage('Build go proejct') {
sh 'cd ${PROJECT_DIR}; go test && go build && go install'
}
}
}
node {
git 'https://github.com/netkiller/api.git'
withEnv(["PATH+MAVEN=${tool 'm3'}/bin"]) {
sh "mvn -B –Dmaven.test.failure.ignore=true clean package"
}
stash excludes: 'target/', includes: '**', name: 'source'
}
參數指令,觸發這個管道需要用戶指定的參數,然後在step中通過params對象訪問這些參數。
// Declarative //
pipeline {
agent any
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
}
stages {
stage('Example') {
steps {
echo "Hello ${params.PERSON}"
}
}
}
}
Jenkinsfile (Declarative Pipeline)
pipeline {
agent any
parameters {
string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
file(name: "FILE", description: "Choose a file to upload")
}
stages {
stage('Example') {
steps {
echo "Hello ${params.PERSON}"
echo "Biography: ${params.BIOGRAPHY}"
echo "Toggle: ${params.TOGGLE}"
echo "Choice: ${params.CHOICE}"
echo "Password: ${params.PASSWORD}"
}
}
}
}
還能定義一些管道特定的選項,介紹幾個常用的:
skipDefaultCheckout - 在agent指令中忽略源碼checkout這一步驟。
timeout - 超時設置options { timeout(time: 1, unit: 'HOURS') }
retry - 直到成功的重試次數options { retry(3) }
timestamps - 控制台輸出前面加時間戳options { timestamps() }
觸發器指令定義了這個管道何時該執行,就可以定義兩種cron和pollSCM
cron - linux的cron格式triggers { cron('H 4/* 0 0 1-5') }
pollSCM - jenkins的poll scm語法,比如triggers { pollSCM('H 4/* 0 0 1-5') }
// Declarative //
pipeline {
agent any
triggers {
cron('H 4/* 0 0 1-5')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
一般我們會將管道和GitHub、GitLab、BitBucket關聯, 然後使用它們的webhooks來觸發,就不需要這個指令了。
定義自動安裝並自動放入PATH裡面的工具集合
// Declarative //
pipeline {
agent any
tools {
maven 'apache-maven-3.5.0' ①
}
stages {
stage('Example') {
steps {
sh 'mvn --version'
}
}
}
}
註:① 工具名稱必須預先在Jenkins中配置好了 → Global Tool Configuration.
post section 定義了管道執行結束後要進行的操作。支持在裡面定義很多Conditions塊: always, changed, failure, success 和 unstable。 這些條件塊會根據不同的返回結果來執行不同的邏輯。
always:不管返回什麼狀態都會執行
changed:如果當前管道返回值和上一次已經完成的管道返回值不同時候執行
failure:當前管道返回狀態值為”failed”時候執行,在Web UI界面上面是紅色的標誌
success:當前管道返回狀態值為”success”時候執行,在Web UI界面上面是綠色的標誌
unstable:當前管道返回狀態值為”unstable”時候執行,通常因為測試失敗,代碼不合法引起的。在Web UI界面上面是黃色的標誌
// Declarative //
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
post {
always {
echo 'I will always say Hello again!'
}
}
}
失敗發送郵件的例子
post {
failure {
mail to: "${email}",
subject: "Failed Pipeline: ${currentBuild.fullDisplayName}",
body: "Something is wrong with ${env.BUILD_URL}"
}
}
branch - 分支匹配才執行 when { branch 'master' }
environment - 環境變數匹配才執行 when { environment name: 'DEPLOY_TO', value: 'production' }
expression - groovy表達式為真才執行 expression { return params.DEBUG_BUILD } }
// Declarative //
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example Deploy') {
when {
branch 'production'
}
echo 'Deploying'
}
}
}
pipeline{
agent any
stages{
stage("isUnix") {
steps{
script {
if(isUnix() == true) {
echo("this jenkins job running on a linux-like system")
}else {
error("the jenkins job running on a windows system")
}
}
}
}
}
}
pipeline {
agent {
label "java-8"
}
stages {
stage("環境") {
steps {
parallel "Maven": {
script{
sh 'mvn -version'
}
}, "Java": {
sh 'java -version'
}, "sshpass": {
sh 'apt install -y sshpass'
sh 'sshpass -v'
}
}
}
stage("檢出") {
steps {
checkout(
[$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]],
userRemoteConfigs: [[url: env.GIT_REPO_URL]]]
)
}
}
stage("構建") {
steps {
echo "構建中..."
sh 'mvn package -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
echo "構建完成."
}
}
stage("測試") {
steps {
parallel "單元測試": {
echo "單元測試中..."
sh 'mvn test'
echo "單元測試完成."
junit 'target/surefire-reports/*.xml'
}, "介面測試": {
echo "介面測試中..."
// 請在這裡放置您項目代碼的單元測試調用過程,例如 mvn test
echo "介面測試完成."
}, "測試敏感詞":{
echo "Username: ${env.username}"
echo "Password: ${env.password}"
}
}
}
stage("運行"){
steps {
sh 'java -jar target/java-0.0.1-SNAPSHOT.jar'
}
}
stage("部署"){
steps {
echo "上傳"
sh 'sshpass -p Passw0rd scp target/*.jar root@dev.netkiller.cn:/root/'
echo "運行"
sh 'sshpass -p Passw0rd ssh root@dev.netkiller.cn java -jar /root/java-0.0.1-SNAPSHOT.jar'
}
}
}
}
stage("部署"){
parallel{
stage("sshpass") {
steps{
sh 'apt install -y sshpass'
sh 'sshpass -v'
}
}
stage('stop') {
steps {
sh 'sshpass -p passw0rd ssh -f dev.netkiller.cn pkill -f java-project-0.0.2-SNAPSHOT'
}
}
stage('start') {
steps {
sh 'sshpass -p passw0rd scp target/*.jar dev.netkiller.cn:/root/'
sh 'sshpass -p passw0rd ssh -f dev.netkiller.cn java -jar /root/java-project-0.0.2-SNAPSHOT.jar'
}
}
}
}
// Jenkinsfile (Scripted Pipeline)
node {
stage('Build') {
echo 'Building....'
}
stage('Test') {
echo 'Building....'
}
stage('Deploy') {
echo 'Deploying....'
}
}
node('vagrant-slave') {
env.JAVA_HOME="${tool 'jdk-8u45'}"
env.PATH="${env.JAVA_HOME}/bin:${env.PATH}"
sh 'java -version'
}
#!groovy
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
/*
Please make sure to add the following environment variables:
HEROKU_PREVIEW=<your heroku preview app>
HEROKU_PREPRODUCTION=<your heroku pre-production app>
HEROKU_PRODUCTION=<your heroku production app>
Please also add the following credentials to the global domain of your organization's folder:
Heroku API key as secret text with ID 'HEROKU_API_KEY'
GitHub Token value as secret text with ID 'GITHUB_TOKEN'
*/
node {
server = Artifactory.server "artifactory"
buildInfo = Artifactory.newBuildInfo()
buildInfo.env.capture = true
// we need to set a newer JVM for Sonar
env.JAVA_HOME="${tool 'Java SE DK 8u131'}"
env.PATH="${env.JAVA_HOME}/bin:${env.PATH}"
// pull request or feature branch
if (env.BRANCH_NAME != 'master') {
checkout()
build()
unitTest()
// test whether this is a regular branch build or a merged PR build
if (!isPRMergeBuild()) {
preview()
sonarServer()
allCodeQualityTests()
} else {
// Pull request
sonarPreview()
}
} // master branch / production
else {
checkout()
build()
allTests()
preview()
sonarServer()
allCodeQualityTests()
preProduction()
manualPromotion()
production()
}
}
def isPRMergeBuild() {
return (env.BRANCH_NAME ==~ /^PR-\d+$/)
}
def sonarPreview() {
stage('SonarQube Preview') {
prNo = (env.BRANCH_NAME=~/^PR-(\d+)$/)[0][1]
mvn "org.jacoco:jacoco-maven-plugin:prepare-agent install -Dmaven.test.failure.ignore=true -Pcoverage-per-test"
withCredentials([[$class: 'StringBinding', credentialsId: 'GITHUB_TOKEN', variable: 'GITHUB_TOKEN']]) {
githubToken=env.GITHUB_TOKEN
repoSlug=getRepoSlug()
withSonarQubeEnv('SonarQube Octodemoapps') {
mvn "-Dsonar.analysis.mode=preview -Dsonar.github.pullRequest=${prNo} -Dsonar.github.oauth=${githubToken} -Dsonar.github.repository=${repoSlug} -Dsonar.github.endpoint=https://api.github.com/ org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar"
}
}
}
}
def sonarServer() {
stage('SonarQube Server') {
mvn "org.jacoco:jacoco-maven-plugin:prepare-agent install -Dmaven.test.failure.ignore=true -Pcoverage-per-test"
withSonarQubeEnv('SonarQube Octodemoapps') {
mvn "org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar"
}
context="sonarqube/qualitygate"
setBuildStatus ("${context}", 'Checking Sonarqube quality gate', 'PENDING')
timeout(time: 1, unit: 'MINUTES') { // Just in case something goes wrong, pipeline will be killed after a timeout
def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
if (qg.status != 'OK') {
setBuildStatus ("${context}", "Sonarqube quality gate fail: ${qg.status}", 'FAILURE')
error "Pipeline aborted due to quality gate failure: ${qg.status}"
} else {
setBuildStatus ("${context}", "Sonarqube quality gate pass: ${qg.status}", 'SUCCESS')
}
}
}
}
def checkout () {
stage 'Checkout code'
context="continuous-integration/jenkins/"
context += isPRMergeBuild()?"pr-merge/checkout":"branch/checkout"
checkout scm
setBuildStatus ("${context}", 'Checking out completed', 'SUCCESS')
}
def build () {
stage 'Build'
mvn 'clean install -DskipTests=true -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -B -V'
}
def unitTest() {
stage 'Unit tests'
mvn 'test -B -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true'
if (currentBuild.result == "UNSTABLE") {
sh "exit 1"
}
}
def allTests() {
stage 'All tests'
// don't skip anything
mvn 'test -B'
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
if (currentBuild.result == "UNSTABLE") {
// input "Unit tests are failing, proceed?"
sh "exit 1"
}
}
def allCodeQualityTests() {
stage 'Code Quality'
lintTest()
coverageTest()
}
def lintTest() {
context="continuous-integration/jenkins/linting"
setBuildStatus ("${context}", 'Checking code conventions', 'PENDING')
lintTestPass = true
try {
mvn 'verify -DskipTests=true'
} catch (err) {
setBuildStatus ("${context}", 'Some code conventions are broken', 'FAILURE')
lintTestPass = false
} finally {
if (lintTestPass) setBuildStatus ("${context}", 'Code conventions OK', 'SUCCESS')
}
}
def coverageTest() {
context="continuous-integration/jenkins/coverage"
setBuildStatus ("${context}", 'Checking code coverage levels', 'PENDING')
coverageTestStatus = true
try {
mvn 'cobertura:check'
} catch (err) {
setBuildStatus("${context}", 'Code coverage below 90%', 'FAILURE')
throw err
}
setBuildStatus ("${context}", 'Code coverage above 90%', 'SUCCESS')
}
def preview() {
stage name: 'Deploy to Preview env', concurrency: 1
def herokuApp = "${env.HEROKU_PREVIEW}"
def id = createDeployment(getBranch(), "preview", "Deploying branch to test")
echo "Deployment ID: ${id}"
if (id != null) {
setDeploymentStatus(id, "pending", "https://${herokuApp}.herokuapp.com/", "Pending deployment to test");
herokuDeploy "${herokuApp}"
setDeploymentStatus(id, "success", "https://${herokuApp}.herokuapp.com/", "Successfully deployed to test");
}
mvn 'deploy -DskipTests=true'
}
def preProduction() {
stage name: 'Deploy to Pre-Production', concurrency: 1
switchSnapshotBuildToRelease()
herokuDeploy "${env.HEROKU_PREPRODUCTION}"
buildAndPublishToArtifactory()
}
def manualPromotion() {
// we need a first milestone step so that all jobs entering this stage are tracked an can be aborted if needed
milestone 1
// time out manual approval after ten minutes
timeout(time: 10, unit: 'MINUTES') {
input message: "Does Pre-Production look good?"
}
// this will kill any job which is still in the input step
milestone 2
}
def production() {
stage name: 'Deploy to Production', concurrency: 1
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
herokuDeploy "${env.HEROKU_PRODUCTION}"
def version = getCurrentHerokuReleaseVersion("${env.HEROKU_PRODUCTION}")
def createdAt = getCurrentHerokuReleaseDate("${env.HEROKU_PRODUCTION}", version)
echo "Release version: ${version}"
createRelease(version, createdAt)
promoteInArtifactoryAndDistributeToBinTray()
}
def switchSnapshotBuildToRelease() {
def descriptor = Artifactory.mavenDescriptor()
descriptor.version = '1.0.0'
descriptor.pomFile = 'pom.xml'
descriptor.transform()
}
def buildAndPublishToArtifactory() {
def rtMaven = Artifactory.newMavenBuild()
rtMaven.tool = "Maven 3.x"
rtMaven.deployer releaseRepo:'libs-release-local', snapshotRepo:'libs-snapshot-local', server: server
rtMaven.resolver releaseRepo:'libs-release', snapshotRepo:'libs-snapshot', server: server
rtMaven.run pom: 'pom.xml', goals: 'install', buildInfo: buildInfo
server.publishBuildInfo buildInfo
}
def promoteBuildInArtifactory() {
def promotionConfig = [
// Mandatory parameters
'buildName' : buildInfo.name,
'buildNumber' : buildInfo.number,
'targetRepo' : 'libs-prod-local',
// Optional parameters
'comment' : 'deploying to production',
'sourceRepo' : 'libs-release-local',
'status' : 'Released',
'includeDependencies': false,
'copy' : true,
// 'failFast' is true by default.
// Set it to false, if you don't want the promotion to abort upon receiving the first error.
'failFast' : true
]
// Promote build
server.promote promotionConfig
}
def distributeBuildToBinTray() {
def distributionConfig = [
// Mandatory parameters
'buildName' : buildInfo.name,
'buildNumber' : buildInfo.number,
'targetRepo' : 'reading-time-dist',
// Optional parameters
//'publish' : true, // Default: true. If true, artifacts are published when deployed to Bintray.
'overrideExistingFiles' : true, // Default: false. If true, Artifactory overwrites builds already existing in the target path in Bintray.
//'gpgPassphrase' : 'passphrase', // If specified, Artifactory will GPG sign the build deployed to Bintray and apply the specified passphrase.
//'async' : false, // Default: false. If true, the build will be distributed asynchronously. Errors and warnings may be viewed in the Artifactory log.
//"sourceRepos" : ["yum-local"], // An array of local repositories from which build artifacts should be collected.
//'dryRun' : false, // Default: false. If true, distribution is only simulated. No files are actually moved.
]
server.distribute distributionConfig
}
def promoteInArtifactoryAndDistributeToBinTray() {
stage ("Promote in Artifactory and Distribute to BinTray") {
promoteBuildInArtifactory()
distributeBuildToBinTray()
}
}
def mvn(args) {
withMaven(
// Maven installation declared in the Jenkins "Global Tool Configuration"
maven: 'Maven 3.x',
// Maven settings.xml file defined with the Jenkins Config File Provider Plugin
// settings.xml referencing the GitHub Artifactory repositories
mavenSettingsConfig: '0e94d6c3-b431-434f-a201-7d7cda7180cb',
// we do not need to set a special local maven repo, take the one from the standard box
//mavenLocalRepo: '.repository'
) {
// Run the maven build
sh "mvn $args -Dmaven.test.failure.ignore"
}
}
def herokuDeploy (herokuApp) {
withCredentials([[$class: 'StringBinding', credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY']]) {
mvn "heroku:deploy -DskipTests=true -Dmaven.javadoc.skip=true -B -V -D heroku.appName=${herokuApp}"
}
}
def getRepoSlug() {
tokens = "${env.JOB_NAME}".tokenize('/')
org = tokens[tokens.size()-3]
repo = tokens[tokens.size()-2]
return "${org}/${repo}"
}
def getBranch() {
tokens = "${env.JOB_NAME}".tokenize('/')
branch = tokens[tokens.size()-1]
return "${branch}"
}
def createDeployment(ref, environment, description) {
withCredentials([[$class: 'StringBinding', credentialsId: 'GITHUB_TOKEN', variable: 'GITHUB_TOKEN']]) {
def payload = JsonOutput.toJson(["ref": "${ref}", "description": "${description}", "environment": "${environment}", "required_contexts": []])
def apiUrl = "https://api.github.com/repos/${getRepoSlug()}/deployments"
def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Token ${env.GITHUB_TOKEN}\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST -d '${payload}' ${apiUrl}").trim()
def jsonSlurper = new JsonSlurper()
def data = jsonSlurper.parseText("${response}")
return data.id
}
}
void createRelease(tagName, createdAt) {
withCredentials([[$class: 'StringBinding', credentialsId: 'GITHUB_TOKEN', variable: 'GITHUB_TOKEN']]) {
def body = "**Created at:** ${createdAt}\n**Deployment job:** [${env.BUILD_NUMBER}](${env.BUILD_URL})\n**Environment:** [${env.HEROKU_PRODUCTION}](https://dashboard.heroku.com/apps/${env.HEROKU_PRODUCTION})"
def payload = JsonOutput.toJson(["tag_name": "v${tagName}", "name": "${env.HEROKU_PRODUCTION} - v${tagName}", "body": "${body}"])
def apiUrl = "https://api.github.com/repos/${getRepoSlug()}/releases"
def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Token ${env.GITHUB_TOKEN}\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST -d '${payload}' ${apiUrl}").trim()
}
}
void setDeploymentStatus(deploymentId, state, targetUrl, description) {
withCredentials([[$class: 'StringBinding', credentialsId: 'GITHUB_TOKEN', variable: 'GITHUB_TOKEN']]) {
def payload = JsonOutput.toJson(["state": "${state}", "target_url": "${targetUrl}", "description": "${description}"])
def apiUrl = "https://api.github.com/repos/${getRepoSlug()}/deployments/${deploymentId}/statuses"
def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Token ${env.GITHUB_TOKEN}\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST -d '${payload}' ${apiUrl}").trim()
}
}
void setBuildStatus(context, message, state) {
// partially hard coded URL because of https://issues.jenkins-ci.org/browse/JENKINS-36961, adjust to your own GitHub instance
step([
$class: "GitHubCommitStatusSetter",
contextSource: [$class: "ManuallyEnteredCommitContextSource", context: context],
reposSource: [$class: "ManuallyEnteredRepositorySource", url: "https://octodemo.com/${getRepoSlug()}"],
errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]],
statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ]
]);
}
def getCurrentHerokuReleaseVersion(app) {
withCredentials([[$class: 'StringBinding', credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY']]) {
def apiUrl = "https://api.heroku.com/apps/${app}/dynos"
def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Bearer ${env.HEROKU_API_KEY}\" -H \"Accept: application/vnd.heroku+json; version=3\" -X GET ${apiUrl}").trim()
def jsonSlurper = new JsonSlurper()
def data = jsonSlurper.parseText("${response}")
return data[0].release.version
}
}
def getCurrentHerokuReleaseDate(app, version) {
withCredentials([[$class: 'StringBinding', credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY']]) {
def apiUrl = "https://api.heroku.com/apps/${app}/releases/${version}"
def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Bearer ${env.HEROKU_API_KEY}\" -H \"Accept: application/vnd.heroku+json; version=3\" -X GET ${apiUrl}").trim()
def jsonSlurper = new JsonSlurper()
def data = jsonSlurper.parseText("${response}")
return data.created_at
}
}
// This shows a simple build wrapper example, using the AnsiColor plugin.
node {
// This displays colors using the 'xterm' ansi color map.
ansiColor('xterm') {
// Just some echoes to show the ANSI color.
stage "\u001B[31mI'm Red\u001B[0m Now not"
}
}
// This shows a simple example of how to archive the build output artifacts.
node {
stage "Create build output"
// Make the output directory.
sh "mkdir -p output"
// Write an useful file, which is needed to be archived.
writeFile file: "output/usefulfile.txt", text: "This file is useful, need to archive it."
// Write an useless file, which is not needed to be archived.
writeFile file: "output/uselessfile.md", text: "This file is useless, no need to archive it."
stage "Archive build output"
// Archive the build output artifacts.
archiveArtifacts artifacts: 'output/*.txt', excludes: 'output/*.md'
}
def modules = [
'Java',
'PHP',
'Python',
'Ruby'
]
node() {
stage("checkout") {
echo "checkout"
}
modules.each { module ->
stage("build:${module}") {
echo "${module}"
}
}
}
node('master') {
stage('Build') {
docker.image('maven:3.5.0').inside {
sh 'mvn --version'
}
}
stage('Deploy') {
if (env.BRANCH_NAME == 'master') {
echo 'I only execute on the master branch'
} else {
echo 'I execute elsewhere'
}
}
}
node {
stage('Git') {
def branch = input message: 'input branch name for this job', ok: 'ok', parameters: [string(defaultValue: 'master', description: 'branch name', name: 'branch')]
echo branch
}
}
node {
stage('Git') {
def result = input message: 'input branch name for this job', ok: 'ok', parameters: [string(defaultValue: 'master', description: 'branch name', name: 'branch'), string(defaultValue: '', description: 'commit to switch', name: 'commit')]
echo result.branch
echo result.commit
}
}
node {
stage('Git') {
def result = input message: 'input branch name for this job', ok: 'ok', parameters: [string(defaultValue: 'master', description: 'branch name', name: 'branch'), string(defaultValue: '', description: 'commit to switch', name: 'commit')]
sh "echo ${result.branch}"
sh "echo ${result.commit}"
}
}
node {
dir('/var/www') {
stage('Git') {
if(fileExists('project')) {
dir('project') {
sh 'git fetch origin'
sh 'git checkout master'
sh 'git pull'
}
} else {
sh 'git clone git@git.netkiller.cn:neo/project.git project'
}
}
}
}
node {
stage("Checkout") {
checkout([$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]],userRemoteConfigs: [[url: env.GIT_REPO_URL]]])
}
docker.image('ruby').inside {
stage("Init") {
sh 'pwd && ls'
sh 'gem install rails'
// sh 'gem install ...'
}
stage("Test") {
sh 'ruby tc_simple_number.rb'
}
stage("Build") {
sh 'ruby --version'
archiveArtifacts artifacts: 'bin/*', fingerprint: true
}
stage("Deploy") {
sh 'rsync -auzv --delete * www@host.netkiller.cn:/path/to/dir'
}
}
}
def projectName = 'myProject'
def jobClosure = {
steps {
conditionalSteps {
condition {
fileExists(projectName+'/target/test.jar', BaseDir.WORKSPACE)
}
runner('Fail')
steps {
batchFile('echo Found some tests')
}
}
}
}
freeStyleJob('AAA-Test', jobClosure)
stage("Deploy") {
nexusArtifactUploader artifacts: [
[artifactId: 'java11', type: 'jar', file: 'target/java11.jar']
],
groupId: 'org.springframework.samples',
nexusUrl: 'netkiller.cn/repository/maven/',
nexusVersion: 'nexus3',
protocol: 'http',
repository: 'maven',
version: '2.0.0.BUILD'
}
environment定義鍵值對的環境變數
// Declarative //
pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
environment {
DEBUG_FLAGS = '-g'
}
steps {
sh 'printenv'
}
}
}
}
// Declarative //
pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
environment {
AN_ACCESS_KEY = credentials('my-prefined-secret-text') ③
}
steps {
sh 'printenv'
}
}
}
}
echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"
${env.WORKSPACE}
println env.JOB_NAME
println env.BUILD_NUMBER
打印所有環境變數
node {
echo sh(returnStdout: true, script: 'env')
echo sh(script: 'env|sort', returnStdout: true)
// ...
}
pipeline {
agent any
stages {
stage('Environment Example') {
steps {
script{
def envs = sh(returnStdout: true, script: 'env').split('\n')
envs.each { name ->
println "Name: $name"
}
}
}
}
}
}
agent 指令指定整個管道或某個特定的stage的執行環境。它的參數可用使用:
agent:
any - 任意一個可用的agent
none - 如果放在pipeline頂層,那麼每一個stage都需要定義自己的agent指令
label - 在jenkins環境中指定標籤的agent上面執行,比如agent { label 'my-defined-label' }
node - agent { node { label 'labelName' } } 和 label一樣,但是可用定義更多可選項
docker - 指定在docker容器中運行
dockerfile - 使用源碼根目錄下面的Dockerfile構建容器來運行
https://jenkins.io/doc/book/pipeline/docker/
添加 jenkins 用戶到 docker 組
[root@localhost ~]# gpasswd -a jenkins docker Adding user jenkins to group docker [root@localhost ~]# cat /etc/group | grep ^docker docker:x:993:jenkins
pipeline {
agent { docker { image 'maven:3.3.3' } }
stages {
stage('build') {
steps {
sh 'mvn --version'
}
}
}
}
pipeline {
agent { docker { image 'php' } }
stages {
stage('build') {
steps {
sh 'php --version'
}
}
}
}
pipeline {
agent {
docker { image 'php:latest' }
}
stages {
stage('Test') {
steps {
sh 'php --version'
}
}
}
}
掛在 /root/.m2 目錄
pipeline {
agent {
docker {
image 'maven:latest'
args '-v $HOME/.m2:/root/.m2'
}
}
stages {
stage('Build') {
steps {
sh 'mvn -B'
}
}
}
}
docker.image('maven').inside("-v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker") {
sh 'docker images'
}
node {
stage("Checkout") {
checkout(
[$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]], userRemoteConfigs: [[url: env.GIT_REPO_URL]]]
)
sh 'pwd'
}
docker.image('maven:latest').inside("-v /root/.m2:/root/.m2") {
stage("Build") {
sh 'java -version'
sh 'mvn package -Dmaven.test.failure.ignore -Dmaven.test.skip=true'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
stage("Test") {
sh 'java -jar target/webflux-0.0.1-SNAPSHOT.jar &'
sleep 20
sh 'mvn test -Dmaven.test.failure.ignore'
junit 'target/surefire-reports/*.xml'
}
}
}
node {
checkout scm
docker.withRegistry('http://hub.netkiller.cn:5000') {
def customImage = docker.build("project/api:1.0")
/* Push the container to the custom Registry */
customImage.push()
}
}
容器內運行腳本
node {
checkout scm
def customImage = docker.build("my-image:${env.BUILD_ID}")
customImage.inside {
sh 'make test'
}
}
dir ('example') {
/* 構建鏡像 */
def customImage = docker.build("example-group/example:${params.VERSION}")
/* hub.netkiller.cn是你的Docker Registry */
docker.withRegistry('https://hub.netkiller.cn/', 'docker-registry') {
/* Push the container to the custom Registry */
// push 指定版本
customImage.push('latest')
}
}
stage('DockerBuild') {
sh """
rm -f src/docker/*.jar
cp target/*.jar src/docker/*.jar
"""
dir ("src/docker/") {
def image = docker.build("your/demo:1.0.0")
image.push()
}
}
stage('test') {
parallel {
stage('test') {
steps {
echo 'hello'
}
}
stage('test1') {
steps {
sleep 1
}
}
stage('test2') {
steps {
retry(count: 5) {
echo 'hello'
}
}
}
}
node {
catchError {
sh 'might fail'
}
step([$class: 'Mailer', recipients: 'admin@somewhere'])
}
stage('teatA') {
steps {
catchError() {
sh 'make'
}
mail(subject: 'Test', body: 'aaaa', to: 'netkiller@msn.com')
}
}
https://github.com/jenkinsci/workflow-scm-step-plugin/blob/master/README.md
下面配置適用與 Webhook 方式
stage('checkout') {
steps {
checkout(scm: [$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]],
userRemoteConfigs: [[url: env.GIT_REPO_URL]]], changelog: true, poll: true)
}
}
從 URL 獲取代碼
node {
checkout ([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '', url: 'https://github.com/bg7nyt/java.git']]])
}
stage("build") {
steps {
sh "mvn package -Dmaven.test.skip=true"
}
}
steps {
script{
sh 'find /etc/'
}
}
例 6.1. Shell Docker 示例
Shell Docker 使用 docker 命令完成構建過程
registryUrl='127.0.0.1:5000' # 私有鏡像倉庫地址
imageName='netkiller/project' # 鏡像名稱
imageTag=$BRANCH # 上面設置Branch分支,這裡可以當做環境變數使用
echo ' >>> [INFO] enter workspace ...'
cd $WORKSPACE/ # 進入到jenkins的工作區,jenkins會將gitlab倉庫代碼pull到這裡,用於製作鏡像檔案
# 根據不同的Branch生成不同的swoft的配置檔案,區分測試還是生成等
echo ' >>> [INFO] create startup.sh ...'
(
cat << EOF
# 啟動 Shell 寫在此處
EOF
) > ./entrypoint.sh
# 生成 Dockerfile
echo ' >>> [INFO] begin create Dockerfile ...'
rm -f ./Dockerfile
(
cat << EOF
FROM netkiller/base
LABEL maintainer=netkiller@msn.com
COPY . /var/www/project
WORKDIR /var/www/project
EXPOSE 80
ENV PHP_ENVIRONMENT $BRANCH
ENTRYPOINT [ "bash", "/var/www/project/entrypoint.sh" ]
EOF
) > ./Dockerfile
#刪除無用鏡像
echo ' >>> [INFO] begin cleaning image ...'
for image in `docker images | grep -w $imageName | grep -i -w $imageTag | awk '{print $3}'`
do
docker rmi $image -f
done
#製作鏡像
echo ' >>> [INFO] begin building image ...'
docker build --tag $imageName:$imageTag --rm .
#給鏡像打標籤
img=`docker images | grep -w $imageName | grep -i -w $imageTag | awk '{print $3}'`
docker tag $img $registryUrl/$imageName:$imageTag
#push到私有鏡像倉庫
echo ' >>> [INFO] begin publishing image ...'
docker push $registryUrl/$imageName:$imageTag
#刪除剛剛製作的鏡像, 釋放存儲空間
echo ' >>> [INFO] cleaning temporary building ...'
docker rmi -f $imageName:$imageTag
docker rmi -f $registryUrl/$imageName:$imageTag
stage('bat') {
steps {
bat(returnStatus: true, returnStdout: true, label: 'aa', encoding: 'utf-8', script: 'dir')
}
}
stage('subtask') {
steps {
dir(path: '/src') {
echo 'begin'
sh '''mvn test'''
echo 'end'
}
}
}
stage('exists') {
steps {
fileExists '/sss'
}
}
def exists = fileExists 'file'
if (exists) {
echo 'Yes'
} else {
echo 'No'
}
if (fileExists('file')) {
echo 'Yes'
} else {
echo 'No'
}
pipeline{
agent any
stages{
stage("Checkout") {
steps {
checkout(
[$class: 'GitSCM', branches: [[name: env.GIT_BUILD_REF]],
userRemoteConfigs: [[url: env.GIT_REPO_URL]]]
)
}
}
stage("fileExists") {
steps{
echo pwd()
sh 'ls -1'
script {
def exists = fileExists 'README.md'
if (exists) {
echo 'Yes'
} else {
echo 'No'
}
}
}
}
}
}
stage('test') {
steps {
cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, cleanupMatrixParent: true, deleteDirs: true, disableDeferredWipeout: true, notFailBuild: true, skipWhenFailed: true, externalDelete: '/aa')
}
}