git_toolbox 0.7.1 → 0.8.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.
@@ -17,82 +17,43 @@
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'
20
+ require_relative '../../../commons/common'
21
+ require_relative '../../../commons/git'
22
+ require_relative '../../command'
23
23
 
24
24
  # Class length is disabled as most of its length is given by formatting.
25
25
  # rubocop:disable Metrics/ClassLength
26
26
  # Subcommand, it manages the description of the current git repository using semantic version.
27
27
  class DescribeDocker < Command
28
- def self.command
29
- @@command ||= new
30
- @@command
31
- end
32
-
33
- private_class_method :new
34
-
35
28
  private
36
29
 
37
30
  INCREMENTAL_VERSION_PATTERN = /(((\d+)\.\d+)\.\d+)/
38
31
 
39
- @@command = nil
40
-
41
- @@usage = 'describe docker -h|(<subcommand> [<subcommand-options])'
42
- @@description = 'Describe the current git repository with a list of version for docker'
43
- @@subcommands = {}
44
- # This block is Optimist configuration. It is as long as the number of options of the command.
45
- # rubocop:disable Metrics/BlockLength
46
- @@option_parser = Optimist::Parser.new do
47
- subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
48
- usage @@usage
49
- synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
50
- Subcommands:
51
- #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
52
- SUBCOMMANDS
53
- opt :separator,
54
- 'Use the given value as separator for versions',
55
- { type: :string, default: '\n' }
56
- opt :not_latest,
57
- 'Do not include "latest" in the version list.',
58
- short: :none
59
- opt :substitute_plus,
60
- 'Set which character will be used in place of "+".',
61
- { type: :string, short: :none }
62
- educate_on_error
63
- stop_on @@subcommands.keys.map(&:to_s)
64
- end
65
- # rubocop:enable Metrics/BlockLength
66
-
67
32
  def initialize
68
- super(@@usage, @@description) do |version|
69
- Common.error 'describe need to be run inside a git repository' unless Git.in_repo?
70
- @options = Common.with_subcommand_exception_handling @@option_parser do
71
- @@option_parser.parse
72
- end
73
- set_options
74
-
75
- puts version_list_from(version).join(@@separator)
33
+ super() do
34
+ @usage = 'describe docker -h|(<subcommand> [<subcommand-options])'
35
+ @description = 'Describe the current git repository with a list of version for docker'
36
+ @subcommands = {}
76
37
  end
77
38
  end
78
39
 
79
40
  def set_options
80
- @@separator = if @options[:separator_given]
81
- @options[:separator]
82
- else
83
- "\n"
84
- end
85
- @@not_latest = @options[:not_latest]
86
- @@plus_substitution = if @options[:substitute_plus_given]
87
- @options[:substitute_plus]
88
- else
89
- '+'
90
- end
41
+ @separator = if @options[:separator_given]
42
+ @options[:separator]
43
+ else
44
+ "\n"
45
+ end
46
+ @not_latest = @options[:not_latest]
47
+ @plus_substitution = if @options[:substitute_plus_given]
48
+ @options[:substitute_plus]
49
+ else
50
+ '+'
51
+ end
91
52
  end
92
53
 
93
54
  def version_list_from(full_version)
94
55
  [
95
- full_version.sub('+', @@plus_substitution),
56
+ full_version.sub('+', @plus_substitution),
96
57
  reduced_versions(full_version),
97
58
  latest
98
59
  ]
@@ -114,5 +75,41 @@ class DescribeDocker < Command
114
75
  ['latest']
115
76
  end
116
77
  end
