iOS CI自动化构建:FastLane+企业重签+上传fir+jenkins

iOS CI自动化构建:FastLane+企业重签+上传fir+jenkins,第1张

最近成功搭建了fastlane自动化构建流程,做下记录。

fastlane 是用 Ruby 语言编写的一个命令行工具。
fastlane 提供了一百多个 Action,它们是 iOS 项目开发中所有自动化 *** 作的基础。Action是 fastlane 自动化流程中的最小执行单元。
fastlane的action分类:

可以根据自己项目的需要,在开发自动化 *** 作时,通过传参,将某几个action配合起来使用。
每一个自动化 *** 作或者自动化任务,在fastlane中称为一个 lane。 iOS 开发中的所有自动化 *** 作,主要通过 Lane 来封装和配置的。
所有的lane保存在fastfile文件,由git统一管理,共享给所有成员。

命令:
fastlane init
用于建立fastlane文件夹
除了Fastfile,还有Applefile,以及执行过程中生成的中间文件。

Fastfile

保存所有的 lane,使用lane封装常用的自动化 *** 作。

Applefile

用于保存App的唯一标识符和Apple ID等信息
当fastlane执行一个action时,首先会使用传递进来的参数
没有参数时,fastlane会从applefile文件中查找并使用对应信息

插件

一般插件从gem下载,当然github上也有插件:

https://rubygems.org/gems
下载插件命令:
gem install fastlane-plugin-xxx
安装命令:
fastlane add_plugin 插件完整名称

如果安装失败,可自己打开Pluginfile文件添加:

open ~/.fastlane/Pluginfile

写入

gem 'fastlane-plugin-xxx'
查看本地缓存的插件:
cd ~/.rvm/gems/ruby-2.7.0/gems

fastlane 切换分支的插件
https://github.com/chdzq/fastlane-plugin-git_switch_branch

双重认证问题

生成一个不需要进行短信认证的application specific password,这个需要到 苹果开发者中心的账号管理下进行生成。注意生成后将其备份,因为页面一旦关闭将无法再次查看

执行命令 fastlane spaceauth -u [开发者账号],生成FASTLANE_SESSION,将其备份
配置环境变量如下:
export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD=[application specific password(替换为你自己的)]
export FASTLANE_SESSION=[FASTLANE_SESSION(替换为你自己的)]
执行source ~/.bash_profile使新增的环境变量生效,分别执行以下命令查看环境变量是否设置成功(成功会在控制台有打印)
echo $FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD
echo $FASTLANE_SESSION
Jenkins中的shell配置

注意:如果在Jenkins中需要执行shell,cd到某个路径,必须要在localhost:8080(端口号)下写相关命令
不可通过局域网IP:端口号方式进入Jenkins,远程配置cd命令 !!

#!/bin/bash
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8

# 工程目录
cd your project path


# 环境切换命令
# 生产环境 与 开发环境 二选一
# 测试环境命令(可切换测试环境)           fastlane development_env
# 生产环境命令(不可切换测试环境)         fastlane product_env

fastlane development_env


# ipa分发
# development 与 开发环境 二选一
# 上传测试平台(fir)	  fastlane app_test
# 上传testFlight       fastlane app_product

# fastlane app_product 默认会切换到生产环境🍺

fastlane app_test

开箱即食

安装 fir插件

fastlane 
fastlane add_plugin firim

安装versioning插件

fastlane add_plugin versioning

以下是我们项目中用的Fastfile文件内容:

# 定义fastlane版本号
fastlane_version "2.198.1"

# 定义打包平台
default_platform :ios

#指定项目的scheme名称
scheme = "xxx"

#导出ipa模式
export_method = "development"

#fir
fir_api_token = "xxx"

pwd_path = sh("pwd")
pwd_path = "#{pwd_path}"[0...pwd_path.length - 1]

ipa_version = get_version_number_from_plist(xcodeproj: "#{scheme}.xcodeproj", # optional
                                    target: scheme,
                                    plist_build_setting_support: true,
                                    build_configuration_name: 'Release')

ipa_build_base_path = "#{pwd_path}/build"
ipa_dir_base_path = "#{ipa_build_base_path}/#{ipa_version}"

