git_toolbox 0.4.1 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3adafc4b788997e3c6f09b238f7638c3fce70099bbf799da197728382ddc7e24
4
- data.tar.gz: d35020b9402e19053d3c75eae5b6cdfbbab3aea07b53e46c88851417e5096e34
3
+ metadata.gz: 15a670824481b1eacf235aeb9fefced3bc5f6febb2674ff11d272013aad665d5
4
+ data.tar.gz: 54be78886d9fa0ec1a2650cbcfbcc6028c7cc0b2f54f85398aa8649d2061d22b
5
5
  SHA512:
6
- metadata.gz: 4a419b04905061a0485434c13dd8c480b587419c5683f89a3b548407d87af918070a495c44fdc1be48143fad39a0f7b1399087935c92f8c4ba57a797e9894aaf
7
- data.tar.gz: 14cfd956d76eb9eb8717bd561c829756b2ed9b2959d5ddf1187ab5e34ee72a1608a3d41578f6ddd5a4b4028588ed70e82221697b9b28200ee2b5a89d41262d54
6
+ metadata.gz: 3ac823eedff5a76c6808ff2615a2a1f1fce8ed1508a868e0f73fdb66616ac23dc2344412d842c775700850a2290d075a495a304beea99ceccc0e74236102d52b
7
+ data.tar.gz: bd690477114dbf9ccf127ebb89531ca8204ccca483a2fe2fa87a97ff841cac9ae99dbd78b6793f46b68a3bd384de5b51e7baec1b3ed7a54d14c7f67fffc4fc90
@@ -17,17 +17,10 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
+ require 'English'
21
+
20
22
  # Utility module
21
23
  module Common
22
- # Groups: 1 = type, 2 = scope with (), 3 = scope, 4 = breaking change
23
- CONVENTIONAL_COMMIT_REGEX = /^(\w+)(\((\w+)\))?(!)?:.*/
24
-
25
- # Check if the command is called while in a git repository.
26
- # If the command fails, it is assumed to not be in a git repository.
27
- def self.in_git_repo?
28
- system('git rev-parse --is-inside-work-tree &>/dev/null')
29
- end
30
-
31
24
  # Print an error message and optionally run a block.
32
25
  # Stdout becomes stderr, so every print is performed to stderr.
33
26
  # This behavior is wanted as this method is called on errors.
@@ -48,17 +41,6 @@ module Common
48
41
  # Version is not needed in this command
49
42
  end
50
43
 
51
- # Run a block of code with the list of commits from the given version as an argument.
52
- # If the block is not given, this method is a nop.
53
- def self.with_commit_list_from(version = nil, &block)
54
- return unless block_given?
55
-
56
- commits_from_version =
57
- `git --no-pager log --oneline --pretty=format:%s #{version.nil? ? '' : "^#{version}"} HEAD`
58
- .split("\n")
59
- block.call(commits_from_version)
60
- end
61
-
62
44
  # Print the given message, execute a block if given,
63
45
  # and exit the program with the given exit status.
64
46
  # If exit_status is not 0, the stdout is redirected to stderr.
