构建流水线实践

Posted by PaysonChen on November 8, 2023

1 背景

​ 为了适应不通项目的自动化的需求,而且构建的方式和能力也在不断的迭代与演进,同时维护多个构建脚本存在成本与遗漏,因此将打包脚本的通用能力抽离,通过配置参数进行不同项目的构建。

1
2
3
4
5
6
7
8
9
10
11
12
# [目的]
# 为了使流水线建设趋于可维护,易维护,可迁移,易迁移,对构建逻辑进行模块化、将构建主流程划分为:构建前、构建、构建后,三大模块
#
#   构建前:参数配置、版本号设置
#   构建中:Pod install、xcbuild
#   构建后:归档、分发、通知
#
# 当前的构建后方案的选择:
#   归档:简单云(ipa+dSYM)、bugly(dSYM)
#   分发:蒲公英、阿里云OSS、AppStore
#   通知:钉钉机器人
# 在未来的持续集成过程中,可以对归档方案进行替换、对分发方案进行替换、对通知方案进行替换、不需要阅读和改动构建中流程

2 开始

构建脚本由构建入口、构建主流程、额外流程三个模块构成,

graph TB START-->入参配置 入参配置-->构建前-第三方APIKEY配置 构建前-第三方APIKEY配置-->构建前-设置构建版本号自增 构建前-设置构建版本号自增-->构建前-获取工程配置 构建前-获取工程配置-->构建中-环境变量设置 构建中-环境变量设置-->构建中-执行Pod依赖安装 构建中-执行Pod依赖安装-->构建中-执行构建 构建中-执行构建-->构建中-执行归档 构建中-执行归档-->构建中-执行分发 构建中-执行分发-->构建中-执行通知 构建中-执行通知-->END

2.1 构建入口

构建入口由UniversalBuild.sh 执行,负责可变参数声明及脚本入参处理

参数说明:

1
2
3
4
5
6
# [参数]
#   -cfg                #打包方式:configuration(0:DailyBuild|1:TestFlight|2:Release|3:TFInner)
#   -target_name        #项目配置:要打包的TargetName
#   -channel_name       #渠道号:pgy下载 url后缀
#   -notify             #是否启用通知 有传值1则启用群通知,2启用内部群通知
#   -publish            #分发方式:1 启用oss| 2 启用pgy+oss | else pgy

以下以【Demo】为例:

​ Target为PSCDemoiOS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# [调用说明]
#   使用了gitsubmodule 构建组件,兼容git 检出不兼容,需要先执行 git submodule update --init --recursive
#   git submodule update --init --recursive
#   拉取自模块更新
#   git submodule update --remote
#
#   打包方式:configuration(0:DailyBuild|1:TestFlight|2:Release|3:TFInner)
#   configuration=0
#
#   项目配置(TargetName与源码所在目录需同名):
#   targetName=PSCDemoiOS
#
#   渠道号:pgy下载 url后缀
#   pgyChannel=129
#
#   是否启用通知 有传值1则启用群通知,2启用内部群通知
#   notify=1
#
#   1 启用oss 2 启用pgy+oss else pgy
#   publish=0
#
#   sh PSC_iOS_Build_SH/UniversalBuild.sh -cfg $configuration -target_name $targetName -channel_name $pgyChannel -notify $notify -publish $publish

2.2 构建主流程

构建主流吃由build_sh.sh 执行,负责iOS构建工作,此部分与打包业务强相关,相对比较稳定,不太需要频繁修改,因此抽离出来

主流程简单说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function build()
{
    #新增耗时统计
    export build_start_time=$(date +%s.%N)

    #初始化环境
    bInitEnv
    print_time_cost "构建前配置"

    #执行pod
    bPodInstall
    print_time_cost "执行pod"

    #执行构建
    bBuildProject
    print_time_cost "执行构建"

    #执行构建后的归档流程:ipa & dsym
    bCollection
    print_time_cost "执行构建归档操作"

    #执行构建后的分发流程:上传(发布)安装包,供下载安装
    bDistribution
    print_time_cost "执行分发操作"

    #执行构建后的通知流程
    bNotification
    print_time_cost "执行通知操作"
}

详细步骤,详见代码相关模块

2.3 额外流程

额外流程抽离是考虑到上传、通知、归档等属于打包的后续操作,不需要与打包流程耦合,且这部分的流程抽离,可实现模块的热插拔替换。

