When I attended 2017@Swift conference in April, I was lucky to hear @Zesming’s Topic about the componentization of Meituan, and I was particularly impressed by one picture.

From the @zesming bigwigs

When I was talking to @Zesming about how to organize components, he mentioned it to me. So I’m going to organize some ideas for iOS engineering automation around this diagram.

Basic knowledge of

First, we need to master some basic knowledge of automatic build publishing, including the following aspects.

GitFlow – Standardize git operation flow

GitFlow is a Git workflow standard developed by Vincent Driessen. Including the following key branches:

The name of the instructions
master The main branch
develop The main development branch, which contains the code identified for release
feature New function branch, generally a new function corresponds to a branch, the separation of functions needs to be more reasonable, in order to avoid some later unnecessary code conflicts
release Release branch, the branch used for release, and the bugs found during general testing are fixed in this branch
hotfix Hotfix branch, used for emergency bug fixes

The advantages of GitFlow are as follows:

  • Parallel development: GitFlow makes it easy to implement parallel development: each new feature creates a new onefeatureBranch, thus isolating the functionality that has already been completed, and only if the new functionality has been developedfeatureBranches are merged into the main development branch (as we often say)developBranch). In addition, if you are working on a feature and a new feature needs to be developed, you only need to commit the currentfeatureAnd then create another onefeatureBranch and complete new function development. And then cut backfeatureThe branch can continue the development of the previous function.
  • Collaborative development: GitFlow also supports multi-person collaborative development, as eachfeatureAny code that changes on a branch is just to make something newfeatureCan run independently. It’s also easy to see what everyone is doing.
  • Release phase: When a newfeatureWhen development is complete, it will be merged intodevelopBranch, this branch is mainly used to temporarily save content that has not been released, so that new development is neededfeature, we just need to start fromdevelopBranch Create a new branch that contains all the completed branchesfeature
  • Support for emergency fixes: GitFlow also includeshotfixBranch. This type of branch is created from a published tag and makes an emergency fix that only affects the published tag, not the new one you are developingfeature.

How does Glow Flow work

New features are developed on the feature branch

Feature branches are created from the Develop branch and then merged to the Develop branch for release.

When we need to publish, we create a Release branch from the Develop branch

The release branch is then released to the test environment for testing, and if problems are found, they are fixed directly in this branch. Until all issues are fixed, we will keep releasing -> testing -> fixing -> re-releasing -> re-testing the process.

After the release, the release branch is merged into the Develop and Master branches to ensure that no code is lost.

The Master branch only keeps track of code that has already been published, and a COMMIT merged on the master can only come from the Release and hotfix branches.

The hotfix branch is used to fix bugs on the fly.

They are created from a tag on the Master branch and then merged into the Develop and Master branches after the fix.

GitFlow tools

If you want to introduce GitFlow into a project, it is recommended to use SourceTree to do the client tool, which contains all GitFlow flow, visual operation, very convenient.

Gitignore – Kill the jamming code

Those of you who have read PR (Pull Request) or MR (Merge Request) should know why gitignore is mentioned. When a PR/MR comes along with a bunch of Pods library code, you feel desperate. So we’ll see how to ignore some non-module code.

Create.gitignore in the project repository

If you create a.gitignore file in your project repository, Git will use this file to determine which files and directories to ignore.

Note: If you want to ignore a file that is already being traced, the new rule will not affect the file. You need to set the file to untraced using the following command:

git rm --cached FILENAMECopy the code

Create global.gitignore

This is not very interesting, but let’s see how it works:

git config --global core.excludesfile ~/.gitignore_globalCopy the code

IOSer requires.gitignore

Github’s official template suggests the following:

# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings*.pbxuser ! default.pbxuser *.mode1v3 ! default.mode1v3 *.mode2v3 ! default.mode2v3 *.perspectivev3 ! default.perspectivev3 xcuserdata/## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/Copy the code

From this file, we can see that the content ignored mainly includes the following:

  • Directories and files generated by the build;
  • Various temporary configuration files;
  • Obj-c/Swift related specific files;
  • CocoaPods third-party Pods library directory
  • Carthage build directory;
  • Fastlane build files;
  • Directories and files generated by code injection tools.

We can adjust and customize these configurations according to our own projects.

Githooks – The ‘Man’ behind it

Note: This paragraph is mainly translated from githooks official documentation.

Githooks profile

Githooks are a set of user-written programs that Git fires when it performs certain operations, sort of like database triggers. By default, these programs are placed in the $GIT_DIR/hooks directory, which can also be changed using git’s environment configuration variable core.hookspath.

