iOS开发实现每日构建生成ipa包

目标:Xcode的工程实现自动编译并且生成ipa包,上传到公司内部服务器,同时发送邮件给各相关人员,让他们下载安装。
环境:Mac OSX 10.x,Xcode5.x或6.x,Python2.7。代码使用git管理。 支持ssh/ftp上传的服务器,最好开启HTTP服务(下载文件)。
方法:通过shell命令编译生成ipa包,使用Python代码实现ftp上传并且发送出邮件。以下为详细代码。
假设:工程名和工程所在目录都是tastegameapp,ftp服务器192.168.0.0使用的是ssh/sftp协议,邮件发送服务器为QQ邮箱。

shell命令dailybuild.sh

#先checkout代码到一个地方然后执行脚本
#定义
varProj="tastegameapp" #工程名,XCode编译target名。一般是一样的。
varProjDir="tastegameapp"
varFile=`date '+%Y-%m-%d'`  #此处最外层符号`会将date解释为当前的时间
#进人工程所在目录
cd ../$varProjDir
#删除以前编译生成的文件
rm -rf build/"$varProj".build/Debug-iphoneos/"$varProj".build   
rm -rf build/Debug-iphoneos/"$varProj".app
rm -rf build/Debug-iphoneos/"$varProj".app.dSYM
#开始编译
xcodebuild -configuration Debug -target "$varProj"  >../ci/"$varProj"-log-$varFile.txt
cd ../ci
mkdir -p ipa/Payload
#删除旧文件
rm -rf ipa/Payload/"$varProj".app
rm -rf ipa/Payload/"$varProj".app.dSYM
#将编译出的程序和符号拷贝到Payload目录下
cp -r ../"$varProjDir"/build/Debug-iphoneos/"$varProj".app ./ipa/Payload/
cp -r ../"$varProjDir"/build/Debug-iphoneos/"$varProj".app.dSYM ./ipa/Payload/
cd ipa
#输出ipa文件,名字带有当日日期
zip -r "$varProj""$varFile".ipa Payload
cd ..
echo '正在上传文件'
python ftpupload.py
echo '正在发送邮件'
python sendemail.py

ftp上传,ftpupload.py

#!/usr/bin/python   
#-*-coding=utf-8-*-   
import sys
import paramiko
import time
import os
import getpass
import socket
from datetime import date, timedelta

#当天的时间
workdate=time.strftime('%Y-%m-%d')

#open an SSH transport
host = "192.168.0.0"
port = 22
transport = paramiko.Transport((host, port))
#authenticate
password = "密码"       #用户名密码记得替换
username = "用户名"
transport.connect(username = username, password = password)
#transport.set_missing_host_key_policy(paramiko.AutoAddPolicy())

#start the SFTP client
filename = "tastegameapp"+ workdate +".ipa"
localfile = "ipa/"+filename
remotefile = "/var/www/html/debug-ipa/"+filename
sftp = paramiko.SFTPClient.from_transport(transport)
sftp.put(localfile,remotefile)
logfile = "tastegameapp-log-"+workdate+".txt"
remotelog = "/var/www/html/debug-ipa/"+logfile
sftp.put(logfile,remotelog)
print "命令执行成功,文件已经上传"
#close the SFTP connection and the transport
sftp.close()
transport.close()

发送邮件,sendemail.py

#!/usr/bin/python
#coding=utf-8


import email
import mimetypes
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
import smtplib
import logging
import sys
import os
import time

reload(sys)
sys.setdefaultencoding('utf8')

mailDict = {} #邮件配置信息

###################
#日志辅助类
#################
class Logger:
    LOG_RELEASE= "releae"
    LOG_RELEASE_FILE = "/tmp/pyMail.log"

    def __init__(self, log_type):
        self._logger = logging.getLogger(log_type)
        if log_type == Logger.LOG_RELEASE:
            self._logFile = Logger.LOG_RELEASE_FILE
        handler = logging.FileHandler(self._logFile)
        if log_type == Logger.LOG_RELEASE:
            formatter = logging.Formatter('%(asctime)s ********* %(message)s')
        else:
            formatter = logging.Formatter('%(message)s')
        handler.setFormatter(formatter)
        self._logger.addHandler(handler)
        self._logger.setLevel(logging.INFO)

    def log(self, msg):
        if self._logger is not None:
            self._logger.info(msg)

