milestoner 11.2.0 → 12.0.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.
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module CLI
5
+ module Parsers
6
+ # Handles parsing of Command Line Interface (CLI) security options.
7
+ class Security
8
+ def self.call configuration = Configuration::Loader.call, client: CLIENT
9
+ new(configuration, client: client).call
10
+ end
11
+
12
+ def initialize configuration = Configuration::Loader.call, client: CLIENT
13
+ @configuration = configuration
14
+ @client = client
15
+ end
16
+
17
+ def call arguments = []
18
+ client.separator "\nSECURITY OPTIONS:\n"
19
+ add_sign
20
+ arguments.empty? ? arguments : client.parse!(arguments)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :configuration, :client
26
+
27
+ def add_sign
28
+ client.on(
29
+ "--[no-]sign",
30
+ %(Sign with GPG key. Default: #{configuration.git_tag_sign}.)
31
+ ) do |value|
32
+ compute_git_tag_sign value
33
+ end
34
+ end
35
+
36
+ def compute_git_tag_sign value
37
+ truth_table = [true, false].repeated_permutation(2).to_a
38
+
39
+ case truth_table.index [value, configuration.git_tag_sign]
40
+ when 0..1 then configuration.git_tag_sign = true
41
+ when 2..3 then configuration.git_tag_sign = false
42
+ else fail Error, "--sign must be a boolean. Check gem configuration."
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module CLI
5
+ # The main Command Line Interface (CLI) object.
6
+ class Shell
7
+ ACTIONS = {
8
+ config: Actions::Config.new,
9
+ publish: Actions::Publish.new,
10
+ status: Actions::Status.new
11
+ }.freeze
12
+
13
+ def initialize parser: Parsers::Assembler.new, actions: ACTIONS, container: Container
14
+ @parser = parser
15
+ @actions = actions
16
+ @container = container
17
+ end
18
+
19
+ def call arguments = []
20
+ perform parser.call(arguments)
21
+ rescue OptionParser::ParseError, Error => error
22
+ logger.error error.message
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :parser, :actions, :container
28
+
29
+ def perform configuration
30
+ case configuration
31
+ in action_config: Symbol => action then config action
32
+ in action_publish: true then publish configuration
33
+ in action_status: true then status
34
+ in action_version: String => version then logger.info version
35
+ in action_help: then usage
36
+ else usage
37
+ end
38
+ end
39
+
40
+ def config(action) = actions.fetch(__method__).call(action)
41
+
42
+ def publish(configuration) = actions.fetch(__method__).call(configuration)
43
+
44
+ def status = actions.fetch(__method__).call
45
+
46
+ def usage = logger.unknown { parser.to_s }
47
+
48
+ def logger = container[__method__]
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "versionaire"
4
+
5
+ module Milestoner
6
+ module Commits
7
+ # Retrieves and categorizes Git repository commit tagged or untagged history.
8
+ class Categorizer
9
+ def initialize expression: Regexp, container: Container
10
+ @expression = expression
11
+ @container = container
12
+ end
13
+
14
+ def call configuration = CLI::Configuration::Loader.call
15
+ prefixes = configuration.git_commit_prefixes
16
+
17
+ prefixes.reduce({}) { |group, prefix| group.merge prefix => [] }
18
+ .merge("Unknown" => [])
19
+ .then { |groups| group_by_prefix prefixes, groups }
20
+ .each_value { |commits| commits.sort_by!(&:subject) }
21
+ .values
22
+ .flatten
23
+ .uniq(&:subject)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :expression, :container
29
+
30
+ def group_by_prefix prefixes, groups
31
+ computed_commits.each.with_object groups do |commit, collection|
32
+ prefix = commit.subject[subject_pattern(prefixes)]
33
+ key = collection.key?(prefix) ? prefix : "Unknown"
34
+ collection[key] << commit
35
+ end
36
+ end
37
+
38
+ def subject_pattern prefixes
39
+ prefixes.empty? ? expression.new(//) : expression.union(prefixes)
40
+ end
41
+
42
+ def computed_commits = repository.tagged? ? tagged_commits : saved_commits
43
+
44
+ def tagged_commits = repository.commits("#{repository.tag_last}..HEAD")
45
+
46
+ def saved_commits = repository.commits
47
+
48
+ def repository = container[__method__]
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-container"
4
+ require "git_plus"
5
+ require "logger"
6
+ require "pastel"
7
+
8
+ module Milestoner
9
+ # Provides a global gem container for injection into other objects.
10
+ module Container
11
+ extend Dry::Container::Mixin
12
+
13
+ register(:configuration) { CLI::Configuration::Loader.call }
14
+ register(:colorizer) { Pastel.new enabled: $stdout.tty? }
15
+ register(:kernel) { Kernel }
16
+
17
+ register :log_colors do
18
+ {
19
+ "DEBUG" => self[:colorizer].white.detach,
20
+ "INFO" => self[:colorizer].green.detach,
21
+ "WARN" => self[:colorizer].yellow.detach,
22
+ "ERROR" => self[:colorizer].red.detach,
23
+ "FATAL" => self[:colorizer].white.bold.on_red.detach,
24
+ "ANY" => self[:colorizer].white.bold.detach
25
+ }
26
+ end
27
+
28
+ register :logger do
29
+ Logger.new $stdout,
30
+ level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
31
+ formatter: (
32
+ lambda do |severity, _at, _name, message|
33
+ self[:log_colors][severity].call "#{message}\n"
34
+ end
35
+ )
36
+ end
37
+
38
+ register(:repository) { GitPlus::Repository.new }
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ # The base class for all Milestoner related errors.
5
+ class Error < StandardError
6
+ end
7
+ end
@@ -5,7 +5,8 @@ module Milestoner
5
5
  module Identity
6
6
  NAME = "milestoner"
7
7
  LABEL = "Milestoner"
8
- VERSION = "11.2.0"
8
+ VERSION = "12.0.0"
9
9
  VERSION_LABEL = "#{LABEL} #{VERSION}"
10
+ SUMMARY = "A command line interface for crafting Git repository tags."
10
11
  end
11
12
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "git_plus"
5
+
6
+ module Milestoner
7
+ module Presenters
8
+ # Wraps the Git Kit Commit for presentation purposes.
9
+ class Commit
10
+ extend Forwardable
11
+
12
+ delegate [*GitPlus::Commit.members, :fixup?, :squash?] => :record
13
+
14
+ def initialize record, container: Container
15
+ @record = record
16
+ @container = container
17
+ end
18
+
19
+ def line_item(delimiter: " - ") = "#{bullet}#{subject}#{delimiter}#{author_name}"
20
+
21
+ private
22
+
23
+ attr_reader :record, :container
24
+
25
+ def bullet
26
+ case container[:configuration].documentation_format
27
+ when "md" then "- "
28
+ when "adoc" then "* "
29
+ else ""
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "versionaire"
4
+
5
+ module Milestoner
6
+ module Tags
7
+ # Handles the creation of project repository tags.
8
+ class Creator
9
+ using Versionaire::Cast
10
+
11
+ def initialize categorizer: Commits::Categorizer.new,
12
+ presenter: Presenters::Commit,
13
+ container: Container
14
+
15
+ @categorizer = categorizer
16
+ @presenter = presenter
17
+ @container = container
18
+ end
19
+
20
+ def call configuration = CLI::Configuration::Loader.call
21
+ return false if local? configuration
22
+ fail Error, "Unable to tag without commits." if categorizer.call.empty?
23
+
24
+ sign configuration
25
+ rescue Versionaire::Errors::Cast, GitPlus::Errors::Base => error
26
+ raise Error, error.message
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :categorizer, :presenter, :container
32
+
33
+ def local? configuration
34
+ version = Version configuration.git_tag_version
35
+
36
+ if repository.tag_local? version
37
+ logger.warn "Local tag exists: #{version}. Skipped."
38
+ true
39
+ else
40
+ false
41
+ end
42
+ end
43
+
44
+ def sign configuration
45
+ version = configuration.git_tag_version
46
+ content = message configuration
47
+
48
+ if configuration.git_tag_sign
49
+ repository.tag_sign version, content
50
+ else
51
+ repository.tag_unsign version, content
52
+ end
53
+
54
+ logger.debug "Local tag created: #{version}."
55
+ end
56
+
57
+ def message configuration
58
+ categorizer.call(configuration)
59
+ .map { |record| presenter.new(record).line_item }
60
+ .then do |line_items|
61
+ %(Version #{configuration.git_tag_version}\n\n#{line_items.join "\n"}\n\n)
62
+ end
63
+ end
64
+
65
+ def repository = container[__method__]
66
+
67
+ def logger = container[__method__]
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module Tags
5
+ # Handles the tagging and pushing of a tag to a remote repository.
6
+ class Publisher
7
+ def initialize tagger: Tags::Creator.new, pusher: Tags::Pusher.new, container: Container
8
+ @tagger = tagger
9
+ @pusher = pusher
10
+ @container = container
11
+ end
12
+
13
+ def call configuration = CLI::Configuration::Loader.call
14
+ tagger.call configuration
15
+ pusher.call configuration
16
+ logger.info { "Published: #{configuration.git_tag_version}!" }
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :tagger, :pusher, :container
22
+
23
+ def logger = container[__method__]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "versionaire"
4
+
5
+ module Milestoner
6
+ module Tags
7
+ # Handles publishing of tags to a remote repository.
8
+ class Pusher
9
+ using Versionaire::Cast
10
+
11
+ def initialize container: Container
12
+ @container = container
13
+ end
14
+
15
+ def call configuration = CLI::Configuration::Loader.call
16
+ version = Version configuration.git_tag_version
17
+
18
+ fail Error, "Remote repository not configured." unless repository.config_origin?
19
+ fail Error, "Remote tag exists: #{version}." if repository.tag_remote? version
20
+ fail Error, "Tags could not be pushed to remote repository." unless push
21
+
22
+ logger.debug "Local tag pushed: #{version}."
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :container
28
+
29
+ def push
30
+ repository.tag_push.then do |_stdout, stderr, status|
31
+ status.success? && stderr.match?(/[new tag]/)
32
+ end
33
+ end
34
+
35
+ def repository = container[__method__]
36
+
37
+ def logger = container[__method__]
38
+ end
39
+ end
40
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: milestoner
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.2.0
4
+ version: 12.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -28,8 +28,22 @@ cert_chain:
28
28
  lkHilIrX69jq8wMPpBhlaw2mRmeSL50Wv5u6xVBvOHhXFSP1crXM95vfLhLyRYod
29
29
  W2A=
30
30
  -----END CERTIFICATE-----
31
- date: 2021-04-04 00:00:00.000000000 Z
31
+ date: 2021-06-04 00:00:00.000000000 Z
32
32
  dependencies:
33
+ - !ruby/object:Gem::Dependency
34
+ name: dry-container
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.7.2
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.7.2
33
47
  - !ruby/object:Gem::Dependency
34
48
  name: git_plus
35
49
  requirement: !ruby/object:Gem::Requirement
@@ -44,6 +58,20 @@ dependencies:
44
58
  - - "~>"
45
59
  - !ruby/object:Gem::Version
46
60
  version: '0.4'
61
+ - !ruby/object:Gem::Dependency
62
+ name: pastel
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.8'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.8'
47
75
  - !ruby/object:Gem::Dependency
48
76
  name: refinements
49
77
  requirement: !ruby/object:Gem::Requirement
@@ -73,33 +101,33 @@ dependencies:
73
101
  - !ruby/object:Gem::Version
74
102
  version: '7.0'
75
103
  - !ruby/object:Gem::Dependency
76
- name: thor
104
+ name: versionaire
77
105
  requirement: !ruby/object:Gem::Requirement
78
106
  requirements:
79
107
  - - "~>"
80
108
  - !ruby/object:Gem::Version
81
- version: '0.20'
109
+ version: '9.2'
82
110
  type: :runtime
83
111
  prerelease: false
84
112
  version_requirements: !ruby/object:Gem::Requirement
85
113
  requirements:
86
114
  - - "~>"
87
115
  - !ruby/object:Gem::Version
88
- version: '0.20'
116
+ version: '9.2'
89
117
  - !ruby/object:Gem::Dependency
90
- name: versionaire
118
+ name: zeitwerk
91
119
  requirement: !ruby/object:Gem::Requirement
92
120
  requirements:
93
121
  - - "~>"
94
122
  - !ruby/object:Gem::Version
95
- version: '9.0'
123
+ version: '2.4'
96
124
  type: :runtime
97
125
  prerelease: false
98
126
  version_requirements: !ruby/object:Gem::Requirement
99
127
  requirements:
100
128
  - - "~>"
101
129
  - !ruby/object:Gem::Version
102
- version: '9.0'
130
+ version: '2.4'
103
131
  description:
104
132
  email:
105
133
  - brooke@alchemists.io
@@ -114,15 +142,25 @@ files:
114
142
  - README.adoc
115
143
  - bin/milestoner
116
144
  - lib/milestoner.rb
117
- - lib/milestoner/cli.rb
118
- - lib/milestoner/commit.rb
119
- - lib/milestoner/errors/base.rb
120
- - lib/milestoner/errors/duplicate_tag.rb
121
- - lib/milestoner/errors/git.rb
145
+ - lib/milestoner/cli/actions/config.rb
146
+ - lib/milestoner/cli/actions/publish.rb
147
+ - lib/milestoner/cli/actions/status.rb
148
+ - lib/milestoner/cli/configuration/content.rb
149
+ - lib/milestoner/cli/configuration/defaults.yml
150
+ - lib/milestoner/cli/configuration/loader.rb
151
+ - lib/milestoner/cli/parsers.rb
152
+ - lib/milestoner/cli/parsers/assembler.rb
153
+ - lib/milestoner/cli/parsers/core.rb
154
+ - lib/milestoner/cli/parsers/security.rb
155
+ - lib/milestoner/cli/shell.rb
156
+ - lib/milestoner/commits/categorizer.rb
157
+ - lib/milestoner/container.rb
158
+ - lib/milestoner/error.rb
122
159
  - lib/milestoner/identity.rb
123
- - lib/milestoner/publisher.rb
124
- - lib/milestoner/pusher.rb
125
- - lib/milestoner/tagger.rb
160
+ - lib/milestoner/presenters/commit.rb
161
+ - lib/milestoner/tags/creator.rb
162
+ - lib/milestoner/tags/publisher.rb
163
+ - lib/milestoner/tags/pusher.rb
126
164
  homepage: https://www.alchemists.io/projects/milestoner
127
165
  licenses:
128
166
  - Apache-2.0
@@ -146,8 +184,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
184
  - !ruby/object:Gem::Version
147
185
  version: '0'
148
186
  requirements: []
149
- rubygems_version: 3.2.15
187
+ rubygems_version: 3.2.19
150
188
  signing_key:
151
189
  specification_version: 4
152
- summary: A command line interface for crafting Git repository milestones.
190
+ summary: A command line interface for crafting Git repository tags.
153
191
  test_files: []