IOS 一键打包上传蒲公英脚本

一、背景

目前的IOS打包不支持蒲公英上传,并且原有的打包脚本是基于Python直接调用xcodebuild来完成打包操作,有以下几个缺点:

● 脚本书写繁杂难以理解,不方便日后维护,并且脚本中强依赖项目路径,可移植性比较差 ● 脚本强依赖Python执行环境 ● 只有单纯的打包功能,没有上传蒲公英、钉钉通知等功能 ● 打完包之后无法区分是哪一个分支、哪一个时间段打的包

在以上的背景之下,我基于FastLane+Python重新开发了一套打包机制,并有较强的可移植性。

二、实现效果

只需工程根目录执行脚本PgyerBuild Debug即可完成打包+上传蒲公英+钉钉通知

三、脚本集成步骤

● 机器安装FastLane,我使用的版本是2.189.1 ● 工程内集成FastLane,修改FastLane配置文件,第四章会给出配置模板 ● 拖入Python工程生成的脚本PgyerBuild到工程根目录(Python脚本内只需修改工程名即可) ● 集成完毕!执行打包脚本,即可完成打包+上传蒲公英+钉钉通知

脚本使用教程

1
2
3
4
#打Debug包
PgyerBuild Debug
#打Release包
PgyerBuild Release

四、脚本源码

Python脚本

  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
# This is a sample Python script.

import json
# Press F10 to execute it or replace it with your code.
# Press Double  to search everywhere for classes, files, tool windows, actions, and settings.
import os
import re
import sys
import time

import requests

# 时间格式化字符串
time_format = '%Y%m%d%H%M%S'
# 当前路径
basedir = os.path.dirname(os.path.realpath(sys.argv[0]))
# 蒲公英Apikey
api_key = "cd17b24dxxxxxxxxxxxxxxxxxxxxxxx"
# 蒲公英UserKey
user_key = "7058cxxxxxxxxxxxxxxxxxxxxxxxx"
# 工程名称
projectName = "TooWellMerchant"


# 获取系统当前时间并转换请求数据所需要的格式
def getTime(format_str):
    now = int(time.time())
    timeStruct = time.localtime(now)
    strTime = time.strftime(format_str, timeStruct)
    return strTime


# 执行IOS打包
def fastlane(buildType):
    if buildType == "Debug":
        os.system("fastlane buildDebug")
    else:
        os.system("fastlane buildRelease")


# 获取Git分支号
def getGitBranch():
    with open(f'{basedir}/.git/HEAD') as f:
        return f.read().replace("ref: refs/heads/", "")


# 获取版本号
def getVersionNo():
    with open(f'{basedir}/{projectName}.xcodeproj/project.pbxproj') as f:
        content = f.read()
        return re.findall(r"MARKETING_VERSION = (.+?);", content)[0]


# 上传蒲公英
def uploadPyger(buildType):
    if buildType == 'Debug':
        description = f'[Debug]business-debug-v{getVersionNo()}-{getTime(time_format)}.ipa'
    else:
        description = f'[Release]business-release-v{getVersionNo()}-{getTime(time_format)}.ipa'
    cmd = f'curl -F _api_key={api_key} -F userKey={user_key} -F file=@{basedir}/{projectName}.ipa -F ' \
          f'buildInstallType=2 -F buildPassword=carzone123 -F buildUpdateDescri' \
          f'ption={description} https://www.pgyer.com/apiv2/app/upload '
    print(f'正在执行上传脚本:{cmd}')
    result = os.popen(cmd)
    resultStr = result.read()
    print(f'蒲公英上传结果:{resultStr}')
    return json.loads(resultStr)


# 获取文件大小
def getFileSize(size):
    def strofsize(integer, remainder, level):
        if integer >= 1024:
            remainder = integer % 1024
            integer //= 1024
            level += 1
            return strofsize(integer, remainder, level)
        else:
            return integer, remainder, level

    units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
    integer, remainder, level = strofsize(size, 0, 0)
    if level + 1 > len(units):
        level = -1
    return ('{}.{:>03d} {}'.format(integer, remainder, units[level]))


