github-ripper 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ #
2
+ # Class docs to go here
3
+ #
4
+
5
+ #
6
+ # This class smells of :reek:DataClump
7
+ #
8
+ class GithubRipper
9
+ class << self
10
+ #
11
+ # Everything below here is private
12
+ #
13
+
14
+ private
15
+
16
+ def get_clone_type(options)
17
+ return 'git clone git@github.com:' if flag_set?(options, :use_git)
18
+
19
+ return "git clone https://#{options[:token]}@github.com/" if get_option(options, :token)
20
+
21
+ 'git clone https://github.com/'
22
+ end
23
+
24
+ def repo_full_path(options, repo)
25
+ "#{options[:base_dir]}/Repos/#{repo}"
26
+ end
27
+
28
+ def repo_exists?(repo_path)
29
+ File.directory?(repo_path)
30
+ end
31
+
32
+ def execute_command(command)
33
+ error_string = ''
34
+ return_code = 0
35
+
36
+ Open3.popen3(command) do |_stdin, stdout, _stderr, wait_thr|
37
+ error_string = stdout.read.chomp.to_s
38
+ return_code = wait_thr.value.exitstatus
39
+ end
40
+ output = error_string.split(/\n+/).reject(&:empty?).join(', ')
41
+ [return_code, output]
42
+ end
43
+
44
+ def clone_repo(options, repo, repo_path)
45
+ return { :repo => repo, :path => repo_path, :status => 'Dry Run', :when => 'git clone', :info => '' } if flag_set?(options, :dry_run)
46
+
47
+ FileUtils.mkdir_p repo_path
48
+
49
+ clone_type = get_clone_type(options)
50
+ command = "#{clone_type}#{repo} #{repo_path}"
51
+ return_code, output = execute_command(command)
52
+
53
+ return { :repo => repo, :path => repo_path, :status => 'Failed', :when => 'git clone', :info => output } if return_code.positive?
54
+
55
+ { :repo => repo, :path => repo_path, :status => 'Success', :when => 'git clone', :info => 'Clone Succeeded' }
56
+ end
57
+
58
+ def update_repo(options, repo, repo_path)
59
+ return { :repo => repo, :path => repo_path, :status => 'Dry Run', :when => 'git clone', :info => '' } if flag_set?(options, :dry_run)
60
+
61
+ olddir = Dir.pwd
62
+ Dir.chdir repo_path
63
+
64
+ command = 'git pull 2>&1'
65
+ return_code, output = execute_command(command)
66
+ Dir.chdir olddir
67
+
68
+ return { :repo => repo, :path => repo_path, :status => 'Failed', :when => 'git pull', :info => output } if return_code.positive?
69
+
70
+ { :repo => repo, :path => repo_path, :status => 'Success', :when => 'git pull', :info => 'Pull Succeeded' }
71
+ end
72
+
73
+ def clone_repo_wrapper(options, repo)
74
+ repo_path = repo_full_path(options, repo)
75
+
76
+ return update_repo(options, repo, repo_path) if repo_exists?(repo_path)
77
+
78
+ clone_repo(options, repo, repo_path)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,38 @@
1
+ #
2
+ # Class level docs
3
+ #
4
+ class GithubRipper
5
+ class << self
6
+ #
7
+ # Everything below here is private
8
+ #
9
+
10
+ private
11
+
12
+ def display_table?(error_count, options)
13
+ # Dry run - force display of all
14
+ return true if flag_set?(options, :dry_run)
15
+
16
+ # Quite / Silent - show nothing
17
+ return false if flag_set?(options, :quiet) || flag_set?(options, :silent)
18
+
19
+ # Errors or full report wanted
20
+ return true if error_count.positive? || flag_set?(options, :full_report)
21
+
22
+ # If in doubt say nothing
23
+ false
24
+ end
25
+
26
+ #
27
+ # This method smells of :reek:LongParameterList, :reek:DuplicateMethodCall
28
+ #
29
+ def draw_report(results, repo_count, error_count, options)
30
+ return unless display_table?(error_count, options)
31
+
32
+ table = create_table
33
+ table = add_title(table, repo_count, error_count, options)
34
+ table = add_rows(table, results, options)
35
+ display_table(table)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,57 @@
1
+ #
2
+ # Class docs to go here
3
+ #
4
+ class GithubRipper
5
+ class << self
6
+ #
7
+ # Everything below here is private
8
+ #
9
+
10
+ private
11
+
12
+ # rubocop:disable Metrics/CyclomaticComplexity
13
+ # This method smells of :reek:NilCheck
14
+ def handle_required_parameters(options)
15
+ return if get_option(options, :token)
16
+
17
+ raise StandardError.new('Please supply a username (-u) or a token (-t)') if (flag_set?(options, :user_repos) || flag_set?(options, :org_members_repos) || flag_set?(options, :all_repos)) && get_option(options, :user).nil?
18
+
19
+ raise StandardError.new('Please supply an organisation name (-o) or a token (-t)') if flag_set?(options, :org_repos) && get_option(options, :org).nil?
20
+ end
21
+ # rubocop:enable Metrics/CyclomaticComplexity
22
+
23
+ #
24
+ # docs go here
25
+ #
26
+ def get_repo_list(options)
27
+ handle_required_parameters(options)
28
+
29
+ function = function_map_lookup(options)
30
+
31
+ raise StandardError.new('Missing parameters') unless function
32
+
33
+ JSON.parse(function_wrapper(function, options))
34
+ end
35
+
36
+ #
37
+ # This method smells of :reek:DuplicateMethodCall
38
+ #
39
+ def rip_repos(options, repos)
40
+ repo_count = repos.size
41
+
42
+ results = if flag_set?(options, :silent)
43
+ Parallel.map(repos) { |repo| clone_repo_wrapper(options, repo) }
44
+ else
45
+ Parallel.map(repos, :progress => "Cloning #{repo_count}") { |repo| clone_repo_wrapper(options, repo) }
46
+ end
47
+ results.compact
48
+ end
49
+
50
+ def process_results(results, options)
51
+ repo_count = count_repos(results)
52
+ error_count = count_errors(results)
53
+ results = filter_results(results, options)
54
+ [results, repo_count, error_count]
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Class level docs
3
+ #
4
+ class GithubRipper
5
+ class << self
6
+ #
7
+ # Everything below here is private
8
+ #
9
+
10
+ private
11
+
12
+ # This method smells of :reek:UtilityFunction
13
+ def create_table
14
+ Terminal::Table.new :headings => ['Repo', 'Path', 'Status', 'When', 'Information']
15
+ end
16
+
17
+ # This method smells of :reek:ControlParameter, :reek:LongParameterList
18
+ def add_title(table, repo_count, error_count, options)
19
+ title = "There were #{error_count} #{plural(error_count, 'error')}"
20
+ title += " from #{repo_count} #{plural(repo_count, 'repository', 'respositories')}" if flag_set?(options, :full_report) || flag_set?(options, :dry_run)
21
+
22
+ table.title = title
23
+ table
24
+ end
25
+
26
+ # This method smells of :reek:UtilityFunction, :reek:ControlParameter
27
+ def visible_row?(repo, options)
28
+ # Dry run - force display of all
29
+ return true if flag_set?(options, :dry_run)
30
+
31
+ repo[:status] == 'Failed' || flag_set?(options, :full_report)
32
+ end
33
+
34
+ def add_single_row(table, repo, options)
35
+ status = repo[:status]
36
+ table.add_row [set_colour(repo[:repo], status), set_colour(repo[:path], status), set_colour(status, status), set_colour(repo[:when], status), set_colour(repo[:info], status)] if visible_row?(repo, options)
37
+ table
38
+ end
39
+
40
+ def add_rows(table, results, options)
41
+ results.each { |repo| table = add_single_row(table, repo, options) }
42
+ table
43
+ end
44
+
45
+ def display_table(table)
46
+ puts table
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,64 @@
1
+ #
2
+ # Class docs to go here
3
+ #
4
+ class GithubRipper
5
+ class << self
6
+ #
7
+ # Everything below here is private
8
+ #
9
+
10
+ private
11
+
12
+ #
13
+ # This method smells of :reek:DuplicateMethodCall
14
+ #
15
+ def set_colour(item, status)
16
+ return Rainbow(item).yellow if item.downcase == 'dry run'
17
+
18
+ case status.downcase
19
+ when 'dry run'
20
+ Rainbow(item).cyan
21
+ when 'failed'
22
+ Rainbow(item).red
23
+ else
24
+ Rainbow(item).green
25
+ end
26
+ end
27
+
28
+ # This method smells :reek:UtilityFunction, :reek:ControlParameter
29
+ def plural(count, singular, plural = nil)
30
+ if count == 1
31
+ singular.to_s
32
+ elsif plural
33
+ plural.to_s
34
+ else
35
+ "#{singular}s"
36
+ end
37
+ end
38
+
39
+ # This method smells of :reek:UtilityFunction
40
+ def count_repos(results)
41
+ results.size
42
+ end
43
+
44
+ # This method smells of :reek:UtilityFunction
45
+ def count_errors(results)
46
+ results.select { |repo| repo[:status] == 'Failed' }.size
47
+ end
48
+
49
+ # This method smells of :reek:UtilityFunction
50
+ def filter_results(results, options)
51
+ results.select { |repo| repo[:status] == 'Failed' || flag_set?(options, :full_report) || flag_set?(options, :dry_run) }.sort_by { |repo| repo[:repo].downcase }
52
+ end
53
+
54
+ def get_option(options, name)
55
+ options[name] if options.key?(name)
56
+ end
57
+
58
+ def flag_set?(options, name)
59
+ return true if options.key?(name) && options[name] == true
60
+
61
+ false
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,6 @@
1
+ #
2
+ # Class docs to go here
3
+ #
4
+ class GithubRipper
5
+ VERSION = '0.1.0'.freeze
6
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Class docs to go here
3
+ #
4
+ class GithubRipper
5
+ class << self
6
+ #
7
+ # Everything below here is private
8
+ #
9
+
10
+ private
11
+
12
+ def function_wrapper(function, options)
13
+ begin
14
+ results = GithubListerCore.send(function, options)
15
+ rescue UnknownError, InvalidTokenError, MissingTokenError, TooManyRequests, NotFoundError, MissingOrganisationError, InvalidOptionsHashError => exception
16
+ raise StandardError.new(exception.to_s)
17
+ end
18
+ results || []
19
+ end
20
+ end
21
+ end
data/stale.yml ADDED
@@ -0,0 +1,17 @@
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 60
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: 7
5
+ # Issues with these labels will never be considered stale
6
+ exemptLabels:
7
+ - pinned
8
+ - security
9
+ # Label to use when marking an issue as stale
10
+ staleLabel: wontfix
11
+ # Comment to post when marking an issue as stale. Set to `false` to disable
12
+ markComment: >
13
+ This issue has been automatically marked as stale because it has not had
14
+ recent activity. It will be closed if no further activity occurs. Thank you
15
+ for your contributions.
16
+ # Comment to post when closing a stale issue. Set to `false` to disable
17
+ closeComment: true
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This is a very simply testing script to allow for testing of local changes without having to install the gem locally
4
+ #
5
+
6
+ require 'json'
7
+
8
+ $LOAD_PATH.unshift('./lib')
9
+
10
+ require 'bundler/setup'
11
+ require 'github-ripper'
12
+
13
+ options = {}
14
+
15
+ pp GithubRipper.rip(options)
data/testing/ghrip ADDED
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift('./lib')
4
+
5
+ require 'bundler/setup'
6
+
7
+ require 'github-ripper'
8
+ require 'json'
9
+ require 'optparse'
10
+ require 'pp'
11
+
12
+ MANDATORY_PARAMETERS = [].freeze
13
+ DEFAULT_VALUES = {}.freeze
14
+
15
+ def init_default_parser
16
+ parser = OptionParser.new
17
+
18
+ parser.banner = "Usage: #{$PROGRAM_NAME}"
19
+
20
+ parser.on('-h', '--help', 'Display this screen') do
21
+ puts parser
22
+ exit(1)
23
+ end
24
+ parser
25
+ end
26
+
27
+ # This method reeks of :reek:UtilityFunction, :reek:TooManyStatements
28
+ def add_parameters(parser, options)
29
+ parser.separator ''
30
+ parser.separator 'Parameters:'
31
+ parser.on('-t', '--token <token>', 'GitHub personal access token (PAT)') { |token| options[:token] = token }
32
+ parser.on('-b', '--base-dir <path>', "The base directory to download to [default: #{File.expand_path('~')}/Downloads/]") { |base_dir| options[:base_dir] = base_dir }
33
+ parser.on('-g', '--use-git', TrueClass, 'Use git instead of https to clone the repositories') { |_errors| options[:use_git] = true }
34
+ [parser, options]
35
+ end
36
+
37
+ # This method reeks of :reek:UtilityFunction, :reek:TooManyStatements
38
+ def add_repo_flag_parameters(parser, options)
39
+ parser.separator ''
40
+ parser.separator 'Cloning Parameters:'
41
+ parser.on('-u', '--user <names>', 'Github username(s) to rip') { |user| options[:user] = user }
42
+ parser.on('-o', '--org <names>', 'Github organisation(s) to rip') { |org| options[:org] = org }
43
+ parser.on('-U', '--user-repos', TrueClass, 'Rip all of the repositories for the named user(s)') { |_dry_run| options[:user_repos] = true }
44
+ parser.on('-M', '--org-member-repos', TrueClass, 'Rip all of the repositories for all organisation the user(s) is a member of') { |_quiet| options[:org_members_repos] = true }
45
+ parser.on('-A', '--all-repos', TrueClass, 'Same as running -U -M') { |_silent| options[:all_repos] = true }
46
+ parser.on('-O', '--org-repos', TrueClass, 'Rip all of the repositories for the named organisation(s)') { |_errors| options[:org_repos] = true }
47
+ [parser, options]
48
+ end
49
+
50
+ # This method reeks of :reek:UtilityFunction, :reek:TooManyStatements
51
+ def add_flag_parameters(parser, options)
52
+ parser.separator ''
53
+ parser.separator 'Flags:'
54
+ parser.on('-d', '--dry-run', 'Show a list of repositories that WOULD be ripped') { |_dry_run| options[:dry_run] = true }
55
+ parser.on('-f', '--full', 'Show status of all repositories in post run report') { |_errors| options[:full_report] = true }
56
+ parser.on('-q', '--quiet', 'Suppress the showing of the post run report') { |_quiet| options[:quiet] = true }
57
+ parser.on('-s', '--silent', 'Suppress all output') { |_silent| options[:silent] = true }
58
+ [parser, options]
59
+ end
60
+
61
+ def create_parser
62
+ options = DEFAULT_VALUES.dup
63
+
64
+ parser = init_default_parser
65
+ parser, options = add_parameters(parser, options)
66
+ parser, options = add_repo_flag_parameters(parser, options)
67
+ parser, options = add_flag_parameters(parser, options)
68
+
69
+ [parser, options]
70
+ end
71
+
72
+ def process_parser(parser, options)
73
+ begin
74
+ parser.parse!
75
+ missing = MANDATORY_PARAMETERS.reject { |param| options.include?(param) }
76
+ raise OptionParser::MissingArgument.new(missing.join(', ')) unless missing.empty?
77
+ rescue OptionParser::InvalidOption, OptionParser::InvalidArgument, OptionParser::MissingArgument => exception
78
+ exit_with_error(parser, exception)
79
+ end
80
+ #
81
+ # The parser will use :"some-symbol" so we convert it to :some_symbol
82
+ #
83
+ options.transform_keys(&:to_s).transform_keys { |key| key.gsub('-', '_') }.transform_keys(&:to_sym)
84
+ end
85
+
86
+ def exit_with_error(parser, exception = nil)
87
+ puts exception if exception
88
+ puts parser.help
89
+ exit
90
+ end
91
+
92
+ def process_arguments
93
+ parser, options = create_parser
94
+ options = process_parser(parser, options)
95
+
96
+ exit_with_error(parser) if options.empty?
97
+ options
98
+ end
99
+
100
+ def main
101
+ options = process_arguments
102
+
103
+ begin
104
+ GithubRipper.rip(options)
105
+ rescue StandardError => exception
106
+ puts exception
107
+ end
108
+ end
109
+
110
+ main