@@ -0,0 +1,59 @@
1
+ # Get is a toolbox based on git which simplifies the adoption of conventions and some git commands.
2
+ # Copyright (C) 2023 Alex Speranza
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program and the additional permissions granted by
16
+ # the Lesser GPL. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require 'English'
21
+ require 'get/commons/common'
22
+
23
+ # Utility module
24
+ module Git
25
+ # Groups: 1 = type, 2 = scope with (), 3 = scope, 4 = breaking change, 5 = summary
26
+ CONVENTIONAL_COMMIT_REGEX = /^(\w+)(\((\w+)\))?(!)?:(.*)/
27
+
28
+ # Check if the command is called while in a git repository.
29
+ # If the command fails, it is assumed to not be in a git repository.
30
+ def self.in_repo?
31
+ system('git rev-parse --is-inside-work-tree &>/dev/null')
32
+ case $CHILD_STATUS.exitstatus
33
+ when 0 then true
34
+ when 127 then Common.error '"git" is not installed.'
35
+ else false
36
+ end
37
+ end
38
+
39
+ # Run a block of code with the list of commits from the given version as an argument.
40
+ # If the block is not given, this method is a nop.
41
+ def self.with_commit_list_from(version = nil, &block)
42
+ return unless block_given?
43
+
44
+ commits_from_version =
45
+ `git --no-pager log --oneline --pretty=format:%s #{version.nil? ? '' : "^#{version}"} HEAD`
46
+ .split("\n")
47
+ block.call(commits_from_version)
48
+ end
49
+
50
+ # Returns the last version and caches it for the next calls.
51
+ def self.last_version
52
+ @@last_version ||= `git describe --tags --abbrev=0`.strip
53
+ end
54
+
55
+ # Returns the last release and caches it for the next calls.
56
+ def self.last_release
57
+ @@last_release ||= `git --no-pager tag --list | sed 's/+/_/' | sort -V | sed 's/_/+/' | tail -n 1`.strip
58
+ end
59
+ end
@@ -0,0 +1,165 @@
1
+ # Get is a toolbox based on git which simplifies the adoption of conventions and some git commands.
2
+ # Copyright (C) 2023 Alex Speranza
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program and the additional permissions granted by
16
+ # the Lesser GPL. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require 'English'
21
+ require 'get/commons/common'
22
+ require 'get/commons/git'
23
+ require 'get/subcommand/command'
24
+
25
+ # Class length is disabled as most of its length is given by formatting.
26
+ # rubocop:disable Metrics/ClassLength
27
+ # Subcommand, generates a changelog.
28
+ class Changelog < Command
29
+ def self.command
30
+ @@command ||= new
31
+ @@command
32
+ end
33
+
34
+ private_class_method :new
35
+
36
+ private
37
+
38
+ UNDEFINED_SCOPE = 'other'
39
+ MARKDOWN_FORMAT = {
40
+ title: '# %s',
41
+ type: '## %s',
42
+ scope: '### %s',
43
+ list: '%s',
44
+ item: '- %s'
45
+ }.freeze
46
+
47
+ @@command = nil
48
+
49
+ @@usage = 'changelog -h|(<subcommand> [<subcommand-options])'
50
+ @@description = 'Generate a changelog. Format options require a "%s" where the content must be.'
51
+ @@subcommands = {}
52
+ # This block is Optimist configuration. It is as long as the number of options of the command.
53
+ # rubocop:disable Metrics/BlockLength
54
+ @@option_parser = Optimist::Parser.new do
55
+ subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
56
+ subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
57
+ Subcommands:
58
+ #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
59
+ SUBCOMMANDS
60
+ usage @@usage
61
+ synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
62
+ opt :latest,
63
+ 'Generate the changelog from the latest version rather than the latest release'
64
+ opt :title_format,
65
+ 'Set the symbol for the title.',
66
+ { type: :string, short: 'T', default: '# %s' }
67
+ opt :type_format,
68
+ 'Set the symbol for the commit types.',
69
+ { type: :string, short: 't', default: '= %s' }
70
+ opt :scope_format,
71
+ 'Set the symbol for the commit scopes.',
72
+ { type: :string, short: 's', default: '- %s' }
73
+ opt :list_format,
74
+ 'Set the symbol for lists.',
75
+ { type: :string, short: 'l', default: '%s' }
76
+ opt :item_format,
77
+ 'Set the symbol for list items.',
78
+ { type: :string, short: 'i', default: '* %s' }
79
+ opt :markdown,
80
+ 'Shortcut for `-T "# %s" -t "## %s" -s "### %s" -l "%s" -i "- %s"`. ' \
81
+ 'Can be overwritten by the single options.'
82
+ educate_on_error
83
+ stop_on @@subcommands.keys.map(&:to_s)
84
+ end
85
+ # rubocop:enable Metrics/BlockLength
86
+
87
+ def initialize
88
+ super(@@usage, @@description) do
89
+ @options = Common.with_subcommand_exception_handling @@option_parser do
90
+ @@option_parser.parse
91
+ end
92
+ Common.error 'changelog need to be run inside a git repository' unless Git.in_repo?
93
+ @format = {}
94
+ set_format
95
+
96
+ puts changelog_from(@options[:latest] ? Git.last_version : Git.last_release)
97
+ end
98
+ end
99
+
100
+ def set_format
101
+ %w[title type scope list item].each do |element|
102
+ @format[element.to_sym] = if @options[:markdown] && !@options["#{element}_given".to_sym]
103
+ MARKDOWN_FORMAT[element.to_sym]
104
+ else
105
+ unless @options["#{element}_format".to_sym].include? '%s'
106
+ Common.error "The given format for '#{element}' must contain '%s'."
107
+ end
108
+ @options["#{element}_format".to_sym]
109
+ end
110
+ end
111
+ end
112
+
113
+ def changelog_from(version)
114
+ commit_map = {}
115
+
116
+ Git.with_commit_list_from(version) do |list|
117
+ list.each do |element|
118
+ match_result = Git::CONVENTIONAL_COMMIT_REGEX.match(element)
119
+ temp_hash = {
120
+ match_result[1] => {
121
+ (match_result[3] || UNDEFINED_SCOPE) => [match_result[5].strip.capitalize]
122
+ }
123
+ }
124
+ commit_map.merge!(temp_hash) do |_key, old_value, new_value|
125
+ old_value.merge(new_value) do |_inner_key, old_array, new_array|
126
+ old_array + new_array
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ format_changelog(version, commit_map)
133
+ end
134
+
135
+ def format_changelog(from_version, changelog)
136
+ formatted_features = changelog.key?('feat') ? [format_type('feat', changelog['feat'])] : []
137
+ formatted_fixes = changelog.key?('fix') ? [format_type('fix', changelog['fix'])] : []
138
+
139
+ formatted_types = []
140
+ changelog.except('feat', 'fix').each { |key, value| formatted_types.push(format_type(key, value)) }
141
+ <<~CHANGELOG
142
+ #{@format[:title].sub('%s', "Changelog from version #{from_version}")}
143
+ #{(formatted_features + formatted_fixes + formatted_types).join("\n").strip}
144
+ CHANGELOG
145
+ end
146
+
147
+ def format_type(type, scopes)
148
+ formatted_scopes = []
149
+ scopes.each { |key, value| formatted_scopes.push(format_scope(key, value)) }
150
+ <<~TYPE
151
+ #{@format[:type].sub('%s', type.to_s)}
152
+ #{formatted_scopes.join("\n").strip}
153
+ TYPE
154
+ end
155
+
156
+ def format_scope(scope, commits)
157
+ formatted_commits = []
158
+ commits.each { |element| formatted_commits.push(@format[:item].sub('%s', element)) }
159
+ <<~SCOPE
160
+ #{@format[:scope].sub('%s', scope.to_s)}
161
+ #{@format[:list].sub('%s', formatted_commits.join("\n"))}
162
+ SCOPE
163
+ end
164
+ end
165
+ # rubocop:enable Metrics/ClassLength
@@ -18,7 +18,8 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  require 'English'
21
- require 'get/common'
21
+ require 'get/commons/common'
22
+ require 'get/commons/git'
22
23
  require 'get/subcommand/command'
