preface

When we do development, we come into contact with the idea of componentization. Componentization of iOS is generally done in the form of pod libraries made using Cocoapods. Pod libraries are divided into public libraries and private libraries. What we upload to Github, for example, is a public library for others to download and use. The ones that are deployed inside the company are private libraries that are inaccessible to others.

concept

This article will focus on how to automate the deployment of POD scripts, so the concepts related to the POD library will only be briefly introduced and will not be covered too much. For more insight, consult the official documentation.

1. repo

A REPo is the equivalent of a repository that holds an index of related POD libraries. How do you understand that? For example, what’s the repo of the public library we’re making on Github? Call trunk, the address is https://cdn.cocoapods.org/. We can run the command: pod repo directly on the machine and see the diagram.

This repository is where the podspec files we deployed are stored.

2. podspec

A PodSpec is used to describe a POD library, such as library structure, version, source code address, etc.


With these two in mind, we’re ready to make our own POD library.

Make pod library

Take the example of making a public library and submitting it to Github. To submit the official document to the Trunk click me

First you need an existing source code repository,

1. Create a Podspec file

pod spec create yourSpecName.podspec
Copy the code

2. Edit the Podspec file

For example, the contents of the Podspec file are as follows. Edit the source and source_files in the file.

Pod: : Spec. New do | Spec | Spec. The name = 'Reachability' Spec. Version = '3.1.0' Spec. The license = {: type = > 'BSD'} spec.homepage = 'https://github.com/tonymillion/Reachability' spec.authors = { 'Tony Million' => '[email protected]'  } 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 ',m' spec.framework = 'SystemConfiguration' spec.requires_arc = true endCopy the code

3. Submit our source code

git commit -am 'submit'
git push origin
Copy the code

4. Tag our POD

Note that podSpec has the concept of a version number, which needs to be consistent with the tag value of your Pod library, the source code library. This way, our Cocoapods can download the correct version of the source code after parsing the PodSpec.

Git tag 0.0.2 # push all tags to remote git push originCopy the code

5. Verify that our PodSpec file is formatted correctly

Local validation:

pod lib lint yourSpecName.podspec --allow-warnings
Copy the code

Remote verification:

pod spec line yourSpecName.podspec --allow-warnings
Copy the code

6. Publish our PodSpec

pod trunk push yourSpecName.podspec --allow-warnings
Copy the code

Update the pod library

We should have a podspec file in our library at this point. All we need to do is update the version number of the file, change the source_files if there is a change in the file structure, and change the dependency.

1. Update the version number of the PodSpec

Open the PodSpec file, edit and update the Version field, usually incrementing.

1. Tag our new version of the code

Git commit -am 'update' git push origin git tag 0.0.2 git push origin 0.0.2Copy the code

2. Verify that the newly modified Podspec file is correct

This step can be skipped by the knight here, usually the first upload is ok, and will not cause problems later.

pod lib lint *.podspec
Copy the code

3. Deploy a new version of PodSpec

pod trunk push *.podspec
Copy the code

Script implementation

The script is implemented in Ruby, and the source code is here.

The implementation is basically the same as updating PodSpec, but with some configuration.

It’s easy to use, put the script in the current pod root directory and execute Ruby specpush.rb for one-click publishing.

Here’s the code:

#! /usr/bin/ruby

class Color
    def self.natural
        0
    end
    def self.black
        30
    end
    def self.red
        31
    end
    def self.green
        32
    end
    def self.yellow
        33
    end
    def self.blue
        34
    end
    def self.magenta
        35
    end
    def self.cyan
        36
    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

# Pull the latest code
# 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 = ' '
user_custom_version = true
verify_podspec_format = true
pod_repo_name = 'trunk'
pod_repo_source =
is_static_lib = false

SpecPushFile: if SpecPushFile does not exist, create it
if notFile::exist? (cur_path +'/PodPushFile')
    system('touch PodPushFile')
    File.open(cur_path + '/PodPushFile'.'w+') do |f|
        f.write(PUSH_DIR_PATH= # specifies whether to allow users to set their own version number. If this is not true or not, it will allow users to set their own version number. VERIFY_PODSPEC_FORMAT=true #pod repo name If it is the private library will fill in the name of the private libraries POD_REPO_NAME = trunk source address POD_REPO_SOURCE=https://github.com/CocoaPods/Specs # # pod repo if the library is static library, So you need to set it to 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 == '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}"

# Search the PodSpec path
podspec_path = ' '
find_podspec_reg = (relate_dir_path.length == 0 ? ' ' : (relate_dir_path + '/')) + '*.podspec'
# puts "Find podspec reg = #{find_podspec_reg}"
Dir::glob(find_podspec_reg) do |f|
    podspec_path = f
end

if podspec_path.length == 0
    puts "Find podspec in current dir"
else
    puts "Find podspec in releate path=#{podspec_path}"
end

if notFile::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 "Find podspec named" + color_text("#{podspec_path}", Color.white)
end

Create a temporary need_delete_temp. Podspec file in the current podspec directory
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 = ' '
Read the current version of the PodSpec file
File.open(podspec_absolute_path, 'r+') do |f|
    f.each_line do |line|
        # lookup. 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)

# Allow custom version numbers
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

    # Check whether the input version is > the current version number
    input_v_s = input_version.to_s.split('. ')
    cur_v_s = cur_version.split('. ')
    The position of comparison, starting from the far left
    v_index = 0
    Whether the version entered is valid
    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
            # Indicates that the version entered by the user is larger than the current 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|
            # # Search for.version
            # s.v ersion = "hundreds"
            Note that the version number can be either '' or ''
            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('. ')
                    # Processing version number 0.0.1
                    for i in 0. v_s.countdo
                        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)

# Write the new data back to the original Podspec
system("cp -f #{temp_podspec_path} #{podspec_path}")
system("rm -f #{temp_podspec_path}")


If the repo is not locally available, add it
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

# Commit code to remote repository
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

Verify that the podSpec format is correct
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

# Submit a POD Spec to the Spec repository
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("The Update success ☕ ️! Current version =#{new_version}", Color.green)
Copy the code

Here’s the PodPushFile, this is the configuration file, and it has these configuration items:

If the script is not executed in the same directory as the podspec file, then you need to configure this directory
PUSH_DIR_PATH=


Whether to allow the user to customize the version number, not true will allow the user to set the custom version number, rather than increment the version number
USER_CUSTOM_VERSION=true


Authentication is enabled by default. You can skip the authentication phase
VERIFY_PODSPEC_FORMAT=true


The name of the #pod repo, or the private library name if it is a private library
POD_REPO_NAME=trunk


The source address of # Pod Repo. If it is a private repository, fill in the address of the private repository. Note that this is the address of the repository where the Podspec is stored
POD_REPO_SOURCE=https://github.com/CocoaPods/Specs


If the library is static, set this to true
POD_IS_STATIC_LIBRARY=false
Copy the code

If you have any questions about the script, please leave a comment. RubyRepo this is my Ruby repository address, which will be updated from time to time some useful and fun Ruby scripts, like to pay attention to.