pod库自动化部署脚本

作者: 红袖吾狗 | 来源:发表于2021-08-25 15:30 被阅读0次

    前言

    我们平时在做开发的时候,多少会接触到组件化的思想。iOS的组件化一般是通过使用 cocoapods 制成pod库的形式完成的。pod库又分为公开库和私有库两种。比如我们上传到 github 的就属于公开库,提供别人下载使用。而部署在公司内部的则属于私有库,别人访问不了,使用不到。

    概念

    本文主要是讲如何实现自动部署pod脚本的,因此关于pod库相关的概念只会做个简单的介绍,不会做过多的讲解。如果想要有更深入的了解,可以查阅官方文档

    1. repo

    一个 repo 就是相当于一个放置相关 pod 库的索引的仓库。怎么理解?比如我们在github上制作的公开库,它的 repo 叫什么?叫 trunk,地址是 https://cdn.cocoapods.org/ 。我们可以直接在本机执行命令: pod repo,将会看到图示。

    截屏2021-08-13 下午2.06.44.png

    这个仓库就是存放我们部署上去的 podspec 文件的。

    2. podspec

    而一个 podspec 则是用于描述一个pod库,如库结构,版本,源代码地址等。


    了解了这两个,那么我们就可以制作自己的pod库了。

    制作pod库

    以制作公开库,提交到 github 为例。提交到trunk的官方文档点我

    首先你需要现有一个源代码仓库,

    1. 创建podspec文件

    pod spec create yourSpecName.podspec
    

    2. 编辑podspec文件

    例如podspec文件内容如下,编辑里面的source、source_files等。

    Pod::Spec.new do |spec|
      spec.name             = 'Reachability'
      spec.version          = '3.1.0'
      spec.license          = { :type => 'BSD' }
      spec.homepage         = 'https://github.com/tonymillion/Reachability'
      spec.authors          = { 'Tony Million' => 'tonymillion@gmail.com' }
      spec.summary          = 'ARC and GCD Compatible Reachability Class for iOS and macOS.'
      spec.source           = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => 'v3.1.0' }
      spec.source_files     = 'Reachability.h,m'
      spec.framework        = 'SystemConfiguration'
      spec.requires_arc     = true
    end
    

    3. 提交我们的源代码

    git commit -am 'submit'
    git push origin
    

    4. 给我们的pod打tag

    注意到podspec是有版本号的概念的,它需要和你pod库,也就是源代码库的tag值是一致的。这样,我们的cocoapods才能在解析podspec之后,下载到正确版本对应的源代码。

    
    git tag 0.0.2
    
    #推送指定tag到远端
    git push origin 0.0.2
    
    # 或者推送本地所有tag到远端
    # git push origin --tags
    

    5. 验证我们的podspec文件格式是否正确

    本地验证:

    pod lib lint yourSpecName.podspec --allow-warnings
    

    远程验证:

    pod spec line yourSpecName.podspec --allow-warnings
    

    6. 发布我们的podspec

    pod trunk push yourSpecName.podspec --allow-warnings
    

    更新pod库

    此时我们的库中应该已经有一个podspec文件了的,我们需要做的就是更新它的版本号,如果文件结构有变动,就需要改动 source_files,有添加新的依赖库,那么需要修改 dependency

    1. 更新podspec的版本号

    打开podspec文件,编辑更新version字段就好,一般采用递增的方式。

    1. 为我们新版本的代码打tag

    # 需要先提交代码
    git commit -am 'update'
    git push origin
    git tag 0.0.2
    git push origin 0.0.2
    # 或 git push origin --tags
    

    2. 本地验证新修改的podspec文件是否有问题

    这一步在这里骑士可以跳过,一般第一次上传没问题,后面也不会出现问题。

    pod lib lint *.podspec
    

    3. 部署新版本的podspec

    pod trunk push *.podspec
    

    脚本实现

    • 【0821更新】当存在多个podspec时,支持指定podspec上传。
    1. PodPushFile新增 PUSH_PODSPEC_NAME 字段,允许用户指定上传的podspec文件
    2. 当未指定podspec文件时,在搜索路径下发现有多个podspec文件,那么允许用户手动指定
    截屏2021-08-22 下午5.44.00.png

    截屏2021-08-22 下午5.44.48.png

    脚本使用 ruby 实现的,最新源码点我。为保证使用最新源码,请去 github 上下载。

    实现的原理其实也是用了更新podspec的步骤,只不过是加了一些配置而已。

    这里直接贴代码【最新】:

    #! /usr/bin/ruby
    
    class Color
        def self.natural
            0
        end
        def self.red
            31
        end
        def self.green
            32
        end
        def self.white
            37
        end
    end
    
    def color_text(text, color = Color.natural)
        if color == 0
            return text
        end
        return "\033[#{color}m#{text}\033[0m"
    end
    
    def die_log(text)
        puts color_text(text, Color.red)
    end
    
    # 拉取最新代码
    # if system('git pull --rebase origin') == false
    #     system('git rebase --abort')
    #     puts color_text("There is a conflict, please handle it and retry", Color.red)
    #     return
    # end
    
    
    cur_path = Dir.pwd
    push_path = cur_path
    relate_dir_path = ''
    push_podspec_name = ''
    user_custom_version = true
    verify_podspec_format = true
    pod_repo_name = 'trunk'
    pod_repo_source =
    is_static_lib = false
    
    # 检查是否存在 SpecPushFile 文件,如果不存在,那么创建
    if not File::exist?(cur_path + '/PodPushFile')
        system('touch PodPushFile')
        File.open(cur_path + '/PodPushFile', 'w+') do |f|
            f.write("#写入*.podspec所在的相对目录,不写默认会在脚本执行的目录下查找
    PUSH_DIR_PATH=
    #用户还可以指定要推送的podspec文件的名字,这个存在多个podspec的时候会用到
    PUSH_PODSPEC_NAME=
    #是否允许用户自定义版本号,不填或填true将允许用户设置自定义的版本号,而不是自增版本号
    USER_CUSTOM_VERSION=true
    #默认开启验证,可以跳过验证阶段
    VERIFY_PODSPEC_FORMAT=true
    #pod repo的名字,如果是私有库就填私有库的名字
    POD_REPO_NAME=trunk
    #pod repo的源地址
    POD_REPO_SOURCE=https://github.com/CocoaPods/Specs
    #如果这个库是静态库,那么需要设置为true
    POD_IS_STATIC_LIBRARY=false")
        end
        puts color_text('Create PodPushFile', Color.green)
        puts color_text("First you should modify 'PodPushFile' file and run the script again", Color.white)
        system('open PodPushFile')
        return
    end
    
    puts color_text('Parse PodPushFile...', Color.white)
    File.open(cur_path + '/PodPushFile') do |f|
        f.each_line do |line|
            key_value = line.split('=')
            key = key_value.first.to_s.gsub("\n", '').gsub(' ','').gsub("\t",'')
            value =
            if key_value.count > 1
                value = key_value.last.to_s.gsub("\n", '').gsub(' ','').gsub("\t",'')
            end
            # puts "key=#{key},value=#{value}"
            if key.to_s == 'PUSH_DIR_PATH' and not value.nil?
                relate_dir_path = value
                push_path = cur_path + '/' + relate_dir_path
            elsif key.to_s == 'PUSH_PODSPEC_NAME' and not value.nil?
                push_podspec_name = value.to_s
            elsif key.to_s == 'USER_CUSTOM_VERSION' and not value.nil?
                user_custom_version = value == 'true'
            elsif key.to_s == 'VERIFY_PODSPEC_FORMAT' and not value.nil?
                verify_podspec_format = value == 'true'
            elsif key.to_s == 'POD_REPO_NAME' and not value.nil?
                pod_repo_name = value.to_s
            elsif key.to_s == 'POD_REPO_SOURCE' and not value.nil?
                pod_repo_source = value
            elsif key.to_s == 'POD_IS_STATIC_LIBRARY' and not value.nil?
                is_static_lib = value == 'true'
            end
        end
    end
    
    # puts "Push path is: #{push_path}, relate dir path is: #{relate_dir_path}"
    
    # 搜索podspec路径
    podspec_path = ''
    find_podspec_reg = relate_dir_path.length == 0 ? '' : (relate_dir_path + '/')
    if push_podspec_name.length > 0
        # 用户指定要推送某个podspec
        if push_podspec_name.include?('.podspec')
            find_podspec_reg += push_podspec_name
        else
            find_podspec_reg += (push_podspec_name + '.podspec')
        end
    else
        find_podspec_reg += '*.podspec'
    end
    #puts "Find podspec reg = #{find_podspec_reg}"
    # 有可能存在多个 podspec,当用户没有指定时,需要给用户自主选择
    find_podspec_count = 0
    podspecs = Array.new
    Dir::glob(find_podspec_reg) do |f|
        find_podspec_count += 1
        podspecs << f
    end
    
    if podspecs.count > 1
        inputTag = true
        serial = 0
        puts color_text("Find #{podspecs.count} podspec files, please enter the serial number selection:",Color.white)
        while inputTag
            for i in 0...podspecs.count do
                puts "#{i+1}. #{podspecs[i]}"
            end
            serial = gets.chomp
            inputTag = (serial.to_i > podspecs.count || serial.to_i <= 0)
            if inputTag
                puts "Input serial = #{serial}, it's invalid and you need to input 1~#{podspecs.count}:"
            end
        end
        podspec_path = podspecs[serial.to_i-1]
    elsif podspecs.count == 1
        podspec_path = podspecs[0]
    else
        puts color_text("Can't find any podspec file", Color.red)
        return
    end
    
    if not File::exist?(podspec_path)
        die_log("Can't find any podspec file in path: #{podspec_path}, please modify PodPushFile' PUSH_DIR_PATH(key)")
        return
    else
        puts "Ready to deal with podspec named " + color_text("#{podspec_path}", Color.white)
    end
    
    # 在当前podspec目录下新建一个临时 need_delete_temp.podspec 文件
    podspec_dir = File.dirname podspec_path
    podspec_absolute_path = cur_path + '/' + podspec_path
    temp_podspec_path = podspec_dir + '/need_delete_temp.podspec'
    temp_podspec_absolute_path = cur_path + '/' + temp_podspec_path
    
    cur_version = ''
    # 读取当前podspec文件的版本
    File.open(podspec_absolute_path, 'r+') do |f|
        f.each_line do |line|
            # 查找.version
            version_desc = /.*\.version[\s]*=.*/.match line
            if not version_desc.nil?
                cur_version = version_desc.to_s.split('=').last.to_s.gsub("'", '')
                cur_version = cur_version.gsub(' ', '')
                break
            end
        end
    end
    
    puts color_text("Current version = ", Color.white) + color_text("#{cur_version}", Color.green)
    
    # 允许自定义版本号
    if user_custom_version == true
        puts color_text "Please input pod lib's new version, if there is no input or less than or equal old version, it will be incremented:", Color.white
        input_version = gets.chomp
    
        # 判断输入的version是否>当前的版本号
        input_v_s = input_version.to_s.split('.')
        cur_v_s = cur_version.split('.')
        # 比较的位置,从最左边开始
        v_index = 0
        # 输入的version是否有效
        input_valid = false
        while v_index < cur_v_s.count && v_index < input_v_s.count do
            if input_v_s[v_index].to_i > cur_v_s[v_index].to_i
                # 说明用户输入的version比当前的大
                input_valid = true
                break
            elsif input_v_s[v_index].to_i == cur_v_s[v_index].to_i
                v_index += 1
            else
                break
            end
        end
    
        if input_valid == false
            puts color_text "Input invalid version = #{input_version},will auto +1 in last component", Color.natural
        end
    end
    
    if not File.exist? temp_podspec_absolute_path
        # system("cp -f #{podspec_path} #{temp_podspec_path}")
        system("touch #{temp_podspec_path}")
    end
    
    new_version = ''
    git_source = ''
    File.open(temp_podspec_absolute_path, 'r+') do |t|
        File.open(podspec_absolute_path) do |f|
            f.each_line do |line|
                # # 查找.version
                # s.version      = "0.0.2"
                # 需要注意的是,版本号可以是'',也可以是""
                write_line = line
                version_desc = /.*\.version[\s]*=.*/.match line
                if not version_desc.nil?
                    version_coms = version_desc.to_s.split('=')
                    if input_valid == true and user_custom_version == true
                        new_version = input_version.to_s
                    else
                        version_num = version_coms.last.to_s.gsub("'",'').gsub("\"",'').gsub(' ','')
                        v_s = version_num.split('.')
                        # 处理版本号 0.0.1
                        for i in 0...v_s.count do
                            if i == v_s.count - 1
                                new_version += (v_s[i].to_i + 1).to_s
                            else
                                new_version += (v_s[i].to_s + '.')
                            end
                        end
                    end
                    puts color_text("New version = ",Color.white) + color_text("#{new_version}", Color.green)
                    write_line = version_coms.first.to_s + '=' + " '#{new_version}'" + "\n"
                end
                source_desc = /.*\.source[\s]*=.*/.match line
                if not source_desc.nil?
                    source_desc = /:git.*,/.match source_desc.to_s
                    source_desc = /'.*'/.match source_desc.to_s
                    git_source = source_desc.to_s.gsub("'",'')
                    puts "git source is #{git_source}"
                end
                t.write write_line
            end
        end
    end
    
    puts color_text("Update version from ",Color.white) + color_text("#{cur_version}",Color.green) + color_text(" to ",Color.white) + color_text("#{new_version}", Color.green)
    
    # 将新数据反写回到原始podspec中
    system("cp -f #{temp_podspec_path} #{podspec_path}")
    system("rm -f #{temp_podspec_path}")
    
    
    # 如果本地没有这个repo,那么添加
    if system("pod repo | grep #{pod_repo_name}") == false
        puts color_text("Add pod repo named '#{pod_repo_name}' with source: #{pod_repo_source}", Color.white)
        system("pod repo add #{pod_repo_name} #{pod_repo_source}")
    end
    
    # 提交代码到远程仓库
    puts color_text('Start upload code to remote', Color.white)
    system("git commit -am 'update version to #{new_version}'")
    if system('git push origin') == false
        die_log('[!] git push code error')
    end
    system("git tag #{new_version}")
    if system('git push origin --tags') == false
        die_log('[!] git push tags error')
        return
    end
    
    # 验证podspec格式是否正确
    if verify_podspec_format == true
        puts color_text("Start verify podspec '#{podspec_path}'...", Color.white)
        if system("pod lib lint #{podspec_path} --allow-warnings") == false
            die_log("[!] pod spec' format invalid")
            return
        end
    end
    
    # 提交pod spec到spec仓库
    puts color_text("Start push pod '#{podspec_path}' to remote repo '#{pod_repo_name}'", Color.white)
    if pod_repo_name == 'trunk'
        if (is_static_lib == true ? system("pod trunk push #{podspec_path} --allow-warnings --use-libraries") : system("pod trunk push #{podspec_path} --allow-warnings")) == false
            puts "If not timeout, you need to check your 'trunk' account like: 'pod trunk me', and register code is 'pod trunk register <your email> <your name>'"
            return
        end
    else
        if (is_static_lib == true ? system("pod repo push #{pod_repo_name} #{podspec_path} --allow-warnings --use-libraries") : system("pod repo push #{pod_repo_name} #{podspec_path} --allow-warnings"))  == false
            return
        end
    end
    puts color_text("Update success ☕️! Current version = #{new_version}", Color.green)
    

    这里提一下 PodPushFile 文件,这个是配置文件,里面有这些配置项:

    #写入*.podspec所在的相对目录,不写默认会在脚本执行的目录下查找,如果脚本执行的目录和podspec文件不在同一目录下,那么需要配置下
    PUSH_DIR_PATH=
    
    
    #是否允许用户自定义版本号,不填或填true将允许用户设置自定义的版本号,而不是自增版本号 
    USER_CUSTOM_VERSION=true
    
    
    #默认开启验证,可以跳过验证阶段
    VERIFY_PODSPEC_FORMAT=true
    
    
    #pod repo的名字,如果是私有库就填私有库的名字
    POD_REPO_NAME=trunk
    
    
    #pod repo的源地址,如果是私有仓库,那么填写私有仓库的地址,注意是存放podspec的仓库的地址
    POD_REPO_SOURCE=https://github.com/CocoaPods/Specs
    
    
    #如果这个库是静态库,那么需要设置为true
    POD_IS_STATIC_LIBRARY=false
    

    如何使用

    使用起来比较简单,将脚本放在在当前的pod根目录下,执行 ruby specpush.rb 即可一键发布。第一次执行会先生成 PodPushFile 文件,配置完后需再次执行 ruby specpush.rb

    如果脚本有任何问题,请评论留言。rubyRepo 这是我的 ruby 仓库地址,里面会不时更新一些有用好玩的 ruby 脚本,喜欢的关注下。

    相关文章

      网友评论

        本文标题:pod库自动化部署脚本

        本文链接:https://www.haomeiwen.com/subject/fsteiltx.html