2.3.1 分发

​ 目前支持以下三种分法方式:

  • 蒲公英
  • 阿里云OSS
  • AppStore

​ 安装包的分发逻辑调用

1
sh build_ext/build_publish.sh

​ 在 build_publish.sh 中执行分发逻辑,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function upload_ipa ()
{
    export upload_start_time=$(date +%s.%N)
    echo '///upload_ipa configurationName = '${configurationName}
    if [ "${packedToAppStore}" == "1" ]; then
        echo 'TFInner 、TestFlight & appstore 上传到appstore 统计耗时'
        upload_ipa_to_AppStore
        print_upload_time_cost "上传AppStore"

    else
        #其他
        #启用pgy时构建包上传到pgy
        if [ "${use_pgy_publish}" = "1" ]; then
            echo '///启用pgy上传,统计耗时'
            upload_ipa_to_pgyer
            print_upload_time_cost "上传PGY"
        fi

        #启用oss
        if [ "${use_oss_publish}" = "1" ]; then
            echo '///启用oss上传,统计耗时'
            upload_ipa_to_aliyunOSS
            print_upload_time_cost "上传oss"
        fi
    fi
}


function upload()
{   
    #发布ipa
    upload_ipa
}

#归档
upload

​ 上传测试包可以从现在的蒲公英换到Fir或者简单云等,则不需要关心打包流程,只需要将上传模块的方法切换即可。

  • dailybuild

​ 蒲公英:

​ 用于测试包构建,安装包目前上传到蒲公英(服务端环境可切换)

​ 上传蒲公英的逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#发布到第三方分发平台:蒲公英
function upload_ipa_to_pgyer ()
{
    echo '/// 上传 PGY 开始 IPA_PATH='${IPA_PATH}
    echo '/// 上传 PGY 开始 渠道='${channel_name}
    echo '/// 上传 PGY 开始 提交 git_log='${git_log}
        
    compileEnvironment="${buildName}:${git_log}"
    echo '/// 上传 PGY 开始 内容='${compileEnvironment}

    if [ -z "$channel_name" ]; then
        # 渠道空,则走默认 不指定渠道号的上传
        # 改成2.0api
        echo '/// 上传 PGY 开始 渠道 为空'
        bash $build_ext_path/publish_res/pgyer_upload.sh -k ${pgyAPIKey} -d ${compileEnvironment} ${IPA_PATH}
    else
        # 渠道非空,走下载地址关联 渠道
        # 改成2.0api
        echo '/// 上传 PGY 开始 渠道='${channel_name}
        bash $build_ext_path/publish_res/pgyer_upload.sh -k ${pgyAPIKey} -d ${compileEnvironment} -c ${pgyDownloadUrlSubfix} ${IPA_PATH}
    fi
            
    echo '/// 上传 PGY 结束'
}

