git_toolbox 0.7.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,60 +17,86 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- require 'get/commons/common'
21
- require 'get/commons/git'
22
- require 'get/subcommand/command'
23
- require 'get/subcommand/license/license_retriever'
20
+ require_relative '../../commons/common'
21
+ require_relative '../../commons/git'
22
+ require_relative '../command'
23
+ require_relative './license_retriever'
24
24
 
25
25
  # Class length is disabled as most of its length is given by formatting.
26
26
  # rubocop:disable Metrics/ClassLength
27
27
  # Subcommand, it allow to choose a license.
28
28
  class License < Command
29
- def self.command
30
- @@command ||= new
31
- @@command
32
- end
33
-
34
- private_class_method :new
35
-
36
29
  private
37
30
 
38
31
  include Retriever
39
32
 
40
- @@command = nil
41
-
42
- @@usage = 'license -h|(<subcommand> [<subcommand-options])'
43
- @@description = 'Create a new LICENSE file with the chosen license. ' \
44
- 'Online licenses are retrieved from https://choosealicense.com/appendix/ . ' \
45
- 'Head there for more information about the licences.'
46
- @@subcommands = {}
47
- # This block is Optimist configuration. It is as long as the number of options of the command.
48
- # rubocop:disable Metrics/BlockLength
49
- @@option_parser = Optimist::Parser.new do
50
- subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
51
- subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
52
- Subcommands:
53
- #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
54
- SUBCOMMANDS
55
- usage @@usage
56
- synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
57
- opt :offline,
58
- 'Force the application to use the offline licenses.'
59
- opt :create_commit,
60
- 'Create a commit which adds the LICENSE file to the repository history.',
61
- short: :none
62
- opt :commit_type,
63
- 'Select the type of the commit. No effect if "--create-commit" is not given.',
64
- default: 'chore'
65
- educate_on_error
66
- stop_on @@subcommands.keys.map(&:to_s)
33
+ def initialize
34
+ super() do
35
+ @usage = 'license -h|(<subcommand> [<subcommand-options])'
36
+ @description = 'Create a new LICENSE file with the chosen license. ' \
37
+ 'Online licenses are retrieved from https://choosealicense.com/appendix/ . ' \
38
+ 'Head there for more information about the licences.'
39
+ @subcommands = {}
40
+ end
67
41
  end
68
- # rubocop:enable Metrics/BlockLength
69
42
 
70
- def initialize
71
- super(@@usage, @@description) do
72
- @options = Common.with_subcommand_exception_handling @@option_parser do
73
- @@option_parser.parse
43
+ def create_license_file
44
+ license_text = ask_for_license(@options[:offline])
45
+ File.write(File.expand_path(@filename), license_text)
46
+ puts 'License file created. You may need to modify it with the year and your name.'
47
+ end
48
+
49
+ def create_license_commit
50
+ CommandIssuer.run('git', 'stash', 'push', '--staged')
51
+
52
+ setup_at_exit_hook
53
+ CommandIssuer.run('git', 'add', "'#{@filename}'").then do |add_result|
54
+ if add_result.exit_status.zero?
55
+ CommandIssuer.run(
56
+ 'git',
57
+ 'commit',
58
+ '-m',
59
+ "'#{@options[:commit_type]}: add license file'"
60
+ ).then do |commit_result|
61
+ if commit_result.exit_status.positive?
62
+ Common.error 'Failed to create license commit'
63
+ else
64
+ puts 'License file committed.'
65
+ end
66
+ end
67
+ else
68
+ Common.error "Failed to add license file '#{@filename}' to stage."
69
+ end
70
+ end
71
+ end
72
+
73
+ protected
74
+
75
+ def setup_option_parser
76
+ @option_parser = Optimist::Parser.new(
77
+ @usage,
78
+ full_description,
79
+ stop_condition
80
+ ) do |usage_header, description, stop_condition|
81
+ usage usage_header
82
+ synopsis description
83
+ opt :offline,
84
+ 'Force the application to use the offline licenses.'
85
+ opt :create_commit,
86
+ 'Create a commit which adds the LICENSE file to the repository history.',
87
+ short: :none
88
+ opt :commit_type,
89
+ 'Select the type of the commit. No effect if "--create-commit" is not given.',
90
+ default: 'chore'
91
+ educate_on_error
92
+ stop_on stop_condition
93
+ end
94
+ end
95
+
96
+ def setup_action
97
+ @action = lambda do
98
+ @options = Common.with_subcommand_exception_handling @option_parser do
99
+ @option_parser.parse
74
100
  end
