├── AndroidDeploy ├── androidarchive.py ├── androiddeploy.py ├── androidtest.py └── libs │ ├── __init__.py │ ├── chinanetcenter.py │ ├── dingtalk.py │ ├── fir.py │ └── libsoss.py ├── IOSDeploy.sh └── README.md /AndroidDeploy/androidarchive.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | import os 5 | import sys 6 | import shutil 7 | from libs.libsoss import OssUtils 8 | from libs.dingtalk import DingTalkNotice 9 | import hashlib 10 | from oss.oss_api import * 11 | 12 | 13 | class AndroidDeploy(): 14 | 15 | def __init__(self): 16 | 17 | self.channel_to_deploy = str(os.getenv("ChannelToDeploy")) 18 | self.workspace = '/usr/share/tomcat/.jenkins/workspace/Android-Test' 19 | self.build_dir = self.workspace + '/app/build/outputs/apk/' 20 | self.channel_to_deploy = os.getenv('ChannelToDeploy') 21 | self.archive_pack_apk = '/data/android_archive/android/' 22 | #self.storepass = '' 23 | #self.keypass = '' 24 | #self.keystore = self.workspace+'/key.jks' 25 | 26 | if not os.listdir(self.build_dir): 27 | sys.exit('APK文件不存在,请先构建!!!') 28 | 29 | self.version = os.popen("ls "+ self.build_dir +"|grep -vE *unaligned|head -1|awk -F '-|.apk' '{print $3}'").read().strip() 30 | print self.version 31 | 32 | if self.version: 33 | self.now_apk = self.build_dir + "app-release-"+ self.version + ".apk ".strip() 34 | else: 35 | sys.exit('APK文件不存在,请先构建2!!!') 36 | 37 | if not os.path.exists(self.archive_pack_apk + self.version): 38 | os.mkdir(self.archive_pack_apk + self.version) 39 | 40 | if not os.path.exists(self.build_dir + 'META-INF'): 41 | os.mkdir(self.build_dir + 'META-INF') 42 | 43 | self.archive_pack_apk_dir = self.archive_pack_apk + self.version + "/" 44 | 45 | 46 | 47 | def archive_apk(self): 48 | 49 | tag = os.popen('cd '+self.workspace+'&& git pull > /dev/null && git tag -l|grep "^v[0-9]" |grep '+self.version).read().strip() 50 | 51 | if tag: 52 | 53 | print "已经创建发布时Tag:" + tag 54 | 55 | else: 56 | 57 | try: 58 | os.popen('cd '+self.workspace+' && git checkout master && git tag V' + self.version + ' && git push origin --tags ' + "V" + self.version) 59 | except: 60 | sys.exit('Tag创建失败,请重新执行脚本!!!') 61 | 62 | if self.channel_to_deploy == 'All Channels': 63 | 64 | shutil.rmtree(self.archive_pack_apk + self.version) 65 | os.mkdir(self.archive_pack_apk + self.version) 66 | 67 | f = open(self.workspace + "/" + 'channels','r') 68 | for channel in f.readlines(): 69 | 70 | channel = channel.strip() 71 | 72 | if channel == 'ma': 73 | apk_name = 'MA' 74 | 75 | elif "_" in channel: 76 | apk_name = 'ma_android'+ channel 77 | 78 | else: 79 | apk_name = 'ma_android'+ "_" + channel 80 | try: 81 | self.pack_apk(channel,self.archive_pack_apk_dir,apk_name) 82 | 83 | except: 84 | sys.exit('签名失败,请检查!!!') 85 | else: 86 | 87 | if "_" in self.channel_to_deploy: 88 | apk_name = 'ma_android'+ self.channel_to_deploy 89 | else: 90 | apk_name = 'ma_android'+ "_" + self.channel_to_deploy 91 | self.pack_apk(self.channel_to_deploy,self.archive_pack_apk_dir,apk_name) 92 | 93 | return self.archive_pack_apk 94 | 95 | def pack_apk(self,channel,archive_pack_apk_dir,apk_name): 96 | 97 | old_channel_file = os.popen("unzip -l " + self.now_apk + "|grep 'META-INF/channel'|awk '{ print $NF }'").read().strip() 98 | print old_channel_file 99 | remove_old_channel_file = os.popen("cd " + self.build_dir + "&& zip -d " + self.now_apk + '\t' + old_channel_file).read().strip() 100 | new_channel_file = os.popen("cd " + self.build_dir + "&&touch " + "META-INF/channel_" + channel).read().strip() 101 | print new_channel_file 102 | os.popen( "cd " + self.build_dir + "&& zip -u " + self.now_apk + " " + "META-INF/channel_" + channel) 103 | print self.now_apk,archive_pack_apk_dir + apk_name.strip() + '.apk' 104 | dest = archive_pack_apk_dir + apk_name.strip() + '.apk' 105 | shutil.copyfile(self.now_apk,dest ) 106 | 107 | if __name__ == "__main__": 108 | 109 | pack = AndroidDeploy() 110 | libsoss = OssUtils() 111 | pack.archive_apk() 112 | version_dir = pack.version 113 | filespath = pack.archive_pack_apk + version_dir + "/" 114 | channel = pack.channel_to_deploy 115 | archive_download_url = libsoss.upload_oss(filespath,version_dir,channel) 116 | content = '各渠道正式包' + version_dir + '版本已经归档OSS(未发布),代码库已经自动创建发布Tag:' + version_dir + '访问地址为:http://download.simlinux.com/app/archive/' + version_dir +'/index.html' 117 | notice = DingTalkNotice() 118 | notice.post_msg(content=content) 119 | -------------------------------------------------------------------------------- /AndroidDeploy/androiddeploy.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | import os 5 | import sys 6 | import shutil 7 | from libs.libsoss import OssUtils 8 | from libs.dingtalk import DingTalkNotice 9 | from libs.chinanetcenter import ChinaNetCenter 10 | import hashlib 11 | from oss.oss_api import * 12 | 13 | 14 | if __name__ == "__main__": 15 | 16 | libsoss = OssUtils() 17 | url_list = libsoss.copy_oss() 18 | content = '_11到_40,渠道包已经上线,可通过http://download.simlinux.com/app/ma_android_11.apk访问,相关CDN地址已经刷新!!!' 19 | chinanetcenter = ChinaNetCenter() 20 | chinanetcenter.flush_files(url_list) 21 | notice = DingTalkNotice() 22 | notice.post_msg(content=content) 23 | -------------------------------------------------------------------------------- /AndroidDeploy/androidtest.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | from libs.fir import FirUtils 5 | from libs.dingtalk import DingTalkNotice 6 | import requests 7 | import requests.packages.urllib3.util.ssl_ 8 | print(requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS) 9 | requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL' 10 | 11 | 12 | if __name__ == "__main__": 13 | 14 | fir = FirUtils() 15 | version = fir.upload_apk() 16 | pic_msg_url = fir.get_qrcode() 17 | title ='Android测试发布' 18 | text = '本次发布为Debug-' + version + ',已经上传fir,请扫描二维码安装!' 19 | notice = DingTalkNotice() 20 | notice.send_link(title=title,text=text,pic_url=pic_msg_url[0],message_url=pic_msg_url[1]) 21 | -------------------------------------------------------------------------------- /AndroidDeploy/libs/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'geekwolf' 2 | -------------------------------------------------------------------------------- /AndroidDeploy/libs/chinanetcenter.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | import requests 5 | import json 6 | import datetime 7 | import hashlib 8 | 9 | class ChinaNetCenter(): 10 | 11 | def __init__(self): 12 | 13 | self.username = '推送用户' 14 | self.password = '推送密码' 15 | 16 | def flush_files(self,url_list): 17 | #print url_list 18 | time = datetime.datetime.now().strftime("%Y%m%d") 19 | headers = {'content-type': 'application/json'} 20 | post_url = 'http://cm.chinanetcenter.com/CM/cm-publish!json.do' 21 | check_code = hashlib.new("md5", time + self.username + 'chinanetcenter' + self.password).hexdigest() 22 | __content = {"user_name":self.username,"check_code":check_code,"need_feedback":"0","fetchOption":"N","url_list":url_list} 23 | content = json.dumps(__content) 24 | print content 25 | try: 26 | r = requests.post(post_url, data=content, headers=headers) 27 | print r.text 28 | except: 29 | print '刷新失败,请重试!!!' 30 | 31 | -------------------------------------------------------------------------------- /AndroidDeploy/libs/dingtalk.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | import json 5 | import requests 6 | import os 7 | import qrcode 8 | import base64 9 | import requests.packages.urllib3.util.ssl_ 10 | print(requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS) 11 | requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL' 12 | 13 | class DingTalkNotice(): 14 | 15 | def __init__(self,**kwargs): 16 | 17 | #self.source_image = '' 18 | self.sender = "发送这ID" 19 | self.chat_id = '会话ID' 20 | 21 | def get_token(self): 22 | 23 | get_url = 'https://oapi.dingtalk.com/gettoken?corpid=钉钉后台coreid&corpsecret=钉钉后台coresecret' 24 | 25 | try: 26 | r = requests.get(get_url) 27 | except: 28 | print "DingTalk token获取失败!!!" 29 | 30 | data = json.loads(r.content) 31 | access_token = data['access_token'] 32 | return access_token 33 | 34 | def post_images(self): 35 | 36 | __access_token = self.get_token() 37 | headers = {'content-type': 'application/json'} 38 | post_url = 'https://oapi.dingtalk.com/media/upload?access_token=' + __access_token + '&type=image' 39 | files = {'media': open(self.source_img,'rb')} 40 | try: 41 | r = requests.post(post_url, files=files) 42 | media_id = json.loads(r.text)['media_id'] 43 | templates = {"chatid": self.chat_id,"sender":self.sender,"msgtype":"image","image":{"media_id":media_id}} 44 | post_data = json.dumps(templates) 45 | r = requests.post(post_url, data=post_data, headers=headers) 46 | 47 | except: 48 | print "DingTalk POST数据失败!!!" 49 | 50 | return r.text 51 | 52 | def post_msg(self,content): 53 | 54 | __access_token = self.get_token() 55 | 56 | headers = {'content-type': 'application/json'} 57 | post_url = 'https://oapi.dingtalk.com/chat/send?access_token=' + __access_token 58 | templates = { "chatid": self.chat_id, "sender": self.sender,"msgtype": "text", "text": { "content": content }} 59 | post_data = json.dumps(templates) 60 | try: 61 | r = requests.post(post_url, data=post_data, headers=headers) 62 | if json.loads(r.content)['errmsg'] != 'ok': 63 | print "发送者ID不存在(sender)" 64 | except: 65 | print "DingTalk POST数据失败!!!" 66 | 67 | return r.text 68 | 69 | def send_link(self,title,text,pic_url,message_url): 70 | 71 | __access_token = self.get_token() 72 | 73 | headers = {'content-type': 'application/json'} 74 | post_url = 'https://oapi.dingtalk.com/chat/send?access_token=' + __access_token 75 | templates = {"chatid":self.chat_id ,"sender":self.sender,"msgtype":"link","link":{"title": title ,"text": text,"pic_url": pic_url,"message_url":message_url}} 76 | post_data = json.dumps(templates) 77 | try: 78 | r = requests.post(post_url, data=post_data, headers=headers) 79 | print r.text 80 | except: 81 | print "DingTalk POST数据失败!!!" 82 | -------------------------------------------------------------------------------- /AndroidDeploy/libs/fir.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | import os 5 | import qrcode 6 | import base64 7 | 8 | class FirUtils(): 9 | 10 | def __init__(self): 11 | 12 | self.token = 'Fir Token' 13 | self.message_url ='http://fir.im/simlinux?utm_source=fir&utm_medium=qr' 14 | 15 | def upload_apk(self): 16 | 17 | workspace = os.getenv('WORKSPACE')+"/app/build/outputs/apk/" 18 | version = os.popen("ls "+workspace+"|grep -vE *unaligned|head -1|awk -F '-|.apk' \'{print $3}\' ").read().strip() 19 | 20 | if version: 21 | 22 | try: 23 | os.popen('fir login ' + self.token) 24 | fir_status = os.popen('fir publish ' + workspace + "app-debug-" + version + '.apk').read() 25 | print 'Fir 上传成功...'+'http://fir.im/simlinux' + '\t'+version 26 | 27 | return version 28 | except: 29 | sys.exit("Fir 上传失败!!!") 30 | 31 | 32 | def get_qrcode(self): 33 | 34 | img = qrcode.make(self.message_url) 35 | 36 | try: 37 | 38 | img.save('/tmp/simlinuxAqrcode.png') 39 | __pic = '/tmp/simlinuxAqrcode.png' 40 | f = open(__pic,'rb') 41 | pic_url = 'data:image/png;base64,' + base64.b64encode(f.read()) 42 | f.close() 43 | print "Qrcode二维码生成成功..." + '\t二维码访问地址:http://fir.im/simlinux' 44 | except: 45 | print 'Qrcode generation failure!!!' 46 | return pic_url,self.message_url 47 | -------------------------------------------------------------------------------- /AndroidDeploy/libs/libsoss.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | __author__ = 'geekwolf' 3 | 4 | import os 5 | import re 6 | import sys 7 | import hashlib 8 | from oss.oss_api import * 9 | from itertools import islice 10 | import oss2 11 | 12 | class OssUtils(): 13 | 14 | def __init__(self): 15 | 16 | self.workspace = '/usr/share/tomcat/.jenkins/workspace/Android-Test' 17 | self.endpoint="oss-cn-beijing.aliyuncs.com" 18 | self.accessKeyId, self.accessKeySecret="","" 19 | self.oss = OssAPI(self.endpoint, self.accessKeyId, self.accessKeySecret) 20 | self.header={"Cache-Control":"max-age=31536000","Content-Type":"application/vnd.android.package-archive"} 21 | self.bucket='staticfiles' 22 | self.filespath='' 23 | 24 | def upload_oss(self,filespath,version_dir,channel): 25 | 26 | version_dir = version_dir + "/" 27 | if channel == 'All Channels': 28 | 29 | listfile=os.listdir(filespath) 30 | file = '/tmp/index.html' 31 | os.remove(file) 32 | f=open('/tmp/index.html','a') 33 | 34 | for src in listfile: 35 | 36 | __channel_tmp = re.split('_|.apk',src) 37 | 38 | try: 39 | 40 | res=self.oss.put_object_from_file(self.bucket,"app/archive/"+ version_dir +src,filespath+src,headers=self.header) 41 | 42 | if __channel_tmp[0] == 'MA': 43 | __channel = 'MA' 44 | else: 45 | __channel = __channel_tmp[2] 46 | 47 | f.write(''+ __channel + '' +'\t' + 'http://download.simlinux.com/app/archive/'+ version_dir + str(src) + '
') 48 | except: 49 | 50 | system.exit('上传不完整或失败!!!') 51 | 52 | res=self.oss.put_object_from_file(self.bucket,"app/archive/"+ version_dir + 'index.html',file,headers={"Content-Type":"text/html"}) 53 | 54 | f.close() 55 | 56 | else: 57 | try: 58 | src = os.popen('cd '+ filespath +';ls *' + channel + '* ').read().strip() 59 | res = self.oss.put_object_from_file(self.bucket,"app/archive/"+ version_dir + src,filespath + src,headers=self.header) 60 | except: 61 | 62 | system.exit('上传不完整或失败!!!') 63 | 64 | archive_download_url = 'http://download.simlinux.com/app/archive/' + version_dir + 'index.html' 65 | return archive_download_url 66 | 67 | def copy_oss(self): 68 | 69 | auth = oss2.Auth(self.accessKeyId,self.accessKeySecret) 70 | bucket = oss2.Bucket(auth,self.endpoint,self.bucket) 71 | latest_version = max(os.popen('cd '+self.workspace+'&& git pull > /dev/null && git tag -l|grep "^v[0-9]"').read().strip().split("\n")).replace('v','') 72 | apk_name = 'app/archive/' + latest_version + '/' + 'MA.apk' 73 | dest_name = 'app/' + latest_version + '/' + 'MA.apk' 74 | result = bucket.copy_object(self.bucket, apk_name , dest_name) 75 | url_list = [] 76 | 77 | #要发布渠道11-41,可根据自身情况改写 78 | for channel in range(11,41): 79 | 80 | apk_name = 'app/archive/' + latest_version + '/ma_android_'+ str(channel) + '.apk' 81 | dest_name = 'app/' + latest_version + '/' + 'ma_android_'+ str(channel) + '.apk' 82 | url_list.append('http://download.simlinux.com/app/' + 'ma_android_'+ str(channel) + '.apk') 83 | result = bucket.copy_object(self.bucket, apk_name , dest_name) 84 | if result.resp.status == 200: 85 | print 'APK发布:由' + apk_name + '\t----->\t' + dest_name 86 | else: 87 | sys.exit('APK拷贝失败,请重试!!!') 88 | url_list.append('http://download.simlinux.com/app/' + 'MA.apk') 89 | return url_list 90 | -------------------------------------------------------------------------------- /IOSDeploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #说明依赖xcode工具集和fir(ruby包),by 公司ioser-lijing 4 | export LC_ALL=zh_CN.UTF-8;export LANG=zh_CN.UTF-8 5 | 6 | now=$(date +"%Y_%m_%d-%H_%M") 7 | 8 | projectname="IOS_TEST_DEMO" 9 | path_project="~/Desktop/workspace/${projectname}/" 10 | path_output_root="~/Desktop/buildtool/" 11 | path_workspace="$path_project/$p{rojectname}.xcworkspace" 12 | 13 | #info 14 | buildConfig="" 15 | scheme="" 16 | infoPlist="" 17 | version_project="" 18 | buildversion_project="" 19 | bundleid_project="" 20 | provisioning_profile="" 21 | code_sign_identity="" 22 | export_option_plist="" 23 | path_export="" 24 | 25 | if [[ ! -d $path_project ]];then 26 | echo "项目目录错误:\n$path_project" 27 | exit 28 | fi 29 | 30 | if [[ ! -d $path_output_root ]];then 31 | mkdir -p $path_output_root 32 | fi 33 | 34 | function project_info(){ 35 | local plist=$1 36 | version_project=`/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $plist` 37 | buildversion_project=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" $plist` 38 | } 39 | 40 | # function set_bversion(){ 41 | # local plist=$1 42 | # lastversion=project_build_version 43 | # echo "$lastversion" 44 | # newversion=100 45 | # `/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $newversion" "$plist"` 46 | # } 47 | 48 | #询问打包环境 49 | trap "echo -e \"\n已中断执行\";exit 0" INT 50 | for((i=1;i<=100;i++));do 51 | read -n1 -p "打包是否为国内版[y/n]:" input 52 | if [ "$input" == "y" ] ; then 53 | is_chin=true 54 | break 55 | else 56 | if [ "$input" == "n" ]; then 57 | is_chin=false 58 | break 59 | else 60 | echo "输入有误" 61 | fi 62 | fi 63 | input="" 64 | done 65 | 66 | echo "" 67 | 68 | for((i=1;i<=100;i++));do 69 | read -n1 -p "打包是否为测试版[y/n]:" input 70 | if [ "$input" == "y" ] ; then 71 | is_fir=true 72 | break 73 | else 74 | if [ "$input" == "n" ]; then 75 | is_fir=false 76 | break 77 | else 78 | echo "输入有误" 79 | fi 80 | fi 81 | input="" 82 | done 83 | 84 | echo "" 85 | 86 | #整理打包所需材料 87 | if $is_chin ; then 88 | #statements 89 | scheme="Demo_CN" 90 | infoPlist="${path_project}${projectname}/${scheme}_Info.plist" 91 | echo "$infoPlist" 92 | project_info $infoPlist 93 | if $is_fir ; then 94 | #statements 95 | buildConfig="Debug" 96 | 97 | bundleid_project="com.simlinux.demo" 98 | provisioning_profile="dc2117ff-9bfe-4234-8047-01d220238fdb" 99 | code_sign_identity="Demo Communication Co., Ltd" 100 | 101 | name_output="${scheme}-FIR_v${version_project}(b${buildversion_project})_${now}" 102 | export_option_plist="${path_output_root}/plist/FirExportOptions.plist" 103 | echo "国内测试包" 104 | else 105 | buildConfig="Release" 106 | 107 | bundleid_project="com.simlinux.demo" 108 | provisioning_profile="dc2117ff-9bfe-4234-8047-01d220238fdb" 109 | code_sign_identity="Demo Communication Co., Ltd" 110 | 111 | name_output="${scheme}-APS_v${version_project}(b${buildversion_project})_${now}" 112 | export_option_plist="${path_output_root}/plist/AppstoreExportOptions.plist" 113 | echo "国内正式包" 114 | fi 115 | 116 | else 117 | scheme="Demo_Inter" 118 | infoPlist="${path_project}${projectname}/${scheme}_Info.plist" 119 | project_info $infoPlist 120 | if $is_fir ; then 121 | #statements 122 | buildConfig="Debug" 123 | 124 | bundleid_project="com.simlinux.demo" 125 | provisioning_profile="dc2117ff-9bfe-4234-8047-01d220238fdb" 126 | code_sign_identity="Demo Communication Co., Ltd" 127 | 128 | name_output="${scheme}-FIR_v${version_project}(b${buildversion_project})_${now}" 129 | export_option_plist="${path_output_root}/plist/FirExportOptions.plist" 130 | echo "国际测试包" 131 | else 132 | buildConfig="Release" 133 | 134 | bundleid_project="com.simlinux.demo" 135 | provisioning_profile="dc2117ff-9bfe-4234-8047-01d220238fdb" 136 | code_sign_identity="Demo Communication Co., Ltd" 137 | 138 | name_output="${scheme}-APS_v${version_project}(b${buildversion_project})_${now}" 139 | export_option_plist="${path_output_root}/plist/AppstoreExportOptions.plist" 140 | echo "国际正式包" 141 | fi 142 | fi 143 | 144 | path_export="${path_output_root}/${scheme}/${name_output}" 145 | path_archive="${path_export}/$scheme.xcarchive" 146 | 147 | #clean 148 | xcodebuild clean -workspace ${path_workspace} -scheme ${scheme} -configuration ${buildConfig} 149 | 150 | #archive 151 | xcodebuild -workspace ${path_workspace} -scheme ${scheme} -configuration ${buildConfig} archive -archivePath ${path_archive} CODE_SIGN_IDENTITY="${code_sign_identity}" PROVISIONING_PROFILE="${provisioning_profile}" PRODUCT_BUNDLE_IDENTIFIER=${bundleid_project} 152 | 153 | #ipa 154 | xcodebuild -exportArchive -archivePath ${path_archive} -exportPath ${path_export} -exportOptionsPlist ${export_option_plist} 155 | 156 | if $is_fir ; then 157 | #statements 158 | #上传到Fir 159 | api_token_fir="Fir Token" 160 | fir login $api_token_fir 161 | ipaPath="${path_export}/${scheme}.ipa" 162 | echo "$ipaPath" 163 | fir publish $ipaPath 164 | fi 165 | 166 | 167 | #上传到AppStore 168 | 169 | 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### Android多渠道打包流程: 2 | - 执行gradlew clean清除build目录 3 | - 执行gradlew assemble编译打包Debug/Release(已自动签名) 4 | - 上传Debug包到Fir 5 | - 通过DingTalk发送通知信息到QA讨论组(发送提测apk包版本,下载地址及扫描下载二维码) 6 | - 提测不通过,修复bug后再次执行前四步 7 | - 提测通过后,点击Jenkins打包归档多渠道按钮,将执行生成多渠道包并归档包到本地目录/data/2.0.1/xxx.apk 8 | - 可选择此步上传归档文件到OSS 9 | - 点击Jenkins发布按钮将最新版本相关渠道归档拷贝至OSS发布目录 10 | - 刷新CDN生效 11 | - 通过DingTalk发送通知信息到QA讨论组哪些渠道已经发布 12 | 13 | #### IOS打包流程: 14 | - xcodebuild clean 清理build目录 15 | - xcodebuild archive 选择不同的环境/BundleID/ProvisionProfile/CodeSigningIdentify 编译,签名生成xcarchive文件 16 | - xcodebuild -exportArchive 打包生成ipa 17 | - 测试包自动上传Fir,生产包手动更新AppStore 18 | 19 | #### Android7.0打包方式: 20 | - http://tech.meituan.com/android-apk-v2-signature-scheme.html 21 | --------------------------------------------------------------------------------