├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── Rakefile ├── big_keeper.gemspec ├── bin ├── big └── setup ├── docs ├── en-US │ ├── FEATURE.md │ └── README.md └── zh-CN │ ├── BIGKEEPER_FILE.md │ ├── FEATURE&HOTFIX.md │ ├── IMAGE.md │ ├── PODFILE.md │ ├── README.md │ ├── RECOMMEND.md │ ├── RELEASE.md │ └── SPEC.md ├── lib ├── big_keeper.rb └── big_keeper │ ├── command │ ├── client.rb │ ├── feature&hotfix.rb │ ├── feature&hotfix │ │ ├── delete.rb │ │ ├── finish.rb │ │ ├── list.rb │ │ ├── publish.rb │ │ ├── pull.rb │ │ ├── push.rb │ │ ├── rebase.rb │ │ ├── start.rb │ │ ├── switch.rb │ │ └── update.rb │ ├── image.rb │ ├── image │ │ └── image.rb │ ├── init.rb │ ├── pod.rb │ ├── pod │ │ └── podfile.rb │ ├── release.rb │ ├── release │ │ ├── finish.rb │ │ ├── home.rb │ │ ├── module.rb │ │ ├── publish.rb │ │ └── start.rb │ ├── spec.rb │ └── spec │ │ ├── add.rb │ │ ├── analyze.rb │ │ ├── delete.rb │ │ ├── list.rb │ │ ├── search.rb │ │ └── sync.rb │ ├── dependency │ ├── dep_gradle_operator.rb │ ├── dep_operator.rb │ ├── dep_pod_operator.rb │ ├── dep_service.rb │ └── dep_type.rb │ ├── model │ ├── gitflow_type.rb │ ├── library_model.rb │ ├── operate_type.rb │ └── podfile_model.rb │ ├── service │ ├── git_service.rb │ ├── module_service.rb │ └── stash_service.rb │ ├── util │ ├── bigkeeper_parser.rb │ ├── cache_operator.rb │ ├── code_operator.rb │ ├── command_line_util.rb │ ├── file_operator.rb │ ├── git_operator.rb │ ├── gitflow_operator.rb │ ├── gradle_content_generator.rb │ ├── gradle_file_operator.rb │ ├── gradle_module_operator.rb │ ├── info_plist_operator.rb │ ├── leancloud_logger.rb │ ├── list_generator.rb │ ├── lockfile_parser.rb │ ├── logger.rb │ ├── pod_operator.rb │ ├── podfile_detector.rb │ ├── podfile_module.rb │ ├── podfile_operator.rb │ ├── verify_operator.rb │ ├── version_config_operator.rb │ └── xcode_operator.rb │ └── version.rb ├── resources ├── banner.png ├── command.png ├── keynote │ ├── big-keeper-readme-analyze.key │ ├── big-keeper-readme-example.key │ ├── big-keeper-readme-feature.key │ └── big-keeper-readme-release.key ├── readme │ └── big-keeper-readme.001.png └── template │ └── Bigkeeper └── spec └── big_stash_help.rb /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Check List 2 | 3 | Thanks for considering to open an issue. Before you submit your issue, please confirm these boxes are checked. 4 | 5 | - [ ] I have read the [README.md](https://github.com/BigKeeper/bigkeeper/blob/master/README.md), but there is no information I need. 6 | - [ ] I have searched in [existing issues](https://github.com/BigKeeper/bigkeeper/issues?utf8=%E2%9C%93&q=is%3Aissue), but did find a same one. 7 | 8 | ### Issue Description 9 | 10 | #### Description 11 | 12 | [Tell us about the issue] 13 | 14 | #### Reproduce 15 | 16 | [The steps to reproduce this issue. What are the parameters, where did you put your code, etc.] 17 | 18 | #### Other Comment 19 | 20 | [Add anything else here] 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | # .env 15 | 16 | ## Specific to RubyMotion: 17 | .dat* 18 | .repl_history 19 | build/ 20 | *.bridgesupport 21 | build-iPhoneOS/ 22 | build-iPhoneSimulator/ 23 | 24 | ## Specific to RubyMotion (use of CocoaPods): 25 | # 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 29 | # 30 | # vendor/Pods/ 31 | 32 | ## Documentation cache and generated files: 33 | /.yardoc/ 34 | /_yardoc/ 35 | /doc/ 36 | /rdoc/ 37 | 38 | ## Environment normalization: 39 | /.bundle/ 40 | /vendor/bundle 41 | /lib/bundler/man/ 42 | 43 | # for a library or gem, you might want to ignore these files since the code is 44 | # intended to run in multiple environments; otherwise, check them in: 45 | # Gemfile.lock 46 | # .ruby-version 47 | # .ruby-gemset 48 | 49 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 50 | .rvmrc 51 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - 2.5.0 5 | - 2.4.3 6 | - 2.3.6 7 | - 2.2.9 8 | - 2.1.10 9 | 10 | before_install: 11 | - gem update --remote bundler 12 | - gem update --system 13 | 14 | sudo: false 15 | 16 | bundler_args: --without=guard 17 | 18 | notifications: 19 | disabled: true 20 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mmoaay@sina.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | This document contains information and guidelines about contributing to this project. 4 | Please read it before you start participating. 5 | 6 | **Topics** 7 | 8 | * [Asking Questions](#asking-questions) 9 | * [Reporting Issues](#reporting-issues) 10 | * [Developers Certificate of Origin](#developers-certificate-of-origin) 11 | 12 | ## Asking Questions 13 | 14 | We don't use GitHub as a support forum. 15 | For any usage questions that are not specific to the project itself, 16 | please ask on [Stack Overflow](https://stackoverflow.com) instead. 17 | By doing so, you'll be more likely to quickly solve your problem, 18 | and you'll allow anyone else with the same question to find the answer. 19 | This also allows maintainers to focus on improving the project for others. 20 | 21 | ## Reporting Issues 22 | 23 | A great way to contribute to the project 24 | is to send a detailed issue when you encounter an problem. 25 | We always appreciate a well-written, thorough bug report. 26 | 27 | Check that the project issues database 28 | doesn't already include that problem or suggestion before submitting an issue. 29 | If you find a match, add a quick "+1" or "I have this problem too." 30 | Doing this helps prioritize the most common problems and requests. 31 | 32 | When reporting issues, please include the following: 33 | 34 | * The version of Xcode you're using 35 | * The version of iOS or macOS you're targeting 36 | * The full output of any stack trace or compiler error 37 | * A code snippet that reproduces the described behavior, if applicable 38 | * Any other details that would be useful in understanding the problem 39 | 40 | This information will help us review and fix your issue faster. 41 | 42 | ## Developer's Certificate of Origin 43 | 44 | By making a contribution to this project, I certify that: 45 | 46 | - (a) The contribution was created in whole or in part by me and I 47 | have the right to submit it under the open source license 48 | indicated in the file; or 49 | 50 | - (b) The contribution is based upon previous work that, to the best 51 | of my knowledge, is covered under an appropriate open source 52 | license and I have the right under that license to submit that 53 | work with modifications, whether created in whole or in part 54 | by me, under the same open source license (unless I am 55 | permitted to submit under a different license), as indicated 56 | in the file; or 57 | 58 | - (c) The contribution was provided directly to me by some other 59 | person who certified (a), (b) or (c) and I have not modified 60 | it. 61 | 62 | - (d) I understand and agree that this project and the contribution 63 | are public and that a record of the contribution (including all 64 | personal information I submit with it, including my sign-off) is 65 | maintained indefinitely and may be redistributed consistent with 66 | this project or the open source license(s) involved. 67 | 68 | --- 69 | 70 | *Some of the ideas and wording for the statements above were based on work by the [Alamofire](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md) communities. We commend them for their efforts to facilitate collaboration in their projects.* 71 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://gems.ruby-china.com/" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in big_keeper.gemspec 6 | gemspec 7 | 8 | group :test do 9 | gem 'rake' 10 | end 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | bigkeeper (0.9.11) 5 | big_resources 6 | big_stash (~> 0.1) 7 | cocoapods 8 | colorize 9 | gli (~> 2.16) 10 | plist 11 | 12 | GEM 13 | remote: https://gems.ruby-china.com/ 14 | specs: 15 | CFPropertyList (3.0.0) 16 | activesupport (4.2.11.1) 17 | i18n (~> 0.7) 18 | minitest (~> 5.1) 19 | thread_safe (~> 0.3, >= 0.3.4) 20 | tzinfo (~> 1.1) 21 | ast (2.4.0) 22 | atomos (0.1.3) 23 | big_resources (0.1.2) 24 | chunky_png 25 | cocoapods 26 | colorize 27 | gli (~> 2.16) 28 | plist 29 | big_stash (0.6.0) 30 | gli (~> 2.16) 31 | chunky_png (1.3.11) 32 | claide (1.0.2) 33 | cocoapods (1.5.0) 34 | activesupport (>= 4.0.2, < 5) 35 | claide (>= 1.0.2, < 2.0) 36 | cocoapods-core (= 1.5.0) 37 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 38 | cocoapods-downloader (>= 1.2.0, < 2.0) 39 | cocoapods-plugins (>= 1.0.0, < 2.0) 40 | cocoapods-search (>= 1.0.0, < 2.0) 41 | cocoapods-stats (>= 1.0.0, < 2.0) 42 | cocoapods-trunk (>= 1.3.0, < 2.0) 43 | cocoapods-try (>= 1.1.0, < 2.0) 44 | colored2 (~> 3.1) 45 | escape (~> 0.0.4) 46 | fourflusher (~> 2.0.1) 47 | gh_inspector (~> 1.0) 48 | molinillo (~> 0.6.5) 49 | nap (~> 1.0) 50 | ruby-macho (~> 1.1) 51 | xcodeproj (>= 1.5.7, < 2.0) 52 | cocoapods-core (1.5.0) 53 | activesupport (>= 4.0.2, < 6) 54 | fuzzy_match (~> 2.0.4) 55 | nap (~> 1.0) 56 | cocoapods-deintegrate (1.0.4) 57 | cocoapods-downloader (1.2.2) 58 | cocoapods-plugins (1.0.0) 59 | nap 60 | cocoapods-search (1.0.0) 61 | cocoapods-stats (1.1.0) 62 | cocoapods-trunk (1.3.1) 63 | nap (>= 0.8, < 2.0) 64 | netrc (~> 0.11) 65 | cocoapods-try (1.1.0) 66 | colored2 (3.1.2) 67 | colorize (0.8.1) 68 | concurrent-ruby (1.1.5) 69 | escape (0.0.4) 70 | fourflusher (2.0.1) 71 | fuzzy_match (2.0.4) 72 | gh_inspector (1.1.3) 73 | gli (2.18.0) 74 | i18n (0.9.5) 75 | concurrent-ruby (~> 1.0) 76 | minitest (5.11.3) 77 | molinillo (0.6.6) 78 | nanaimo (0.2.6) 79 | nap (1.1.0) 80 | netrc (0.11.0) 81 | parallel (1.12.1) 82 | parser (2.5.1.2) 83 | ast (~> 2.4.0) 84 | plist (3.5.0) 85 | powerpack (0.1.2) 86 | rainbow (2.2.2) 87 | rake 88 | rake (10.5.0) 89 | rubocop (0.50.0) 90 | parallel (~> 1.10) 91 | parser (>= 2.3.3.1, < 3.0) 92 | powerpack (~> 0.1) 93 | rainbow (>= 2.2.2, < 3.0) 94 | ruby-progressbar (~> 1.7) 95 | unicode-display_width (~> 1.0, >= 1.0.1) 96 | ruby-macho (1.4.0) 97 | ruby-progressbar (1.9.0) 98 | thread_safe (0.3.6) 99 | tzinfo (1.2.5) 100 | thread_safe (~> 0.1) 101 | unicode-display_width (1.4.0) 102 | xcodeproj (1.8.2) 103 | CFPropertyList (>= 2.3.3, < 4.0) 104 | atomos (~> 0.1.3) 105 | claide (>= 1.0.2, < 2.0) 106 | colored2 (~> 3.1) 107 | nanaimo (~> 0.2.6) 108 | 109 | PLATFORMS 110 | ruby 111 | 112 | DEPENDENCIES 113 | big_resources (~> 0.1.2) 114 | big_stash (~> 0.1) 115 | bigkeeper! 116 | bundler (~> 1.15) 117 | cocoapods 118 | colorize 119 | gli (~> 2.16) 120 | plist 121 | rake 122 | rubocop (~> 0.50.0) 123 | 124 | BUNDLED WITH 125 | 1.17.2 126 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 BigKeeper. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [English](./docs/en-US/README.md) 2 | 3 | --- 4 | 5 | # bigkeeper 6 | 7 | ![](./resources/banner.png) 8 | 9 | [![Build Status](https://travis-ci.org/BigKeeper/bigkeeper.svg?branch=master)](https://travis-ci.org/BigKeeper/bigkeeper) 10 | [![Maintainability](https://api.codeclimate.com/v1/badges/c6dc4161e84fcec9a890/maintainability)](https://codeclimate.com/github/BigKeeper/big-keeper/maintainability) 11 | [![Gem Version](https://badge.fury.io/rb/bigkeeper.svg)](https://rubygems.org/gems/bigkeeper) 12 | [![Language: Ruby](https://img.shields.io/badge/language-Ruby-da212f.svg)](https://www.ruby-lang.org/) 13 | [![License](https://img.shields.io/badge/license-MIT-000000.svg)](https://github.com/BigKeeper/big-keeper/blob/master/LICENSE) 14 | [![Gitter](https://img.shields.io/gitter/room/BigKeeper/BigKeeper.svg)](https://gitter.im/Big-Keeper/Lobby) 15 | 16 | bigkeeper 是一个 **iOS&Android 模块化项目**效率提升工具,旨在帮助有模块化需求的团队更轻松的渡过“半模块化”尴尬期。 17 | 18 | 使用这个工具后,开发者**在主项目内**即可完成**包括其他业务模块在内**所有代码的编写和管理。 19 | 20 | 我们借鉴 CocoaPods 的做法,开发团队只需要在主项目中加入一个 Bigkeeper 文件,然后利用我们提供的 `big` 命令,即可更便捷的实现**代码分支**和**业务模块**的管理;另外,通过 `big` 命令提供的流程,也能帮助开发者养成更规范的日常开发习惯,减轻项目管理者的负担。 21 | 22 | ## 应用场景 23 | 24 | 在模块化项目的过渡阶段,我们往往会碰到业务拆分不是那么彻底的情况(PS:比如主项目中仍然存在业务相关代码),这样就会导致一系列问题: 25 | 26 | - 开发流程方面:开发者在开发一个 feature 时需要维护多个项目的分支,同时还要频繁修改主项目的 `Podfile`; 27 | - 项目管理者需要花很大精力去和团队成员同步项目管理规范流程,尤其对于新人,挑战很大; 28 | - 开发过程中如果碰到突发情况需要开新分支处理问题时对当前工作区的保存操作会很繁琐(PS:因为涉及到多个项目); 29 | - 日常开发过程中类似 commit、pull、push 等操作比较繁琐(PS:因为涉及到多个项目); 30 | - 资源(比如图片)散落在各个业务模块,可能存在重名、重复等问题,难以管理; 31 | - 业务模块可能存在横向依赖的问题,导致业务模块间耦合严重,不能单独编译运行。 32 | - 各模块依赖的版本没有统一的同步更新方式,导致各个模块的开发环境不一致,问题百出。 33 | 34 | bigkeeper 的出现就是为了解决这些问题。 35 | 36 | > 注:也有很多人认为直接做一个完整的组件化项目更合理,而不需要采用这样曲折的方案,但是很多时候我们并没有这么多的精力直接去做这些事情,bigkeeper 存在的价值也是为了帮助开发团队在** iOS&Android 模块化项目过渡阶段**能更轻松的往前走。 37 | 38 | ## 安装 39 | 40 | - 当作 Ruby Gem 使用: 41 | 在项目的 Gemfile 中加入下面这行: 42 | 43 | ```ruby 44 | gem 'bigkeeper' 45 | ``` 46 | 47 | 然后执行: 48 | 49 | $ bundle 50 | 51 | - 直接安装: 52 | 53 | $ gem install bigkeeper 54 | 55 | ## 使用方法 56 | 57 | - [Bigkeeper 文件配置](./docs/zh-CN/BIGKEEPER_FILE.md) 58 | - `bigkeeper` 工具使用: 59 | 60 | 直接在命令行执行 `bigkeeper` 可以查看其提供的所有功能: 61 | 62 | ``` 63 | NAME 64 | big - Efficiency improvement for iOS&Android module development, iOSer&Android using this tool can make module development easier. 65 | 66 | SYNOPSIS 67 | big [global options] command [command options] [arguments...] 68 | 69 | 70 | GLOBAL OPTIONS 71 | --help - Show this message 72 | -p, --path=arg - (default: ./) 73 | -u, --user=arg - (default: mmoaay) 74 | -v, --ver=arg - (default: Version in Bigkeeper file) 75 | 76 | COMMANDS 77 | feature - Gitflow feature operations 78 | help - Shows a list of commands or help for one command 79 | hotfix - Gitflow hotfix operations 80 | image - Image operations 81 | podfile - Podfile operation 82 | release - Gitflow release operations 83 | spec - Spec operations 84 | version - Show version of bigkeeper 85 | ``` 86 | 87 | 全局参数如下: 88 | 89 | - -p, --path:主项目所在的目录,默认是执行 bigkeeper 命令的当前目录; 90 | - -v, --ver:版本号,如果没有指定的话,会以主项目 [Bigkeeper 文件](./docs/zh-CN/BIGKEEPER_FILE.md)中指定的版本为准。 91 | - -u, --user:用户名,默认是 git global config 的 user.name,会显示在命令提示信息中,比如上述提示信息中的默认用户名是 mmoaay 92 | 93 | 功能列表如下: 94 | 95 | - [feature](./docs/zh-CN/FEATURE&HOTFIX.md):功能开发流程; 96 | - [hotfix](./docs/zh-CN/FEATURE&HOTFIX.md):线上 Bug 修复流程; 97 | - [release](./docs/zh-CN/RELEASE.md):发布流程; 98 | - [podfile](./docs/zh-CN/PODFILE.md):Podfile 管理; 99 | - [image](./docs/zh-CN/IMAGE.md):图片管理; 100 | - [spec](./docs/zh-CN/SPEC.md):spec 管理; 101 | - version:查看 bigkeeper 当前版本号; 102 | - help:查看 bigkeeper 帮助文档。 103 | 104 | ## 其他 105 | 106 | - [一些建议](./docs/zh-CN/RECOMMEND.md) 107 | - [一些思考]() 108 | 109 | ## 协议 110 | 111 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/License_icon-mit-88x31-2.svg/128px-License_icon-mit-88x31-2.svg.png) 112 | 113 | bigkeeper 基于 MIT 协议进行分发和使用,更多信息参见协议文件。 114 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | task :default => :spec 3 | -------------------------------------------------------------------------------- /big_keeper.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "big_keeper/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "bigkeeper" 8 | spec.version = BigKeeper::VERSION 9 | spec.authors = ["mmoaay"] 10 | spec.email = ["mmoaay@sina.com"] 11 | 12 | spec.summary = %q{Efficiency improvement for iOS&Android modular development.} 13 | spec.description = %q{Efficiency improvement for iOS&Android modular development, iOSer&Androider using this tool can make modular development easier.} 14 | spec.homepage = "https://github.com/BigKeeper/bigkeeper" 15 | spec.license = "MIT" 16 | 17 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' 18 | # to allow pushing to a single host or delete this section to allow pushing to any host. 19 | if spec.respond_to?(:metadata) 20 | spec.metadata["allowed_push_host"] = "https://rubygems.org" 21 | else 22 | raise "RubyGems 2.0 or newer is required to protect against " \ 23 | "public gem pushes." 24 | end 25 | 26 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 27 | f.match(%r{^(test|spec|features)/}) 28 | end 29 | spec.bindir = "bin" 30 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 31 | spec.require_paths = ["lib"] 32 | 33 | spec.add_runtime_dependency "gli", "~> 2.16" 34 | spec.add_runtime_dependency "big_stash", "~> 0.1" 35 | spec.add_runtime_dependency "cocoapods" 36 | spec.add_runtime_dependency "plist" 37 | spec.add_runtime_dependency "colorize" 38 | spec.add_runtime_dependency "big_resources" 39 | 40 | spec.add_development_dependency "bundler", "~> 1.15" 41 | spec.add_development_dependency "rake", "~> 10.0" 42 | spec.add_development_dependency "gli", "~> 2.16" 43 | spec.add_development_dependency "big_stash", "~> 0.1" 44 | spec.add_development_dependency "big_resources", "~> 0.1.2" 45 | spec.add_development_dependency "cocoapods" 46 | spec.add_development_dependency "plist" 47 | spec.add_development_dependency "colorize" 48 | spec.add_development_dependency "rubocop", "~> 0.50.0" 49 | end 50 | -------------------------------------------------------------------------------- /bin/big: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | #require "bundler/setup" 4 | require "big_keeper" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /docs/en-US/FEATURE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/docs/en-US/FEATURE.md -------------------------------------------------------------------------------- /docs/en-US/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [中文](../zh-CN/README.md) 4 | 5 | --- 6 | 7 | # bigkeeper - All in home project 8 | 9 | ![](../../resources/banner.png) 10 | 11 | [![Build Status](https://travis-ci.org/BigKeeper/bigkeeper.svg?branch=master)](https://travis-ci.org/BigKeeper/bigkeeper) 12 | [![Maintainability](https://api.codeclimate.com/v1/badges/c6dc4161e84fcec9a890/maintainability)](https://codeclimate.com/github/BigKeeper/bigkeeper/maintainability) 13 | [![Gem Version](https://badge.fury.io/rb/bigkeeper.svg)](https://rubygems.org/gems/bigkeeper) 14 | [![Language: Ruby](https://img.shields.io/badge/language-Ruby-da212f.svg)](https://www.ruby-lang.org/) 15 | [![License](https://img.shields.io/badge/license-MIT-000000.svg)](https://github.com/BigKeeper/big-keeper/blob/master/LICENSE) 16 | [![Gitter](https://img.shields.io/gitter/room/BigKeeper/BigKeeper.svg)](https://gitter.im/Big-Keeper/Lobby) 17 | 18 | bigkeeper is an efficiency improvement for iOS&Android modular development, iOSer&Androider using this tool can make modular development easier. 19 | 20 | ## Installation 21 | 22 | Add this line to your application's Gemfile: 23 | 24 | ```ruby 25 | gem 'bigkeeper' 26 | ``` 27 | 28 | And then execute: 29 | 30 | $ bundle 31 | 32 | Or install it yourself as: 33 | 34 | $ gem install bigkeeper 35 | 36 | ## Usage 37 | 38 | Run `big` to learn how to use bigkeeper. 39 | 40 | ## Development 41 | 42 | After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 43 | 44 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 45 | 46 | ## Contributing 47 | 48 | Bug reports and pull requests are welcome on GitHub at https://github.com/BigKeeper/bigkeeper. 49 | 50 | ## License 51 | 52 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/License_icon-mit-88x31-2.svg/128px-License_icon-mit-88x31-2.svg.png) 53 | 54 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 55 | -------------------------------------------------------------------------------- /docs/zh-CN/BIGKEEPER_FILE.md: -------------------------------------------------------------------------------- 1 | # Bigkeeper 文件 2 | 3 | 首先,我们在主项目 Podfile 所在的目录加入 **Bigkeeper** 文件。 4 | 5 | ## 配置版本 6 | 7 | ``` 8 | version '2.8.8' 9 | ``` 10 | 11 | 版本号的用途有: 12 | 13 | - feature 和 hotfix 流程里面做为分支名前缀的一部分,方便后期通过版本号匹配分支; 14 | - release 流程里面做为发布版本号,影响范围包括业务模块和主项目。 15 | 16 | ## 配置主项目 17 | 18 | ``` 19 | home 'BigKeeperMain', :git => 'git@github.com:BigKeeper/BigKeeperMain.git', :pulls => 'https://github.com/BigKeeper/BigKeeperMain/pulls' 20 | ``` 21 | 22 | 这个配置包含三个部分: 23 | 24 | - 主项目的名字; 25 | - 主项目的 git 远程仓库地址; 26 | - 主项目的 pull request / merge request 页面地址,完成某个 feature / hotfix 时,会自动打开这个页面,让开发者提交 pull request / merge request。 27 | 28 | ## 配置模块源 29 | 30 | ``` 31 | source 'git@git.elenet.me:LPD-iOS/LPDSpecs.git, elenet-lpdspecs' do 32 | modules do 33 | ... 34 | end 35 | end 36 | 37 | source ... 38 | ... 39 | end 40 | ``` 41 | 42 | 考虑到我们的业务模块可能属于不同的源,所以配置业务模块之前,要先在最外层配置源信息: 43 | 44 | - 源 git 地址; 45 | - 源名称。 46 | 47 | 配置这个主要是方便对我们关心的源执行 `pod repo update`,从而节省命令执行的时间。 48 | 49 | ## 配置业务模块 50 | 51 | ``` 52 | modules do 53 | mod 'BigKeeperModular', :git => 'git@github.com:BigKeeper/BigKeeperModular.git', :pulls => 'https://github.com/BigKeeper/BigKeeperModular/pulls' 54 | mod ... 55 | end 56 | ``` 57 | 58 | 这个配置包含可以配置多个业务模块,建议是把当前所有非第三方库都加入到这个配置里面: 59 | 60 | - 业务模块在 Podfile 中的名字; 61 | - 业务模块的 git 远程仓库地址; 62 | - 业务模块的 pull request / merge request 页面地址,完成某个 feature / hotfix 时,会自动打开这个页面,让开发者提交 pull request / merge request。 63 | 64 | ## 配置用户自定义信息 65 | 66 | ``` 67 | user 'perry' do 68 | mod 'BigKeeperModular', :path => '../BigKeeperModular' 69 | end 70 | ``` 71 | 72 | 如果用户需要配置一些自定义信息,比如业务模块在本地的路径,就可以增加一个这样的配置。 73 | 74 | 这个配置同样支持对指定的用户名(上述配置用户名为 perry)配置多个业务模块,目前支持的是: 75 | 76 | - 配置业务模块的本地路径,通过这个路径,我们就可以在主工程直接对业务模块做一些 CocoaPods 和 git 相关的操作。 77 | 78 | > 注: 79 | > 80 | > 1. 默认用户名是 git global config 的 user.name; 81 | > 82 | > 2. 默认我们会把本地路径配置成 `../{业务模块在 Podfile 中的名字}` 的形式,因为大部分情况下,我们会把项目都放在同级目录下,这也是我们推荐的; 83 | > 84 | > 3. 另外,在使用 bigkeeper 相关功能时,如果某些业务模块并没有 clone 到本地,bigkeeper 会根据之前配置的业务模块远程 git 地址 clone 业务模块仓库到**主项目同级目录下**。 85 | -------------------------------------------------------------------------------- /docs/zh-CN/FEATURE&HOTFIX.md: -------------------------------------------------------------------------------- 1 | # feature - 功能开发流程 & hotfix - 线上修复流程 2 | 3 | > 注:hotfix 除了基础分支是 **master**,其他操作和 feature 一致,所以我们这里以 feature 为例说明。 4 | 5 | 直接在命令行执行 `big feature --help` 可以查看其提供的所有功能: 6 | 7 | ``` 8 | NAME 9 | feature - Gitflow feature operations 10 | 11 | SYNOPSIS 12 | big [global options] feature delete 13 | big [global options] feature finish 14 | big [global options] feature list 15 | big [global options] feature publish 16 | big [global options] feature pull 17 | big [global options] feature push 18 | big [global options] feature rebase 19 | big [global options] feature start 20 | big [global options] feature switch 21 | big [global options] feature update 22 | 23 | COMMANDS 24 | delete - Delete feature with name 25 | finish - Finish current feature 26 | list - List all the features 27 | publish - Publish current feature 28 | pull - Pull remote changes for current feature 29 | push - Push local changes to remote for current feature 30 | rebase - Rebase 'develop' to current feature 31 | start - Start a new feature with name for given modules and main project 32 | switch - Switch to the feature with name 33 | update - Update modules for the feature with name 34 | ``` 35 | 36 | 功能列表如下: 37 | 38 | - start: 39 | 开始一个新的 feature,输入参数依次为: 40 | - feature 的名字; 41 | - 开发该 feature 需要改动的业务模块名。可以多个,用空格隔开;如果不指定,取 **Bigkeeper 文件中所有的业务模块名**。 42 | - finish:结束当前 feature; 43 | - publish:发布当前 feature; 44 | - switch:切换到一个已经存在的 feature,输入参数为 feature 名; 45 | - update: 46 | 更新一个 feature 需要改动的业务模块,输入参数依次为: 47 | - feature 的名字; 48 | - 开发该 feature 需要改动的业务模块名。可以多个,用空格隔开;如果不指定,取 **Bigkeeper 文件中所有的业务模块名**。 49 | - pull:拉取当前 feature 主项目和业务模块的远程 git 仓库更新; 50 | - push:提交并推送当前 feature 主项目和业务模块的本地变更到远程 git 仓库,输入参数为提交信息; 51 | - rebase:rebase 主项目和业务模块 `develop` 分支到当前 feature; 52 | - delete:根据 featue 名删除主项目和业务模块所有相关分支;(PS:慎用,因为会删除本地和远程所有相关分支) 53 | - list:显示当前的 feature 列表。 54 | 55 | ## 各个指令的使用场景 56 | 57 | - 如果需要开发新的功能,使用 big feature start; 58 | - 开发功能过程中如果发现某个模块其实不用改动,使用 big feature update 来把这个模块从这个 feature 移除; 59 | - 使用 big feature pull 来同步主项目和相关业务模块远端变更; 60 | - 使用 big feature push 来推送主项目和相关业务模块变更到远端; 61 | - 使用 big feature rebase 来 rebase 主项目和业务模块 `develop` 分支到当前 feature; 62 | - 使用 big feature switch 来切换 feature,比如:两个功能并行开发的时候,就可以用这个命令互相切换了; 63 | - 功能开发完成,需要单独打包提交测试时,使用 big feature finish 结束开发; 64 | - 开始进行集成测试,需要将代码 PR 到 develop 分支时,使用 big feature publish; 65 | - 如果开发过程中突然有新的功能需要紧急开发,直接使用 big feature start 进行新功能开发,旧功能未提交的内容我们会保存在 stash 中。 66 | 67 | ## 各个指令执行结束后工程的状态 68 | 69 | ### feature 的工作区 70 | 71 | 在开始了解各条指令执行后工程的状态之前,我们先了解一下 bigkeeper feature 流程的工作区。 72 | 73 | ![](../../resources/readme/big-keeper-readme.001.png) 74 | 75 | feature 的工作区主要由两部分组成: 76 | 77 | - 主项目; 78 | - 相关业务模块,我们把 Podfile 中引用方式为 `:path => {业务模块本地路径}` 的模块做为相关业务模块。 79 | 80 | 主项目和每个相关业务模块又有各自的工作区,由三个部分组成: 81 | 82 | - 当前代码区改动; 83 | - stash 缓存区,当用户需要切换新的 feature 时,对于用户来不及提交的改动,我们会缓存到各个项目的 stash 中,(PS:所以代码突然不见了不要担心,都在 git 的 stash 里面),而当用户切换回某个 feature 时,我们会把和该 feature 分支同名的 stash 恢复回来,从而使用户可以继续开发之前未完成的部分,因为需要通过 feature 的分支名来匹配 stash,而 git stash 又没有提供给 stash 命名的功能,所以我们实现了 [bigstash](https://github.com/BigKeeper/bigstash) 来完成这个功能; 84 | - git。 85 | 86 | > 注: 87 | > 1. 所有的指令我们都做了良好的出错提示和异常恢复机制,当某个步骤报错时,只需要根据提示的错误手动修复问题,重新输入指令,bigkeeper 会继续执行接下来的流程。 88 | > 2. 所有的代码都会保存在工作区中,除了 delete 操作,bigkeeper 不会删除任何工作区的代码,也就是说:你总是可以在变更区、Stash 中或者远端找到你变更的代码。 89 | -------------------------------------------------------------------------------- /docs/zh-CN/IMAGE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/docs/zh-CN/IMAGE.md -------------------------------------------------------------------------------- /docs/zh-CN/PODFILE.md: -------------------------------------------------------------------------------- 1 | # podfile - podfile版本控制流程 2 | 3 | 直接在命令行执行 `big podfile --help` 可以查看其提供的所有功能 4 | 5 | ``` 6 | NAME 7 | podfile - Podfile operation 8 | 9 | SYNOPSIS 10 | big [global options] podfile [command options] detect 11 | big [global options] podfile [command options] lock 12 | big [global options] podfile [command options] upgrade 13 | 14 | COMMAND OPTIONS 15 | --pod, --podfile=arg - (default: none) 16 | 17 | COMMANDS 18 | detect - Detect podname should be locked. 19 | lock - Lock podname should be locked. 20 | upgrade - Update module version 21 | 22 | ``` 23 | 24 | 全局参数如下: 25 | 26 | - -pod --podfile :podfile文件路径 27 | 28 | 功能列表如下: 29 | 30 | - detect: 31 | 检测当前同步到本地的项目工程中podfile,得到未锁定的第三方库。 32 | 如果存在,提示需要指定版本。 33 | 如果不存在,提示无需指定版本的第三方库。 34 | 35 | - lock: 36 | 检测到存在需指定代版本的三方库后,执行lock指令对所有需要锁定的库进行锁定。 37 | 38 | - upgrade: 39 | 检测配置文件中指定业务库,有无最新可更新版本。 40 | 41 | ## podfile lock 流程 42 | 43 | ![](../../resources/readme/big-keeper-readme.011.png) 44 | -------------------------------------------------------------------------------- /docs/zh-CN/README.md: -------------------------------------------------------------------------------- 1 | [English](../en-US/README.md) 2 | 3 | --- 4 | 5 | # bigkeeper 6 | 7 | ![](../../resources/banner.png) 8 | 9 | [![Build Status](https://travis-ci.org/BigKeeper/bigkeeper.svg?branch=master)](https://travis-ci.org/BigKeeper/bigkeeper) 10 | [![Maintainability](https://api.codeclimate.com/v1/badges/c6dc4161e84fcec9a890/maintainability)](https://codeclimate.com/github/BigKeeper/big-keeper/maintainability) 11 | [![Gem Version](https://badge.fury.io/rb/bigkeeper.svg)](https://rubygems.org/gems/bigkeeper) 12 | [![Language: Ruby](https://img.shields.io/badge/language-Ruby-da212f.svg)](https://www.ruby-lang.org/) 13 | [![License](https://img.shields.io/badge/license-MIT-000000.svg)](https://github.com/BigKeeper/big-keeper/blob/master/LICENSE) 14 | [![Gitter](https://img.shields.io/gitter/room/BigKeeper/BigKeeper.svg)](https://gitter.im/Big-Keeper/Lobby) 15 | 16 | bigkeeper 是一个 **iOS&Android 模块化项目**效率提升工具,旨在帮助有模块化需求的团队更轻松的渡过“半模块化”尴尬期。 17 | 18 | 使用这个工具后,开发者**在主项目内**即可完成**包括其他业务模块在内**所有代码的编写和管理。 19 | 20 | 我们借鉴 CocoaPods 的做法,开发团队只需要在主项目中加入一个 Bigkeeper 文件,然后利用我们提供的 `big` 命令,即可更便捷的实现**代码分支**和**业务模块**的管理;另外,通过 `big` 命令提供的流程,也能帮助开发者养成更规范的日常开发习惯,减轻项目管理者的负担。 21 | 22 | ## 应用场景 23 | 24 | 在模块化项目的过渡阶段,我们往往会碰到业务拆分不是那么彻底的情况(PS:比如主项目中仍然存在业务相关代码),这样就会导致一系列问题: 25 | 26 | - 开发流程方面:开发者在开发一个 feature 时需要维护多个项目的分支,同时还要频繁修改主项目的 `Podfile`; 27 | - 项目管理者需要花很大精力去和团队成员同步项目管理规范流程,尤其对于新人,挑战很大; 28 | - 开发过程中如果碰到突发情况需要开新分支处理问题时对当前工作区的保存操作会很繁琐(PS:因为涉及到多个项目); 29 | - 日常开发过程中类似 commit、pull、push 等操作比较繁琐(PS:因为涉及到多个项目); 30 | - 资源(比如图片)散落在各个业务模块,可能存在重名、重复等问题,难以管理; 31 | - 业务模块可能存在横向依赖的问题,导致业务模块间耦合严重,不能单独编译运行。 32 | - 各模块依赖的版本没有统一的同步更新方式,导致各个模块的开发环境不一致,问题百出。 33 | 34 | bigkeeper 的出现就是为了解决这些问题。 35 | 36 | > 注:也有很多人认为直接做一个完整的组件化项目更合理,而不需要采用这样曲折的方案,但是很多时候我们并没有这么多的精力直接去做这些事情,bigkeeper 存在的价值也是为了帮助开发团队在** iOS&Android 模块化项目过渡阶段**能更轻松的往前走。 37 | 38 | ## 安装 39 | 40 | - 当作 Ruby Gem 使用: 41 | 在项目的 Gemfile 中加入下面这行: 42 | 43 | ```ruby 44 | gem 'bigkeeper' 45 | ``` 46 | 47 | 然后执行: 48 | 49 | $ bundle 50 | 51 | - 直接安装: 52 | 53 | $ gem install bigkeeper 54 | 55 | ## 使用方法 56 | 57 | - [Bigkeeper 文件配置](BIGKEEPER_FILE.md) 58 | - `bigkeeper` 工具使用: 59 | 60 | 直接在命令行执行 `bigkeeper` 可以查看其提供的所有功能: 61 | 62 | ``` 63 | NAME 64 | big - Efficiency improvement for iOS&Android module development, iOSer&Android using this tool can make module development easier. 65 | 66 | SYNOPSIS 67 | big [global options] command [command options] [arguments...] 68 | 69 | 70 | GLOBAL OPTIONS 71 | --help - Show this message 72 | -p, --path=arg - (default: ./) 73 | -u, --user=arg - (default: mmoaay) 74 | -v, --ver=arg - (default: Version in Bigkeeper file) 75 | 76 | COMMANDS 77 | feature - Gitflow feature operations 78 | help - Shows a list of commands or help for one command 79 | hotfix - Gitflow hotfix operations 80 | image - Image operations 81 | podfile - Podfile operation 82 | release - Gitflow release operations 83 | spec - Spec operations 84 | version - Show version of bigkeeper 85 | ``` 86 | 87 | 全局参数如下: 88 | 89 | - -p, --path:主项目所在的目录,默认是执行 bigkeeper 命令的当前目录; 90 | - -v, --ver:版本号,如果没有指定的话,会以主项目 [Bigkeeper 文件](BIGKEEPER_FILE.md)中指定的版本为准。 91 | - -u, --user:用户名,默认是 git global config 的 user.name,会显示在命令提示信息中,比如上述提示信息中的默认用户名是 mmoaay 92 | 93 | 功能列表如下: 94 | 95 | - [feature](FEATURE&HOTFIX.md):功能开发流程; 96 | - [hotfix](FEATURE&HOTFIX.md):线上 Bug 修复流程; 97 | - [release](RELEASE.md):发布流程; 98 | - [podfile](PODFILE.md):Podfile 管理; 99 | - [image](IMAGE.md):图片管理; 100 | - [spec](SPEC.md):spec 管理; 101 | - version:查看 bigkeeper 当前版本号; 102 | - help:查看 bigkeeper 帮助文档。 103 | 104 | ## 其他 105 | 106 | - [一些建议](RECOMMEND.md) 107 | - [一些思考]() 108 | 109 | ## 协议 110 | 111 | ![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f8/License_icon-mit-88x31-2.svg/128px-License_icon-mit-88x31-2.svg.png) 112 | 113 | bigkeeper 基于 MIT 协议进行分发和使用,更多信息参见协议文件。 114 | -------------------------------------------------------------------------------- /docs/zh-CN/RECOMMEND.md: -------------------------------------------------------------------------------- 1 | # 代码管理规范 2 | 3 | ## 基本原则 4 | 5 | - master 分支保证是线上最新包的代码。 6 | - develop 分支保证是下一个发布包的代码,随时支持发布。 7 | 8 | ### 开发和测试流程 9 | 10 | feature 分支为 feature 开发和提测分支,提测前需要 rebase/merge develop 的代码,测试完成后,pr 到 develop 分支,确定下个版本发布才 accept pr。 11 | 12 | ### develop 分支 bug 修复流程 13 | 14 | 单独开分支,fix 完成后,pr 到 develop 分支。 15 | 16 | ### 发布流程 17 | 18 | develop->release->master 瞬间操作,release 不能长期存在。 19 | 20 | ### hotfix 流程 21 | 22 | master 拉出 hotfix,fix 完成后分别 pr 到 master、develop 两个分支,完成后新建发布版本。 23 | -------------------------------------------------------------------------------- /docs/zh-CN/RELEASE.md: -------------------------------------------------------------------------------- 1 | 饿了么物流模块化效率提升思考 - 发布流程篇 2 | ### 背景 3 | bigkeeper 的目的是为了在项目的模块化过渡阶段提升效率,在过渡到模块化的过程中要做很多的代码抽离,而越来越多的 Pod依赖(假设用Cocoapods做包管理)对项目管理者的负担越来越大. 4 | 移动端完成一个需求迭代,除了代码开发外,还需要有模块发版,集成,打包,回归这些步骤,而往往在回归到发版的时候,研发同学要花较多时间在发版,这严重影响了开发效率. 5 | ### 解决方案 6 | 为了提高要提高发布流程的发布效率,我们开发了big release 功能是来提升发布效率.首先说明,bigkeeper的开发以及发布流程是完全遵循[git-flow 流程](https://jeffkreeftmeijer.com/git-flow/). 7 | 我们建议直接把`Pods`和`Podfile.lock`移出版本控制系统,因为模块化的操作会频繁进行 `Pods`的增删以及更新,但是这会带来版本锁定的困扰,我们建议把版本锁定的工作从 `Podfile.lock`移到`Podfile`中,为此我们做了一个功能 [big pod](---.). 8 | `bigkeeper`提供了`release module`模块发版和`release home`主工程发版,每种发版方式分为` start`和` finish`, 这两种方式的最大区别是` start`没有`git push`,不会因为可能存在的问题而污染`origin`仓库,这也留一个口子给研发人员进行二次确认. 9 | #### 模块发版 10 | 在过渡到模块化和已经模块化的项目中, 会存在很多依赖库, 一般会分为: 11 | * 业务模块库 12 | * 业务基础库 13 | * 二方库(公司内部库) 14 | * 三方库 15 | 一般来说,二方库和三方库的版本稳定,而每次业务迭代频繁改动往往就是业务模块库与业务基础库,这这两种类型的库在发布的时候往往因为会因为依赖库的原因而导致发版失败,浪费开发人员的时间. 16 | 所以,模块发布提供了两种解决方案: 17 | ``` 18 | big release module finish ExampleModule 19 | Options: 20 | -s, --spec : 模块需要发布版本 21 | ``` 22 | `--spec`指令决定模块需要发版,如果不需要,那仅会在`master`分支打上`tag`. 23 | 而在`start`指令中我们也做了一些安全性检查: 24 | * 根据开发流程的分支命名规范(branch: feature/x.x.x),检查当前是否有分支还没有合并到`develop`分支; 25 | * 检查`master`分支相较于`develop`分支是否有超前的` commit`,防止有不规范的操作导致污染` master`分支代码. 26 | ``` 27 | big release module start ExampleModule 28 | Options: 29 | -i, --ignore : 忽略安全性检查(默认开启) 30 | ``` 31 | #### 主工程发版 32 | 在模块化完成之后,主工程基本上是个壳工程,只会有`Podfile`和配置文件的改动, 根据`git-flow`的规则`bigkeeper`在发布主工程时会从`develop`切出` release/x.x.x`分支,`release`分支是一个暂时性分支,在代码已经并入`master`之后会删除`release`分支. 33 | `release home start`中有两种模块的引用方式: 34 | ``` 35 | pod 'ExampleModule', '0.1.0' 36 | or 37 | pod 'ExampleModule', :git => 'ExampleModule.git', :tag => '0.1.0' 38 | ``` 39 | 如果模块没有发布,` bigkeeper`会根据在根据仓库里的最新`tag`和`.cocoapods/repos` 里的已发布版本去找到合适的依赖方式. 40 | 在` release home finish`中有一个需要注意的地方就是` release`分支合并到` develop`之前`reset`掉对` Podfile`的操作,这样保持了不会有在` Podfile`文件不会有冲突. 41 | ### 举个例子 42 | 模块发版: 43 | ``` 44 | big -v 0.1.0 release module start ExampleModule 45 | big -v 0.1.0 release module finish ExampleModule 46 | Options: 47 | -v : 发布是指定版本号,如未指定,则用bigkeeper文件中的vesion 48 | ``` 49 | 主工程发版: 50 | ``` 51 | big -v 0.1.0 release home start 52 | big -v 0.1.0 release home finish 53 | Options: 54 | -v : 发布是指定版本号,如未指定,则用bigkeeper文件中的vesion 55 | ``` 56 | ### 展望 57 | 对于很多中大型公司来说都是自己的CI系统,可以把发版本这种耗时而且占内存的工作放在CI机器上,所以我们也有计划把`release module`最后一步的发版本的步骤留个口子,研发同学可以在这里去触发各自CI系统的发版流程. 58 | ### 结束语 59 | `bigkeeper`在饿了么物流已经实践了半年多了,从数个模块到现在40+的模块数量,仅`release`流程节省项目管理者非常多的时间,提升了效率而且规范化了开发流程,也希望大家在使用中有` issue`反馈给到我们. 60 | 61 | -------------------------------------------------------------------------------- /docs/zh-CN/SPEC.md: -------------------------------------------------------------------------------- 1 | # spec - spec 管理 2 | 3 | ## 背景 4 | 5 | 模块化推进过程中势必存在业务模块间的横向依赖,原则上这种依赖我们都需要通过 router 进行解耦,不能直接源码依赖其他业务模块,因为这样的依赖会导致如下的问题: 6 | 7 | - 因为直接横向依赖业务模块,导致业务模块无法独立编译运行。 8 | - 各个业务模块如果存在大量类似依赖,必将导致后期模块代码依赖错综复杂,难以维护。 9 | 10 | ## 功能简介 11 | 12 | 直接在命令行执行 `big spec --help` 可以查看其提供的所有功能: 13 | 14 | ``` 15 | NAME 16 | spec - Spec operations 17 | 18 | SYNOPSIS 19 | big [global options] spec [command options] add 20 | big [global options] spec [command options] analyze 21 | big [global options] spec [command options] delete 22 | big [global options] spec [command options] list 23 | big [global options] spec [command options] search 24 | 25 | COMMAND OPTIONS 26 | -a, --[no-]all - 27 | 28 | COMMANDS 29 | add - Add a spec (Coming soon). 30 | analyze - Analyze spec dependency infomation. 31 | delete - Delete a spec (Coming soon). 32 | list - List all the specs. 33 | search - Search a spec with name (Coming soon). 34 | ``` 35 | 36 | 功能列表如下: 37 | 38 | - analyze:分析所有指定模块之间的依赖,通常为业务模块。 39 | - list:显示当前所有的业务 spec。 40 | -------------------------------------------------------------------------------- /lib/big_keeper.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'big_keeper/version' 4 | 5 | require 'big_keeper/util/bigkeeper_parser' 6 | require 'big_keeper/util/git_operator' 7 | require 'big_keeper/util/verify_operator' 8 | 9 | require 'big_keeper/model/gitflow_type' 10 | 11 | require 'big_keeper/command/feature&hotfix' 12 | require 'big_keeper/command/release' 13 | require 'big_keeper/command/pod' 14 | require 'big_keeper/command/spec' 15 | require 'big_keeper/command/image' 16 | require 'big_keeper/command/init' 17 | require 'big_keeper/command/client' 18 | require 'big_keeper/service/git_service' 19 | require 'big_keeper/util/leancloud_logger' 20 | 21 | require 'gli' 22 | 23 | include GLI::App 24 | 25 | module BigKeeper 26 | # Your code goes here... 27 | program_desc 'Efficiency improvement for iOS&Android module development, iOSer&Android using this tool can make module development easier.' 28 | 29 | flag %i[p path], default_value: './' 30 | flag %i[v ver], default_value: 'Version in Bigkeeper file' 31 | flag %i[u user], default_value: GitOperator.new.user 32 | flag %i[l log], default_value: true 33 | 34 | if VerifyOperator.already_in_process? 35 | p %Q(There is another 'big' command in process, please wait) 36 | exit 37 | end 38 | 39 | if !GitflowOperator.new.verify_git_flow_command 40 | p %Q('git-flow' not found, use 'brew install git-flow' to install it) 41 | exit 42 | end 43 | 44 | # pre do |global_options, command, options, args| 45 | # LeanCloudLogger.instance.start_log(global_options, args) 46 | # end 47 | # 48 | # post do |global_options, command, options, args| 49 | # is_show_log = true 50 | # if global_options[:log] == 'true' 51 | # is_show_log = false 52 | # end 53 | # LeanCloudLogger.instance.end_log(true, is_show_log) 54 | # end 55 | 56 | feature_and_hotfix_command(GitflowType::FEATURE) 57 | 58 | feature_and_hotfix_command(GitflowType::HOTFIX) 59 | 60 | release_command 61 | 62 | pod_command 63 | 64 | spec_command 65 | 66 | image_command 67 | 68 | init_command 69 | 70 | client_command 71 | 72 | desc 'Show version of bigkeeper' 73 | command :version do |version| 74 | version.action do |global_options, options, args| 75 | LeanCloudLogger.instance.set_command("version") 76 | p "bigkeeper (#{VERSION})" 77 | end 78 | end 79 | 80 | exit run(ARGV) 81 | end 82 | -------------------------------------------------------------------------------- /lib/big_keeper/command/client.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/leancloud_logger' 2 | require 'big_keeper/command/pod/podfile' 3 | require 'big_keeper/command/spec/list' 4 | require 'big_keeper/util/list_generator' 5 | 6 | module BigKeeper 7 | 8 | def self.client_command 9 | desc 'API for bigkeeper-client.' 10 | command :client do | c | 11 | c.desc 'Commands about operate modules.' 12 | c.command :modules do |modules| 13 | modules.desc 'Get modules list from Bigkeeper file.' 14 | modules.command :list do |list| 15 | list.action do |global_options, options, args| 16 | LeanCloudLogger.instance.set_command("spec/list") 17 | path = File.expand_path(global_options[:path]) 18 | version = global_options[:ver] 19 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 20 | spec_list(path, user, options) 21 | end 22 | end 23 | modules.desc 'Update modules.' 24 | modules.command :update do |update| 25 | update.action do |global_options, options, args| 26 | LeanCloudLogger.instance.set_command("spec/list") 27 | path = File.expand_path(global_options[:path]) 28 | version = global_options[:ver] 29 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 30 | spec_list(path, user, options) 31 | end 32 | end 33 | end 34 | c.desc 'Commands about features.' 35 | c.command :feature do |feature| 36 | feature.desc "List all the features including origin." 37 | feature.command :list do | list | 38 | list.flag %i[v version] , default_value: 'all versions' 39 | list.action do |global_options, options, args| 40 | LeanCloudLogger.instance.set_command("feature/list/json") 41 | options[:json] = true 42 | path = File.expand_path(global_options[:path]) 43 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 44 | list(path, user, GitflowType::FEATURE, options) 45 | end 46 | end 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/command/feature&hotfix/start' 2 | require 'big_keeper/command/feature&hotfix/finish' 3 | require 'big_keeper/command/feature&hotfix/switch' 4 | require 'big_keeper/command/feature&hotfix/update' 5 | require 'big_keeper/command/feature&hotfix/pull' 6 | require 'big_keeper/command/feature&hotfix/push' 7 | require 'big_keeper/command/feature&hotfix/rebase' 8 | require 'big_keeper/command/feature&hotfix/publish' 9 | require 'big_keeper/command/feature&hotfix/delete' 10 | require 'big_keeper/command/feature&hotfix/list' 11 | require 'big_keeper/util/leancloud_logger' 12 | 13 | module BigKeeper 14 | def self.feature_and_hotfix_command(type) 15 | desc "Gitflow #{GitflowType.name(type)} operations" 16 | command GitflowType.command(type) do |c| 17 | c.desc "Start a new #{GitflowType.name(type)} with name for given modules and main project" 18 | c.command :start do |start| 19 | start.action do |global_options, options, args| 20 | path = File.expand_path(global_options[:path]) 21 | version = global_options[:ver] 22 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 23 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/start") 24 | 25 | help_now!('user name is required') if user and user.empty? 26 | help_now!("#{GitflowType.name(type)} name is required") if args.length < 1 27 | name = args[0] 28 | modules = args[(1...args.length)] if args.length > 1 29 | start(path, version, user, name, modules, type) 30 | end 31 | end 32 | 33 | c.desc "Update modules for the #{GitflowType.name(type)} with name" 34 | c.command :update do |update| 35 | update.action do |global_options, options, args| 36 | path = File.expand_path(global_options[:path]) 37 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 38 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/update") 39 | 40 | help_now!('user name is required') if user and user.empty? 41 | modules = args[(0...args.length)] if args.length > 0 42 | update(path, user, modules, type) 43 | end 44 | end 45 | 46 | c.desc "Switch to the #{GitflowType.name(type)} with name" 47 | c.command :switch do |switch| 48 | switch.action do |global_options, options, args| 49 | path = File.expand_path(global_options[:path]) 50 | version = global_options[:ver] 51 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 52 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/switch") 53 | 54 | help_now!('user name is required') if user and user.empty? 55 | help_now!("#{GitflowType.name(type)} name is required") if args.length < 1 56 | name = args[0] 57 | switch_to(path, version, user, name, type) 58 | end 59 | end 60 | 61 | c.desc "Pull remote changes for current #{GitflowType.name(type)}" 62 | c.command :pull do |pull| 63 | pull.action do |global_options, options, args| 64 | path = File.expand_path(global_options[:path]) 65 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 66 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/pull") 67 | 68 | help_now!('user name is required') if user and user.empty? 69 | pull(path, user, type) 70 | end 71 | end 72 | 73 | c.desc "Push local changes to remote for current #{GitflowType.name(type)}" 74 | c.command :push do |push| 75 | push.action do |global_options, options, args| 76 | path = File.expand_path(global_options[:path]) 77 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 78 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/push") 79 | 80 | help_now!('user name is required') if user and user.empty? 81 | help_now!('comment message is required') if args.length < 1 82 | help_now!(%Q(comment message should be wrappered with '' or "")) if args.length > 1 83 | comment = args[0] 84 | push(path, user, comment, type) 85 | end 86 | end 87 | 88 | c.desc "Rebase '#{GitflowType.base_branch(type)}' to current #{GitflowType.name(type)}" 89 | c.command :rebase do |rebase| 90 | rebase.action do |global_options, options, args| 91 | path = File.expand_path(global_options[:path]) 92 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 93 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/rebase") 94 | 95 | help_now!('user name is required') if user and user.empty? 96 | rebase(path, user, type) 97 | end 98 | end 99 | 100 | c.desc "Finish current #{GitflowType.name(type)}" 101 | c.command :finish do |finish| 102 | finish.action do |global_options, options, args| 103 | path = File.expand_path(global_options[:path]) 104 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 105 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/finish") 106 | 107 | help_now!('user name is required') if user and user.empty? 108 | finish(path, user, type) 109 | end 110 | end 111 | 112 | c.desc "Publish current #{GitflowType.name(type)}" 113 | c.command :publish do |publish| 114 | publish.action do |global_options, options, args| 115 | path = File.expand_path(global_options[:path]) 116 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 117 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/publish") 118 | 119 | help_now!('user name is required') if user and user.empty? 120 | publish(path, user, type) 121 | end 122 | end 123 | 124 | c.desc "Delete #{GitflowType.name(type)} with name" 125 | c.command :delete do |delete| 126 | delete.action do |global_options, options, args| 127 | path = File.expand_path(global_options[:path]) 128 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 129 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/delete") 130 | 131 | help_now!('user name is required') if user and user.empty? 132 | help_now!("#{GitflowType.name(type)} name is required") if args.length < 1 133 | name = args[0] 134 | delete(path, user, name, type) 135 | end 136 | end 137 | 138 | c.desc "List all the #{GitflowType.name(type)}s" 139 | c.command :list do |list| 140 | list.flag %i[v version] , default_value: 'all versions' 141 | list.desc "Print list of TREE format." 142 | list.command :tree do |tree| 143 | tree.action do |global_options, options, args| 144 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/list/tree") 145 | 146 | Logger.highlight("Generating #{GitflowType.name(type)} tree of all version...") if args.length < 1 147 | path = File.expand_path(global_options[:path]) 148 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 149 | list(path, user, type, options) 150 | end 151 | end 152 | 153 | list.desc "Print list of JSON format." 154 | list.command :json do |json| 155 | json.action do |global_options, options, args| 156 | LeanCloudLogger.instance.set_command("#{GitflowType.command(type)}/list/json") 157 | 158 | options[:json] = true 159 | path = File.expand_path(global_options[:path]) 160 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 161 | list(path, user, type, options) 162 | end 163 | end 164 | end 165 | end 166 | end 167 | end 168 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/delete.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'big_keeper/util/podfile_operator' 4 | require 'big_keeper/util/gitflow_operator' 5 | require 'big_keeper/util/bigkeeper_parser' 6 | require 'big_keeper/util/logger' 7 | require 'big_keeper/util/pod_operator' 8 | 9 | require 'big_keeper/dependency/dep_service' 10 | 11 | require 'big_keeper/dependency/dep_type' 12 | 13 | require 'big_keeper/service/stash_service' 14 | require 'big_keeper/service/module_service' 15 | 16 | 17 | module BigKeeper 18 | def self.delete(path, user, name, type) 19 | begin 20 | # Parse Bigkeeper file 21 | BigkeeperParser.parse("#{path}/Bigkeeper") 22 | branch_name = "#{GitflowType.name(type)}/#{name}" 23 | version = 'all version' 24 | 25 | modules = BigkeeperParser.module_names 26 | modules.each do |module_name| 27 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 28 | 29 | if FileOperator.definitely_exists?(module_full_path) 30 | StashService.new.pop_stash(module_full_path, branch_name, module_name) 31 | GitService.new.verify_del(module_full_path, branch_name, module_name, type) 32 | end 33 | end 34 | 35 | StashService.new.pop_stash(path, branch_name, 'Home') 36 | GitService.new.verify_del(path, branch_name, 'Home', type) 37 | ensure 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/finish.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'big_keeper/util/podfile_operator' 4 | require 'big_keeper/util/logger' 5 | require 'big_keeper/util/pod_operator' 6 | require 'big_keeper/util/xcode_operator' 7 | require 'big_keeper/util/cache_operator' 8 | require 'big_keeper/util/bigkeeper_parser' 9 | require 'big_keeper/model/operate_type' 10 | require 'big_keeper/dependency/dep_service' 11 | 12 | require 'big_keeper/dependency/dep_type' 13 | 14 | 15 | module BigKeeper 16 | 17 | def self.finish(path, user, type) 18 | begin 19 | # Parse Bigkeeper file 20 | BigkeeperParser.parse("#{path}/Bigkeeper") 21 | 22 | branch_name = GitOperator.new.current_branch(path) 23 | Logger.error("Not a #{GitflowType.name(type)} branch, exit.") unless branch_name.include? GitflowType.name(type) 24 | 25 | # Cache git modules 26 | if ModuleCacheOperator.new(path).all_path_modules.empty? 27 | Logger.error("Branch '#{branch_name}' is already finished, exit.") 28 | end 29 | 30 | ModuleCacheOperator.new(path).cache_git_modules(ModuleCacheOperator.new(path).all_path_modules) 31 | 32 | modules = ModuleCacheOperator.new(path).remain_git_modules 33 | # Rebase modules and modify module as git 34 | modules.each do |module_name| 35 | ModuleService.new.finish(path, user, module_name, branch_name, type) 36 | end 37 | 38 | Logger.highlight("Finish branch '#{branch_name}' for 'Home'") 39 | 40 | # Delete all path modules 41 | ModuleCacheOperator.new(path).cache_path_modules([], [], []) 42 | 43 | # Install 44 | DepService.dep_operator(path, user).install(modules, OperateType::FINISH, false) 45 | 46 | # Open home workspace 47 | DepService.dep_operator(path, user).open 48 | 49 | # Push home changes to remote 50 | GitService.new.verify_push(path, "finish branch #{branch_name}", branch_name, 'Home') 51 | ensure 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/list.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'big_keeper/util/bigkeeper_parser' 3 | require 'big_keeper/util/list_generator' 4 | require 'big_keeper/service/module_service' 5 | 6 | 7 | module BigKeeper 8 | def self.list(path, user, type, options) 9 | BigkeeperParser.parse("#{path}/Bigkeeper") 10 | home_path = File.expand_path(path) 11 | #get home project branches 12 | branches = GitService.new.branchs_with_type(home_path, type) 13 | #get modules list 14 | is_print_log = false if options[:json] 15 | #get search version 16 | version = options[:version] 17 | cache_path = File.expand_path("#{path}/.bigkeeper") 18 | json_path = "#{cache_path}/branches.json" 19 | begin 20 | #get cache file path 21 | FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path) 22 | file = File.new(json_path, 'w', :encoding => 'UTF-8') 23 | begin 24 | #get all modules info 25 | module_list_dic = get_module_info(path, user, type, version, branches, is_print_log) 26 | file << module_list_dic.to_json 27 | file.close 28 | end 29 | #print list 30 | generate_list_with_option(options, json_path, version, branches) 31 | ensure 32 | file.close 33 | end 34 | end 35 | 36 | def self.get_module_info(path, user, type, version, home_branches, is_print_log) 37 | #get module list 38 | modules = BigkeeperParser.module_names 39 | git_operator = GitOperator.new 40 | module_info_list = [] 41 | modules.each do |module_name| 42 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 43 | #local project verify 44 | if !File.exist? module_full_path 45 | Logger.default("No local repository for module '#{module_name}', clone it...") if is_print_log 46 | module_git = BigkeeperParser.module_git(module_name) 47 | git_operator.clone(File.expand_path("#{module_full_path}/../"), module_git) 48 | end 49 | #每个模块详细信息 50 | module_branch_info = ModuleService.new.module_info(module_full_path, home_branches, user, type, module_name, version) 51 | module_info_list << module_branch_info 52 | end 53 | module_info_list 54 | end 55 | 56 | def self.generate_list_with_option(options, file_path, version, home_branches) 57 | if options[:json] 58 | ListGenerator.generate_json(file_path, home_branches, version) 59 | else 60 | ListGenerator.generate_tree(file_path, home_branches, version) 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/publish.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'big_keeper/util/podfile_operator' 4 | require 'big_keeper/util/logger' 5 | require 'big_keeper/util/pod_operator' 6 | require 'big_keeper/util/xcode_operator' 7 | require 'big_keeper/util/cache_operator' 8 | require 'big_keeper/util/bigkeeper_parser' 9 | require 'big_keeper/model/operate_type' 10 | require 'big_keeper/dependency/dep_service' 11 | 12 | require 'big_keeper/dependency/dep_type' 13 | 14 | 15 | module BigKeeper 16 | 17 | def self.publish(path, user, type) 18 | begin 19 | # Parse Bigkeeper file 20 | BigkeeperParser.parse("#{path}/Bigkeeper") 21 | 22 | branch_name = GitOperator.new.current_branch(path) 23 | Logger.error("Not a #{GitflowType.name(type)} branch, exit.") unless branch_name.include? GitflowType.name(type) 24 | 25 | path_modules = ModuleCacheOperator.new(path).current_path_modules 26 | Logger.error("You have unfinished modules #{path_modules}, Use 'finish' first please.") unless path_modules.empty? 27 | 28 | # Push modules changes to remote then rebase 29 | modules = ModuleCacheOperator.new(path).current_git_modules 30 | modules.each do |module_name| 31 | ModuleService.new.pre_publish(path, user, module_name, branch_name, type) 32 | end 33 | 34 | # Install 35 | DepService.dep_operator(path, user).install(modules, OperateType::PUBLISH, false) 36 | 37 | # Modify module as git 38 | modules.each do |module_name| 39 | ModuleService.new.publish(path, user, module_name, branch_name, type) 40 | end 41 | 42 | Logger.highlight("Publish branch '#{branch_name}' for 'Home'") 43 | 44 | # Recover home 45 | DepService.dep_operator(path, user).recover 46 | 47 | # Push home changes to remote 48 | GitService.new.verify_push(path, "publish branch #{branch_name}", branch_name, 'Home') 49 | # Rebase Home 50 | GitService.new.verify_rebase(path, GitflowType.base_branch(type), 'Home') 51 | 52 | `open #{BigkeeperParser.home_pulls()}` 53 | ensure 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/pull.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'big_keeper/util/cache_operator' 3 | 4 | require 'big_keeper/dependency/dep_service' 5 | 6 | module BigKeeper 7 | def self.pull(path, user, type) 8 | begin 9 | # Parse Bigkeeper file 10 | BigkeeperParser.parse("#{path}/Bigkeeper") 11 | branch_name = GitOperator.new.current_branch(path) 12 | 13 | Logger.error("Not a #{GitflowType.name(type)} branch, exit.") unless branch_name.include? GitflowType.name(type) 14 | 15 | modules = ModuleCacheOperator.new(path).current_path_modules 16 | 17 | modules.each do |module_name| 18 | ModuleService.new.pull(path, user, module_name, branch_name, type) 19 | end 20 | 21 | Logger.highlight("Pull branch '#{branch_name}' for 'Home'...") 22 | GitOperator.new.pull(path) 23 | ensure 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/push.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'big_keeper/util/cache_operator' 3 | 4 | require 'big_keeper/dependency/dep_service' 5 | 6 | module BigKeeper 7 | 8 | def self.push(path, user, comment, type) 9 | begin 10 | # Parse Bigkeeper file 11 | BigkeeperParser.parse("#{path}/Bigkeeper") 12 | branch_name = GitOperator.new.current_branch(path) 13 | 14 | Logger.error("Not a #{GitflowType.name(type)} branch, exit.") unless branch_name.include? GitflowType.name(type) 15 | 16 | modules = ModuleCacheOperator.new(path).current_path_modules 17 | 18 | modules.each do |module_name| 19 | ModuleService.new.push(path, user, module_name, branch_name, type, comment) 20 | end 21 | 22 | Logger.highlight("Push branch '#{branch_name}' for 'Home'...") 23 | GitService.new.verify_push(path, comment, branch_name, 'Home') 24 | ensure 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/rebase.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'big_keeper/util/cache_operator' 3 | 4 | require 'big_keeper/dependency/dep_service' 5 | 6 | module BigKeeper 7 | def self.rebase(path, user, type) 8 | begin 9 | # Parse Bigkeeper file 10 | BigkeeperParser.parse("#{path}/Bigkeeper") 11 | branch_name = GitOperator.new.current_branch(path) 12 | 13 | Logger.error("Not a #{GitflowType.name(type)} branch, exit.") unless branch_name.include? GitflowType.name(type) 14 | 15 | modules = ModuleCacheOperator.new(path).current_path_modules 16 | 17 | modules.each do |module_name| 18 | ModuleService.new.rebase(path, user, module_name, branch_name, type) 19 | end 20 | 21 | Logger.highlight("Rebase '#{GitflowType.base_branch(type)}' "\ 22 | "to branch '#{branch_name}' for 'Home'...") 23 | 24 | # Rebase Home 25 | Logger.error("You have some changes in branch '#{branch_name}' "\ 26 | "for 'Home'. Use 'push' first please") if GitOperator.new.has_changes(path) 27 | 28 | GitService.new.verify_rebase(path, GitflowType.base_branch(type), 'Home') 29 | ensure 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/start.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'big_keeper/util/podfile_operator' 4 | require 'big_keeper/util/gitflow_operator' 5 | require 'big_keeper/util/bigkeeper_parser' 6 | require 'big_keeper/util/logger' 7 | require 'big_keeper/util/pod_operator' 8 | require 'big_keeper/util/xcode_operator' 9 | require 'big_keeper/util/cache_operator' 10 | require 'big_keeper/model/operate_type' 11 | require 'big_keeper/dependency/dep_service' 12 | 13 | require 'big_keeper/dependency/dep_type' 14 | 15 | require 'big_keeper/service/stash_service' 16 | require 'big_keeper/service/module_service' 17 | 18 | 19 | module BigKeeper 20 | def self.start(path, version, user, name, modules, type) 21 | begin 22 | # Parse Bigkeeper file 23 | BigkeeperParser.parse("#{path}/Bigkeeper") 24 | 25 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 26 | full_name = "#{version}_#{user}_#{name}" 27 | branch_name = "#{GitflowType.name(type)}/#{full_name}" 28 | 29 | GitService.new.verify_home_branch(path, branch_name, OperateType::START) 30 | 31 | stash_modules = ModuleCacheOperator.new(path).all_path_modules 32 | 33 | # Stash current branch 34 | StashService.new.stash_all(path, branch_name, user, stash_modules) 35 | 36 | # Verify input modules 37 | modules = BigkeeperParser.verify_modules(modules) 38 | 39 | Logger.highlight("Add branch '#{branch_name}' for 'Home'...") 40 | # Start home feature 41 | GitService.new.start(path, full_name, type) 42 | 43 | # Clean module cache 44 | ModuleCacheOperator.new(path).clean_modules 45 | 46 | # Cache all path modules 47 | ModuleCacheOperator.new(path).cache_path_modules(modules, modules, []) 48 | modules = ModuleCacheOperator.new(path).remain_path_modules 49 | 50 | # Backup home 51 | DepService.dep_operator(path, user).backup 52 | 53 | # Start modules feature and modify module as path 54 | modules.each do |module_name| 55 | ModuleService.new.add(path, user, module_name, full_name, type) 56 | end 57 | 58 | # install 59 | DepService.dep_operator(path, user).install(modules, OperateType::START, true) 60 | 61 | # Open home workspace 62 | DepService.dep_operator(path, user).open 63 | 64 | # Push home changes to remote 65 | Logger.highlight("Push branch '#{branch_name}' for 'Home'...") 66 | GitService.new.verify_push( 67 | path, 68 | "init #{GitflowType.name(type)} #{full_name}", 69 | branch_name, 70 | 'Home') 71 | ensure 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/switch.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'big_stash/stash_operator' 3 | require 'big_keeper/util/logger' 4 | require 'big_keeper/util/pod_operator' 5 | require 'big_keeper/util/xcode_operator' 6 | require 'big_keeper/model/operate_type' 7 | require 'big_keeper/dependency/dep_service' 8 | 9 | module BigKeeper 10 | def self.switch_to(path, version, user, full_name, type) 11 | begin 12 | # Parse Bigkeeper file 13 | BigkeeperParser.parse("#{path}/Bigkeeper") 14 | 15 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 16 | branch_name = "#{GitflowType.name(type)}/#{full_name}" 17 | 18 | GitService.new.verify_home_branch(path, branch_name, OperateType::SWITCH) 19 | 20 | stash_modules = ModuleCacheOperator.new(path).all_path_modules 21 | 22 | # Stash current branch 23 | StashService.new.stash_all(path, branch_name, user, stash_modules) 24 | 25 | # Switch to new feature 26 | GitOperator.new.checkout(path, branch_name) 27 | GitOperator.new.pull(path) 28 | 29 | # Apply home stash 30 | StashService.new.pop_stash(path, branch_name, 'Home') 31 | 32 | modules = ModuleCacheOperator.new(path).all_path_modules 33 | 34 | modules.each do |module_name| 35 | ModuleService.new.switch_to(path, user, module_name, branch_name, type) 36 | end 37 | 38 | # Install 39 | DepService.dep_operator(path, user).install(modules, OperateType::SWITCH, false) 40 | 41 | # Open home workspace 42 | DepService.dep_operator(path, user).open 43 | ensure 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/big_keeper/command/feature&hotfix/update.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | 3 | require 'big_keeper/util/podfile_operator' 4 | require 'big_keeper/util/gitflow_operator' 5 | require 'big_keeper/util/bigkeeper_parser' 6 | require 'big_keeper/util/logger' 7 | require 'big_keeper/util/pod_operator' 8 | require 'big_keeper/util/xcode_operator' 9 | require 'big_keeper/util/cache_operator' 10 | require 'big_keeper/model/operate_type' 11 | require 'big_keeper/dependency/dep_service' 12 | 13 | require 'big_keeper/dependency/dep_type' 14 | 15 | require 'big_keeper/service/stash_service' 16 | require 'big_keeper/service/module_service' 17 | 18 | 19 | module BigKeeper 20 | def self.update(path, user, modules, type) 21 | begin 22 | # Parse Bigkeeper file 23 | BigkeeperParser.parse("#{path}/Bigkeeper") 24 | branch_name = GitOperator.new.current_branch(path) 25 | 26 | Logger.error("Not a #{GitflowType.name(type)} branch, exit.") unless branch_name.include? GitflowType.name(type) 27 | 28 | full_name = branch_name.gsub(/#{GitflowType.name(type)}\//, '') 29 | 30 | current_modules = ModuleCacheOperator.new(path).current_path_modules 31 | 32 | # Verify input modules 33 | modules = BigkeeperParser.verify_modules(modules) 34 | 35 | Logger.highlight("Start to update modules for branch '#{branch_name}'...") 36 | 37 | add_modules = modules - current_modules 38 | del_modules = current_modules - modules 39 | 40 | # Clean module cache 41 | ModuleCacheOperator.new(path).clean_modules 42 | ModuleCacheOperator.new(path).cache_path_modules(modules, add_modules, del_modules) 43 | remain_path_modules = ModuleCacheOperator.new(path).remain_path_modules 44 | 45 | if add_modules.empty? and del_modules.empty? 46 | Logger.default("There is nothing changed with modules #{modules}.") 47 | else 48 | # Modify podfile as path and Start modules feature 49 | remain_path_modules.each do |module_name| 50 | ModuleService.new.add(path, user, module_name, full_name, type) 51 | end 52 | 53 | del_modules.each do |module_name| 54 | ModuleService.new.del(path, user, module_name, full_name, type) 55 | end 56 | end 57 | 58 | # Install 59 | DepService.dep_operator(path, user).install(modules, OperateType::UPDATE, false) 60 | 61 | # Open home workspace 62 | DepService.dep_operator(path, user).open 63 | ensure 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/big_keeper/command/image.rb: -------------------------------------------------------------------------------- 1 | require 'big_resources/util/image/name_analyze_util' 2 | require 'big_keeper/util/leancloud_logger' 3 | 4 | module BigKeeper 5 | def self.image_command 6 | desc 'Image operations' 7 | command :image do | c | 8 | c.desc "Detect duplicate name images." 9 | c.command :name do | name | 10 | name.action do | global_options, options, args | 11 | LeanCloudLogger.instance.set_command("image/name") 12 | 13 | path = File.expand_path(global_options[:path]) 14 | BigResources::ImageAnalyzeUtil.get_duplicate_name_file_with_type(path, BigResources::PNG) 15 | end 16 | end 17 | 18 | c.desc "Detect duplicate content images." 19 | c.command :content do | content | 20 | content.action do | global_options, options, args | 21 | LeanCloudLogger.instance.set_command("image/content") 22 | path = File.expand_path(global_options[:path]) 23 | BigResources::ImageAnalyzeUtil.get_duplicate_content_file_with_type(path, BigResources::PNG) 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/big_keeper/command/image/image.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/lib/big_keeper/command/image/image.rb -------------------------------------------------------------------------------- /lib/big_keeper/command/init.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/leancloud_logger' 2 | require 'big_keeper/util/file_operator' 3 | require 'fileutils' 4 | require 'big_keeper/util/logger' 5 | 6 | module BigKeeper 7 | def self.init_command 8 | desc 'BigKeeper file initialize' 9 | command :init do | c | 10 | c.desc "BigKeeper template file initialize." 11 | c.action do | global_options, options, args | 12 | LeanCloudLogger.instance.set_command("big/init") 13 | 14 | bin_path = File.dirname(__FILE__) 15 | bin_path = File.dirname(bin_path) 16 | bin_path = File.dirname(bin_path) 17 | bin_path = File.dirname(bin_path) 18 | path = global_options['path'] 19 | Logger.highlight("Initialize BigKeeper File...") 20 | #template path 21 | source_file = File.join(bin_path, 'resources/template/BigKeeper') 22 | #BigKeeper file need exist path 23 | target_path = File.join(path, 'BigKeeper') 24 | 25 | if !File.exists?(target_path) 26 | FileUtils.cp(source_file, target_path) 27 | Logger.highlight("Initialize BigKeeper Complete!") 28 | else 29 | Logger.highlight("BigKeeper File Has Exist!") 30 | end 31 | 32 | LeanCloudLogger.instance.set_command("file/init") 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/big_keeper/command/pod.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/command/pod/podfile' 2 | require 'big_keeper/util/leancloud_logger' 3 | 4 | module BigKeeper 5 | 6 | def self.pod_command 7 | desc 'Podfile operation' 8 | command :podfile do |podfile| 9 | podfile.desc 'Podfile' 10 | 11 | podfile.desc 'Detect podname should be locked.' 12 | podfile.command :detect do |detect| 13 | detect.action do |global_options, options, args| 14 | LeanCloudLogger.instance.set_command("podfile/detect") 15 | 16 | path = File.expand_path(global_options[:path]) 17 | podfile_detect(path) 18 | end 19 | end 20 | 21 | podfile.desc 'Lock podname should be locked.' 22 | podfile.command :lock do |lock| 23 | lock.desc 'Lock pods accouding to Podfile.' 24 | lock.command :module do |m| 25 | m.action do |global_options, options, args| 26 | LeanCloudLogger.instance.set_command("podfile/lock/module") 27 | 28 | path = File.expand_path(global_options[:path]) 29 | podfile_lock(path, false) 30 | end 31 | end 32 | lock.desc 'Lock pods accouding to Podfile.lock.' 33 | lock.command :submodule do |s| 34 | s.action do |global_options, options, args| 35 | LeanCloudLogger.instance.set_command("podfile/lock/submodule") 36 | 37 | path = File.expand_path(global_options[:path]) 38 | podfile_lock(path, true) 39 | end 40 | end 41 | 42 | end 43 | 44 | podfile.desc 'Update modules should be upgrade.' 45 | podfile.command :update do |update| 46 | update.action do |global_options, options, args| 47 | LeanCloudLogger.instance.set_command("podfile/update") 48 | 49 | path = File.expand_path(global_options[:path]) 50 | podfile_modules_update(path) 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/big_keeper/command/pod/podfile.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/podfile_detector' 2 | require 'big_keeper/util/podfile_operator' 3 | require 'big_keeper/util/podfile_module' 4 | require 'big_keeper/util/gitflow_operator' 5 | require 'big_keeper/util/bigkeeper_parser' 6 | require 'big_keeper/dependency/dep_type' 7 | require 'big_keeper/util/logger' 8 | require 'big_keeper/util/lockfile_parser' 9 | 10 | module BigKeeper 11 | 12 | def self.podfile_detect(path) 13 | # Parse Bigkeeper file 14 | BigkeeperParser.parse("#{path}/Bigkeeper") 15 | # Get modules' name 16 | # module_list = BigkeeperParser.module_names 17 | # initialize PodfileDetector 18 | detector = PodfileParser.instance 19 | detactor.parse 20 | # Get unlocked third party pods list 21 | unlock_pod_list = detector.get_unlock_pod_list 22 | # Print out unlock pod list 23 | unlock_pod_list.each do |pod_name| 24 | Logger.default("#{pod_name} should be locked.") 25 | end 26 | Logger.separator 27 | 28 | end 29 | 30 | def self.podfile_lock(path, is_all) 31 | # Parse Bigkeeper file 32 | BigkeeperParser.parse("#{path}/Bigkeeper") 33 | # initialize PodfileDetector 34 | pod_parser = PodfileParser.instance 35 | #Parser Podfile.lock 36 | pod_parser.parse(path) 37 | #initialize LockfileParser 38 | lock_parser = LockfileParser.instance 39 | #Parser Podfile.lock 40 | lock_parser.parse(path) 41 | # Get unlocked third party pods list 42 | unlock_pod_info = lock_parser.get_unlock_pod_list(is_all) 43 | # Lock modules in podfile 44 | if unlock_pod_info.empty? 45 | Logger.warning("There is nothing to be locked.") 46 | else 47 | PodfileOperator.new.find_and_lock("#{path}/Podfile", unlock_pod_info) 48 | Logger.highlight("The Podfile has been changed.") 49 | Logger.separator 50 | end 51 | 52 | end 53 | 54 | def self.podfile_modules_update(path) 55 | # Parse Bigkeeper file 56 | BigkeeperParser.parse("#{path}/Bigkeeper") 57 | # Get modules' name 58 | module_list = BigkeeperParser.module_names 59 | # initialize PodfileDetector 60 | detector = PodfileModuleDetector.new(path) 61 | # Get module latest version 62 | module_dictionary = detector.check_version_list 63 | # Check if anything should be upgrade 64 | if module_dictionary.empty? 65 | Logger.warning("There is nothing to be upgrade.") 66 | else 67 | PodfileOperator.new.find_and_upgrade("#{path}/Podfile", module_dictionary) 68 | Logger.highlight("The Podfile has been changed.") 69 | end 70 | end 71 | 72 | end 73 | -------------------------------------------------------------------------------- /lib/big_keeper/command/release.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/command/release/home' 2 | require 'big_keeper/command/release/module' 3 | require 'big_keeper/util/leancloud_logger' 4 | require 'big_keeper/command/release/start' 5 | require 'big_keeper/command/release/finish' 6 | require 'big_keeper/util/command_line_util' 7 | 8 | module BigKeeper 9 | def self.release_command 10 | desc 'Gitflow release operations' 11 | command :release do |c| 12 | 13 | c.desc 'release project start' 14 | c.command :start do |start| 15 | start.action do |global_options, options, args| 16 | path = File.expand_path(global_options[:path]) 17 | version = global_options[:ver] 18 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 19 | modules = args[(0...args.length)] if args.length > 0 20 | release_start(path, version, user, modules) 21 | end 22 | end 23 | 24 | c.desc 'release project finish' 25 | c.command :finish do |finish| 26 | finish.action do |global_options, options, args| 27 | path = File.expand_path(global_options[:path]) 28 | version = global_options[:ver] 29 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 30 | modules = args[(0...args.length)] if args.length > 0 31 | release_finish(path, version, user, modules) 32 | end 33 | end 34 | 35 | c.desc 'Release home project operations' 36 | c.command :home do |home| 37 | home.desc 'Start release home project' 38 | home.command :start do |start| 39 | start.action do |global_options, options, args| 40 | path = File.expand_path(global_options[:path]) 41 | version = global_options[:ver] 42 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 43 | LeanCloudLogger.instance.set_command("release/home/start") 44 | 45 | help_now!('user name is required') if user and user.empty? 46 | raise Logger.error("release version is required") if version == nil 47 | release_home_start(path, version, user) 48 | end 49 | end 50 | 51 | home.desc 'Finish release home project' 52 | home.command :finish do |finish| 53 | finish.action do |global_options, options, args| 54 | path = File.expand_path(global_options[:path]) 55 | version = global_options[:ver] 56 | LeanCloudLogger.instance.set_command("release/home/finish") 57 | 58 | raise Logger.error("release version is required") if version == nil 59 | release_home_finish(path, version) 60 | end 61 | end 62 | end 63 | 64 | c.desc 'release module' 65 | c.switch [:i,:ignore] 66 | c.command :module do |m| 67 | m.desc 'Start release module project' 68 | m.command :start do |start| 69 | start.action do |global_options, options, args| 70 | path = File.expand_path(global_options[:path]) 71 | version = global_options[:ver] 72 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 73 | LeanCloudLogger.instance.set_command("release/module/start") 74 | 75 | help_now!('module name is required') if args.length != 1 76 | raise Logger.error("release version is required") if version == nil 77 | module_name = args[0] 78 | release_module_start(path, version, user, module_name, options[:ignore]) 79 | end 80 | end 81 | 82 | m.desc 'finish release module project' 83 | m.switch [:s,:spec] 84 | m.command :finish do |finish| 85 | finish.action do |global_options, options, args| 86 | path = File.expand_path(global_options[:path]) 87 | version = global_options[:ver] 88 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 89 | LeanCloudLogger.instance.set_command("release/module/finish") 90 | 91 | help_now!('module name is required') if args.length != 1 92 | raise Logger.error("release version is required") if version == nil 93 | module_name = args[0] 94 | release_module_finish(path, version, user, module_name, options[:spec]) 95 | end 96 | end 97 | end 98 | end 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /lib/big_keeper/command/release/finish.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | def self.release_finish(path, version, user, modules) 3 | BigkeeperParser.parse("#{path}/Bigkeeper") 4 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 5 | modules = release_check_changed_modules(path, user) if (modules.nil? || modules.empty?) 6 | 7 | if modules.nil? || modules.empty? 8 | Logger.error('no module need to release') 9 | end 10 | if !CommandLineUtil.double_check("module #{modules} will changed version to #{version}, are you sure?") 11 | Logger.error('release finish interrupt') 12 | end 13 | #stash home 14 | StashService.new.stash(path, GitOperator.new.current_branch(path), 'home') 15 | # delete cache 16 | CacheOperator.new(path).clean() 17 | # checkout develop 18 | GitService.new.verify_checkout_pull(path, 'develop') 19 | 20 | modules.each do |module_name| 21 | Logger.highlight("release start module #{module_name}") 22 | ModuleService.new.release_finish(path, user, modules, module_name, version) 23 | end 24 | 25 | #release home 26 | DepService.dep_operator(path, user).release_home_finish(modules, version) 27 | 28 | # Push home changes to remote 29 | Logger.highlight("Push branch 'develop' for 'Home'...") 30 | GitService.new.verify_push( 31 | path, 32 | "release finish for #{version}", 33 | 'develop', 34 | 'Home') 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/big_keeper/command/release/home.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'big_keeper/util/podfile_operator' 3 | require 'big_keeper/util/gitflow_operator' 4 | require 'big_keeper/dependency/dep_type' 5 | require 'big_keeper/util/info_plist_operator' 6 | require 'big_keeper/util/logger' 7 | require 'big_keeper/util/xcode_operator' 8 | require 'big_keeper/model/operate_type' 9 | 10 | module BigKeeper 11 | def self.release_home_start(path, version, user) 12 | BigkeeperParser.parse("#{path}/Bigkeeper") 13 | 14 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 15 | modules = BigkeeperParser.module_names 16 | 17 | #stash 18 | StashService.new.stash_all(path, GitOperator.new.current_branch(path), user, modules) 19 | 20 | # delete cache 21 | CacheOperator.new(path).clean() 22 | # cache Podfile 23 | CacheOperator.new(path).save('Podfile') 24 | 25 | # check 26 | GitOperator.new.check_diff(path, "develop", "master") 27 | 28 | #checkout release branch 29 | Logger.highlight(%Q(Start to checkout Branch release/#{version})) 30 | if GitOperator.new.current_branch(path) != "release/#{version}" 31 | if GitOperator.new.has_branch(path, "release/#{version}") 32 | GitOperator.new.checkout(path, "release/#{version}") 33 | else 34 | GitflowOperator.new.start(path, version, GitflowType::RELEASE) 35 | GitOperator.new.push_to_remote(path, "release/#{version}") 36 | end 37 | end 38 | 39 | Logger.highlight(%Q(Start to release/#{version})) 40 | # step 2 replace_modules 41 | PodfileOperator.new.replace_all_module_release(path, 42 | user, 43 | modules, 44 | ModuleOperateType::RELEASE) 45 | 46 | # step 3 change Info.plist value 47 | InfoPlistOperator.new.change_version_build(path, version) 48 | 49 | GitService.new.verify_push(path, "Change version to #{version}", "release/#{version}", 'Home') 50 | DepService.dep_operator(path, user).install(modules, OperateType::RELEASE, true) 51 | XcodeOperator.open_workspace(path) 52 | end 53 | 54 | def self.release_home_finish(path, version) 55 | BigkeeperParser.parse("#{path}/Bigkeeper") 56 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 57 | Logger.highlight("Start finish release home for #{version}") 58 | 59 | if GitOperator.new.has_branch(path, "release/#{version}") 60 | if GitOperator.new.current_branch(path) != "release/#{version}" 61 | GitOperator.new.checkout(path, "release/#{version}") 62 | end 63 | 64 | GitService.new.verify_push(path, "finish release branch", "release/#{version}", 'Home') 65 | 66 | # master 67 | GitOperator.new.checkout(path, "master") 68 | GitOperator.new.merge(path, "release/#{version}") 69 | GitService.new.verify_push(path, "release V#{version}", "master", 'Home') 70 | 71 | GitOperator.new.tag(path, version) 72 | 73 | # release branch 74 | GitOperator.new.checkout(path, "release/#{version}") 75 | CacheOperator.new(path).load('Podfile') 76 | CacheOperator.new(path).clean() 77 | GitOperator.new.commit(path, "reset #{version} Podfile") 78 | GitService.new.verify_push(path, "reset #{version} Podfile", "release/#{version}", 'Home') 79 | 80 | # develop 81 | GitOperator.new.checkout(path, "develop") 82 | GitOperator.new.merge(path, "release/#{version}") 83 | GitService.new.verify_push(path, "merge release/#{version} to develop", "develop", 'Home') 84 | GitOperator.new.check_diff(path, "develop", "master") 85 | 86 | Logger.highlight("Finish release home for #{version}") 87 | else 88 | raise Logger.error("There is no release/#{version} branch, please use release home start first.") 89 | end 90 | end 91 | 92 | end 93 | -------------------------------------------------------------------------------- /lib/big_keeper/command/release/module.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'big_keeper/util/podfile_operator' 3 | require 'big_keeper/util/gitflow_operator' 4 | require 'big_keeper/dependency/dep_type' 5 | require 'big_keeper/util/info_plist_operator' 6 | require 'big_keeper/util/git_operator' 7 | require 'big_keeper/util/logger' 8 | require 'big_keeper/util/pod_operator' 9 | 10 | module BigKeeper 11 | def self.release_module_start(path, version, user, module_name, ignore) 12 | BigkeeperParser.parse("#{path}/Bigkeeper") 13 | 14 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 15 | module_path = BigkeeperParser.module_full_path(path, user, module_name) 16 | 17 | # stash 18 | StashService.new.stash(module_path, GitOperator.new.current_branch(module_path), module_name) 19 | 20 | #check 21 | if ignore != true 22 | GitOperator.new.check_merge(module_path, "feature/#{version}") 23 | GitOperator.new.check_diff(module_path, "develop", "master") 24 | Logger.highlight(%Q(#{module_name} release check finish)) 25 | end 26 | 27 | # checkout to develop branch 28 | Logger.highlight(%Q(Start checkout #{module_name} to Branch develop)) 29 | GitService.new.verify_checkout_pull(module_path, "develop") 30 | 31 | Logger.highlight(%Q(#{module_name} release start finish)) 32 | end 33 | 34 | ## release finish 35 | def self.release_module_finish(path, version, user, module_name, spec) 36 | BigkeeperParser.parse("#{path}/Bigkeeper") 37 | 38 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 39 | module_path = BigkeeperParser.module_full_path(path, user, module_name) 40 | 41 | # check commit 42 | Logger.error("current branch has unpush files") if GitOperator.new.has_changes(module_path) 43 | 44 | #修改 podspec 文件 45 | # TO DO: - advanced to use Regular Expression 46 | has_change = PodfileOperator.new.podspec_change(%Q(#{module_path}/#{module_name}.podspec), version, module_name) 47 | GitService.new.verify_push(module_path, "Change version number", "develop", "#{module_name}") if has_change == true 48 | 49 | # check out master 50 | Logger.highlight("'#{module_name}' checkout branch to master...") 51 | GitService.new.verify_checkout_pull(module_path, "master") 52 | 53 | Logger.highlight(%Q(Merge develop to master)) 54 | # merge develop to master 55 | GitOperator.new.merge(module_path, "develop") 56 | GitOperator.new.push_to_remote(module_path, "master") 57 | 58 | GitOperator.new.tag(module_path, version) 59 | # pod repo push 60 | if spec == true 61 | PodOperator.pod_repo_push(module_path, module_name, BigkeeperParser.source_spec_path(module_name), version) 62 | end 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /lib/big_keeper/command/release/publish.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | def self.publish(path, version, user) 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /lib/big_keeper/command/release/start.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | def self.release_start(path, version, user, modules) 3 | BigkeeperParser.parse("#{path}/Bigkeeper") 4 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 5 | modules = release_check_changed_modules(path, user) if (modules.nil? || modules.empty?) 6 | 7 | if modules.nil? || modules.empty? 8 | Logger.error('no module need to release') 9 | end 10 | 11 | if !CommandLineUtil.double_check("module #{modules} will changed version to #{version}-SNAPSHOT, are you sure?") 12 | Logger.error('release start interrupt') 13 | end 14 | 15 | #stash home 16 | StashService.new.stash(path, GitOperator.new.current_branch(path), 'home') 17 | # delete cache 18 | CacheOperator.new(path).clean() 19 | # checkout develop 20 | GitService.new.verify_checkout_pull(path, 'develop') 21 | 22 | modules.each do |module_name| 23 | Logger.highlight("release start module #{module_name}") 24 | ModuleService.new.release_start(path, user, modules, module_name, version) 25 | end 26 | 27 | #release home 28 | DepService.dep_operator(path, user).release_home_start(modules, version) 29 | 30 | # Push home changes to remote 31 | Logger.highlight("Push branch 'develop' for 'Home'...") 32 | GitService.new.verify_push( 33 | path, 34 | "release start for #{version}", 35 | 'develop', 36 | 'Home') 37 | end 38 | 39 | def self.release_finish(path, version, user, modules) 40 | BigkeeperParser.parse("#{path}/Bigkeeper") 41 | version = BigkeeperParser.version if version == 'Version in Bigkeeper file' 42 | 43 | #stash home 44 | StashService.new.stash(path, GitOperator.new.current_branch(path), 'home') 45 | # delete cache 46 | CacheOperator.new(path).clean() 47 | # checkout develop 48 | GitService.new.verify_checkout_pull(path, 'develop') 49 | 50 | modules.each do |module_name| 51 | Logger.highlight("release start module #{module_name}") 52 | ModuleService.new.release_finish(path, user, modules, module_name, version) 53 | end 54 | 55 | #release home 56 | DepService.dep_operator(path, user).release_home_finish(modules, version) 57 | 58 | # Push home changes to remote 59 | Logger.highlight("Push branch 'develop' for 'Home'...") 60 | GitService.new.verify_push( 61 | path, 62 | "release finish for #{version}", 63 | 'develop', 64 | 'Home') 65 | end 66 | 67 | def self.release_check_changed_modules(path, user) 68 | changed_modules = [] 69 | BigkeeperParser.parse("#{path}/Bigkeeper") 70 | allModules = BigkeeperParser.module_names 71 | allModules.each do |module_name| 72 | if ModuleService.new.release_check_changed(path, user, module_name) 73 | changed_modules << module_name 74 | end 75 | end 76 | changed_modules 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/command/spec/analyze' 2 | require 'big_keeper/command/spec/list' 3 | require 'big_keeper/command/spec/add' 4 | require 'big_keeper/command/spec/delete' 5 | require 'big_keeper/command/spec/search' 6 | require 'big_keeper/command/spec/sync' 7 | require 'big_keeper/util/leancloud_logger' 8 | 9 | module BigKeeper 10 | 11 | def self.spec_command 12 | desc 'Spec operations' 13 | 14 | command :spec do |spec| 15 | spec.switch [:a,:all] 16 | spec.desc 'Analyze spec dependency infomation.' 17 | spec.command :analyze do |analyze| 18 | analyze.action do |global_options, options, args| 19 | LeanCloudLogger.instance.set_command("spec/analyze") 20 | 21 | path = File.expand_path(global_options[:path]) 22 | is_all = options[:all] 23 | module_names = args 24 | spec_analyze(path, is_all, module_names) 25 | end 26 | end 27 | 28 | spec.desc 'List all the specs.' 29 | spec.command :list do | list | 30 | list.action do |global_options, options, args| 31 | LeanCloudLogger.instance.set_command("spec/list") 32 | 33 | path = File.expand_path(global_options[:path]) 34 | version = global_options[:ver] 35 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 36 | 37 | spec_list(path, user, options) 38 | end 39 | end 40 | 41 | spec.desc 'Sync Module dependency from Home.' 42 | spec.command :sync do | sync| 43 | sync.action do |global_options, options, args| 44 | LeanCloudLogger.instance.set_command("spec/sync") 45 | 46 | path = File.expand_path(global_options[:path]) 47 | version = global_options[:ver] 48 | user = global_options[:user].gsub(/[^0-9A-Za-z]/, '').downcase 49 | module_name = args 50 | 51 | spec_sync(path, version, user, module_name) 52 | end 53 | end 54 | 55 | spec.desc 'Add a spec (Coming soon).' 56 | spec.command :add do |add| 57 | add.action do 58 | spec_add() 59 | end 60 | end 61 | 62 | spec.desc 'Delete a spec (Coming soon).' 63 | spec.command :delete do |delete| 64 | delete.action do 65 | spec_delete() 66 | end 67 | end 68 | 69 | spec.desc 'Search a spec with name (Coming soon).' 70 | spec.command :search do |search| 71 | search.action do 72 | spec_search() 73 | end 74 | end 75 | 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec/add.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/dependency/dep_type' 3 | require 'big_keeper/util/logger' 4 | 5 | module BigKeeper 6 | def self.spec_add 7 | Logger.default('Coming soon.') 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec/analyze.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/dependency/dep_type' 3 | require 'big_keeper/util/logger' 4 | require 'big_keeper/model/library_model' 5 | 6 | module BigKeeper 7 | def self.spec_analyze(path,is_all,find_module_names) 8 | # Parse Bigkeeper file 9 | BigkeeperParser.parse("#{path}/Bigkeeper") 10 | 11 | is_default = !is_all&&find_module_names.size==0 12 | if is_all && find_module_names.size>0 13 | Logger.error("parameter conflict: [--all] | [module_names]") 14 | return 15 | end 16 | Logger.highlight('Start spec analyze...') 17 | Logger.default(Time.now.to_s) 18 | 19 | # Parse Bigkeeper file 20 | # BigkeeperParser.parse("#{path}/Bigkeeper") 21 | # module_names = BigkeeperParser.module_names 22 | 23 | # find modules 24 | Logger.highlight('Get all modules...') 25 | module_names = [] 26 | pod_path = path+"/Pods/" 27 | dir = Dir.open(pod_path) 28 | dir.each do |dir_name| 29 | if !dir_name.include?(".") && dir_name != "Headers" && dir_name != "Local Podspecs" && dir_name != "Target Support Files" 30 | module_names[module_names.size]=dir_name 31 | end 32 | end 33 | 34 | for input_moudle_name in find_module_names do 35 | if !module_names.include?(input_moudle_name) 36 | Logger.error("["+input_moudle_name+"] not exist.") 37 | end 38 | end 39 | 40 | # setup modules 41 | module_list = [] 42 | module_keyword_map = Hash.new 43 | file_count = 0 44 | for module_name in module_names do 45 | library = LibraryModel.new(module_name) 46 | library.get_all_public_file(path) 47 | module_list[module_list.size]=library 48 | module_keyword_map[module_name]=library.keyword_list 49 | if is_all || find_module_names.include?(library.name) 50 | file_count = file_count + library.file_list.size 51 | end 52 | end 53 | # analyze modules spec 54 | 55 | Logger.highlight('Analyze modules...') 56 | Logger.default(Time.now.to_s) 57 | file_index = 0 58 | for library in module_list do 59 | if is_all || find_module_names.include?(library.name) 60 | Logger.default('Analyzing ' + library.name) 61 | file_index = file_index + library.file_list.size 62 | library.spec_dependece_library(module_keyword_map.clone)#(Hash.new(module_keyword_map)).to_hash) 63 | progress = (file_index*100.0)/file_count 64 | progress = format("%.02f", progress).to_f 65 | Logger.default('progress >>>> ' + String(progress) + '% [' + library.name + ' done] ') 66 | end 67 | end 68 | Logger.highlight('Analyze complete.') 69 | Logger.default(Time.now.to_s) 70 | 71 | # log spec info 72 | for library in module_list do 73 | if is_all || find_module_names.include?(library.name) 74 | Logger.highlight("\n-"+library.name+":") 75 | for spec_library in library.spec_library do 76 | puts " -"+spec_library 77 | end 78 | end 79 | end 80 | 81 | # save cache to file 82 | if is_all 83 | 84 | end 85 | 86 | end 87 | 88 | end 89 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec/delete.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/dependency/dep_type' 3 | require 'big_keeper/util/logger' 4 | 5 | module BigKeeper 6 | def self.spec_delete 7 | Logger.default('Coming soon.') 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec/list.rb: -------------------------------------------------------------------------------- 1 | require 'logger' 2 | require 'json' 3 | 4 | module BigKeeper 5 | def self.spec_list(path, user, options) 6 | BigkeeperParser.parse("#{path}/Bigkeeper") 7 | 8 | module_dic = BigkeeperParser.parse_modules 9 | module_list = Array.new 10 | module_dic.keys.each do | key | 11 | dic = Hash["module_name" => key, 12 | "git" => module_dic[key][:git], 13 | "pulls" => module_dic[key][:pulls]] 14 | module_list << dic 15 | end 16 | json = JSON.pretty_generate(module_list) 17 | puts json 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec/search.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/dependency/dep_type' 3 | require 'big_keeper/util/logger' 4 | 5 | module BigKeeper 6 | def self.spec_search 7 | Logger.default('Coming soon.') 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/big_keeper/command/spec/sync.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/dependency/dep_type' 3 | require 'big_keeper/util/logger' 4 | 5 | module BigKeeper 6 | def self.spec_sync(path, version, user, module_name) 7 | # Parse Bigkeeper file 8 | BigkeeperParser.parse("#{path}/Bigkeeper") 9 | 10 | Logger.default('Coming soon.') 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/big_keeper/dependency/dep_gradle_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/dependency/dep_operator' 2 | require 'big_keeper/util/gradle_module_operator' 3 | require 'big_keeper/util/gradle_file_operator' 4 | require 'big_keeper/model/operate_type' 5 | require 'big_keeper/util/version_config_operator' 6 | 7 | module BigKeeper 8 | # Operator for podfile 9 | class DepGradleOperator < DepOperator 10 | 11 | BUILD_GRADLE = "app/build.gradle" 12 | SETTINGS_GRADLE = "settings.gradle" 13 | 14 | def backup 15 | end 16 | 17 | def recover 18 | build_file = "#{@path}/#{BUILD_GRADLE}" 19 | settings_file = "#{@path}/#{SETTINGS_GRADLE}" 20 | GradleFileOperator.new(@path, @user).recover_bigkeeper_config_file(build_file) 21 | GradleFileOperator.new(@path, @user).recover_bigkeeper_config_file(settings_file) 22 | 23 | cache_operator = CacheOperator.new(@path) 24 | cache_operator.clean 25 | end 26 | 27 | def update_module_config(module_name, module_operate_type) 28 | if ModuleOperateType::ADD == module_operate_type 29 | GradleModuleOperator.new(@path, @user, module_name).update_module(ModuleOperateType::ADD) 30 | elsif ModuleOperateType::DELETE == module_operate_type 31 | GradleModuleOperator.new(@path, @user, module_name).recover() 32 | elsif ModuleOperateType::FINISH == module_operate_type 33 | GradleModuleOperator.new(@path, @user, module_name).update_module(ModuleOperateType::FINISH) 34 | elsif ModuleOperateType::PUBLISH == module_operate_type 35 | GradleModuleOperator.new(@path, @user, module_name).recover() 36 | end 37 | end 38 | 39 | def install(modules, type, should_update) 40 | if OperateType::START == type || OperateType::UPDATE == type || OperateType::SWITCH == type || OperateType::FINISH == type 41 | GradleFileOperator.new(@path, @user).update_home_depends("#{@path}/#{BUILD_GRADLE}", "#{@path}/#{SETTINGS_GRADLE}",type) 42 | elsif OperateType::PUBLISH == type 43 | recover() 44 | end 45 | end 46 | 47 | def release_module_start(modules, module_name, version) 48 | module_full_path = BigkeeperParser.module_full_path(@path, @user, module_name) 49 | version_config_file = "#{module_full_path}/doc/config/version-config.gradle" 50 | version = "#{version}-SNAPSHOT" unless version.include?'SNAPSHOT' 51 | VersionConfigOperator.change_version(version_config_file, modules, version) 52 | end 53 | 54 | def release_module_finish(modules, module_name, version) 55 | module_full_path = BigkeeperParser.module_full_path(@path, @user, module_name) 56 | version_config_file = "#{module_full_path}/doc/config/version-config.gradle" 57 | VersionConfigOperator.change_version(version_config_file, modules, version) 58 | end 59 | 60 | def release_home_start(modules, version) 61 | version_config_file = "#{@path}/doc/config/version-config.gradle" 62 | version = "#{version}-SNAPSHOT" unless version.include?'SNAPSHOT' 63 | VersionConfigOperator.change_version(version_config_file, modules, version) 64 | end 65 | 66 | def release_home_finish(modules, version) 67 | version_config_file = "#{@path}/doc/config/version-config.gradle" 68 | VersionConfigOperator.change_version(version_config_file, modules, version) 69 | end 70 | 71 | def open 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/big_keeper/dependency/dep_operator.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | # Operator for podfile 3 | class DepOperator 4 | @path 5 | @user 6 | 7 | def initialize(path, user) 8 | @path = path 9 | @user = user 10 | end 11 | 12 | def backup 13 | raise "You should override this method in subclass." 14 | end 15 | 16 | def recover 17 | raise "You should override this method in subclass." 18 | end 19 | 20 | def update_module_config(module_name, module_operate_type) 21 | raise "You should override this method in subclass." 22 | end 23 | 24 | def release_module_start(modules, module_name, version) 25 | raise "You should override this method in subclass." 26 | end 27 | 28 | def release_module_finish(modules, module_name, version) 29 | raise "You should override this method in subclass." 30 | end 31 | 32 | def release_home_start(modules, version) 33 | raise "You should override this method in subclass." 34 | end 35 | 36 | def release_home_finish(modules, version) 37 | raise "You should override this method in subclass." 38 | end 39 | 40 | def install(modules, type, should_update) 41 | raise "You should override this method in subclass." 42 | end 43 | 44 | def open 45 | raise "You should override this method in subclass." 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/big_keeper/dependency/dep_pod_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/dependency/dep_operator' 2 | 3 | require 'big_keeper/util/pod_operator' 4 | require 'big_keeper/util/xcode_operator' 5 | require 'big_keeper/util/cache_operator' 6 | require 'big_keeper/util/file_operator' 7 | 8 | module BigKeeper 9 | # Operator for podfile 10 | class DepPodOperator < DepOperator 11 | def backup 12 | CacheOperator.new(@path).save('Podfile') 13 | end 14 | 15 | def recover 16 | cache_operator = CacheOperator.new(@path) 17 | cache_operator.load('Podfile') 18 | cache_operator.clean 19 | end 20 | 21 | def update_module_config(module_name, module_operate_type) 22 | file = "#{@path}/Podfile" 23 | temp_file = Tempfile.new('.Podfile.tmp', :encoding => 'UTF-8') 24 | 25 | begin 26 | File.open(file, 'r', :encoding => 'UTF-8') do |file| 27 | file.each_line do |line| 28 | temp_file.puts generate_module_config(line, module_name, module_operate_type) 29 | end 30 | end 31 | temp_file.close 32 | FileUtils.mv(temp_file.path, file) 33 | ensure 34 | temp_file.close 35 | temp_file.unlink 36 | end 37 | end 38 | 39 | def install(modules, type, should_update) 40 | PodOperator.pod_install(@path, should_update) 41 | end 42 | 43 | def open 44 | XcodeOperator.open_workspace(@path) 45 | end 46 | 47 | def generate_module_config(line, module_name, module_operate_type) 48 | line.sub(/(\s*)pod(\s*)('|")#{module_name}((\/[_a-zA-Z0-9]+)?)('|")([\s\S]*)/){ 49 | if ModuleOperateType::ADD == module_operate_type 50 | module_path = BigkeeperParser.module_path(@user, module_name) 51 | "#{$1}pod '#{module_name}#{$4}', :path => '#{module_path}'" 52 | elsif ModuleOperateType::DELETE == module_operate_type 53 | origin_config_of_module = origin_config_of_module(module_name) 54 | if origin_config_of_module.empty? 55 | line 56 | else 57 | origin_config_of_module 58 | end 59 | elsif ModuleOperateType::FINISH == module_operate_type 60 | module_git = BigkeeperParser.module_git(module_name) 61 | branch_name = GitOperator.new.current_branch(@path) 62 | "#{$1}pod '#{module_name}#{$4}', :git => '#{module_git}', :branch => '#{branch_name}'" 63 | elsif ModuleOperateType::PUBLISH == module_operate_type 64 | module_git = BigkeeperParser.module_git(module_name) 65 | branch_name = GitOperator.new.current_branch(@path) 66 | base_branch_name = GitflowType.base_branch(GitService.new.current_branch_type(@path)) 67 | "#{$1}pod '#{module_name}#{$4}', :git => '#{module_git}', :branch => '#{base_branch_name}'" 68 | elsif ModuleOperateType::RELEASE == module_operate_type 69 | module_git = BigkeeperParser.module_git(module_name) 70 | lastest_tag, is_spec = find_lastest_tag(module_name) 71 | if is_spec == true 72 | Logger.default("#{module_name} lastest tag is #{lastest_tag}, this tag has published.") 73 | "#{$1}pod '#{module_name}#{$4}', '#{lastest_tag}'" 74 | else 75 | Logger.default("#{module_name} lastest tag is #{lastest_tag}, this tag not publish.") 76 | "#{$1}pod '#{module_name}#{$4}', :git => '#{module_git}', :tag => '#{lastest_tag}'" 77 | end 78 | else 79 | line 80 | end 81 | } 82 | end 83 | 84 | def origin_config_of_module(module_name) 85 | origin_config = '' 86 | 87 | File.open("#{@path}/.bigkeeper/Podfile", 'r', :encoding => 'UTF-8') do |file| 88 | file.each_line do |line| 89 | if line =~ /(\s*)pod(\s*)('|")#{module_name}('|")([\s\S]*)/ 90 | origin_config = line 91 | break 92 | end 93 | end 94 | end 95 | 96 | origin_config.chop 97 | end 98 | 99 | def find_lastest_tag(module_name) 100 | username = FileOperator.new.current_username 101 | tags_repos_pwd = Array.new 102 | tags_spec_list = Array.new 103 | tags_module_list = Array.new 104 | 105 | IO.popen("find /Users/#{username}/.cocoapods/repos -type d -name #{module_name}") do |io| 106 | io.each do |line| 107 | tags_repos_pwd.push(line) if line.include? "#{module_name}" 108 | end 109 | end 110 | for pwd in tags_repos_pwd do 111 | path = pwd.chomp 112 | IO.popen("cd '#{path}'; ls") do |io| 113 | io.each do |line| 114 | tags_spec_list.push(line) 115 | end 116 | end 117 | end 118 | 119 | tags_module_list = GitOperator.new.tag_list(BigkeeperParser.module_full_path(@path, @user, module_name)) 120 | last_tag = tags_module_list[tags_module_list.length - 1] 121 | if tags_module_list.include?(last_tag) && tags_spec_list.include?(last_tag) 122 | return [last_tag.chomp, true] 123 | else 124 | return [last_tag.chomp, false] 125 | end 126 | end 127 | 128 | private :generate_module_config, :origin_config_of_module, :find_lastest_tag 129 | end 130 | end 131 | -------------------------------------------------------------------------------- /lib/big_keeper/dependency/dep_service.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/git_operator' 2 | require 'big_keeper/model/gitflow_type' 3 | require 'big_keeper/model/operate_type' 4 | require 'big_keeper/util/logger' 5 | require 'big_keeper/dependency/dep_type' 6 | 7 | module BigKeeper 8 | # Operator for podfile 9 | class DepService 10 | def self.dep_operator(path, user) 11 | DepType.operator(path, user) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/big_keeper/dependency/dep_type.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/dependency/dep_operator' 2 | require 'big_keeper/dependency/dep_pod_operator' 3 | require 'big_keeper/dependency/dep_gradle_operator' 4 | require 'big_keeper/util/file_operator' 5 | 6 | module BigKeeper 7 | module DepType 8 | NONE = 0 9 | COCOAPODS = 1 10 | GRADLE = 2 11 | 12 | def self.type(path) 13 | if FileOperator.definitely_exists?("#{path}/Podfile") 14 | COCOAPODS 15 | elsif FileOperator.definitely_exists?("#{path}/build.gradle") 16 | GRADLE 17 | else 18 | NONE 19 | end 20 | end 21 | 22 | def self.operator(path, user) 23 | operator_type = type(path) 24 | if COCOAPODS == operator_type 25 | DepPodOperator.new(path, user) 26 | elsif GRADLE == operator_type 27 | DepGradleOperator.new(path, user) 28 | else 29 | DepOperator.new(path, user) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/big_keeper/model/gitflow_type.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | module GitflowType 3 | FEATURE = 1 4 | HOTFIX = 2 5 | RELEASE = 3 6 | 7 | def self.name(type) 8 | if FEATURE == type 9 | "feature" 10 | elsif HOTFIX == type 11 | "hotfix" 12 | elsif RELEASE == type 13 | "release" 14 | else 15 | "feature" 16 | end 17 | end 18 | 19 | def self.command(type) 20 | if FEATURE == type 21 | :feature 22 | elsif HOTFIX == type 23 | :hotfix 24 | elsif RELEASE == type 25 | :release 26 | else 27 | :feature 28 | end 29 | end 30 | 31 | def self.base_branch(type) 32 | if FEATURE == type 33 | "develop" 34 | elsif HOTFIX == type 35 | "master" 36 | elsif RELEASE == type 37 | "develop" 38 | else 39 | "master" 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/big_keeper/model/library_model.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/file_operator' 2 | require 'big_keeper/util/code_operator' 3 | 4 | 5 | module BigKeeper 6 | class LibraryModel 7 | attr_accessor :name, :file_list, :header_file_list, :keyword_list, :spec_library 8 | def initialize(name) 9 | @name = name 10 | @file_list = [] 11 | @header_file_list = [] 12 | @spec_library = [] 13 | @keyword_list = [] 14 | end 15 | 16 | def get_all_public_file(path) 17 | all_header = FileOperator.find_all_header_file("#{path}/Pods/#{@name}") 18 | for file_path in all_header do 19 | @header_file_list[@header_file_list.size] = file_path 20 | file_name = File.basename(file_path) 21 | @keyword_list[@keyword_list.size] = file_name 22 | in_note = false 23 | File.foreach(file_path) { |line| 24 | hash = Hash.new 25 | hash["in_note"]=in_note 26 | hash["line"]=line 27 | OCCodeOperator.in_note_code(hash) 28 | in_note = hash["in_note"] 29 | line = hash["line"] 30 | if line.empty? 31 | next 32 | end 33 | if line.include?("@interface ") 34 | line[0,line.index("@interface ")+11]="" 35 | column = line.split(/:/) 36 | if column.size > 1 37 | class_name = column[0] 38 | if class_name.include?("<") 39 | class_name = class_name[0, class_name.index("<")] 40 | end 41 | class_name = class_name.strip 42 | if (@keyword_list.include?(class_name+".h")) 43 | @keyword_list.delete(class_name+".h") 44 | end 45 | @keyword_list[@keyword_list.size] = class_name 46 | end 47 | end 48 | } 49 | end 50 | # if @name == "" 51 | # puts @keyword_list 52 | # end 53 | @file_list = FileOperator.find_all_code_file("#{path}/Pods/#{@name}") 54 | end 55 | 56 | def spec_dependece_library(library_keywords_hash) 57 | if library_keywords_hash.include?(@name) 58 | library_keywords_hash.delete(@name) 59 | end 60 | 61 | for file_path in @file_list do 62 | # note for coding 63 | in_note = false 64 | File.foreach(file_path) { |line| 65 | hash = Hash.new 66 | hash["in_note"]=in_note 67 | hash["line"]=line 68 | OCCodeOperator.in_note_code(hash) 69 | in_note = hash["in_note"] 70 | line = hash["line"] 71 | if line.empty? 72 | next 73 | end 74 | library_keywords_hash.each {|library_name, keyword_list| 75 | is_dependence = false 76 | tip = "" 77 | for keyword in keyword_list do 78 | if line.include?(keyword) 79 | last_char = '.' 80 | last_index = line.index(keyword)-1 81 | if last_index >= 0 82 | last_char = line[last_index] 83 | end 84 | next_char = '.' 85 | next_index = line.index(keyword)+keyword.size 86 | if next_index < line.size 87 | next_char = line[next_index] 88 | end 89 | if !(((next_char<='z'&&next_char>='a')||(next_char<='Z'&&next_char>='A')||(next_char<='9'&&next_char>='0')||next_char=='_')||((last_char<='z'&&last_char>='a')||(last_char<='Z'&&last_char>='A')||(last_char<='9'&&last_char>='0')||last_char=='_')) 90 | if keyword.include?(".h") && line.include?("import") && line.include?("/"+keyword+">") 91 | dependence_library_name = line[line.index("<")+1...line.index("/"+keyword+">")] 92 | if dependence_library_name == library_name 93 | tip = " [file]:"+File.basename(file_path)+" [line]: "+line.strip+" [keyword]: "+keyword 94 | is_dependence = true 95 | break 96 | end 97 | else 98 | tip = " [file]:"+File.basename(file_path)+" [line]: "+line.strip+" [keyword]: "+keyword 99 | is_dependence = true 100 | break 101 | end 102 | end 103 | end 104 | end 105 | if is_dependence 106 | @spec_library[@spec_library.size] = library_name+tip 107 | library_keywords_hash.delete(library_name) 108 | end 109 | } 110 | 111 | } 112 | end 113 | end 114 | 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /lib/big_keeper/model/operate_type.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | module OperateType 3 | START = 1 4 | UPDATE = 2 5 | SWITCH = 3 6 | FINISH = 4 7 | PUBLISH = 5 8 | RELEASE = 6 9 | 10 | def self.name(type) 11 | if START == type 12 | "start" 13 | elsif UPDATE == type 14 | "update" 15 | elsif SWITCH == type 16 | "switch" 17 | else 18 | name 19 | end 20 | end 21 | end 22 | 23 | module ModuleOperateType 24 | ADD = 1 25 | DELETE = 2 26 | FINISH = 3 27 | PUBLISH = 4 28 | RELEASE = 5 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/big_keeper/model/podfile_model.rb: -------------------------------------------------------------------------------- 1 | 2 | module BigKeeper 3 | 4 | class PodfileModel 5 | attr_accessor :name, :git, :path, :configurations, :branch,:tag, :comment 6 | def initialize(sentence) 7 | if sentence.include?('#') 8 | list = sentence.split('#') 9 | @comment = list[1] 10 | sentence = list[0] 11 | end 12 | 13 | sentence_slip_list = sentence.split(',') 14 | return if sentence_slip_list.size.zero? 15 | for piece in sentence_slip_list do 16 | if /:git =>/ =~ piece 17 | @git = $~.post_match.strip 18 | elsif /:path =>/ =~ piece 19 | @path = $~.post_match.strip 20 | elsif /:configurations =>/ =~ piece 21 | @configurations = $~.post_match.strip 22 | elsif /:branch =>/ =~ piece 23 | @branch = $~.post_match.strip 24 | elsif /:tag =>/ =~ piece 25 | @tag = $~.post_match.strip 26 | elsif /pod /=~ piece 27 | @name = $~.post_match.delete("'\n ") 28 | end 29 | # p %Q{model name:#{@name},git:#{@git},path:#{@path},config:#{@configurations},branch:#{@branch},tag:#{@tag},comment:#{@comment}} 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/big_keeper/service/git_service.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/git_operator' 2 | require 'big_keeper/util/gitflow_operator' 3 | require 'big_keeper/model/gitflow_type' 4 | require 'big_keeper/model/operate_type' 5 | require 'big_keeper/util/logger' 6 | 7 | module BigKeeper 8 | # Operator for got 9 | class GitService 10 | def start(path, name, type) 11 | git = GitOperator.new 12 | 13 | branch_name = "#{GitflowType.name(type)}/#{name}" 14 | if !git.has_remote_branch(path, branch_name) && !git.has_local_branch(path, branch_name) 15 | 16 | verify_special_branch(path, 'master') 17 | verify_special_branch(path, 'develop') 18 | 19 | GitflowOperator.new.verify_git_flow(path) 20 | 21 | GitflowOperator.new.start(path, name, type) 22 | git.push_to_remote(path, branch_name) 23 | else 24 | verify_checkout(path, branch_name) 25 | 26 | if !git.has_remote_branch(path, branch_name) 27 | git.push_to_remote(path, branch_name) 28 | end 29 | end 30 | end 31 | 32 | def verify_checkout(path, branch_name) 33 | Dir.chdir(path) do 34 | cmd = "git checkout -b #{branch_name}" 35 | if GitOperator.new.has_branch(path, branch_name) 36 | cmd = "git checkout #{branch_name}" 37 | end 38 | IO.popen(cmd) do |io| 39 | io.each do |line| 40 | Logger.error("Checkout #{branch_name} failed.") if line.include? 'error' 41 | end 42 | end 43 | end 44 | end 45 | 46 | def verify_checkout_pull(path, branch_name) 47 | GitService.new.verify_checkout(path, branch_name) 48 | GitService.new.pull(path, branch_name) 49 | end 50 | 51 | def verify_special_branch(path, name) 52 | git = GitOperator.new 53 | 54 | if git.has_remote_branch(path, name) 55 | if git.has_local_branch(path, name) 56 | if git.has_commits(path, name) 57 | Logger.error(%Q('#{name}' has unpushed commits, you should fix it manually...)) 58 | end 59 | pull(path, name) 60 | else 61 | git.checkout(path, name) 62 | end 63 | else 64 | verify_checkout(path, name) 65 | git.push_to_remote(path, name) 66 | end 67 | end 68 | 69 | def verify_home_branch(path, branch_name, type) 70 | Logger.highlight('Sync local branchs from remote, waiting...') 71 | git = GitOperator.new 72 | 73 | git.fetch(path) 74 | 75 | if OperateType::START == type 76 | if git.current_branch(path) == branch_name 77 | Logger.error(%(Current branch is '#{branch_name}' already. Use 'update' please)) 78 | end 79 | if git.has_branch(path, branch_name) 80 | Logger.error(%(Branch '#{branch_name}' already exists. Use 'switch' please)) 81 | end 82 | elsif OperateType::SWITCH == type 83 | if !git.has_branch(path, branch_name) 84 | Logger.error(%(Can't find a branch named '#{branch_name}'. Use 'start' please)) 85 | end 86 | if git.current_branch(path) == branch_name 87 | Logger.error(%(Current branch is '#{branch_name}' already. Use 'update' please)) 88 | end 89 | elsif OperateType::UPDATE == type 90 | if !git.has_branch(path, branch_name) 91 | Logger.error(%(Can't find a branch named '#{branch_name}'. Use 'start' please)) 92 | end 93 | if git.current_branch(path) != branch_name 94 | Logger.error(%(Current branch is not '#{branch_name}'. Use 'switch' please)) 95 | end 96 | else 97 | Logger.error(%(Not a valid command for '#{branch_name}'.)) 98 | end 99 | end 100 | 101 | def current_branch_type(path) 102 | branch_name = GitOperator.new.current_branch(path) 103 | if branch_name =~ /^feature\/S*/ 104 | GitflowType::FEATURE 105 | elsif branch_name =~ /^hotfix\/S*/ 106 | GitflowType::HOTFIX 107 | elsif branch_name =~ /^release\/S*/ 108 | GitflowType::RELEASE 109 | else 110 | GitflowType::FEATURE 111 | end 112 | end 113 | 114 | def branchs_with_type(path, type) 115 | branchs = [] 116 | Dir.chdir(path) do 117 | IO.popen('git branch -a') do | io | 118 | io.each do | line | 119 | branchs << line.gsub('\n', '') if line.include?('develop') 120 | branchs << line.gsub(/\s/, '') if line =~ /[\s\S]*#{GitflowType.name(type)}*/ 121 | end 122 | end 123 | end 124 | branchs 125 | end 126 | 127 | def pull(path, branch_name) 128 | git = GitOperator.new 129 | current_branch_name = git.current_branch(path) 130 | if current_branch_name == branch_name 131 | git.pull(path) 132 | else 133 | git.checkout(path, branch_name) 134 | git.pull(path) 135 | git.checkout(path, current_branch_name) 136 | end 137 | end 138 | 139 | def verify_del(path, branch_name, name, type) 140 | git = GitOperator.new 141 | 142 | if git.has_local_branch(path, branch_name) 143 | Logger.highlight("Delete local branch '#{branch_name}' for '#{name}'...") 144 | 145 | if git.current_branch(path) == branch_name 146 | git.discard(path) 147 | git.checkout(path, GitflowType.base_branch(type)) 148 | end 149 | git.del_local(path, branch_name) 150 | end 151 | 152 | if git.has_remote_branch(path, branch_name) 153 | Logger.highlight("Delete remote branch '#{branch_name}' for '#{name}'...") 154 | git.del_remote(path, branch_name) 155 | end 156 | end 157 | 158 | def verify_push(path, comment, branch_name, name) 159 | git = GitOperator.new 160 | if git.has_changes(path) || git.has_commits(path, branch_name) 161 | 162 | git.commit(path, comment) if git.has_changes(path) 163 | 164 | if git.has_remote_branch(path, branch_name) 165 | Dir.chdir(path) do 166 | `git push` 167 | end 168 | else 169 | git.push_to_remote(path, branch_name) 170 | end 171 | 172 | GitOperator.new.check_push_success(path, branch_name, "origin/#{branch_name}") 173 | else 174 | Logger.default("Nothing to push for '#{name}'.") 175 | end 176 | end 177 | 178 | def verify_rebase(path, branch_name, name) 179 | 180 | # pull rebased branch 181 | pull(path, branch_name) 182 | 183 | Dir.chdir(path) do 184 | IO.popen("git rebase #{branch_name} --ignore-whitespace") do |io| 185 | unless io.gets 186 | Logger.error("#{name} is already in a rebase-apply, Please:\n"\ 187 | " 1.Resolve it;\n"\ 188 | " 2.Commit the changes;\n"\ 189 | " 3.Push to remote;\n"\ 190 | " 4.Create a MR;\n"\ 191 | " 5.Run 'finish' again.") 192 | end 193 | io.each do |line| 194 | next unless line.include? 'Merge conflict' 195 | Logger.error("Merge conflict in #{name}, Please:\n"\ 196 | " 1.Resolve it;\n"\ 197 | " 2.Commit the changes;\n"\ 198 | " 3.Push to remote;\n"\ 199 | " 4.Create a MR;\n"\ 200 | " 5.Run 'finish' again.") 201 | end 202 | end 203 | if GitOperator.new.current_branch(path) != 'develop' && GitOperator.new.current_branch(path) != 'master' 204 | `git push -f` 205 | else 206 | Logger.error("You should not push 'master' or 'develop'") 207 | end 208 | end 209 | end 210 | end 211 | end 212 | -------------------------------------------------------------------------------- /lib/big_keeper/service/module_service.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/service/git_service' 2 | 3 | require 'big_keeper/util/logger' 4 | require 'big_keeper/util/cache_operator' 5 | 6 | module BigKeeper 7 | # Operator for got 8 | class ModuleService 9 | 10 | def verify_module(path, user, module_name, home_branch_name, type) 11 | name = home_branch_name.gsub(/#{GitflowType.name(type)}\//, '') 12 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 13 | 14 | git = GitOperator.new 15 | if !File.exist? module_full_path 16 | Logger.default("No local repository for module '#{module_name}', clone it...") 17 | module_git = BigkeeperParser.module_git(module_name) 18 | git.clone(File.expand_path("#{module_full_path}/../"), module_git) 19 | end 20 | 21 | current_branch_name = git.current_branch(module_full_path) 22 | if current_branch_name != home_branch_name 23 | # stash current branch 24 | StashService.new.stash(module_full_path, current_branch_name, module_name) 25 | 26 | GitService.new.start(module_full_path, name, type) 27 | 28 | StashService.new.pop_stash(module_full_path, home_branch_name, module_name) 29 | end 30 | end 31 | 32 | def push(path, user, module_name, home_branch_name, type, comment) 33 | Logger.highlight("Push branch '#{home_branch_name}' for module '#{module_name}'...") 34 | 35 | verify_module(path, user, module_name, home_branch_name, type) 36 | 37 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 38 | GitService.new.verify_push(module_full_path, comment, home_branch_name, module_name) 39 | end 40 | 41 | def rebase(path, user, module_name, home_branch_name, type) 42 | Logger.highlight("Rebase '#{GitflowType.base_branch(type)}' "\ 43 | "to branch '#{home_branch_name}' for module "\ 44 | "'#{module_name}'...") 45 | 46 | verify_module(path, user, module_name, home_branch_name, type) 47 | 48 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 49 | 50 | Logger.error("You have some changes in branch "\ 51 | "'#{home_branch_name}' for module '#{module_name}'. "\ 52 | "Use 'push' first please") if GitOperator.new.has_changes(module_full_path) 53 | 54 | GitService.new.verify_rebase(module_full_path, GitflowType.base_branch(type), module_name) 55 | end 56 | 57 | def pull(path, user, module_name, home_branch_name, type) 58 | Logger.highlight("Pull branch '#{home_branch_name}' for module '#{module_name}'...") 59 | 60 | verify_module(path, user, module_name, home_branch_name, type) 61 | 62 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 63 | GitOperator.new.pull(module_full_path) 64 | end 65 | 66 | def switch_to(path, user, module_name, home_branch_name, type) 67 | Logger.highlight("Switch to branch '#{home_branch_name}' for module '#{module_name}'...") 68 | 69 | verify_module(path, user, module_name, home_branch_name, type) 70 | end 71 | 72 | def pre_publish(path, user, module_name, home_branch_name, type) 73 | Logger.highlight("Prepare to publish branch '#{home_branch_name}' for module '#{module_name}'...") 74 | 75 | verify_module(path, user, module_name, home_branch_name, type) 76 | 77 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 78 | GitService.new.verify_push(module_full_path, "prepare to rebase '#{GitflowType.base_branch(type)}'", home_branch_name, module_name) 79 | GitService.new.verify_rebase(module_full_path, GitflowType.base_branch(type), module_name) 80 | end 81 | 82 | def publish(path, user, module_name, home_branch_name, type) 83 | Logger.highlight("Publish branch '#{home_branch_name}' for module '#{module_name}'...") 84 | 85 | DepService.dep_operator(path, user).update_module_config(module_name, ModuleOperateType::PUBLISH) 86 | 87 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 88 | GitService.new.verify_push(module_full_path, "publish branch #{home_branch_name}", home_branch_name, module_name) 89 | 90 | `open #{BigkeeperParser.module_pulls(module_name)}` 91 | 92 | ModuleCacheOperator.new(path).del_git_module(module_name) 93 | end 94 | 95 | def finish(path, user, module_name, home_branch_name, type) 96 | Logger.highlight("Finish branch '#{home_branch_name}' for module '#{module_name}'...") 97 | 98 | verify_module(path, user, module_name, home_branch_name, type) 99 | 100 | DepService.dep_operator(path, user).update_module_config(module_name, ModuleOperateType::FINISH) 101 | 102 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 103 | GitService.new.verify_push(module_full_path, "finish branch #{home_branch_name}", home_branch_name, module_name) 104 | 105 | ModuleCacheOperator.new(path).add_git_module(module_name) 106 | ModuleCacheOperator.new(path).del_path_module(module_name) 107 | end 108 | 109 | def add(path, user, module_name, name, type) 110 | home_branch_name = "#{GitflowType.name(type)}/#{name}" 111 | Logger.highlight("Add branch '#{home_branch_name}' for module '#{module_name}'...") 112 | 113 | verify_module(path, user, module_name, home_branch_name, type) 114 | 115 | DepService.dep_operator(path, user).update_module_config(module_name, ModuleOperateType::ADD) 116 | 117 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 118 | GitService.new.verify_push(module_full_path, "init #{GitflowType.name(type)} #{name}", home_branch_name, module_name) 119 | 120 | ModuleCacheOperator.new(path).add_path_module(module_name) 121 | end 122 | 123 | def del(path, user, module_name, name, type) 124 | home_branch_name = "#{GitflowType.name(type)}/#{name}" 125 | 126 | Logger.highlight("Delete branch '#{home_branch_name}' for module '#{module_name}'...") 127 | 128 | module_git = BigkeeperParser.module_git(module_name) 129 | DepService.dep_operator(path, user).update_module_config(module_name, ModuleOperateType::DELETE) 130 | 131 | # Stash module current branch 132 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 133 | current_branch_name = GitOperator.new.current_branch(module_full_path) 134 | StashService.new.stash(module_full_path, current_branch_name, module_name) 135 | GitOperator.new.checkout(module_full_path, GitflowType.base_branch(type)) 136 | 137 | ModuleCacheOperator.new(path).del_path_module(module_name) 138 | end 139 | 140 | def module_info(module_path, home_branch_name, user, type, module_name, version) 141 | result_dic = {} 142 | matched_branches = [] 143 | branches = GitService.new.branchs_with_type(module_path, type) 144 | if version == 'all versions' 145 | matched_branches = branches 146 | else 147 | branches.each do | branch | 148 | matched_branches << branch if branch.include?(version) 149 | end 150 | end 151 | result_dic[:module_name] = module_name 152 | result_dic[:current_branch] = GitOperator.new.current_branch(module_path) 153 | result_dic[:branches] = matched_branches 154 | result_dic 155 | end 156 | 157 | def release_check_changed(path, user, module_name) 158 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 159 | 160 | git = GitOperator.new 161 | if !File.exist? module_full_path 162 | Logger.default("No local repository for module '#{module_name}', clone it...") 163 | module_git = BigkeeperParser.module_git(module_name) 164 | git.clone(File.expand_path("#{module_full_path}/../"), module_git) 165 | end 166 | GitService.new.verify_checkout_pull(module_full_path, 'develop') 167 | git.check_remote_branch_diff(module_full_path, 'develop', 'master') 168 | end 169 | 170 | def release_start(path, user, modules, module_name, version) 171 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 172 | 173 | git = GitOperator.new 174 | if !File.exist? module_full_path 175 | Logger.default("No local repository for module '#{module_name}', clone it...") 176 | module_git = BigkeeperParser.module_git(module_name) 177 | git.clone(File.expand_path("#{module_full_path}/../"), module_git) 178 | end 179 | #stash module 180 | StashService.new.stash(module_full_path, GitOperator.new.current_branch(module_full_path), module_name) 181 | # delete cache 182 | CacheOperator.new(module_full_path).clean() 183 | # checkout develop 184 | GitService.new.verify_checkout_pull(module_full_path, 'develop') 185 | DepService.dep_operator(path, user).release_module_start(modules, module_name, version) 186 | 187 | # Push home changes to remote 188 | Logger.highlight("Push branch 'develop' for #{module_name}...") 189 | GitService.new.verify_push( 190 | module_full_path, 191 | "release start for #{version}", 192 | 'develop', 193 | "#{module_name}") 194 | end 195 | 196 | def release_finish(path, user, modules, module_name, version) 197 | module_full_path = BigkeeperParser.module_full_path(path, user, module_name) 198 | 199 | git = GitOperator.new 200 | if !File.exist? module_full_path 201 | Logger.default("No local repository for module '#{module_name}', clone it...") 202 | module_git = BigkeeperParser.module_git(module_name) 203 | git.clone(File.expand_path("#{module_full_path}/../"), module_git) 204 | end 205 | #stash module 206 | StashService.new.stash(module_full_path, GitOperator.new.current_branch(module_full_path), module_name) 207 | # delete cache 208 | CacheOperator.new(module_full_path).clean() 209 | # checkout develop 210 | GitService.new.verify_checkout_pull(module_full_path, 'develop') 211 | DepService.dep_operator(path, user).release_module_finish(modules, module_name, version) 212 | 213 | # Push home changes to remote 214 | Logger.highlight("Push branch 'develop' for #{module_name}...") 215 | GitService.new.verify_push( 216 | module_full_path, 217 | "release finish for #{version}", 218 | 'develop', 219 | "#{module_name}") 220 | end 221 | 222 | private :verify_module 223 | end 224 | end 225 | -------------------------------------------------------------------------------- /lib/big_keeper/service/stash_service.rb: -------------------------------------------------------------------------------- 1 | require 'big_stash/stash_operator' 2 | require 'big_keeper/util/bigkeeper_parser' 3 | require 'big_keeper/util/git_operator' 4 | require 'big_keeper/util/logger' 5 | 6 | module BigKeeper 7 | # Operator for got 8 | class StashService 9 | def pop_stash(path, branch_name, name) 10 | # pop stash 11 | if BigStash::StashOperator.new(path).stash_for_name(branch_name) 12 | Logger.highlight(%Q(Branch '#{branch_name}' of '#{name}' has stash , start to pop...)) 13 | BigStash::StashOperator.new(path).pop_stash(branch_name) 14 | end 15 | end 16 | 17 | def stash(path, branch_name, name) 18 | # stash 19 | if GitOperator.new.has_changes(path) 20 | Logger.highlight(%Q(Branch '#{branch_name}' of '#{name}' needs stash , start to stash...)) 21 | BigStash::StashOperator.new(path).stash(branch_name) 22 | end 23 | end 24 | 25 | def stash_all(path, new_branch_name, user, modules) 26 | # Stash modules 27 | Logger.highlight('Stash for current workspace...') 28 | 29 | modules.each do |module_name| 30 | module_path = BigkeeperParser.module_full_path(path, user, module_name) 31 | branch_name = GitOperator.new.current_branch(module_path) 32 | 33 | if branch_name != new_branch_name 34 | stash(module_path, branch_name, module_name) 35 | end 36 | end 37 | 38 | # Stash home 39 | branch_name = GitOperator.new.current_branch(path) 40 | if branch_name != new_branch_name 41 | stash(path, branch_name, 'Home') 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/big_keeper/util/bigkeeper_parser.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'big_keeper/util/file_operator' 3 | 4 | # Bigkeeper module 5 | module BigKeeper 6 | def self.version(name) 7 | BigkeeperParser.parse_version(name) 8 | end 9 | 10 | def self.user(name) 11 | BigkeeperParser.parse_user(name) 12 | yield if block_given? 13 | end 14 | 15 | def self.home(name, params) 16 | BigkeeperParser.parse_home(name, params) 17 | end 18 | 19 | def self.mod(name, params) 20 | BigkeeperParser.parse_mod(name, params) 21 | end 22 | 23 | def self.modules 24 | BigkeeperParser.parse_modules 25 | yield if block_given? 26 | end 27 | 28 | def self.source(name) 29 | BigkeeperParser.parse_source(name) 30 | yield if block_given? 31 | end 32 | 33 | def self.configs 34 | BigkeeperParser.parse_configs 35 | yield if block_given? 36 | end 37 | 38 | def self.param(key, value) 39 | BigkeeperParser.parse_param(key, value) 40 | yield if block_given? 41 | end 42 | 43 | # Bigkeeper file parser 44 | class BigkeeperParser 45 | @@config = {} 46 | @@current_user = '' 47 | 48 | def self.parse(bigkeeper) 49 | if @@config.empty? 50 | 51 | Logger.error("Can't find a Bigkeeper file in current directory.") if !FileOperator.definitely_exists?(bigkeeper) 52 | 53 | content = File.read(bigkeeper, :encoding => 'UTF-8') 54 | content.gsub!(/version\s/, 'BigKeeper::version ') 55 | content.gsub!(/user\s/, 'BigKeeper::user ') 56 | content.gsub!(/home\s/, 'BigKeeper::home ') 57 | content.gsub!(/source\s/, 'BigKeeper::source ') 58 | content.gsub!(/mod\s/, 'BigKeeper::mod ') 59 | content.gsub!(/modules\s/, 'BigKeeper::modules ') 60 | content.gsub!(/configs\s/, 'BigKeeper::configs ') 61 | content.gsub!(/param\s/, 'BigKeeper::param ') 62 | eval content 63 | end 64 | end 65 | 66 | def self.parse_source(name) 67 | @@config.delete("tmp_spec") 68 | source_split = name.split(",") unless name.split(",").length != 2 69 | if source_split != nil 70 | sources = Hash["#{source_split[1].lstrip}" => "#{source_split[0]}"] 71 | @@config[:source] = sources 72 | @@config[:tmp_spec] = source_split[1].lstrip 73 | end 74 | end 75 | 76 | def self.parse_version(name) 77 | @@config[:version] = name 78 | end 79 | 80 | def self.parse_user(name) 81 | @@current_user = name 82 | users = @@config[:users] 83 | users = {} if users.nil? 84 | users[name] = {} 85 | @@config[:users] = users 86 | end 87 | 88 | def self.parse_home(name, params) 89 | @@config[:home] = params 90 | @@config[:name] = name 91 | end 92 | 93 | def self.parse_mod(name, params) 94 | if params[:path] 95 | parse_user_mod(name, params) 96 | elsif params[:git] 97 | parse_modules_mod(name, params) 98 | else 99 | Logger.error(%(There should be ':path =>' or ':git =>' ':alias =>' for mod #{name})) 100 | end 101 | end 102 | 103 | def self.parse_user_mod(name, params) 104 | users = @@config[:users] 105 | user = users[@@current_user] 106 | mods = user[:mods] 107 | mods = {} if mods.nil? 108 | mods[name] = params 109 | user[:mods] = mods 110 | @@config[:users] = users 111 | end 112 | 113 | def self.parse_modules_mod(name, params) 114 | if @@config[:source] != nil 115 | params[:spec] = "#{@@config[:tmp_spec]}" 116 | end 117 | modules = @@config[:modules] 118 | modules[name] = params 119 | @@config[:modules] = modules 120 | end 121 | 122 | def self.parse_modules 123 | modules = @@config[:modules] 124 | modules = {} if modules.nil? 125 | @@config[:modules] = modules 126 | end 127 | 128 | def self.parse_configs 129 | @@config[:configs] = {} 130 | end 131 | 132 | def self.parse_param(key, value) 133 | @@config[:configs] = @@config[:configs].merge(key => value) 134 | end 135 | 136 | def self.version 137 | @@config[:version] 138 | end 139 | 140 | def self.home_name 141 | @@config[:name] 142 | end 143 | 144 | def self.home_git() 145 | @@config[:home][:git] 146 | end 147 | 148 | def self.home_modules_workspace() 149 | if @@config[:home][:modules_workspace] 150 | home_modules_workspace = @@config[:home][:modules_workspace] 151 | if home_modules_workspace.rindex('/') != home_modules_workspace.length - 1 152 | home_modules_workspace = home_modules_workspace + '/' 153 | end 154 | 155 | home_modules_workspace 156 | else 157 | '../' 158 | end 159 | end 160 | 161 | def self.home_pulls() 162 | @@config[:home][:pulls] 163 | end 164 | 165 | def self.source_spec_path(module_name) 166 | spec = @@config[:modules][module_name][:spec] 167 | @@config[:source][spec] 168 | end 169 | 170 | def self.source_spec_name(module_name) 171 | spec = @@config[:modules][module_name][:spec] 172 | end 173 | 174 | def self.sources 175 | @@config[:source].keys 176 | end 177 | 178 | def self.global_configs(key) 179 | if @@config[:configs] == nil 180 | return 181 | end 182 | @@config[:configs][key] 183 | end 184 | 185 | def self.module_full_path(home_path, user_name, module_name) 186 | if @@config[:users] \ 187 | && @@config[:users][user_name] \ 188 | && @@config[:users][user_name][:mods] \ 189 | && @@config[:users][user_name][:mods][module_name] \ 190 | && @@config[:users][user_name][:mods][module_name][:path] 191 | File.expand_path(@@config[:users][user_name][:mods][module_name][:path]) 192 | else 193 | if @@config[:modules][module_name][:alias] 194 | File.expand_path("#{home_path}/#{home_modules_workspace}/#{@@config[:modules][module_name][:alias]}") 195 | else 196 | File.expand_path("#{home_path}/#{home_modules_workspace}/#{module_name}") 197 | end 198 | end 199 | end 200 | 201 | def self.module_path(user_name, module_name) 202 | if @@config[:users] \ 203 | && @@config[:users][user_name] \ 204 | && @@config[:users][user_name][:mods] \ 205 | && @@config[:users][user_name][:mods][module_name] \ 206 | && @@config[:users][user_name][:mods][module_name][:path] 207 | File.expand_path(@@config[:users][user_name][:mods][module_name][:path]) 208 | else 209 | p @@config[:modules][module_name] 210 | if @@config[:modules][module_name][:alias] 211 | "#{home_modules_workspace}#{@@config[:modules][module_name][:alias]}" 212 | else 213 | "#{home_modules_workspace}#{module_name}" 214 | end 215 | end 216 | end 217 | 218 | def self.module_git(module_name) 219 | @@config[:modules][module_name][:git] 220 | end 221 | 222 | def self.module_source(module_name) 223 | @@config[:modules][module_name][:mod_path] 224 | end 225 | 226 | def self.module_maven(module_name) 227 | @@config[:modules][module_name][:maven_group] + ':' + @@config[:modules][module_name][:maven_artifact] 228 | end 229 | 230 | def self.module_maven_artifact(module_name) 231 | @@config[:modules][module_name][:maven_artifact] 232 | end 233 | 234 | def self.module_version_alias(module_name) 235 | @@config[:modules][module_name][:version_alias] 236 | end 237 | 238 | def self.module_pulls(module_name) 239 | @@config[:modules][module_name][:pulls] 240 | end 241 | 242 | def self.verify_modules(modules) 243 | modules = [] unless modules 244 | modules = modules.uniq 245 | modules.each do |item| 246 | Logger.error("Can not find module #{item} in Bigkeeper file") unless @@config[:modules][item] 247 | end 248 | modules 249 | end 250 | 251 | def self.module_names 252 | @@config[:modules].keys 253 | end 254 | 255 | def self.config 256 | @@config 257 | end 258 | end 259 | 260 | end 261 | -------------------------------------------------------------------------------- /lib/big_keeper/util/cache_operator.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'json' 3 | 4 | module BigKeeper 5 | class CacheOperator 6 | def initialize(path) 7 | @path = File.expand_path(path) 8 | @cache_path = File.expand_path("#{path}/.bigkeeper") 9 | end 10 | 11 | def save(file) 12 | dest_path = File.dirname("#{@cache_path}/#{file}") 13 | FileUtils.mkdir_p(dest_path) unless File.exist?(dest_path) 14 | FileUtils.cp("#{@path}/#{file}", "#{@cache_path}/#{file}"); 15 | end 16 | 17 | def load(file) 18 | FileUtils.cp("#{@cache_path}/#{file}", "#{@path}/#{file}"); 19 | end 20 | 21 | def clean 22 | if File.exist?(@cache_path) 23 | FileUtils.rm_r(@cache_path) 24 | end 25 | end 26 | end 27 | 28 | class ModuleCacheOperator 29 | def initialize(path) 30 | @cache_path = File.expand_path("#{path}/.bigkeeper") 31 | 32 | FileUtils.mkdir_p(@cache_path) unless File.exist?(@cache_path) 33 | 34 | if File.exist?("#{@cache_path}/module.cache") 35 | file = File.open("#{@cache_path}/module.cache", 'r', :encoding => 'UTF-8') 36 | @modules = JSON.load(file.read(), :encoding => 'UTF-8') 37 | file.close 38 | else 39 | @modules = {"git" => {"all" => [], "current" => []}, "path" => {"all" => [], "add" => [], "del" => [], "current" => []}} 40 | end 41 | end 42 | 43 | def all_path_modules 44 | @modules["path"]["all"] 45 | end 46 | 47 | def add_path_modules 48 | @modules["path"]["add"] 49 | end 50 | 51 | def del_path_modules 52 | @modules["path"]["del"] 53 | end 54 | 55 | def current_path_modules 56 | @modules["path"]["current"] 57 | end 58 | 59 | def remain_path_modules 60 | @modules["path"]["all"] - @modules["path"]["current"] 61 | end 62 | 63 | def all_git_modules 64 | @modules["git"]["all"] 65 | end 66 | 67 | def current_git_modules 68 | @modules["git"]["current"] 69 | end 70 | 71 | def remain_git_modules 72 | @modules["git"]["all"] - @modules["git"]["current"] 73 | end 74 | 75 | def cache_path_modules(modules, add_modules, del_modules) 76 | @modules["path"]["all"] = modules.uniq 77 | @modules["path"]["add"] = add_modules.uniq 78 | @modules["path"]["del"] = del_modules.uniq 79 | cache_modules 80 | end 81 | 82 | def cache_git_modules(modules) 83 | @modules["git"]["all"] = modules.uniq 84 | cache_modules 85 | end 86 | 87 | def add_git_module(module_name) 88 | @modules["git"]["current"] << module_name unless @modules["git"]["current"].include?(module_name) 89 | cache_modules 90 | end 91 | 92 | def del_git_module(module_name) 93 | @modules["git"]["current"].delete(module_name) if @modules["git"]["current"].include?(module_name) 94 | cache_modules 95 | end 96 | 97 | def add_path_module(module_name) 98 | @modules["path"]["current"] << module_name unless @modules["path"]["current"].include?(module_name) 99 | cache_modules 100 | end 101 | 102 | def del_path_module(module_name) 103 | @modules["path"]["current"].delete(module_name) if @modules["path"]["current"].include?(module_name) 104 | cache_modules 105 | end 106 | 107 | def clean_modules 108 | @modules = {"git" => {"all" => [], "current" => []}, "path" => {"all" => [], "add" => [], "del" => [], "current" => []}} 109 | cache_modules 110 | end 111 | 112 | def cache_modules 113 | file = File.new("#{@cache_path}/module.cache", 'w', :encoding => 'UTF-8') 114 | file << @modules.to_json 115 | file.close 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/big_keeper/util/code_operator.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | 3 | class OCCodeOperator 4 | end 5 | 6 | class << OCCodeOperator 7 | 8 | def in_note_code(line_hash) 9 | line = line_hash["line"] 10 | in_note = line_hash["in_note"] 11 | line = line.strip 12 | if in_note 13 | line_hash["line"]="" 14 | if (line.include?("*/")) 15 | line_hash["in_note"] = false 16 | end 17 | return 18 | end 19 | if line[0,2] == "//" || line[0,7] == "#pragma" 20 | line_hash["line"]="" 21 | return 22 | end 23 | if line.include?("/*") 24 | line_hash["in_note"] = true 25 | before_line = line[line.index("/*")+1...line.size] 26 | if before_line.include?("*/") 27 | line_hash["in_note"] = false 28 | end 29 | line_hash["line"] = line[0,line.index("/*")] 30 | return 31 | end 32 | 33 | end 34 | 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /lib/big_keeper/util/command_line_util.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | class CommandLineUtil 3 | def self.double_check(tips) 4 | Logger.highlight("#{tips} (y/n)") 5 | input = STDIN.gets().chop 6 | input.eql?('y') 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/big_keeper/util/file_operator.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | # Operator for got 3 | class FileOperator 4 | def self.definitely_exists? path 5 | folder = File.dirname path 6 | filename = File.basename path 7 | # Unlike Ruby IO, ls, and find -f, this technique will fail to locate the file if the case is wrong: 8 | not %x( find "#{folder}" -name "#{filename}" ).empty? 9 | end 10 | 11 | def find_all(path, name) 12 | Dir.glob("#{path}/*/#{name}") 13 | end 14 | 15 | def current_username 16 | current_name = `whoami` 17 | current_name.chomp 18 | end 19 | 20 | end 21 | 22 | class << FileOperator 23 | def find_all_header_file(path) 24 | return Dir.glob("#{path}/**/*.h") 25 | end 26 | def find_all_code_file(path) 27 | header_file_list = Dir.glob("#{path}/**/*.[h]") 28 | m_file_list = Dir.glob("#{path}/**/*.[m]") 29 | return header_file_list+m_file_list 30 | end 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /lib/big_keeper/util/git_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | 3 | module BigKeeper 4 | # Operator for got 5 | class GitOperator 6 | def current_branch(path) 7 | Dir.chdir(path) do 8 | `git rev-parse --abbrev-ref HEAD`.chop 9 | end 10 | end 11 | 12 | def has_remote_branch(path, branch_name) 13 | has_branch = false 14 | IO.popen("cd '#{path}'; git branch -r") do |io| 15 | io.each do |line| 16 | has_branch = true if line.include? branch_name 17 | end 18 | end 19 | has_branch 20 | end 21 | 22 | def has_local_branch(path, branch_name) 23 | has_branch = false 24 | IO.popen("cd '#{path}'; git branch") do |io| 25 | io.each do |line| 26 | has_branch = true if line.include? branch_name 27 | end 28 | end 29 | has_branch 30 | end 31 | 32 | def has_branch(path, branch_name) 33 | has_branch = false 34 | IO.popen("cd '#{path}'; git branch -a") do |io| 35 | io.each do |line| 36 | has_branch = true if line.include? branch_name 37 | end 38 | end 39 | has_branch 40 | end 41 | 42 | def checkout(path, branch_name) 43 | Dir.chdir(path) do 44 | IO.popen("git checkout #{branch_name}") do |io| 45 | io.each do |line| 46 | Logger.error("Checkout #{branch_name} failed.") if line.include? 'error' 47 | end 48 | end 49 | end 50 | end 51 | 52 | def fetch(path) 53 | Dir.chdir(path) do 54 | `git fetch origin` 55 | end 56 | end 57 | 58 | def rebase(path, branch_name) 59 | Dir.chdir(path) do 60 | `git rebase origin/#{branch_name}` 61 | end 62 | end 63 | 64 | def clone(path, git_base) 65 | Dir.chdir(path) do 66 | `git clone #{git_base}` 67 | end 68 | end 69 | 70 | def commit(path, message) 71 | Dir.chdir(path) do 72 | `git add .` 73 | `git commit -m "#{Logger.formatter_output(message)}"` 74 | end 75 | end 76 | 77 | def push_to_remote(path, branch_name) 78 | Dir.chdir(path) do 79 | `git push -u origin #{branch_name}` 80 | end 81 | GitOperator.new.check_push_success(path, branch_name, "origin/#{branch_name}") 82 | end 83 | 84 | def pull(path) 85 | Dir.chdir(path) do 86 | `git pull` 87 | end 88 | end 89 | 90 | def has_commits(path, branch_name) 91 | has_commits = false 92 | IO.popen("cd '#{path}'; git log --branches --not --remotes") do |io| 93 | io.each do |line| 94 | has_commits = true if line.include? "(#{branch_name})" 95 | end 96 | end 97 | has_commits 98 | end 99 | 100 | def has_changes(path) 101 | has_changes = true 102 | clear_flag = 'nothing to commit, working tree clean' 103 | IO.popen("cd '#{path}'; git status") do |io| 104 | io.each do |line| 105 | has_changes = false if line.include? clear_flag 106 | end 107 | end 108 | has_changes 109 | end 110 | 111 | def discard(path) 112 | Dir.chdir(path) do 113 | `git checkout . && git clean -df` 114 | end 115 | end 116 | 117 | def del_local(path, branch_name) 118 | Dir.chdir(path) do 119 | `git branch -D #{branch_name}` 120 | end 121 | end 122 | 123 | def del_remote(path, branch_name) 124 | Dir.chdir(path) do 125 | `git push origin --delete #{branch_name}` 126 | end 127 | end 128 | 129 | def user 130 | name = `git config user.name`.chop 131 | cn_reg = /[\u4e00-\u9fa5]{1}/ 132 | cn_arr = name.scan(cn_reg) 133 | if cn_arr.count > 0 134 | Logger.error("git config user.name has Chinese character") 135 | else 136 | name.gsub(/[^0-9A-Za-z]/, '').downcase 137 | end 138 | end 139 | 140 | def tag(path, version) 141 | tags = Array.new 142 | IO.popen("cd '#{path}'; git tag") do |io| 143 | io.each do |line| 144 | tags << line 145 | end 146 | end 147 | unless tags.include? "#{version}\n" 148 | Dir.chdir(path) do 149 | `git tag -a #{version} -m "release: V #{version}" master;` 150 | `git push --tags` 151 | end 152 | return 153 | end 154 | Logger.highlight("tag already exists in the remote, skip this step") 155 | end 156 | 157 | def tag_list(path) 158 | tag_list = Array.new 159 | IO.popen("cd '#{path}'; git tag -l") do |io| 160 | io.each do |line| 161 | unless line=~(/[a-zA-Z]/) 162 | tag_list << line 163 | end 164 | end 165 | end 166 | tag_list 167 | end 168 | 169 | def check_merge(path, condition) 170 | unmerged_branch = Array.new 171 | IO.popen("cd '#{path}'; git branch --no-merged") do |io| 172 | io.each do |line| 173 | unmerged_branch.push(line) if line.include? "#{condition}" 174 | end 175 | end 176 | if (unmerged_branch.size > 0) 177 | unmerged_branch.map { |item| 178 | Logger.default(item) 179 | } 180 | Logger.error("Still has unmerged feature branch, please check") 181 | end 182 | end 183 | 184 | def check_remote_branch_diff(path, branch, compare_branch) 185 | fetch(path) 186 | compare_branch_commits = Array.new 187 | IO.popen("cd '#{path}';git log --left-right #{branch}...origin/#{compare_branch} --pretty=oneline") do |io| 188 | io.each do |line| 189 | compare_branch_commits.push(line) unless (line.include? '>') && (line.include? "Merge branch \'#{branch}\'") 190 | end 191 | end 192 | if compare_branch_commits.size > 0 193 | return true 194 | else 195 | return false 196 | end 197 | end 198 | 199 | # TODO: 需要改造,util方法不应该有业务逻辑 200 | def check_diff(path, branch, compare_branch) 201 | compare_branch_commits = Array.new 202 | IO.popen("cd '#{path}'; git log --left-right #{branch}...#{compare_branch} --pretty=oneline") do |io| 203 | io.each do |line| 204 | compare_branch_commits.push(line) if (line.include? '>') && (line.include? "Merge branch #{branch} into #{compare_branch}") 205 | end 206 | end 207 | if compare_branch_commits.size > 0 208 | compare_branch_commits.map { |item| 209 | Logger.default(item) 210 | } 211 | Logger.error("#{compare_branch} branch has commit doesn't committed in #{branch}, please check") 212 | else 213 | Logger.highlight("#{compare_branch} branch doesn't have commit before #{branch}") 214 | end 215 | end 216 | 217 | def merge(path, branch_name) 218 | IO.popen("cd '#{path}'; git merge #{branch_name}") do |line| 219 | Logger.error("Merge conflict in #{branch_name}") if line.include? 'Merge conflict' 220 | end 221 | end 222 | 223 | def check_push_success(path, branch, compare_branch) 224 | compare_branch_commits = Array.new 225 | IO.popen("cd '#{path}'; git log --left-right #{branch}...#{compare_branch} --pretty=oneline") do |io| 226 | io.each do |line| 227 | compare_branch_commits.push(line) if (line.include? '>') || (line.include? 'fatal') 228 | end 229 | end 230 | if compare_branch_commits.size > 0 231 | compare_branch_commits.map { |item| 232 | Logger.default(item) 233 | } 234 | Logger.error("#{branch} branch push unsuccess, please check") 235 | else 236 | Logger.highlight("#{branch} branch push success") 237 | end 238 | end 239 | 240 | end 241 | 242 | end 243 | -------------------------------------------------------------------------------- /lib/big_keeper/util/gitflow_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/model/gitflow_type' 2 | 3 | module BigKeeper 4 | # Operator for gitflow 5 | class GitflowOperator 6 | def start(path, name, type) 7 | Dir.chdir(path) do 8 | gitflow_type_name = GitflowType.name(type) 9 | `git flow #{gitflow_type_name} start #{name}` 10 | end 11 | end 12 | 13 | def verify_git_flow_command 14 | has_git_flow_command = false 15 | IO.popen('command -v git-flow') do |io| 16 | io.each do |line| 17 | has_git_flow_command = true 18 | end 19 | end 20 | has_git_flow_command 21 | end 22 | 23 | def verify_git_flow(path) 24 | has_git_flow = false 25 | Dir.chdir(path) do 26 | clear_flag = 'Already initialized for gitflow' 27 | IO.popen('git flow init -d') do |io| 28 | io.each do |line| 29 | if line.include? clear_flag 30 | has_git_flow = true 31 | break 32 | end 33 | end 34 | end 35 | end 36 | has_git_flow 37 | end 38 | 39 | def finish_release(path, release_name) 40 | Dir.chdir(path) do 41 | p `git checkout master` 42 | p `git merge release/#{release_name}` 43 | p `git push` 44 | p `git checkout develop` 45 | p `git merge release/#{release_name}` 46 | p `git push` 47 | p `git branch -d release/#{release_name}` 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/big_keeper/util/gradle_content_generator.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | class GradleConentGenerator 3 | def self.generate_bigkeeper_settings_gradle_content() 4 | " 5 | //bigkeeper config start 6 | Properties properties = new Properties() 7 | properties.load(new File('local.properties').newDataInputStream()) 8 | if (\'true\' == properties.getProperty(\'ENABLE_BIGKEEPER_LOCAL\')) { 9 | \tapply from: \'./.bigkeeper/bigkeeper_settings.gradle\' 10 | } 11 | //bigkeeper config end" 12 | end 13 | 14 | def self.generate_bigkeeper_build_gradle_content() 15 | " 16 | //bigkeeper config start 17 | Properties properties = new Properties() 18 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 19 | if (\'true\' != properties.getProperty(\'ENABLE_BIGKEEPER_LOCAL\')) { 20 | \tapply from: \'../.bigkeeper/bigkeeper_build.gradle\' 21 | } 22 | //bigkeeper config end 23 | " 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/big_keeper/util/gradle_file_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'big_keeper/util/gradle_content_generator' 3 | 4 | module BigKeeper 5 | class GradleFileOperator 6 | @path 7 | @user 8 | def initialize(path, user) 9 | @path = path 10 | @user = user 11 | end 12 | 13 | def get_module_depends(build_file, module_name) 14 | Logger.highlight("get module #{module_name} depends ...") 15 | depend_modules = [] 16 | modules = ModuleCacheOperator.new(@path).all_path_modules 17 | File.open(build_file, 'r') do |file| 18 | file.each_line do |line| 19 | modules.each do |name| 20 | if line.strip().index('//') != 0 && !line.include?(BigkeeperParser.module_maven(module_name)) && !depend_modules.include?(name) && line.include?(BigkeeperParser.module_maven(name)) 21 | depend_modules << name 22 | end 23 | end 24 | end 25 | end 26 | print "module #{module_name} depends: " 27 | p depend_modules 28 | depend_modules 29 | end 30 | 31 | def update_module_settings(module_name, settings_file, depend_modules) 32 | module_full_path = BigkeeperParser.module_full_path(@path, @user, module_name) 33 | cache_path = File.expand_path("#{module_full_path}/.bigkeeper") 34 | big_settings_file = "#{cache_path}/bigkeeper_settings.gradle" 35 | 36 | if depend_modules.empty? && !File.exist?(big_settings_file) 37 | return 38 | end 39 | 40 | result = '' 41 | depend_modules.each do |name| 42 | artifact_id = BigkeeperParser.module_maven_artifact(name) 43 | source = BigkeeperParser.module_source(name) 44 | result << "include \':module:#{artifact_id}\'\nproject(':module:#{artifact_id}').projectDir = new File(rootProject.projectDir, '../#{name}/#{source}')\n" 45 | end 46 | 47 | dest_path = File.dirname(big_settings_file) 48 | FileUtils.mkdir_p(dest_path) unless File.exist?(dest_path) 49 | file = File.new(big_settings_file, 'w', :encoding => 'UTF-8') 50 | begin 51 | file << result 52 | file.close 53 | ensure 54 | file.close 55 | end 56 | 57 | if has_bigkeeper_config(settings_file) 58 | return 59 | end 60 | 61 | temp_file = Tempfile.new('.settings.gradle.tmp', :encoding => 'UTF-8') 62 | begin 63 | File.open(settings_file, 'r', :encoding => 'UTF-8') do |file| 64 | file.each_line do |line| 65 | temp_file.puts(line) 66 | end 67 | end 68 | temp_file.puts(GradleConentGenerator.generate_bigkeeper_settings_gradle_content()) 69 | temp_file.close 70 | FileUtils.mv(temp_file.path, settings_file) 71 | ensure 72 | temp_file.close 73 | temp_file.unlink 74 | end 75 | end 76 | 77 | def update_module_build(build_file, module_name, depend_modules, version_name) 78 | module_full_path = BigkeeperParser.module_full_path(@path, @user, module_name) 79 | cache_path = File.expand_path("#{module_full_path}/.bigkeeper") 80 | big_build_file = "#{cache_path}/bigkeeper_build.gradle" 81 | 82 | if depend_modules.empty? && !File.exist?(big_build_file) 83 | return 84 | end 85 | 86 | result = "configurations.all {\n\tresolutionStrategy {\n" 87 | depend_modules.each do |name| 88 | module_maven = BigkeeperParser.module_maven(name) 89 | result << "\t\tforce \'#{module_maven}:#{version_name}-SNAPSHOT\'\n" 90 | end 91 | result << "\t}\n}\n" 92 | 93 | dest_path = File.dirname(big_build_file) 94 | FileUtils.mkdir_p(dest_path) unless File.exist?(dest_path) 95 | file = File.new(big_build_file, 'w', :encoding => 'UTF-8') 96 | begin 97 | file << result 98 | file.close 99 | ensure 100 | file.close 101 | end 102 | 103 | if has_bigkeeper_config(build_file) 104 | return 105 | end 106 | 107 | temp_file = Tempfile.new('.build.gradle.tmp') 108 | begin 109 | File.open(build_file, 'r') do |file| 110 | file.each_line do |line| 111 | temp_file.puts(line) 112 | end 113 | end 114 | temp_file.puts(GradleConentGenerator.generate_bigkeeper_build_gradle_content()) 115 | temp_file.close 116 | FileUtils.mv(temp_file.path, build_file) 117 | ensure 118 | temp_file.close 119 | temp_file.unlink 120 | end 121 | end 122 | 123 | def get_home_depends() 124 | path_modules = ModuleCacheOperator.new(@path).all_path_modules 125 | git_modules = ModuleCacheOperator.new(@path).all_git_modules 126 | path_modules | git_modules 127 | end 128 | 129 | def update_home_settings(settings_file, depend_modules) 130 | cache_path = File.expand_path("#{@path}/.bigkeeper") 131 | big_settings_file = "#{cache_path}/bigkeeper_settings.gradle" 132 | 133 | if depend_modules.empty? && !File.exist?(big_settings_file) 134 | return 135 | end 136 | 137 | result = '' 138 | depend_modules.each do |name| 139 | artifact_id = BigkeeperParser.module_maven(name).split(':')[1] 140 | path = BigkeeperParser.module_full_path(@path, @user, name) 141 | source = BigkeeperParser.module_source(name) 142 | result << "include \':module:#{artifact_id}\'\nproject(':module:#{artifact_id}').projectDir = new File(rootProject.projectDir, '../#{name}/#{source}')\n" 143 | end 144 | 145 | dest_path = File.dirname(big_settings_file) 146 | FileUtils.mkdir_p(dest_path) unless File.exist?(dest_path) 147 | file = File.new(big_settings_file, 'w') 148 | begin 149 | file << result 150 | file.close 151 | ensure 152 | file.close 153 | end 154 | 155 | if has_bigkeeper_config(settings_file) 156 | return 157 | end 158 | 159 | temp_file = Tempfile.new('.settings.gradle.tmp') 160 | begin 161 | File.open(settings_file, 'r') do |file| 162 | file.each_line do |line| 163 | temp_file.puts(line) 164 | end 165 | end 166 | temp_file.puts(GradleConentGenerator.generate_bigkeeper_settings_gradle_content()) 167 | temp_file.close 168 | FileUtils.mv(temp_file.path, settings_file) 169 | ensure 170 | temp_file.close 171 | temp_file.unlink 172 | end 173 | end 174 | 175 | def update_home_build(build_file, depend_modules, version_name) 176 | cache_path = File.expand_path("#{@path}/.bigkeeper") 177 | big_build_file = "#{cache_path}/bigkeeper_build.gradle" 178 | 179 | if depend_modules.empty? && !File.exist?(big_build_file) 180 | return 181 | end 182 | 183 | result = "configurations.all {\n\tresolutionStrategy {\n" 184 | depend_modules.each do |module_name| 185 | module_maven = BigkeeperParser.module_maven(module_name) 186 | result << "\t\tforce \'#{module_maven}:#{version_name}-SNAPSHOT\'\n" 187 | end 188 | result << "\t}\n}\n" 189 | 190 | dest_path = File.dirname(big_build_file) 191 | FileUtils.mkdir_p(dest_path) unless File.exist?(dest_path) 192 | file = File.new(big_build_file, 'w') 193 | begin 194 | file << result 195 | file.close 196 | ensure 197 | file.close 198 | end 199 | 200 | if has_bigkeeper_config(build_file) 201 | return 202 | end 203 | 204 | temp_file = Tempfile.new('.build.gradle.tmp') 205 | begin 206 | File.open(build_file, 'r') do |file| 207 | file.each_line do |line| 208 | temp_file.puts(line) 209 | end 210 | end 211 | temp_file.puts(GradleConentGenerator.generate_bigkeeper_build_gradle_content()) 212 | temp_file.close 213 | FileUtils.mv(temp_file.path, build_file) 214 | ensure 215 | temp_file.close 216 | temp_file.unlink 217 | end 218 | end 219 | 220 | def update_module_version_name(build_file, version_name) 221 | temp_file = Tempfile.new('.build.gradle.tmp') 222 | isModifyPom = false 223 | isBigkeeperScript = false 224 | isBigkeeperBackupScript = false 225 | hasBigkeeperBackup = false 226 | begin 227 | File.open(build_file, 'r') do |file| 228 | file.each_line do |line| 229 | if line.include?('modifyPom') 230 | isModifyPom = true 231 | elsif line.include?('bigkeeper config start') 232 | isBigkeeperScript = true 233 | elsif line.include?('bigkeeper config end') 234 | isBigkeeperScript = false 235 | elsif line.include?('bigkeeper config backup start') 236 | isBigkeeperBackupScript = true 237 | hasBigkeeperBackup = true 238 | elsif line.include?('bigkeeper config backup end') 239 | isBigkeeperBackupScript = false 240 | end 241 | 242 | if isModifyPom && !isBigkeeperBackupScript && line.match(/[\s\S]*version[\s\S]*/) 243 | if !hasBigkeeperBackup 244 | temp_file.puts("\t\t//bigkeeper config backup start") 245 | temp_file.puts("\t\t//"+line.strip) 246 | temp_file.puts("\t\t//bigkeeper config backup end") 247 | end 248 | 249 | if isBigkeeperScript 250 | temp_file.puts("\t\tversion \'#{version_name}\'") 251 | else 252 | temp_file.puts("\t\t//bigkeeper config start") 253 | temp_file.puts("\t\tversion \'#{version_name}\'") 254 | temp_file.puts("\t\t//bigkeeper config end") 255 | end 256 | isModifyPom = false 257 | else 258 | temp_file.puts(line) 259 | end 260 | end 261 | end 262 | temp_file.close 263 | FileUtils.mv(temp_file.path, build_file) 264 | ensure 265 | temp_file.close 266 | temp_file.unlink 267 | end 268 | end 269 | 270 | def update_module_depends(build_file, settings_file, module_name, version_name) 271 | depend_modules = get_module_depends(build_file, module_name) 272 | update_module_settings(module_name, settings_file, depend_modules) 273 | update_module_build(build_file, module_name, depend_modules, version_name) 274 | end 275 | 276 | def update_home_depends(build_file, settings_file, type) 277 | depend_modules = get_home_depends() 278 | update_home_settings(settings_file, depend_modules) 279 | update_home_build(build_file, depend_modules, generate_version_name(type)) 280 | end 281 | 282 | def generate_version_name(type) 283 | branch_name = GitOperator.new.current_branch(@path) 284 | version_name = '' 285 | if OperateType::FINISH == type 286 | version_name = branch_name.sub(/([\s\S]*)\/(\d+.\d+.\d+)_([\s\S]*)/){ $2 } 287 | else 288 | version_name = branch_name.sub(/([\s\S]*)\/([\s\S]*)/){ $2 } 289 | version_name = version_name.gsub('_', '-') 290 | end 291 | version_name 292 | end 293 | 294 | def recover_bigkeeper_config_file(bigkeeper_config_file) 295 | if !File.exist?(bigkeeper_config_file) 296 | return 297 | end 298 | temp_file = Tempfile.new('.bigkeeper_config.tmp', :encoding => 'UTF-8') 299 | isBigkeeperScript = false 300 | isBigkeeperBackupScript = false 301 | begin 302 | File.open(bigkeeper_config_file, 'r') do |file| 303 | file.each_line do |line| 304 | if line.include?('bigkeeper config start') 305 | isBigkeeperScript = true 306 | elsif line.include?('bigkeeper config end') 307 | isBigkeeperScript = false 308 | elsif line.include?('bigkeeper config backup start') 309 | isBigkeeperBackupScript = true 310 | elsif line.include?('bigkeeper config backup end') 311 | isBigkeeperBackupScript = false 312 | elsif isBigkeeperBackupScript 313 | temp_file.puts(line.gsub('//','')) 314 | elsif !isBigkeeperScript 315 | temp_file.puts(line) 316 | end 317 | end 318 | end 319 | temp_file.close 320 | FileUtils.mv(temp_file.path, bigkeeper_config_file) 321 | ensure 322 | temp_file.close 323 | temp_file.unlink 324 | end 325 | end 326 | 327 | def has_bigkeeper_config(file) 328 | File.open(file, 'r') do |file| 329 | file.each_line do |line| 330 | if line.include?('bigkeeper config start') 331 | return true 332 | end 333 | end 334 | end 335 | false 336 | end 337 | 338 | end 339 | end 340 | -------------------------------------------------------------------------------- /lib/big_keeper/util/gradle_module_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/cache_operator' 2 | require 'big_keeper/util/gradle_file_operator' 3 | require 'big_keeper/util/bigkeeper_parser' 4 | 5 | module BigKeeper 6 | # Operator for podfile 7 | class GradleModuleOperator 8 | @path 9 | @user 10 | @module_name 11 | 12 | BUILD_GRADLE = "build.gradle" 13 | SETTINGS_GRADLE = "settings.gradle" 14 | 15 | def initialize(path, user, module_name) 16 | @path = path 17 | @user = user 18 | @module_name = module_name 19 | end 20 | 21 | def backup 22 | end 23 | 24 | ## TODO 25 | def recover() 26 | module_full_path = BigkeeperParser.module_full_path(@path, @user, @module_name) 27 | source = BigkeeperParser.module_source(@module_name) 28 | build_file = "#{module_full_path}/#{source}/#{BUILD_GRADLE}" 29 | settings_file = "#{module_full_path}/#{SETTINGS_GRADLE}" 30 | GradleFileOperator.new(@path, @user).recover_bigkeeper_config_file(build_file) 31 | GradleFileOperator.new(@path, @user).recover_bigkeeper_config_file(settings_file) 32 | 33 | cache_operator = CacheOperator.new(module_full_path) 34 | cache_operator.clean 35 | end 36 | 37 | def update_module(module_operate_type) 38 | update_module_depends(module_operate_type) 39 | update_module_version_name(module_operate_type) 40 | end 41 | 42 | def update_module_depends(module_operate_type) 43 | module_full_path = BigkeeperParser.module_full_path(@path, @user, @module_name) 44 | source = BigkeeperParser.module_source(@module_name) 45 | version_name = generate_version_name(module_operate_type) 46 | build_file = "#{module_full_path}/#{source}/#{BUILD_GRADLE}" 47 | settings_file = "#{module_full_path}/#{SETTINGS_GRADLE}" 48 | GradleFileOperator.new(@path, @user).update_module_depends(build_file, settings_file, @module_name, version_name) 49 | end 50 | 51 | def update_module_version_name(module_operate_type) 52 | module_full_path = BigkeeperParser.module_full_path(@path, @user, @module_name) 53 | source = BigkeeperParser.module_source(@module_name) 54 | GradleFileOperator.new(@path, @user).update_module_version_name("#{module_full_path}/#{source}/#{BUILD_GRADLE}", generate_version_name(module_operate_type)) 55 | end 56 | 57 | def generate_version_name(module_operate_type) 58 | branch_name = GitOperator.new.current_branch(@path) 59 | version_name = '' 60 | if ModuleOperateType::ADD == module_operate_type 61 | version_name = branch_name.sub(/([\s\S]*)\/([\s\S]*)/){ $2 } 62 | version_name = version_name.gsub('_', '-') 63 | elsif ModuleOperateType::FINISH == module_operate_type 64 | version_name = branch_name.sub(/([\s\S]*)\/(\d+.\d+.\d+)_([\s\S]*)/){ $2 } 65 | end 66 | return version_name 67 | end 68 | 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/big_keeper/util/info_plist_operator.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'plist' # gem install plist 3 | require 'big_stash/stash_operator' 4 | require 'pathname' 5 | 6 | module BigKeeper 7 | # Operator for Info.plist 8 | class InfoPlistOperator 9 | def change_version_build(path, version) 10 | if find_infoPlist_filePath(path) == '' 11 | raise %(Not find be Info.plist at #{path}) 12 | end 13 | info_plist_path = find_infoPlist_filePath(path) 14 | result = Plist.parse_xml(info_plist_path) 15 | result['CFBundleVersion'] = getBuildVersion(version, result['CFBundleShortVersionString'], result['CFBundleVersion']).to_s 16 | result['CFBundleShortVersionString'] = version.to_s 17 | Plist::Emit.save_plist(result, info_plist_path) 18 | puts %Q('Version has changed to #{version}') 19 | end 20 | 21 | # Find Info.plist file path 22 | # @return [String] pathName of info.plist 23 | def find_infoPlist_filePath(path) 24 | paths = Pathname.new(path).children.select { |pn| pn.extname == '.xcodeproj' } 25 | xcodePath = paths[0].to_s.split('/')[-1] 26 | projectName = xcodePath.split('.')[0] 27 | projectPath = '' 28 | Pathname.new("#{path}/#{projectName}").children.select { |pn| 29 | if pn.to_s == "#{path}/#{projectName}/Info.plist" 30 | projectPath = "#{path}/#{projectName}/Info.plist" 31 | end 32 | } 33 | projectPath 34 | end 35 | 36 | private 37 | def getBuildVersion(build_string, old_build_string, old_build_version) 38 | if build_string == old_build_string 39 | return old_build_version.to_i + 1 40 | else 41 | version_arr = build_string.split('.') 42 | return version_arr[0].to_i * 1000 + version_arr[1].to_i * 100 + version_arr[2].to_i * 10 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/big_keeper/util/leancloud_logger.rb: -------------------------------------------------------------------------------- 1 | require 'Singleton' 2 | require 'net/http' 3 | require 'net/https' 4 | require 'big_keeper/util/logger' 5 | 6 | module BigKeeper 7 | class LeanCloudLogger 8 | include Singleton 9 | 10 | attr_accessor :user, :version, :start_timestamp, :end_timestamp, :command, :parameter, :is_success, :path, :need_log 11 | 12 | def initialize 13 | @need_log = "true" 14 | end 15 | 16 | def set_command(set_command) 17 | @command = set_command 18 | end 19 | 20 | def is_need_log 21 | @need_log == "true" 22 | end 23 | 24 | def start_log(global_options, args) 25 | @start_timestamp = Time.new.to_i 26 | @user = global_options['user'].to_s 27 | @parameter = args.join(",") 28 | @version = global_options['ver'] 29 | @path = global_options['path'] 30 | @need_log = "#{global_options[:log]}" 31 | end 32 | 33 | def end_log(is_success, is_show_log) 34 | @end_timestamp = Time.new.to_i 35 | @is_success = is_success 36 | @version = BigkeeperParser.version if @version == 'Version in Bigkeeper file' 37 | 38 | # require 39 | parameter = {'start_timestamp' => @start_timestamp, 'end_timestamp' =>@end_timestamp, 'user' =>@user, 'is_success' =>@is_success} 40 | 41 | # optional 42 | parameter = parameter.merge('command' => @command) unless @command == nil 43 | parameter = parameter.merge('version' => @version) unless @version == nil || @version == "" 44 | parameter = parameter.merge('parameter' => @parameter) unless @parameter == nil || @parameter == "" 45 | 46 | if @command 47 | leancloud_file = @command.split("/").first 48 | send_log_cloud(leancloud_file, parameter, is_show_log) 49 | end 50 | end 51 | 52 | def send_log_cloud(file_name, parameter, is_show_log) 53 | if file_name == nil 54 | return 55 | end 56 | 57 | if BigkeeperParser.global_configs("LeanCloudId") == nil || BigkeeperParser.global_configs("LeanCloudKey") == nil 58 | return 59 | end 60 | 61 | header = assemble_request 62 | 63 | uri = URI.parse("https://api.leancloud.cn/1.1/classes/#{file_name}") 64 | 65 | https = Net::HTTP.new(uri.host, 443) 66 | https.use_ssl = true 67 | https.ssl_version = :TLSv1 68 | https.verify_mode = OpenSSL::SSL::VERIFY_PEER 69 | req = Net::HTTP::Post.new(uri.path, header) 70 | req.body = parameter.to_json 71 | res = https.request(req) 72 | 73 | if is_show_log == true 74 | Logger.highlight("Send LeanCloud success, response #{res.body}") 75 | end 76 | end 77 | 78 | def assemble_request 79 | return {'Content-Type' =>'application/json', 'X-LC-Id' =>BigkeeperParser.global_configs("LeanCloudId"), 'X-LC-Key' =>BigkeeperParser.global_configs("LeanCloudKey")} 80 | end 81 | 82 | protected :send_log_cloud, :assemble_request 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/big_keeper/util/list_generator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'json' 3 | 4 | module BigKeeper 5 | class ListGenerator 6 | #generate tree print throught console 7 | def self.generate_tree(file_path, home_branches, version) 8 | module_branches_dic = {} 9 | json_data = File.read(file_path, :encoding => 'UTF-8') 10 | module_branches_dic = JSON.parse(json_data) 11 | to_tree(module_branches_dic, home_branches, version) 12 | File.delete(file_path) 13 | end 14 | 15 | #generate json print throught console 16 | def self.generate_json(file_path, home_branches, version) 17 | module_branches_dic = {} 18 | json_data = File.read(file_path, :encoding => 'UTF-8') 19 | module_branches_dic = JSON.parse(json_data) 20 | json = to_json(home_branches, module_branches_dic, version) 21 | puts JSON.pretty_generate(json) 22 | File.delete(file_path) 23 | end 24 | 25 | def self.to_json(home_branches, module_info_list, version) 26 | json_array = [] 27 | print_all = version == "all versions" 28 | home_branches = home_branches.uniq 29 | home_branches.each do | home_branch_name | 30 | next unless home_branch_name.include?(version) || print_all 31 | branch_dic = {} 32 | involve_modules = [] 33 | module_info_list.collect do | module_info_dic | 34 | next unless module_info_dic["branches"] != nil 35 | module_name = module_info_dic["module_name"] 36 | module_info_dic["branches"].each do | module_branch | 37 | if module_branch.strip.delete("*") == home_branch_name.strip.delete("*") 38 | module_current_info = {} 39 | module_current_info["module_name"] = module_name 40 | module_current_info["current_branch"] = module_info_dic["current_branch"] 41 | involve_modules << module_current_info 42 | end 43 | end 44 | end 45 | 46 | branch_dic["is_remote"] = false 47 | branch_dic["is_current"] = false 48 | 49 | if home_branch_name =~ /^remotes\// 50 | home_branch_name = $~.post_match 51 | branch_dic["is_remote"] = true 52 | end 53 | 54 | if home_branch_name =~ /^origin\// 55 | home_branch_name = $~.post_match 56 | end 57 | 58 | if home_branch_name.include?("*") 59 | home_branch_name = home_branch_name.delete("*") 60 | branch_dic["is_current"] = true 61 | end 62 | 63 | if home_branch_name =~ /^feature\// 64 | home_branch_name = $~.post_match 65 | end 66 | 67 | if home_branch_name =~ /^hotfix\// 68 | home_branch_name = $~.post_match 69 | end 70 | 71 | branch_dic["home_branch_name"] = home_branch_name 72 | branch_dic["involve_modules"] = involve_modules 73 | json_array << branch_dic 74 | end 75 | json_array 76 | end 77 | 78 | def self.to_tree(module_branches_dic, branches_name, version) 79 | home_name = BigkeeperParser.home_name 80 | print_all = version == "all versions" 81 | branches_name.each do | home_branch_name | 82 | next unless home_branch_name.include?(version) || print_all 83 | Logger.highlight(home_branch_name.strip) 84 | module_branches_dic.each do | module_info_dic | 85 | module_name = module_info_dic["module_name"] 86 | next if module_info_dic["branches"] == nil 87 | module_info_dic["branches"].each do | module_branch | 88 | if module_branch.include?(home_branch_name.strip.delete('*')) 89 | if !module_branch.include?("*") && home_branch_name.include?("*") 90 | Logger.warning(" ├── #{module_name} (current branch :#{module_info_dic["current_branch"]})") 91 | else 92 | Logger.default(" ├── #{module_name}") 93 | end 94 | break 95 | end 96 | end 97 | end 98 | end 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /lib/big_keeper/util/lockfile_parser.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/util/logger' 3 | require 'big_keeper/util/podfile_detector' 4 | require 'Singleton' 5 | 6 | module BigKeeper 7 | class LockfileParser 8 | include Singleton 9 | attr_accessor :main_path, :dependencies, :pods, :podfile_hash 10 | $mode = 'PODS' 11 | 12 | def initialize 13 | self.pods = {} 14 | self.dependencies = [] 15 | end 16 | 17 | def parse(main_path) 18 | self.main_path = main_path 19 | $mode = 'PODS' 20 | podfile_lock_lines = File.readlines("#{main_path}/Podfile.lock", :encoding => 'UTF-8') 21 | Logger.highlight("Analyzing Podfile.lock...") 22 | podfile_lock_lines.each do |sentence| 23 | if sentence.include?('PODS') 24 | $mode = 'PODS' 25 | elsif sentence.include?('DEPENDENCIES') 26 | $mode = 'DEPENDENCIES' 27 | elsif sentence.include?('SPEC REPOS') 28 | $mode = 'SPEC REPOS' 29 | elsif sentence.include?('SPEC CHECKSUMS') 30 | $mode = 'SPEC CHECKSUMS' 31 | elsif sentence.include?('CHECKOUT OPTIONS') 32 | $mode = 'CHECKOUT OPTIONS' 33 | elsif sentence.include?('EXTERNAL SOURCES') 34 | $mode = 'EXTERNAL SOURCES' 35 | elsif sentence.include?('PODFILE CHECKSUM') 36 | $mode = 'PODFILE CHECKSUM' 37 | else 38 | if $mode == 'PODS' 39 | deal_pod(sentence.strip.delete('\n')) 40 | end 41 | if $mode == 'SPEC CHECKSUMS' 42 | deal_spec(sentence.strip.delete('\n')) 43 | end 44 | end 45 | end 46 | end 47 | 48 | def get_unlock_pod_list(is_all) 49 | result = {} 50 | pod_parser = PodfileParser.instance 51 | #podfile 中 unlock pods 52 | unlock_pods = pod_parser.get_unlock_pod_list 53 | # @unlock_pod_list << pod_name unless @module_list.include pod_name 54 | if is_all 55 | self.dependencies.each do |pod_name| 56 | if pod_parser.pod_list.include?(pod_name) 57 | next 58 | end 59 | if self.pods[pod_name] != nil 60 | result[pod_name] = self.pods[pod_name] 61 | end 62 | end 63 | 64 | unlock_pods.each do |pod_name| 65 | if self.pods[pod_name] != nil 66 | result[pod_name] = self.pods[pod_name] 67 | end 68 | end 69 | return result 70 | else 71 | unlock_pods.each do |pod_name| 72 | if self.pods[pod_name] != nil 73 | result[pod_name] = self.pods[pod_name] 74 | end 75 | end 76 | return result 77 | end 78 | end 79 | 80 | #处理PODS 81 | # TODO 去除重复 82 | def deal_pod(s) 83 | pod_name = get_lock_podname(s) 84 | return if pod_name == nil 85 | pod_version = get_lock_version(s) 86 | if self.pods.keys.include?(pod_name) 87 | current_version = self.pods[pod_name] 88 | if pod_version != nil && current_version != nil 89 | self.pods[pod_name] = chose_version(current_version, pod_version) 90 | else 91 | self.pods[pod_name] = pod_version unless pod_version == nil 92 | end 93 | end 94 | self.pods[pod_name] = pod_version unless pod_version == nil 95 | end 96 | # 97 | # #处理EXTERNAL SOURCES 98 | # def deal_sources(s) 99 | # 100 | # end 101 | # #处理CHECKOUT OPTIONS 102 | # def deal_checkout(s) 103 | # 104 | # end 105 | 106 | #处理SPEC CHECKSUMS 107 | def deal_spec(s) 108 | if /: +/ =~ s 109 | dependency = $~.pre_match.strip 110 | self.dependencies << dependency unless self.dependencies.include?(dependency) 111 | end 112 | end 113 | 114 | def get_lock_podname(sentence) #获得lock pod名称 115 | match_result = /(\d+.){1,2}\d+/.match(sentence.delete('- :~>=')) 116 | pod_name = match_result.pre_match unless match_result == nil 117 | return pod_name.delete('()') unless pod_name == nil 118 | end 119 | 120 | def get_lock_version(sentence)#获得lock pod版本号 121 | match_result = /(\d+.){1,2}\d+/.match(sentence) 122 | return match_result[0].strip unless match_result == nil 123 | end 124 | 125 | def chose_version(cur_version,temp_version) 126 | # p "cur:#{cur_version},temp:#{temp_version}" 127 | cur_list = cur_version.split('.') 128 | temp_list = temp_version.split('.') 129 | cur_list << 0.to_s if cur_list.size == 2 130 | temp_list << 0.to_s if temp_list.size == 2 131 | if cur_list[0] >= temp_list[0] 132 | if cur_list[1] >= temp_list[1] 133 | if cur_list[2] > temp_list[2] 134 | return cur_version 135 | end 136 | return temp_version 137 | end 138 | return temp_version 139 | end 140 | return temp_version 141 | end 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /lib/big_keeper/util/logger.rb: -------------------------------------------------------------------------------- 1 | require 'colorize' 2 | require 'big_keeper/util/leancloud_logger' 3 | 4 | module BigKeeper 5 | DEFAULT_LOG = 1 6 | HIGHLIGHT_LOG = 2 7 | ERROR_LOG = 3 8 | WARNING_LOG = 4 9 | 10 | class Logger 11 | 12 | def self.log_with_type(sentence,type) 13 | case type 14 | when DEFAULT_LOG then puts sentence.to_s.colorize(:default) 15 | when HIGHLIGHT_LOG then puts sentence.to_s.colorize(:green) 16 | when ERROR_LOG then raise sentence.to_s.colorize(:red) 17 | when WARNING_LOG then puts sentence.to_s.colorize(:yellow) 18 | end 19 | end 20 | 21 | def self.default(sentence) 22 | puts formatter_output(sentence).colorize(:default) 23 | end 24 | 25 | def self.highlight(sentence) 26 | puts formatter_output(sentence).colorize(:green) 27 | end 28 | 29 | def self.error(sentence) 30 | is_need_log = LeanCloudLogger.instance.is_need_log 31 | LeanCloudLogger.instance.end_log(false, is_need_log) 32 | raise formatter_output(sentence).colorize(:red) 33 | end 34 | 35 | def self.warning(sentence) 36 | puts formatter_output(sentence).colorize(:yellow) 37 | end 38 | 39 | def self.separator 40 | puts "- - - - - - - - - - - - - - - - - - - - - - - - - - -".colorize(:light_blue) 41 | end 42 | 43 | def self.formatter_output(sentence) 44 | "[big] ".concat(sentence.to_s).to_s 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/big_keeper/util/pod_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | require 'open3' 3 | 4 | module BigKeeper 5 | class PodOperator 6 | def self.pod_install(path, repo_update) 7 | # pod install 8 | if repo_update 9 | PodOperator.pod_update_private_repos(true) 10 | end 11 | Logger.highlight('Start pod install, waiting...') 12 | cmd = "pod install --project-directory='#{path}'" 13 | Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr| 14 | while line = stdout.gets 15 | p line 16 | end 17 | end 18 | Logger.highlight('Finish pod install.') 19 | end 20 | 21 | def self.pod_repo_push(path, module_name, source, version) 22 | Logger.highlight(%Q(Start Pod repo push #{module_name})) 23 | Dir.chdir(path) do 24 | command = "" 25 | p BigkeeperParser.source_spec_name(module_name) 26 | if source.length > 0 27 | command = "pod repo push #{BigkeeperParser.source_spec_name(module_name)} #{module_name}.podspec --allow-warnings --sources=#{source} --verbose --use-libraries" 28 | else 29 | command = "pod repo push #{BigkeeperParser.source_spec_name(module_name)} #{module_name}.podspec --allow-warnings --verbose --use-libraries" 30 | end 31 | 32 | IO.popen(command) do |io| 33 | is_success = false 34 | error_info = Array.new 35 | io.each do |line| 36 | error_info.push(line) 37 | is_success = true if line.include? "Updating spec repo" 38 | end 39 | unless is_success 40 | puts error_info 41 | Logger.error("Fail: '#{module_name}' Pod repo fail") 42 | end 43 | Logger.highlight(%Q(Success release #{module_name} V#{version})) 44 | end 45 | end 46 | end 47 | 48 | def self.pod_update_private_repos(update_private) 49 | if update_private 50 | BigkeeperParser.sources.map { |spec| 51 | Logger.highlight('Start pod repo update, waiting...') 52 | cmd = "pod repo update #{spec}" 53 | cmd(cmd) 54 | } 55 | else 56 | cmd = "pod repo update" 57 | cmd(cmd) 58 | end 59 | end 60 | 61 | def self.cmd(cmd) 62 | Open3.popen3(cmd) do |stdin , stdout , stderr, wait_thr| 63 | while line = stdout.gets 64 | puts line 65 | end 66 | end 67 | end 68 | 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/big_keeper/util/podfile_detector.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/model/podfile_model' 3 | require 'big_keeper/util/logger' 4 | require 'Singleton' 5 | module BigKeeper 6 | 7 | class PodfileParser 8 | include Singleton 9 | attr_accessor :module_list, :main_path, :pod_list 10 | $unlock_pod_list = [] 11 | $modify_pod_list = {} 12 | 13 | def initialize 14 | @module_list = BigkeeperParser.module_names 15 | @pod_list = [] 16 | end 17 | 18 | def parse(path) 19 | @main_path = path 20 | podfile_lines = File.readlines("#{@main_path}/Podfile", :encoding => 'UTF-8') 21 | Logger.highlight("Analyzing Podfile...") unless podfile_lines.size.zero? 22 | podfile_lines.collect do |sentence| 23 | if /pod / =~ sentence 24 | pod_name = get_pod_name(sentence) 25 | @pod_list << pod_name 26 | end 27 | end 28 | end 29 | 30 | def get_unlock_pod_list 31 | podfile_lines = File.readlines("#{@main_path}/Podfile", :encoding => 'UTF-8') 32 | #Logger.highlight("Analyzing Podfile...") unless podfile_lines.size.zero? 33 | podfile_lines.collect do |sentence| 34 | deal_podfile_line(sentence) unless sentence =~(/'(\d+.){1,2}\d+'/) 35 | end 36 | $unlock_pod_list 37 | end 38 | 39 | def deal_podfile_line(sentence) 40 | return unless !sentence.strip.start_with?("#") 41 | if sentence.strip.include?('pod ') 42 | pod_model = PodfileModel.new(sentence) 43 | if !pod_model.name.empty? && 44 | pod_model.configurations != '[\'Debug\']' && 45 | pod_model.path == nil && 46 | pod_model.tag == nil 47 | pod_names = pod_model.name.split('/') 48 | if pod_names.size > 1 49 | pod_name = pod_names[0] 50 | else 51 | pod_name = pod_model.name 52 | end 53 | $unlock_pod_list << pod_name unless @module_list.include?(pod_name) 54 | end 55 | pod_model 56 | end 57 | end 58 | 59 | def get_pod_name(sentence) 60 | 61 | pod_model = deal_podfile_line(sentence) 62 | if pod_model != nil 63 | return pod_model.name 64 | end 65 | end 66 | 67 | def self.get_pod_model(sentence) 68 | if sentence.include?('pod ') 69 | pod_model = PodfileModel.new(sentence) 70 | return pod_model 71 | end 72 | end 73 | 74 | def get_lock_podname(sentence) #获得pod名称 75 | match_result = /(\d+.){1,2}\d+/.match(sentence.delete('- :~>=')) 76 | pod_name = match_result.pre_match unless match_result == nil 77 | return pod_name.delete('()') unless pod_name == nil 78 | end 79 | 80 | def get_lock_version(sentence)#获得pod版本号 81 | match_result = /(\d+.){1,2}\d+/.match(sentence) 82 | return match_result[0] unless match_result == nil 83 | end 84 | 85 | def chose_version(cur_version,temp_version) 86 | # p "cur:#{cur_version},temp:#{temp_version}" 87 | cur_list = cur_version.split('.') 88 | temp_list = temp_version.split('.') 89 | temp_list << 0.to_s if temp_list.size == 2 90 | if cur_list[0] >= temp_list[0] 91 | if cur_list[1] >= temp_list[1] 92 | if cur_list[2] > temp_list[2] 93 | return cur_version 94 | end 95 | return temp_version 96 | end 97 | return temp_version 98 | end 99 | return temp_version 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /lib/big_keeper/util/podfile_module.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/bigkeeper_parser' 2 | require 'big_keeper/util/podfile_detector' 3 | require 'big_keeper/model/podfile_model' 4 | require 'big_keeper/util/logger' 5 | 6 | module BigKeeper 7 | 8 | class PodfileModuleDetector 9 | @module_list = [] 10 | 11 | def initialize(main_path) 12 | @module_list = BigkeeperParser.module_names 13 | @main_path = main_path 14 | @update_modules = {} 15 | # check_version_list 16 | end 17 | 18 | #检查需要更新业务库列表 19 | def check_version_list 20 | if @module_list.empty? 21 | Logger.highlight('There is not any module should to be check.') 22 | return 23 | else 24 | Logger.highlight('Checking..') 25 | @module_list.each do |module_name| 26 | get_pod_search_result(module_name) 27 | end 28 | 29 | #获得pod信息后 30 | deal_module_info 31 | end 32 | end 33 | 34 | def get_pod_search_result(pod_name) 35 | #输入pod Search 结果 36 | `pod search #{pod_name} --ios --simple >> #{@main_path}/bigKeeperPodInfo.tmp` 37 | end 38 | 39 | def deal_module_info 40 | podfile_lines = File.readlines("#{@main_path}/bigKeeperPodInfo.tmp", :encoding => 'UTF-8') 41 | Logger.highlight("Analyzing modules info...") unless podfile_lines.size.zero? 42 | podfile_lines.collect do |sentence| 43 | if sentence =~(/pod/) 44 | sentence = sentence.sub('pod', '') 45 | sentence = sentence.delete('\n\'') 46 | match_result = sentence.split(',') 47 | pod_name = match_result[0].strip 48 | latest_version = match_result[1].strip 49 | @update_modules[pod_name] = latest_version unless @update_modules.include?(pod_name) 50 | end 51 | end 52 | p @update_modules 53 | File.delete("#{@main_path}/bigKeeperPodInfo.tmp") 54 | @update_modules 55 | end 56 | 57 | def get_module_latest_version(pod_model) 58 | 59 | end 60 | 61 | end 62 | 63 | end 64 | -------------------------------------------------------------------------------- /lib/big_keeper/util/podfile_operator.rb: -------------------------------------------------------------------------------- 1 | require 'tempfile' 2 | require 'fileutils' 3 | require 'big_keeper/dependency/dep_type' 4 | require 'big_keeper/util/podfile_detector' 5 | 6 | module BigKeeper 7 | # Operator for podfile 8 | class PodfileOperator 9 | def has(podfile, module_name) 10 | File.open(podfile, 'r') do |file| 11 | file.each_line do |line| 12 | if line.include?module_name 13 | return true 14 | end 15 | end 16 | end 17 | false 18 | end 19 | 20 | def generate_pod_config(pod_name, version, comment) 21 | module_config = '' 22 | if comment != nil 23 | module_config = " pod '#{pod_name}' , '#{version}' # #{comment}" 24 | else 25 | module_config = " pod '#{pod_name}' , '#{version}'" 26 | end 27 | end 28 | 29 | def replace_all_module_release(path, user, module_names, version) 30 | module_names.each do |module_name| 31 | DepService.dep_operator(path, user).update_module_config( 32 | module_name, 33 | ModuleOperateType::RELEASE) 34 | end 35 | 36 | end 37 | 38 | def find_and_lock(podfile, dictionary) 39 | temp_file = Tempfile.new('.Podfile.tmp', :encoding => 'UTF-8') 40 | begin 41 | File.open(podfile, 'r') do |file| 42 | file.each_line do |line| 43 | pod_model = PodfileParser.get_pod_model(line) 44 | if pod_model != nil && pod_model.name != nil && dictionary[pod_model.name] != nil 45 | temp_file.puts generate_pod_config(pod_model.name, dictionary[pod_model.name], pod_model.comment) 46 | dictionary.delete(pod_model.name) 47 | else 48 | temp_file.puts line 49 | end 50 | end 51 | end 52 | if !dictionary.empty? 53 | temp_file.puts 'def sub_dependency' 54 | dictionary.keys.each do |sub_pod| 55 | temp_file.puts generate_pod_config(sub_pod, dictionary[sub_pod], 'bigkeeper') 56 | end 57 | temp_file.puts 'end' 58 | end 59 | temp_file.close 60 | FileUtils.mv(temp_file.path, podfile) 61 | ensure 62 | temp_file.close 63 | temp_file.unlink 64 | end 65 | end 66 | 67 | def find_and_upgrade(podfile, dictionary) 68 | temp_file = Tempfile.new('.Podfile.tmp', :encoding => 'UTF-8') 69 | begin 70 | File.open(podfile, 'r', :encoding => 'UTF-8') do |file| 71 | file.each_line do |line| 72 | pod_model = PodfileParser.get_pod_model(line) 73 | if pod_model != nil && pod_model.name != nil && dictionary[pod_model.name] != nil 74 | #替换 75 | temp_file.puts generate_pod_config(pod_model.name, dictionary[pod_model.name], pod_model.comment) 76 | else 77 | temp_file.puts line 78 | end 79 | end 80 | end 81 | temp_file.close 82 | FileUtils.mv(temp_file.path, podfile) 83 | ensure 84 | temp_file.close 85 | temp_file.unlink 86 | end 87 | end 88 | 89 | def podspec_change(podspec_file, version, module_name) 90 | temp_file = Tempfile.new(".#{module_name}.podspec", :encoding => 'UTF-8') 91 | has_change = false 92 | begin 93 | File.open(podspec_file, 'r', :encoding => 'UTF-8') do |file| 94 | file.each_line do |line| 95 | if line.include?("s.version") 96 | temp_line = line 97 | temp_line_arr = temp_line.split("=") 98 | if temp_line_arr[0].delete(" ") == "s.version" 99 | unless temp_line_arr[temp_line_arr.length - 1].include? "#{version}" 100 | temp_file.puts "s.version = '#{version}'" 101 | has_change = true 102 | else 103 | temp_file.puts line 104 | Logger.highlight("The version in PodSpec is equal your input version") 105 | end 106 | else 107 | temp_file.puts line 108 | end 109 | else 110 | temp_file.puts line 111 | end 112 | end 113 | end 114 | temp_file.close 115 | FileUtils.mv(temp_file.path, podspec_file) 116 | ensure 117 | temp_file.close 118 | temp_file.unlink 119 | end 120 | has_change 121 | end 122 | 123 | private :generate_pod_config 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /lib/big_keeper/util/verify_operator.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | # Operator for got 3 | class VerifyOperator 4 | def self.already_in_process? 5 | already_in_process = false 6 | Open3.popen3('ps aux | grep \ -c') do |stdin , stdout , stderr, wait_thr| 7 | while line = stdout.gets 8 | if line.rstrip.to_i > 2 9 | already_in_process = true 10 | break 11 | end 12 | end 13 | end 14 | already_in_process 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/big_keeper/util/version_config_operator.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | class VersionConfigOperator 3 | def self.change_version(version_config_file, modules, version) 4 | temp_file = Tempfile.new('.version-config.gradle.tmp') 5 | begin 6 | File.open(version_config_file, 'r') do |file| 7 | file.each_line do |line| 8 | temp_file.puts(replace_module_version(line, modules, version)) 9 | end 10 | end 11 | temp_file.close 12 | FileUtils.mv(temp_file.path, version_config_file) 13 | ensure 14 | temp_file.close 15 | temp_file.unlink 16 | end 17 | end 18 | 19 | def self.replace_module_version(line, modules, version) 20 | modules.each do |module_name| 21 | version_alias = BigkeeperParser.module_version_alias(module_name) 22 | if !version_alias.nil? && !version_alias.empty? && line.match(/\s*#{version_alias}\s*=\s*('|")([\s\S]*)('|")\s*/) 23 | return line.sub(/(\s*#{version_alias}\s*=\s*)('|")([\s\S]*)('|")\s*/){"#{$1}\'#{version}\'"} 24 | end 25 | end 26 | line 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/big_keeper/util/xcode_operator.rb: -------------------------------------------------------------------------------- 1 | require 'big_keeper/util/logger' 2 | 3 | module BigKeeper 4 | class XcodeOperator 5 | def self.open_workspace(path) 6 | # Close Xcode 7 | # `pkill Xcode` 8 | 9 | # sleep 0.5 10 | 11 | # Open home workspace 12 | # `open #{path}/*.xcworkspace` 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/big_keeper/version.rb: -------------------------------------------------------------------------------- 1 | module BigKeeper 2 | VERSION = "0.9.12" 3 | end 4 | -------------------------------------------------------------------------------- /resources/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/banner.png -------------------------------------------------------------------------------- /resources/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/command.png -------------------------------------------------------------------------------- /resources/keynote/big-keeper-readme-analyze.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/keynote/big-keeper-readme-analyze.key -------------------------------------------------------------------------------- /resources/keynote/big-keeper-readme-example.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/keynote/big-keeper-readme-example.key -------------------------------------------------------------------------------- /resources/keynote/big-keeper-readme-feature.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/keynote/big-keeper-readme-feature.key -------------------------------------------------------------------------------- /resources/keynote/big-keeper-readme-release.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/keynote/big-keeper-readme-release.key -------------------------------------------------------------------------------- /resources/readme/big-keeper-readme.001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleme/bigkeeper/600f7c3784f78a842cb165ef795e35039f8dea03/resources/readme/big-keeper-readme.001.png -------------------------------------------------------------------------------- /resources/template/Bigkeeper: -------------------------------------------------------------------------------- 1 | # TEMPLATE FOR BIGKEEPER 2 | # PLEASE CHANGE THE CONTENT FOR YOUR PROJECT 3 | # REMEMBER DELETE THESE TIPS 4 | 5 | # version '0.0.1' 6 | 7 | # home 'BigkeeperMain', :git => 'git@github.com:BigKeeper/BigKeeperMain.git', :pulls => 'https://github.com/BigKeeper/BigKeeperMain/pulls' 8 | 9 | # source 'git@github.com:CocoaPods/Specs.git, CocoaPods' do 10 | # modules do 11 | # mod 'BigKeeperUserCenterModule', :git => 'git@github.com:BigKeeper/BigKeeperUserCenterModule.git', :pulls => 'https://github.com/BigKeeper/BigKeeperUserCenterModule/pulls' 12 | # mod 'BigKeeperOrderModule', :git => 'git@github.com:BigKeeper/BigKeeperOrderModule.git', :pulls => 'https://github.com/BigKeeper/BigKeeperOrderModule/pulls' 13 | # end 14 | # end 15 | 16 | 17 | # user 'perry' do 18 | # mod 'BigKeeperModular', :path => '../BigKeeperModular' 19 | # end 20 | 21 | # user 'tom' do 22 | # mod 'BigKeeperModular', :path => '../BigKeeperModular' 23 | # end 24 | -------------------------------------------------------------------------------- /spec/big_stash_help.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe big_keeper do 4 | 5 | end 6 | --------------------------------------------------------------------------------