git_toolbox 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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