# 发送钉钉通知
def sendDingMessage(message):
    # 构建通知内容
    fileSize = getFileSize(int(message['buildFileSize']))
    buildUpdated = message['buildUpdated']
    buildUpdateDescription = message['buildUpdateDescription']
    buildBuildVersion = message['buildBuildVersion']
    buildVersion = message['buildVersion']
    buildKey = message['buildKey']
    buildQRCodeURL = message['buildQRCodeURL']
    text = []
    text.append('\n\n### IOS构建成功,已上传蒲公英')
    text.append('\n\n**构建应用**:\n\nXXXX')
    text.append(f'\n\n**构建时间**:\n\n{buildUpdated}')
    text.append(f'\n\n**构建描述**:\n\n{buildUpdateDescription}')
    text.append(f'\n\n**蒲公英版本号**:\n\nbuild{buildBuildVersion}')
    text.append(f'\n\n**IOS包大小**:\n\n{fileSize}')
    text.append(f'\n\n**IOS版本号**:\n\n{buildVersion}')
    text.append(f'\n\n**Git分支**:\n\n{getGitBranch()}')
    text.append(f'\n\n**下载地址**:\n\nhttps://www.pgyer.com/{buildKey}')
    text.append(f'\n\n**安装密码**:\n\ncarzone123')
    text.append(f'\n\n![]({buildQRCodeURL})')
    context = ''.join(text)

    # 发送钉钉通知
    webhook = 'https://oapi.dingtalk.com/robot/send?access_token' \
              '=1c7d9f9d880ca4ca9fxxxxxxxxxxxxxxxxxxxxxxxxxxxx '
    headers = {'Content-Type': 'application/json'}
    data = {
        'msgtype': 'markdown',
        'markdown': {
            "title": '蒲公英机器人',
            'text': context
        },
        'at': {
            'atMobiles': [],
            'isAtAll': False
        }
    }
    x = requests.post(url=webhook, data=json.dumps(data), headers=headers)
    if x.json()["errcode"] == 0:
        print("\n*************** 钉钉消息已发送 ***************")


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    # 构建应用
    fastlane(sys.argv[1])
    # 上传蒲公英
    uploadResult = uploadPyger(sys.argv[1])
    # 发送钉钉通知
    sendDingMessage(uploadResult['data'])
# See PyCharm help at https://www.jetbrains.com/help/pycharm/
  • 使用pyinstaller 打出脚本,重命名PgyerBuild

FastLane配置模板

 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
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:ios)

platform :ios do
  desc "build project"
  lane :buildDebug do
    #配置工程基本信息
    update_app_identifier(
      xcodeproj: "TooWellMerchant.xcodeproj", # Optional path to xcodeproj, will use the first .xcodeproj if not set
      plist_path: "TooWellMerchant/Info.plist", # Path to info plist file, relative to xcodeproj
      app_identifier: "com.xxxxxx.xxxxxx" # The App Identifier
    )
    #配置签名以及证书
    automatic_code_signing(
      path: "TooWellMerchant.xcodeproj",
      #启用自动签名
      use_automatic_signing: true,
      code_sign_identity: "Apple Development",
      #根据证书拿到团队ID或者直接去苹果开发者官方查阅
      team_id:"K4T4XXXXXX",
    )
    #配置生成包的类型以及发布渠道
    gym(scheme: "TooWellMerchant",
      workspace: "TooWellMerchant.xcworkspace",
      configuration: "Debug",
      clean: true,
      output_name:"TooWellMerchant.ipa",
      #只打Arm64,缩小包体积
      xcargs:"ARCHS='arm64'",
      #IOS 9适配
      export_xcargs:"-allowProvisioningUpdates",
      export_method: "development"           # app-store, ad-hoc, package, enterprise, development, developer-id
    )
  end
  lane :buildRelease do
    update_app_identifier(
      xcodeproj: "TooWellMerchant.xcodeproj", # Optional path to xcodeproj, will use the first .xcodeproj if not set
      plist_path: "TooWellMerchant/Info.plist", # Path to info plist file, relative to xcodeproj
      app_identifier: "com.xxxxxx.xxxxxx" # The App Identifier
    )
    automatic_code_signing(
      path: "TooWellMerchant.xcodeproj",
      use_automatic_signing: true,
      code_sign_identity: "Apple Development",
      team_id:"K4T4XXXXXX",
    )
    gym(scheme: "TooWellMerchant",
      workspace: "TooWellMerchant.xcworkspace",
      configuration: "Release",
      clean: true,
      output_name:"TooWellMerchant.ipa",
      #只打Arm64,缩小包体积
      xcargs:"ARCHS='arm64'",
      #IOS 9适配
      export_xcargs:"-allowProvisioningUpdates",
      export_method: "development"           # app-store, ad-hoc, package, enterprise, development, developer-id
    )
  end
end
Built with Hugo
主题 StackJimmy 设计