78
+
79
+ protected
80
+
81
+ def setup_option_parser
82
+ @option_parser = Optimist::Parser.new(
83
+ @usage,
84
+ full_description,
85
+ stop_condition
86
+ ) do |usage_header, description, stop_condition|
87
+ usage usage_header
88
+ synopsis description
89
+ opt :separator,
90
+ 'Use the given value as separator for versions',
91
+ { type: :string, default: '\n' }
92
+ opt :not_latest,
93
+ 'Do not include "latest" in the version list.',
94
+ short: :none
95
+ opt :substitute_plus,
96
+ 'Set which character will be used in place of "+".',
97
+ { type: :string, short: :none }
98
+ educate_on_error
99
+ stop_on stop_condition
100
+ end
101
+ end
102
+
103
+ def setup_action
104
+ @action = lambda do |version|
105
+ Common.error 'describe need to be run inside a git repository' unless Git.in_repo?
106
+ @options = Common.with_subcommand_exception_handling @option_parser do
107
+ @option_parser.parse
108
+ end
109
+ set_options
110
+
111
+ puts version_list_from(version).join(@separator)
112
+ end
113
+ end
117
114
  end
118
115
  # rubocop:enable Metrics/ClassLength
@@ -17,37 +17,34 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
+ require_relative '../../commons/command_issuer'
21
+
20
22
  # Module with methods to handle tag metadata.
21
23
  #
22
24
  # To add a new metadata type, create a new method and link it to a symbol.
23
25
  module MetadataHandler
24
- @@metadata_computers = {}
26
+ def compute_metadata(metadata_specs)
27
+ requested_metadata = metadata_specs.split(',')
28
+ unless requested_metadata.all? { |element| metadata_computers.include?(element.to_sym) }
29
+ Common.error('Some of the metadata requested are not supported')
30
+ end
31
+ requested_metadata.map { |element| metadata_computers[element.to_sym].call }.join('-')
32
+ end
25
33
 
26
- module_function
34
+ private
27
35
 
28
36
  def last_commit_sha
29
- `git --no-pager log -n 1 --pretty=%h`.strip
37
+ CommandIssuer.run('git', '--no-pager', 'log', '-n', '1', '--pretty=%h').output.strip
30
38
  end
31
39
 
32
40
  def current_date
33
41
  Time.now.strftime('%0Y%0m%0d')
34
42
  end
35
43
 
36
- def init_computers
37
- @@metadata_computers[:sha] = proc { last_commit_sha }
38
- @@metadata_computers[:date] = proc { current_date }
39
- end
40
-
41
- public
42
-
43
- def compute_metadata(metadata_specs)
44
- metadata_specs
45
- .split(',')
46
- .map { |element| @@metadata_computers[element.to_sym].call }
47
- .join('-')
48
- end
49
-
50
- def self.included(_mod)
51
- init_computers
44
+ def metadata_computers
45
+ @metadata_computers ||= {
46
+ sha: proc { last_commit_sha },
47
+ date: proc { current_date },
48
+ }
52
49
  end
53
50
  end
@@ -22,18 +22,24 @@ module PrereleaseHandler
22
22
  FIRST_PRERELEASE = 1
23
23
  DEFAULT_PRERELEASE_STRING = 'dev'
24
24
  PRERELEASE_PLACEHOLDER = '(p)'
25
+ DEFAULT_PRERELEASE_PATTERN = "#{DEFAULT_PRERELEASE_STRING}#{PRERELEASE_PLACEHOLDER}".freeze
25
26
 
26
- @@prerelease_pattern = "#{DEFAULT_PRERELEASE_STRING}#{PRERELEASE_PLACEHOLDER}"
27
- @@old_prerelease_pattern = proc { @@prerelease_pattern }
27
+ Common.module_instance_attr(self, 'prerelease_pattern', :DEFAULT_PRERELEASE_PATTERN)
28
+ Common.module_instance_attr(self, 'old_prerelease_pattern', 'proc { prerelease_pattern }')
28
29
 
29
- module_function
30
+ def compute_prerelease(current_prerelease, need_reset: false)
31
+ new_prerelease = (need_reset ? FIRST_PRERELEASE : (extract_prerelease_number(current_prerelease) + 1)).to_s
32
+ PrereleaseHandler.prerelease_pattern.sub(PRERELEASE_PLACEHOLDER, new_prerelease)
33
+ end
34
+
35
+ private
30
36
 
31
37
  def extract_prerelease_number(current_prerelease)
32
38
  actual_old_prerelease_pattern =
