git_toolbox 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27cbd5cdcfa5aba4e9e23c32e9db785360fbb3e9d044e9a613a2d6249e5131c2
4
- data.tar.gz: 6e59f2ad922d63d9ab8a73fb28c4b2f8c61da3d96538664033e0587e75e80085
3
+ metadata.gz: 15a670824481b1eacf235aeb9fefced3bc5f6febb2674ff11d272013aad665d5
4
+ data.tar.gz: 54be78886d9fa0ec1a2650cbcfbcc6028c7cc0b2f54f85398aa8649d2061d22b
5
5
  SHA512:
6
- metadata.gz: 52f10e7a912dbda4211dec1ab913d7411ea029dbe27e1ba208f68b427da74c7a9c711e20acbf952c064b52f91713456dbb604384dd5158dc91d059e5ffe9c740
7
- data.tar.gz: c642826b2c4a798ce1ce85d15b458d2eff955c0be874e307b18e25056e747d6d044e7ed69dc94093bc03865041a17b7db32c3de456d9b3fc910b4457b68f4f23
6
+ metadata.gz: 3ac823eedff5a76c6808ff2615a2a1f1fce8ed1508a868e0f73fdb66616ac23dc2344412d842c775700850a2290d075a495a304beea99ceccc0e74236102d52b
7
+ data.tar.gz: bd690477114dbf9ccf127ebb89531ca8204ccca483a2fe2fa87a97ff841cac9ae99dbd78b6793f46b68a3bd384de5b51e7baec1b3ed7a54d14c7f67fffc4fc90
@@ -21,20 +21,6 @@ require 'English'
21
21
 
22
22
  # Utility module
23
23
  module Common
24
- # Groups: 1 = type, 2 = scope with (), 3 = scope, 4 = breaking change
25
- CONVENTIONAL_COMMIT_REGEX = /^(\w+)(\((\w+)\))?(!)?:.*/
26
-
27
- # Check if the command is called while in a git repository.
28
- # If the command fails, it is assumed to not be in a git repository.
29
- def self.in_git_repo?
30
- system('git rev-parse --is-inside-work-tree &>/dev/null')
31
- case $CHILD_STATUS.exitstatus
32
- when 0 then true
33
- when 127 then Common.error '"git" is not installed.'
34
- else false
35
- end
36
- end
37
-
38
24
  # Print an error message and optionally run a block.
39
25
  # Stdout becomes stderr, so every print is performed to stderr.
40
26
  # This behavior is wanted as this method is called on errors.
@@ -55,17 +41,6 @@ module Common
55
41
  # Version is not needed in this command
56
42
  end
57
43
 
58
- # Run a block of code with the list of commits from the given version as an argument.
59
- # If the block is not given, this method is a nop.
60
- def self.with_commit_list_from(version = nil, &block)
61
- return unless block_given?
62
-
63
- commits_from_version =
64
- `git --no-pager log --oneline --pretty=format:%s #{version.nil? ? '' : "^#{version}"} HEAD`
65
- .split("\n")
66
- block.call(commits_from_version)
67
- end
68
-
69
44
  # Print the given message, execute a block if given,
70
45
  # and exit the program with the given exit status.
71
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
 
@@ -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
 
24
25
  # Class length is disabled as most of its length is given by formatting.
@@ -37,17 +38,18 @@ class Init < Command
37
38
  @@command = nil
38
39
 
39
40
  @@usage = 'init -h|(<subcommand> [<subcommand-options])'
40
- @@description = 'Initialize a new git repository with an initial empty commit'
41
+ @@description = 'Initialize a new git repository with an initial empty commit.'
41
42
  @@subcommands = {}
42
43
  # This block is Optimist configuration. It is as long as the number of options of the command.
43
44
  # rubocop:disable Metrics/BlockLength
44
- @@commit_parser = Optimist::Parser.new do
45
+ @@option_parser = Optimist::Parser.new do
45
46
  subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
46
- usage @@usage
47
- synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
47
+ subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
48
48
  Subcommands:
49
49
  #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
50
50
  SUBCOMMANDS
51
+ usage @@usage
52
+ synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
51
53
  opt :empty,
52
54
  'Do not create the first, empty commit.'
