Super灬LV

SwiftySwift

Xcode9.3 xcodebuild 自动化打包发布到蒲公英并发送邮件通知

前言

从事iOS最经常遇到的莫过于 测试同学过来 通知你 赶紧发个包 ~ ~ 。。然后一顿操作。作为一个有着多年iOS开发经验的程序猿,肯定不能再走寻常路,遂决定亲自写个脚本 ps: 其实是python忘的差不多了(本来也不咋的) 赶紧练练手 复习复习

相关配置

前提:mac配置python3
IDE工具:Pycharm
网络请求工具:Requests
脚本工具: Xcode

代码实现

编译并打包生成ipa文件

清理工程(command+shift+k)

  def cleanProject(self):
        global isWorkSpace
        if isWorkSpace:
            os.system(
                'cd %s;xcodebuild -workspace %s.xcworkspace -scheme %s clean' % (mainPath,  targetName, targetName))
        else:
            os.system('cd %s;xcodebuild -target %s clean' % (mainPath, targetName))
        return

编译工程

  def buildProject(self):

        if isWorkSpace:
            os.system(
                "cd %s;xcodebuild -workspace %s.xcworkspace -scheme %s -configuration Release  -archivePath %s/%s.xcarchive clean archive" % (
                    mainPath, targetName, targetName, archivePath, targetName))
        else:
            os.system(
                "cd %s;xcodebuild -project %s.xcodeproj -scheme %s -configuration Release  -archivePath %s/%s.xcarchive clean archive" % (
                    mainPath, targetName, targetName, archivePath, targetName))
        return

导出并生成IPA文件

        def cerateIPA(self):
        if isDev:
          os.system("cd %s; xcodebuild -exportArchive -archivePath %s/%s.xcarchive -exportPath %s/%s -exportOptionsPlist %s/DevExportOptions.plist -allowProvisioningUpdates -quiet" % (
                mainPath, archivePath, targetName, archivePath, targetName, archivePath))
        else:
            os.system(
                "cd %s; xcodebuild -exportArchive -archivePath %s/%s.xcarchive -exportPath %s/%s -exportOptionsPlist %s/DisExportOptions.plist -allowProvisioningUpdates -quiet" % (
                    mainPath, archivePath, targetName, archivePath, targetName, archivePath))
        return
上传到蒲公英

调用蒲公英API,将ipa文件上传至自己账户

   def uploadToPGY(self):
        print("上传到蒲公英")
        path = "%s/%s.ipa/%s.ipa" % (archivePath, targetName, targetName)
        f_op = open(path, 'rb')
        print(path)
        print(f_op.readlines())

        if os.path.exists(path):
            print("找到ipa文件")
            # 请求参数字典
            params = {
                'uKey': PGY_UKey,
                '_api_key': PGY_APIKey,
                'installType': PGY_INSTALL_type,
                'password': PGY_PW,
                'updateDescription': PGY_DES
            }

            response = requests.post(PGY_URL, files={"file": open(path, 'rb')}, data=params)


        else:
            print("没有找到ipa文件")
上传给Itunes Connect
  """
          altoolPath="/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
          ${altoolPath} --validate-app -f ${ipaPath} -u xxxxxx -p xxxxxx -t ios --output-format xml >>
          ${altoolPath} --upload-app -f ${ipaPath} -u xxxxxx -p xxxxxx -t ios --output-format xml

          --validate-app 您要验证指定的 App
          --upload-app   您要上传指定的 App
          -f file        正在验证或上传的 App 的路径和文件名。
          -u username    您的用户名
          -p password    您的用户密码
          --output-format [xml | normal]   您想让 Application Loader 以结构化的 XML 格式还是非结构化的文本格式返回输出信息。默认情况下,Application Loader 以文本格式返回输出信息


          success-message
          product-errors

          """
    def uploadToItunesConnect(self):
        print("上传到ItunesConnect")
        altoolPath = "/Applications/Xcode.app/Contents/Applications/Application\ Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/altool"
        validateresult = os.popen("%s --validate-app -f %s/ -u %s -p %s -t ios --output-format xml" % (altoolPath, ipaPath, DeveloperAccountName, DeveloperAccountPWD)).read()
        uploadresult = os.popen("%s --upload-app -f %s -u %s -p %s -t ios --output-format xml" % (altoolPath, ipaPath, DeveloperAccountName, DeveloperAccountPWD)).read()

        # 处理plist文件
        pl = plistlib.readPlistFromBytes(str(uploadresult).encode())

        print(pl["product-errors"])
        print("输出成功")

        fp = open("1.plist", 'w')
        fp.write(str(uploadresult))
        fp.close()

        if("success-message" in str(uploadresult)):
             print("上传成功")
        elif("product-errors" in str(uploadresult)):
             print("上传失败 %s", uploadresult)
        else:
             print("未知失败原因")
        return
        self.sendMail({"appName": targetName,
                        "appVersion": "appVersion",
                        "appShortcutUrl": "暂无",
                        "appUpdated": "暂无"
                       })