23
24
  require 'get/subcommand/commit/prompt'
24
25
 
@@ -40,17 +41,18 @@ class Commit < Command
40
41
  @@command = nil
41
42
 
42
43
  @@usage = 'commit -h|(<subcommand> [<subcommand-options])'
43
- @@description = 'Create a new semantic commit'
44
+ @@description = 'Create a new semantic commit.'
44
45
  @@subcommands = {}
45
46
  # This block is Optimist configuration. It is as long as the number of options of the command.
46
47
  # rubocop:disable Metrics/BlockLength
47
- @@commit_parser = Optimist::Parser.new do
48
+ @@option_parser = Optimist::Parser.new do
48
49
  subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
49
- usage @@usage
50
- synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
50
+ subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
51
51
  Subcommands:
52
52
  #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
53
53
  SUBCOMMANDS
54
+ usage @@usage
55
+ synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
54
56
  opt :type,
55
57
  'Define the type of the commit. Enabling this option skips the type selection.',
56
58
  { type: :string }
@@ -75,9 +77,9 @@ class Commit < Command
75
77
 
76
78
  def initialize
77
79
  super(@@usage, @@description) do
78
- Common.error 'commit need to be run inside a git repository' unless Common.in_git_repo?
79
- @options = Common.with_subcommand_exception_handling @@commit_parser do
80
- @@commit_parser.parse
80
+ Common.error 'commit need to be run inside a git repository' unless Git.in_repo?
81
+ @options = Common.with_subcommand_exception_handling @@option_parser do
82
+ @@option_parser.parse
81
83
  end