75
101
 
76
102
  @filename = 'LICENSE'
@@ -84,24 +110,20 @@ class License < Command
84
110
 
85
111
  create_license_commit
86
112
  end
113
+ rescue Interrupt
114
+ Common.print_then_do_and_exit "\nLicense creation cancelled"
87
115
  end
88
116
  end
89
117
 
90
- def create_license_file
91
- license_text = ask_for_license(@options[:offline])
92
- File.write(File.expand_path(@filename), license_text)
93
- puts 'License file created. You may need to modify it with the year and your name.'
94
- end
95
-
96
- def create_license_commit
97
- `git stash push --staged`
118
+ private
98
119
 
120
+ def setup_at_exit_hook
99
121
  # This block is given to at_exit to execute it in any case.
100
- at_exit { `[ "$(git stash list | wc -l)" -gt "0" ] && git stash pop > /dev/null` }
101
- `git add "#{@filename}" && git commit -m "chore: add license file"`
102
- Common.error 'Failed to create license commit' if $CHILD_STATUS.exitstatus.positive?
103
-
104
- puts 'License file committed.'
122
+ at_exit do
123
+ if CommandIssuer.run('git', 'stash', 'list').output.lines.length.positive?
124
+ CommandIssuer.run('git', 'stash', 'pop')
125
+ end
126
+ end
105
127
  end
106
128
  end
107
129
  # rubocop:enable Metrics/ClassLength
@@ -18,6 +18,7 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  require 'highline'
21
+ require_relative '../../commons/http_client'
21
22
 
22
23
  # The retrieving module for licenses. It can gather licenses online (from https://choosealicense.com/appendix/)
23
24
  # or offline (from a predefined subset of licenses).
@@ -25,24 +26,25 @@ module Retriever
25
26
  BASE_OFFLINE_LICENSE_PATH = "#{File.dirname(File.expand_path(__FILE__))}/offline_licenses".freeze
26
27
  BASE_ONLINE_LICENSE_URI = 'https://choosealicense.com'
27
28
 
28
- @@cli = HighLine.new
29
+ Common.module_instance_value(self, 'cli', 'HighLine.new')
30
+ Common.add_module_self_reference(self)
29
31
 
30
32
  def ask_for_license(offline)
31
- @@offline = offline
32
- list = if @@offline
33
+ @offline = offline
34
+ list = if @offline
33
35
  offline_license_list
34
36
  else
35
37
  online_license_list
36
38
  end
37
39
 
38
- @@cli.puts 'Choose which license you want to use:'
39
- choice = @@cli.choose do |menu|
40
+ MOD_REF.cli.puts 'Choose which license you want to use:'
41
+ choice = MOD_REF.cli.choose do |menu|
40
42
  menu.flow = :column_down
41
43
  menu.prompt = ''
42
44
  list.each { |element| menu.choice(element) }
43
45
  end
44
46
 
45
- if @@offline
47
+ if @offline
46
48
  offline_license_text(choice)
47
49
  else
48
50
  online_license_text(choice)
@@ -56,18 +58,25 @@ module Retriever
56
58
  end
57
59
 
58
60
  def online_license_list
59
- @@online_licenses = {}
60
- text = `curl -fsL "#{BASE_ONLINE_LICENSE_URI}/appendix/" | grep '<th scope="row">'`.strip
61
- if $CHILD_STATUS.exitstatus.positive?
62
- puts 'WARNING: Unable to retrieve list of online licenses, falling back to offline ones.'
63
- @@offline = true
64
- offline_license_list
65
- else
66
- text.split("\n").each do |element|
61
+ @online_licenses = {}
62
+ response = HTTPClient.instance.http_get_request("#{BASE_ONLINE_LICENSE_URI}/appendix/")
63
+ if response.is_a?(Net::HTTPSuccess)
64
+ response.body
65
+ .strip
66
+ .lines
67
+ .select { |line| line.include?('<th scope="row">') }
68
+ .each do |element|
67
69
  match_result = element.match(%r{<a href="(.*)">(.*)</a>})