发送邮件给相关测试人员
    def sendMail(self, responseResult):
        print("发送邮件")

        # if not os.path.exists("%s/%s.ipa/%s.ipa" % (archivePath, targetName, targetName)):
        #     print("发送邮件 没有找到ipa文件")
        #     return
        msgInfo = "<html><body><h1>您好,新版本信息如下</h1><table><tr><th>应用名称</th><th>%s</th></tr><tr><th>应用版本</th><th>%s</th></tr><tr><th>应用地址</th><th>%s</th></tr><tr><th>应用更新时间</th><th>%s</th></tr></table></body></html>" % (
                      responseResult["appName"], str(responseResult["appVersion"]), str(responseResult["appQRCodeURL"]), str(responseResult["appUpdated"]))
        print(msgInfo)
        msg = MIMEText(msgInfo, 'html', 'utf-8')
        msg["Subject"] = MAIL_SUBJECT
        msg['From'] = self._format_addr('客户端 <%s>' % MAIL_FROM)
        msg['To'] = self._format_addr('测试同学 <%s>' % MAIL_TO)
        try:
            # // 创建一个SMTP对象
            server = smtplib.SMTP()
            print(server)
            # // 通过connect方法链接到smtp主机
            server.connect(MAIL_HOST, "25")
            server.set_debuglevel(1)
            # // 启动安全传输模式
            # server.starttls()
            # // 登录邮箱
            # 校验用户,密码
            server.login(MAIL_FROM, Mail_PassWord)
            # // 发送邮件
            server.sendmail(MAIL_FROM, [MAIL_TO], msg.as_string())
            server.quit()
            # 发送成功并打印
            print("邮件发送成功 \n发送人:%s\n发送内容:\n%s接收者:%s " % (MAIL_FROM, msg, MAIL_TO))

        except Exception as e:
            print("邮件发送失败:" + str(e))

备注

1、自动化打包方式的选择

 自动化打包方式
 1、xcodebuild + xcrun(不推荐)
 2、 arhive+exportArchive

之前的打包方式大多都是采用第一种 适用于Xcode8之前的版本。 本人之前用的shell 也是找的网上大佬 用第一种方式实现 的。 本人在实践中,最后一只过不去,遂直接选择用第二种。实验证明可行。

2、Pycharm破解

感谢网上的大佬,我使用的是pycharm 2018.也是从网上找的激活方式。

3、最后附上完整的源码

"""
 1、找到工程 并打包生成ipa文件
 2、ipa文件上传
 3、发送邮件给测试人员

 自动化打包方式
 xcodebuild + xcrun(不推荐)
 arhive+exportArchive

  """

import os
import getpass
import requests
import smtplib
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
from email.header import Header

# 蒲公英相关参数设置
PGY_URL = "http://www.pgyer.com/apiv1/app/upload" //蒲公英的的接口
PGY_UKey = "************"                /// 蒲公英账户下的用户key
PGY_APIKey = "*********"                 /// 蒲公英账户下的接口key
PGY_PW = ""                              ///应用安装密码 (选填)
PGY_INSTALL_type = 1                    /// 应用安装方式 值为(1,2,3)。1:公开,2:密码安装,3:邀请安装。默认为1公开
PGY_DES = ""                            /// 应用更新描述信息
# 项目参数相关设置
isWorkSpace = False                     /// 是否是WorkSpace