There are many types of Githooks, and these hooks automatically do what you want to do for specific operations, such as packaging code when submitting it to the Develop branch.

Currently supported Githooks

git commitprocess

pre-commit

This hook takes no arguments and is called before the commit message is received and the commit begins. If a non-zero value is returned, the git commit will fail.

When the default pre-commit hook is enabled, if it finds a blank line at the end of the file, the commit is aborted.

If the git commit command does not specify an editor to modify the commit message, any Git commit hook will be called with the environment variable GIT_EDITOR=:

prepare-commit-msg

This hook will be called after the git commit command is executed, after the default commit message is ready but before the editor starts.

It takes one to three arguments. The first is the name of the file that contains the commit message. The second is the source of the commit message, which can be:

  • message(If specified-mor-FOption)
  • template(If specified-tChoice, or in thegit configTurn on thecommit.templateOption)
  • mergeIf this commit is a merge, or there are files.git/MERGE_MSG)
  • squash(If there are files.git/SQUASH_MSG
  • commitAnd the third argument is a submitted SHA1 value (if specified)-c.-Cor--amendOption)

If the return value is not zero, the git commit command is aborted.

This hook is intended to be used to edit information files at work and is not skipped by the –no-verify option. A non-zero value means that the hook work failed and the commit is terminated. It should not be used as an alternative to the pre-commit hook.

commit-msg

Git commit is triggered by the –no-verify option, which can be skipped by using the –no-verify option. This parameter takes the name of the file to which the message was submitted, and aborts the git commit command if it returns a non-zero value.

This hook can be used to standardize submitted information, such as formatting it into a standard format customized for the project, or rejecting the submission if it is found to be out of format.

post-commit

Triggered by a Git commit and invoked after the commit, it does not affect the result of a Git commit.

git pushprocess

pre-push

Executed during a Git push run when a remote reference has been updated but the object has not yet been transferred. If a non-zero is returned, the push process is aborted. It takes the name and location of the remote branch as parameters and reads a list of references to be updated from standard input. You can use it to validate updates to references before pushing starts.

pre-receive

The hook that is invoked first when the server processes a push operation from the client. It takes a set of pushed references from standard input. If it exits with a non-zero value, all push content will not be accepted. You can use this hook to block non-fast-forward updates or to control access to all references and files that the push changes.

update

It is very similar to pre-receive hook, except that it runs once for each branch ready for update. Pre-receive only runs once if a pusher pushes content to multiple branches at the same time, whereas Update runs once for each branch that is pushed. Instead of reading from standard input, it takes three parameters: the branch name of the reference; The sha-1 value of the referenced content before pushing; And the sha-1 value of what the user is going to push. If the hook exits with a non-zero value, only the corresponding reference is rejected; The rest will still be updated.

post-receive

Run at the end of the process and can be used to update other system services or notify users. It accepts the same standard input data as pre-receive. Its uses include sending a message to a mailing list, notifying a continuous integration server, or updating a defect tracking system — or even analyzing submissions to determine whether a problem should be turned on, modified, or closed. The script cannot abort the push process, but the client will remain connected until it finishes running, so use it with caution if you want to do something else, as it will take you a long time.

post-update

Called by the remote repository’s Git-receive-pack when the local repository completes a Git push. This directive can only be used for notifications and cannot change the result of git-receive-pack.

push-to-checkout

Called by the remote repository’s Git-receive-pack when the local repository completes a Git push. If the hook returns a non-zero value, git push is interrupted.

applypatch-msg & pre-applypatch & post-applypatch

This command is triggered by git AM and is used when importing third-party patches. Because there is no such application scenario in the project, we will not do specific research.

pre-rebase

Triggered by git rebase, it can be used to prevent a branch from being rebase. This hook takes 1 or 2 arguments. The first parameter is the upstream branch, and the second parameter is the branch where rebase will be performed.

post-checkout

Execute after a successful git Checkout run. You can use it to adjust your working directory according to your project environment. This includes putting in large binaries, automatically generating documents, or doing something similar.

post-merge

Execute git Merge after it runs successfully. You can use it to recover workspace data that Git cannot track, such as permission data. This hook can also be used to verify the existence of files that are outside Git’s control, so that you can copy them in when your workspace changes.

post-rewrite

The hook is called by commands that replace commit records, such as git commit –amend and git rebase (but not git filter-branch). Its only parameter is the name of the command that triggered the override, and it accepts a series of override commit records from standard input. This hook is used in much the same way as post-checkout and post-merge.

sendemail-validate