68
- @@online_licenses[match_result[2]] = match_result[1]
70
+ @online_licenses[match_result[2]] = match_result[1]
69
71
  end
70
- @@online_licenses.keys
72
+ @online_licenses.keys
73
+ else
74
+ warning_message = 'WARNING: Unable to retrieve list of online licenses ' \
75
+ "(cause: #{HTTPClient.instance.response_error_message(response)}), " \
76
+ 'falling back to offline ones.'
77
+ puts warning_message
78
+ @offline = true
79
+ offline_license_list
71
80
  end
72
81
  end
73
82
 
@@ -76,12 +85,18 @@ module Retriever
76
85
  end
77
86
 
78
87
  def online_license_text(license)
79
- page = `curl -fsL "#{BASE_ONLINE_LICENSE_URI}#{@@online_licenses[license]}"`
80
- Common.error 'Failed to retrieve the license text.' if $CHILD_STATUS.exitstatus.positive?
81
-
82
- match_result = page.match(%r{<pre id="license-text">(.*)</pre>}m)
83
- Common.error 'Invalid license text' if match_result[1].nil?
84
-
85
- match_result[1]
88
+ response = HTTPClient.instance.http_get_request("#{BASE_ONLINE_LICENSE_URI}#{@online_licenses[license]}")
89
+ if response.is_a?(Net::HTTPSuccess)
90
+ match_result = response.body.match(%r{<pre id="license-text">(.*)</pre>}m)
91
+ if match_result[1].nil?
92
+ Common.error 'Invalid license text'
93
+ else
94
+ match_result[1]
95
+ end
96
+ else
97
+ error_message = 'Failed to retrieve the license text ' \
98
+ "(cause: #{HTTPClient.instance.response_error_message(response)})."
99
+ Common.error error_message
100
+ end
86
101
  end
87
102
  end
@@ -17,64 +17,44 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- require 'English'
21
- require 'get/commons/common'
22
- require 'get/commons/git'
23
- require 'get/subcommand/command'
20
+ require_relative '../../commons/common'
21
+ require_relative '../../commons/git'
22
+ require_relative '../command'
24
23
 
25
24
  # Class length is disabled as most of its length is given by formatting.
26
25
  # rubocop:disable Metrics/ClassLength
27
26
  # Subcommand, it allow to create a new repository and add an initial, empty commit to it.
28
27
  class Tree < Command
29
- def self.command
30
- @@command ||= new
31
- @@command
32
- end
33
-
34
- private_class_method :new
35
-
36
28
  private
37
29
 
38
- @@command = nil
39
-
40
- @@usage = 'tree -h|(<subcommand> [<subcommand-options])'
41
- @@description = 'Print the tree of commits.'
42
- @@subcommands = {}
43
- # This block is Optimist configuration. It is as long as the number of options of the command.
44
- # rubocop:disable Metrics/BlockLength
45
- @@option_parser = Optimist::Parser.new do
46
- subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
47
- subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
48
- Subcommands:
49
- #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
50
- SUBCOMMANDS
51
- usage @@usage
52
- synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
53
- educate_on_error
54
- stop_on @@subcommands.keys.map(&:to_s)
55
- end
56
- # rubocop:enable Metrics/BlockLength
57
-
58
30
  def initialize
59
- super(@@usage, @@description) do
60
- @options = Common.with_subcommand_exception_handling @@option_parser do
61
- @@option_parser.parse
62
- end
63
- Common.error 'tree need to be run inside a git repository' unless Git.in_repo?
64
-
65
- view_tree
31
+ super() do
32
+ @usage = 'tree -h|(<subcommand> [<subcommand-options])'
33
+ @description = 'Print the tree of commits. ' \
34
+ 'If the output is redirected to a pager (i.e. \'less\'), ' \
35
+ 'you may need to enable the parsing of escape sequences.'
36
+ @subcommands = {}
66
37
  end
67
38
  end
68
39
 
69
40
  def view_tree
70
- page_log(transform_log(log))
41
+ puts transform_log(log)
71
42
  end
72
43
 
73
44
  TREE_FORMAT = '%C(bold blue)%h%C(reset)§%C(dim normal)(%cr)%C(reset)§%C(auto)%d%C(reset)§§%n' \
74
45
  '§§§ %C(normal)%an%C(reset)%C(dim normal): %s%C(reset)'
