实操:开发 Fastlane 插件

本文最后更新于:2022年7月6日 上午

开始整活

jenkins 打包机器坏了还没修,本地打包之后每次都要通知测试去下载测试包。太麻烦了,直接搞个插件,打包之后自动发送钉钉消息到群里吧

创建 gem 项目

fastlane new_plugin ykz_dingding_notify

创建好的项目目录如下:

├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── Rakefile
├── coverage
├── fastlane
│   ├── Fastfile
│   ├── Pluginfile
│   ├── README.md
│   └── report.xml
├── fastlane-plugin-ykz_upload_bugly.gemspec
├── lib
│   └── fastlane
│       └── plugin
│           ├── ykz_upload_bugly
│           │   ├── actions
│           │   │   └── ykz_upload_bugly_action.rb
│           │   ├── helper
│           │   │   └── ykz_upload_bugly_helper.rb
│           │   └── version.rb
│           └── ykz_upload_bugly.rb
├── spec

其中需要重点关注下面2个文件(目录):

  • fastlane-plugin-ykz_upload_bugly.gemspec

    基础配置文件,如果我们有额外的三方依赖的话,可以在这里进行配置,这次没有改动次文件

  • lib 目录

    是我们需要重点关注的目录,我们主要的工作就是在 actions/ykz_upload_bugly_action.rb 中

    编写主要的代码逻辑,例如处理参数、调用钉钉通知、处理返回结果等。

    其中,helper/ykz_upload_bugly_helper.rb 中可以定义一些工具方法,versions.rb 则是配置此插件的版本号。

Fastlane Action

actions/ykz_upload_bugly_action.rb 文件内容如下:

require 'fastlane/action'
require_relative '../helper/ykz_dingding_notify_helper'

module Fastlane
  module Actions
    class YkzDingdingNotifyAction < Action
      def self.run(params)
        # 处理核心逻辑的地方
        # 处理参数、调用接口、处理返回
      end

      def self.description
        "notify after app build"
      end

      def self.authors
        ["yadong"]
      end

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

      def self.details
        # Optional:
        "notify after app build"
      end

      def self.available_options
      	# 一般来说,我们肯定是需要接受外部传递参数的,这时就需要在此定义需要接收哪些参数
        # 返回值为一个 FastlaneCore::ConfigItem 类型的数组
      end

      def self.is_supported?(platform)
        # Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
        # See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
        #
        [:ios, :mac].include?(platform)
        true
      end
    end
  end
end

我们先来想一下钉钉通知插件需要什么参数吧,最简单来说,我们需要如下2个参数:

  • access_token: 钉钉机器人发送消息,这个是必备的了
  • message: 简单点,我们只处理 text 类型消息,那么我们就再额外传递一个消息内容就 OK 了

首先我们来写 self.available_options 方法,编辑我们需要接收的参数:

def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :access_token,
                            env_name: "YKZ_DINGDING_NOTIFY_ACCESS_TOKEN",
                         description: "dingding access_token",
                            optional: false,
                                type: String),
    FastlaneCore::ConfigItem.new(key: :message,
                            env_name: "YKZ_DINGDING_NOTIFY_MESSAGE",
                         description: "message if needed",
                            optional: false,
                                type: String)
  ]
end

参数定义好了,可以写核心的逻辑了。其实核心的逻辑相当简单,利用接收到的参数,拼接好 curl 请求然后调用并处理返回结果就行了。我们先看一下官方文档的钉钉通知 curl 请求:

curl 'https://oapi.dingtalk.com/robot/send?access_token=your_access_token' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text","text": {"content": "我就是我, 是不一样的烟火"}}'

我们只要替换到请求中的 access_tokencontent 两个地方就可以达到我们的目的。

继续写 self.run方法:

def self.run(params)
  require 'json'

  UI.message("start notify dingding...")

  # 处理 curl 返回,返回结果会被存储到 json_file 中
  json_file = 'notify_dingding_result.json'

  # 获取 access_token
  begin
    access_token = ''
    unless params[:access_token].empty?
      access_token = params[:access_token]
      UI.message("access_token: #{access_token}")
    end
  rescue => exception
    UI.message("error at checking access_token, caused by #{exception}")
    return
  end

  # 获取 message
  begin
    message = ''
    unless params[:message].empty?
      message = params[:message]
    end
  rescue => exception
    UI.message("error at checking message, caused by #{exception}")
    return
  end

  # 拼接 curl
  cmd = <<-DESC
    curl 'https://oapi.dingtalk.com/robot/send?access_token=#{access_token}' \
    -H 'Content-Type: application/json' \
    -d '{"msgtype": "text","text": {"content": "#{message}"}}' \
    -o #{json_file}
  DESC

  # 调用 curl 执行请求
  sh(cmd)
	# 处理返回结果
  obj = JSON.parse(File.read(json_file))
  ret = obj['errcode']
  if ret == 0
    UI.message("notify dingding successfully")
  else
    UI.message("notify dingding failed, result is #{obj}")
  end
	# 最后删除 json 文件,避免占用空间
  `rm notify_dingding_result.json`
end

测试

最主要的逻辑写完了,我们想要测试一下。我们在项目内 fastlane/Fastfile 文件中编写我们的测试逻辑:

lane :test do
  ykz_dingding_notify(
    access_token: 'xxxxx',
    message: '构建:哈哈哈哈哈',
  )
end

然后命令行进入我们项目目录,执行:

fastlane test
# 由于我这里用了 bundle,因此我执行了 bundle exec fastlane test

iOS 工程里使用插件

我们把插件项目上传到 git 仓库中,然后打开 iOS 项目中的 fastlane/Pluginfile 文件:

gem 'fastlane-plugin-ykz_dingding_notify', git: 'your plugin git'

然后再安装我们的插件信息:

fastlane install_plugins
# 由于我用了 bundle,因此我这里执行 bundle install bundle exec fastlane install_plugins

现在,我们可以在我们的 lane 中使用通知插件了:

desc "ad-hoc"
lane :adhoc do |options|
	...
  ...
  ykz_dingding_notify(
    access_token: 'xxxx',
    message: '构建通知:老铁们,打包成功了啊',
  )
end

喜大普奔!!!!


 目录