Transferred from:
Http://www.infoq.com/cn/news/2011/05/ci-dependency-management
Http://kb.cnblogs.com/page/101101/
In the previous article branch policy (continued), we discussed multi-component applications.ProgramContinuous integration policy, that is, to create your own exclusive for relatively independent componentsCodeLibrary, and then continuous integration between components through modern continuous integration tools. After the first release, Joe's team began to use this method. However, it wasn't long before they encountered a problem: it took too long to submit a build.
One day, Joe came to the office early. Because the previous day before he got off work, there was still a small point in the user story he developed. He wants to finish it in the morning and hand it over to the tester for testing. He modified a piece of code for a module. After the local build test is passed, he submitted the code and got up and went downstairs to buy breakfast. Fifteen minutes later, he returned to the computer and was frustrated that the build was still in its final phase, namely, integration testing for all modules and system-level testing. He had to get up again and rush for a cup of coffee. Then, while watching the progress bar on the screen, I drank coffee. Seven minutes later, the build was successfully completed. Although this was a successful build, I always felt uncomfortable. It took more than 20 minutes to complete the commit build. As a result, he began to carefully view the build script and build logs.
I. One generation and multiple reuse
After lunch at noon, he called Bob and Alice to discuss the problems he encountered in the morning.
"It is really annoying. It takes too long to build now ." Said Alice.
"I checked our build log this morning and found that one of the reasons for the long build time was that the code was updated and re-compiled before each test started ." Said Joe.
Bob proposed a solution and painted it on the whiteboard. "Can we establish a unified product library, and every time the product is built, it is placed in it with certain rules? In this way, if you need to use these binary products for subsequent tests, you can directly obtain them from the product library ." (1)
"It sounds good. However, do we need to put all the content produced in each build into the product library? This will eat our disk space very quickly ." Alice said with no worries.
"After the current build is complete, all the products will be placed on that build machine. We have also encountered the loss of all important historical information due to hardware problems or misoperations on machines. Therefore, we need to back up at least ." Bob replied, "In addition, we can solve the repeated compilation problem that Joe just proposed by putting the product of each build in the unified product library. Of course, we need to selectively place important building products into a unified product library, rather than all the content. By adding an upload task after each build, each team can upload the information they think is useful to the product library, such as building logs, test reports, and binary files after the build. But some temporary files are unnecessary. Of course, this can only alleviate the expansion speed of the product library. Although the number of continuous builds is very large, we do not need to keep all the building products, so we can regularly Delete building products that do not retain value, for example, you can mark important building products and delete others."
Alice and Joe both nodded and agreed. But Joe immediately frowned again. "Well, it seems that there are still some problems here ."
"What's the problem ?" Alice and Bob asked at the same time.
Joe said: "For some small game components on our platform, this is no problem. Because their construction products are not big, the network transmission bandwidth and speed are not problems. However, for those large binary files or test data, this may cause problems ." Everyone nodded and began to think about the problem.
Suddenly, Joe shouted, "Sorry, this is not a real problem. First, our test data changes less frequently. We didn't put it in the product library, but in a shared directory for version management. Therefore, the practice of this part in building is no different from that before. Second, for a large binary file, you only need to cache it on the build machine that requires it. During the next build, the build script can verify the local version. if the version is correct and has not been destroyed (such as MD5 verification), it can continue to be used. Otherwise, you can retrieve the correct file from the Unified product library and overwrite it ."
"There is another benefit and it is a very important benefit ." Alice added, "Our manual test versions can also be obtained from the Unified product library, this ensures that all the binary files for automated testing are the same as the binary files deployed in the manual testing environment, this will not cause inconsistency problems due to different environments during re-compilation. When we launch and deploy the product, we also get it from the Unified product library, so that the binary package from the beginning of compilation to the launch and deployment is consistent ."
As a result, Joe and the team transformed their continuous integration platform and all the construction to form a continuous integration and release management platform with an organization-level product library. They not only effectively shorten the time for each build, but also easily track the corresponding code of each online version in the code version control library through the product library, making issue tracing easier.
Ii. Dependency Management
A month later, according to market demand feedback, a game they developed has been upgraded, and the response speed is very fast and the effect is very good. However, the question is: the upgrade frequency of the game and platform is inconsistent, and what should we do with continuous integration. For Joe's team, it is a very big problem because their development process is heavily dependent on the continuous integration platform. So Joe and the core team members planned to discuss how to cope with the current situation.
In front of the whiteboard in the meeting room, Joe draws the current CI strategy (as shown in the previous figure ).
Bob said: "So far, we have released several times, and only one game application has been released recently. How can we manage our release process? I have worked in a company before, there are several versions of the product, including stable version, released or to be released, the latest version: used for internal testing within the company. Whenever a new version is to be released, a branch is pulled out for internal testing and serious defects are fixed. It can be published as a stable version only when there are no serious defects ."
Alice replied: "for a single Software Delivery product, development can usually be done by" pull branch by release ", just as the CI strategy we used at the beginning. However, our gaming platform is different from a single delivery product. We have our own server clusters. As long as the test coverage rate and test quality are good enough, and the test speed is fast enough, we can release the cluster on a large scale after small traffic test deployment. Now, the problem is that the release frequency of each game component is different, and components are dependent, which makes it difficult to decide which version should be used in the continuous integration process. In particular, we now have a public library which is used by multiple components ."
Joe said: "Let's sort out the dependencies on the entire platform first. In general, dependencies in software usually include compile-time dependencies, test-time dependencies, and runtime dependencies. The dependency form can be divided into library dependency and component dependency. Library dependencies refer to uncontrolled library files. For example, we use open-source or paid class library files or tools. These library files are updated slowly, no updates are required. Component dependency is dependent on those components developed by your own team or other teams in the company. Such dependency is characterized by relatively high update frequency, and sometimes even very frequent. For library file dependencies, we can create a directory named Lib in the code library, and create three subdirectories: build, test, and run, place the library files we depend on in the corresponding sub-directories. At the same time, it is best to include its version number in the file name of each library file, such as the nunit-2.6.0.11089.bin. In this way, it is easy to see which library files are depended on ."
Bob said: "Unfortunately, we didn't use the Java platform. Otherwise, we can use tools like Maven Or Ivy to manage these external library dependencies. In addition, the company can also use open-source tools such as artifactory or nexus to build an internal unified server to specifically manage the library dependencies used within the company ."
Alice said: "We can also build a simple dependency management system. For example, you can use a text file in the key-value format to describe the library file name, version number, and storage location, and then write a general script to read the information and download it to your local computer ."
Bob went on to ask: "dependency management for such library files is relatively easy. The important problems we face are component dependency management. Is there any good way ?"
Joe thought for a moment and said, "There are several methods, each with its own advantages and disadvantages. One way is to convert component dependencies into library dependencies. The applicable scenario is that the component has become stable after a period of development and maintenance and has not changed much. In this case, you can package this component together with other external dependent libraries and add the correct description so that all components dependent on it can get the correct version correctly. Another method is our current method. That is, each component is continuously built, and then integrated and built. The problem is how to manage the composite relationship between different versions of each component. The policy we always use is that the entire build is triggered no matter which commit. Currently, there are two things to do: one is to separate the public libraries and build them separately. Once the build is successful, other components dependent on the public libraries are automatically triggered and finally integrated and constructed. As long as we record the versions andSource codeThe revision is enough for tracking. Second, the continuous construction of the game platform triggers continuous integration of other game components. Therefore, the trigger relationship should be like this ." Joe picked up the pen and drew a new trigger diagram on the whiteboard (figure 2 ).
Bob shook his head and said, "This still cannot solve the problem we mentioned earlier, that is, our release frequency is inconsistent, and how to manage the relationship between these releases ."
"Oh, this is the problem ." Joe replied: "I think it is wrong to release a single game component. We deploy the game component directly to the production environment due to market pressure, even though the evaluation prior to release shows that the platform interface on which the game depends has not changed. There are two correct methods: (solution a) release the Platform as a whole because we have modified the platform. At that time, all continuous integration tests are based on the latest major version. (Solution B) Allow all game components to be developed based on the latest stable version of the game platform. Due to the slow development of new functions of the Platform, as long as the platform interface is not changed, all game applications can be quickly updated based on the stable release version of the platform. However, as long as a game needs to modify the platform interface, it must be continuously integrated with the latest platform code and released together ."
Alice frowned and said, "In this way, for the entire software, it is easier to manage component dependencies only when the backbone can be released at any time. This is because you can directly publish the trunk whenever you need it. If this is not the case, you just need to pull all the components out of a production branch at the same time, and then launch them in a unified manner ."
Bob said: "This is also a problem. Our deployment will be very troublesome and may take a long time ."
Joe said with a smile: "deployment is troublesome. We can solve this problem through automated operations in one system column. If the deployment takes a long time, we use cluster deployment, so we can use the batch replacement method for deployment. However, the benefit of this release method is that it can quickly respond to market demands ."
Joe picked up the cup and drank coffee. Then he said, "of course, this poses a challenge to our development work. We must use multiple methods to ensure that the trunk can be continuously released. For example, (1) Hide the new function until it is complete; (2) change all the changes into very small incremental changes, and each change is made publishable; (3) branch by branch action ). In addition, our automated testing also needs to maintain a high coverage rate and enrich other types of automated testing, such as performance testing and stress testing. In special circumstances, let's sit down and discuss countermeasures ."
Bob is still a bit hesitant. "This may increase our development costs. However, you can try it to see how it works ."
As a result, the entire team began to take action. What else will they encounter on this road? Let's have time to answer this question.