本文描述了使用Ubuntu 18.04和Python 3.6.8作为Jenkins Node来对Python项目做CICD Pipeline。
一般地Ubuntu 18.04已经自带Git,可以运行git --version
来检查是否已经安装Git。
如果还未安装,可以运行以下命令来安装:
sudo apt-get update
sudo apt-get install git -y
一般地Ubuntu 18.04已经自带Python 3.6.8。
运行以下命令安装Python工具和依赖:
sudo apt-get update
sudo apt-get install python3-pip python3-venv python3-dev gcc libmysqlclient-dev -y
python3 --version
pip3 --version
参考下面的文章以SSH方式配置Jenkins Node:
Pipeline用的工具列表如下:
分类 | 工具 |
---|---|
版本控制工具 | GitLab + Git |
流水线 | Jenkins Pipeline |
依赖管理 | pip3 + venv + requirements.txt |
单元测试 | nosetests |
测试覆盖率检查 | coverage + Jenkins Cobertura插件 |
代码质量分析 | pylint + Jenkins Warnings插件 |
应用打包 | pyinstaller |
让Pipeline运行在指定label的Jenkins Node上:
agent {label 'ubuntu-python3'}
options {
// Keep max num of recent builds
buildDiscarder(logRotator(numToKeepStr: "15"))
// Add timestamps on console output
timestamps()
// Set GitLab connection
gitLabConnection('GitlabAccess')
// Disable concurrent builds
disableConcurrentBuilds()
}
在构建前清空对应的workspace:
stage('Initialize') {
steps {
sh "env"
sh "pwd && ls -ltra"
cleanWs()
sh "pwd && ls -ltra"
}
}
从GitLab上拉取代码:
stage('Checkout') {
steps {
git branch: "${GIT_REPO_BRANCH}", credentialsId: "${GIT_CREDENTIALS_ID}", url: "${GIT_REPO_URL}"
}
}
准备Python的构建环境,包括:
stage('Prepare Build Env') {
steps {
sh """
python3 --version
python3 -m venv .venv
. .venv/bin/activate
pip3 install wheel
pip3 install nose
pip3 install coverage
pip3 install pylint
pip3 install -r requirements.txt
pip3 list
"""
}
}
说明:
. .venv/bin/activate
如果写成source .venv/bin/activate
,需要打开Manage Jenkins -> Configure System -> Shell -> Shell executable, 设置该值为 /bin/bash
,但是不建议修改Jenkins的该项设置,会导致其它非Linux的Jenkins Node可能出现"script.sh: not found"的错误。~/.cache/pip
, ~是Jenkins Node的Home目录在venv上运行单元测试和代码测试覆盖率检查:
stage('Unit Test') {
steps {
echo "Run unit test and coverage check"
sh """
. .venv/bin/activate
nosetests -v --with-xunit --xunit-file=${UNIT_TEST_REPORT} --with-coverage --cover-xml --cover-xml-file="${COVERAGE_REPORT}" --cover-package=${COVER_PACKAGE} --cover-erase
"""
}
post {
always {
echo "Archive test results"
junit allowEmptyResults: true, testResults: "${UNIT_TEST_REPORT}"
// Requires Cobertura plugin to archive and display converage report
// https://wiki.jenkins.io/display/JENKINS/Cobertura+Plugin
echo "Archive coverage report"
cobertura autoUpdateHealth: false, autoUpdateStability: false, coberturaReportFile: "${COVERAGE_REPORT}", failNoReports: false, failUnhealthy: false, failUnstable: false, maxNumberOfBuilds: 0, onlyStable: false, zoomCoverageChart: false
}
}
}
说明:
nosetests
以verbose模式运行(-v
),生成xunit格式的单元测试报告(--with-unit
),并可指定单元测试报告生成的位置(--xunit-file
);调用coverage插件(--with-coverage
)生成xml格式的代码测试覆盖率报告(--cover-xml
),并可指定代码测试覆盖率报告生成的位置(--cover-xml-file
),并可指定对哪些代码目录进行测试覆盖率检查(--cover-package
);并清除上一次的代码测试覆盖率报告(--cover-erase
)。在venv上运行代码质量检查:
stage('Code Quality Check') {
steps {
echo "Run pylint code style check"
// Ignore pylint comments via "-d C"
sh """
. .venv/bin/activate
pylint -d C -f parseable ${SOURCE_ROOT} --exit-zero | tee ${PYLINT_REPORT}
"""
}
post {
always {
// Requires Warnings plugin since Violations plugin is deprecated
// http://wiki.jenkins-ci.org/x/G4CGAQ
warnings canComputeNew: false, canResolveRelativePaths: false, canRunOnFailed: true, categoriesPattern: '', defaultEncoding: '', excludePattern: '', healthy: '', includePattern: '', messagesPattern: '', parserConfigurations: [[parserName: 'PyLint', pattern: "${PYLINT_REPORT}"]], unHealthy: ''
}
}
}
说明:
-d C
忽略Comment级别的警告,可以通过--exit-zero
参数让pylint总是返回成功代码0,并可以指定生成的报告文件在venv上做应用打包:
stage('Packaging') {
steps {
echo "Run packaging Python program"
// Requires to run pyinstaller in each kind of machine
script {
PACKAGE_NAME = "${PACKAGE_ID}"
if ("${params.PACKAGE_VERSION}" != "") {
PACKAGE_NAME = "${PACKAGE_NAME}-${params.PACKAGE_VERSION}"
}
PACKAGE_PATH = "./dist/${PACKAGE_NAME}"
}
sh """
. .venv/bin/activate
pip3 install pyinstaller
pyinstaller ${ENTRYPOINT_SCRIPT} --onefile --name ${PACKAGE_NAME}
pwd && ls -ltra
echo "Package: ${PACKAGE_PATH}"
du -sh ${PACKAGE_PATH}
sha256sum ${PACKAGE_PATH}
"""
}
}
说明:
--onefile
参数来生成一个应用二进制包,并通过--name
指定应用二进制包的名称