milestoner 11.2.0 → 12.0.0

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