├── .gitignore ├── files_to_copy ├── .hound.yml ├── .rspec ├── Gemfile ├── .travis.yml ├── circle.yml ├── .gitignore ├── README.md └── .rubocop.yml ├── Gemfile ├── tools.rb ├── README.md ├── Gemfile.lock ├── migrate_links.rb ├── LICENSE ├── migrate_code.rb └── migrate_issues.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .keys 2 | .krausefx 3 | all_cloned 4 | workspace 5 | -------------------------------------------------------------------------------- /files_to_copy/.hound.yml: -------------------------------------------------------------------------------- 1 | ruby: 2 | config_file: .rubocop.yml 3 | -------------------------------------------------------------------------------- /files_to_copy/.rspec: -------------------------------------------------------------------------------- 1 | --color --require spec_helper --format d 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | gem "octokit", "~> 4.0" 2 | gem "pry" 3 | gem "excon" 4 | gem "colored" 5 | -------------------------------------------------------------------------------- /files_to_copy/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec path: "fastlane" 4 | 5 | gem "rake" 6 | -------------------------------------------------------------------------------- /tools.rb: -------------------------------------------------------------------------------- 1 | @tools = %w(fastlane fastlane_core deliver snapshot frameit pem sigh produce cert gym pilot credentials_manager scan supply watchbuild match spaceship screengrab) 2 | -------------------------------------------------------------------------------- /files_to_copy/.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | osx_image: xcode7.1 3 | os: osx 4 | before_install: 5 | - gem update --system 6 | - gem install bundler 7 | rvm: 8 | - 2.1.6 9 | script: rake test_all 10 | -------------------------------------------------------------------------------- /files_to_copy/circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | xcode: 3 | version: "7.2" 4 | dependencies: 5 | override: 6 | - gem install bundler 7 | - bundle check --path=/tmp/vendor/bundle || bundle install --path=/tmp/vendor/bundle --jobs=4 --retry=3 8 | cache_directories: 9 | - /tmp/vendor/bundle 10 | test: 11 | override: 12 | - bundle exec rake test_all 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastlane - monorepo 2 | Scripts to migrate to a monorepo, more information on [krausefx.com](https://krausefx.com) 3 | 4 | ## Source Code 5 | 6 | ### migrate_code.rb 7 | 8 | Migrate the actual repos (source code) 9 | 10 | ### migrate_links.rb 11 | 12 | Updates all the links from 13 | 14 | ``` 15 | https://github.com/fastlane/sigh 16 | ``` 17 | 18 | to 19 | 20 | ``` 21 | https://github.com/fastlane/fastlane/tree/master/sigh 22 | ``` 23 | 24 | ### migrate_issues.rb 25 | 26 | Migrate all the GitHub issues 27 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | specs: 3 | addressable (2.5.0) 4 | public_suffix (~> 2.0, >= 2.0.2) 5 | coderay (1.1.1) 6 | colored (1.2) 7 | excon (0.54.0) 8 | faraday (0.11.0) 9 | multipart-post (>= 1.2, < 3) 10 | method_source (0.8.2) 11 | multipart-post (2.0.0) 12 | octokit (4.6.2) 13 | sawyer (~> 0.8.0, >= 0.5.3) 14 | pry (0.10.4) 15 | coderay (~> 1.1.0) 16 | method_source (~> 0.8.1) 17 | slop (~> 3.4) 18 | public_suffix (2.0.5) 19 | sawyer (0.8.1) 20 | addressable (>= 2.3.5, < 2.6) 21 | faraday (~> 0.8, < 1.0) 22 | slop (3.6.0) 23 | 24 | PLATFORMS 25 | ruby 26 | 27 | DEPENDENCIES 28 | colored 29 | excon 30 | octokit (~> 4.0) 31 | pry 32 | 33 | BUNDLED WITH 34 | 1.13.7 35 | -------------------------------------------------------------------------------- /files_to_copy/.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | /doc/ 21 | /rdoc/ 22 | 23 | ## Environment normalisation: 24 | /.bundle/ 25 | /lib/bundler/man/ 26 | 27 | # for a library or gem, you might want to ignore these files since the code is 28 | # intended to run in multiple environments; otherwise, check them in: 29 | Gemfile.lock 30 | .ruby-version 31 | .ruby-gemset 32 | 33 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 34 | .rvmrc 35 | 36 | # Added by Felix 37 | /*.itmsp 38 | .idea 39 | spec/fixtures/*.itmsp/*.png 40 | /integration 41 | **/report.xml 42 | **/.bundle 43 | -------------------------------------------------------------------------------- /migrate_links.rb: -------------------------------------------------------------------------------- 1 | require 'pry' 2 | 3 | new_repo = "fastlane" 4 | exceptions = ["countdown", "boarding", "fastlane.tools", "refresher", "examples", "setups", "shenzhen", "itc-api-docs", "enhancer", "brewed-jenkins", "codes", "code-of-conduct", "spaceship.airforce"] 5 | not_in_line = ["/graphs", "/releases", "/tree/", "/blob", "/issues"] 6 | 7 | Dir["./workspace/**/*"].each do |path| 8 | next unless File.exist?(path) 9 | next if File.directory?(path) 10 | next unless ["rb", "txt", "md"].include?(path.split(".").last) 11 | 12 | puts "Converting #{path}" 13 | 14 | content = File.read(path) 15 | content.gsub!(/https\:\/\/github.com\/fastlane\/([\w\-\d]+)[\w\d\-\/]*/) do |line| 16 | tool_name = Regexp.last_match[1] 17 | if exceptions.include?(tool_name) or not_in_line.any? { |a| line.include?(a) } 18 | line 19 | else 20 | "https://github.com/fastlane/#{new_repo}/tree/master/#{tool_name}" 21 | end 22 | end 23 | 24 | File.write(path, content) 25 | end 26 | 27 | puts "Changed files locally, open the workspace to commit and push those changes" 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 fastlane 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /migrate_code.rb: -------------------------------------------------------------------------------- 1 | # From http://scottwb.com/blog/2012/07/14/merge-git-repositories-and-preseve-commit-history/ 2 | 3 | require 'tmpdir' 4 | require 'colored' 5 | require 'pry' 6 | 7 | def cmd(command) 8 | puts "$ #{command}".yellow 9 | puts `#{command}` 10 | end 11 | 12 | require './tools' 13 | names = @tools 14 | names << "countdown" 15 | 16 | url = "https://github.com/fastlane/fastlane" # the repo everything goes to 17 | 18 | new_branch_name = "monorepo" 19 | path = Dir.mktmpdir 20 | path = "all_cloned" 21 | destination = "workspace" 22 | FileUtils.rm_rf(path) 23 | FileUtils.rm_rf(destination) 24 | FileUtils.mkdir_p(path) 25 | FileUtils.mkdir_p(destination) 26 | 27 | cmd "cd '#{destination}' && git clone '#{url}'" 28 | parent_name = url.split("/").last 29 | destination = File.join(destination, parent_name) 30 | raise "Destination repo must be the fastlane repo".red unless File.exist?(File.join(destination, "fastlane.gemspec")) 31 | 32 | Dir.chdir(destination) do 33 | cmd "git checkout -b '#{new_branch_name}'" 34 | end 35 | 36 | # Move the main tool into its subfolder 37 | subfolder_name = ENV["SUBFOLDER_NAME"] || "fastlane" 38 | 39 | 40 | def copy_with_hidden(from, to) 41 | FileUtils.mv(Dir[File.join(from, "*")], to) # move everything away to create a new fastlane folder 42 | 43 | # to also copy hidden files... 44 | Dir.foreach(from).each do |current| 45 | next if current == '.' or current == '..' 46 | next if current.include?("git") 47 | FileUtils.mv(File.join(from, current), File.join(to, File.basename(current))) 48 | end 49 | end 50 | 51 | tmp = Dir.mktmpdir 52 | copy_with_hidden(destination, tmp) 53 | # FileUtils.mv(Dir[File.join(destination, "*")], tmp) # move everything away to create a new fastlane folder 54 | FileUtils.mkdir_p(File.join(destination, subfolder_name)) 55 | # FileUtils.mv(Dir[File.join(tmp, "*")], File.join(destination, subfolder_name)) 56 | copy_with_hidden(tmp, File.join(destination, subfolder_name)) 57 | 58 | names.each do |name| 59 | cmd "cd '#{path}' && git clone 'https://github.com/fastlane/#{name}' && cd #{name} && git remote rm origin" 60 | end 61 | 62 | names.each do |name| 63 | puts "Rewriting history of '#{name}'" 64 | commit_message = "Migrate #{name} to the fastlane mono repo" 65 | commit_body = "You can read more about the change in our blog post: https://krausefx.com/blog/our-goal-to-unify-fastlane-tools" 66 | 67 | ref = File.expand_path("#{path}/#{name}") 68 | puts "Going to '#{ref}'".green 69 | Dir.chdir(ref) do 70 | cmd "mkdir #{name}" 71 | Dir.foreach(".") do |current| # foreach instead of glob to have hidden items too 72 | next if current == '.' or current == '..' 73 | next if current.include?(".git") 74 | cmd "git mv '#{current}' '#{name}/'" 75 | end 76 | cmd "git add -A" 77 | cmd "git commit -m '#{commit_message}' -m '#{commit_body}'" 78 | end 79 | 80 | puts "Going to '#{destination}' (to merge stuff)".green 81 | Dir.chdir(destination) do 82 | cmd "git remote add local_ref '#{ref}'" 83 | cmd "git pull local_ref master" 84 | cmd "git remote rm local_ref" 85 | cmd "git add -A" 86 | cmd "git commit -m '#{commit_message}' -m '#{commit_body}'" 87 | end 88 | end 89 | 90 | # foreach => hidden files as well 91 | 92 | def remove_dot_files(path) 93 | Dir.chdir(path) do 94 | Dir.foreach(".") do |current| 95 | next if current == '.' or current == '..' 96 | next if current == ".git" 97 | next if current == ".rspec" 98 | 99 | if current.start_with?(".") 100 | puts "Deleting '#{current}' dot file" 101 | FileUtils.rm_rf(current) 102 | end 103 | end 104 | end 105 | end 106 | 107 | remove_dot_files(destination) 108 | names.each do |current| 109 | remove_dot_files(File.join(destination, current)) 110 | end 111 | cmd "cd #{destination} && git add -A && git commit -m 'Removed dot files'" 112 | 113 | # Migrate the countdown repo too 114 | FileUtils.mv(File.join(destination, "countdown", "Rakefile"), File.join(destination, "Rakefile")) 115 | 116 | # Copy files from files_to_copy 117 | Dir.foreach("files_to_copy").each do |current| 118 | next if current == '.' or current == '..' 119 | FileUtils.cp(File.join("files_to_copy", current), File.join(destination, File.basename(current))) 120 | end 121 | 122 | Dir[File.join(destination, "*/Gemfile")].each do |current| # one * only, that's important, otherwise it matches ./Gemfile 123 | next if current.include?("fastlane/Gemfile") 124 | File.write(current, "source \"https://rubygems.org\"\n\ngemspec\n") 125 | end 126 | 127 | # We leave the countdown folder for now, as it also contains documentation about things 128 | Dir.chdir(destination) do 129 | cmd "git add -A" 130 | cmd "git commit -m 'Switched to fastlane mono repo'" 131 | end 132 | 133 | puts `open '#{path}'` 134 | puts `open '#{destination}'` 135 | 136 | puts "To push the changes run this:" 137 | puts "cd '#{destination}' && git push origin #{new_branch_name}".green 138 | -------------------------------------------------------------------------------- /files_to_copy/README.md: -------------------------------------------------------------------------------- 1 |

2 | fastlane Logo 3 |

4 | 5 | fastlane 6 | ============ 7 | 8 | [![Twitter: @FastlaneTools](https://img.shields.io/badge/contact-@FastlaneTools-blue.svg?style=flat)](https://twitter.com/FastlaneTools) 9 | [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/fastlane/fastlane/blob/master/LICENSE) 10 | [![Gem](https://img.shields.io/gem/v/fastlane.svg?style=flat)](http://rubygems.org/gems/fastlane) 11 | [![Build Status](https://img.shields.io/travis/fastlane/fastlane/master.svg?style=flat)](https://travis-ci.org/fastlane/fastlane) 12 | 13 | `fastlane` is a tool for iOS and Android developers to automate tedious tasks like generating screenshots, dealing with provisioning profiles and releasing your application. 14 | 15 | You define how your process looks like 16 | 17 | ```ruby 18 | lane :beta do 19 | increment_build_number 20 | cocoapods 21 | match 22 | deliver 23 | sh "./customScript.sh" 24 | 25 | slack 26 | end 27 | ``` 28 | 29 | To deploy your app to the App Store you can run 30 | ``` 31 | fastlane beta 32 | ``` 33 | 34 | 35 | | fastlane 36 | --------------------------|------------------------------------------------------------ 37 | :sparkles: | Connect all iOS and Android build tools into one workflow (both `fastlane` tools and third party tools) 38 | :monorail: | Define different `deployment lanes` for App Store deployment, beta builds or testing 39 | :ship: | Deploy from any computer, including a CI-server 40 | :wrench: | Extend and customise the functionality 41 | :thought_balloon: | Never remember any difficult commands, just `fastlane` 42 | :tophat: | Easy setup assistant to get started in a few minutes 43 | :email: | Automatically pass on information from one build step to another (e.g. path to the `ipa` file) 44 | :page_with_curl: | Store **everything** in git. Never again lookup the build commands in the `Jenkins` configs 45 | :rocket: | Saves you **hours** for every app update you release 46 | :pencil2: | Very flexible configuration using a fully customisable `Fastfile` 47 | :mountain_cableway: | Implement a fully working Continuous Delivery process 48 | :ghost: | [Jenkins Integration](https://github.com/fastlane/fastlane/blob/master/docs/Jenkins.md): Show the output directly in the Jenkins test results 49 | :book: | Automatically generate a markdown documentation of your lane config 50 | :hatching_chick: | Over 150 built-in integrations available 51 | :computer: | Support for both iOS, Mac OS and Android apps 52 | :octocat: | Full git and mercurial support 53 | 54 | ##### Like this tool? [Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx). 55 | 56 | ## Installation 57 | 58 | sudo gem install fastlane --verbose 59 | 60 | Make sure, you have the latest version of the Xcode command line tools installed: 61 | 62 | xcode-select --install 63 | 64 | If you experience slow launch times of fastlane, try running 65 | 66 | gem cleanup 67 | 68 | System Requirements: `fastlane` requires Mac OS X or Linux with Ruby 2.0.0 or above. 69 | 70 | If you want to take a look at a project, already using `fastlane`, check out the [fastlane-examples](https://github.com/fastlane/examples) with `fastlane` setups by Wikipedia, Product Hunt, MindNode and more. 71 | 72 | ## Quick Start 73 | 74 | The setup assistant will create all the necessary files for you, using the existing app metadata from iTunes Connect. 75 | 76 | - ```cd [your_project_folder]``` 77 | - ```fastlane init``` 78 | - Follow the setup assistant, which will set up ```fastlane``` for you 79 | - Further customise the ```Fastfile``` with [actions](https://github.com/fastlane/fastlane/blob/master/docs/Actions.md). 80 | 81 | ## Available commands 82 | 83 | Usually you'll use fastlane by triggering individual lanes: 84 | 85 | fastlane [lane_name] 86 | 87 | #### Other commands 88 | 89 | - `fastlane actions`: List all available `fastlane` actions 90 | - `fastlane action [action_name]`: Shows a more detailed description of an action 91 | - `fastlane lanes`: Lists all available lanes with description 92 | - `fastlane list`: Lists all available lanes without description 93 | - `fastlane new_action`: Create a new action (integration) for fastlane 94 | 95 | ## [`fastlane`](https://fastlane.tools) Toolchain 96 | 97 | Additionally to the `fastlane` commands, you now have access to those `fastlane` tools: 98 | 99 | - [`deliver`](https://github.com/fastlane/deliver): Upload screenshots, metadata and your app to the App Store 100 | - [`snapshot`](https://github.com/fastlane/snapshot): Automate taking localized screenshots of your iOS app on every device 101 | - [`frameit`](https://github.com/fastlane/frameit): Quickly put your screenshots into the right device frames 102 | - [`pem`](https://github.com/fastlane/pem): Automatically generate and renew your push notification profiles 103 | - [`sigh`](https://github.com/fastlane/sigh): Because you would rather spend your time building stuff than fighting provisioning 104 | - [`produce`](https://github.com/fastlane/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line 105 | - [`cert`](https://github.com/fastlane/cert): Automatically create and maintain iOS code signing certificates 106 | - [`codes`](https://github.com/fastlane/codes): Create promo codes for iOS Apps using the command line 107 | - [`spaceship`](https://github.com/fastlane/spaceship): Ruby library to access the Apple Dev Center and iTunes Connect 108 | - [`pilot`](https://github.com/fastlane/pilot): The best way to manage your TestFlight testers and builds from your terminal 109 | - [`boarding`](https://github.com/fastlane/boarding): The easiest way to invite your TestFlight beta testers 110 | - [`gym`](https://github.com/fastlane/gym): Building your iOS apps has never been easier 111 | - [`match`](https://github.com/fastlane/match): Easily sync your certificates and profiles across your team using git 112 | 113 | ## Need help? 114 | Please submit an issue on GitHub and provide information about your setup 115 | 116 | ## Special Thanks 117 | 118 | Thanks to all [contributors](https://github.com/fastlane/fastlane/graphs/contributors) for extending and improving `fastlane`. 119 | 120 | ## Code of Conduct 121 | 122 | Help us keep `fastlane` open and inclusive. Please read and follow our [Code of Conduct](https://github.com/fastlane/code-of-conduct). 123 | 124 | ## License 125 | This project is licensed under the terms of the MIT license. See the LICENSE file. 126 | 127 | > This project and all fastlane tools are in no way affiliated with Apple Inc or Google. This project is open source under the MIT license, which means you have full access to the source code and can modify it to fit your own needs. All fastlane tools run on your own computer or server, so your credentials or other sensitive information will never leave your own computer. You are responsible for how you use fastlane tools. 128 | -------------------------------------------------------------------------------- /files_to_copy/.rubocop.yml: -------------------------------------------------------------------------------- 1 | 2 | Style/ClassCheck: 3 | EnforcedStyle: kind_of? 4 | 5 | # Cop supports --auto-correct. 6 | # Configuration parameters: EnforcedStyle, SupportedStyles. 7 | Style/BracesAroundHashParameters: 8 | Enabled: false 9 | 10 | Lint/UselessAssignment: 11 | Exclude: 12 | - '**/spec/**/*' 13 | 14 | # Cop supports --auto-correct. 15 | # Configuration parameters: EnforcedStyle, SupportedStyles. 16 | Style/IndentHash: 17 | Enabled: false 18 | 19 | Style/RaiseArgs: 20 | EnforcedStyle: exploded 21 | 22 | Style/DoubleNegation: 23 | Enabled: false 24 | 25 | Lint/HandleExceptions: 26 | Enabled: false 27 | 28 | # Cop supports --auto-correct. 29 | Lint/UnusedBlockArgument: 30 | Enabled: false 31 | 32 | # Needed for $verbose 33 | Style/GlobalVars: 34 | Enabled: false 35 | 36 | Style/FileName: 37 | Enabled: false 38 | 39 | # $? Exit 40 | Style/SpecialGlobalVars: 41 | Enabled: false 42 | 43 | Metrics/AbcSize: 44 | Max: 63 45 | Exclude: 46 | - '**/lib/*/options.rb' 47 | 48 | # Both string notations are okay 49 | Style/StringLiterals: 50 | Enabled: false 51 | 52 | # The %w might be confusing for new users 53 | Style/WordArray: 54 | MinSize: 19 55 | 56 | # Not a good thing 57 | Style/RedundantSelf: 58 | Enabled: false 59 | 60 | # raise and fail are both okay 61 | Style/SignalException: 62 | Enabled: false 63 | 64 | # Better too much 'return' than one missing 65 | Style/RedundantReturn: 66 | Enabled: false 67 | 68 | # Having if in the same line might not always be good 69 | Style/IfUnlessModifier: 70 | Enabled: false 71 | 72 | # That looks wrong 73 | Style/AlignHash: 74 | Enabled: false 75 | 76 | # and and or is okay 77 | Style/AndOr: 78 | Enabled: false 79 | 80 | # Configuration parameters: CountComments. 81 | Metrics/ClassLength: 82 | Max: 320 83 | 84 | Metrics/CyclomaticComplexity: 85 | Max: 17 86 | 87 | # Configuration parameters: AllowURI, URISchemes. 88 | Metrics/LineLength: 89 | Max: 370 90 | 91 | # Configuration parameters: CountKeywordArgs. 92 | Metrics/ParameterLists: 93 | Max: 17 94 | 95 | Metrics/PerceivedComplexity: 96 | Max: 18 97 | 98 | Style/DotPosition: 99 | Enabled: false 100 | 101 | Style/GuardClause: 102 | Enabled: false 103 | 104 | 105 | 106 | # Split 107 | 108 | 109 | # e.g. 110 | # def self.is_supported?(platform) 111 | # we may never use `platform` 112 | Lint/UnusedMethodArgument: 113 | Enabled: false 114 | 115 | # the let(:key) { ... } 116 | Lint/ParenthesesAsGroupedExpression: 117 | Exclude: 118 | - '**/spec/**/*' 119 | 120 | # We use `is_supported?` everywhere already 121 | Style/PredicateName: 122 | Enabled: false 123 | 124 | # Disable '+ should be surrounded with a single space' for xcodebuild_spec.rb 125 | Style/SpaceAroundOperators: 126 | Exclude: 127 | - '**/spec/actions_specs/xcodebuild_spec.rb' 128 | 129 | Metrics/MethodLength: 130 | Exclude: 131 | - '**/lib/fastlane/actions/*.rb' 132 | - '**/bin/fastlane' 133 | - '**/lib/*/options.rb' 134 | Max: 60 135 | 136 | AllCops: 137 | Include: 138 | - '**/fastlane/Fastfile' 139 | Exclude: 140 | - '**/lib/assets/custom_action_template.rb' 141 | 142 | ################## 143 | # TODO 144 | ################## 145 | 146 | # Offense count: 7 147 | # Configuration parameters: CountComments. 148 | Metrics/ClassLength: 149 | Max: 320 150 | 151 | # Offense count: 4 152 | Metrics/CyclomaticComplexity: 153 | Max: 17 154 | 155 | # Offense count: 489 156 | # Configuration parameters: AllowURI, URISchemes. 157 | Metrics/LineLength: 158 | Max: 372 159 | 160 | # Offense count: 5 161 | # Configuration parameters: CountKeywordArgs. 162 | Metrics/ParameterLists: 163 | Max: 17 164 | 165 | # Offense count: 3 166 | Metrics/PerceivedComplexity: 167 | Max: 18 168 | 169 | # Offense count: 1 170 | # Cop supports --auto-correct. 171 | Style/Alias: 172 | Enabled: false 173 | 174 | # Offense count: 14 175 | # Cop supports --auto-correct. 176 | # Configuration parameters: EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle, SupportedLastArgumentHashStyles. 177 | Style/AlignHash: 178 | Enabled: false 179 | 180 | # Offense count: 22 181 | # Cop supports --auto-correct. 182 | # Configuration parameters: EnforcedStyle, SupportedStyles. 183 | Style/AndOr: 184 | Enabled: false 185 | 186 | # Offense count: 1 187 | # Configuration parameters: EnforcedStyle, SupportedStyles. 188 | Style/ClassAndModuleChildren: 189 | Enabled: false 190 | 191 | # Offense count: 19 192 | Style/Documentation: 193 | Enabled: false 194 | 195 | # Offense count: 112 196 | # Cop supports --auto-correct. 197 | # Configuration parameters: EnforcedStyle, SupportedStyles. 198 | Style/DotPosition: 199 | Enabled: false 200 | 201 | # Offense count: 12 202 | # Cop supports --auto-correct. 203 | # Configuration parameters: EnforcedStyle, SupportedStyles. 204 | Style/EmptyLinesAroundClassBody: 205 | Enabled: false 206 | 207 | # Configuration parameters: MinBodyLength. 208 | Style/GuardClause: 209 | Enabled: false 210 | 211 | # Offense count: 4 212 | # Cop supports --auto-correct. 213 | # Configuration parameters: MaxLineLength. 214 | Style/IfUnlessModifier: 215 | Enabled: false 216 | 217 | # Offense count: 74 218 | # Cop supports --auto-correct. 219 | # Configuration parameters: EnforcedStyle, SupportedStyles. 220 | Style/MultilineOperationIndentation: 221 | Enabled: false 222 | 223 | # Offense count: 10 224 | # Cop supports --auto-correct. 225 | Style/NumericLiterals: 226 | MinDigits: 14 227 | 228 | # Offense count: 2 229 | # Cop supports --auto-correct. 230 | Style/PerlBackrefs: 231 | Enabled: false 232 | 233 | # Offense count: 19 234 | # Cop supports --auto-correct. 235 | # Configuration parameters: AllowMultipleReturnValues. 236 | Style/RedundantReturn: 237 | Enabled: false 238 | 239 | # Offense count: 77 240 | # Cop supports --auto-correct. 241 | Style/RedundantSelf: 242 | Enabled: false 243 | 244 | # Offense count: 38 245 | # Cop supports --auto-correct. 246 | # Configuration parameters: EnforcedStyle, SupportedStyles. 247 | Style/SignalException: 248 | Enabled: false 249 | 250 | # Offense count: 5 251 | # Cop supports --auto-correct. 252 | # Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. 253 | Style/SpaceInsideBlockBraces: 254 | Enabled: false 255 | 256 | # Offense count: 291 257 | # Cop supports --auto-correct. 258 | # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles. 259 | Style/SpaceInsideHashLiteralBraces: 260 | Enabled: false 261 | 262 | # Offense count: 8 263 | # Cop supports --auto-correct. 264 | Style/SpaceInsideParens: 265 | Enabled: false 266 | 267 | # Offense count: 881 268 | # Cop supports --auto-correct. 269 | # Configuration parameters: EnforcedStyle, SupportedStyles. 270 | Style/StringLiterals: 271 | Enabled: false 272 | 273 | # Offense count: 9 274 | # Cop supports --auto-correct. 275 | # Configuration parameters: WordRegex. 276 | Style/WordArray: 277 | MinSize: 19 278 | -------------------------------------------------------------------------------- /migrate_issues.rb: -------------------------------------------------------------------------------- 1 | require 'octokit' 2 | require 'pry' 3 | require 'excon' 4 | require 'colored' 5 | require 'json' 6 | 7 | 8 | class Hendl 9 | # e.g. KrauseFx/fastlane 10 | attr_accessor :source 11 | attr_accessor :destination 12 | 13 | attr_accessor :open_only 14 | 15 | # Reason on why this was necessary 16 | attr_accessor :reason 17 | 18 | def initialize(source: nil, destination: nil, reason: nil, open_only: false) 19 | self.source = source 20 | self.destination = destination 21 | self.reason = reason 22 | self.open_only = open_only 23 | self.start 24 | end 25 | 26 | def client 27 | @client ||= Octokit::Client.new(access_token: ENV["GITHUB_API_TOKEN"]) 28 | end 29 | 30 | def start 31 | client.auto_paginate = true 32 | puts "Fetching issues from '#{source}'..." 33 | counter = 0 34 | client.issues(source, per_page: 1000, state: "all").each do |original| 35 | if self.open_only and original.state != "open" 36 | puts "Skipping #{original.number} as it's not an open one" 37 | next 38 | end 39 | 40 | next unless original.pull_request.nil? # no PRs for now 41 | 42 | labels = original.labels.collect { |a| a[:name] } 43 | if labels.include?("migrated") or labels.include?("migration_failed") 44 | puts "Skipping #{original.number} because it's already migrated or failed" 45 | next 46 | end 47 | 48 | hendl(original) 49 | smart_sleep 50 | counter += 1 51 | end 52 | puts "[SUCCESS] Migrated #{counter} issues / PRs" 53 | end 54 | 55 | def hendl(original) 56 | puts "Hendling #{original.number}" 57 | if original.pull_request.nil? 58 | hendl_issue(original) 59 | else 60 | # hendl_pr(original) 61 | end 62 | end 63 | 64 | def smart_sleep 65 | # via https://developer.github.com/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits 66 | # at least one second between requests 67 | # also https://developer.github.com/v3/#rate-limiting 68 | # maximum of 5000 requests an hour => 83 requests per minute 69 | sleep 2.5 70 | end 71 | 72 | def table(user_id, body) 73 | " 74 | 75 | 78 | 81 | 82 |
76 | 77 | 79 | #{body} 80 |
" 83 | end 84 | 85 | # We copy over all the issues, and also mention everyone 86 | # so that people are automatically subscribed to notifications 87 | def hendl_issue(original) 88 | original_comments = client.issue_comments(source, original.number) 89 | comments = [] 90 | original_comments.each do |original_comment| 91 | table_code = table(original_comment.user.id, "@#{original_comment.user.login} commented") 92 | body = [table_code, original_comment.body] 93 | comments << { 94 | created_at: original_comment.created_at.iso8601, 95 | body: body.join("\n\n") 96 | } 97 | end 98 | 99 | actual_label = original.labels.collect { |a| a[:name] } 100 | 101 | tool_name_label = source.split("/").last 102 | table_link = "Imported from #{source}##{original.number}" 103 | table_code = table(original.user.id, "Original issue by @#{original.user.login} - #{table_link}") 104 | body = [table_code, original.body] 105 | data = { 106 | issue: { 107 | title: original.title, 108 | body: body.join("\n\n"), 109 | created_at: original.created_at.iso8601, 110 | labels: actual_label + [tool_name_label], 111 | closed: original.state != "open" 112 | }, 113 | comments: comments 114 | } 115 | data[:issue][:closed_at] = original.closed_at.iso8601 if original.state != "open" 116 | 117 | response = Excon.post("https://api.github.com/repos/#{destination}/import/issues", body: data.to_json, headers: request_headers) 118 | response = JSON.parse(response.body) 119 | status_url = response['url'] 120 | puts response 121 | 122 | new_issue_url = nil 123 | 124 | begin 125 | (5..35).each do |request_num| 126 | sleep(request_num) 127 | 128 | puts "Sending #{status_url}" 129 | async_response = Excon.get(status_url, headers: request_headers) # if this crashes, make sure to have a valid token with admin permission to the actual repo 130 | async_response = JSON.parse(async_response.body) 131 | puts async_response.to_s.yellow 132 | 133 | new_issue_url = async_response['issue_url'] 134 | break if new_issue_url.to_s.length > 0 135 | puts "unable to get new issue url for #{original.number} after #{request_num - 4} requests".yellow 136 | end 137 | rescue => ex 138 | puts "Something went wrong, wups" 139 | puts ex.to_s 140 | # If the error message is 141 | # {"message"=>"Not Found", "documentation_url"=>"https://developer.github.com/v3"} 142 | # that just means that fastlane-bot doesn't have admin access 143 | end 144 | 145 | if new_issue_url.to_s.length > 0 146 | new_issue_url.gsub!("api.github.com/repos", "github.com") 147 | 148 | client.update_issue(source, original.number, labels: (actual_label + ["migrated"])) 149 | 150 | # reason, link to the new issue 151 | puts "closing old issue #{original.number}" 152 | body = [] 153 | body << "This issue was migrated to #{new_issue_url}. Please post all further comments there." 154 | body << reason 155 | puts new_issue_url 156 | client.add_comment(source, original.number, body.join("\n\n")) 157 | smart_sleep 158 | client.close_issue(source, original.number) unless original.state == "closed" 159 | else 160 | puts "unable to find new issue url, not closing or commenting".red 161 | client.update_issue(source, original.number, labels: (actual_label + ["migration_failed"])) 162 | puts "Status URL: #{status_url}" 163 | # This means we have to manually migrate the issue 164 | # if you want to try it again, just remove the migration_failed tag 165 | end 166 | end 167 | 168 | def request_headers 169 | { 170 | "Accept" => "application/vnd.github.golden-comet-preview+json", 171 | "Authorization" => ("token " + ENV["GITHUB_API_TOKEN"]), 172 | "Content-Type" => "application/x-www-form-urlencoded", 173 | "User-Agent" => "fastlane bot" 174 | } 175 | end 176 | 177 | # We want to comment on PRs and tell the user to re-submit it 178 | # on the new repo, as we can't migrate them automatically 179 | def hendl_pr(original) 180 | puts "#{original.number} is a pull request" 181 | if original.state != "open" 182 | puts "#{original.number} is already closed - nothing to do here" 183 | return 184 | end 185 | 186 | body = ["Hello @#{original.user.login},"] 187 | body << reason 188 | body << "Sorry for the troubles, we'd appreciate if you could re-submit your Pull Request with these changes to the new repository" 189 | 190 | client.add_comment(source, original.number, body.join("\n\n")) 191 | smart_sleep 192 | client.close_pull_request(source, original.number) 193 | end 194 | end 195 | 196 | require './tools' 197 | destination = "fastlane/fastlane" # TODO: Should be fastlane 198 | names = Array(ENV["TOOL"] || @tools.delete_if { |a| a == "fastlane" }) # we don't want to import issues from our own repo 199 | open_only = !ENV["ALL"] 200 | 201 | puts "Migrating #{names.join(', ')}" 202 | 203 | names.each do |current| 204 | Hendl.new(source: "fastlane/#{current}", 205 | destination: destination, 206 | reason: "`fastlane` is now a mono repo, you can read more about the change in our [blog post](https://krausefx.com/blog/our-goal-to-unify-fastlane-tools). All tools are now available in the [fastlane main repo](https://github.com/fastlane/fastlane) :rocket:", 207 | open_only: open_only) 208 | end 209 | --------------------------------------------------------------------------------