82
84
 
83
85
  message = full_commit_message
@@ -18,6 +18,7 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  require 'highline'
21
+ require 'get/commons/git'
21
22
 
22
23
  # Module for asking to the user informations about a commit message.
23
24
  module PromptHandler
@@ -107,9 +108,9 @@ module PromptHandler
107
108
  def extract_types_and_scopes
108
109
  return unless @@custom_values_initialized.nil?
109
110
 
110
- Common.with_commit_list_from(FIRST_COMMIT) do |commit_list|
111
+ Git.with_commit_list_from(FIRST_COMMIT) do |commit_list|
111
112
  commit_list.map do |element|
112
- match = Common::CONVENTIONAL_COMMIT_REGEX.match(element)
113
+ match = Git::CONVENTIONAL_COMMIT_REGEX.match(element)
113
114
  next if match.nil?
114
115
 
115
116
  type_already_added = DEFAULT_TYPES.include?(match[1].to_sym) || @@custom_types.include?(match[1])
@@ -0,0 +1,92 @@
1
+ # Get is a toolbox based on git which simplifies the adoption of conventions and some git commands.
2
+ # Copyright (C) 2023 Alex Speranza
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program and the additional permissions granted by
16
+ # the Lesser GPL. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ # Module with bash completion generation functions
21
+ module BashCompletion
22
+ def bash_completion(main_module, command_name)
23
+ generate_functions(main_module, command_name, INITIAL_LEVEL)
24
+
25
+ "#{HEADER}\n#{@@function_stack.reverse.join("\n")}"
26
+ end
27
+
28
+ private
29
+
30
+ INITIAL_LEVEL = 1
31
+
32
+ HEADER = <<~HEADER
33
+ #!/usr/bin/env bash
34
+ #
35
+ # This file has been generated by `get complete`.
36
+ # The script structure was inspired by https://opensource.com/article/18/3/creating-bash-completion-script
37
+ #
38
+ HEADER
39
+
40
+ @@function_stack = []
41
+
42
+ def full_subcommand_name(base, name)
43
+ "#{base}_#{name}"
44
+ end
45
+
46
+ def completion_function_definition(name, level, long_options, short_options, subcommands)
47
+ <<~FUNCTION
48
+ _#{name}_completion()
49
+ {
50
+ if [ "${COMP_CWORD}" -eq "#{level}" ] ; then
51
+ COMPREPLY=($(compgen -W "#{(long_options | short_options | subcommands).join(' ')}" -- "${COMP_WORDS[#{level}]}"))
52
+ else
53
+ case "${COMP_WORDS[#{level}]}" in
54
+ #{subcommands.map { |element| subcommand_case_completion(name, element) }.join("\n")}
55
+ *)
56
+ COMPREPLY=()
57
+ ;;
58
+ esac
59
+ fi
60
+ }
61
+ FUNCTION
62
+ end
63
+
64
+ def subcommand_case_completion(base, name)
65
+ <<~CASE
66
+ "#{name}")
67
+ _#{full_subcommand_name(base, name)}_completion
68
+ ;;
69
+ CASE
70
+ end
71
+
72
+ def generate_functions(command_class, name, level)
73
+ long_options = []
74
+ short_options = []
75
+ subcommands = []
76
+
77
+ command_class.class_variable_get(:@@option_parser).specs.each_value do |option|
78
+ long_options.push("--#{option.long}")
79
+ short_options.push("-#{option.short.nil? ? option.long[0] : option.short}") if option.short != :none
80
+ end
81
+
82
+ command_class.class_variable_get(:@@subcommands).each_key do |subcommand|
83
+ subcommands.push(subcommand.to_s)
84
+ end
85
+
86
+ @@function_stack.push(completion_function_definition(name, level, long_options, short_options, subcommands))
87
+
88
+ command_class.class_variable_get(:@@subcommands).each do |element|
89
+ generate_functions(element[1].class, full_subcommand_name(name, element[0].to_s), level + 1)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,82 @@
1
+ # Get is a toolbox based on git which simplifies the adoption of conventions and some git commands.
2
+ # Copyright (C) 2023 Alex Speranza
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program and the additional permissions granted by
16
+ # the Lesser GPL. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require 'English'
21
+ require 'get/commons/common'
22
+ require 'get/subcommand/command'
23
+ require 'get/subcommand/complete/bash_completion'
24
+ require 'get'
25
+
26
+ # Class length is disabled as most of its length is given by formatting.
27
+ # rubocop:disable Metrics/ClassLength
28
+ # Subcommand, prints the bash completion script.
29
+ class Complete < Command
30
+ def self.command
31
+ @@command ||= new
32
+ @@command
33
+ end
34
+
35
+ private_class_method :new
36
+
37
+ private
38
+
39
+ include BashCompletion
40
+
41
+ @@command = nil
42
+
43
+ @@usage = 'complete -h|(<subcommand> [<subcommand-options])'
44
+ @@description = 'Print the shell completion script.'
45
+ @@subcommands = {}
46
+ # This block is Optimist configuration. It is as long as the number of options of the command.
47
+ # rubocop:disable Metrics/BlockLength
48
+ @@option_parser = Optimist::Parser.new do
49
+ subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
50
+ subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
51
+ Subcommands:
52
+ #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
53
+ SUBCOMMANDS
54
+ usage @@usage
55
+ synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
56
+ opt :shell,
57
+ 'Select the type of shell of which the completion will be generated.',
58
+ { type: :string, default: 'bash' }
59
+ educate_on_error
60
+ stop_on @@subcommands.keys.map(&:to_s)
61
+ end
62
+ # rubocop:enable Metrics/BlockLength
63
+
64
+ def initialize
65
+ super(@@usage, @@description) do
66
+ @options = Common.with_subcommand_exception_handling @@option_parser do
67
+ @@option_parser.parse
68
+ end
69
+
70
+ @completions = {
71
+ bash: proc { bash_completion(Get, 'get') }
72
+ }
73
+
74
+ selected_shell = @options[:shell].to_sym
75
+
76
+ Common.error "Completion for shell '#{selected_shell}' not available." unless @completions.key?(selected_shell)
77
+
78
+ puts @completions[selected_shell].call
79
+ end
80
+ end
81
+ end
82
+ # rubocop:enable Metrics/ClassLength
@@ -51,7 +51,7 @@ module ChangeHandler
51
51
  class ::String
