十四、Jenkins打包完成后,执行脚本,将版本发行包压缩上传禅道提交禅道版本
十三、禅道登录/提交版本/编辑版本接口 ,书接上回,在禅道中注册一个Jenkins账号,利用禅道的接口,提交到禅道中,具体代码如下
基本流程:
Jenkins打包完成==>执行python脚本==>包括打版本压缩包==>打源码压缩包==>新建或编辑版本==>上传发行包和源码包==>禅道版本新增完成
1. 代码上传Jenkins服务器 zentao_add_edit_version.py
代码上传Jenkins服务器 zentao_add_edit_version.py
# -*- coding: utf-8 -*-
"""
-------------------------------------------------------------------------------
File : zentao_add_edit_version.py
Time : 2023/4/11 12:05
author : mojin
Email : [email protected]
-------------------------------------------------------------------------------
"""
import requests
import datetime
import os
import sys
import time
import urllib.request
import shutil
import zipfile
import os,datetime
import logging
# 设置日志输出的格式
logging.basicConfig(format='%(asctime)s %(levelname)s-%(filename)s-[%(funcName)s] line %(lineno)d - %(message)s', level=logging.INFO)
class VersionManagement():
'''
打包完成后在禅道上传发行包,补充版本描述创建下个版本 用于fix bug
'''
def __init__(self):
self.build_info=eval(sys.argv[1])
# build_info = {
# 'building_number': '%BUILD_NUMBER%',
# 'execution': '105',
# 'product': '35',
# 'project_zt':'104',
# 'build_name': '*v1.0.0.6.service.***',
# 'build_name_keywords': '*,service',
# 'scmPath': '%GIT_URL%',
# 'filePath': '%GIT_URL%',
# 'desc_doc': '%SCM_CHANGELOG%',
# 'files_path': '%WORKSPACE%\\service-admin\\target\\service-admin.jar,%WORKSPACE%',
# 'branch': 'dev',
# 'project_name': '%PROJECT_NAME%',
# }
#
build_info2 = {
'building_number': '%BUILD_NUMBER%',#构建编号
'execution': '105',#禅道迭代 id
'product': '35', #禅道产品id
'project_zt': '104', #产品 禅道项目id
'build_name': '*v1.0.0.6.web.***', #创建下个新版本的名称
'build_name_keywords': '*,web', #标识字符,用于找到在版本名称找到换个字符标书,编辑补充发行包和描述,下载地址等等信息
'scmPath': '%GIT_URL%', #代码地址
'filePath': '%GIT_URL%', #下载地址
'desc_doc': '%SCM_CHANGELOG%', # 版本描述
'files_path': '%WORKSPACE%\\dist,%WORKSPACE%', #打包要删除的目录node_modules
'branch': 'dev', #分支
'project_name': '%PROJECT_NAME%', #项目名称
}
# self.build_info={
# 'building_number':'265',
# 'execution':'3',
# 'product': '2',
# 'build_name': '*v1.0.1.h5.***',
# 'build_name_keywords': '*,h5',
# 'scmPath': 'http://192.16***',
# 'filePath': 'http://192***',
# 'desc_doc': '*****变更记录************************************************************************************',
# 'files_path': 'E:/java_xm/service-admin.jar,E:\\java_xm\\services',
# 'branch':'dev',
# 'project_name':'services',
# 'version_txt_path':'services',
# }
# 'source_code_url':'http://192.168.1.******************chive/dev.zip',
# 'source_code_zip':'service-services.zip'
# self.dir_path = r'E:\java_xm\service\mobile\risk-pro'
# self.zip_filename = 'service-mobile'
self.building_number=self.build_info['building_number'] #构建编号
self.execution=self.build_info['execution'] #禅道迭代 id
self.product = self.build_info['product'] #禅道产品id
self.project_zt=self.build_info['project_zt'] #产品 禅道项目id
self.build_name=self.build_info['build_name'] #创建下个新版本的名称
self.build_name_keywords = self.build_info['build_name_keywords'] #标识字符,用于找到在版本名称找到换个字符标书,编辑补充发行包和描述,下载地址等等信息
self.scmPath=self.build_info['scmPath'] #代码地址
self.filePath=self.build_info['filePath'] #下载地址
self.desc_doc=self.build_info['desc_doc'] # 版本描述
self.files_path=self.build_info['files_path'] #r'E:/java_xm/service-admin.jar,E:/java_xm/service-admin.jar' 附件路径
self.branch=self.build_info['branch'] #代码分支
self.project_name = self.build_info['project_name'] # jenkins项目名称
self.exclude_dir=self.build_info.get('exclude_dir','node_modules') #打包要删除的目录node_modules
self.version_file=self.build_info.get('version_file','')
# self.source_code_zip=self.build_info['source_code_zip'] #源码包名
# self.source_code_url =self.build_info['source_code_url'] #源码链接
self.url ='http://192.168.1.52:81'
self.account="jenkins"
self.password='jenkins135766'
# self.building_number='265' #构建编号
# self.execution='3' #迭代
# self.product = '2' #产品
# self.build_name='*v1.0.1.h5.***' #创建下个新版本的名称
# self.build_name_keywords = '*,h5' #标识字符,用于找到在版本名称找到换个字符标书,编辑补充发行包和描述,下载地址等等信息
# self.scmPath='代码地址' #代码地址
# self.filePath='下载地址' #下载地址
# self.desc_doc="[APP\n]"
# self.files_path=r'E:/java_xm/service-admin.jar,E:/java_xm/service-admin.jar'
self.file_path_list=[]
self.Package_Name=''
self.source_code_zip=''
self.current_time = datetime.datetime.now().strftime("%Y-%m-%d")
self.now_time=datetime.datetime.now().strftime("%Y%m%d%H%M%S")
self.token=self.login() #'fa7f18713d8921d98d84cffd567cb6a9'###self.login()
self.headers = {
'Token': self.token,
'Cookie': f'zentaosid= {self.token}'
}
def zipdir(self,dir_path,):
"""
压缩指定路径中的所有文件和子目录到一个ZIP文件中。
:param path: 要压缩的路径
:param ziph: ZipFile对象,用于写入压缩文件
:param exclude_dir: 要排除的目录名,默认为None表示不排除
"""
# 创建 ZipFile 对象,并调用自定义的 zipdir() 函数进行压缩
# zip_filename = self.Package_Name # self.Package_Name = f'{build_name}-{os.path.basename(file_path)}'
zip_filename = f"{self.Package_Name}.zip"
with zipfile.ZipFile(zip_filename, mode='w') as ziph:
logging.info(f"正在将目录 {dir_path} 打包成 {zip_filename}...")
# zipdir(dir_path, z, exclude_dir)
for root, dirs, files in os.walk(dir_path):
if self.exclude_dir and self.exclude_dir in dirs:
dirs.remove(self.exclude_dir) # 排除指定的目录
for file in files:
file_path = os.path.join(root, file)
ziph.write(file_path, os.path.relpath(file_path, dir_path))
zip_filename_path=r'./%s' % zip_filename
self.file_path_list.append(zip_filename_path)
return zip_filename_path
def files_converting(self, dir_path):
'''
判断传来的参数是目录还是文件,是目录打包成压缩包输出路径,是文件直接输出路径
:param dir_path:
:return:
'''
if os.path.isfile(dir_path):
file_name = os.path.basename(dir_path)
new_path = f'{self.Package_Name}'
logging.info(f'{dir_path},是一个文件')
dir_path = [dir_path, new_path]
elif os.path.isdir(dir_path):
logging.info(f'{dir_path},是一个目录')
zip_filename = f"{self.Package_Name}.zip"
dir_path =[ self.zipdir(dir_path),zip_filename]
else:
logging.info('无法判断', dir_path, '是文件还是目录')
raise
logging.info(f'{dir_path}')
return dir_path
def changes(self):
changes = ''
#self.desc_doc = self.desc_doc.replace('[', '').replace(']', '')
n=1
logging.info(f'更新内容:{self.desc_doc}')
for i in self.desc_doc.split('\n'):
if i != '':
#<p><span>1.描述描述描述</span></p
changes = changes + f'<p><span>{n}.{i}</span></p'
#changes= changes + f'{n}.{i}\n'
n += 1
if changes != '':
changes= f'<p><span>变更记录:(AFS)</span></p{changes}'
else:
changes = f'<p><span>变更记录:(AFS)</span></p<p><span>本次没有发生变化</span></p'
logging.info(f'更新内容:{changes}')
return changes
def requests_api(self,path,payload,method,parame_type,file=None):
url = f"{self.url}/{path}"
if parame_type=='josn':
parameter={'josn':payload}
elif parame_type=='data':
parameter={'data':payload}
elif parame_type=='params':
parameter={'params':payload}
else:
logging.error(f'输入的参数类型[{parame_type}]不符合要求!!!!')
raise
if file==None:
file={}
try:
response = requests.request(method, url, headers=self.headers,**parameter,files=file)
resp_dic = response.json()
#logging.info(response.status_code)
except Exception as e:
resp_dic={"response":f'{e}'}
logging.error(f'请求失败:{resp_dic}')
raise
return resp_dic
def login(self): #登录
url = f"{self.url}/zentao/api.php/v1/tokens"
payload={
'account':self.account,
'password':self.password
}
response = requests.request("POST", url, headers={}, json=payload)
resp_dic=response.json()
#logging.info(resp_dic)
if resp_dic.get('token','')=='':
logging.error(f'登录失败!{resp_dic}')
raise
return resp_dic['token']
def executions_builds_list(self,): #获取版本列表
path = f"zentao/api.php/v1/executions/{self.execution}/builds"
payload = {}
response = self.requests_api( path, payload, 'GET', 'data',)
#logging.info(f'获取版本列表:{response}')
return response
#版本详情
def builds_info(self):
path = f"zentao/api.php/v1/builds/366"
payload={}
response = self.requests_api(path, payload, 'GET', 'data', )
logging.info(response)
return response
def add_version(self): #新增版本
path = f"zentao/build-create-{self.execution}.json"
payload = {
#'isIntegrated': 'no',
'execution': f'{self.execution}',
'product': f'{self.product}',
'name': self.build_name.replace('.***',f'.{int(self.building_number)+1}.***'),
'builder': 'Jenkins',
'date': self.current_time,
# 'scmPath': '代码地址',
# 'filePath': '下载地址',
# 'desc': '<p>1.描述描述描述</p><p><span>2.描述描述描述</span></p>',
# 'uid': '643603d229574',
'project': self.project_zt
}
response = self.requests_api(path, payload, 'POST', 'data', )
logging.info(f'新增下个版本:{response}')
return response
def edit_version(self,id,edit=1): #编辑版本
if edit==1: #编辑
path = f"zentao/build-edit-{id}.json"
elif edit==2: #新增
path = f"zentao/build-create-{self.execution}.json"
else:
logging.error(f'edit参数异常[{edit}]!!)')
raise
#'*v1.0.0.6.web.***'
build_name=self.build_name.replace('.***',f'.{self.building_number}.***').replace("*","")
try:
with open(self.version_file, "r") as f:
build_name = f.read()
logging.info(f'读取新的版本号:{build_name}')
except Exception as e:
logging.info(f'没有读取到新本号,出现异常({e})')
raise
# build_name = f'{build_name}{self.now_time}'
# logging.info(f'生成新版本号:({build_name})')
payload = {
#'branch[]': ' 0',
'execution': f'{self.execution}',
'product': f'{self.product}',
#.replace('.***',f'.{self.building_number}.***')
'name': build_name,
'builder': 'Jenkins',
'date': self.current_time,
'scmPath': self.scmPath,
'filePath': self.filePath,
'desc':self.changes(), #'1.版本描述',
# 'uid': '6437b2ec9764a'
'project': self.project_zt
}
files=[]
files_path_list=self.files_path.split(',')
#files_path_list.append(self.source_code_zip)
logging.info(f'{files_path_list}')
for file_path in files_path_list:
self.Package_Name = f'{build_name}-{os.path.basename(file_path)}'
file_path = self.files_converting(file_path)
n=1
while True:
if n>180:
logging.error('打压缩包超时……………………')
raise
try:
logging.info(file_path)
files.append(('files[]', (file_path[1], open(file_path[0], 'rb'))))
break
except Exception as e:
logging.info(f'{file_path}')
logging.info(f'打压缩包中……{e}')
time.sleep(1)
n+=1
response = self.requests_api(path, payload, 'POST', 'data', files) #""#
logging.info(f'新增或编辑版本:{response}')
return response
def main(self):
#self.download_source()
version_exists=True #版本是否存在的标识 默认版本不存在True,成功找到版本后 编辑成功后 修改标识为False 版本存在
for build in self.executions_builds_list()['builds']:
keywords_lsit=[]
for keywords in self.build_name_keywords.split(','): #匹配版本找到后获取到版本id 进行编辑
keywords_lsit.append(keywords in build['name'] ) #所有的版本标识 都存在 为T
if False not in keywords_lsit: # 列表中没有False
logging.info(f'获取到版本id【{build["id"]}】') #获取到了版本id
logging.info(f'版本信息:{build}')
self.edit_version(build["id"]) #编辑版本
version_exists=False #编辑版本存在的
break
if version_exists: #当版本不存在时 创建新版本,输入完整的数据 发行包 源码等等
self.edit_version('',edit = 2)
self.add_version() #创建下个版本
for file_path in self.file_path_list: #删除之前创建的压缩包
if os.path.exists(file_path):
os.remove(file_path)
logging.info(f"删除上传完成的压缩包文件[{file_path}]")
else:
logging.info(f'压缩包file不存在[{file_path}]')
if __name__=="__main__":
VersionMan=VersionManagement()
VersionMan.main()
2. 配置Jenkins服务器 执行zentao_add_edit_version.py
Windows执行python3脚本:
cd ..
set build_info="{'building_number':'%BUILD_NUMBER%','execution':'108','product':'35','project_zt':'104','build_name':'*v1.1.0.h5.***','build_name_keywords':'*,h5','scmPath':'%GIT_URL%','filePath':'%GIT_URL%','desc_doc':'%SCM_CHANGELOG%','files_path':r'%WORKSPACE%\\risk-pro\\dist,%WORKSPACE%','branch':'release','project_name':'%PROJECT_NAME%','version_file':r'%WORKSPACE%\risk-pro\public\version.txt'}"
python D:\anaconda\jks\zentao_add_edit_version.py %build_info%
centos7执行python3脚本:
source /usr/python/envs/everyday/bin/activate #激活虚拟环境
deactivate #退出虚拟环境
source /usr/python/envs/everyday/bin/activate
cd ..
build_info="{'building_number':'$BUILD_NUMBER','execution':'108','product':'35','project_zt':'104','build_name':'*v1.1.0.web.***','build_name_keywords':'*,web','scmPath':'$GIT_URL','filePath':'$GIT_URL','desc_doc':'$SCM_CHANGELOG','files_path':r'${WORKSPACE}/dist,$WORKSPACE','branch':'release','project_name':'$PROJECT_NAME','version_file':r'${WORKSPACE}/public/version.txt'}"
python3 /jenkins/jks/zentao_add_edit_version.py "$build_info"
deactivate