75
46
 
76
47
  def log
77
- `git log --all --graph --decorate=short --date-order --color --pretty=format:"#{TREE_FORMAT}"`
48
+ CommandIssuer.run(
49
+ 'git',
50
+ 'log',
51
+ '--all',
52
+ '--graph',
53
+ '--decorate=short',
54
+ '--date-order',
55
+ '--color',
56
+ "--pretty=format:\"#{TREE_FORMAT}\""
57
+ ).output
78
58
  end
79
59
 
80
60
  TIME_REGEX = /(\([a-z0-9 ,]+\))/
@@ -90,21 +70,18 @@ class Tree < Command
90
70
  # calc color escape codes length
91
71
  time_color_length = first_line_time.length - first_line_time.match(TIME_REGEX)[1].length
92
72
 
93
- # calc max length of time references
94
- time_padding = 0
95
- split_lines.each { |element| time_padding = [time_padding, element[1].length - time_color_length].max }
73
+ time_padding = global_fitting_time_padding(split_lines, time_color_length)
96
74
 
97
- # format strings
98
75
  split_lines
99
76
  .map do |element|
100
77
  # Only lines with the date reference have the color escape codes,
101
78
  # the other lines do not need the additional padding
102
79
  left_padding = TIME_MINIMUM_PADDING + time_padding +
