git_toolbox 0.4.1
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 +7 -0
- data/bin/get +8 -0
- data/bin/setup +8 -0
- data/lib/get/common.rb +72 -0
- data/lib/get/subcommand/command.rb +31 -0
- data/lib/get/subcommand/commit/commit.rb +145 -0
- data/lib/get/subcommand/commit/prompt.rb +123 -0
- data/lib/get/subcommand/describe/change.rb +71 -0
- data/lib/get/subcommand/describe/describe.rb +230 -0
- data/lib/get/subcommand/describe/docker/docker.rb +116 -0
- data/lib/get/subcommand/describe/metadata.rb +53 -0
- data/lib/get/subcommand/describe/prerelease.rb +55 -0
- data/lib/get/subcommand/init/init.rb +85 -0
- data/lib/get/version.rb +22 -0
- data/lib/get.rb +67 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3adafc4b788997e3c6f09b238f7638c3fce70099bbf799da197728382ddc7e24
|
4
|
+
data.tar.gz: d35020b9402e19053d3c75eae5b6cdfbbab3aea07b53e46c88851417e5096e34
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a419b04905061a0485434c13dd8c480b587419c5683f89a3b548407d87af918070a495c44fdc1be48143fad39a0f7b1399087935c92f8c4ba57a797e9894aaf
|
7
|
+
data.tar.gz: 14cfd956d76eb9eb8717bd561c829756b2ed9b2959d5ddf1187ab5e34ee72a1608a3d41578f6ddd5a4b4028588ed70e82221697b9b28200ee2b5a89d41262d54
|
data/bin/get
ADDED
data/bin/setup
ADDED
data/lib/get/common.rb
ADDED
@@ -0,0 +1,72 @@
|
|
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
|
+
# Utility module
|
21
|
+
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
|
+
# Print an error message and optionally run a block.
|
32
|
+
# Stdout becomes stderr, so every print is performed to stderr.
|
33
|
+
# This behavior is wanted as this method is called on errors.
|
34
|
+
def self.error(message, &block)
|
35
|
+
Common.print_then_do_and_exit("Error: #{message}", 1, block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Subcommand exception handling for Optimist.
|
39
|
+
# Generally subcommands do not have a version to print.
|
40
|
+
def self.with_subcommand_exception_handling(parser)
|
41
|
+
yield
|
42
|
+
rescue Optimist::CommandlineError => e
|
43
|
+
parser.die(e.message, nil, e.error_code)
|
44
|
+
rescue Optimist::HelpNeeded
|
45
|
+
parser.educate
|
46
|
+
exit
|
47
|
+
rescue Optimist::VersionNeeded
|
48
|
+
# Version is not needed in this command
|
49
|
+
end
|
50
|
+
|
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
|
+
# Print the given message, execute a block if given,
|
63
|
+
# and exit the program with the given exit status.
|
64
|
+
# If exit_status is not 0, the stdout is redirected to stderr.
|
65
|
+
def self.print_then_do_and_exit(message, exit_code = 0, action = proc {})
|
66
|
+
$stdout = $stderr unless exit_code.zero?
|
67
|
+
|
68
|
+
puts message
|
69
|
+
action.call if action.respond_to?('call')
|
70
|
+
exit(exit_code)
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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
|
+
# Base class for (sub)commands. (Sub)Commands should be singletons.
|
21
|
+
class Command
|
22
|
+
attr_reader :description, :action
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def initialize(usage, description, &action)
|
27
|
+
@usage = usage
|
28
|
+
@description = description
|
29
|
+
@action = action
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,145 @@
|
|
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/common'
|
22
|
+
require 'get/subcommand/command'
|
23
|
+
require 'get/subcommand/commit/prompt'
|
24
|
+
|
25
|
+
# Class length is disabled as most of its length is given by formatting.
|
26
|
+
# rubocop:disable Metrics/ClassLength
|
27
|
+
# Subcommand, it manages the description of the current git repository using semantic version.
|
28
|
+
class Commit < Command
|
29
|
+
def self.command
|
30
|
+
@@command ||= new
|
31
|
+
@@command
|
32
|
+
end
|
33
|
+
|
34
|
+
private_class_method :new
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
include PromptHandler
|
39
|
+
|
40
|
+
@@command = nil
|
41
|
+
|
42
|
+
@@usage = 'commit -h|(<subcommand> [<subcommand-options])'
|
43
|
+
@@description = 'Create a new semantic commit'
|
44
|
+
@@subcommands = {}
|
45
|
+
# This block is Optimist configuration. It is as long as the number of options of the command.
|
46
|
+
# rubocop:disable Metrics/BlockLength
|
47
|
+
@@commit_parser = Optimist::Parser.new do
|
48
|
+
subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
|
49
|
+
usage @@usage
|
50
|
+
synopsis <<~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
|
+
opt :type,
|
55
|
+
'Define the type of the commit. Enabling this option skips the type selection.',
|
56
|
+
{ type: :string }
|
57
|
+
opt :scope,
|
58
|
+
'Define the scope of the commit. Enabling this option skips the scope selection.',
|
59
|
+
{ type: :string, short: 'S' }
|
60
|
+
opt :summary,
|
61
|
+
'Define the summary message of the commit. Enabling this option skips the summary message prompt.',
|
62
|
+
{ type: :string, short: 's' }
|
63
|
+
opt :message,
|
64
|
+
'Define the message body of the commit. Enabling this option skips the message body prompt.',
|
65
|
+
{ type: :string }
|
66
|
+
opt :breaking,
|
67
|
+
'Set the commit to have a breaking change. ' \
|
68
|
+
'Can be negated with "--no-breaking". ' \
|
69
|
+
'Enabling this option skips the breaking change prompt.',
|
70
|
+
{ type: :flag, short: :none }
|
71
|
+
educate_on_error
|
72
|
+
stop_on @@subcommands.keys.map(&:to_s)
|
73
|
+
end
|
74
|
+
# rubocop:enable Metrics/BlockLength
|
75
|
+
|
76
|
+
def initialize
|
77
|
+
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
|
81
|
+
end
|
82
|
+
|
83
|
+
message = full_commit_message
|
84
|
+
puts message
|
85
|
+
output = `git commit --no-status -m "#{message.gsub('"', '\"')}"`
|
86
|
+
Common.error "git commit failed: #{output}" if $CHILD_STATUS.exitstatus.positive?
|
87
|
+
rescue Interrupt
|
88
|
+
Common.print_then_do_and_exit "\nCommit cancelled"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def full_commit_message
|
93
|
+
type = commit_type
|
94
|
+
scope = commit_scope
|
95
|
+
breaking = commit_breaking?
|
96
|
+
summary = commit_summary
|
97
|
+
body = commit_body
|
98
|
+
"#{type}" \
|
99
|
+
"#{scope.nil? || scope.empty? ? '' : "(#{scope})"}" \
|
100
|
+
"#{breaking ? '!' : ''}" \
|
101
|
+
": #{summary}" \
|
102
|
+
"#{body.empty? ? '' : "\n\n#{body}"}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def commit_type
|
106
|
+
if @options[:type_given]
|
107
|
+
@options[:type]
|
108
|
+
else
|
109
|
+
ask_for_type
|
110
|
+
end.to_s.strip
|
111
|
+
end
|
112
|
+
|
113
|
+
def commit_scope
|
114
|
+
if @options[:scope_given]
|
115
|
+
@options[:scope]
|
116
|
+
else
|
117
|
+
ask_for_scope
|
118
|
+
end.to_s.strip
|
119
|
+
end
|
120
|
+
|
121
|
+
def commit_breaking?
|
122
|
+
if @options[:breaking_given]
|
123
|
+
@options[:breaking]
|
124
|
+
else
|
125
|
+
ask_for_breaking
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def commit_summary
|
130
|
+
if @options[:summary_given]
|
131
|
+
@options[:summary]
|
132
|
+
else
|
133
|
+
ask_for_summary
|
134
|
+
end.to_s.strip
|
135
|
+
end
|
136
|
+
|
137
|
+
def commit_body
|
138
|
+
if @options[:message_given]
|
139
|
+
@options[:message]
|
140
|
+
else
|
141
|
+
ask_for_message
|
142
|
+
end.to_s.strip
|
143
|
+
end
|
144
|
+
end
|
145
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,123 @@
|
|
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 'highline'
|
21
|
+
|
22
|
+
# Module for asking to the user informations about a commit message.
|
23
|
+
module PromptHandler
|
24
|
+
@@cli = HighLine.new
|
25
|
+
|
26
|
+
@@custom_values_initialized = nil
|
27
|
+
@@custom_types = []
|
28
|
+
@@custom_scopes = []
|
29
|
+
|
30
|
+
STRING_VALUE_VALIDATOR = /\s*\S+\s*/
|
31
|
+
BODY_END_DELIMITER = "\n\n\n"
|
32
|
+
|
33
|
+
DEFAULT_TYPES = %i[
|
34
|
+
feat
|
35
|
+
fix
|
36
|
+
build
|
37
|
+
chore
|
38
|
+
ci
|
39
|
+
docs
|
40
|
+
style
|
41
|
+
refactor
|
42
|
+
perf
|
43
|
+
test
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
def ask_for_type
|
47
|
+
extract_types_and_scopes
|
48
|
+
@@cli.choose do |menu|
|
49
|
+
menu.flow = :columns_down
|
50
|
+
menu.prompt = 'Choose the type of your commit: '
|
51
|
+
DEFAULT_TYPES.union(@@custom_types).each do |type|
|
52
|
+
menu.choice(type.to_sym)
|
53
|
+
end
|
54
|
+
menu.choice('Create a new type (rarely needed)') do |_|
|
55
|
+
@@cli.ask('Write the new type to use', String) do |question|
|
56
|
+
question.verify_match = true
|
57
|
+
question.validate = STRING_VALUE_VALIDATOR
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def ask_for_scope
|
64
|
+
extract_types_and_scopes
|
65
|
+
@@cli.choose do |menu|
|
66
|
+
menu.flow = :columns_down
|
67
|
+
menu.prompt = 'Choose the scope of your commit '
|
68
|
+
@@custom_scopes.each do |scope|
|
69
|
+
menu.choice(scope.to_sym)
|
70
|
+
end
|
71
|
+
menu.choice('Create a new scope') do |_|
|
72
|
+
@@cli.ask('Write the new scope to use', String) do |question|
|
73
|
+
question.verify_match = true
|
74
|
+
question.validate = STRING_VALUE_VALIDATOR
|
75
|
+
end
|
76
|
+
end
|
77
|
+
menu.choice('None') { '' }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ask_for_breaking
|
82
|
+
@@cli.agree('Does the commit contain a breaking change? (yes/no) ') do |question|
|
83
|
+
question.default = false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def ask_for_summary
|
88
|
+
@@cli.ask('The summary of the commit:') do |question|
|
89
|
+
question.verify_match = true
|
90
|
+
question.validate = STRING_VALUE_VALIDATOR
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def ask_for_message
|
95
|
+
# This method needs a special implementation as the body message can span multiple lines.
|
96
|
+
@@cli.puts('The body of the commit (ends after 3 new lines):')
|
97
|
+
@@cli.input.gets(BODY_END_DELIMITER)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
FIRST_COMMIT = nil
|
103
|
+
|
104
|
+
# This method tries to optimize input parsing by performing multiple operations in one go.
|
105
|
+
# So its complexity is a bit higher as it needs to make multiple checks.
|
106
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
107
|
+
def extract_types_and_scopes
|
108
|
+
return unless @@custom_values_initialized.nil?
|
109
|
+
|
110
|
+
Common.with_commit_list_from(FIRST_COMMIT) do |commit_list|
|
111
|
+
commit_list.map do |element|
|
112
|
+
match = Common::CONVENTIONAL_COMMIT_REGEX.match(element)
|
113
|
+
next if match.nil?
|
114
|
+
|
115
|
+
type_already_added = DEFAULT_TYPES.include?(match[1].to_sym) || @@custom_types.include?(match[1])
|
116
|
+
@@custom_types.append(match[1]) unless type_already_added
|
117
|
+
@@custom_scopes.append(match[3]) unless match[3].nil? || @@custom_scopes.include?(match[3])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
@@custom_values_initialized = true
|
121
|
+
end
|
122
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
123
|
+
end
|
@@ -0,0 +1,71 @@
|
|
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 which handles change-related tasks.
|
21
|
+
module ChangeHandler
|
22
|
+
# Array with change types in ascending order of importance.
|
23
|
+
CHANGE_TYPE = %i[NONE PATCH MINOR MAJOR].freeze
|
24
|
+
|
25
|
+
@@major_trigger = 'is_breaking'
|
26
|
+
@@minor_trigger = "type == 'feat'"
|
27
|
+
@@patch_trigger = "type == 'fix'"
|
28
|
+
|
29
|
+
module_function
|
30
|
+
|
31
|
+
# In this block method arguments can be used by user.
|
32
|
+
# Also `eval` is needed to allow users to define their custom triggers.
|
33
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
34
|
+
# rubocop:disable Security/Eval
|
35
|
+
def triggers_major?(type, scope, is_breaking)
|
36
|
+
eval(@@major_trigger)
|
37
|
+
end
|
38
|
+
|
39
|
+
def triggers_minor?(type, scope)
|
40
|
+
eval(@@minor_trigger)
|
41
|
+
end
|
42
|
+
|
43
|
+
def triggers_patch?(type, scope)
|
44
|
+
eval(@@patch_trigger)
|
45
|
+
end
|
46
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
47
|
+
# rubocop:enable Security/Eval
|
48
|
+
|
49
|
+
# Open String class to inject method to convert a (commit) string into
|
50
|
+
# a change.
|
51
|
+
class ::String
|
52
|
+
# Convert the string (as a conventional commit string) into a change type.
|
53
|
+
def to_change
|
54
|
+
groups = Common::CONVENTIONAL_COMMIT_REGEX.match(self)
|
55
|
+
return :MAJOR if ChangeHandler.triggers_major?(groups[1], groups[3], !groups[4].nil?)
|
56
|
+
return :MINOR if ChangeHandler.triggers_minor?(groups[1], groups[2])
|
57
|
+
return :PATCH if ChangeHandler.triggers_patch?(groups[1], groups[2])
|
58
|
+
|
59
|
+
:NONE
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
public
|
64
|
+
|
65
|
+
def greatest_change_in(commit_list)
|
66
|
+
commit_list
|
67
|
+
.grep(Common::CONVENTIONAL_COMMIT_REGEX)
|
68
|
+
.map(&:to_change)
|
69
|
+
.max { |a, b| CHANGE_TYPE.index(a) <=> CHANGE_TYPE.index(b) }
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,230 @@
|
|
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 'get/subcommand/command'
|
21
|
+
require 'get/subcommand/describe/change'
|
22
|
+
require 'get/subcommand/describe/prerelease'
|
23
|
+
require 'get/subcommand/describe/metadata'
|
24
|
+
require 'get/subcommand/describe/docker/docker'
|
25
|
+
|
26
|
+
# Class length is disabled as most of its length is given by formatting.
|
27
|
+
# rubocop:disable Metrics/ClassLength
|
28
|
+
# Subcommand, it manages the description of the current git repository using semantic version.
|
29
|
+
class Describe < Command
|
30
|
+
def self.command
|
31
|
+
@@command ||= new
|
32
|
+
@@command
|
33
|
+
end
|
34
|
+
|
35
|
+
private_class_method :new
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
include ChangeHandler
|
40
|
+
include PrereleaseHandler
|
41
|
+
include MetadataHandler
|
42
|
+
|
43
|
+
@@command = nil
|
44
|
+
|
45
|
+
DEFAULT_RELEASE_VERSION = '0.1.0'
|
46
|
+
FULL_SEMANTIC_VERSION_REGEX = /
|
47
|
+
^((0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)) # Stable version, major, minor, patch
|
48
|
+
(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))? # prerelease
|
49
|
+
(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ # metadata
|
50
|
+
/x
|
51
|
+
|
52
|
+
@@usage = 'describe -h|(<subcommand> [<subcommand-options])'
|
53
|
+
@@description = 'Describe the current git repository with semantic version'
|
54
|
+
@@subcommands = {
|
55
|
+
docker: DescribeDocker.command,
|
56
|
+
}
|
57
|
+
# This block is Optimist configuration. It is as long as the number of options of the command.
|
58
|
+
# rubocop:disable Metrics/BlockLength
|
59
|
+
@@describe_parser = Optimist::Parser.new do
|
60
|
+
subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
|
61
|
+
usage @@usage
|
62
|
+
synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
|
63
|
+
Subcommands:
|
64
|
+
#{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
|
65
|
+
SUBCOMMANDS
|
66
|
+
opt :prerelease,
|
67
|
+
'Describe a prerelease rather than a release',
|
68
|
+
short: :none
|
69
|
+
opt :exclude_metadata,
|
70
|
+
'Do not include metadata in version.'
|
71
|
+
opt :metadata,
|
72
|
+
'Set which metadata to include in the string. ' \
|
73
|
+
'Multiple value can be specified by separating the with a comma \',\'.',
|
74
|
+
{ type: :string, default: 'sha' }
|
75
|
+
opt :major_trigger,
|
76
|
+
'Set the trigger for a major release. ' \
|
77
|
+
'This must be a valid Ruby expression. ' \
|
78
|
+
'In this expression the string values "type" and "scope" ' \
|
79
|
+
'and the boolean value "is_breaking" can be used.',
|
80
|
+
{ short: :none, type: :string, default: 'is_breaking' }
|
81
|
+
opt :minor_trigger,
|
82
|
+
'Set the trigger for a minor release. ' \
|
83
|
+
'This must be a valid Ruby expression. ' \
|
84
|
+
'In this expression the string values "type" and "scope" can be used.',
|
85
|
+
{ short: :none, type: :string, default: "type == 'feat'" }
|
86
|
+
opt :patch_trigger,
|
87
|
+
'Set the trigger for a patch release. ' \
|
88
|
+
'This must be a valid Ruby expression. ' \
|
89
|
+
'In this expression the string values "type" and "scope" can be used.',
|
90
|
+
{ short: :none, type: :string, default: "type == 'fix'" }
|
91
|
+
opt :prerelease_pattern,
|
92
|
+
'Set the pattern of the prerelease. This must contain the placeholder "(p)".',
|
93
|
+
{ short: :none, type: :string, default: 'dev(p)' }
|
94
|
+
opt :old_prerelease_pattern,
|
95
|
+
'Set the pattern of the old prerelease. It is useful for changing prerelease pattern.',
|
96
|
+
{ short: :none, type: :string, default: 'prerelease-pattern value' }
|
97
|
+
opt :diff,
|
98
|
+
'Print also the last version.'
|
99
|
+
opt :create_tag,
|
100
|
+
'Create a signed tag with the computed version.',
|
101
|
+
{ short: :none }
|
102
|
+
opt :tag_message,
|
103
|
+
'Add the given message to the tag. Requires "--create-tag".',
|
104
|
+
{ short: :none, type: :string }
|
105
|
+
educate_on_error
|
106
|
+
stop_on @@subcommands.keys.map(&:to_s)
|
107
|
+
end
|
108
|
+
# rubocop:enable Metrics/BlockLength
|
109
|
+
|
110
|
+
def initialize
|
111
|
+
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
|
+
end
|
116
|
+
set_options
|
117
|
+
|
118
|
+
if ARGV.length.positive?
|
119
|
+
subcommand = ARGV.shift.to_sym
|
120
|
+
if @@subcommands.include?(subcommand)
|
121
|
+
@@subcommands[subcommand].action.call(describe_current_commit)
|
122
|
+
else
|
123
|
+
# This error should not be disabled by -W0
|
124
|
+
# rubocop:disable Style/StderrPuts
|
125
|
+
$stderr.puts "Error: subcommand '#{subcommand}' unknown."
|
126
|
+
# rubocop:enable Style/StderrPuts
|
127
|
+
exit 1
|
128
|
+
end
|
129
|
+
else
|
130
|
+
puts describe_current_commit
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def set_options
|
136
|
+
@@major_trigger = @options[:major_trigger] if @options[:major_trigger_given]
|
137
|
+
@@minor_trigger = @options[:minor_trigger] if @options[:minor_trigger_given]
|
138
|
+
@@patch_trigger = @options[:patch_trigger] if @options[:patch_trigger_given]
|
139
|
+
@@old_prerelease_pattern = @options[:old_prerelease_pattern] if @options[:old_prerelease_pattern_given]
|
140
|
+
@@prerelease_pattern = @options[:prerelease_pattern] if @options[:prerelease_pattern_given]
|
141
|
+
end
|
142
|
+
|
143
|
+
def describe_current_commit
|
144
|
+
return last_version if Common.with_commit_list_from(last_version, &:empty?)
|
145
|
+
|
146
|
+
puts "Last version: #{last_version}" if @options[:diff]
|
147
|
+
|
148
|
+
current_commit_version = next_release
|
149
|
+
create_signed_tag(current_commit_version) if @options[:create_tag]
|
150
|
+
|
151
|
+
current_commit_version
|
152
|
+
end
|
153
|
+
|
154
|
+
def next_release
|
155
|
+
if @options[:prerelease]
|
156
|
+
prepare_prerelease_tag(last_release, last_version)
|
157
|
+
else
|
158
|
+
prepare_release_tag(last_release)
|
159
|
+
end + metadata
|
160
|
+
end
|
161
|
+
|
162
|
+
def prepare_release_tag(last_release)
|
163
|
+
updated_stable_version(last_release).to_s
|
164
|
+
end
|
165
|
+
|
166
|
+
def prepare_prerelease_tag(last_release, last_version)
|
167
|
+
new_stable_version = updated_stable_version(last_release)
|
168
|
+
base_version_match_data = FULL_SEMANTIC_VERSION_REGEX.match(last_version)
|
169
|
+
no_changes_from_last_release = base_version_match_data[1] == new_stable_version && base_version_match_data[5].nil?
|
170
|
+
Common.error 'No changes from last release' if no_changes_from_last_release
|
171
|
+
new_stable_version +
|
172
|
+
"-#{updated_prerelease(base_version_match_data[5], need_reset: base_version_match_data[1] != new_stable_version)}"
|
173
|
+
end
|
174
|
+
|
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
|
+
def updated_stable_version(stable_version)
|
186
|
+
return DEFAULT_RELEASE_VERSION if stable_version.nil?
|
187
|
+
|
188
|
+
greatest_change_from_stable_version = Common.with_commit_list_from(stable_version) do |commits_from_version|
|
189
|
+
greatest_change_in(commits_from_version)
|
190
|
+
end
|
191
|
+
split_version = stable_version.split('.')
|
192
|
+
case greatest_change_from_stable_version
|
193
|
+
when :MAJOR
|
194
|
+
"#{split_version[0].to_i + 1}.0.0"
|
195
|
+
when :MINOR
|
196
|
+
"#{split_version[0].to_i}.#{split_version[1].to_i + 1}.0"
|
197
|
+
when :PATCH
|
198
|
+
"#{split_version[0].to_i}.#{split_version[1].to_i}.#{split_version[2].to_i + 1}"
|
199
|
+
else
|
200
|
+
"#{split_version[0].to_i}.#{split_version[1].to_i}.#{split_version[2].to_i}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Return the updated prerelease number
|
205
|
+
def updated_prerelease(prerelease = nil, need_reset: false)
|
206
|
+
compute_prerelease(prerelease, need_reset: prerelease.nil? || need_reset)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Compute the metadata string
|
210
|
+
def metadata
|
211
|
+
return '' if @options[:exclude_metadata] || @options[:metadata].empty?
|
212
|
+
|
213
|
+
"+#{compute_metadata(@options[:metadata])}"
|
214
|
+
end
|
215
|
+
|
216
|
+
def create_signed_tag(computed_version)
|
217
|
+
system(
|
218
|
+
'git tag -s ' \
|
219
|
+
"#{
|
220
|
+
if @options[:tag_message_given]
|
221
|
+
"-m #{@options[:tag_message]}"
|
222
|
+
else
|
223
|
+
''
|
224
|
+
end
|
225
|
+
} " \
|
226
|
+
"'#{computed_version}'"
|
227
|
+
)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,116 @@
|
|
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 'get/subcommand/command'
|
21
|
+
|
22
|
+
# Class length is disabled as most of its length is given by formatting.
|
23
|
+
# rubocop:disable Metrics/ClassLength
|
24
|
+
# Subcommand, it manages the description of the current git repository using semantic version.
|
25
|
+
class DescribeDocker < Command
|
26
|
+
def self.command
|
27
|
+
@@command ||= new
|
28
|
+
@@command
|
29
|
+
end
|
30
|
+
|
31
|
+
private_class_method :new
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
INCREMENTAL_VERSION_PATTERN = /(((\d+)\.\d+)\.\d+)/
|
36
|
+
|
37
|
+
@@command = nil
|
38
|
+
|
39
|
+
@@usage = 'describe docker -h|(<subcommand> [<subcommand-options])'
|
40
|
+
@@description = 'Describe the current git repository with a list of version for docker'
|
41
|
+
@@subcommands = {}
|
42
|
+
# This block is Optimist configuration. It is as long as the number of options of the command.
|
43
|
+
# rubocop:disable Metrics/BlockLength
|
44
|
+
@@describe_parser = Optimist::Parser.new do
|
45
|
+
subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
|
46
|
+
usage @@usage
|
47
|
+
synopsis <<~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
|
+
opt :separator,
|
52
|
+
'Use the given value as separator for versions',
|
53
|
+
{ type: :string, default: '\n' }
|
54
|
+
opt :not_latest,
|
55
|
+
'Do not include "latest" in the version list.',
|
56
|
+
short: :none
|
57
|
+
opt :substitute_plus,
|
58
|
+
'Set which character will be used in place of "+".',
|
59
|
+
{ type: :string, short: :none }
|
60
|
+
educate_on_error
|
61
|
+
stop_on @@subcommands.keys.map(&:to_s)
|
62
|
+
end
|
63
|
+
# rubocop:enable Metrics/BlockLength
|
64
|
+
|
65
|
+
def initialize
|
66
|
+
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
|
70
|
+
end
|
71
|
+
set_options
|
72
|
+
|
73
|
+
puts version_list_from(version).join(@@separator)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_options
|
78
|
+
@@separator = if @options[:separator_given]
|
79
|
+
@options[:separator]
|
80
|
+
else
|
81
|
+
"\n"
|
82
|
+
end
|
83
|
+
@@not_latest = @options[:not_latest]
|
84
|
+
@@plus_substitution = if @options[:substitute_plus_given]
|
85
|
+
@options[:substitute_plus]
|
86
|
+
else
|
87
|
+
'+'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def version_list_from(full_version)
|
92
|
+
[
|
93
|
+
full_version.sub('+', @@plus_substitution),
|
94
|
+
reduced_versions(full_version),
|
95
|
+
latest
|
96
|
+
]
|
97
|
+
end
|
98
|
+
|
99
|
+
def reduced_versions(full_version)
|
100
|
+
base_version = full_version.partition('+')[0]
|
101
|
+
if base_version.include?('-')
|
102
|
+
base_version
|
103
|
+
else
|
104
|
+
INCREMENTAL_VERSION_PATTERN.match(base_version).captures
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def latest
|
109
|
+
if @options[:not_latest]
|
110
|
+
[]
|
111
|
+
else
|
112
|
+
['latest']
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,53 @@
|
|
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 methods to handle tag metadata.
|
21
|
+
#
|
22
|
+
# To add a new metadata type, create a new method and link it to a symbol.
|
23
|
+
module MetadataHandler
|
24
|
+
@@metadata_computers = {}
|
25
|
+
|
26
|
+
module_function
|
27
|
+
|
28
|
+
def last_commit_sha
|
29
|
+
`git --no-pager log -n 1 --pretty=%h`.strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def current_date
|
33
|
+
Time.now.strftime('%0Y%0m%0d')
|
34
|
+
end
|
35
|
+
|
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
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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 methods for managing prerelease updates.
|
21
|
+
module PrereleaseHandler
|
22
|
+
FIRST_PRERELEASE = 1
|
23
|
+
DEFAULT_PRERELEASE_STRING = 'dev'
|
24
|
+
PRERELEASE_PLACEHOLDER = '(p)'
|
25
|
+
|
26
|
+
@@prerelease_pattern = "#{DEFAULT_PRERELEASE_STRING}#{PRERELEASE_PLACEHOLDER}"
|
27
|
+
@@old_prerelease_pattern = proc { @@prerelease_pattern }
|
28
|
+
|
29
|
+
module_function
|
30
|
+
|
31
|
+
def extract_prerelease_number(current_prerelease)
|
32
|
+
actual_old_prerelease_pattern =
|
33
|
+
if @@old_prerelease_pattern.respond_to?('call')
|
34
|
+
@@old_prerelease_pattern.call
|
35
|
+
else
|
36
|
+
@@old_prerelease_pattern
|
37
|
+
end
|
38
|
+
Common.error "The given old pattern does not contains the placeholder '(p)'" unless
|
39
|
+
actual_old_prerelease_pattern.include?(PRERELEASE_PLACEHOLDER)
|
40
|
+
old_prerelease_regex = actual_old_prerelease_pattern.sub(PRERELEASE_PLACEHOLDER, '(\\d+)')
|
41
|
+
begin
|
42
|
+
Regexp.new(old_prerelease_regex).match(current_prerelease)[1].to_i
|
43
|
+
rescue NoMethodError
|
44
|
+
Common.error "The given old prerelease pattern '#{actual_old_prerelease_pattern}' " \
|
45
|
+
"does not match the analyzed prerelease: '#{current_prerelease}'."
|
46
|
+
end
|
47
|
+
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
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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/common'
|
22
|
+
require 'get/subcommand/command'
|
23
|
+
|
24
|
+
# Class length is disabled as most of its length is given by formatting.
|
25
|
+
# rubocop:disable Metrics/ClassLength
|
26
|
+
# Subcommand, it allow to create a new repository and add an initial, empty commit to it.
|
27
|
+
class Init < Command
|
28
|
+
def self.command
|
29
|
+
@@command ||= new
|
30
|
+
@@command
|
31
|
+
end
|
32
|
+
|
33
|
+
private_class_method :new
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
include PromptHandler
|
38
|
+
|
39
|
+
@@command = nil
|
40
|
+
|
41
|
+
@@usage = 'init -h|(<subcommand> [<subcommand-options])'
|
42
|
+
@@description = 'Initialize a new git repository with an initial empty commit'
|
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
|
+
@@commit_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 :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
|
+
def initialize
|
61
|
+
super(@@usage, @@description) do
|
62
|
+
@options = Common.with_subcommand_exception_handling @@commit_parser do
|
63
|
+
@@commit_parser.parse
|
64
|
+
end
|
65
|
+
Common.error 'The current directory is already a git repository' if Common.in_git_repo?
|
66
|
+
|
67
|
+
init_repository
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def init_repository
|
72
|
+
`git init`
|
73
|
+
Common.error 'Failed to init the repository' if $CHILD_STATUS.exitstatus.positive?
|
74
|
+
|
75
|
+
create_first_commit unless @options[:empty]
|
76
|
+
|
77
|
+
puts 'Git repository initialized'
|
78
|
+
end
|
79
|
+
|
80
|
+
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?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
# rubocop:enable Metrics/ClassLength
|
data/lib/get/version.rb
ADDED
@@ -0,0 +1,22 @@
|
|
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 Get
|
21
|
+
VERSION = '0.4.1'
|
22
|
+
end
|
data/lib/get.rb
ADDED
@@ -0,0 +1,67 @@
|
|
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 'optimist'
|
21
|
+
|
22
|
+
require 'get/subcommand/describe/describe'
|
23
|
+
require 'get/subcommand/commit/commit'
|
24
|
+
require 'get/subcommand/init/init'
|
25
|
+
require 'get/version'
|
26
|
+
require 'get/common'
|
27
|
+
|
28
|
+
# Entrypoint of Get
|
29
|
+
module Get
|
30
|
+
class Error < StandardError; end
|
31
|
+
|
32
|
+
@@subcommands = {
|
33
|
+
describe: Describe.command,
|
34
|
+
commit: Commit.command,
|
35
|
+
init: Init.command,
|
36
|
+
}
|
37
|
+
@@option_parser = Optimist::Parser.new do
|
38
|
+
subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
|
39
|
+
usage '-h|-v|(<subcommand> [<subcommand-options])'
|
40
|
+
synopsis <<~SUBCOMMANDS unless @@subcommands.empty?
|
41
|
+
Subcommands:
|
42
|
+
#{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
|
43
|
+
SUBCOMMANDS
|
44
|
+
version "Get version: #{Get::VERSION}"
|
45
|
+
educate_on_error
|
46
|
+
stop_on @@subcommands.keys.map(&:to_s)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.main
|
50
|
+
@options = Optimist.with_standard_exception_handling(@@option_parser) do
|
51
|
+
@@option_parser.parse
|
52
|
+
end
|
53
|
+
error 'No command or option specified' if ARGV.empty?
|
54
|
+
command = ARGV.shift.to_sym
|
55
|
+
if @@subcommands.include?(command)
|
56
|
+
@@subcommands[command].action.call
|
57
|
+
else
|
58
|
+
error "Unknown subcommand '#{command}'"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.error(message)
|
63
|
+
Common.error message do
|
64
|
+
@@option_parser.educate
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: git_toolbox
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Speranza
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: optimist
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.0.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: highline
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 2.0.3
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 2.0.3
|
47
|
+
description:
|
48
|
+
email:
|
49
|
+
- alex.speranza@studio.unibo.it
|
50
|
+
executables:
|
51
|
+
- get
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- bin/get
|
56
|
+
- bin/setup
|
57
|
+
- lib/get.rb
|
58
|
+
- lib/get/common.rb
|
59
|
+
- lib/get/subcommand/command.rb
|
60
|
+
- lib/get/subcommand/commit/commit.rb
|
61
|
+
- lib/get/subcommand/commit/prompt.rb
|
62
|
+
- lib/get/subcommand/describe/change.rb
|
63
|
+
- lib/get/subcommand/describe/describe.rb
|
64
|
+
- lib/get/subcommand/describe/docker/docker.rb
|
65
|
+
- lib/get/subcommand/describe/metadata.rb
|
66
|
+
- lib/get/subcommand/describe/prerelease.rb
|
67
|
+
- lib/get/subcommand/init/init.rb
|
68
|
+
- lib/get/version.rb
|
69
|
+
homepage: https://github.com/asperan/get
|
70
|
+
licenses:
|
71
|
+
- LGPL-3.0-or-later
|
72
|
+
metadata:
|
73
|
+
homepage_uri: https://github.com/asperan/get
|
74
|
+
source_code_uri: https://github.com/asperan/get
|
75
|
+
allowed_push_host: https://rubygems.org
|
76
|
+
rubygems_mfa_required: 'true'
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 3.1.0
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubygems_version: 3.3.7
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Git Enhancement Toolbox
|
96
|
+
test_files: []
|