├── .gitignore ├── README.md ├── bin └── gitlab-clone ├── changelog ├── gitlab_clone.gemspec └── lib ├── github.rb ├── gitlab.rb ├── setup.rb ├── version └── version.rb /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore builder and gemfiles 2 | *.gem 3 | buildagain 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitlab-clone [![Gem Version](https://badge.fury.io/rb/gitlab_clone.png)](http://badge.fury.io/rb/gitlab_clone) 2 | 3 | ## Description 4 | gitlab-clone allows you to clone repositories from a gitlab server's group using the gitlab api. 5 | 6 | The itch I was scratching by writing this was that I was writing cookbooks for home and work. I wanted an easier way to download all of my cookbooks so that I could work with all of them at once and not have to break my flow by having to download depended cookbooks later. Anyone that has worked with chef knows that you can have a lot of repos comporising all of your cookbooks... 7 | 8 | 9 | Your tokens for either Gitlab or Gitgub are stored in the settings of your user profile. 10 | 11 | 12 | For gitlab it will be: 13 | 14 | http://gitlab_server/profile/account 15 | 16 | 17 | For github it will be: 18 | 19 | https://github.com/settings/applications 20 | 21 | ## Features 22 | gitlab-clone currently features the following: 23 | * Github organization supprt. You can bring down all the repos in an organization that you have access to. 24 | * Default cloning of all repos that are in a group called Cookbooks. 25 | * Can pick what group you can download all the repos in that group from. 26 | * Will do a git pull if an existing repo has been detected. 27 | * Support for using either ssh or the web url to download. 28 | * List the repos in a group before cloning them. 29 | 30 | ## Examples 31 | ### Get a list of repos in a group named Home: 32 | gitlab-clone -l -g Home 33 | 34 | \------------------------------------------------------------------- 35 | 36 | 37 | The following 3 repo(s) were found in the group Home. 38 | 39 | Repo1 40 | Repo2 41 | Repo3 42 | 43 | \------------------------------------------------------------------- 44 | 45 | ### Clone the repos in the group named Home: 46 | gitlab-clone -w -g Home 47 | 48 | \------------------------------------------------------------------- 49 | 50 | 51 | ### Starting Web Clone Process Of The Group Home ### 52 | 53 | Downloading 3 repo(s) into [HOME_DIR]/projects/Home 54 | 55 | Repo1 directory exists, doing a git pull instead. 56 | Cloning Repo2... 57 | Cloning Repo3... 58 | 59 | \------------------------------------------------------------------- 60 | 61 | ### Clone the repos in the group named Home from Github: 62 | gitlab-clone -w -g Home -o 63 | 64 | \------------------------------------------------------------------- 65 | 66 | 67 | ### Starting Web Clone Process Of The Group Home ### 68 | 69 | Downloading 3 repo(s) into [HOME_DIR]/projects/Home 70 | 71 | Repo1 directory exists, doing a git pull instead. 72 | Cloning Repo2... 73 | Cloning Repo3... 74 | 75 | \------------------------------------------------------------------- 76 | 77 | 78 | 79 | ## Installation 80 | 81 | gem install gitlab_clone 82 | Will give you a command named gitlab-clone to use. 83 | -------------------------------------------------------------------------------- /bin/gitlab-clone: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Lets require our gems and start this 4 | require "json" 5 | require "fileutils" 6 | require "git" 7 | require "gitlab" 8 | require "setup" 9 | require "github" 10 | require "version" 11 | require "slop" 12 | require "httparty" 13 | require "rainbow" 14 | 15 | class GitlabClone 16 | def self.dothething 17 | opts = Slop.parse do |o| 18 | o.bool '-h', '--help', 'help' 19 | o.bool '-c', '--clone', 'clone' 20 | o.bool '-w', '--web', 'web' 21 | o.bool '-l', '--list', 'list' 22 | o.bool '-v', '--version', 'version' 23 | o.string '-g', '--group', 'group' 24 | o.bool '-o', '--github', 'github' 25 | o.bool '-n', '--config', 'print config' 26 | o.bool '-r', '--reconfigure', 'reconfigure gitlab server and token settings' 27 | end 28 | 29 | # If no arugments are passed, or none of approved ones, give help information 30 | if ARGV[0].nil? 31 | Gitlab.printhelp 32 | end 33 | 34 | if opts[:github] && !Setup.github_precheck 35 | puts Rainbow("Whoops looks like you have not set your Github token yet. Would you like to do so now?").yellow 36 | answer = STDIN.gets.chomp.downcase 37 | if answer == "y" or answer == "yes" 38 | Setup.github_configure 39 | end 40 | exit; 41 | end 42 | 43 | if opts[:clone] && opts[:web] 44 | puts Rainbow("\n\t############################################").red 45 | puts Rainbow("\t You can't web clone and ssh clone").red 46 | puts Rainbow("\t gitlab-clone -h for more information").red 47 | puts Rainbow("\t############################################\n").red 48 | else 49 | 50 | if opts[:github] && !opts[:group] 51 | puts Rainbow("Github option requiers you to enter a organizational name.\n\n").red 52 | end 53 | 54 | if opts[:group] 55 | clone_group = opts[:group] 56 | elsif opts[:list] || opts[:github] || opts[:web] || opts[:clone] 57 | puts Rainbow("\nNo organizational or group name was given. Using default of \"Cookbooks\"\n\n").purple.italic 58 | clone_group = "Cookbooks" 59 | end 60 | 61 | if opts[:github] 62 | class_to_use = Github 63 | else 64 | class_to_use = Gitlab 65 | end 66 | 67 | begin 68 | if opts[:help] 69 | Gitlab.printhelp 70 | elsif opts[:reconfigure] 71 | Setup.configure 72 | elsif opts[:clone] 73 | class_to_use.clone(0, clone_group) 74 | elsif opts[:web] 75 | class_to_use.clone(1, clone_group) 76 | elsif opts[:version] 77 | Version::version 78 | elsif opts[:config] 79 | Gitlab.config 80 | elsif opts[:list] 81 | class_to_use.list_repos(clone_group) 82 | end 83 | rescue 84 | puts Rainbow("Looks like there was an issue. Please verify host and api version with \"#{File.basename($0)} -n\" to show configuration.").red.italic 85 | end 86 | end 87 | end 88 | end 89 | 90 | begin 91 | GitlabClone.dothething 92 | rescue Interrupt 93 | puts Rainbow("\n\nStopping...").yellow.bright 94 | rescue 95 | puts Rainbow("\nUnknown options. Wanna try that again?").red.bright 96 | end 97 | -------------------------------------------------------------------------------- /changelog: -------------------------------------------------------------------------------- 1 | v 11.0 2 | - Added support for github 3 | 4 | v 10.2 5 | - Initial release 6 | 7 | -------------------------------------------------------------------------------- /gitlab_clone.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('../lib', __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'version' 4 | 5 | Gem::Specification.new do |s| 6 | s.name = 'gitlab_clone' 7 | s.version = Version::current 8 | s.date = '2019-01-30' 9 | s.summary = "Pulls down the latest repos from a group in gitlab or a organization group in Github." 10 | s.description = "Clones All Repos In A Gitlab Group Or Github Org." 11 | s.authors = ["Nestor N. Camacho III"] 12 | s.email = 'ncamacho@nnc3.com.to' 13 | s.files = `git ls-files`.split($/) 14 | s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } 15 | s.add_dependency("git", "~> 1.2.6") 16 | s.add_dependency("slop", ">= 4.0.0") 17 | s.add_dependency("httparty", "~> 0.13.1") 18 | s.add_dependency("rainbow", "~> 3.0.0") 19 | s.homepage = 'https://github.com/ncamacho/gitlab_clone' 20 | s.license = 'MIT' 21 | s.post_install_message = "\n\n\tGitlab Clone is now installed!\n\n" 22 | end 23 | -------------------------------------------------------------------------------- /lib/github.rb: -------------------------------------------------------------------------------- 1 | class Github 2 | TOKEN=ENV['github_token'] 3 | SERVER="https://api.github.com" 4 | HOME=ENV['HOME'] 5 | 6 | 7 | def self.list_repos(group_name) 8 | repos_list = get_repos(group_name) 9 | puts "-------------------------------------------------------------------\n" 10 | puts "\tThe following #{repos_list.count} repo(s) were found in the Org #{group_name}.\n\n" 11 | repos_list.each do |get| 12 | puts "\t\t#{get["name"]}" 13 | end 14 | puts "\n-------------------------------------------------------------------" 15 | end 16 | 17 | 18 | def self.clone(web, group_name) 19 | repos_list = get_repos(group_name) 20 | repos_dir = "#{HOME}/projects/#{group_name}" 21 | 22 | if File.directory?("#{repos_dir}") 23 | FileUtils::mkdir_p repos_dir 24 | end 25 | 26 | if web == 1 27 | repo_location = 'clone_url' 28 | message = "Web" 29 | else 30 | repo_location = 'ssh_url' 31 | message = "Ssh" 32 | end 33 | puts "-------------------------------------------------------------------\n" 34 | puts "\t### Starting #{message} Clone Process Of The Org #{group_name} ###\n\n" 35 | puts "\tDownloading #{repos_list.count} repo(s) into #{repos_dir}\n\n" 36 | 37 | repos_list.each do |get| 38 | repo_name = get["name"] 39 | repo = get["#{repo_location}"] 40 | dir = get["name"] 41 | repo_dir = "#{repos_dir}/#{dir}" 42 | 43 | if File.directory?("#{repo_dir}") 44 | puts "\t#{repo_name} directory exists, doing a git pull instead." 45 | Dir.chdir("#{repo_dir}") 46 | g = Git.init 47 | g.pull 48 | else 49 | puts "\tCloning #{repo_name}..." 50 | Git.clone("#{repo}", "#{repo_dir}") 51 | end 52 | end 53 | puts "-------------------------------------------------------------------\n" 54 | end 55 | 56 | def self.get_repos(group_name) 57 | string = HTTParty.get("#{SERVER}/orgs/#{group_name}/repos", :headers => {"Authorization" => "token #{TOKEN}", 'User-Agent' => 'HTTParty'}, :verify => false).to_json 58 | rep = JSON.parse(string) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/gitlab.rb: -------------------------------------------------------------------------------- 1 | class Gitlab 2 | require "setup" 3 | require "rainbow" 4 | 5 | user_home = ENV['HOME'] 6 | user_defined_dir = "code" ### need to add the ability to set this dynamically 7 | HOME = "#{user_home}/#{user_defined_dir}" 8 | 9 | def self.printhelp 10 | puts Rainbow("---------------------------------------------------------------------------------------\n").green 11 | puts Rainbow("\t\t###### Options for #{File.basename($0)} ######\n\n").green 12 | puts Rainbow("\t-h, --help: shows this help message").green 13 | puts Rainbow("\t-c, --clone: clones all repos from https://gitlab_server/groups/Cookbooks 14 | into a ~/code/Cookbooks directory by default").green 15 | puts Rainbow("\t-w, --web: will clone using web protocol instead of ssh").green 16 | puts Rainbow("\t-l, --list: will give you a list of repos in git").green 17 | puts Rainbow("\t-g, --group: will let you choose which gitlab group to look for repos in").green 18 | puts Rainbow("\t-o, --github: will allow you to clone from Github").green 19 | puts Rainbow("\t-n, --config: will print your current configuration settings").green 20 | puts Rainbow("\t-r, --reconfigure: will start the configuration process\n\n").green 21 | puts Rainbow("\t NOTE: You need to configure your settings first, with -r or --reconfigure.").green 22 | puts Rainbow("\n---------------------------------------------------------------------------------------\n\n").green 23 | end 24 | 25 | def self.list_repos(group_name) 26 | repos_list = get_repos(group_name) 27 | puts Rainbow("-------------------------------------------------------------------\n").green 28 | puts Rainbow("\tThe following #{repos_list["projects"].length} repo(s) were found in the group #{group_name}.").green 29 | repos_list["projects"].length.times do |get| 30 | puts Rainbow("\t\t#{repos_list["projects"][get]["name"]}").blue 31 | end 32 | puts Rainbow("\n-------------------------------------------------------------------").green 33 | end 34 | 35 | def self.clone(web, group_name) 36 | repos_list = get_repos(group_name) 37 | repos_dir = "#{HOME}/#{group_name}" 38 | 39 | if File.directory?("#{repos_dir}") 40 | FileUtils::mkdir_p repos_dir 41 | end 42 | 43 | if web == 1 44 | repo_location = 'http_url_to_repo' 45 | message = "Web" 46 | else 47 | repo_location = 'ssh_url_to_repo' 48 | message = "Ssh" 49 | end 50 | puts Rainbow("-------------------------------------------------------------------\n").green 51 | puts Rainbow("\t### Starting #{message} Clone Process Of The Group #{group_name} ###").green 52 | puts Rainbow("\tDownloading #{repos_list["projects"].length} repo(s) into #{repos_dir}\n").green 53 | 54 | repos_list["projects"].length.times do |get| 55 | repo_name = repos_list["projects"][get]["name"] 56 | repo = repos_list["projects"][get]["#{repo_location}"] 57 | dir = repos_list["projects"][get]["name"] 58 | repo_dir = "#{repos_dir}/#{dir}" 59 | 60 | if File.directory?("#{repo_dir}") 61 | puts Rainbow("\t\"#{repo_name}\" repo directory exists, doing a git pull instead.").purple 62 | Dir.chdir("#{repo_dir}") 63 | g = Git.init 64 | g.pull 65 | else 66 | puts Rainbow("\tCloning repo \"#{repo_name}\"...").blue 67 | Git.clone("#{repo}", "#{repo_dir}") 68 | end 69 | end 70 | puts Rainbow("\n-------------------------------------------------------------------\n").green 71 | end 72 | 73 | def self.get_repos(group_name) 74 | group_id = get_groups[group_name] 75 | string = HTTParty.get("#{Setup.get_gitlabserver}/groups/#{group_id}", :headers => {"PRIVATE-TOKEN" => "#{Setup.get_token}" }, :verify => false).to_json 76 | rep = JSON.parse(string) 77 | end 78 | 79 | def self.get_groups 80 | string = HTTParty.get("#{Setup.get_gitlabserver}/groups", :headers => {"PRIVATE-TOKEN" => "#{Setup.get_token}" }, :verify => false).to_json 81 | api_ids = JSON.parse(string) 82 | group_ids = {} 83 | api_ids.each do |id| 84 | group_ids["#{id["name"]}"] = id["id"] 85 | end 86 | return group_ids 87 | end 88 | 89 | def self.config 90 | puts Rainbow("################### Current Gitlab Configuration ###################").green 91 | puts 92 | puts Rainbow("\tCurrent Gitlab token:\t\t #{Setup.get_token}").green 93 | puts Rainbow("\tCurrent Gitlab server address:\t #{Setup.get_gitlabserver}").green 94 | if Setup.github_precheck 95 | puts Rainbow("\tCurrent Github token:\t\t #{Setup.get_github_token}").green 96 | end 97 | puts Rainbow("\tCurrent home directory:\t\t #{HOME}").green 98 | puts 99 | puts Rainbow("#######################################################################").green 100 | end 101 | 102 | end 103 | -------------------------------------------------------------------------------- /lib/setup.rb: -------------------------------------------------------------------------------- 1 | class Setup 2 | require "rainbow" 3 | 4 | user_home=ENV['HOME'] 5 | CONFIG = "#{user_home}/.gitlab_config" 6 | 7 | ### This checks to see if the json we are getting from the file is valid or not. 8 | def self.valid_json 9 | JSON.parse(File.read(CONFIG)) 10 | return true 11 | rescue JSON::ParserError 12 | return false 13 | end 14 | 15 | ### Precheck to see if we have a token yet in the file 16 | def self.github_precheck 17 | if File.exist?(CONFIG) && self.valid_json 18 | JSON.parse(File.read(CONFIG)) 19 | params = JSON.parse(File.read(CONFIG)) 20 | if params["github_token"] 21 | return true 22 | else 23 | return false 24 | end 25 | else 26 | return false 27 | end 28 | end 29 | 30 | ### Prehceck to see if we dont have a file or vaild json in the file 31 | def self.precheck 32 | if !File.exist?(CONFIG) or !self.valid_json 33 | return false 34 | else 35 | return true 36 | end 37 | end 38 | 39 | ### This does a check to see if we have a server or token set and if we don't start the configureation process 40 | def self.check 41 | if File.exist?(CONFIG) && self.valid_json 42 | JSON.parse(File.read(CONFIG)) 43 | params = JSON.parse(File.read(CONFIG)) 44 | unless params["gitlab_server"] && params["gitlab_token"] 45 | puts Rainbow("It looks like either your server or token is not set properly. Do you wish to add them now? ").yellow 46 | answer = STDIN.gets.chomp.downcase 47 | if answer == "y" or answer == "yes" 48 | self.configure 49 | end 50 | end 51 | else 52 | puts Rainbow("You do not have a valid config file. Do you wish to create one now and configure it? ").yellow 53 | answer = STDIN.gets.chomp.downcase 54 | if answer == "y" or answer == "yes" 55 | self.configure 56 | else 57 | puts Rainbow("Ok, but nothing is going to work till you do....\n").yellow 58 | end 59 | end 60 | end 61 | 62 | ### This configures the config file for gitlab settings. 63 | def self.configure 64 | if File.exist?(CONFIG) 65 | JSON.parse(File.read(CONFIG)) 66 | params = JSON.parse(File.read(CONFIG)) 67 | else 68 | params = {} 69 | end 70 | puts Rainbow("What is the name of your gitlab server?\nExample: http[s]://server.domain.tld:\nServer: ").purple 71 | host = STDIN.gets.chomp.downcase 72 | 73 | ### Lets get/set the version we are going to use for the API 74 | puts Rainbow("What Gitlab API version are you using: v3 or v4? Press enter if unsure (default is v4).").purple 75 | check = STDIN.gets.chomp.downcase 76 | if check == "" 77 | ver = "v4" 78 | else 79 | ver = check 80 | end 81 | 82 | gitlab_host = "#{host}/api/#{ver}" 83 | params["gitlab_server"] = gitlab_host 84 | params["gitlab_server"] 85 | puts Rainbow("What is your token?\nExample: 3pe14gZfap:\nToken: ").purple 86 | params["gitlab_token"] = STDIN.gets.chomp 87 | puts Rainbow("Do you wish to do the Github token now too? ").purple 88 | answer = STDIN.gets.chomp.downcase 89 | 90 | if answer == "y" or answer == "yes" 91 | puts Rainbow("What is your Github token? ").purple 92 | params["github_token"] = STDIN.gets.chomp 93 | end 94 | 95 | save = JSON.generate(params) 96 | puts Rainbow("\n\nUpdating config file with the following:").purple 97 | puts Rainbow("Gitlab server: #{params["gitlab_server"]}").purple 98 | puts Rainbow("Gitlab token: #{params["gitlab_token"]}").purple 99 | 100 | if params["github_token"] 101 | puts Rainbow("Github token: #{params["github_token"]}").purple 102 | end 103 | 104 | puts Rainbow("\nIs this information correct? ").yellow 105 | answer = STDIN.gets.chomp.downcase 106 | if answer == "y" or answer == "yes" 107 | config_file = File.open(CONFIG, "w") 108 | config_file.write(save) 109 | config_file.close 110 | puts Rainbow("Configuration saved.\n\n").green 111 | exit 112 | else 113 | self.configure 114 | end 115 | end 116 | 117 | ### This configures the github token 118 | def self.github_configure 119 | if self.precheck && !self.github_precheck 120 | params = JSON.parse(File.read(CONFIG)) 121 | puts Rainbow("What is your Github token?\nExample: 3pe14gZfap:\nToken: ").purple 122 | params["github_token"] = STDIN.gets.chomp 123 | save = JSON.generate(params) 124 | puts Rainbow("\n\nUpdating config file with the following: 125 | Github token: #{params["github_token"]} \n\n").purple 126 | puts Rainbow("Is this information correct? ").yellow 127 | answer = STDIN.gets.chomp.downcase 128 | if answer == "y" or answer == "yes" 129 | config_file = File.open(CONFIG, "w") 130 | config_file.write(save) 131 | config_file.close 132 | puts Rainbow("Configuration saved.\n\n").green 133 | exit 134 | end 135 | else 136 | puts "Whoa! Looks like we don't have configuration file yet. Do you want to create one now?" 137 | answer = STDIN.gets.chomp.downcase 138 | if answer == "y" or answer == "yes" 139 | self.configure 140 | end 141 | end 142 | end 143 | 144 | ### Get the token in the file 145 | def self.get_token 146 | unless Setup.precheck 147 | puts Rainbow("\n\nWhoops! Looks like we have not setup a config before...\n").yellow 148 | Setup.configure 149 | else 150 | JSON.parse(File.read(CONFIG)) 151 | params = JSON.parse(File.read(CONFIG)) 152 | return params["gitlab_token"] 153 | end 154 | end 155 | 156 | ### Get the token in the file 157 | def self.get_github_token 158 | unless Setup.github_precheck 159 | puts Rainbow("\n\nWhoops! Looks like we have not setup a github token yet...\n").yellow 160 | Setup.github_configure 161 | else 162 | JSON.parse(File.read(CONFIG)) 163 | params = JSON.parse(File.read(CONFIG)) 164 | return params["github_token"] 165 | end 166 | end 167 | 168 | ### Get the gitlabserver in the file 169 | def self.get_gitlabserver 170 | unless Setup.precheck 171 | puts Rainbow("\n\nWhoops! Looks like we have not setup a config before...\n").yellow 172 | Setup.configure 173 | else 174 | JSON.parse(File.read(CONFIG)) 175 | params = JSON.parse(File.read(CONFIG)) 176 | return params["gitlab_server"] 177 | end 178 | end 179 | end 180 | -------------------------------------------------------------------------------- /lib/version: -------------------------------------------------------------------------------- 1 | 0.14.0 2 | -------------------------------------------------------------------------------- /lib/version.rb: -------------------------------------------------------------------------------- 1 | module Version 2 | require "rainbow" 3 | def self.current 4 | File.open(File.expand_path('../version', __FILE__)).read 5 | end 6 | 7 | def self.version 8 | puts Rainbow("\n\n\----------------------------------------------------------------\n").green 9 | puts Rainbow("\t\tCurrent Gitlab-Clone version is #{Version::current}").green 10 | puts Rainbow("----------------------------------------------------------------\n\n").green 11 | end 12 | end 13 | --------------------------------------------------------------------------------