103
- (element[1].match?(TIME_REGEX) ? time_color_length : 0)
80
+ (!element[1].nil? && element[1].match?(TIME_REGEX) ? time_color_length : 0)
104
81
  format(
105
82
  '%<date>s %<tree_mark>s %<pointers>s %<commit_text>s',
106
83
  {
107
- date: element[1].rjust(left_padding),
84
+ date: (element[1].nil? ? '' : element[1]).rjust(left_padding),
108
85
  tree_mark: element[0],
109
86
  pointers: element[2],
110
87
  commit_text: element[3]
@@ -115,8 +92,40 @@ class Tree < Command
115
92
  end
116
93
  # rubocop:enable Metrics/MethodLength
117
94
 
118
- def page_log(text)
119
- system("less -RfS <(echo -e '#{text}')")
95
+ def global_fitting_time_padding(lines, time_color_length)
96
+ time_padding = 0
97
+ lines
98
+ # If element[1].nil? then the line refers to a intersection between branch lines
99
+ # they do not have a time reference
100
+ .reject { |element| element[1].nil? }
101
+ .each { |element| time_padding = [time_padding, element[1].length - time_color_length].max }
102
+ time_padding
103
+ end
104
+
105
+ protected
106
+
107
+ def setup_option_parser
108
+ @option_parser = Optimist::Parser.new(
109
+ @usage,
110
+ full_description,
111
+ stop_condition
112
+ ) do |usage_header, description, stop_condition|
113
+ usage usage_header
114
+ synopsis description
115
+ educate_on_error
116
+ stop_on stop_condition
117
+ end
118
+ end
119
+
120
+ def setup_action
121
+ @action = lambda do
122
+ @options = Common.with_subcommand_exception_handling @option_parser do
123
+ @option_parser.parse
124
+ end
125
+ Common.error 'tree need to be run inside a git repository' unless Git.in_repo?
126
+
127
+ view_tree
128
+ end
120
129
  end
121
130
  end
122
131
  # rubocop:enable Metrics/ClassLength
data/lib/get/version.rb CHANGED
@@ -17,6 +17,4 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- module Get
21
- VERSION = '0.7.2'
22
- end
20
+ GET_VERSION = '0.9.0'
data/lib/get.rb CHANGED
@@ -28,48 +28,61 @@ require 'get/subcommand/changelog/changelog'
28
28
  require 'get/subcommand/tree/tree'
29
29
  require 'get/version'
30
30
  require 'get/commons/common'
31
+ require 'get/subcommand/command'
31
32
 
32
- # Entrypoint of Get
33
- module Get
33
+ # Main command of Get.
34
+ class Get < Command
34
35
  class Error < StandardError; end
35
36
 
36
- @@subcommands = {
37
- describe: Describe.command,
38
- commit: Commit.command,
39
- init: Init.command,
40
- license: License.command,
41
- complete: Complete.command,
42
- changelog: Changelog.command,
43
- tree: Tree.command,
44
- }
45
- @@option_parser = Optimist::Parser.new do
46
- subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
47
- usage '-h|-v|(<subcommand> [<subcommand-options])'
48
- synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
49
- Subcommands:
50
- #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
51
- SUBCOMMANDS
52
- version "Get version: #{Get::VERSION}"
53
- educate_on_error
54
- stop_on @@subcommands.keys.map(&:to_s)
37
+ def initialize
38
+ super() do
39
+ @usage = '-h|-v|(<subcommand> [<subcommand-options])'
40
+ @description = ''
41
+ @subcommands = {
42
+ describe: Describe.instance,
43
+ commit: Commit.instance,
44
+ init: Init.instance,
45
+ license: License.instance,
46
+ complete: Complete.instance,
47
+ changelog: Changelog.instance,
48
+ tree: Tree.instance,
49
+ }
50
+ end
55
51
  end
56
52
 
57
- def self.main
58
- @options = Optimist.with_standard_exception_handling(@@option_parser) do
59
- @@option_parser.parse
60
- end
61
- error 'No command or option specified' if ARGV.empty?
62
- command = ARGV.shift.to_sym
63
- if @@subcommands.include?(command)
64
- @@subcommands[command].action.call
65
- else
66
- error "Unknown subcommand '#{command}'"
53
+ def main
54
+ @action.call
55
+ end
56
+
57
+ protected
58
+
59
+ def setup_option_parser
60
+ @option_parser = Optimist::Parser.new(
61
+ @usage,
62
+ full_description,
63
+ GET_VERSION,
64
+ stop_condition
65
+ ) do |usage_header, description, version, stop_condition|
66
+ usage usage_header
67
+ synopsis description
68
+ version "Get version: #{version}"
69
+ educate_on_error
70
+ stop_on stop_condition
67
71
  end
68
72
  end
69
73
 
70
- def self.error(message)
71
- Common.error message do
72
- @@option_parser.educate
74
+ def setup_action
75
+ @action = lambda do
76
+ @options = Optimist.with_standard_exception_handling(@option_parser) do
77
+ @option_parser.parse
78
+ end
79
+ educated_error 'No command or option specified' if ARGV.empty?
80
+ command = ARGV.shift.to_sym
81
+ if @subcommands.include?(command)
82
+ @subcommands[command].action.call
83
+ else
84
+ educated_error "Unknown subcommand '#{command}'"
85
+ end
73
86
  end
74
87
  end
75
88
  end
metadata CHANGED
@@ -1,49 +1,49 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_toolbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Speranza
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-02 00:00:00.000000000 Z
11
+ date: 2023-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: optimist
14
+ name: highline
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.0'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 3.0.1
19
+ version: 2.0.3
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '3.0'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 3.0.1
26
+ version: 2.0.3
33
27
  - !ruby/object:Gem::Dependency
34
- name: highline
28
+ name: optimist
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
31
  - - "~>"
38
32
  - !ruby/object:Gem::Version
39
- version: 2.0.3
33
+ version: '3.0'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 3.0.1
40
37
  type: :runtime
41
38
  prerelease: false
42
39
  version_requirements: !ruby/object:Gem::Requirement
43
40
  requirements:
44
41
  - - "~>"
45
42
  - !ruby/object:Gem::Version
46
- version: 2.0.3
43
+ version: '3.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 3.0.1
47
47
  description:
48
48
  email:
49
49
  - alex.speranza@studio.unibo.it
@@ -55,8 +55,10 @@ files:
55
55
  - bin/get
56
56
  - bin/setup
57
57
  - lib/get.rb
58
+ - lib/get/commons/command_issuer.rb
58
59
  - lib/get/commons/common.rb
59
60
  - lib/get/commons/git.rb
61
+ - lib/get/commons/http_client.rb
60
62
  - lib/get/subcommand/changelog/changelog.rb
61
63
  - lib/get/subcommand/command.rb
62
64
  - lib/get/subcommand/commit/commit.rb
@@ -94,7 +96,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
94
96
  requirements:
95
97
  - - ">="
96
98
  - !ruby/object:Gem::Version
97
- version: 3.1.0
99
+ version: 3.0.0
98
100
  required_rubygems_version: !ruby/object:Gem::Requirement
99
101
  requirements:
100
102
  - - ">="