52
52
  # Convert the string (as a conventional commit string) into a change type.
53
53
  def to_change
54
- groups = Common::CONVENTIONAL_COMMIT_REGEX.match(self)
54
+ groups = Git::CONVENTIONAL_COMMIT_REGEX.match(self)
55
55
  return :MAJOR if ChangeHandler.triggers_major?(groups[1], groups[3], !groups[4].nil?)
56
56
  return :MINOR if ChangeHandler.triggers_minor?(groups[1], groups[2])
57
57
  return :PATCH if ChangeHandler.triggers_patch?(groups[1], groups[2])
@@ -64,7 +64,7 @@ module ChangeHandler
64
64
 
65
65
  def greatest_change_in(commit_list)
66
66
  commit_list
67
- .grep(Common::CONVENTIONAL_COMMIT_REGEX)
67
+ .grep(Git::CONVENTIONAL_COMMIT_REGEX)
68
68
  .map(&:to_change)
69
69
  .max { |a, b| CHANGE_TYPE.index(a) <=> CHANGE_TYPE.index(b) }
70
70
  end
@@ -17,6 +17,8 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
+ require 'get/commons/common'
21
+ require 'get/commons/git'
20
22
  require 'get/subcommand/command'
21
23
  require 'get/subcommand/describe/change'
22
24
  require 'get/subcommand/describe/prerelease'
@@ -50,19 +52,20 @@ class Describe < Command
50
52
  /x
51
53
 
52
54
  @@usage = 'describe -h|(<subcommand> [<subcommand-options])'
53
- @@description = 'Describe the current git repository with semantic version'
55
+ @@description = 'Describe the current git repository with semantic version.'
54
56
  @@subcommands = {
55
57
  docker: DescribeDocker.command,
56
58
  }
57
59
  # This block is Optimist configuration. It is as long as the number of options of the command.
58
60
  # rubocop:disable Metrics/BlockLength