​ 阿里云OSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#在下载二维码增加git日志
function add_log_commit_title_msg_to_qrcode ()
{
    #二维码打上版本信息
    #TODO: 这里需要安装下工具 ImageMgick [brew install ImageMgick]
    #check_command_and_install "convert" "ImageMgick"
    bash $build_ext_path/build_tools/build_tools.sh "convert" "ImageMagick"
    
    title=$xcodeVersion3number.${channel_name}.$configurationName.${BuildNo}

    if [ $1 = "1" ]; then
        dst_line_cnt=0
        #FIXME: 这样取会为空???? ${source_log_arr}
        export oss_source_log_arr=(`echo $source_log_str | tr '}{' ' '`)
        export oss_log_line_count=${#oss_source_log_arr[@]}
        echo '/// oss 分发: 添加 二维码msg log_line_count='$oss_log_line_count
        echo '/// oss 分发: 添加 二维码msg source_log_arr count='${#source_log_arr[@]}

        for(( i=0;i<${oss_log_line_count};i++))
        do
            single_len=$(echo -n "${oss_source_log_arr[i]}" | wc -m)
            if [ -z "${oss_source_log_arr[i]}" ]; then
                oss_singleStr=${oss_source_log_arr[i]};
                echo '///--遍历'${oss_source_log_arr[i]}'为空文字长度:'$single_len
                dst_line_cnt=$((dst_line_cnt+1))
            else
                line_max_len=30
                ingle_cnt=$((single_len / line_max_len))


                if ((ingle_cnt > 0)); then
                    for(( j=0;j<=${ingle_cnt};j++))
                    do
                        string=${oss_source_log_arr[i]}
                        single_line1=${string:(line_max_len * j):line_max_len}
                        echo '///--遍历' ${oss_source_log_arr[i]}'文字长度:'$single_len'超阈值阶段:第'$j'行:'$single_line1
                        oss_singleStr+="\n"${single_line1};
                        dst_line_cnt=$((dst_line_cnt+1))
                    done;
                else
                    echo '///--遍历' ${oss_source_log_arr[i]}' 文字长度:'$single_len
                    dst_line_cnt=$((dst_line_cnt+1))
                    oss_singleStr+="\n"${oss_source_log_arr[i]};
                fi
            fi
        done;
        msg=$oss_singleStr
        echo "/// oss render msg="$msg
        
        contentsize_width=600
        contentsize_height=$((contentsize_width + $dst_line_cnt * 25 + 40 + 40)) #第一个40是title的高度 第二个40是距离底部空间
        echo "contentsize_height="$contentsize_height
        #改变原图大小
        convert $qrcode_path -resize ${contentsize_width}x${contentsize_width}^ -gravity center ${qrcode_path}_1.png
        echo "1 convert:"${contentsize_width}x${contentsize_width}

        #变高,兼容msg
        convert ${qrcode_path}_1.png -gravity north -extent ${contentsize_width}x${contentsize_height} ${qrcode_path}_2.png
        echo "2 convert:"${contentsize_width}x${contentsize_height}

        #汉字字体
        chn_tff_path=$build_ext_path/publish_res/chn.ttf
        
        #渲染title
        convert ${qrcode_path}_2.png -gravity north -pointsize 30 -font $chn_tff_path -fill black -annotate +0+${contentsize_width} $title ${qrcode_path}_3.png
        echo "3 convert:title:"${title}

        #渲染msg
        if [ -z "$msg" ]; then
            echo "4 convert:msg:为空"
        fi
        
        convert ${qrcode_path}_3.png -gravity north -pointsize 20 -font $chn_tff_path -fill black -annotate +0+$((${contentsize_width}+40)) $msg ${qrcode_path}_4.png
        echo "4 convert:msg:"${msg}
        
        if test -e "${qrcode_path}_4.png"; then
            echo "4 convert:msg 存在"
            qrcode_path_with_msg=${qrcode_path}_4.png
        else
            echo "4 convert:msg 不存在 使用3"
            qrcode_path_with_msg=${qrcode_path}_3.png
        fi

    else
        #没有传 1 不需要msg,只给title
        convert $qrcode_path -gravity south -pointsize 10 -fill gray -annotate +0+0 $qrcode_print_msg  $qrcode_path_with_msg
        echo '/// oss 分发: 添加 二维码title='${title}
    fi

}

#发布到阿里云OSS
function upload_ipa_to_aliyunOSS ()
{
    #更新plist的键值对:
    #    /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${version3number}" ${WeSingNotificationServiceExtension_plist}
    manifest_path=${exportPath}/manifest.plist
    echo '/// oss 分发: manifest_path='${manifest_path}
    
    oss_bucket="oss_bucket_name"
    oss_platform="iOS"
    oss_bucket_path=$oss_platform/${TARGET_NAME}/$configurationName/${channel_name}/${BuildNo}/${TARGET_NAME}_${channel_name}_$configurationName_${BuildNo}
    oss_path="oss://$oss_bucket/$oss_bucket_path"
    oss_url="https://$oss_bucket.oss-cn-beijing.aliyuncs.com/$oss_bucket_path"

    oss_ipa_upload_url=$oss_path".ipa"
    oss_ipa_url=$oss_url".ipa"

    #icon下载地址配置
    oss_icon_url="${aliyunOSSBuicket}/icon/icon.png"
    oss_icon_fs_url="${aliyunOSSBuicket}/icon/icon_fs.png"

    
    #修改ipa下载地址
    /usr/libexec/PlistBuddy -c "Set :items:0:assets:0:url $oss_ipa_url" $manifest_path

    #修改icon地址
    /usr/libexec/PlistBuddy -c "Set :items:0:assets:1:url $oss_icon_url" $manifest_path

    #修改icon full size地址
    /usr/libexec/PlistBuddy -c "Set :items:0:assets:2:url $oss_icon_fs_url" $manifest_path


    #传ipa
    ossutil cp ${IPA_PATH} $oss_ipa_upload_url -f
    
    #传manifest
    oss_manifest_extension="plist"
    oss_manifest_upload_url=$oss_path"."$oss_manifest_extension
    oss_manifest_url=$oss_url"."$oss_manifest_extension
    ossutil cp ${manifest_path} $oss_manifest_upload_url -f
    
    #上述做归档后,生成下载二维码
    download_url="itms-services://?action=download-manifest&url="$oss_manifest_url
    qrcode_path=${exportPath}/${TARGET_NAME}_${BuildNo}.png
    
    #TODO: 这里需要安装下工具 qrencode [brew install qrencode]
    bash $build_ext_path/build_tools/build_tools.sh "qrencode"

    qrencode -o $qrcode_path $download_url
    
    #将二维码上传到oss
    oss_qrencode_extension="png"
    oss_qrencode_upload_url=$oss_path"."$oss_qrencode_extension
    qrcode_path_with_msg=${qrcode_path}_with_msg
        
    #如果需要二维码打上log信息则调用
    need_msg=1
    add_log_commit_title_msg_to_qrcode $need_msg
        
    #上传到阿里云oss
    ossutil cp ${qrcode_path_with_msg} $oss_qrencode_upload_url -f
    
    #二维码的url 通知出来
    export oss_app_download_qrcode_url=$oss_url"."$oss_qrencode_extension
    echo '/// oss 分发: 二维码的oss_app_download_qrcode_url='${oss_app_download_qrcode_url}
}
  • TestFlight

​ 打灰度包将执行ipa上传到AppStore后台的操作(服务端环境是正式环境,未来有外网的灰度需求可改成灰度环境)

  • Release

​ 打正式包将执行ipa上传到AppStore后台的操作(服务端环境是正式环境)

  • TFinner

​ 打灰度包给没有添加测试设备的同事下载,解决100设备不够用(带Debug模式、可切换环境、上传到AppStore)

AppStore分发:

1
2
3
4
5
6
7
8
#发布到AppStore
function upload_ipa_to_AppStore ()
{
    echo '/// 上传App Store 开始'
    xcrun altool --validate-app -f  $IPA_PATH --type  ios  --apiKey $appStore_apikey --apiIssuer $appStore_apiIssuer
    xcrun altool --upload-app -f  $IPA_PATH --type  ios  --apiKey $appStore_apikey --apiIssuer $appStore_apiIssuer
    echo '/// 上传App Store 结束'
}

2.3.2 通知

​ 通知模块目前通过钉钉通知,未来也可改成邮件甚至其他方式,考量亦是如此。通知实现内容可定制,当参数notify不为空,才需要通知测试团队进行更新:

1
2
3
4
5
6
 if [ -z "$notify" ]; then
        # 空,不发通知
        echo '/// 本次打包静默,不发通知 '
 else
    ....
 fi

​ 通知通过 build_notification.sh 实现如下逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#构建通知
function notification()
{
    param=$1
    if [ -z "$param" ]; then
        echo '///构建通知(参数为空)'
        notify
    else
        echo '///构建通知(参数不为空):'$param
        if [ "${param}" = "AppStore" ]; then
            echo '///构建通知AppStore'
            notify_AppStore
        else
            echo '///构建异常(参数:)'$param
            notify_fail $param
        fi
    fi

}

echo '///构建通知('$1')'

#通知
notification $1

通知兼容蒲公英下载地址 + oss下载地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#执行通知逻辑
function notify()
{
    modify_msg=${git_log}
    ProductName=${xcodeProductName}
    downloadPrefix=${PrefixID}
    channel=${channel_name}
    title=${buildName}
    
    app_donwnload_url=""
    pgy_app_donwnload_url=""
    #启用传pgy
    if [ "${use_pgy_publish}" = "1" ]; then
        #采用直接 拼 channel后缀
        shortUrl=${downloadPrefix}${ProductName}${channel}


        pgy_app_donwnload_url="https://www.xcxwo.com/${shortUrl}"
        echo "short_download_url = "$short_download_url
        if [ -z "$short_download_url" ]; then
            app_donwnload_url=$pgy_app_donwnload_url
        else
            app_donwnload_url=$short_download_url
        fi
        #蒲公英下载地址
        echo "通知:蒲公英分发,下载地址:'${app_donwnload_url}'"
    fi
    
    
    #启用传oss
    if [ "${use_oss_publish}" = "1" ]; then
        if [ -z "$oss_app_download_qrcode_url" ]; then
            #oss下载地址为空
            echo "通知:oss_app_download_qrcode_url 为空则 下载地址:'${app_donwnload_url}'"
        else
            echo "通知:oss_app_download_qrcode_url 不为空 下载地址:'${app_donwnload_url}' and '$oss_app_download_qrcode_url'"
            app_donwnload_url=${pgy_app_donwnload_url}'\n\n'oss下载地址:$oss_app_download_qrcode_url

        fi
    fi
    
    echo "通知 COME下载地址:'${app_donwnload_url}'"
    echo "通知 内容:'""'${ProductName}' App更新:'${title}'\n'${modify_msg}'\n下载地址:'${app_donwnload_url}'"
    
    if [ "${notify}" = "1" ]; then
        echo '/// 本次打发通知到群里 '
    elif [ "${notify}" = "2" ]; then
        echo '/// 本次打发通知到内部群里 '
        access_token=$internal_access_token
    else
        echo '/// 本次打发通知参数'${notify}
    fi
    
    if [ -z "$notify" ]; then
        # 空,不发通知
        echo '/// 本次打包静默,不发通知 '

    else
        echo '/// 本次打包发通知 '
        #发送发版通知
        curl 'https://oapi.dingtalk.com/robot/send?access_token='${access_token} \
            -H 'Content-Type: application/json' \
            -d '{"msgtype": "text",
                "at": {
                "atMobiles":[
                    "xx1"
                    "xx2"
                    "xx3"
                    "xx4"
                    "xx5"
                    "xx6"
                    "xx7"
                ],
                "isAtAll": false
                },
                 "text": {
                      "content": "'${ProductName}' App更新:'${title}'\n'${modify_msg}'\n下载地址:'${app_donwnload_url}'"            }
               }'
               
               
        echo '///-------------------------------------------------------'
        echo '/// 通知Come3 '
        echo '///-------------------------------------------------------'
    fi

}

​ 构建失败通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#打包出错时通知
function notify_fail()
{
    #发布到移动端内部群
    access_token=$internal_access_token
    #产物
    ProductName=${xcodeProductName}
    #渠道名
    channel=${channel_name}
    #流水线名称
    title=${buildName}
    
    #失败原因
    reson=$1
    
    echo '/// 本次打包异常:发通知到内部群 '
    #发送发版通知
    curl 'https://oapi.dingtalk.com/robot/send?access_token='${access_token} \
        -H 'Content-Type: application/json' \
        -d '{"msgtype": "text",
            "at": {
            "atMobiles":[
                "DD1",
                "DD2"
            ],
            "isAtAll": false
            },
             "text": {
                  "content": "❌构建失败:\nApp:'${ProductName}'\n原  因:'${reson}'\n流水线:'${title}'\n渠  道:'${buildName}'"}
           }'
           
           
    echo '///-------------------------------------------------------'
    echo '/// 构建通知'
    echo '///-------------------------------------------------------'
}