# 项目的相关配置
mainPath = "/Users/xxxxxx/xxxxx/xxxx/xxxxxxx"    /// 项目的主路径
targetName = "xxxxxxx"                          ///项目的名称 类似xxxxx.xcodeproj
certificateName = "xxxxxxxxxxxx"                 ///证书名 Automatic 模式下不需要
archivePath = "/Users/xxxxxxxx/xxxxx/xxxxxx/"   /// 生成的解压缩文件路径

# 邮件的相关参数设置
MAIL_HOST = "smtp.xxxxx.com"                  /// 邮箱host        
# //邮件标题
MAIL_SUBJECT = "测试版本已经发布"               /// 邮箱的标题
# //收件人
MAIL_TO = "xxxxx@xxxxx.cn"                   /// 收件人邮箱地址 可传数组
# //发件人MAIL_
MAIL_FROM = "xxxxx@xxxxx.cn"                 /// 发件人邮箱地址
Mail_PassWord = "************"               /// 发件人邮箱登录密码


class IPAHelper(object):
    def __init__(self):
        print("工具初始化")


     # clean工程
    def cleanProject(self):
        global isWorkSpace
        if isWorkSpace:
            os.system(
                'cd %s;xcodebuild -workspace %s.xcworkspace -scheme %s clean' % (mainPath, targetName, targetName))
        else:
            os.system('cd %s;xcodebuild -target %s clean' % (mainPath, targetName))
        return
    # build工程
    def buildProject(self):

        if isWorkSpace:
            os.system(
                "cd %s;xcodebuild -workspace %s.xcworkspace -scheme %s -configuration Release  -archivePath %s/%s.xcarchive clean archive" % (
                    mainPath, targetName, targetName, archivePath, targetName))
        else:
            os.system(
                "cd %s;xcodebuild -project %s.xcodeproj -scheme %s -configuration Release  -archivePath %s/%s.xcarchive clean archive" % (
                    mainPath, targetName, targetName, archivePath, targetName))
        return
    #生成ipa文件
    def cerateIPA(self):
        os.system(
            "cd %s; xcodebuild -exportArchive -archivePath %s/%s.xcarchive -exportPath %s/%s.ipa -exportOptionsPlist %s/IPA.plist -allowProvisioningUpdates -quiet" % (
                mainPath, archivePath, targetName, archivePath, targetName, archivePath))

        return
    # 上传到蒲公英
    @property
    def uploadToPGY(self):
        print("上传到蒲公英")
        path = "%s/%s.ipa/%s.ipa" % (archivePath, targetName, targetName)
        f_op = open(path, 'rb')
        print(path)
        print(f_op.readlines())

        if os.path.exists(path):
            print("找到ipa文件")
            # 请求参数字典
            params = {
                'uKey': PGY_UKey,
                '_api_key': PGY_APIKey,
                'installType': PGY_INSTALL_type,
                'password': PGY_PW,
                'updateDescription': PGY_DES
            }

            response = requests.post(PGY_URL, files={"file": open(path, 'rb')}, data=params)

            if str(response.json()["code"]) == "0":
                self.sendMail(response.json()["data"])


        else:
            print("没有找到ipa文件")

    def _format_addr(self, s):
        name, addr = parseaddr(s)
        return formataddr((Header(name, 'utf-8').encode(), addr))
    #发送邮件给测试人员
    def sendMail(self, responseResult):
        print("发送邮件")

        # if not os.path.exists("%s/%s.ipa/%s.ipa" % (archivePath, targetName, targetName)):
        #     print("发送邮件 没有找到ipa文件")
        #     return
        msgInfo = "<html><body><table><tr><th>应用名称</th><th>%s</th></tr><tr><th>应用版本</th><th>%s</th></tr><tr><th>应用地址</th><th>%s</th></tr><tr><th>应用更新时间</th><th>%s</th></tr></table></body></html>" % (
                      responseResult["appName"], str(responseResult["appVersion"]), str(responseResult["appQRCodeURL"]), str(responseResult["appUpdated"]))
        print(msgInfo)
        msg = MIMEText(msgInfo, 'html', 'utf-8')
        msg["Subject"] = MAIL_SUBJECT
        msg['From'] = self._format_addr('iOS开发 <%s>' % MAIL_FROM)
        msg['To'] = self._format_addr('白天不懂夜的黑 <%s>' % MAIL_TO)
        try:
            # // 创建一个SMTP对象
            server = smtplib.SMTP()
            print(server)
            # // 通过connect方法链接到smtp主机
            server.connect(MAIL_HOST, "25")
            #server.set_debuglevel(1)
            # // 启动安全传输模式
            # server.starttls()
            # // 登录邮箱
            # 校验用户,密码
            server.login(MAIL_FROM, Mail_PassWord)
            # // 发送邮件
            server.sendmail(MAIL_FROM, [MAIL_TO], msg.as_string())
            server.quit()
            # 发送成功并打印
            print("邮件发送成功 \n发送人:%s\n发送内容:\n%s接收者:%s " % (MAIL_FROM, msg, MAIL_TO))

        except Exception as e:
            print("邮件发送失败:" + str(e))