# 任务脚本
platform :ios do

    lane :local_noti do|options|
        download_url = "请留意testFlight更新通知"
        if options[:url]
            download_url = options[:url]
        end
        
        puts("*************| 🐮上传ipa成功🍺 |*************")
        puts("*************| #{ipa_version} build #{get_build_number()} 下载地址: |*************")
        puts("\n\n\t 最新包地址  #{options[:newest_url]} \n")
        puts("\n\n\t 当前包地址  #{download_url}\n")
        puts("*************| 🎉🎉🎉🎉🎉🎉🎉 |*************")
        # 本地通知
        # notification(subtitle: "Finished upload", message: "all work done",open: download_url)
    end

    lane :development_env do
        change_env env_state:"open"
    end
    lane :product_env do
        change_env env_state:"close"
    end

    lane :change_env do|options|
        sh("cd ../#{scheme} && /usr/libexec/PlistBuddy -c 'Set :EnvironmentSwitch #{options[:env_state]}' ./Info.plist")
    end
   
    lane :upload_build do
        sh("cd .. && git add . && git commit -m \"change number to #{ipa_version} build #{get_build_number()}\" && git push")
    end

    lane :upload_fir do|options|
        puts("*************| 开始上传fir... |*************")
        
        output_name = "#{scheme}_#{export_method}_#{get_build_number()}"
        ipa_path = "#{ipa_dir_base_path}/#{get_build_number()}/#{output_name}.ipa"
        enterprise_ipa_path = "#{ipa_path[0...ipa_path.length-4]}_enterprise.ipa"

        download_url = "xxxrelease_id="
        newest_download_url = "xxx"
        if options[:no_resign]
            enterprise_ipa_path = ipa_path
            # 从fir对应bundleId下复制下载地址
            download_url = "xxx?release_id="
            newest_download_url = "xxx"
        end
        answer = firim(firim_api_token: fir_api_token, ipa: enterprise_ipa_path)
        
        download_url = "#{download_url}#{answer[:rows][-2][1]}"

        local_noti url:download_url,newest_url:newest_download_url
    end

    # build递增
    lane :increase_build_num do|options|
        if options[:build_num] then
            increment_build_number(
                build_number: "#{options[:build_num]}"
            )
        else
            increment_build_number
        end
        # 也可用以下方式可按照日期处理build号 例: 0222.01
        # currentTime = Time.new.strftime("%m%d")
        # build = get_build_number()
        # if build.include?"#{currentTime}."
        #     # 当天版本计算迭代版本号
        #     lastStr = build[build.length-2..build.length-1]
        #     lastNum = lastStr.to_i
        #     lastNum = lastNum + 1
        #     lastStr = lastNum.to_s
        #     if lastNum < 10
        #         lastStr = lastStr.insert(0,"0")
        #     end
        #     build = "#{currentTime}.#{lastStr}"
        # else
        #     # => 非当天版本 build 号重置
        #     build = "#{currentTime}.01"
        # end
        # puts("*************| 更新build #{build} |*************")
        # # 更改项目 build 号
        # increment_build_number(build_number: "#{build}")
        puts("*************| 当前build号 #{get_build_number()} |*************")
    end

    # pod install全量更新
    lane :update_project do
        sh("cd ../ && git pull")
        sh("if [ -e \"../Podfile.lock\" ]; then rm ../Podfile.lock; fi")
        sh("if [ -d \"../Pods\" ]; then rm -rf ../Pods; fi")
        cocoapods(
          clean_install: true,
          # 存在 Gemfile 时使用 bundle exec
          use_bundle_exec: false
        )
    end

    # 按scheme导出ipa
    lane :output_ipa do|options|
        if options[:export_method]
            export_method = options[:export_method]
        end
        dir_path = "/"
        if options[:dir_path]
            dir_path = options[:dir_path]
        end
        output_directory = "#{ipa_dir_base_path}#{dir_path}#{get_build_number()}"
        output_name = "#{scheme}_#{export_method}_#{get_build_number()}"
        # 开始打包
        gym(
            scheme: scheme,
            #输出的ipa名称
            output_name: output_name,
            # 是否清空以前的编译信息 true:是
            clean:true,
            # 指定打包方式,Release 或者 Debug
            configuration: "Release",
            # 指定打包所使用的输出方式
            #目前支持app-store, package, ad-hoc, enterprise, development
            export_method: export_method,
            # 指定输出文件夹
            output_directory: output_directory,
            # 自动管理证书的时候,Xcode 9及以上没有权限获取钥匙串里面的证书,必须加上这个才能打包成功
            export_xcargs: "-allowProvisioningUpdates",
            include_bitcode: false,
            include_symbols: true
        )
    end
    
    # 企业签
    lane :resign_ipa do|options|
        output_name = "#{scheme}_#{export_method}_#{get_build_number()}"
        ipa_path = "#{ipa_dir_base_path}/#{get_build_number()}/#{output_name}.ipa"
        if options[:ipa_path]
            ipa_path = options[:ipa_path]            
        end
        
        enterprise_ipa_path = "#{ipa_path[0...ipa_path.length-4]}_enterprise.ipa"
        
        sh("cp #{ipa_path} #{enterprise_ipa_path}")
        puts("*************| 待重签ipa路径:#{ipa_path} |*************")
        puts("*************| 开始重签 |*************")
        resign(
            ipa: enterprise_ipa_path,
            signing_identity: "iPhone Distribution: xxx Co. Ltd.",
            use_app_entitlements: true,
            # 1. plugIn的bundleId前缀务必和主工程bundleId一致
            # 2. 每一个plugIn的bundleId要在和主工程.mobileprovision相同账号下生成对应的 .mobileprovision
            provisioning_profile: {
                "com.demo.test" => "./enterprise_inhouse.mobileprovision",
                "com.demo.test.boardcastupload" => "./boardcastupload.mobileprovision"
            }   
        )
    end

    lane :manual_reupload_ipa do|options|
        ipa_version = "#{options[:version]}"        
        version_path_arr = []

        if ipa_version.include? ".."
            version_path_arr = ipa_version.split("..")
        end

        if  version_path_arr.length == 0 ||
            version_path_arr[0].split(".").length != 3 ||
            version_path_arr.length != 2
            puts("\n\n\n\n\n\n")
            puts("\t******| 参数有误,格式:版本号 build号 | ******")
            puts("\t******| 例如:fastlane manual_reupload_ipa version:1.1.1..11 | ******")


            exit(0)


        else
            auto_reupload_ipa version_path_arr:version_path_arr
        end
    end

    lane :auto_reupload_ipa do|options|
        if options[:scheme]
            scheme = options[:scheme]
        end

        ipa_path = ""
        version_arr = options[:version_path_arr]
        if version_arr
            version_path = "#{version_arr[0]}/#{version_arr[1]}"
            ipa_path = "#{ipa_build_base_path}/#{version_path}/#{scheme}_#{export_method}_#{version_arr[1]}.ipa"
        else
            ipa_path = "#{ipa_dir_base_path}/#{get_build_number()}/#{scheme}_#{export_method}_#{get_build_number()}.ipa"
        end
        
        if FileTest::exist?(ipa_path) == false
            puts("\n\n\n\n\n\n\t******| 版本#{version_arr[0]} #{version_arr[1]} 不存在!请重新打包******")


            exit(0)
            

        else
            resign_ipa ipa_path:ipa_path
            upload_fir
        end
        
    end
    
    lane :upload_testFlight do|options|
        output_name = "#{scheme}_#{export_method}_#{get_build_number()}"
        ipa_path = "#{ipa_dir_base_path}/appstore/#{get_build_number()}/#{output_name}.ipa"
        upload_to_testflight(ipa: ipa_path,username:"xxx@qq.com")
    end

    lane :app_test_common do|options|
        if options[:export_method]
            export_method = options[:export_method]
        end
        puts("*************| 开始打 #{export_method} ipa |*************")
        puts("*************| 更换模式命令 fastlane app_test:xxx(options:app-store, package, ad-hoc, enterprise, development) |*************")
        puts("*************| 命令 fastlane app_product 可输出包并上传testFlight |*************")
        # 重装所有库
        update_project
        #更改项目build号
        increase_build_num build_num:options[:build_num]
    end

    # 不重新安装pods,重新编译输出app-store ipa
    lane :app_product_fast do|options|
        product_env
        output_ipa export_method:"app-store",dir_path:"/appstore/"        
        # 用python脚本调用transporter方式替换
        # upload_testFlight dir_path:"/appstore/"
    end

    # 重装pods,重新编译输出app-store ipa
    lane :app_product do|options|
        app_test_common export_method:"app-store"
        app_product_fast
        upload_build
    end
    
    lane :app_test_no_resign do|options|
        app_test no_resign:true
    end

    lane :app_test do|options|
        app_test_common export_method:options[:export_method]
        output_ipa export_method:export_method
        if !options[:no_resign]
            resign_ipa
        end
        upload_fir no_resign:options[:no_resign]
        upload_build
    end
end

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/993435.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存