构建到AppStore时需要测试团队对上线进行验证:也发送下通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#打包AppStore通知,通知到群给出TestFlight下载链接二维码
function notify_AppStore()
{
    #发布到移动端内部群
    access_token=$internal_access_token
    #产物
    ProductName=${xcodeProductName}
    #渠道名
    channel=${channel_name}
    #流水线名称
    title=${buildName}
    
    #testFlight 二维码in OSS
    downloadTF="${aliyunOSSBuicket}/AppStoreValid/testflight.png"
    
    echo '/// 本次打包构建到AppStore:发通知到内部群 '
    #发送发版通知
    curl 'https://oapi.dingtalk.com/robot/send?access_token='${access_token} \
        -H 'Content-Type: application/json' \
        -d '{"msgtype": "text",
            "at": {
            "atMobiles":[
                "DD1",
                "DD2"
            ],
            "isAtAll": false
            },
             "text": {
                  "content": "✅AppSore构建成功:\nApp:'${ProductName}'\n构建名:'${configurationName}'\n流水线:'${title}'\n版本号:'${xcodeVersion3number}'\n构建号:'${BuildNo}'\nTestFlight:'${downloadTF}'"}
           }'
           
           
    echo '///-------------------------------------------------------'
    echo '/// 构建通知'
    echo '///-------------------------------------------------------'
}