if __name__ == '__main__':
    helper = IPAHelper()

    # clean工程
    helper.cleanProject()
    # build app
    helper.buildProject()
    # 生成ipa
    helper.cerateIPA()
    # 上传到蒲公英
    helper.uploadToPGY()
    # 发送邮件给测试人员
    helper.sendMail();

4、xcodebuild tools 简介
在上面的代码中,我们使用到了一些 xcodebuild 的一下简单命令 接下来 我们就解释一下

clean 指令

  # -target {工程的名字}
  os.system('cd %s;xcodebuild -target %s clean' % (mainPath, targetName))

build指令

# -project {项目名称.xcodeproj}  
# -scheme {项目名称}
# -configuration  {构建版本(Debug or Release)}
# -archivePath  {archive文件的生成路径}
# clean archive  清理
 os.system("cd %s;xcodebuild -project %s.xcodeproj -scheme %s -configuration Release  -archivePath %s/%s.xcarchive clean archive" % (
                    mainPath, targetName, targetName, archivePath, targetName))

exportArchive生成ipa指令

#-archivePath {之前生成archive文件的路径}
#-exportPath {到处ipa的路径}
# -exportOptionsPlist exportOptionsPlist文件路径 

# -allowProvisioningUpdates 允许配置更新
os.system("cd %s; xcodebuild -exportArchive -archivePath %s/%s.xcarchive -exportPath %s/%s.ipa -exportOptionsPlist %s/IPA.plist -allowProvisioningUpdates -quiet"  % (mainPath, archivePath, targetName, archivePath, targetName, archivePath))

exportOptionsPlist文件格式 如果不想编辑 也可以从之前的ipa包中找到

这里写图片描述

# uploadSymbols
# uploadBitcode
# compileBitcode
# method 打包方式(development、app-store、ad-hoc、enterprise)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>uploadSymbols</key>
    <true/>
    <key>uploadBitcode</key>
    <true/>
    <key>method</key>
    <string>development</string>
    <key>compileBitcode</key>
    <true/>
</dict>
</plist>

altool打包到Itunes Connect

您可以使用 Application Loader 的命令行工具 altool,验证 App 二进制文件并将其上传至 App Store。

若要在将有效构建版本上传或自动上传至 App Store 之前验证构建版本,您可在持续集成系统中包含 altool。altool 位于以下文件夹:Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/。

若要运行 altool,请在命令行指定以下一项操作:

$ altool --validate-app -f file -u username [-p password] [--output-format xml]
$ altool --upload-app -f file -u username [-p password] [--output-format xml]

 --validate-app 您要验证指定的 App
 --upload-app   您要上传指定的 App
 -f file        正在验证或上传的 App 的路径和文件名。
 -u username    您的用户名
 -p password    您的用户密码
 --output-format [xml | normal]   您想让 Application Loader 以结构化的 XML 格式还是非结构化的文本格式返回输出信息。默认情况下,Application Loader 以文本格式返回输出信息


 success-message   成功标识
 product-errors    失败标识

最后 发现了一个看起来比较好用的工具:nomad-cli

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvchenqiang_/article/details/79916585
个人分类: iOS Python
上一篇Distribution requires enrollment in Apple Developer Program
下一篇Python 处理iOS ipa文件里面的.plist
想对作者说点什么? 我来说一句

iOS 9.3 Xcode开发包

2016年03月25日 11.87MB 下载

Xcode7.3 iOS9.3

2016年05月24日 11.87MB 下载

Xcode iOS9.3配置包

2016年03月25日 11.87MB 下载

没有更多推荐了,返回首页

关闭
关闭