效率:iOS基于Fastlane的自动化打包/发布工具

lazy模式:因为同时负责多个项目,本着fastlane文件写一个就能所有项目拖过去直接用的目的而创建。(涉及到项目的地方已做除密处理)

dingding.jpeg
slack.jpeg

使用方法:

  1. 把actions和Fastfile拖入项目中的fastlane文件夹中;
  2. 配置Fastfile文件中相关参数;
  3. 修改verison值(Build为脚本自动设置yyyyMMddHHmm);
  4. 把项目文件夹拖入终端窗口;
  5. 执行命令:
    fastlane release 打包并上传到appStore;
    fastlane fir 打包上传到 firim;
    fastlane pgy 打包上传到 蒲公英;

6.支持钉钉和 slack 机器人

Fastlane_lazy

.env文件

 
Apple_Id        = "*" # Your Apple email address
App_Identifier  = "*"

Team_Id         = "*" # Developer Portal Team ID

Itc_Team_Name   = "*"
Itc_Team_Id     = "*"

Scheme_Name     = "*"
App_Identifier  = "*"

# ipa包文件路径
IpaDir_Development  = "*/ipa_development"
IpaDir_AppStore     = "*/ipa_appStore"

APP_Slogan          = "*"
App_Icon_Local      = "*/app_icon.jpg"
App_Icon_Link       = "*"
App_Store_Link      = "itms-apps://apps.apple.com/cn/app/*?mt=8"

Firim_Shortcut_Link = "*"
Pgyer_Shortcut_Link = "*"

Dingtalk_Url = "*"
Slack_Url           = "*"

# APP元数据及截图存放路径
App_Icon_Local      = "./fastlane/metadata/app_icon.jpg"
Metadata_Path       = "./fastlane/metadata"
Screenshots_Path    = "./fastlane/screenshots"

Fastfile文件

# fastlane_version "2.112.0"
default_platform :ios