53
55
  educate_on_error
@@ -57,10 +59,10 @@ class Init < Command
57
59
 
58
60
  def initialize
59
61
  super(@@usage, @@description) do
60
- @options = Common.with_subcommand_exception_handling @@commit_parser do
61
- @@commit_parser.parse
62
+ @options = Common.with_subcommand_exception_handling @@option_parser do
63
+ @@option_parser.parse
62
64
  end
63
- Common.error 'The current directory is already a git repository' if Common.in_git_repo?
65
+ Common.error 'The current directory is already a git repository' if Git.in_repo?
64
66
 
65
67
  init_repository
66
68
  end
@@ -17,7 +17,8 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- require 'get/common'
20
+ require 'get/commons/common'
21
+ require 'get/commons/git'
21
22
  require 'get/subcommand/command'
22
23
  require 'get/subcommand/license/license_retriever'
23
24
 
@@ -45,7 +46,7 @@ class License < Command
45
46
  @@subcommands = {}
46
47
  # This block is Optimist configuration. It is as long as the number of options of the command.
47
48
  # rubocop:disable Metrics/BlockLength
48
- @@commit_parser = Optimist::Parser.new do
49
+ @@option_parser = Optimist::Parser.new do
49
50
  subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
50
51
  subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
51
52
  Subcommands:
@@ -68,8 +69,8 @@ class License < Command
68
69
 
69
70
  def initialize
70
71
  super(@@usage, @@description) do
71
- @options = Common.with_subcommand_exception_handling @@commit_parser do
72
- @@commit_parser.parse
72
+ @options = Common.with_subcommand_exception_handling @@option_parser do
73
+ @@option_parser.parse
73
74
  end
74
75
 
75
76
  @filename = 'LICENSE'
@@ -79,7 +80,7 @@ class License < Command
79
80
  create_license_file
80
81
 
81
82
  if @options[:create_commit]
82
- Common.error 'Not in a git repository: a commit cannot be created.' unless Common.in_git_repo?
83
+ Common.error 'Not in a git repository: a commit cannot be created.' unless Git.in_repo?
83
84
 
84
85
  create_license_commit
85
86
  end
data/lib/get/version.rb CHANGED
@@ -18,5 +18,5 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  module Get
21
- VERSION = '0.5.0'
21
+ VERSION = '0.6.0'
22
22
  end
data/lib/get.rb CHANGED
@@ -23,8 +23,10 @@ require 'get/subcommand/describe/describe'
23
23
  require 'get/subcommand/commit/commit'
24
24
  require 'get/subcommand/init/init'
25
25
  require 'get/subcommand/license/license'
26
+ require 'get/subcommand/complete/complete'
27
+ require 'get/subcommand/changelog/changelog'
26
28
  require 'get/version'
27
- require 'get/common'
29
+ require 'get/commons/common'
28
30
 
29
31
  # Entrypoint of Get
30
32
  module Get
@@ -35,6 +37,8 @@ module Get
35
37
  commit: Commit.command,
36
38
  init: Init.command,
37
39
  license: License.command,
40
+ complete: Complete.command,
41
+ changelog: Changelog.command,
38
42
  }
39
43
  @@option_parser = Optimist::Parser.new do
40
44
  subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git_toolbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.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-03-03 00:00:00.000000000 Z
11
+ date: 2023-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: optimist
@@ -55,10 +55,14 @@ files:
55
55
  - bin/get
56
56
  - bin/setup
57
57
  - lib/get.rb
58
- - lib/get/common.rb
58
+ - lib/get/commons/common.rb
59
+ - lib/get/commons/git.rb
60
+ - lib/get/subcommand/changelog/changelog.rb
59
61
  - lib/get/subcommand/command.rb
60
62
  - lib/get/subcommand/commit/commit.rb
61
63
  - lib/get/subcommand/commit/prompt.rb
64
+ - lib/get/subcommand/complete/bash_completion.rb
65
+ - lib/get/subcommand/complete/complete.rb
62
66
  - lib/get/subcommand/describe/change.rb
63
67
  - lib/get/subcommand/describe/describe.rb
64
68
  - lib/get/subcommand/describe/docker/docker.rb