MyLogger = Logger(Logger.LOG_RELEASE) #Logger

def initMailConf():#初始化邮件配置信息
    global mailDict
    timestr = time.strftime('%Y-%m-%d %A %X %Z', time.localtime())
    mailDict['server'] = "smtp.exmail.qq.com"
    mailDict['port'] = '465'
    mailDict['user'] = "#邮箱用户名#"   #用户名密码请替换为自己的
    mailDict['password'] = "#密码#"
    mailDict["from"] = ""  #显示在邮件里的发件人
    mailDict["to"] = ""   #收件人
    mailDict["subject"]  = "tastegameapp Daily Build " + timestr
    mailDict["text"] = "现在tastegameapp项目已经做了每日构建,每天都可以生成daily build文件\r\n安装文件可以在公司内网下载。地址下载地址\r\n"+"http://192.168.0.0/debug-ipa/tastegameapp" + time.strftime('%Y-%m-%d', time.localtime()) + ".ipa \r\n如果需要查看输出日志,详情请看附件。"
    
def sendMail(paramMap):#发送邮件
    smtp = smtplib.SMTP_SSL()
    msgRoot = MIMEMultipart('related')
    msgAlternative = MIMEMultipart('alternative')
    if paramMap.has_key("server") and paramMap.has_key("user") and paramMap.has_key("password"):
        try:
            smtp.set_debuglevel(1)
            smtp.connect(paramMap["server"], paramMap["port"])
            smtp.login(paramMap["user"], paramMap["password"])
        except Exception, e:
            MyLogger.log("smtp login exception!")
            return False
    else:
        MyLogger.log("Parameters incomplete!")
        return False

    if paramMap.has_key("subject") == False or  paramMap.has_key("from")== False or paramMap.has_key("to") == False:
        MyLogger.log("Parameters incomplete!")
        return False
    msgRoot['subject'] = paramMap["subject"]
    msgRoot['from'] = paramMap["from"]
    if paramMap.has_key("cc"):
        msgRoot['cc'] = paramMap["cc"]
    msgRoot['to'] = paramMap["to"]
    msgRoot.preamble = 'This is a multi-part message in MIME format.' 
    msgRoot.attach(msgAlternative)
    TempAddTo = paramMap["to"]
    if paramMap.has_key("text"):
        msgText = MIMEText(paramMap["text"], 'plain', 'utf-8')
        msgAlternative.attach(msgText)
    if paramMap.has_key("html"):
        msgText = MIMEText(paramMap["html"], 'html', 'utf-8')
        msgAlternative.attach(msgText)  
    if paramMap.has_key("image"):
        fp = open(paramMap["image"], 'rb')
        msgImage = MIMEImage(fp.read())
        fp.close()
        msgImage.add_header('Content-ID', '<image>' )
        msgRoot.attach(msgImage) 
    if paramMap.has_key("cc"):
        TempAddTo = paramMap["to"] + "," + paramMap["cc"]   
    if TempAddTo.find(",") != -1:
        FinallyAdd = TempAddTo.split(",")
    else:
        FinallyAdd = TempAddTo 
        
    #构造附件 
    fileName = "tastegameapp-log-"+time.strftime('%Y-%m-%d', time.localtime())+".txt"
    basename = os.path.basename(fileName)
    if os.path.exists(fileName): #数据文件存在
        data = open(fileName, 'rb')
        att = MIMEText(data.read(), 'base64', 'utf8')
        att["Content-Type"] = 'application/octet-stream'
        att["Content-Disposition"] = 'attachment; filename="%s"' % basename
        msgRoot.attach(att)

    smtp.sendmail(paramMap["from"], FinallyAdd, msgRoot.as_string())
    smtp.quit()  
    return True
     
def process():
    global mailDict
    initMailConf()
    sendMail(mailDict)
    
if __name__ == "__main__":
    process()

本文章为原创,引用请保留链接 http://ccimage.cn/2014-11/ios-dev-dailybuild-ipa.html