platform :ios do

  #firim token
  FirToken = "*"
  #蒲同英
  PgyerApiKey = "*"
  PgyerUserKey = "*"


  ## Common
  desc "发布新版本到AppStore"
  lane :release do
    #默认env文件
    archiveRelease()
    uploadRelease()
  end

  desc "上传新版本到AppStore"
  lane :releaseupload do
    #默认env文件
    uploadRelease()
  end

  desc "发布One到AppStore"
  lane :releaseA do
    # actionRelease(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_AppStore"])
    sh "fastlane release --env targetA"
  end

  desc "发布到Fir"
  lane :fir do
    #默认env文件
    archiveDevelopment() 
    uploadFir()
  end

  lane :firupload do 
    uploadFir()
  end

  desc "发布到pgyer"
  lane :pgy do
    #默认env文件
    archiveDevelopment()
    uploadPgyer()
  end

  desc "发布到pgyer"
  lane :pgyupload do
    #默认env文件
    uploadPgyer()
  end

  desc "one发布到Fir"
  lane :firA do
    # actionFir(name: ENV["Scheme_name"], ipaDir: ENV["IpaDir_Development"]) 
    sh "fastlane fir --env targetA"
  end

  desc "发布A到pgyer"
  lane :pgyA do
    #默认env文件
    sh "fastlane pgy --env targetA"
  end
 
  desc "[通用]发布新版本到 AppStore"
  def archiveRelease(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_AppStore"])
    clear_cache_files()
    # cocoapods()
    updateBuildVersion(showHash: false)
    gym(
      output_directory: ipaDir,
      scheme: name,
      export_method: 'app-store',
      export_options: {
        xcargs: "-allowProvisioningUpdates",
        iCloudContainerEnvironment: 'Production'
      }
    )
  end

  desc "[通用]发布新版本到 AppStore"
  def uploadRelease(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_AppStore"])
    deliver(force: true,
      # ipa: "/Users/shang/ACS/access-admin-app-ios/ipa_appStore/AccessControlSystem.ipa",
      ipa: "#{ipaDir}/#{name}.ipa",
      metadata_path: ENV["Metadata_Path"], 
      screenshots_path: ENV["Screenshots_Path"], 
    )
    # system "open #{ipaDir}"
    handleDingTalk(name: name, ipaDir: ipaDir, appUrl: ENV["App_Store_Link"])
  end 

  desc "[通用]发布新版本到 fir"
  def archiveDevelopment(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_Development"])
    updateBuildVersion(showHash: true)
    gym(
      clean: true,
      include_symbols: true,
      output_directory: ipaDir,
      scheme: name,
      configuration: 'Debug',
      export_method: 'development',
      export_options: {iCloudContainerEnvironment: 'Development'}
    )
  end

  desc "上传ipad 到 firim"
  def uploadFir(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_Development"], firToken: FirToken)
    firim(firim_api_token: firToken, icon: ENV["App_Icon_Local"], ipa: "#{ipaDir}/#{name}.ipa")
    handleDingTalk(name: name, ipaDir: ipaDir)
  end

  desc "上传ipad 到 Pgyer"
  def uploadPgyer(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_Development"], apiKey: PgyerApiKey, userKey: PgyerUserKey)
    # uploadtime = Time.now.strftime("%Y%m%d%H%M")
    uploadtime = Time.now.strftime("%Y-%m-%d %H:%M:%S")

    pgyer(api_key: apiKey, 
      user_key: userKey,
      # update_description: "fastlane自动打包上传测试 pgyer",  
      update_description: "#{uploadtime}", 
      password: "123456", 
      install_type: "2",
      ipa: "#{ipaDir}/#{name}.ipa",
    )
    handleDingTalk(name: name, ipaDir: ipaDir, appUrl: ENV["Pgyer_Shortcut_Link"])
  end


  desc "更新BuildVersion"
  def updateBuildVersion(name: ENV["Scheme_Name"], showHash: true)
    plistDir = File.dirname(Dir.pwd)
    puts "plist所在目录:_#{plistDir}_"

    case name
    when "IntelligentOfParkingOne"
      plistFile = plist_from_path(path: "#{plistDir}/#{name}/#{name}-Info.plist")

    else
      plistFile = plist_from_path(path: "#{plistDir}/#{name}/*Info.plist")

    end

    puts "plistFile:_#{plistFile}_"
    update_build({plist: plistFile, showHash: showHash})
  end


  desc "webhook 钉钉"
  def handleDingTalk(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_Development"], downloadUrl: ENV["Firim_Shortcut_Link"])
    appPath = ipaDir + "/#{name}.ipa"
    params = {appPath: appPath,
              appIcon: ENV["App_Icon_Link"],
              dingUrl: ENV["Dingtalk_Url"],
              slogan: ENV["APP_Slogan"],
              downloadUrl: downloadUrl
              }
    puts "---#{params.to_json}--"
    dingdingtalk_robot(params)
  end


  desc "推送通知到slack"
  def handleDingSlack(name: ENV["Scheme_Name"], ipaDir: ENV["IpaDir_Development"], downloadUrl: ENV["Firim_Shortcut_Link"])
    appPath = ipaDir + "/#{name}.ipa"
    params = {appPath: appPath,
              appIcon: ENV["App_Icon_Link"],
              slackUrl: ENV["Slack_Url"],
              slogan: ENV["APP_Slogan"],
              downloadUrl: downloadUrl
              }
    puts "---#{params.to_json}--"
    dingslack_robot(params)
  end


  desc "测试"
  lane :test do
    handleDingSlack()
    # handleDingTalk()
  end

  desc "one发布到Fir"
  lane :testA do
    # actionFir(name: ENV["Scheme_name"], ipaDir: ENV["IpaDir_Development"]) 
    sh "fastlane test --env targetA"
  end

  # You can define as many lanes as you want
  after_all do |lane|

  end

  error do |lane, exception|

  end
end

dingdingtalk_robot.rb

module Fastlane
  module Actions
    module SharedValues
      DINGDINGTALK_ROBOT_CUSTOM_VALUE = :DINGDINGTALK_ROBOT_CUSTOM_VALUE
    end

    class DingdingtalkRobotAction < Action
      def self.run(params)
        appPath = params[:appPath]
        appIcon = params[:appIcon]
        dingUrl = params[:dingUrl]
        downloadUrl = params[:downloadUrl]
        slogan = params[:slogan]
        markdownText = params[:markdownText]

        appName    = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleDisplayName")
        bundleName = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleBundleName")
        appVersion = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleShortVersionString")
        appBuild   = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleVersion")
        ipaName    = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleName")#备用

        appName = appName.empty? == false ? appName : bundleName

        platformInfo = "已更新至 #{downloadUrl.split(".")[1].capitalize} 啦!"
        title = "iOS #{appName} #{appVersion} #{platformInfo}"
        time = Time.new.strftime("%Y-%m-%d %H:%M:%S")

        markdown ={
          msgtype: "link",
          link: {
              title: title,
              text: "版  本:#{appBuild}\n地  址:#{downloadUrl}\n时  间:#{time}",
              picUrl: "#{appIcon}",
              messageUrl: "#{downloadUrl}"
          }
        }

        if markdownText
          markdownText = "#{markdownText}   \n  - [Download](#{downloadUrl})"
          markdown ={
               "msgtype": "markdown",
               "markdown": {"title": "#{title}",
                            "text": "### #{title}\n#{markdownText}",
               }
           }
        end

        ##请求
        uri = URI.parse(dingUrl)
        https = Net::HTTP.new(uri.host, uri.port)
        https.use_ssl = true

        request = Net::HTTP::Post.new(uri.request_uri)
        request.add_field('Content-Type', 'application/json')
        request.body = markdown.to_json

        response = https.request(request)
        puts "-----------#{uri.request_uri}-------------------"
        puts "Response #{response.code} #{response.message}: #{response.body}"
      end

      #####################################################
      # @!group Documentation
      #####################################################

      def self.description
        "A short description with <= 80 characters of what this action does"
      end

      def self.details
        # Optional:
        # this is your chance to provide a more detailed description of this action
        "You can use this action to do cool things..."
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :appPath,
                                     env_name: "GET_IPA",
                                  description: "ipa文件所在的文件夹路径",
                                     optional: false,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :downloadUrl,
                                  description: "fir的ipa文件下载网址",
                                     optional: false,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :appIcon,
                                  description: "ipa图标网络地址",
                                     optional: true,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :dingUrl,
                                  description: "钉钉机器人网络接口",
                                     optional: false,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :markdownText,
                                  description: "钉钉机器人 msgtype: markdown时的text",
                                     optional: true,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :slogan,
                                  description: "slogan",
                                     optional: true,
                                         type: String),
        ]
      end

      def self.output
        # Define the shared values you are going to provide
        # Example
        [
          ['DINGDINGTALK_ROBOT_CUSTOM_VALUE', 'A description of what this value contains']
        ]
      end

      def self.return_value
        # If your method provides a return value, you can describe here what it does
      end

      def self.authors
        # So no one will ever forget your contribution to fastlane :) You are awesome btw!
        ["Your GitHub/Twitter Name"]
      end

      def self.is_supported?(platform)
        # you can do things like
        #
        #  true
        #
        #  platform == :ios
        #
        #  [:ios, :mac].include?(platform)
        #

        platform == :ios
      end
    end
  end
end

dingslack_robot.rb

module Fastlane
  module Actions
    module SharedValues
      DINGSLACK_ROBOT_CUSTOM_VALUE = :DINGSLACK_ROBOT_CUSTOM_VALUE
    end

    class DingslackRobotAction < Action
      def self.run(params)
        appPath = params[:appPath]
        downloadUrl = params[:downloadUrl]
        appIcon = params[:appIcon]
        slackUrl = params[:slackUrl]
        slogan = params[:slogan]

        appName    = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleDisplayName")
        bundleName = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleBundleName")
        appVersion = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleShortVersionString")
        appBuild   = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleVersion")
        ipaName    = other_action.get_ipa_info_plist_value(ipa: appPath, key: "CFBundleName")#备用

        appName = appName.empty? == false ? appName : bundleName

        platformInfo = "已更新至 #{downloadUrl.split(".")[1].capitalize} 啦!"
        message = "iOS #{appName} v#{appVersion} #{platformInfo}"

        author = `git log -1 --pretty=format:"%ae"`
        time = Time.new.strftime("%Y-%m-%d %H:%M:%S")

        other_action.slack(
          # message: "<项目名称> Successfully deployed new App Update.",
          message: message,
          slack_url: slackUrl,
          # default_payloads: [:lane, :git_branch, :git_author, :last_git_commit],
          default_payloads: [],
          attachment_properties: {
            thumb_url: appIcon,
            author_name: author,
            pretext: slogan,
            fields: [
            {
              # title: "DownloadUrl",
              value: "版本:#{appBuild}",
              short: true
            },
            {
              # title: "DownloadUrl",
              value: "下载:#{downloadUrl}",
              short: true
            },
            {
              # title: "时间日期",
              value: "时间:#{time}",
              short: false
            }]
          }
        )
      end

      #####################################################
      # @!group Documentation
      #####################################################

      def self.description
        "A short description with <= 80 characters of what this action does"
      end

      def self.details
        # Optional:
        # this is your chance to provide a more detailed description of this action
        "You can use this action to do cool things..."
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :appPath,
                                     env_name: "GET_IPA",
                                  description: "ipa文件所在的文件夹路径",
                                     optional: false,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :downloadUrl,
                                  description: "fir的ipa文件下载网址",
                                     optional: false,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :appIcon,
                                  description: "ipa图标网络地址",
                                     optional: true,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :slackUrl,
                                  description: "slack webhook Url",
                                     optional: false,
                                         type: String),
             FastlaneCore::ConfigItem.new(key: :slogan,
                                  description: "slogan",
                                     optional: true,
                                         type: String),
        ]
      end

      def self.output
        # Define the shared values you are going to provide
        # Example
        [
          ['DINGSLACK_ROBOT_CUSTOM_VALUE', 'A description of what this value contains']
        ]
      end

      def self.return_value
        # If your method provides a return value, you can describe here what it does
      end

      def self.authors
        # So no one will ever forget your contribution to fastlane :) You are awesome btw!
        ["Your GitHub/Twitter Name"]
      end

      def self.is_supported?(platform)
        # you can do things like
        #
        #  true
        #
        #  platform == :ios
        #
        #  [:ios, :mac].include?(platform)
        #

        platform == :ios
      end
    end
  end
end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 【日精进打卡第334天】 【知~学习】 《六项精进》0遍 共259遍 《六项精进通篇》0遍 共3遍 大学译文 共4...
    努力1314阅读 143评论 0 0
  • 我徜徉在中新史的海洋里,溺毙。 背近代开端那一块,我真的觉得要乱死了。这个xx国的人在xx地方办了一个xx语言的报...
    吉野佳阅读 60评论 0 1
  • 感赏孩子对我的及时回应,有了和妈妈沟通的好兆头。 感赏孩子自己动手洗衣服,是动手能力强,爱干净讲究生活品质的人。 ...
    风景_6b35阅读 156评论 0 1
  • 为什么他是作家,因为他能把主题升华,他能把“致良知"放大到极致,他能把美好的人性还原到"原始森林"状态;他是吸收了...
    江苏刘志祥阅读 442评论 0 0

友情链接更多精彩内容