milestoner 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a383b5ca439d263af13d3a99df04900c5cf9af6d
4
- data.tar.gz: 7bbeabe417fa74b1ea2de76f98ca4ad5cbc02468
3
+ metadata.gz: 2f8ac8304b27843a087593b12cf0ed45945701bc
4
+ data.tar.gz: 2fc0db25d0afba6db7131dc340d3404b478e19a2
5
5
  SHA512:
6
- metadata.gz: 203b4857ce7f978e63560a760ed56369853cad84405bfadb8b83e0b1f459023f495a44a0bd7b196ae4a17c14da44ab17cd6f6fe0b156b7d72047e442a28db62e
7
- data.tar.gz: 87add04071261e77f5964cf8202a914891228af308b3f90bf65c66ea94f585c3671b903a2a488dd6c1d6e92eb63e9003ec303dcaeb05c177625ac75637ac42b9
6
+ metadata.gz: 1eebacca1ca1eb38c801ffbd1ee35ad749e07a6f64394faa381385a325f83a11f6ad2fbdb533b313109e267775ce9fca4057f6eebf076929c205ead896cd57b4
7
+ data.tar.gz: bf566fa3250f24929aa519d647f64b2b77e47e0c6c235a79d70e68060d0bbf61d562bf91454988f187834d8e43c70cf9b77081240b1e226d7e12bdcad74bde83
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -16,6 +16,8 @@ A tool for automating and releasing Git repository milestones.
16
16
  - [Requirements](#requirements)
17
17
  - [Setup](#setup)
18
18
  - [Usage](#usage)
19
+ - [Command Line Interface (CLI)](#command-line-interface-cli)
20
+ - [Customization](#customization)
19
21
  - [Tests](#tests)
20
22
  - [Versioning](#versioning)
21
23
  - [Code of Conduct](#code-of-conduct)
@@ -28,15 +30,19 @@ A tool for automating and releasing Git repository milestones.
28
30
 
29
31
  # Features
30
32
 
31
- - Provides [Semantic Versioning](http://semver.org) for Git repositories in the form of
32
- `v<major>.<minor>.</maintenance>` tags. Example: `v0.1.0`.
33
- - Provides optional security for signing tags using GPG signing key.
34
- - Automatically includes commits since last tag (or HEAD if no tags exist) within each tag message.
35
- - Groups commit messages by consistent prefixes in order defined: "Fixed", "Added", "Updated", "Removed", "Refactored".
36
- Otherwise, they are alphabetically sorted.
37
- - Alphabetically sorts commit messages within each prefix group.
38
- - Ensures duplicate commit messages are removed (if any).
39
- - Sanitizes commit messages by removing extra spaces and `[ci skip]` text.
33
+ - Ensures [Semantic Versioning](http://semver.org) of Git repository tags:
34
+ - Format: `v<major>.<minor>.</maintenance>`.
35
+ - Example: `v0.1.0`.
36
+ - Ensures Git commits since last tag (or since initialization of repository if no tags exist) are included within each
37
+ Git tag message.
38
+ - Ensures Git commit messages are grouped by prefix, in order defined, for each Git tag message (prefixes can be
39
+ customized): "Fixed", "Added", "Updated", "Removed", "Refactored".
40
+ - Ensures Git commit messages are alphabetically sorted within each Git tag message.
41
+ - Ensures duplicate Git commit messages are removed (if any) within each Git tag message.
42
+ - Ensures Git commit messages are sanitized by removing extra spaces and `[ci skip]` text within each Git tag message.
43
+ - Provides optional security for signing Git tags with [GnuPG](https://www.gnupg.org) signing key.
44
+
45
+ [![asciicast](https://asciinema.org/a/ecyzvq1uvy3mn6mrknd36f5b7.png)](https://asciinema.org/a/ecyzvq1uvy3mn6mrknd36f5b7)
40
46
 
41
47
  # Requirements
42
48
 
@@ -48,7 +54,7 @@ A tool for automating and releasing Git repository milestones.
48
54
 
49
55
  For a secure install, type the following (recommended):
50
56
 
51
- gem cert --add <(curl -Ls http://www.my-website.com/gem-public.pem)
57
+ gem cert --add <(curl -Ls https://www.alchemists.io/gem-public.pem)
52
58
  gem install milestoner --trust-policy MediumSecurity
53
59
 
54
60
  NOTE: A HighSecurity trust policy would be best but MediumSecurity enables signed gem verification while
@@ -60,20 +66,23 @@ For an insecure install, type the following (not recommended):
60
66
 
61
67
  # Usage
62
68
 
63
- From the command line, type: milestoner help
69
+ ## Command Line Interface (CLI)
70
+
71
+ From the command line, type: `milestoner help`
64
72
 
65
- milestoner -P, [--publish=PUBLISH] # Tag and push to remote repository.
66
- milestoner -c, [--commits] # Show tag message commits for next milestone.
73
+ milestoner -P, [--publish=PUBLISH] # Tag and push milestone to remote repository.
74
+ milestoner -c, [--commits] # Show commits for next milestone.
75
+ milestoner -e, [--edit] # Edit Milestoner settings in default editor.
67
76
  milestoner -h, [--help=HELP] # Show this message or get help for a command.
68
- milestoner -p, [--push] # Push tags to remote repository.
77
+ milestoner -p, [--push] # Push local tag to remote repository.
69
78
  milestoner -t, [--tag=TAG] # Tag local repository with new version.
70
- milestoner -v, [--version] # Show version.
79
+ milestoner -v, [--version] # Show Milestoner version.
71
80
 
72
- For tag options, type: milestoner help tag
81
+ For tag options, type: `milestoner help tag`
73
82
 
74
83
  -s, [--sign], [--no-sign] # Sign tag with GPG key.
75
84
 
76
- For publish options, type: milestoner help publish
85
+ For publish options, type: `milestoner help publish`
77
86
 
78
87
  -s, [--sign], [--no-sign] # Sign tag with GPG key.
79
88
 
@@ -81,6 +90,51 @@ When using Milestoner, the `--publish` command is intended to be the only comman
81
90
  release as it handles all of the steps necessary for tagging and pushing a new release. Should individual steps
82
91
  be needed, then the `--tag` and `--push` options are available.
83
92
 
93
+ ## Customization
94
+
95
+ Should the default settings not be desired, customization is allowed via the `.milestonerrc` file. The `.milestonerrc`
96
+ can be created at a global and/or local level. Example:
97
+
98
+ - Global: `~/.milestonerrc`
99
+ - Local: `<project repository root>/.milestonerrc`
100
+
101
+ Order of precedence for any setting is resolved as follows (with the first taking top priority):
102
+
103
+ 0. CLI option. Example: A version passed to either the `--tag` or `--publish` commands.
104
+ 0. Local project repository `.milestonerrc`.
105
+ 0. Global `~/.milestonerrc`.
106
+
107
+ Any setting provided to the CLI during runtime would trump a local/global setting and a local setting would trump a
108
+ global setting. The global setting is the weakest of all but great for situations where custom settings should be
109
+ applied to *all* projects. It is important to note that local settings completely trump any global settings -- there is
110
+ no inheritance when local *and* global settings exist at the same time.
111
+
112
+ The `.milestonerrc` uses the following default settings:
113
+
114
+ ---
115
+ :version: ""
116
+ :git_commit_prefixes:
117
+ - Fixed
118
+ - Added
119
+ - Updated
120
+ - Removed
121
+ - Refactored
122
+ :git_tag_sign: false
123
+
124
+ Each `.milestonerrc` setting can be configured as follows:
125
+
126
+ - `version`: By default it is left blank so you'll be prompted by the CLI. However, it can be nice to bump this version
127
+ prior to each release and have the current version checked into source code at a per project level. The version, if
128
+ set, will be used to tag the repository. If the version is a duplicate, an error will be thrown. When supplying a
129
+ version, use this format: `<major>.<minor>.</maintenance>`. Example: `0.1.0`.
130
+ - `git_commit_prefixes`: Should the default prefixes not be desired, you can define Git commit prefixes that match your
131
+ style. *NOTE: Prefix order is important with the first prefix defined taking precedence over the second and so forth.*
132
+ Special characters are allowed for prefixes but should be enclosed in quotes if used. To disable prefix usage
133
+ completely, use an empty array. Example: `:git_commit_prefixes: []`.
134
+ - `git_tag_sign`: Defaults to `false` but can be enabled by setting to `true`. When enabled, a Git tag will require GPG
135
+ signing for enhanced security and include a signed signature as part of the Git tag. This is useful for public
136
+ milestones where the author of a milestone can be verified to ensure milestone integrity/security.
137
+
84
138
  # Tests
85
139
 
86
140
  To test, run:
data/bin/milestoner CHANGED
@@ -4,5 +4,5 @@ require "milestoner"
4
4
  require "milestoner/cli"
5
5
  require "milestoner/identity"
6
6
 
7
- Process.setproctitle Milestoner::Identity.label_version
7
+ Process.setproctitle Milestoner::Identity.version_label
8
8
  Milestoner::CLI.start
data/lib/milestoner.rb CHANGED
@@ -1,4 +1,10 @@
1
+ require "milestoner/aids/git"
2
+ require "milestoner/errors/base"
3
+ require "milestoner/errors/configuration"
4
+ require "milestoner/errors/duplicate_tag"
5
+ require "milestoner/errors/git"
6
+ require "milestoner/errors/version"
7
+ require "milestoner/configuration"
1
8
  require "milestoner/identity"
2
- require "milestoner/exceptions"
3
9
  require "milestoner/pusher"
4
10
  require "milestoner/tagger"
@@ -0,0 +1,10 @@
1
+ module Milestoner
2
+ module Aids
3
+ # Augments an object with Git support.
4
+ module Git
5
+ def git_supported?
6
+ File.exist? File.join(Dir.pwd, ".git")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -9,58 +9,63 @@ module Milestoner
9
9
  include Thor::Actions
10
10
  include ThorPlus::Actions
11
11
 
12
- package_name Milestoner::Identity.label
12
+ package_name Milestoner::Identity.version_label
13
13
 
14
14
  def initialize args = [], options = {}, config = {}
15
15
  super args, options, config
16
+ @configuration = Milestoner::Configuration.new Milestoner::Identity.file_name, defaults: defaults
17
+ @tagger = Milestoner::Tagger.new configuration.settings[:version],
18
+ commit_prefixes: configuration.settings[:git_commit_prefixes]
19
+ @pusher = Milestoner::Pusher.new
16
20
  end
17
21
 
18
- desc "-c, [--commits]", "Show tag message commits for next milestone."
22
+ desc "-c, [--commits]", "Show commits for next milestone."
19
23
  map %w(-c --commits) => :commits
20
- def commits version
21
- tagger = Milestoner::Tagger.new version
22
- say "Milestone #{version} Commits:"
24
+ def commits
23
25
  tagger.commit_list.each { |commit| say commit }
26
+ rescue Milestoner::Errors::Base => base_error
27
+ error base_error.message
24
28
  end
25
29
 
26
30
  desc "-t, [--tag=TAG]", "Tag local repository with new version."
27
31
  map %w(-t --tag) => :tag
28
32
  method_option :sign, aliases: "-s", desc: "Sign tag with GPG key.", type: :boolean, default: false
29
- def tag version
30
- tagger = Milestoner::Tagger.new version
31
- tagger.create sign: options[:sign]
33
+ def tag version = configuration.settings[:version]
34
+ tagger.create version, sign: sign_tag?(options[:sign])
32
35
  say "Repository tagged: #{tagger.version_label}."
33
- rescue Milestoner::VersionError => version_error
34
- error version_error.message
35
- rescue Milestoner::DuplicateTagError => tag_error
36
- error tag_error.message
36
+ rescue Milestoner::Errors::Base => base_error
37
+ error base_error.message
37
38
  end
38
39
 
39
- desc "-p, [--push]", "Push tags to remote repository."
40
+ desc "-p, [--push]", "Push local tag to remote repository."
40
41
  map %w(-p --push) => :push
41
42
  def push
42
- pusher = Milestoner::Pusher.new
43
43
  pusher.push
44
44
  info "Tags pushed to remote repository."
45
+ rescue Milestoner::Errors::Base => base_error
46
+ error base_error.message
45
47
  end
46
48
 
47
- desc "-P, [--publish=PUBLISH]", "Tag and push to remote repository."
49
+ desc "-P, [--publish=PUBLISH]", "Tag and push milestone to remote repository."
48
50
  map %w(-P --publish) => :publish
49
51
  method_option :sign, aliases: "-s", desc: "Sign tag with GPG key.", type: :boolean, default: false
50
- def publish version
51
- tagger = Milestoner::Tagger.new version
52
- pusher = Milestoner::Pusher.new
53
- tag_and_push tagger, pusher, options
54
- rescue Milestoner::VersionError => version_error
55
- error version_error.message
56
- rescue Milestoner::DuplicateTagError => tag_error
57
- error tag_error.message
52
+ def publish version = configuration.settings[:version]
53
+ tag_and_push version, options
54
+ rescue Milestoner::Errors::Base => base_error
55
+ error base_error.message
58
56
  end
59
57
 
60
- desc "-v, [--version]", "Show version."
58
+ desc "-e, [--edit]", "Edit #{Milestoner::Identity.label} settings in default editor."
59
+ map %w(-e --edit) => :edit
60
+ def edit
61
+ info "Editing: #{configuration.computed_file_path}..."
62
+ `#{editor} #{configuration.computed_file_path}`
63
+ end
64
+
65
+ desc "-v, [--version]", "Show #{Milestoner::Identity.label} version."
61
66
  map %w(-v --version) => :version
62
67
  def version
63
- say Milestoner::Identity.label_version
68
+ say Milestoner::Identity.version_label
64
69
  end
65
70
 
66
71
  desc "-h, [--help=HELP]", "Show this message or get help for a command."
@@ -71,10 +76,24 @@ module Milestoner
71
76
 
72
77
  private
73
78
 
74
- def tag_and_push tagger, pusher, options
75
- if tagger.create(sign: options[:sign]) && pusher.push
76
- say "Repository tagged and pushed: #{tagger.version_label}."
77
- say "Milestone published!"
79
+ attr_reader :configuration, :tagger, :pusher
80
+
81
+ def defaults
82
+ {
83
+ version: "",
84
+ git_commit_prefixes: %w(Fixed Added Updated Removed Refactored),
85
+ git_tag_sign: false
86
+ }
87
+ end
88
+
89
+ def sign_tag? sign
90
+ sign | configuration.settings[:git_tag_sign]
91
+ end
92
+
93
+ def tag_and_push version, options
94
+ if tagger.create(version, sign: sign_tag?(options[:sign])) && pusher.push
95
+ info "Repository tagged and pushed: #{tagger.version_label}."
96
+ info "Milestone published!"
78
97
  else
79
98
  tagger.destroy
80
99
  end
@@ -0,0 +1,36 @@
1
+ module Milestoner
2
+ # Default configuration for gem with support for custom settings.
3
+ class Configuration
4
+ def initialize file_name = ".unknownrc", defaults: {}
5
+ @file_name = file_name
6
+ @defaults = defaults
7
+ end
8
+
9
+ def local_file_path
10
+ File.join Dir.pwd, file_name
11
+ end
12
+
13
+ def global_file_path
14
+ File.join ENV["HOME"], file_name
15
+ end
16
+
17
+ def computed_file_path
18
+ File.exist?(local_file_path) ? local_file_path : global_file_path
19
+ end
20
+
21
+ def settings
22
+ defaults.merge(load_settings).reject { |_, value| value.nil? }
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :file_name, :defaults
28
+
29
+ def load_settings
30
+ yaml = YAML.load_file computed_file_path
31
+ yaml.is_a?(Hash) ? yaml : {}
32
+ rescue
33
+ defaults
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ module Milestoner
2
+ module Errors
3
+ # The base class for all Milestoner related errors.
4
+ class Base < StandardError
5
+ def initialize message = "Invalid Milestoner action."
6
+ super message
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Milestoner
2
+ module Errors
3
+ # Raised for invalid gem configuration.
4
+ class Configuration < Base
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Milestoner
2
+ module Errors
3
+ # Raised for duplicate tags.
4
+ class DuplicateTag < Base
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Milestoner
2
+ module Errors
3
+ # Raised for projects not initialized as Git repositories.
4
+ class Git < Base
5
+ def initialize message = "Invalid Git repository."
6
+ super message
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Milestoner
2
+ module Errors
3
+ # Raised for invalid version formats.
4
+ class Version < Base
5
+ end
6
+ end
7
+ end
@@ -10,11 +10,15 @@ module Milestoner
10
10
  end
11
11
 
12
12
  def self.version
13
- "0.3.0"
13
+ "0.4.0"
14
14
  end
15
15
 
16
- def self.label_version
16
+ def self.version_label
17
17
  [label, version].join " "
18
18
  end
19
+
20
+ def self.file_name
21
+ ".#{name}rc"
22
+ end
19
23
  end
20
24
  end
@@ -1,11 +1,14 @@
1
1
  module Milestoner
2
2
  # Handles publishing of Git tags to remote repository.
3
3
  class Pusher
4
+ include Aids::Git
5
+
4
6
  def initialize kernel: Kernel
5
7
  @kernel = kernel
6
8
  end
7
9
 
8
10
  def push
11
+ fail(Errors::Git) unless git_supported?
9
12
  kernel.system "git push --tags"
10
13
  end
11
14
 
@@ -1,42 +1,45 @@
1
1
  module Milestoner
2
2
  # Handles the tagging of a project repository.
3
3
  class Tagger
4
- attr_reader :version
4
+ include Aids::Git
5
5
 
6
- def self.commit_prefixes
7
- %w(Fixed Added Updated Removed Refactored) # Order is important for controlling the sort.
8
- end
9
-
10
- def self.commit_prefix_regex
11
- /\A(#{commit_prefixes.join "|"})/
12
- end
6
+ attr_reader :version_number, :commit_prefixes
13
7
 
14
8
  def self.version_regex
15
9
  /\A\d{1}\.\d{1}\.\d{1}\z/
16
10
  end
17
11
 
18
- def initialize version
19
- @version = validate_version version
12
+ def initialize version = nil, commit_prefixes: []
13
+ @version_number = version
14
+ @commit_prefixes = commit_prefixes
20
15
  end
21
16
 
22
17
  def version_label
23
- "v#{version}"
18
+ "v#{version_number}"
24
19
  end
25
20
 
26
21
  def version_message
27
- "Version #{version}."
22
+ "Version #{version_number}."
23
+ end
24
+
25
+ def commit_prefix_regex
26
+ return // if commit_prefixes.empty?
27
+ Regexp.union commit_prefixes
28
28
  end
29
29
 
30
30
  def tagged?
31
+ fail(Errors::Git) unless git_supported?
31
32
  response = `git tag`
32
33
  !(response.nil? || response.empty?)
33
34
  end
34
35
 
35
36
  def duplicate?
37
+ fail(Errors::Git) unless git_supported?
36
38
  system "git rev-parse #{version_label} > /dev/null 2>&1"
37
39
  end
38
40
 
39
41
  def commits
42
+ fail(Errors::Git) unless git_supported?
40
43
  groups = build_commit_prefix_groups
41
44
  group_by_commit_prefix! groups
42
45
  sort_by_commit_prefix! groups
@@ -47,20 +50,15 @@ module Milestoner
47
50
  commits.map { |commit| "- #{commit}" }
48
51
  end
49
52
 
50
- def create sign: false
51
- fail(DuplicateTagError, "Duplicate tag exists: #{version_label}.") if duplicate?
52
-
53
- begin
54
- message_file = Tempfile.new Milestoner::Identity.name
55
- File.open(message_file, "w") { |file| file.write tag_message }
56
- `git tag #{tag_options message_file, sign: sign}`
57
- ensure
58
- message_file.close
59
- message_file.unlink
60
- end
53
+ def create version = version_number, sign: false
54
+ fail(Errors::Git) unless git_supported?
55
+ @version_number = validate_version version
56
+ fail(Errors::DuplicateTag, "Duplicate tag exists: #{version_label}.") if duplicate?
57
+ create_tag sign: sign
61
58
  end
62
59
 
63
60
  def destroy
61
+ fail(Errors::Git) unless git_supported?
64
62
  `git tag --delete #{version_label}`
65
63
  end
66
64
 
@@ -68,7 +66,7 @@ module Milestoner
68
66
 
69
67
  def validate_version version
70
68
  message = "Invalid version: #{version}. Use: <major>.<minor>.<maintenance>."
71
- fail(VersionError, message) unless version.match(self.class.version_regex)
69
+ fail(Errors::Version, message) unless version.match(self.class.version_regex)
72
70
  version
73
71
  end
74
72
 
@@ -81,7 +79,7 @@ module Milestoner
81
79
  end
82
80
 
83
81
  def build_commit_prefix_groups
84
- groups = self.class.commit_prefixes.map.with_object({}) { |prefix, group| group.merge! prefix => [] }
82
+ groups = commit_prefixes.map.with_object({}) { |prefix, group| group.merge! prefix => [] }
85
83
  groups.merge! "Other" => []
86
84
  end
87
85
 
@@ -91,7 +89,7 @@ module Milestoner
91
89
 
92
90
  def group_by_commit_prefix! groups = {}
93
91
  raw_commits.each do |commit|
94
- prefix = commit[self.class.commit_prefix_regex]
92
+ prefix = commit[commit_prefix_regex]
95
93
  key = groups.key?(prefix) ? prefix : "Other"
96
94
  groups[key] << sanitize_commit(commit)
97
95
  end
@@ -110,5 +108,14 @@ module Milestoner
110
108
  return options.gsub("--sign ", "") unless sign
111
109
  options
112
110
  end
111
+
112
+ def create_tag sign: false
113
+ message_file = Tempfile.new Milestoner::Identity.name
114
+ File.open(message_file, "w") { |file| file.write tag_message }
115
+ `git tag #{tag_options message_file, sign: sign}`
116
+ ensure
117
+ message_file.close
118
+ message_file.unlink
119
+ end
113
120
  end
114
121
  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: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -30,7 +30,7 @@ cert_chain:
30
30
  aSif+qBc6oHD7EQWPF5cZkzkIURuwNwPBngZGxIKaMAgRhjGFXzUMAaq++r59cS9
31
31
  xTfQ4k6fglKEgpnLAXiKdo2c8Ym+X4rIKFfedQ==
32
32
  -----END CERTIFICATE-----
33
- date: 2015-09-09 00:00:00.000000000 Z
33
+ date: 2015-09-13 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: thor
@@ -312,8 +312,14 @@ files:
312
312
  - README.md
313
313
  - bin/milestoner
314
314
  - lib/milestoner.rb
315
+ - lib/milestoner/aids/git.rb
315
316
  - lib/milestoner/cli.rb
316
- - lib/milestoner/exceptions.rb
317
+ - lib/milestoner/configuration.rb
318
+ - lib/milestoner/errors/base.rb
319
+ - lib/milestoner/errors/configuration.rb
320
+ - lib/milestoner/errors/duplicate_tag.rb
321
+ - lib/milestoner/errors/git.rb
322
+ - lib/milestoner/errors/version.rb
317
323
  - lib/milestoner/identity.rb
318
324
  - lib/milestoner/pusher.rb
319
325
  - lib/milestoner/tagger.rb
metadata.gz.sig CHANGED
Binary file
@@ -1,9 +0,0 @@
1
- module Milestoner
2
- # Raised for invalid version formats.
3
- class VersionError < StandardError
4
- end
5
-
6
- # Raised for duplicate tags.
7
- class DuplicateTagError < StandardError
8
- end
9
- end