2.3.3 归档

​ 目前在Release、TestFlight 打包时将ipa与dysm打包上传到简单云制品库

​ 构建归档,将ipa与dysm打程zip包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function ProductCollection()
{
    if [ "${configurationName}" = "Release" ] || [ "${configurationName}" = "TestFlight" ]; then

        dSYMCollection
    
        IPACollection
        
        export ProductCollectionPath=${dSYM_dict_PATH}/ProductCollection.zip
        echo "ProductCollectionPath="$ProductCollectionPath
        zip $ProductCollectionPath ${dSYMZip_PATH} ${IPAZip_PATH}
        #执行归档,(ipa+dSYM)并找个地方存起来
        bash $build_ext_path/$collection_sh
    fi

}

function dSYMCollection()
{
    # zip and copy dSYM
    echo "dSYMCollection"

    cd ${build_path}/Build/Products/${configurationName}-iphoneos
    find . -name '*.dSYM' | zip -r ${dSYMZip_PATH} -@
    find . -name '*.dSYM' -exec cp -R {} ${dSYM_dict_PATH} \;
    cd -
    echo "Copy dSYM.zip file success save path:" ${dSYMZip_PATH}
}

function IPACollection()
{
    # zip and copy dSYM
    echo "IPACollection"
    cd ${build_path}/Build/Products/${configurationName}-iphoneos
    find . -name '*.ipa' | zip -r ${IPAZip_PATH} -@
    find . -name '*.ipa' -exec cp -R {} ${dSYM_dict_PATH} \;
    cd -
    echo "Copy IPAZip_PATH.zip file success save path:" ${IPAZip_PATH}
}

