美文网首页
iOS自动打包流程

iOS自动打包流程

作者: AdamShi | 来源:发表于2022-07-08 10:47 被阅读0次

    做完了iOS jenkins自动打包,做个小总结,最好申请个MacBook或者MacMini作为专门的打包机.
    有些老插件很久不更新了,新版本的jenkins不兼容这些老插件,或者有bug,最后几乎都是用脚本搞定的.
    有些问题找了很久,网上的文章很多都是很久以前的,现在不适用了; 而且很多文章看着日期是最近的,结果内容是拷贝其他的老文章; 有的甚至把很久以前过时不能用的文章抄袭过来,还特么的收费,鄙视CSDN....
    后面有时间的话还需要研究组件化怎么自动打包...
    参考文章: 参考文档

    一.开发环境

    1.安装Xcode

    2.安装homebrew, rvm等工具

    3.安装java,配置java环境变量,安装Jenkins

    这一步没啥好说的,网上内容很多

    二.Jenkins配置

    1.全局属性:系统管理-系统配置-全局属性:

    key: PATH
    value: 在终端中输入echo $PATH将输出内容复制填写。

    2.插件

    Git Parameter(用于git分支参数化构建)
    xcode integration(没用到)
    Keychains and Code Signing Identities(没用到,很长时间不更新的老插件, 新版本的jenkins上能安装,但是上传有bug; 2.263.4 版本以前的jenkins才能用)
    Environment Injector(没用到,job内部环境变量)

    3.参数化构建

    git参数

    Git Parameter插件: 分支可智能排序,我用的DESCENDING_SMART,按时间倒序,最新的分支在上面

    选项参数:

    Scheme(项目target), Configuration(Debug,Release), Method(development, app-store)

    字符参数:

    Tips(简短信息,比如哪块需要重点测,等等)

    三.打包方案

    1.shell:

    脚本打包方式,较为主流,灵活可控,不用侵入工程中

    a.生成项目所需的plist文件
    b.有用的脚本

    #!/bin/sh
    #!/bin/bash --login(fir-cli需要用这个)

    export LANG=en_US.UTF-8
    export LANGUAGE=en_US.UTF-8
    export LC_ALL=en_US.UTF-8
    

    解锁对login.keychain的访问,codesign可能会用到( 我暂时没用到这些,如果需要使用,自己试试路径啥的对不对)
    这两个不知道哪个对,可以都试试
    (1).security unlock-keychain -p "password" $HOME/Library/Keychains/login.keychain

    (2).

    ln -s ~/Library/Keychains/login.keychain-db ~/Library/Keychains/login.keychain
    security unlock-keychain -p "login pwd" ~/Library/Keychains/login.keychain
    
    c.获取git最后一次commit提交记录的message信息

    网上能查到的信息是一个jenkins插件,但最新版本jenkins不能用; 后来想到git命令估计可以实现,果然有这个命令,cd到工程下,然后执行这个脚本命令
    git_change_log=$(git log --pretty=format:“%s” -1)

    d.最终脚本

    这个脚本不能直接拿去用,只能作为参考,因为是根据我自己项目需要所写的脚本,需要做一些提前设置,比如安装配置一些cli工具,创建plist文件和一些文件夹等等.另外做了脱敏处理,删掉了一部分脚本,去掉了一些项目信息,XXX的地方要根据自己项目,或者是根据自己的电脑用户路径等等做修改.

    #!/bin/sh --login
    export LANG=en_US.UTF-8
    
    echo "===========构建参数=========="
    if test "$Configuration" = "Test"; then
        Configuration_CN="测试环境"
    elif test "$Configuration" = "TF"; then
        Configuration_CN="TestFlight环境"
    else
        Configuration_CN="正式环境"
    fi
    
    if test "$Method" = "development"; then
        Method_CN="普通包"
    else
        Method_CN="上线包"
    fi
    
    echo "=====分支: ${Branch}"
    echo "=====target: ${Scheme}"
    echo "=====网络环境: ${Configuration_CN}${Configuration}"
    echo "=====包类型: ${Method_CN}${Method}"
    echo "=====小提示: ${Tips}"
    
    echo "===========打包前的参数=========="
    
    # 计时 
    SECONDS=0
    
    # 工程名(根据项目自行填写)
    WORKSPACE_NAME="XXX"
    
    # TARGET名称(根据项目自行填写)
    SCHEME_NAME=${Scheme}
    
    # 设置配置标识,编译环境
    configuration=${Configuration}
    
    # 工程源码目录
    CODE_PATH="${WORKSPACE}"
    
    # 导包方式
    EXPORT_METHOD=${Method}
    
    # 日期
    DATE=$(date +%Y-%m-%d_%H-%M-%S)
    # xcarchive文件名字
    ARCHIVE_NAME="${SCHEME_NAME}_${DATE}_${Configuration}_${Method}.xcarchive"
    # 存放ipa的文件夹名称
    IPA_FOLDER_NAME="${SCHEME_NAME}_${DATE}_${Configuration}_${Method}"
    
    #路径
    ROOT_PATH="/Users/adamshi/Documents/Work/Archive"
    # xcarchive文件路径
    ARCHIVE_PATH="${ROOT_PATH}/XCArchive/${SCHEME_NAME}/${ARCHIVE_NAME}"
    #要上传的ipa文件路径 
    IPA_PATH="${ROOT_PATH}/IPA/${SCHEME_NAME}/${IPA_FOLDER_NAME}"
    echo "ARCHIVE_PATH: ${ARCHIVE_PATH}"
    echo "IPA_PATH: ${IPA_PATH}"
    
    
    # 导包方式配置文件路径
    if test "$EXPORT_METHOD" = "ad-hoc"; then
        EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/AdHocExportOptions.plist
    elif test "$EXPORT_METHOD" = "app-store"; then
        EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/AppStoreExportOptios.plist
    elif test "$EXPORT_METHOD" = "enterprise"; then
        EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/EnterpriseExportOptions.plist
    else
        EXPORT_METHOD_PLIST_PATH=${ROOT_PATH}/Plist/DevelopmentExportOptions.plist
    fi
    
    # 指ipa定输出文件夹,如果有删除后再创建,如果没有就直接创建
    if test -d ${IPA_PATH}; then
        rm -rf ${IPA_PATH}
        mkdir -pv ${IPA_PATH}
        echo ${IPA_PATH}
    else
        mkdir -pv ${IPA_PATH}
    fi
    
    # 进入工程源码根目录
    cd "${CODE_PATH}"
    
    # 执行pod
    #pod install --verbose --no-repo-update
    
    #mkdir -p build
    
    echo "===========开始打包=========="
    
    # 清除工程
    echo "++++++++++++++++clean++++++++++++++++"
    xcodebuild clean -workspace ${WORKSPACE_NAME}.xcworkspace \
                     -scheme ${SCHEME_NAME} \
                     -configuration ${configuration}
                     
    
    # 将app打包成xcarchive格式文件
    echo "+++++++++++++++++archive+++++++++++++++++"
    xcodebuild archive -workspace ${WORKSPACE_NAME}.xcworkspace \
                       -scheme ${SCHEME_NAME} \
                       -configuration ${configuration} \
                       -archivePath ${ARCHIVE_PATH}
                      
    #xcarchive文件非空判断
    if [ -e "${ARCHIVE_PATH}" ];then
        echo "生成xcarchive成功"
    else
        echo "生成xcarchive失败,停止构建"
        exit 1
    fi
    
    
    # 将xcarchive格式文件打包成ipa
    echo "+++++++++++++++++ipa+++++++++++++++++"
    xcodebuild -exportArchive \
               -archivePath ${ARCHIVE_PATH} \
               -exportPath "${IPA_PATH}" \
               -exportOptionsPlist ${EXPORT_METHOD_PLIST_PATH} \
               -allowProvisioningUpdates
               
    #IPA文件非空判断
    if [ -e "${IPA_PATH}" ];then
        echo "生成IPA成功"
    else
        echo "生成IPA失败,停止构建"
        exit 1
    fi
                              
    
    echo "===========打包完成后的参数=========="
    
    #最后一次git提交记录的message信息
    git_change_log=$(git log --pretty=format:“%s” -1)
    
    #代码工程内商户对应的info.plist路径
    scheme_infoplist_path="${CODE_PATH}/${SCHEME_NAME}/${SCHEME_NAME}.plist"
    #获取DisplayName(有的target和displayName不一样)
    Bundle_Display_Name=$(/usr/libexec/PlistBuddy -c "print :CFBundleDisplayName" "${scheme_infoplist_path}")
    echo "Bundle_Display_Name: ${Bundle_Display_Name}"
    
    #xcarchive包内的plist路径
    xcarchive_infoplist_path="${ARCHIVE_PATH}/Info.plist"
    #BundleID
    BundleID=$(/usr/libexec/PlistBuddy -c "print :ApplicationProperties:CFBundleIdentifier" "${xcarchive_infoplist_path}")
    #版本号
    versionNumber=$(/usr/libexec/PlistBuddy -c "print :ApplicationProperties:CFBundleShortVersionString" "${xcarchive_infoplist_path}")
    #build号
    buildNumber=$(/usr/libexec/PlistBuddy -c "print :ApplicationProperties:CFBundleVersion" "${xcarchive_infoplist_path}")
    echo "BundleID: ${BundleID}"
    echo "versionNumber: ${versionNumber}"
    echo "buildNumber: ${buildNumber}"
    
    #符号表上传bugly相关参数
    dSYM_Path=${ARCHIVE_PATH}/dSYMs/${Bundle_Display_Name}.app.dSYM
    bugly_platform="IOS"
    if test "$SCHEME_NAME" = "XXX"; then
        bugly_appid="1234567890"
        bugly_appkey="1234567890"
    elif test "$SCHEME_NAME" = "YYY"; then
        bugly_appid="abcdefgh"
        bugly_appkey="abcdefgh"
    else
        bugly_appid=""
        bugly_appkey=""
    fi
    
    echo "==========上传内测托管平台=========="
    
    if test "$EXPORT_METHOD" = "app-store"; then
    
        echo "==========线上包逻辑=========="
        echo "==========开始上传阿里云=========="
        
        #平台名
        Platform_Name="ipa包已上传阿里云OSS, \n\
                        符号表文件已上传bugly"
        #app icon base64
        Image_Base64=$(base64 ${ROOT_PATH}/Icon/${SCHEME_NAME}.png)
    
        
    
        #ipa名字
        Ipa_Name=${SCHEME_NAME}_${DATE}_V${versionNumber}_${Ipa_Configuration_Type}.ipa
        Ipa_Name_UTF8=${SCHEME_NAME}_${DATE}_V${versionNumber}_${Ipa_Configuration_UTF8}.ipa
    
        #下载地址
        Download_URL="https://shenzhen.aliyuncs.com/iOS/${SCHEME_NAME}/${Ipa_Name_UTF8}"
        echo "阿里云OSS下载地址: ${Download_URL}"
        
        #app-store, 企业签或TF签, 上传alioss
        /Users/adamshi/ossutilmac64 cp ${IPA_PATH}/${Bundle_Display_Name}.ipa oss://file-app/iOS/${SCHEME_NAME}/${Ipa_Name}
        
        echo "==========阿里云上传结束=========="
    
        echo "==========即将上传符号表到bugly=========="
        
        #上传符号表到bugly
        java -jar /Users/adamshi/Documents/Work/Archive/Bugly/buglyqq-upload-symbol.jar \
             -appid ${bugly_appid} \
             -appkey ${bugly_appkey} \
             -bundleid ${BundleID} \
             -version ${versionNumber} \
             -platform ${bugly_platform} \
             -inputSymbol ${dSYM_Path}
             
        echo "==========符号表已上传bugly=========="
        
    else
        echo "==========测试包逻辑==========="
        echo "==========开始上传fir=========="
        
        Short="${SCHEME_NAME}${configuration}"
    
        #测试包, 上传fir
        fir publish ${IPA_PATH}/${Bundle_Display_Name}.ipa -H \
                                                           -Q \
                                                           -S \
                                                           -s "${Short}" \
                                                           -T aaaaaaaaaa \
                                                           -c "${SCHEME_NAME}_${BUILD_NUMBER} 
                                                                分支: ${Branch}
                                                                changeLog: ${git_change_log}
                                                                Tips: ${Tips}" 
                                                                
        
        #平台名
        Platform_Name="已上传至Fir"
        #二维码base64
        Image_Base64=$(base64 ${IPA_PATH}/fir-${Bundle_Display_Name}.png)
        Ipa_Configuration_Type="IPA包"
        #ipa名字
        Ipa_Name=${SCHEME_NAME}_${DATE}_V${versionNumber}_${Ipa_Configuration_Type}.ipa
        #下载地址
        Download_URL="http://d.maps9.com/${Short}" 
        
        echo "fir下载地址: ${Download_URL}"
        
        echo "==========上传fir结束=========="
    fi
    
    echo "==========删除工程文件=========="
    rm -rf $ARCHIVE_PATH
    rm -rf $IPA_PATH
    echo "已删除构建时生成的xcarchive和IPA文件"
    
    echo "==========开始发送teams通知=========="
    
    #图片url
    Image_Base64_URL="data:image/png;base64,${Image_Base64}"
    #Image_Base64_URL="https://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=Applelogo&step_word=&hs=0&pn=3&spn=0&di=7108135681917976577&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=2&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=2040973445%2C2450782249&os=1098261057%2C2881870715&simid=4105555652%2C494769902&adpicid=0&lpn=0&ln=1341&fr=&fmq=1657265865057_R&fm=result&ic=&s=undefined&hd=1&latest=0&copyright=0&se=&sme=&tab=0&width=&height=&face=undefined&ist=&jit=&cg=&bdtype=0&oriquery=&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F01%2F21%2F32%2F5905a3b241978_610.jpg%26refer%3Dhttp%3A%2F%2Fpic.51yuansu.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1659857895%26t%3D26f6c80dd9bbde7af85a66b6b07f69b1&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bc8y7wgf7_z%26e3Bv54AzdH3FfvAzdH3Ffssgphxzzp_z%26e3Bip4s&gsm=4&rpstart=0&rpnum=0&islist=&querylist=&nojc=undefined&dyTabStr=MCwzLDYsMiw0LDEsNSw3LDgsOQ%3D%3D"
    
    echo "teams参数: 
            Ipa_Name: ${Ipa_Name}
            git_change_log:${git_change_log}
            Tips: ${Tips}
            Platform_Name:${Platform_Name}
            Download_URL:${Download_URL}
            Image_Base64_URL:${Image_Base64_URL}"
    
    #teams发通知
    curl -H 'Content-Type: application/json' \
         -d "{
       \"type\":\"message\",
       \"attachments\":[
          {
             \"contentType\":\"application/vnd.microsoft.card.adaptive\",
             \"contentUrl\":null,
             \"content\":{
                \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",
                \"type\": \"AdaptiveCard\",
                \"version\": \"1.0\",
                \"body\": [
                    {
                        \"type\": \"FactSet\",
                        \"facts\": [
                            {
                                \"title\": \"Git分支:\",
                                \"value\": \"${Branch}\"
                            },
                            {
                                \"title\": \"Target:\",
                                \"value\": \"${Scheme}\"
                            }
                        ]
                    },
                    {
                        \"type\": \"FactSet\",
                        \"facts\": [
                            {
                                \"title\": \"网络环境:\",
                                \"value\": \"${Configuration_CN}${Configuration}\"
                            },
                            {
                                \"title\": \"上线包或普通包:\",
                                \"value\": \"${Method_CN}\"
                            }
                        ]
                    },
                    {
                        \"type\": \"FactSet\",
                        \"facts\": [
                            {
                                \"title\": \"Tips:\",
                                \"value\": \"${Tips}\"
                            },
                            {
                                \"title\": \"Git最后提交内容:\",
                                \"value\": \"${git_change_log}\"
                            }
                        ]
                    },
                    {
                        \"type\": \"FactSet\",
                        \"facts\": [
                            {
                                \"title\": \"iOS-IPA包名:\",
                                \"value\": \"${Ipa_Name}\"
                            },
                            {
                                \"title\": \"内测托管平台:\",
                                \"value\": \"${Platform_Name}\"
                            }
                        ]
                    },
                    {
                        \"type\": \"Image\",
                        \"url\": \"${Image_Base64_URL}\",
                        \"size\": \"Stretch\",
                        \"width\": \"200px\",
                        \"height\": \"200px\",
                        \"horizontalAlignment\": \"Center\"
                    },
                    {
                        \"type\": \"ActionSet\",
                        \"actions\": [
                            {
                                \"type\": \"Action.OpenUrl\",
                                \"id\": \"9cbbcc71-be19-8f72-524d-5aa6c8a096e8\",
                                \"title\": \"去下载\",
                                \"url\": \"${Download_URL}\"
                            }
                        ],
                        \"horizontalAlignment\": \"Left\"
                    }
                ],
                \"padding\": \"None\",
                \"@type\": \"AdaptiveCard\",
                \"@context\": \"http://schema.org/extensions\"
            }
          }
       ]
    }" \
    https://hyperlegend.webhook.office.com/webhookb2/xxxxx
    
    #发送teams通知结束
    echo "==========发送teams通知结束=========="
    
    
    # 输出打包总用时
    echo "==========打包总用时: ${SECONDS}s=========="
    
    

    2.plugin:

    xcode integration插件打包,最终没用这个方案

    3.fastlane:

    类似cocoapods的模式,比较方便,也很强大,但是会有一些配置文件侵入到工程中,也没用这个方案

    4.Xcode Server

    苹果官方的自动打包方案,集成在xcode里面,比较简单方便,但是好像用的人不多,不如jenkins功能那么全面

    四.上传内测平台

    1.蒲公英

    网上文章较多,不多介绍

    2.fir

    安装fir-cli工具,添加脚本

    3.阿里云oss

    安装和配置阿里云oss工具:ossutil和ossbrowser; 添加上传ipa包到oss的脚本

    五.符号表上传

    1.友盟

    2.bugly

    下载bugly的jar工具,配置脚本

    六.发送通知

    1.钉钉

    配置webhook

    2.企业微信

    配置webhook

    3.微软Teams(这玩意真难用....)

    配置Incoming Webhooks, 调用curl命令

    七.节点

    把打包机设置成打包子节点,然后在运维的jenkins网站上配置好子节点,就可以多人使用了,开发和测试都可以比较舒服的打包

    八.内网穿透,内网映射到外网

    如果想远程多人使用,又没有配置节点,可以做内网穿透; 收费版本会稳定,网速也比较好, 但是其实免费版本就够用了,只不过不太稳定...
    有几种方式:

    (一).ngrok

    1.国外官网ngrok

    https://ngrok.com/
    国外服务器,比较慢,而且会有个中间页,需要做其他配置才能去除中间页; 而且没有免费版

    2.小米球ngrok

    也是基于ngrok,但是服务器在国内,速度较快; 不过免费版不太稳定,经常报错,报错的话,刷新一下网址就行; VIP是一次性付款,永久使用,还算合适

    3.Sunny-Ngrok

    https://www.ngrok.cc/
    服务器在国内,也有免费版,免费版也是不稳定; 收费版是每月订阅,需要一直付钱,看需要吧

    4.natapp

    https://natapp.cn/
    同上

    (二). Frp

    国内的:
    https://gofrp.org/

    (三).其他工具

    内网穿透工具比较(ngrok,frp,lanproxy,goproxy,nps)

    九.提高打包编译速度

    ccache

    十.遇到的问题和坑

    2022-10-12 17:49:29.074 xcodebuild[31843:10154938]  DVTPortal: Service '<DVTPortalViewDeveloperService: 0x600003729100; action='viewDeveloper'>' encountered an unexpected result code from the portal ('1100')
    2022-10-12 17:49:29.074 xcodebuild[31843:10154938]  DVTPortal: Error:
    Error Domain=DVTPortalServiceErrorDomain Code=1100 "Your session has expired. Please log in." UserInfo={payload={
        creationTimestamp = "2022-10-12T09:49:29Z";
        httpCode = 200;
        protocolVersion = QH65B2;
        requestUrl = "[https://developerservices2.apple.com/services/QH65B2/viewDeveloper.action](https://developerservices2.apple.com/services/QH65B2/viewDeveloper.action)";
        responseId = "3f0a5d82-c60b-4332-bd82-fcff6b2a6d17";
        resultCode = 1100;
        resultString = "Your session has expired. Please log in.";
        userLocale = "en_US";
        userString = "Your session has expired. Please log in.";
    }, NSLocalizedDescription=Your session has expired. Please log in.}
    
    

    遇到这样的报错,说是登录过期,请重新登录.
    解决办法:
    我试过前两种方法,都是好不了多长时间,可能又会出现; 然后我试了第三种方法,目前解决了,没再出现这个问题

    1.重新下载证书文件

    Xcode-Preferences-Accounts-选中账号,点击Download Manuel Profiles.重新下载证书文件

    2.重新登录

    Xcode-Preferences-Accounts退出账号,然后重新登录,然后下载证书文件

    3.重新获取权限

    先把xcode账户退出登录 - 把xcode退出-终端执行如下命令

    defaults write com.apple.dt.Xcode DVTDeveloperAccountUseKeychainService_2 -bool NO
    

    打开xcode,重新登录账户,然后下载相关证书; 打包过程中会重新申请权限,点始终允许
    来源: Using Automatic Code Signing from the Command Line via SSH Session

    4.打包命令添加解锁逻辑

    security unlock-keychain -p PASSWORD
    

    来源: Constant "Your session has expired" (error 1100)

    5.打开两步验证

    Xcode export/upload error: Your session has expired. Please log in

    相关文章

      网友评论

          本文标题:iOS自动打包流程

          本文链接:https://www.haomeiwen.com/subject/lqzmbrtx.html