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.
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 one
feature
Branch, thus isolating the functionality that has already been completed, and only if the new functionality has been developedfeature
Branches are merged into the main development branch (as we often say)develop
Branch). In addition, if you are working on a feature and a new feature needs to be developed, you only need to commit the currentfeature
And then create another onefeature
Branch and complete new function development. And then cut backfeature
The branch can continue the development of the previous function. - Collaborative development: GitFlow also supports multi-person collaborative development, as each
feature
Any code that changes on a branch is just to make something newfeature
Can run independently. It’s also easy to see what everyone is doing. - Release phase: When a new
feature
When development is complete, it will be merged intodevelop
Branch, 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 fromdevelop
Branch Create a new branch that contains all the completed branchesfeature
。 - Support for emergency fixes: GitFlow also includes
hotfix
Branch. 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 commit
process
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-m
or-F
Option)template
(If specified-t
Choice, or in thegit config
Turn on thecommit.template
Option)merge
If this commit is a merge, or there are files.git/MERGE_MSG
)squash
(If there are files.git/SQUASH_MSG
)commit
And the third argument is a submitted SHA1 value (if specified)-c
.-C
or--amend
Option)
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 push
process
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.0 It 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 projectfeature
branch
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;
feature
Branch name.
The execution process is as follows:
- Main project creation
feature
Branch; - Go to the directory where the service module resides and create a service module
feature
Branch; - Set the service module reference mode in the main project Podfile to local path reference.
- 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 module
feature
Branch name [Optional].
The actions performed should include:
- If a business module is specified
feature
Branch name, you need to create a branch for the service module. Otherwise, go to Step 2. - Modify the reference mode of the service module in the main project Podfile to local path reference.
pod install
;- 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 module
feature
Branch name.
The actions performed should include:
- Submit the service module code according to the current service module local path.
- Modify the reference mode of the business module in the main project Podfile to remote Git reference.
pod install
;- 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 project
feature
Branch name [Optional].
The functions are as follows:
- If the main works are provided
feature
Branch name, the main engineering branch needs to be switched first; Otherwise skip this step; - Based on the current version number passed in from
develop
Branch set uprelease
Branch. If it already exists, skip this step. - Replace Podfile references to this module in the main project Git repository with references to remote Git repositories
release
Branch; - It is then executed in the main project
Pod Update [module name]
Update code; - Push main project code to a remote Git repository.
- Package the main project with Githooks and submit it for testing;
- 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.
- 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:
- First we modify the version number in the.podspec file to the current version number passed in and submit the push to
release
Branch. - Then according to GitFlow
release
Process, mergerelease
Branch todevelop
Branches andmaster
Branch, and then inmaster
The branch creates a tag for the corresponding version number and pushes it to a remote Git repository. - 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:
- Check that all branches of the main project that currently need to be released have been submitted for PR/MR and merged
develop
Branch. - 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.
- Based on the current version number passed in from
develop
Branch set uprelease
Branch. If it already exists, skip this step. - Push main project code to remote Git repository
- Package the main project with Githooks and submit it for testing;
- 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.
- Repeat step 1 after the bug fix.
Test completed release
- According to the GitFlow
release
Process, mergerelease
Branch todevelop
Branches andmaster
Branch, and then inmaster
The branch creates a tag corresponding to the version number and pushes it to a remote Git repository. - 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/…