​ 将zip包归档到简单云

1
2
3
4
5
6
7
8
9
function upload_to_store()
{
    export p=$1
    echo '/// 上传 dsym ipa 到制品库 '${v}'pkgName='${pkgName}
    echo "dSYMZip_PATH = "${dSYMZip_PATH}"https://PSC-devops.com/pkg/PSC/raw/OperationsAppAnddSYM/release?version="${v}"&pkgName="${pkgName}
    #上传简单云制品库
    curl -F "file=@"${p} -u 简单云账号:简单云密码 "https://PSC-devops.com/pkg/PSC/raw/OperationsAppAnddSYM/release?version="${v}"&pkgName="${pkgName} -k
}
        

​ 将dSYM上传到bugly,用于定位线上crash:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function upload_dSYM_to_bugly()
{
    export p=${dSYM_dict_PATH}
    
    if [ "${TARGET_NAME}" = "PSCDemoiOS" ]; then
        bugly_appid=$Demo_bugly_appid
        bugly_appkey=$Demo_bugly_appkey
    fi
    
    if [ "${TARGET_NAME}" = "Operations" ]; then
        bugly_appid=$operations_bugly_appid
        bugly_appkey=$operations_bugly_appkey
    fi
    
    echo "/// 上传 dsym path ${p}"
    echo "/// 上传 dsym target ${TARGET_NAME}"
    echo "/// 上传 dsym bugly_appid ${bugly_appid}"
    echo "/// 上传 dsym bugly_appkey ${bugly_appkey}"
    echo '/// 上传 dsym bugly '${versionName}'pkgName='${pkgName}
    
    #切换java 版本在流水线实现,这边实现不了
    java -version
#    jdk8
#    java -version
    
    bugly_jar_path=$build_ext_path/collection_res/buglyqq-upload-symbol/buglyqq-upload-symbol.jar
    
    java -jar ${bugly_jar_path} -appid ${bugly_appid} -appkey ${bugly_appkey} -bundleid ${pkgName} -version ${versionName} -platform IOS -inputSymbol ${p}
}

3.补充说明

​ 打包脚本抽离成git submodule,以供各个工程项目的集成,减少维护成本,如果没有跟主工程一起更新,则每次构建时需要同步下最新的脚本

1
2
3
4
#使用了gitsubmodule 构建组件,兼容git 检出不兼容,需要先执行 git submodule update --init --recursive
git submodule update --init --recursive
#拉取自模块更新
git submodule update --remote

4.Author

​ 有任何问题,欢迎联系:wangshu2015@gmail.com