33
- if @@old_prerelease_pattern.respond_to?('call')
34
- @@old_prerelease_pattern.call
39
+ if PrereleaseHandler.old_prerelease_pattern.respond_to?('call')
40
+ PrereleaseHandler.old_prerelease_pattern.call
35
41
  else
36
- @@old_prerelease_pattern
42
+ PrereleaseHandler.old_prerelease_pattern
37
43
  end
38
44
  Common.error "The given old pattern does not contains the placeholder '(p)'" unless
39
45
  actual_old_prerelease_pattern.include?(PRERELEASE_PLACEHOLDER)
@@ -45,11 +51,4 @@ module PrereleaseHandler
45
51
  "does not match the analyzed prerelease: '#{current_prerelease}'."
46
52
  end
47
53
  end
48
-
49
- public
50
-
51
- def compute_prerelease(current_prerelease, need_reset: false)
52
- new_prerelease = (need_reset ? FIRST_PRERELEASE : (extract_prerelease_number(current_prerelease) + 1)).to_s
53
- @@prerelease_pattern.sub(PRERELEASE_PLACEHOLDER, new_prerelease)
54
- end
55
54
  end
@@ -17,60 +17,28 @@
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 Init < 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 = 'init -h|(<subcommand> [<subcommand-options])'
41
- @@description = 'Initialize a new git repository with an initial empty commit.'
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
- opt :empty,
54
- 'Do not create the first, empty commit.'
55
- educate_on_error
56
- stop_on @@subcommands.keys.map(&:to_s)
57
- end
58
- # rubocop:enable Metrics/BlockLength
59
-
60
30
  def initialize
61
- super(@@usage, @@description) do
62
- @options = Common.with_subcommand_exception_handling @@option_parser do
63
- @@option_parser.parse
64
- end
65
- Common.error 'The current directory is already a git repository' if Git.in_repo?
66
-
67
- init_repository
31
+ super() do
32
+ @usage = 'init -h|(<subcommand> [<subcommand-options])'
33
+ @description = 'Initialize a new git repository with an initial empty commit.'
34
+ @subcommands = {}
68
35
  end
69
36
  end
70
37
 
71
38
  def init_repository
72
- `git init`
73
- Common.error 'Failed to init the repository' if $CHILD_STATUS.exitstatus.positive?
39
+ command_result = CommandIssuer.run('git', 'init')
40
+
41
+ Common.error 'Failed to init the repository' if command_result.exit_status.positive?
74
42
 
75
43
  create_first_commit unless @options[:empty]
76
44
 
@@ -78,8 +46,39 @@ class Init < Command
78
46
  end
79
47
 
80
48
  def create_first_commit
81
- `git commit --allow-empty -m "chore: initialize repository"`
82
- Common.error 'Failed to create first commit' if $CHILD_STATUS.exitstatus.positive?
49
+ command_result = CommandIssuer.run('git', 'commit', '--allow-empty', '-m', '"chore: initialize repository"')
50
+ Common.error 'Failed to create first commit' if command_result.exit_status.positive?
51
+ end
52
+
53
+ protected
54
+
55
+ def setup_option_parser
56
+ # This block is Optimist configuration. It is as long as the number of options of the command.
57
+ # rubocop:disable Metrics/BlockLength
58
+ @option_parser = Optimist::Parser.new(
59
+ @usage,
60
+ full_description,
61
+ stop_condition
62
+ ) do |usage_header, description, stop_condition|
63
+ usage usage_header
64
+ synopsis description
65
+ opt :empty,
66
+ 'Do not create the first, empty commit.'
67
+ educate_on_error
68
+ stop_on stop_condition
69
+ end
70
+ # rubocop:enable Metrics/BlockLength
71
+ end
72
+
73
+ def setup_action
74
+ @action = lambda do
75
+ @options = Common.with_subcommand_exception_handling @option_parser do
76
+ @option_parser.parse
77
+ end
78
+ Common.error 'The current directory is already a git repository' if Git.in_repo?
79
+
80
+ init_repository
81
+ end
83
82
  end
84
83
  end
85
84
  # rubocop:enable Metrics/ClassLength
@@ -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