59
- @@describe_parser = Optimist::Parser.new do
61
+ @@option_parser = Optimist::Parser.new do
60
62
  subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
61
- usage @@usage
62
- synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
63
+ subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
63
64
  Subcommands:
64
65
  #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
65
66
  SUBCOMMANDS
67
+ usage @@usage
68
+ synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
66
69
  opt :prerelease,
67
70
  'Describe a prerelease rather than a release',
68
71
  short: :none
@@ -109,9 +112,9 @@ class Describe < Command
109
112
 
110
113
  def initialize
111
114
  super(@@usage, @@description) do
112
- Common.error 'describe need to be run inside a git repository' unless Common.in_git_repo?
113
- @options = Common.with_subcommand_exception_handling @@describe_parser do
114
- @@describe_parser.parse
115
+ Common.error 'describe need to be run inside a git repository' unless Git.in_repo?
116
+ @options = Common.with_subcommand_exception_handling @@option_parser do
117
+ @@option_parser.parse
115
118
  end
116
119
  set_options
117
120
 
@@ -141,9 +144,9 @@ class Describe < Command
141
144
  end
142
145
 
143
146
  def describe_current_commit
144
- return last_version if Common.with_commit_list_from(last_version, &:empty?)
147
+ return Git.last_version if Git.with_commit_list_from(Git.last_version, &:empty?)
145
148
 
146
- puts "Last version: #{last_version}" if @options[:diff]
149
+ puts "Last version: #{Git.last_version}" if @options[:diff]
147
150
 
148
151
  current_commit_version = next_release
149
152
  create_signed_tag(current_commit_version) if @options[:create_tag]
@@ -153,9 +156,9 @@ class Describe < Command
153
156
 
154
157
  def next_release
155
158
  if @options[:prerelease]
156
- prepare_prerelease_tag(last_release, last_version)
159
+ prepare_prerelease_tag(Git.last_release, Git.last_version)
157
160
  else
158
- prepare_release_tag(last_release)
161
+ prepare_release_tag(Git.last_release)
159
162
  end + metadata
160
163
  end
161
164
 
@@ -172,20 +175,10 @@ class Describe < Command
172
175
  "-#{updated_prerelease(base_version_match_data[5], need_reset: base_version_match_data[1] != new_stable_version)}"
173
176
  end
174
177
 
175
- # Returns the last version and caches it for the next calls.
176
- def last_version
177
- @last_version ||= `git describe --tags --abbrev=0`.strip
178
- end
179
-
180
- # Returns the last release and caches it for the next calls.
181
- def last_release
182
- @last_release ||= `git --no-pager tag --list | sed 's/+/_/' | sort -V | sed 's/_/+/' | tail -n 1`.strip
183
- end
184
-
185
178
  def updated_stable_version(stable_version)
186
179
  return DEFAULT_RELEASE_VERSION if stable_version.nil?
187
180
 
188
- greatest_change_from_stable_version = Common.with_commit_list_from(stable_version) do |commits_from_version|
181
+ greatest_change_from_stable_version = Git.with_commit_list_from(stable_version) do |commits_from_version|
189
182
  greatest_change_in(commits_from_version)
190
183
  end
191
184
  split_version = stable_version.split('.')
@@ -17,6 +17,8 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
+ require 'get/commons/common'
21
+ require 'get/commons/git'
20
22
  require 'get/subcommand/command'
21
23
 
22
24
  # Class length is disabled as most of its length is given by formatting.
@@ -41,7 +43,7 @@ class DescribeDocker < Command
41
43
  @@subcommands = {}
42
44
  # This block is Optimist configuration. It is as long as the number of options of the command.
43
45
  # rubocop:disable Metrics/BlockLength
44
- @@describe_parser = Optimist::Parser.new do
46
+ @@option_parser = Optimist::Parser.new do
45
47
  subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
46
48
  usage @@usage
47
49
  synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
@@ -64,9 +66,9 @@ class DescribeDocker < Command
64
66
 
65
67
  def initialize
66
68
  super(@@usage, @@description) do |version|
67
- Common.error 'describe need to be run inside a git repository' unless Common.in_git_repo?
68
- @options = Common.with_subcommand_exception_handling @@describe_parser do
69
- @@describe_parser.parse
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
70
72
  end
71
73
  set_options
72
74