This hook is triggered by git send-email, which takes a single parameter: the file name containing the E-mail recipient’s mailbox. Git send-email is aborted if the hook returns a non-zero value.

pre-auto-gc

Git gc — Auto occasionally calls Git GC –auto for garbage collection at runtime. This hook is called just before garbage collection begins and can be used to remind you that garbage collection is now underway, or to determine whether to interrupt the collection depending on the situation.

Cocoapods – Great Butler

As a third-party library dependency management tool, Cocoapods plays a central role in modular development. I’m not going to cover all the features of Cocoapods, but let’s take a look at some of the things that are relevant to the modular development process.

Customize the Cocoapods template

–template-url= url, which specifies the template to create the library. The official template address is github.com/CocoaPods/p… The project directory structure generated from this template is as follows:

MyLib ├ ─ ─. Travis. Yml ├ ─ ─ _Pods. Xcproject ├ ─ ─ Example │ ├ ─ ─ MyLib │ ├ ─ ─ MyLib. Xcodeproj │ ├ ─ ─ MyLib. Xcworkspace │ ├ ─ ─ │ ├─ Podfile │ ├─ Exercises │ ├─ exercises │ ├─ Podfile │ ├─ Exercises │ ├─ exercises │ ├─ Podfile │ ├─ Exercises │ ├─ exercises │ ├─ exercises │ ├─ RemoveMe. Swift/m └ ─ ─ the README, mdCopy the code

That is, we can fork the official template address and then customize our own templates (including the main project and business modules) to add custom functionality. Such as:

  • Add all base library dependencies.
  • Add the private Cocoapods repository.
  • Add the.gitignore file.
  • Add a custom script.

Private Cocoapods repository

Like the main project, modules need to be versioned, and the best way to do this is through Cocoapods. Git repository: Cocoapods repository: Git repository: Cocoapods repository

pod repo add [Spec name] [Git url]Copy the code

When publishing a library, use the following command to publish it to a private repository:

pod repo push [Spec name] [Lib name].podspecCopy the code

Cocoapods references third-party libraries in several ways

As anyone who has used Cocoapods knows, there are three ways to reference Cocoapods:

way example instructions
Version reference Pod ‘Alamofire’, ‘~ > 3.0’ This method refers to the released version, which contains> ` ` > = ` ` < ` ` < = ` ` ~ >Several version limit symbols, among which~ >The symbol represents updating only the latest minor version number, for example~ > 1.0.0It will only update to the latest version of 1.0.x and will not update to versions higher than 1.x.0
Local path reference pod ‘Alamofire’, :path => ‘~/Documents/Alamofire’ This approach refers directly to the native code, and changes to the reference library are still committed to git in the reference library, not to the main project.
Remote Git path references pod ‘Alamofire’, :git => ‘Github.com/Alamofire/A…’ This approach directly references remote Git code, does not require the referenced library to publish, and is supported:branch =>,:tag =>:commit =>Three options

Process analysis

Here are my thoughts on the various processes in the flowchart shared by @Zesming.

Business side requirements to development

According to the specification of GitFlow, the new requirement follows the process of feature. Here we should be able to develop some scripts to aid development.

One-click new service module and main projectfeaturebranch

At the full stage of componentization, the main project should have no code, just a shell. However, in the development stage, the main project will also contain some business codes, so when we develop a feature, there are often some specific business codes in the module, and the main project also has some call codes. At this time, the same feature branch needs to be created in both the main project and the business module. So we added a script to the main project. The parameters to this script include:

  • Business module name;
  • Local path of the service module;
  • featureBranch name.

The execution process is as follows:

  1. Main project creationfeatureBranch;
  2. Go to the directory where the service module resides and create a service modulefeatureBranch;
  3. Set the service module reference mode in the main project Podfile to local path reference.
  4. Open the main project and business module Example project.

Switch the state of main project development and debugging with one key

A key switch state of the main engineering development need to rely on the main project is to point to some business module to debug (PS: of course, is the ideal state is a business module can run independently, but in general, the ideal is very good, the reality is cruel 😂), need to amend the way this module references in the Podfile reference way for the local path. In this way, the modification of the main project code and the modification of the business module code will be submitted to their respective Git repository, thus realizing the code submission while debugging and development.

This feature is considered to be implemented as a script, placed in the common scripts directory of the main project. The parameters should contain:

  • Business module name;
  • Local path of the service module;
  • Business modulefeatureBranch name [Optional].

The actions performed should include:

  1. If a business module is specifiedfeatureBranch name, you need to create a branch for the service module. Otherwise, go to Step 2.
  2. Modify the reference mode of the service module in the main project Podfile to local path reference.
  3. pod install;
  4. Close the main project and reopen it.

Switch master project submission status by one click

After the business module development and debugging is completed, the main project needs to be restored to the normal state and submitted.

This function is still implemented as a script, placed in the main project’s common scripts directory. The parameters should contain:

  • Business module name;
  • Remote Git address of the business module;
  • Business modulefeatureBranch name.

The actions performed should include:

  1. Submit the service module code according to the current service module local path.
  2. Modify the reference mode of the business module in the main project Podfile to remote Git reference.
  3. pod install;
  4. Close the main project and reopen it.

Commit code to the business code repository

This process we mainly use Githooks to do some automatic detection. Include:

  • OCLint
  • Unit testing

Publish components to Intranet Pods

Preparation stage

The main thing this phase does is check to see if all branches that need to be published currently have committed a PR/MR and merged into the Develop branch.

Note: There are some rules for naming branches. For example, if you need to include the current version number in the branch name, all branches that need to be published can be matched with this version number.

Release -> Test -> Bug fix -> Re-release phase

Here you need to combine GitFlow with Cocoapods’ publishing process.

Considering that we generally need to rely on the main project for testing, we need to add a local script to the main project to assist the release, including the following parameters:

  • Current version number;
  • Local path of the service module;
  • The main projectfeatureBranch name [Optional].

The functions are as follows:

  1. If the main works are providedfeatureBranch name, the main engineering branch needs to be switched first; Otherwise skip this step;
  2. Based on the current version number passed in fromdevelopBranch set upreleaseBranch. If it already exists, skip this step.
  3. Replace Podfile references to this module in the main project Git repository with references to remote Git repositoriesreleaseBranch;
  4. It is then executed in the main projectPod Update [module name]Update code;
  5. Push main project code to a remote Git repository.
  6. Package the main project with Githooks and submit it for testing;
  7. If there is a problem in the test process, the script can directly switch the development state and fix the bug by switching the debugging state of the main project with one key.
  8. Repeat step 1 after the bug fix.

The test completes the release to Intranet Pods phase

Once the test is complete, you can publish the business module to the internal Pods. Here we prepare a script in the business module project with the following parameters:

  • Current version number.
  • Local path of the main project.

The functions are as follows:

  1. First we modify the version number in the.podspec file to the current version number passed in and submit the push toreleaseBranch.
  2. Then according to GitFlowreleaseProcess, mergereleaseBranch todevelopBranches andmasterBranch, and then inmasterThe branch creates a tag for the corresponding version number and pushes it to a remote Git repository.
  3. After publishing the business module, switch to the main project path and change the reference mode of the module in the main project Podfile to version number reference.

Integrate components into the main project

This is because the reference mode of the main project to each business module has been changed to version number reference when the components were published. So at this stage we only need to verify whether the current main project references the latest release version of each business module. The parameters are as follows:

  • List of all business modules that the main project depends on

The completed functions are as follows:

Checks whether the latest versions of all business modules in the main project dependency module are the same as those specified in the Podfile. If they are not, an error is reported.

Release the main project

Preparation stage

There are two things to do at this stage:

  1. Check that all branches of the main project that currently need to be released have been submitted for PR/MR and mergeddevelopBranch.
  2. Check whether all service modules currently referenced by the main project are version number references.

Release -> Test -> Bug fix -> Re-release phase

This phase can be understood as the integration testing phase. Also a script in the main project. The parameters are as follows:

  • Current version number.
  1. Based on the current version number passed in fromdevelopBranch set upreleaseBranch. If it already exists, skip this step.
  2. Push main project code to remote Git repository
  3. Package the main project with Githooks and submit it for testing;
  4. If there is a problem in the test process, the script can directly switch the development state and fix the bug by switching the debugging state of the main project with one key.
  5. Repeat step 1 after the bug fix.

Test completed release

  1. According to the GitFlowreleaseProcess, mergereleaseBranch todevelopBranches andmasterBranch, and then inmasterThe branch creates a tag corresponding to the version number and pushes it to a remote Git repository.
  2. Package the main project and release it via Githooks.

conclusion

This paper is just an immature thinking, and some details may be improved in the follow-up implementation process. You are welcome to correct the unreasonable places in my thinking, and I will be very grateful.

The next article should document some specific practices from business requirements to development. Stay tuned!

The resources

Introducing GitFlow: datasift. Making. IO/GitFlow/Int…

Using Git/Ignoring files: help.github.com/articles/ig…

Githooks:git-scm.com/docs/githoo…

Custom Git – Git hooks: git-scm.com/book/zh/v2/…